talker/
talker/bin/
talker/files/whois/
talker/update/
talker/update/bin/
/*
 * main.c
 * The Main line of the Message Server
 *
 * $Id: main.c,v 1.3 1996/03/29 16:51:01 athan Exp $
 *
 * $Log: main.c,v $
 * Revision 1.3  1996/03/29 16:51:01  athan
 * Proper cli arguments now
 * Added code for restart, background, verbose, SIGUSR1 support
 *
 * Revision 1.2  1996/03/28 20:02:07  athan
 * General cleanups
 *
 *
 */

#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#if defined(SUNOS)
#include <memory.h>
#endif
#include <sys/wait.h>

#include "config.h"
#include "file.h"

/* Extern Function Prototypes */

extern int accept_connection(void);
extern int check_timeout(long thistime, int *count);
extern void do_login(int newfd, long ltime);
extern void init_comms(void);
extern void load_file(char *fn, struct afile *it);
extern int open_main_socket(int port);
extern void shut_down(void);

/* Extern Variables */

extern char *optarg;
extern int optind, opterr, optopt;
extern struct afile thefile;

/* Local Function Prototypes */

void catch_hup(int i);
void catch_int(int i);
void cleanupanddie(int i);
void catch_alrm(int i);
void do_help(void);
void set_read_msg_file(int i);
void do_server(void);
void sendusr1_to_child(int i);

/* Local Variables */

int status = INIT;
int read_msg_file = 0;
int port = DEFAULT_PORT;
char *msg_file = DEFAULT_MSG_FILE;
int loop = 0;
time_t loopdelay = 1;
int verbose = 0;
struct afile thefile = {NULL, 0};
int timeout = DEFAULT_TIMEOUT;
int childpid;


int main(int argc,char *argv[])
{
   int optret;
   int restart = 0;
   int background = 0;
   int childstatus;
   
   /* Process command line arguments */
   while (-1 != (optret = getopt(argc, argv, "p:m:l:t:vrbh")))
   {
      switch (optret)
      {
         case 'p': /* Port number */
	    port = atoi(optarg);
	    if (getuid() && port < 1024)
	    {
	       fprintf(stderr, "%s: Must be uid 0 to use ports below 1024!\n",
	       	       argv[0]);
	       exit(-1);
	    }
	    break;
	 case 'm': /* Message file */
	    msg_file = strdup(optarg);
	    if (!msg_file)
	    {
	       fprintf(stderr, "%s: Error duplicating file name!\n",
	       	       argv[0]);
	       exit(-1);
	    }
	    break;
	 case 'l': /* Loop until can bind port */
	    loop = 1;
	    loopdelay = atoi(optarg);
	    if (loopdelay < 1)
	    {
	       fprintf(stderr, "%s: Loop delay must be at least 1 second\n",
	       	       argv[0]);
	       exit(-1);
	    }
	    break;
	 case 't': /* Connection timeout */
	    timeout = atoi(optarg);
	    if (timeout < 0)
	    {
	       fprintf(stderr, "%s: Timeout must be positive\n", argv[0]);
	       exit(-1);
	    }
	 case 'v': /* Print (verbose) errors */
	    verbose++;
	    break;
	 case 'r': /* Restart if crash */
	    restart++;
	    /* Implies background */
	    background++;
	    break;
	 case 'b': /* Run in background */
	    background++;
	    break;
	 case 'h': /* Help */
	    printf("%s: Help...\n", argv[0]);
	    do_help();
	    exit(0);
	    break;
         case ':': /* Missing argument */
	    fprintf(stderr, "%s: Missing argument\n", argv[0]);
	    exit(-1);
	    break;
	 case '?': /* Unknown option */
	    break;
	 default: /* Erk ? */
	    printf("%s: Erk, option processing caused an error!\n", argv[0]);
      }
   }

   if (background)
   {
      childpid = fork();
      switch (childpid)
      {
         case -1: /* Error */
            fprintf(stderr, "%s: Failed to fork!\n", argv[0]);
            exit(-1);
         case 0: /* Child */
            setpgrp();
            break;
         default: /* Parent */
            exit(0);
      }
   }
   if (restart)
   {
      /* Server should restart if it dies for any reason */
      while (1)
      {
	 signal(SIGHUP, SIG_IGN);
	 signal(SIGINT, cleanupanddie);
	 signal(SIGQUIT, cleanupanddie);
	 signal(SIGTERM, cleanupanddie);
	 signal(SIGUSR1, sendusr1_to_child);
         childpid = fork();
         switch (childpid)
	 {
	    case -1: /* Error */
	       exit(-1);
	    case 0: /* Child */
	       do_server();
	       break;
	    default: /* Parent */
	       waitpid(childpid, &childstatus, 0);
	       if (WEXITSTATUS(childstatus) == 42)
	       {
		  /* Child exitted normally */
	          exit(0);
	       }
	 }
	 sleep(10);
      }
   } else
   {
      do_server();
   }

   return 0;
}

