DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T p

⟦411d50e31⟧ TextFile

    Length: 17745 (0x4551)
    Types: TextFile
    Names: »process.c«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦f1ce22008⟧ »EurOpenD3/news/newsxd2.5.0.tar.Z« 
        └─⟦caa165e81⟧ 
            └─⟦this⟧ »process.c« 

TextFile

/*
 * #include <legal/bs.h>
 >
 > Copyright (c) 1989 Washington University in Saint Louis, Missouri and
 > Chris Myers. All rights reserved.
 >
 > Permission is hereby granted to copy, reproduce, redistribute or
 > otherwise use this software as long as: (1) there is no monetary
 > profit gained specifically from the use or reproduction of this
 > software, (2) it is not sold, rented, traded, or otherwise marketed,
 > (3) the above copyright notice and this paragraph is included
 > prominently in any copy made, and (4) that the name of the University
 > is not used to endorse or promote products derived from this software
 > without the specific prior written permission of the University.
 > THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 > IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 > WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 >
 */

#include "defs.h"
extern int errno;

/*************************************************************************/
/* FUNCTION  : kill_children                                             */
/* PURPOSE   : Kills all outstanding transmitters.  Kill_children will   */
/*             wait ten seconds to allow all of the transmitters to exit */
/*             gracefully.  If invoked by a signal (SIGTERM?), exit else */
/*             return to caller.                                         */
/* ARGUMENTS : Signal number or 0 if to return to caller                 */
/*************************************************************************/

void
kill_children(sig)
   int  sig;            /* sig will be nonzero if called by SIGTERM */

{
struct  host    *hostptr;

   if ((hostlist == NULL) & (sig == 0)) return;

   log(LOG_INFO, "newsxd: shutting down all transmitters\n");

   foreach (hostptr, hostlist) {
      if (hostptr->pid != 0) {
         dprintf("newsxd: killing transmitter for %s\n", hostptr->hostname);
         (void) kill (hostptr->pid, SIGTERM);
      }
   }

   dprintf("(kill_children): sleeping 10 seconds\n");

   (void) sleep(15);            /* allow transmitters to quit gracefully */

   if (sig == 0) return;

   log(LOG_INFO, "shut down by signal %d\n", sig);

   (void) exit(0);              /* invoked by SIGTERM, time to quit */

}

/*************************************************************************/
/* FUNCTION  : xmit_done                                                 */
/* PURPOSE   : Catch the SIGCHLD from completed transmitters and do the  */
/*             necessary cleanup.  Also wait synchronously for a xmitter */
/*             to complete; also perform forced cleanup for an xmitter   */
/* ARGUMENTS : sig, > 0 if xmit_done is called by SIGCHLD                */
/*                 == 0 if called to check for a zombie w/NOHANG         */
/*                 ==-1 if called to sync wait for a transmitter         */
/*                 < -1 to perform a forced cleanup on a transmitter     */
/*************************************************************************/

void
xmit_done(sig)
   int  sig;                 /* sig will be nonzero if called by SIGCHLD */

{
struct  host    *hostptr;
struct  class   *classptr;
union   wait    status;
struct  rusage  usage;

int     options,
	loop,
        pid;

   (void) sigsetmask(0);

   /*
    * Try and catch any completed transmitters first and process them.
    * Then, if xmit_done() was called to synchronously wait on a transmitter
    * or to perform forced cleanup on a transmitter, do it.
    */

   while (1) {
      Dprintf("(xmit_done) sig %d\n", sig);

      if (sig != -1) options = WNOHANG;

      pid = wait3(&status, options, &usage);

      if (pid <= 0) 
         if (sig < -1) {
            pid = -sig;
            sig = 0;
         } else
            return;
   
      Dprintf("(xmit_done) pid %d\n", pid);

      for (loop = 0; loop < MAXXMITTERS; loop++) {
         if (pidlist[loop] == pid) {
            hostptr = pidmap[loop];
            pidlist[loop] = 0;
            pidmap[loop] = (struct host *) NULL;
            foreach (classptr, classlist) {
               if (strcmp(hostptr->class, classptr->classname) == 0) {
                  classptr->curxmits--;
                  hostptr->pid = 0;
                  freeclassslot(classptr, hostptr->classslot);
                  hostptr->whynot = WN_NOTMYTURN;
                  dprintf("%s: transmission completed\n",hostptr->hostname);
                  return;
               }
            }

            /*
             * The (sig < -1) check is here just in case a transmitter exitted
             * normally just as run_queue tries to force a cleanup on it. If
             * this isn't here, newsxd will abort with a corrupted data err.
             */

            if (sig < -1) return;

            /* Something is seriously wrong here -- quit somewhat gracefully */

            logerr("newsxd: corrupted host/class structure!\n");
            kill_children(1);
         }
      }
   }
}

