/* ============================================================================
 * << File: main.c >>
 * ------------------
 *  Authors: Emiliano Castagnari (aka Torian) <ecastag@fi.uba.ar>
 *           Diego Essaya <dessaya@fi.uba.ar>
 *     Date: 29/12/03
 *
 *  Description:
 *    Server initialization functions, including main().
 * ============================================================================
 * ============================================================================
 *
 * ChangeLog: View Changelog
 *
 * ============================================================================
 * Copyright (C) 2003, 2004 Diego Essaya, Emiliano Castagnari
 * 
 * This file is part of gems.
 * 
 * gems is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * gems 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * ============================================================================
 */

#include <ctype.h>          /* isdigit() */
#include <stdlib.h>         /* atoi() */
#include <locale.h>         /* setlocale() */
#include <string.h>         /* strlen() */
#include <fcntl.h>          /* open() */
#include <sys/stat.h>       /* open() */
#include <unistd.h>         /* close(), unlink() */

#include "defaults.h"
#include "gems-server.h"
#include "log.h"
#include "call_script.h"
#include "version_server.h"

#include "main.h"

/* Command line arguments: */
#define OPT_TOKEN       '-'
#define OPT_IP          "-ip"
#define OPT_PORT        "-port"
#define OPT_FLOG        "-log"
#define OPT_NOSCRIPT    "-noscript"
#define OPT_MAXCONN     "-maxconn"
#define OPT_SCRIPT_BIN	"-script_bin"
#define OPT_HELP		"-h"
#define OPT_VERSION		"-v"
#define OPT_WAIT		"-wait"

/*****************************************************************************/
int main(int argc, char *argv[])
{
	t_options options;				/* Server options */ 
	status_t status = CONTINUE;		/* Program status, and return value */

	/* Locale configuration: */
	setlocale(LC_ALL, "");
	bindtextdomain("gems", LOCALEDIR);
	textdomain("gems");

	/* Set default options: */
	init_options(&options);

	/* Parse arguments: */
	status = parse_args(argc, argv, &options);

	if (status == CONTINUE)
	{
		/* Validate arguments: */
		if (check_options(&options) == FAIL) return EXIT_FAIL;

		status = init_server(&options);
	}

	return status;
}


status_t parse_args(int argc, char *argv[], t_options *options)
{
	status_t status = CONTINUE;	/* state variable, and return value */
	int i;
	int missing_arg = 0;		/* ¿arguments missing? */

	for (i = 1; (i < argc) && (status == CONTINUE); i++)
	{
#ifdef DEBUG
		(i % 2) ? printf(" argv[%i]: %5s: ", i,
						 argv[i]) : printf("%s%c", argv[i], '\n');
		((argc - i - 1)) ? 0 : printf("\n");
#endif

		/* IP address */
		if (cmpstr(argv[i], OPT_IP) == TRUE)
		{
			if (i + 1 < argc) options->ip.ip_str = argv[++i];
			else missing_arg = 1;
		}

		/* TCP port */
		else if (cmpstr(argv[i], OPT_PORT) == TRUE)
		{
			if (i + 1 < argc) options->port = atoi(argv[++i]);
			else missing_arg = 1;
		}

		/* 'wait' option */
		else if (cmpstr(argv[i], OPT_WAIT) == TRUE)
		{
			if (i + 1 < argc) options->expected_connections = atoi(argv[++i]);
			else missing_arg = 1;
		}

		/* Log filename */
		else if (cmpstr(argv[i], OPT_FLOG) == TRUE)
		{
			if ((i + 1) < argc) options->flog = argv[++i];
			else missing_arg = 1;
		}

		/* 'noscript' option */
		else if (cmpstr(argv[i], OPT_NOSCRIPT) == TRUE)
			options->script = 0;

		/* Script location */
		else if (cmpstr(argv[i], OPT_SCRIPT_BIN) == TRUE)
		{
			if (i + 1 < argc) options->script_bin = argv[++i];
			else missing_arg = 1;
		}

		/* Max number of connections: */
		else if (cmpstr(argv[i], OPT_MAXCONN) == TRUE)
		{
			if (i + 1 < argc) options->maxconn = atoi(argv[++i]);
			else missing_arg = 1;
		}

		/* Show version : */
		else if (cmpstr(argv[i], OPT_VERSION) == TRUE)
		{
			PRINT_VERSION(APPNAME)
			status = EXIT_OK;
		}

		/* Help: */
		else if (cmpstr(argv[i], OPT_HELP) == TRUE)
		{
			long_help();
			status = EXIT_OK;
		}

		else /* Invalid option */
		{
			g_log(ERR_NOT_OPT, argv[i]);
			short_help();
			status = EXIT_FAIL;
		}

		/* Arguments missing? */
		if (missing_arg)
		{
			g_log(ERR_OPT_WO_ARG, argv[i]);
			status = EXIT_FAIL;
		}
	}

#ifdef DEBUG
	print_options(options);
#endif

	return status;
}


/*****************************************************************************/
char *create_lock(t_options *options)
{
	int fd;
	static char name[64];
	char pid_str[16];
	int pid_strlen;

	snprintf(name, 64, "%s%d", LOCK_PREFIX, options->port);

	if ((fd = open(name, O_CREAT | O_EXCL | O_RDWR, 0600)) == -1)
	{
		g_log(ERR_LOCK, name, APPNAME);
		return NULL;
	}

	pid_strlen = snprintf(pid_str, 16, "%d\n", getpid());
	write(fd, pid_str, pid_strlen);
	close(fd);
	return name;
}


/*****************************************************************************/
void release_lock(char *name)
{
	if (unlink(name) == -1) perror(APPNAME);
}


