/*
 *========================================================================
 * $Id: xmlsysd_daemon.c 138 2004-06-20 08:37:07Z rgb $
 *
 * See copyright in copyright.h and the accompanying file COPYING
 *========================================================================
 */


#include "xmlsysd.h"

void xmlsysd_daemon()
{

 struct sockaddr_in serverINETaddress;
 struct sockaddr_in clientINETaddress;
 struct sockaddr* serverSockAddrPtr;
 struct sockaddr* clientSockAddrPtr;
 struct linger linger;
 struct hostent* hostidentptr;
 struct in_addr* hostaddr;

 int on = 1;				/* To reuse socket */
 int pid;				/* for forking */
 fd_set fdset;				/* for the select statement */
 int ret;				/* for return value of select */
 int retries;				/* number of times to try to get the port */

 int n;
 char procname[K];
 int pnamelen;

 if((verbose == D_ALL) || (verbose == D_DAEMON)){
   printf("D_DAEMON: Starting xmlsysd_daemon.  Use -v %d to focus.\n",D_DAEMON);
 }

/* 
 * These are needed in init/get/eval_indentity(); we set them very early
 * because we like to know who we are early when we are debugging.
 */
 if(gethostname(hostname,K-2)){
   snprintf(hostname,K-2,"localhost");
 }
 hostidentptr = gethostbyname(hostname);
 if(hostidentptr == NULL){
   fprintf(stderr,"Warning: hostname %s not set/resolvable.\n",hostname);
 }
 hostaddr = (struct in_addr*) hostidentptr->h_addr_list[0];
 sprintf(hostip,"%s",inet_ntoa(*hostaddr));
 if((verbose == D_ALL) || (verbose == D_DAEMON)){
   printf("D_DAEMON: xmlsysd running on host = %s, hostip = %s\n",hostname,hostip);
 }

/*
 * The daemon runs in two modes.  It can run as an inetd daemon (well,
 * really as an xinetd daemon if you are smart), which reads from stdin
 * and writes to stdout, buffered by xinetd in between.  It can also
 * run as a real forking daemon that listens at a port and forks
 * off copies of itself to manage connections.  One day it may even be
 * a multicast daemon that broadcasts its updates like rwhod used to.
 */
 switch(daemonmode){
   case FORK:
     if((verbose == D_ALL) || (verbose == D_DAEMON)){
       printf("D_DAEMON: I am a Forking Daemon!  You'd have to be crazy not to be scared!\n");
     }
     break;
   case INETD:
     if((verbose == D_ALL) || (verbose == D_DAEMON)){
       printf("D_DAEMON: I am an xinetd Daemon.  Hopefully I'm secure(d)!\n");
     }
     input_fd = STDIN_FILENO;
     output_fd = STDOUT_FILENO;
     error_fd = STDERR_FILENO;
     /*
      * so_linger controls whether or not any unsent data is to be 
      * delivered on close.  If linger is zero, the data is not sent and
      * the socket is closed immediately.  If linger is nonzero, the
      * queued message will be sent.  The service is closed, but enters
      * the dread CLOSE_WAIT state and hangs on, probably forever,
      * waiting for a read that never comes and consuming resources.
      */
     linger.l_onoff = 0;	/* Linger for just a bit */
     linger.l_linger = 0;	/* do NOT linger -- exit and discard data. */
     setsockopt(output_fd, SOL_SOCKET, SO_LINGER, (void *)&linger, 
                 sizeof(linger));
     /*
      * this loops until a quit command is received.
      */
     xmlsysd_work_loop();
     break;
   case MCAST:
   default:
     if((verbose == D_ALL) || (verbose == D_DAEMON)){
       printf("Huh?\n");
     }
     fprintf(stderr,"Multicast (or other) operation not yet supported.  Try again.\n");
     exit(0);
     break;
 }

 /* 
  * Ignore death of child signals to prevent zombies 
  */
 signal(SIGCHLD,SIG_IGN);

 /* 
  * Create a INET socket, bidirectional, default protocol.  This is
  * all boringly standard code, a shame it is so hard to read.
  */
 server_fd = socket(AF_INET,SOCK_STREAM,0);
 if (server_fd < 0){
   fprintf(stderr,"socket: %.100s", strerror(errno));
   exit(1);
 }

 /* 
  * Set socket options.  We try to make the port reusable and have it
  * close as fast as possible without waiting in unnecessary wait states
  * on close. 
  */
 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, 
                 sizeof(on));
 /*
  * so_linger controls whether or not any unsent data is to be 
  * delivered on close.  If linger is zero, the data is not sent and
  * the socket is closed immediately.  If linger is nonzero, the
  * queued message will be sent.  The service is closed, but enters
  * the dread CLOSE_WAIT state and hangs on, probably forever,
  * waiting for a read that never comes and consuming resources.
  */
 linger.l_onoff = 1;	/* Linger for just a bit */
 linger.l_linger = 0;	/* do NOT linger -- exit and discard data. */
 setsockopt(server_fd, SOL_SOCKET, SO_LINGER, (void *)&linger, 
                 sizeof(linger));
 serverlen = sizeof(serverINETaddress);
 bzero( (char*) &serverINETaddress,serverlen);	/* clear structure */
 serverINETaddress.sin_family = AF_INET;	/* Internet domain */
 serverINETaddress.sin_addr.s_addr = htonl(INADDR_ANY);	/* Accept all */
 serverINETaddress.sin_port = htons(port);	/* Server port number */
 serverSockAddrPtr = (struct sockaddr*) &serverINETaddress;

 /* 
  * Bind the socket to the desired port.  Try up to six times (30sec) IF the
  * port is in use
  */
 retries = 6;
 errno = 0;	/* To zero any possible garbage value */
 while(retries--){
   if(bind(server_fd,serverSockAddrPtr,serverlen) < 0) {
     if(errno != EADDRINUSE){
       close(server_fd);
       fprintf(stderr,"bind: %.100s\n", strerror(errno));
       fprintf(stderr,"socket bind to port %d failed: %d.\n", port,errno);
       exit(255);
     }
   } else break;
   /* printf("Got no port: %s\n",strerror(errno)); */
   sleep(5);
 }
 if(errno){
   if(errno == EADDRINUSE){
     fprintf(stderr,"Timeout (tried to bind six times five seconds apart)\n");
   } 
   close(server_fd);
   fprintf(stderr,"bind to port %d failed: %.100s\n",port,strerror(errno));
   exit(0);
  }

 /* 
  * Socket exists.  Service it.  Queue up to n_connxns incoming connections 
  * or die. Default 10 matches the limits in the default xinetd.
  */
 if(listen(server_fd,nconnxns) < 0){
   fprintf(stderr,"listen: %.100s", strerror(errno));
   exit(255);
 }

 /* Arrange SIGCHLD to be caught. */
 signal(SIGCHLD, sigchld_handler);

 /*
  * Initialize client structures.
  */
 clientlen = sizeof(clientINETaddress);
 clientSockAddrPtr = (struct sockaddr*) &clientINETaddress;

 /*
  * Loop "forever", or until daemon crashes or is killed with a signal.
  */
 while(1){
   /* Accept a client connection */
   if((verbose == D_ALL) || (verbose == D_DAEMON)){
     printf("D_DAEMON: Accepting Client connection...\n");
   }
          
   /* 
    * Wait in select until there is a connection. Presumably this is
    * more efficient than just blocking on the accept
    */
   FD_ZERO(&fdset);
   FD_SET(server_fd, &fdset);
   ret = select(server_fd + 1, &fdset, NULL, NULL, NULL);
   if (ret < 0 || !FD_ISSET(server_fd, &fdset)) {
     if (errno == EINTR)
       continue;
     fprintf(stderr,"select: %.100s", strerror(errno));
     continue;
   }
          
   /*
    * A call is waiting.  Accept it.
    */
   client_fd = accept(server_fd,clientSockAddrPtr,&clientlen);
   if (client_fd < 0){
     if (errno == EINTR)
       continue;
     fprintf(stderr,"accept: %.100s", strerror(errno));
     continue;
   }
   if((verbose == D_ALL) || (verbose == D_DAEMON)){
     printf("D_DAEMON:                ...client connection made.\n");
   }

   /*
    * IF I GET HERE... 
    * ...I'm a real daemon.  I therefore fork and have the child process
    * the connection.  The parent continues listening and can service
    * multiple connections in parallel.
    */

   /* 
    * CHILD.  Close the listening (server) socket, and start using the 
    * accepted (client) socket.  We break out of the (infinite) loop to 
    * handle the connection. 
    */
   if ((pid = fork()) == 0){
     close(server_fd);
     break;
   }

   /* 
    * PARENT.  Stay in the loop.  Close the client socket (it's the child's)
    * but leave the server socket open.
    */
   if (pid < 0)
     fprintf(stderr,"fork: %.100s", strerror(errno));
   else
     if((verbose == D_ALL) || (verbose == D_DAEMON)){
       printf("D_DAEMON: Forked child %d to handle socket %d.\n", pid,client_fd);
     }
   close(client_fd);
 }

 /* No need to wait for children -- I'm the child */
 signal(SIGCHLD, SIG_DFL);
 /* Dissociate from calling process group and control terminal */
 setsid();

 if((verbose == D_ALL) || (verbose == D_DAEMON)){
   printf("D_DAEMON: I am the child %d forked to handle socket %d.\n", pid,client_fd);
 }

 /* 
  * This routine needs to be written to work EITHER inside inetd OR
  * as a forking daemon.  For the moment, try encapsulating the handler
  * from the old forking daemon code.  We'll have to rename the streams
  * somehow to permit fprintf to be used to send the messages in both
  * cases...
  */
 input_fd = client_fd;
 output_fd = client_fd;
 error_fd = client_fd;

 /*
  * Finally, we send a message out (that will generally be ignored)
  * concerning our True Nature -- ie revision information -- in case
  * that is relevant.  To debuggers (like me) it might be!
  */
 if((verbose == D_ALL) || (verbose == D_DAEMON)){
   printf("D_DAEMON:==========================================================\n");
   printf("D_DAEMON: xmlsysd v. %d.%.1f, copyright Robert G. Brown 2003.\n",
     VERSION_MAJOR,VERSION_MINOR);
   printf("D_DAEMON:==========================================================\n");
 }

}  /* End of xmlsysd_daemon() */

