#include "headers.h"

int streamfd;

/* initialize spooling files */
void initSpool()
{
#  if !defined(SUN) && !defined(BSD)
   int res;
#  endif

   char spoolfile[MAXFNAMESIZE];
   memset(spoolfile, 0, sizeof(spoolfile));

   if (spoolFile == NULL)
      (void)sprintf(spoolfile, "%s/spool/%s", SRSdir, LOGFILE);

   else 
      (void)snprintf(spoolfile, sizeof(spoolFile)-1, "%s", spoolFile);

   if (spoolfd != NULL) (void)fclose(spoolfd);

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR)
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   spoolfd = fopen(spoolfile, "a+b");
   if (spoolfd == NULL)
   {
      error("(in spool child) error opening %s: %s\n\n", spoolfile,
            strerror(errno));

      quit(ERROR);
   }

   (void)chmod(spoolfile, S_IREAD | S_IWRITE);

# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
# endif
}


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


/* spool system logs to spool file locally */
void doSpooling()
{
   int res;

#  if !defined(BSD) && !defined(SUN)
   int fd;
#  endif

#  ifndef SUN
   int count = 0;
#  endif

#  ifndef SUN
   char newmsg[MAXLOGSIZE];
   char logmsg[MAXLOGSIZE];
#  endif

   char readbuf[MAXREADSIZE];   

#  ifndef SUN
   char *logptr, *dataptr, *newptr;
#  endif

#  ifdef BSD
   u_char cmsg[MAXREADSIZE/4];

   struct iovec iov;
   struct msghdr msghdr;

#  elif SUN
   char *bufptr;
   int flags = 0;
#  endif

   FD_ZERO(&readfds);
   errno = 0, spooling = 1;

   debug("awaiting system logs to spool...\n\n");

#  ifdef BSD
   iov.iov_base = readbuf;
   iov.iov_len = sizeof(readbuf)-1;
#  endif

   while(1)
   {
#     ifdef SUN
      struct log_ctl hdr;
      struct strbuf ctl, dat;
#     endif

      memset(readbuf, 0, sizeof(readbuf));

#     if !defined(SUN) && !defined(BSD)
      for (nfds = 0; nfds < FD_SETSIZE-2; nfds++)
         if (FD_ISSET(nfds, &unixm)) FD_SET(nfds, &readfds);
#     endif

      FD_SET(funix, &readfds);
      if (klogfd > 0) FD_SET(klogfd, &readfds);

      nfds = 0, errno = 0;
      nfds = select(FD_SETSIZE, (fd_set *) &readfds, (fd_set *) NULL,
                    (fd_set *) NULL, (struct timeval *) NULL);

      /* won't happen.. we don't have a timeout */
      if (nfds == 0)
      {
         error("(in spool child) no new data/activity... "
               "timed out in select()\n\n");

         FD_CLR(funix, &readfds);
         if (klogfd > 0) FD_CLR(klogfd, &readfds);

         continue;
      }
    
      else if (nfds == ERROR)
      {
         if (errno == EINTR) continue;

         error("(in spool child) error with select(): %s\n\n",
               strerror(errno));

         quit(ERROR);
      }

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

      if ((klogfd > 0) && (FD_ISSET(klogfd, &readfds)))
      {
         memset(readbuf, 0, sizeof(readbuf));

         res = read(klogfd, readbuf, sizeof(readbuf)-1);
         if (res == ERROR)
         {
            error("error reading klogfd.. removing\n\n");

            FD_CLR(klogfd, &readfds);
            (void)close(klogfd), klogfd = ERROR;
         }

         else if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                  (isprint((int)readbuf[1]) != 0))
         {
            /* FIX - do it (add the kern def pri.. "kernel: " and */
            /*     - doLogging it.. then sending it to server     */

            debug("new klog msg = %s\n", readbuf);
         }

         FD_CLR(klogfd, &readfds);
      }

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

#     if !defined(SUN) && !defined(BSD)
      for (fd = 0; fd < FD_SETSIZE; fd++)
         if ((FD_ISSET(fd, &readfds)) && (FD_ISSET(fd, &unixm)))

#     else
      if (FD_ISSET(funix, &readfds))
#     endif

      {
         memset(readbuf, 0, sizeof(readbuf));

#        ifdef BSD
         memset(cmsg, 0, sizeof(cmsg));
         memset(&msghdr, 0, sizeof(msghdr));

         msghdr.msg_iov = &iov;
         msghdr.msg_iovlen = 1;
         msghdr.msg_flags = 0;
         msghdr.msg_control = (caddr_t)cmsg;
         msghdr.msg_controllen = sizeof(cmsg);

         /* FIX - do something with credentials.. */

         res = recvmsg(funix, &msghdr, 0);
         if (res == ERROR)
         {
            error("error with recvmsg from %s: %s\n\n", SYSLOGFILE,
                  strerror(errno));

            quit(ERROR);
         }

#        elif SUN
         dat.buf = readbuf;
         dat.maxlen = sizeof(readbuf) - 1;

         ctl.buf = (caddr_t)&hdr;
         ctl.maxlen = sizeof(struct log_ctl);

         while(1)
         {
            errno = 0;

            res = getmsg(funix, &ctl, &dat, &flags);
            if (res != MOREDATA)
               if ((res != ERROR) || ((res == ERROR) && (errno != EINTR)))
                  break;

            bufptr = &dat.buf[dat.len], *bufptr = '\0';
            while ((*bufptr != '\n') && (bufptr != readbuf)) bufptr -= 1;

            /* FIX - on all these STREAM's we gotta send pri prolly */

            if (bufptr != readbuf) *bufptr = '\0';

            if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                (isprint((int)readbuf[1]) != 0))
            {
               send_data("STREAM : %s%c", readbuf,
                         (readbuf[strlen(readbuf)-1] != '\n' ? '\n' : '\0'));

               doLogging(readbuf);
            }

            if (readbuf != bufptr)
            {
               strcpy(readbuf, bufptr);
               dat.maxlen = sizeof(readbuf) - strlen(readbuf) - 1;
               dat.buf = &readbuf[strlen(readbuf)];
            }

            else
            {
               dat.maxlen = sizeof(readbuf) - 1;
               dat.buf = readbuf;
            }
         }

         if ((res == 0) && (dat.len > 0))
         {
            dat.buf[dat.len] = '\0';

            if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
                (isprint((int)readbuf[1]) != 0))
            {
               send_data("STREAM : %s%c", readbuf,
                         (readbuf[strlen(readbuf)-1] != '\n' ? '\n' : '\0'));

               doLogging(readbuf);
            }
         }

         else if ((res == ERROR) && (errno != EINTR))
         {
            error("error in getmsg: %s\n\n", strerror(errno));

            (void)close(funix);
            funix = ERROR;

            quit(ERROR);
         }

