/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* queue.c								*/
/*									*/
/*  Copyright (C) 1997,1998,1999 Angelo Masci				*/
/*									*/
/*  This library is free software; you can redistribute it and/or	*/
/*  modify it under the terms of the GNU Library General Public		*/
/*  License as published by the Free Software Foundation; either	*/
/*  version 2 of the License, or (at your option) any later version.	*/
/*									*/
/*  This library is distributed in the hope that it will be useful,	*/
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of	*/
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU	*/
/*  Library General Public License for more details.			*/
/*									*/
/*  You should have received a copy of the GNU Library General Public	*/
/*  License along with this library; if not, write to the Free		*/
/*  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	*/
/*									*/
/*  You can contact the author at this e-mail address:			*/
/*									*/
/*  angelo@styx.demon.co.uk						*/
/*									*/
/* -------------------------------------------------------------------- */
/* $Id$
   -------------------------------------------------------------------- */

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>

#include "common/common.h"
#include "logfile/logfile.h"
#include "npipe.h"
#include "process.h"

#include "resource/resource.h"
#include "lock/lock.h"
#include "parser/gs_translate.h"
#include "driver/driver.h"
#include "version.h"


#include "parser/gs_api/gs_create.h"
#include "parser/gs_api/gs_getdict.h"


/* -------------------------------------------------------------------- */

#if !defined(MVERSION)
#error "MVERSION undefined"
#else
#define VERSION MVERSION
#endif

#if !defined(MSMSDLOGFILE)
#error "MSMSDLOGFILE undefined"
#else
#define LOGFILE         MSMSDLOGFILE
#endif

#if !defined(MSMSDLOGLEVEL)
#error "MSMSDLOGLEVEL undefined" 
#else
#define LOGLEVEL	MSMSDLOGLEVEL
#endif

/* -------------------------------------------------------------------- */

char	queue_lockfile[512],
	queue_errordir[512],
	queue_queuedir[512];

char	*queue_spool,
	*queue_incoming,
	*queue_services,
	*queue_errors,
	*queue_named_pipes,
	*queue_locks;

TOKEN_HEAP
	*queue_services_heap;

static 	RESOURCE resource_list[] = 
	{
		{ RESOURCE_STRING,   "directories.spool", 	0, 1, NULL, 0,  "/var/spool/sms",  0,  		&queue_spool	},
		{ RESOURCE_STRING,   "directories.incoming",	0, 1, NULL, 0,  "incoming",        0,  		&queue_incoming	},
		{ RESOURCE_STRING,   "directories.services", 	0, 1, NULL, 0,  "services",        0,  		&queue_services	},
		{ RESOURCE_STRING,   "directories.errors", 	0, 1, NULL, 0,  "errors",          0,  		&queue_errors	},
		{ RESOURCE_STRING,   "directories.named_pipes", 0, 1, NULL, 0,  "named_pipes",     0,  		&queue_named_pipes	},
		{ RESOURCE_STRING,   "directories.locks", 	0, 1, NULL, 0,  "locks",           0,  		&queue_locks	},
		{ RESOURCE_HEAP,     "services", 		0, 1, NULL, 0,  NULL,        	   0,  		&queue_services_heap	},
		{ RESOURCE_NULL,     NULL, 			0, 1, NULL, 0,  NULL,              0,  		NULL  	}
	};

#define SPOOLDIR	queue_spool
#define LOCKDIR		queue_locks
#define ERRORDIR	queue_errors
#define INCOMINGDIR	queue_incoming
#define SERVICESDIR	queue_services
#define NAMEDPIPESDIR	queue_named_pipes

/* -------------------------------------------------------------------- */

#if !defined(MSERVICEDIR)
#error "MSERVICEDIR undefined"
#else
#define SERVICEDIR      MSERVICEDIR
#endif

#define DAEMONCONFIGFILE (MSERVICEDIR "/sms_daemons")

/* -------------------------------------------------------------------- */

volatile int term_flag = FALSE;