/*****************************************************************************/
void short_help()
{
	printf(_("Usage: %s [%s IP] [%s PORT] [%s N] [%s M] [%s LOGFILE]\n"
		"\t\t [%s] [%s SCRIPT] [%s] [%s]\n"), 
		APPNAME, OPT_IP, OPT_PORT, OPT_MAXCONN, OPT_WAIT, OPT_FLOG,
		OPT_NOSCRIPT, OPT_SCRIPT_BIN, OPT_HELP, OPT_VERSION);
	printf(_("Try `%s %s' for more information.\n"), APPNAME, OPT_HELP);
}

/*****************************************************************************/
void long_help()
{
	printf(_("Usage: %s [options]\n"), APPNAME);
	putchar('\n');
	printf(_("Options:\n"));
	printf(_("  %s IP\t\tIP where connections will be accepted.\n"), OPT_IP);
	printf(_("  %s PORT\t\tTCP port. Default: %d.\n"), 
		OPT_PORT, DEFAULT_TCP_PORT);
	printf(_("  %s N\t\tAccept N simultaneous clients. Default: %d.\n"),
		OPT_MAXCONN, SERVER_DEF_MAX_CONN);
	printf(_("  %s M\t\tWait for the connection of M clients before starting\n"
		"\t\t\ttransmission. Default: %d.\n"), OPT_WAIT, SERVER_DEF_WAIT);
	printf(_("  %s LOGFILE\t\tSpecify the log destination. "
		"The possible values are:\n"
		"\t\t\tthe name of a file, `syslog', or `stderr'.\n"), OPT_FLOG);
	printf(_("  %s\t\tDo not run script(1).\n"), OPT_NOSCRIPT);
	printf(_("  %s SCRIPT\tSpecify the location of script.\n"
		"\t\t\tDefault: %s.\n"), OPT_SCRIPT_BIN, SERVER_DEF_SCRIPT_BIN);
	printf(_("  %s\t\t\tShow this help message.\n"), OPT_HELP);
	printf(_("  %s\t\t\tShow version information.\n"), OPT_VERSION);
}

/*****************************************************************************/
void init_options(t_options * opcion)
{
	opcion->ip.s_addr = SERVER_DEF_IP;		/* Equivalent to INADDR_ANY */
	opcion->ip.ip_str = NULL;
	opcion->port = DEFAULT_TCP_PORT;
	opcion->flog = SERVER_DEF_FLOG;
	opcion->script = SERVER_DEF_SCRIPT;
	opcion->script_bin = SERVER_DEF_SCRIPT_BIN;
	opcion->maxconn = SERVER_DEF_MAX_CONN;
	opcion->expected_connections = SERVER_DEF_WAIT;
}


/*****************************************************************************/
fbool_t cmpstr(const char *str_1, const char *str_2)
{
	int size_str1 = strlen(str_1);
	int size_str2 = strlen(str_2);
	int i;

	if (size_str1 != size_str2) return FALSE;

	for (i = 0; (i < size_str1) && (str_1[i] == str_2[i]); i++) ;

	return (i == size_str1) ? TRUE : FALSE;
}

/*****************************************************************************/
fbool_t check_options(t_options * opcion)
{
	/* Validate TCP port: */
	if (!port_range(opcion->port))
	{
		g_log(ERR_PORT);
		return FAIL;
	}
	/* Validate 'maxconn' option: */
	if (opcion->maxconn <= 0)
	{
		g_log(ERR_MAXCONN);
		return FAIL;
	}
	/* Validate 'wait' option: */
	if ((opcion->expected_connections < 0) || 
		(opcion->expected_connections > opcion->maxconn))
	{
		g_log(ERR_WAIT);
		return FAIL;
	}
	return TRUE;
}


/*****************************************************************************/
#ifdef DEBUG
void print_options(const t_options * option)
{
	printf("%c", '\n');
	printf(" IP: ........ %s\n",
		   (option->ip.ip_str == NULL) ? "*" : option->ip.ip_str);
	printf("       Port: ... %i\n", option->port);
	printf("    LogFile: ... %s\n", option->flog);
	printf("     Script: ... %d\n", option->script);
	printf(" Script_bin: ... %s\n", option->script_bin);
	printf("    MaxConn: ... %d\n", option->maxconn);
	printf("       Wait: ... %d\n", option->expected_connections);
	printf("\n");
}
#endif /* DEBUG */

/*****************************************************************************/
status_t init_server(t_options *options)
{
	fbool_t r;
	int input_fd;	/* data input file descriptor */
	char *lockname;

	if (options == NULL) return EXIT_FAIL;

	if ((lockname = create_lock(options)) == NULL)
		return EXIT_FAIL;

	fprintf(stderr, _("%s initialized.\n"), APPNAME);

	/* Must call script? */
	if (options->script)
	{
		if ((r = start_script(options->script_bin, &input_fd)) != CONTINUE)
		{
			kill_script();
			release_lock(lockname);
			return r;
		}
	}
	else input_fd = 0;  /* stdin */

	/* Configure logging: */
	if (options->flog)
	{
		if (cmpstr(options->flog, "syslog") == TRUE)
			g_set_logdest(G_LOG_SYSLOG);
		else if (cmpstr(options->flog, "stderr") == TRUE)
			g_set_logdest(G_LOG_STDERR);
		else
			g_set_logdest(G_LOG_FILE, options->flog);
	}
	else g_set_logdest(G_LOG_NOLOG);

	/* Main server function: */
	r = gems_server(input_fd, options);

	kill_script();

	if (r == EXIT_OK)
		fprintf(stderr, _("\n%s terminated.\n"), APPNAME);

	release_lock(lockname);
	return r;
}

/* vim: set ts=4 sw=4: */