#        else
         while(1)
         {
            errno = 0;

            memset(readbuf, 0, sizeof(readbuf));
            res = read(fd, readbuf, sizeof(readbuf)-1);

            if ((res <= 0) && (errno > 0))
            {
               if (errno == EINTR) continue;

               error("error with read(): %s\n", strerror(errno));
               (void)close(fd), quit(ERROR);
            }

            else break;
         }

         FD_CLR(fd, &readfds);
         FD_CLR(fd, &unixm);
#        endif

#        ifndef SUN
         if (((readbuf[0] != '\0') && (readbuf[0] != '\n')) &&
             (isprint((int)readbuf[1]) != 0))
         {
            memset(newmsg, 0, sizeof(newmsg));
            memset(logmsg, 0, sizeof(logmsg));

            write_spool("STREAM : %s%c", readbuf, 
                        (readbuf[strlen(readbuf)-1] != '\n' ? '\n' : '\0'));

            doLogging(readbuf);

            logptr = logmsg;
            dataptr = readbuf, dataptr += 20;

            count = 0;
            while ((*dataptr) && (isprint((int)*dataptr) != 0) &&
                   (count < (int)sizeof(logmsg)))
            {
               *logptr++ = *dataptr++;
               count++;
            }

            if (isprint((int)prevlogmsg[0]) == 0)
            {
               (void)strncpy(prevlogmsg, logmsg, sizeof(prevlogmsg)-1);
               prevlogtime = time(NULL);

               doLogging(readbuf);
            }

            else if (strncmp(logmsg, prevlogmsg, strlen(prevlogmsg)) == 0)
            {
               if ((time(NULL) - prevlogtime) >= REPTIME)
               {
                  char repmsg[MAXLOGSIZE - 20];

                  memset(repmsg, 0, sizeof(repmsg));

                  if (repcount == 1)
                     sprintf(repmsg, " last message repeated %d time\n",
                             repcount);

                  else
                     sprintf(repmsg, " last message repeated %d times\n",
                             repcount);

                  repcount = 0;
                  prevlogtime = time(NULL);

                  count = 0;
                  newptr = newmsg, dataptr = readbuf;

                  while ((*dataptr) && (isprint((int)*dataptr) != 0) &&
                         (count < (int)sizeof(newmsg)) && (count < 19))
                  {
                     *newptr++ = *dataptr++;
                     count++;
                  }

                  strncat(newmsg, repmsg,
                          sizeof(newmsg) - strlen(newmsg) - 1);

                  debug("newmsg = %s\n", newmsg);
                  doLogging(newmsg);
               }

               else
               {
                  repcount++;
                  debug("last message repeated...\n\n");
               }
            }

            else
            {
               prevlogtime = time(NULL);
               (void)strncpy(prevlogmsg, logmsg, sizeof(prevlogmsg)-1);
               doLogging(readbuf);
            }
         }