/* -------------------------------------------------------------------- */

static void exit_func(int sig);
static void usage(char *file);
static int deamon_init(void);
static int install_sighandlers(void);

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static void exit_func(int sig)
{	term_flag = TRUE;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int install_sighandlers(void)
{
	struct 	sigaction 
		act, 
		oact;


	act.sa_handler = exit_func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (sigaction(SIGTERM, &act, &oact) != 0)
	{	return -1;
	}

	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int deamon_init(void)
{
	pid_t	pid;


	set_consolelog(FALSE);
	
	pid = fork();
	if (pid < 0)
	{
		lprintf(LOG_ERROR, "fork() failed\n");
		return -1;
	}
	else 
	if (pid != 0)
	{	exit(0);
	}

	if (setsid() < 0)	/* Become session leader 	*/
	{
		lprintf(LOG_ERROR, "setsid() failed\n");
		return -1;
	}


	if (chdir("/") < 0)	/* Change working directory	*/
	{
		lprintf(LOG_ERROR, "chdir() failed\n");
		return -1;
	}

	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static void usage(char *file)
{
	lprintf(LOG_STANDARD, "Usage: %s [-l loglevel] service\n", file);
	lprintf(LOG_STANDARD, "       %s -v\n", file);
	lprintf(LOG_STANDARD, "       %s -a\n", file);
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static void release_lock(void)
{	resource_unlock(queue_lockfile);
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
static int list_services(TOKEN_HEAP *heap)
{
	/* unfinished */




	return 0;
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int main(int argc, char *argv[])
{
	DEVICE_ENTRY 
		*device;
	int 	c, i,
		list_services_flag;
	char	*ptr,
		service_pipe[512],
		*service,
		*protocol,
		*value;

	/* ---------------------------- */

	set_logfile(LOGFILE);
	set_loglevel(LOGLEVEL);
	set_consolelog(TRUE);

	/* ---------------------------- */

	list_services_flag = FALSE;

	while ((c = getopt (argc, argv, "l:va")) != -1)
        {
                switch (c)
                {
                        case 'l':  
				set_loglevel((int)strtol(optarg, &ptr, 10));
				if (ptr == optarg)
				{
					lprintf(LOG_ERROR, "Option l requires an argument\n");
	     	                        usage(argv[0]);
                	                return -1;
				}
                                
                                break;
                        case 'v':
				lprintf(LOG_STANDARD, "queue %s\n", VERSION);
                                return 0;

                        case 'a':
				list_services_flag = TRUE;
				break;

                        case '?':
                                lprintf(LOG_ERROR, "Unknown option `-%c'\n", optopt);
                                usage(argv[0]);
                                return -1;

                        default:
                                return -1;
                }
        }

	/* -------------------------------------------------------------------- */

	if (read_resource_file(DAEMONCONFIGFILE, resource_list, 1) == RESOURCE_FILE_ERROR)
	{	lprintf(LOG_ERROR, "Failed to read resource file '%s'\n", DAEMONCONFIGFILE);
		return -1;
	}

	if (list_services_flag)
	{	return list_services(queue_services_heap);
	}

	/* -------------------------------------------------------------------- */

	if ((argc - optind) != 1)
	{	usage(argv[0]);
		return -1;
	}

	service = argv[optind];

	/* -------------------------------------------------------------------- */

	if ((value = get_strvalue(queue_services_heap, service)) != NULL)
	{	lprintf(LOG_VERBOSE, "Service '%s' Found\n", service);

		if (strcmp(value, "ENABLED") == 0)
		{	lprintf(LOG_VERBOSE, "Service '%s' is enabled\n", service);	
		}
		else
		{	lprintf(LOG_WARNING, "Service '%s' is NOT enabled\n", service);	
			return -1;
		}

		if ((protocol = get_protocol(service)) == NULL)
		{
			lprintf(LOG_ERROR, "Service '%s' has no entry for 'protocol'\n", service);	
			return -1;
		}
	}
	else
	{	lprintf(LOG_ERROR, "Service '%s' Not Found\n", service);
		return -1;
	}

	/* -------------------------------------------------------------------- */

	strcpy(queue_queuedir, SPOOLDIR);
	sms_filecat(queue_queuedir, SERVICESDIR);
	sms_filecat(queue_queuedir, service);

	if (access(queue_queuedir, F_OK) == -1)
	{
		lprintf(LOG_ERROR, "Queue directory '%s' Not Found\n", queue_queuedir);
		return -1;
	}

	/* -------------------------------------------------------------------- */

	strcpy(queue_errordir, SPOOLDIR);
	sms_filecat(queue_errordir, ERRORDIR);
	sms_filecat(queue_errordir, service);

	if (access(queue_errordir, F_OK) == -1)
	{
		lprintf(LOG_ERROR, "Queue Error directory '%s' Not Found\n", queue_errordir);
		return -1;
	}


	/* -------------------------------------------------------------------- */
	/* Initialise the driver for this queue					*/
	/* From here on we access the driver using the 'device' entry		*/
	/* -------------------------------------------------------------------- */

	device = get_device(protocol);
	if (device == NULL)
	{	lprintf(LOG_ERROR, "Driver for service %s NOT found\n", service);
		return -1;
	}

	(*device->init)(service, device);


	/* -------------------------------------------------------------------- */


	strcpy(queue_lockfile, SPOOLDIR);
	sms_filecat(queue_lockfile, LOCKDIR);
	sms_filecat(queue_lockfile, service);

	resource_check_lockdir(queue_lockfile);	
	if (resource_lock(queue_lockfile))
	{	lprintf(LOG_ERROR, "Queue aborted, lockfile held by another queue daemon\n");
		return -1;
	}

	atexit(release_lock); 


	/* -------------------------------------------------------------------- */

#if 0
	if (install_sighandlers() == -1)
	{	return -1;
	}

	if (deamon_init() == -1)
	{	return -1;
	}
#endif

	/* -------------------------------------------------------------------- */

	lprintf(LOG_STANDARD, "Starting SMS Queue for '%s'...\n", service);

	strcpy(service_pipe, SPOOLDIR);
	sms_filecat(service_pipe, NAMEDPIPESDIR);
	sms_filecat(service_pipe, service);

	lprintf(LOG_VERBOSE, "Creating pipe '%s'\n", service_pipe);


	if (create_named_pipe(service_pipe) == -1)
	{	lprintf(LOG_ERROR, "create_named_pipe() failed\n");
		return -1;
	}


	/* -------------------------------------------------------------------- */

#if 1
	lprintf(LOG_VERBOSE, "Flushing queue...\n");
	process_queue(queue_queuedir, queue_errordir, device);
#endif

	while(!term_flag)
	{
		lprintf(LOG_VERBOSE, "Waiting on named pipe '%s'...\n", service_pipe);

		if (block_on_named_pipe(service_pipe) == -1)
		{
			if (errno == EINTR)
			{	continue;
			}
			else
			{	lprintf(LOG_ERROR, "block_on_named_pipe() failed\n");
				return -1;
			}
		}

		lprintf(LOG_VERBOSE, "Woken by write to '%s'\n", service_pipe);
		lprintf(LOG_VERBOSE, "Processing queue '%s'...\n", queue_queuedir);


		process_queue(queue_queuedir, queue_errordir, device);
	}

	if (term_flag)
	{	lprintf(LOG_STANDARD, "SMS Queue for '%s' received TERM signal\n", service_pipe);
	}

	lprintf(LOG_STANDARD, "Stopping SMS Queue for '%s'...\n", service_pipe);
	lprintf(LOG_VERBOSE, "Deleting named pipe '%s'\n", service_pipe);

	if (delete_named_pipe(service_pipe) == -1)
	{	lprintf(LOG_ERROR, "delete_named_pipe() failed\n");
		return -1;
	}


	resource_unlock(queue_lockfile);
	return 0;
}              