void do_server(void)
{
   int newfd;
   int count = 0;
   long mytime = 0;
   struct itimerval oldtimer, newtimer;
   int ret = 0;

   signal(SIGTERM, catch_int);
   signal(SIGINT, catch_int);
   signal(SIGHUP, SIG_IGN);
   signal(SIGPIPE, SIG_IGN);
   signal(SIGALRM, catch_alrm);
   signal(SIGUSR1, set_read_msg_file);

   newtimer.it_interval.tv_sec = loopdelay;
   newtimer.it_interval.tv_usec = 0;
   newtimer.it_value.tv_sec = loopdelay;
   newtimer.it_value.tv_usec = 0;
   setitimer(ITIMER_REAL, &newtimer, &oldtimer);

   init_comms();
   /* Attempt to open a socket and bind it to the port */
   do
   {
      if ((ret = open_main_socket(port)) < 0)
      {
	 if (verbose || !loop)
	 {
            fprintf(stderr, "Error: Failed to open main socket.\n");
	 }
         if (!loop)
         {
            exit(-1);
         }
         sigpause(SIGALRM);
      }
   } while (loop && (ret < 0));
   if (verbose)
   {
      fprintf(stderr, "Socket open and bound to port %d\n", port);
   }
   status = RUNNING;
   
   /* Load the message */
   
   load_file(msg_file, &thefile);
   /* Accept new connections */
   while (status != SHUTDOWN)
   {
      if (read_msg_file)
      {
         load_file(msg_file, &thefile);
	 read_msg_file = 0;
      }
      mytime = (long) time(NULL);
      /* Check for new connections */
      if (-1 != (newfd = accept_connection()))
      {
         do_login(newfd, mytime);
         count++;
      }
      /* Check for timeout on the logins */
      while (check_timeout(mytime, &count) == -1)
      {
      }
      sigpause(SIGALRM);
   }   
   
   shut_down();
   exit(42);
}

void catch_alrm(int i)
{
   signal(SIGALRM, catch_alrm);
}


void do_help(void)
{
   printf(" Possible command line arguments are:\n"
	  "\n"
	  "  -p <port>               - Set port to listen on\n"
	  "  -m <file>               - Set message file to use\n"
	  "  -l <delay (seconds)>    - Loop until port is bound\n"
	  "  -t <timeout (seconds)>  - Time before connection close\n"
	  "  -v                      - Verbose messages\n"
	  "  -r                      - Run with 'angel'\n"
	  "  -b                      - Run in background (implied by -r)\n"
	  "  -h                      - This help message\n");
}


void set_read_msg_file(int i)
{
   read_msg_file = 1;
   signal(SIGUSR1, set_read_msg_file);
}

void catch_int(int i)
{
   status = SHUTDOWN;
}

void cleanupanddie(int i)
{
   kill(childpid, SIGKILL);
   exit(0);
}

void sendusr1_to_child(int i)
{
   kill(childpid, SIGUSR1);
   signal(SIGUSR1, sendusr1_to_child);
}