#        endif
      }     

#     if !defined(SUN) && !defined(BSD)
      if (FD_ISSET(funix, &readfds))
      {
         /* accept a new connection */
         /* debug("got new unix connect\n"); */

         fd = accept(funix, (struct sockaddr *)NULL, 0);
         if (fd >= 0)
         {
            FD_SET(fd, &unixm);
            FD_SET(fd, &readfds);
         }

         else error("error with accept(): %s\n", strerror(errno));
      }
#     endif

   }
}


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


/* write data to spool file */
void write_spool(char *fmt, ...)
{
   int res;

   va_list args;
   char writebuf[MAXWRITESIZE];
   
   memset(writebuf, 0, sizeof(writebuf));
   
   va_start(args, fmt);
   (void)vsnprintf(writebuf, sizeof(writebuf)-1, fmt, args);
   va_end(args);
   
   writebuf[sizeof(writebuf)-1] = '\0'; /* NULL terminate the string */
   
   debug("(in write_spool) logging %s%c", writebuf,
         (strstr(writebuf, "\n\n") == NULL ? '\n' : '\0'));
   
   while(1)
   {
      res = fputs(writebuf, spoolfd);

      if (strchr(writebuf, '\n') != NULL)
         if (logs[curlogfd].nsync != 1) (void)fflush(spoolfd);

      if (res == ERROR)
      {
         if (errno == EINTR) continue;

         error("(in spool child) error writing to %s/spool/%s: %s\n\n", 
               SRSdir, LOGFILE, strerror(errno));

         quit(ERROR);
      }

      else break;
   }

   if (strchr(writebuf, '\n') == NULL) 
      if (logs[curlogfd].nsync != 1)
         (void)fflush(spoolfd);

   while(1)
   {
      res = fprintf(spoolfd, "%s", writebuf);

      if (strchr(writebuf, '\n') == NULL) 
         if (logs[curlogfd].nsync != 1)
            (void)fflush(spoolfd);

      if (res == ERROR)
      {
         if (errno == EINTR) continue;

         error("(in spool child) error writing to %s/spool/%s: %s\n\n", 
               SRSdir, LOGFILE, strerror(errno));

         quit(ERROR);
      }

      else break;
   }

   if (strchr(writebuf, '\n') == NULL) 
      if (logs[curlogfd].nsync != 1)
         (void)fflush(spoolfd);
}


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


/* kill the spooling process */
void killSpooler()
{
   int res;

   if (spooling != 1) return;

   debug("(in spool parent) now killing spooling process\n");

# if !defined(SUN) && !defined(BSD)
#  ifdef _POSIX_SAVED_IDS
   res = setuid(0);
#  else
   res = seteuid(0);
#  endif

   if (res == ERROR)
   {
      error("error setting [e]uid: %s\n\n", strerror(errno));
      quit(ERROR);
   }
# endif

   res = kill(spoolpid, SIGTERM);
   if (res == ERROR)
   {
      error("(in spool parent)\n"
            "error killing spooling process (pid %d): %s\n",
            spoolpid, strerror(errno));
   }

# if !defined(SUN) && !defined(BSD)
   if (pwd != NULL)
   {
#     ifdef _POSIX_SAVED_IDS
      res = setuid(pwd->pw_uid);
#     else
      res = seteuid(pwd->pw_uid);
#     endif

      if (res == ERROR)
      {
         error("error setting [e]uid: %s\n\n", strerror(errno));
         quit(ERROR);
      }
   }
# endif

   spoolpid = ERROR;
   spooling = 0, didspool = 1;
   debug("(in spool parent) spooling now is being turned off\n");
}


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


/* fork a child to spool */
void forkSpool()
{
   int res;

   spooling = 1;

   res = fork();
   if (res == ERROR)
   {
      error("error forking child to spool locally: %s\n\n",
            strerror(errno));

      quit(ERROR);
   }

   else if (res == 0)
   {
      (void)signal(SIGCHLD, SIG_IGN);

      spoolpid = getpid();

      /* initSpool() already done by initStream() */
      /* initSpool(), */ doSpooling();
   }

   else
   {
      spoolpid = res;

      if (gotInfoServ == 1)
      {
         debug("(in parent)\nnow restarting/reconnecting "
               "at the top of the server list\n\n");

         curServ = 0;

         /* wait MAXTIMEOUT * 3 before giving up and getting new list */
         signal(SIGALRM, restart);
         alarm(MAXTIMEOUT * 3); 
      }

      else
      {
         debug("(in parent) now attempting to reconnect to info servers\n"); 

         if (done == 1) return;
         else longjmp(infoconn, 1);
      }
   }
}