/*************************************************************************/
/* FUNCTION  : run_queue                                                 */
/* PURPOSE   : Go through the list of hosts to transmit to and start any */
/*             transmitter that meets all of the necessary conditions    */
/* ARGUMENTS : none                                                      */
/*************************************************************************/

void
run_queue()

{
struct  host    *hostptr;
struct  class   *classptr, *lastclass;
struct  stat    statbuf;
struct  tm      *curtime;

int     clock,
        pid,
        hadtheirchance,
	which,
	which2,
        loop,
        loop2;

char    fnbuf[MAXPATHLEN],
        fnbuf2[MAXPATHLEN];

   CONFIGCHANGED = 0;
   hadtheirchance = 0;
   lastclass = (struct class *) NULL;

   foreach (hostptr, hostlist) {

      /*
       * If the configuration has been changed (possibly via SIGHUP), we
       * need to assume that all of the current pointers and such are
       * invalid since the current host MAY have been deleted...
       *
       * There is still a race condition, but this check lessens the chance
       * of problems considerably.
       *
       */

      if (CONFIGCHANGED) {
         dprintf("Reconfigured during queue run -- aborting queue run\n");
         CONFIGCHANGED = 0;
         return;
      }

      (void) time(&clock);
      curtime = localtime(&clock);

      classptr = getclass(hostptr->class);

      if (classptr != lastclass) {
         if (lastclass) {
            dprintf("class %s: members %d, hadtheirchance %d\n",
               lastclass->classname, lastclass->members, hadtheirchance);
            if (lastclass->members == hadtheirchance) {
               lastclass->xmitsernum++;
               dprintf("class %s: add 1 to xmitsernum, now %d\n",
                  lastclass->classname, lastclass->xmitsernum);
            }
         }
         hadtheirchance = 0;
         lastclass = classptr;
      }

      /*
       * Check and see if we somehow missed the SIGCHLD for a transmitter and
       * it's really gone and we don't know it.  If so, force a cleanup.
       */

      if ((hostptr->pid) && (kill(hostptr->pid, 0) == -1) && (errno == ESRCH)) {
         xmit_done(-hostptr->pid);
      }

      /*
       * Check to see if the host has had a transmitter running for more
       * than the ttl of its transmission class.  If so, kill it.
       */

      which = (hostptr->options.ttl) ?
         hostptr->options.ttl : classptr->options.ttl;
      which2 = (hostptr->options.ttlpenalty) ?
         hostptr->options.ttlpenalty : classptr->options.ttlpenalty;

      dprintf("%s: checking ttl (%d/%d)\n", hostptr->hostname, which, which2);

      if ((hostptr->pid > 0) && (clock > (hostptr->lasttime + which))) {
         dprintf("%s: exceeded ttl, killing\n");
         hostptr->penaltytime = clock + which2;
         if (kill(hostptr->pid, SIGTERM) != 0) xmit_done(-hostptr->pid);
         hostptr->whynot = WN_TTL;
         continue;  /* skip this host to give others a chance */
      }

      /*
       * If there is already a running transmitter for this host, skip it. We
       * don't want more than one!
       */

      dprintf("%s: checking for active daemon (%d)\n", hostptr->hostname,
         hostptr->pid);

      if (hostptr->pid > 0) {
         hostptr->whynot = WN_RUNNING;
         hadtheirchance++;
         continue;
      }

      /*
       * Check to see if this host has already had a chance to start
       * a transmitter.  If so, let someone else have a chance.
       */

      dprintf("%s: xmitsernum is %d, class xmitsernum is %d\n",
         hostptr->hostname, hostptr->xmitsernum, classptr->xmitsernum);

/*
      if ((hostptr->xmitsernum == classptr->xmitsernum) &&
          (classptr->maxxmits < classptr->members)) {
 */

      if (hostptr->xmitsernum == classptr->xmitsernum) {
         hadtheirchance++;
/*       hostptr->whynot = WN_NOTMYTURN; */
         continue;
      }

      /*
       * Check the current time against the last time an xmit was started
       * for this class to make sure we don't start too many daemons at
       * one time.
       */

      which = (hostptr->options.startint) ?
         hostptr->options.startint : classptr->options.startint;

      dprintf("%s: checking class startup interval (clock is %d, start %d)\n",
         hostptr->hostname, clock, (classptr->laststart + which));

      if (clock < (classptr->laststart + which)) {
         hostptr->whynot = WN_CLASSSTARTINT;
         continue;
      }

      /*
       * Check the current load and compare against the maximum allowed
       * load for starting new transmitters for this hosts's class.
       */

      which = (hostptr->options.maxload) ?
         hostptr->options.maxload : classptr->options.maxload;

      dprintf("%s: checking maximum load (cur %d, max %d)\n",
         hostptr->hostname, getla(), which);

      if (getla() > which) {
         hostptr->whynot = WN_LOAD;
         continue;
      }

      /*
       * Check the number of currently running transmitters for this host's
       * transmission class.  If we're already running at the limit, skip
       * this host.
       */

      dprintf("%s (%s): checking running xmit count (%d of %d)\n",
         hostptr->hostname, classptr->classname, classptr->curxmits,
         classptr->maxxmits);

      if (classptr->curxmits >= classptr->maxxmits) {
         hostptr->whynot = WN_MAXXMITS;
         continue;
      }

      /*
       * All tests after this point should be tests for some characteristic
       * of the host that would cause it to avoid its turn to start its
       * transmitter.  Things like: the host startup interval hasn't passed
       * yet, or it's a bad time to send to that host, or there's no work
       * to send to that host.
       *
       * Tests before this point should be tests for some characteristic not
       * related to the particular host that prevents it from being able to
       * start its transmitter.  Things like: load too high, too many xmits
       * already running...
       */

      /*
       * This host had a chance to send news (and can possibly skip it).
       */

      hostptr->xmitsernum = classptr->xmitsernum;
      hadtheirchance++;

      /*
       * Check to see if the host had a transmitter killed because it ran
       * longer than its class' ttl.  If so, see if its penalty time is
       * still unexpired.
       */

      dprintf("%s: checking ttl penalty\n", hostptr->hostname);

      if (hostptr->penaltytime > clock) {
         hostptr->whynot = WN_PENALTYTIME;
         continue;
      }

      /*
       * Check the current time against the last time an xmit was started
       * for this class to make sure we don't start a transmitter for this
       * host too often...
       */

      which = (hostptr->options.interval) ?
         hostptr->options.interval : classptr->options.interval;

      dprintf("%s: checking host startup interval (%d)\n", hostptr->hostname,
         which);

      if (clock < (hostptr->lasttime + which)) {
         hostptr->whynot = WN_HOSTSTARTINT;
         continue;
      }

      /*
       * Check to see that the current time is within the permitted range
       * of transmission times.  If not, skip this host.
       */

      dprintf("%s: checking valid transmission times (%s)\n",
         hostptr->hostname, hostptr->times);

      if (!validtime(hostptr->times)) {
         hostptr->whynot = WN_BADTIME;
         continue;
      }

      /*
       * See if there is an outstanding work file for this host, if so we
       * just need to run a transmitter, otherwise rename the batch file
       * to the work file and run the transmitter.
       */

      (void) sprintf(fnbuf, workfile, hostptr->hostname);

      dprintf("%s: checking for workfile (%s)\n",
            hostptr->hostname, fnbuf);

      if (classptr->flags[C_NOWORK] || (stat(fnbuf, &statbuf) != 0)) {
         (void) sprintf(fnbuf2, batchfile, hostptr->hostname);

         dprintf("%s: checking for batchfile (%s)\n",
               hostptr->hostname, fnbuf2);

         if (!classptr->flags[C_NOBATCH]) {
            if (stat(fnbuf2, &statbuf) != 0) {
               hostptr->whynot = WN_NOWORK;
               continue;
            } else {
               hostptr->whynot = WN_RENAMEFAILED;
               if (!classptr->flags[C_NOWORK] && rename(fnbuf2, fnbuf) != 0) {
                  dprintf("%s: rename failed (%s to %s)\n",
                        hostptr->hostname, fnbuf2, fnbuf);
                  continue;
               }
            }
         }
      }
      
      /*
       * Fork off a transmitter for this host
       */

      hostptr->whynot = WN_RUNNING;

      if ((hostptr->classslot = getclassslot(classptr)) == -1) {
         hostptr->whynot = WN_NOSLOT;
         exit(1);
      }

      if ((pid = fork()) == 0) {
	 /* Child. */
         Dprintf("(%s): classslot is %d\n", hostptr->hostname,
            hostptr->classslot);

         dprintf("%s: spawning transmitter\n", hostptr->hostname);

         if (!newsxdebug) {
            (void) sprintf(fnbuf2, xmitlogs, hostptr->hostname);
            (void) freopen(fnbuf2, "a", stdout);
            (void) freopen(fnbuf2, "a", stderr);
         }

         (void) fprintf(stderr, "%s: begin at %02d:%02d:%02d\n",
            hostptr->hostname, curtime->tm_hour, curtime->tm_min,
            curtime->tm_sec);
         (void) fflush(stderr);

         which = (hostptr->options.deltanice) ?
            hostptr->options.deltanice : classptr->options.deltanice;

         if (which != 0) {
            Dprintf("Changing transmitter nice by %d for %s\n",
               which, hostptr->hostname);

            (void) nice(which);
         }

         if (classptr->xargc == 0) {
            classptr = getclass("DEFAULT");
            if (classptr->xargc == 0) {
               logerr("host %s: No DEFAULT xmitter defined -- aborting\n",
                  hostptr->hostname);
               exit(1);
            }
         }

         Dprintf("classptr->xargc = %d\n", classptr->xargc);

         Dprintf("%s: EXECing %s with parameters:\n",
            hostptr->hostname, classptr->xpath);

         classptr->xargv[classptr->xargc] = NULL;

         for (loop = 0; loop < classptr->xargc; loop++)
            if (strcmp(classptr->xargv[loop], "%f") == NULL) {
               if (hostptr->xargc == 0) {
                  classptr->xargc--;
                  for (loop2 = loop; loop2 < MAXEXECARGS-1; loop2++)
                     classptr->xargv[loop2] = classptr->xargv[loop2+1];
               } else {
                  classptr->xargc += hostptr->xargc - 1;
                  for (loop2=MAXEXECARGS-hostptr->xargc; loop2 > loop; loop2--)
                     classptr->xargv[loop2+hostptr->xargc-1] = classptr->xargv[loop2];
                  for (loop2 = loop; loop2 < loop + hostptr->xargc; loop2++)
                     classptr->xargv[loop2] = hostptr->xargv[loop2 - loop];
                  loop += hostptr->xargc - 1;
               }
            }

         Dprintf("(after host flag insertion) classptr->xargc = %d\n",
            classptr->xargc);

         for (loop = 0; loop <= classptr->xargc; loop++) {
            /* WARNING: we are mangling the xargv array here! */
            processarg(loop, hostptr, classptr);

            if (classptr->xargv[loop] == NULL) {
               Dprintf("arg[%d] = NULL\n", loop);
            } else {
               Dprintf("arg[%d] = %s\n", loop, classptr->xargv[loop]);
            }
         }

         (void) execvp(classptr->xpath, classptr->xargv);

         logerr("%s: can't exec %s\n", hostptr->hostname, classptr->xpath);

         (void) exit(1); /* could not exec the news transmitter! */
      } else {
	 /* Parent. */
	 if (pid == -1) {
	    logerr("host %s: Can't fork child.\n", hostptr->hostname);
	    continue;
	 }
         for (loop = 0; loop < MAXXMITTERS; loop++) {
            if (pidlist[loop] == 0) {
               pidlist[loop] = pid;
               pidmap[loop] = hostptr;
               Dprintf("%s: entered into pidmap/list at %d\n",
                  hostptr->hostname, loop);
               break;
            }
         }
         hostptr->pid = pid;
         hostptr->lasttime = clock;
         classptr->laststart = clock;
         classptr->curxmits++;
         if (newsxdebug) xmit_done(-1); /* wait for xmitter to complete */
      }
   }

   if (classptr->members == hadtheirchance) {
      dprintf("class %s: add 1 to sernum (members %d, hadchance %d)\n",
         classptr->classname, classptr->members, hadtheirchance);
      classptr->xmitsernum++;
   }
}