rsalz@bbn.com (Rich Salz) (08/31/90)
Submitted-by: Chris Myers <chris@wugate.wustl.edu> Posting-number: Volume 23, Issue 12 Archive-name: newsxd/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: defs.h patchlevel.h process.c simple.conf util.c version.c # Wrapped by rsalz@litchi.bbn.com on Fri Jul 13 15:03:57 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 2 (of 3)."' if test -f 'defs.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'defs.h'\" else echo shar: Extracting \"'defs.h'\" \(6742 characters\) sed "s/^X//" >'defs.h' <<'END_OF_FILE' X/* Define everything that all of the various pieces of code will be using */ X X#include <stdio.h> X#include <signal.h> X#include <strings.h> X#include <syslog.h> X#include <nlist.h> X#include <errno.h> X#include <ctype.h> X#include <sys/param.h> X#include <sys/errno.h> X#include <sys/file.h> X#include <sys/ioctl.h> X#include <sys/wait.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <varargs.h> X X#include "newsxd.h" X#include "patchlevel.h" X X/* If it's good enough for news 2.11, it's good enough for me ... */ X X#ifdef BSD4_2 X#include <sys/time.h> X#else X#include <time.h> X#endif X X#include <sys/resource.h> X X#if defined(mips) & defined(ultrix) X#include <sys/../h/fixpoint.h> X#endif X Xchar *calloc(); X X#define foreach(ctl, list) for (ctl = list; ctl != NULL; ctl = ctl->next) X X#ifdef CNEWSLOCKING Xextern char *progname; X#endif X Xint debug, /* is debugging enabled */ X DEBUG, /* painfully verbose debugging is enabled */ X newsxdebug, /* run xmitter synchronously with debugging? */ X tallying, /* is use of a tally file enabled? */ X locking, /* is use of a locking file enabled? */ X CONFIGCHANGED, /* a new configuration was read in */ X#ifdef FAKESYSLOG X CONFIGCHANGEDFILE, /* a new configuration was read in */ X#endif X queueinterval, /* number of seconds between xmit checks */ X daemon_idle, /* prevent newsxd from running queue if set */ X pidlist[MAXXMITTERS]; /* map pid --> xmitter quicker */ X Xstruct host *pidmap[MAXXMITTERS]; X Xchar batchfile[MAXPATHLEN], /* file inews places article IDs/paths in */ X workfile[MAXPATHLEN], /* file to use for transmitter work file */ X xmitlogs[MAXPATHLEN], /* where to log the output of transmitters */ X tallyfile[MAXPATHLEN], /* where the tally file can be found */ X statusfile[MAXPATHLEN], /* where newsxd's status should be written */ X pidfile[MAXPATHLEN], /* where newsxd's pid should be written */ X#ifdef FAKESYSLOG X fakelogfile[MAXPATHLEN],/* where is the newsxd log file? */ X#endif X configfile[MAXPATHLEN]; /* where is the newsxd configuration file? */ X X/* STRUCT OPTIONS X * Used to define the options controlling the starting of transmitters for X * classes and hosts. X * X * interval : minimum start-to-start interval for each host's transmitter X * startint : minimum interval between starting transmitters in this class X * ttl : maximum time-to-live (secs) for a transmitter in this class X * ttlpenalty : penalty time (secs) for a transmitter exceeding the ttl X * deltanice : amount to change nice before execing the transmitter X * maxload : maximum load where new xmitters can be started for this class X * X */ X Xtypedef struct options { X X int deltanice; X int interval; X int startint; X int ttl; X int ttlpenalty; X int maxload; X X}; X X/* X * STRUCT CLASS X * Contains the current state and description of each class of transmitters X * that newsxd handles. X * X * classname : textual name of this transmission class X * maxxmits : maximum number of simultaneous transmitters for this class X * curxmits : current number of active transmitters for this class X * laststart : last time a transmitter was started for this class X * xmitsernum : transmission serial number used for fair news transmission X * options : defines default transmitter startup parameters for this class X * members : number of hosts that are a member of this class X * flags : special option flags for this class X * 0 : don't rename <batchfile> to <workfile> X * 1 : don't look for <batchfile> or <workfile> X * xpath : file path for an alternate transmission program X * xargv : arguments to be passed to the alternate transmitter X * xargc : number of arguments to be passed to the alternate xmitter X * valid : used to detect which classes are valid after a config update X * next : pointer to the next class descriptor in the list X * X */ X Xstruct class { X X char classname[MAXCLASSNAMELEN]; X int maxxmits; X int curxmits; X int laststart; X int xmitsernum; X int members; X struct options options; X char slots[MAXCLASSXMITTERS]; X int flags[MAXCLASSFLAGS]; X char batchfile[MAXPATHLEN]; X char workfile[MAXPATHLEN]; X char xpath[MAXPATHLEN]; X char *xargv[MAXEXECARGS]; X int xargc; X char *debugargv[MAXEXECARGS]; X int debugargc; X int valid; X struct class *next; X X}; X X/* X * STRUCT HOST X * Contains the current state and description of each host that newsxd X * will be communicating with. X * X * hostname : name of the host to pass to the transmitter X * class : name of the transmission class this host is in X * times : list of valid times to transmit in UUCP L.sys format X * pid : pid of forked transmitter X * lasttime : last time transmitter was forked off X * xmitsernum : transmission serial number used for fair news transmission X * penaltytime : host xmitted to past ttl; secs before penalty is over X * whynot : why is there no transmitter running for this host NOW? X * valid : used to detect which hosts are valid after a config update X * options : defines default transmitter startup parameters for this host X * next : pointer to next host in the list of all hosts X * xargv : arguments to be passed to the transmitter X * xargc : number of arguments to be passed to the transmitter X * X */ X Xstruct host { X X char hostname[MAXHOSTNAMELEN]; X char class[MAXCLASSNAMELEN]; X char times[MAXTIMENAMELEN]; X int pid; X int lasttime; X int penaltytime; X int xmitsernum; X int whynot; X int valid; X int classslot; X struct options options; X char *xargv[MAXEXECARGS]; X int xargc; X struct host *next; X X}; X Xstruct class *classlist; Xstruct host *hostlist; X X/* Predefine the return types of all of the functions */ X Xvoid addclass(); Xvoid addhost(); Xvoid clear_invalid(); Xvoid daemon_start(); Xvoid debug_off(); Xvoid debug_on(); Xvoid dprintf(); Xvoid Dprintf(); Xvoid dump_config(); Xvoid dump_info(); Xvoid idle(); Xvoid reset(); Xstruct class *getclass(); Xvoid freeclassslot(); Xint getclassslot(); Xint getla(); Xvoid kill_children(); Xvoid log(); Xvoid logerr(); Xvoid make_invalid(); Xint parsetime(); Xvoid processarg(); Xvoid read_config(); Xvoid run_queue(); Xint validtime(); Xvoid xmit_done(); END_OF_FILE if test 6742 -ne `wc -c <'defs.h'`; then echo shar: \"'defs.h'\" unpacked with wrong size! fi # end of 'defs.h' fi if test -f 'patchlevel.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'patchlevel.h'\" else echo shar: Extracting \"'patchlevel.h'\" \(41 characters\) sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE' X#define VERSION 2.5 X#define PATCHLEVEL 1 END_OF_FILE if test 41 -ne `wc -c <'patchlevel.h'`; then echo shar: \"'patchlevel.h'\" unpacked with wrong size! fi # end of 'patchlevel.h' fi if test -f 'process.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'process.c'\" else echo shar: Extracting \"'process.c'\" \(20001 characters\) sed "s/^X//" >'process.c' <<'END_OF_FILE' X/* X * #include <legal/bs.h> X > X > Copyright (c) 1989 Washington University in Saint Louis, Missouri and X > Chris Myers. All rights reserved. X > X > Permission is hereby granted to copy, reproduce, redistribute or X > otherwise use this software as long as: (1) there is no monetary X > profit gained specifically from the use or reproduction of this X > software, (2) it is not sold, rented, traded, or otherwise marketed, X > (3) the above copyright notice and this paragraph is included X > prominently in any copy made, and (4) that the name of the University X > is not used to endorse or promote products derived from this software X > without the specific prior written permission of the University. X > THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X > IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X > WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X > X */ X X#include "defs.h" Xextern int errno; X X/*************************************************************************/ X/* FUNCTION : kill_children */ X/* PURPOSE : Kills all outstanding transmitters. Kill_children will */ X/* wait thirty seconds to allow all of the transmitters to */ X/* exit gracefully. If invoked by a signal (SIGTERM?), exit */ X/* else return to caller. */ X/* ARGUMENTS : Signal number or 0 if to return to caller */ X/*************************************************************************/ X Xvoid Xkill_children(sig) X int sig; /* sig will be nonzero if called by SIGTERM */ X X{ Xstruct host *hostptr; X X if ((hostlist == NULL) && (sig == 0)) return; X X log(LOG_INFO, "newsxd: shutting down all transmitters\n"); X X foreach (hostptr, hostlist) { X if (hostptr->pid != 0) { X dprintf("newsxd: killing transmitter for %s\n", hostptr->hostname); X (void) kill (hostptr->pid, SIGTERM); X } X } X X if (sig == 0) return; X X dprintf("(kill_children): sleeping 30 seconds\n"); X X (void) sleep(30); /* allow transmitters to quit gracefully */ X X log(LOG_INFO, "shut down by signal %d\n", sig); X X if (debug == 0 && DEBUG == 0) { X (void) unlink(pidfile); X (void) unlink(statusfile); X } X X (void) exit(0); /* invoked by SIGTERM, time to quit */ X X} X X/*************************************************************************/ X/* FUNCTION : xmit_done */ X/* PURPOSE : Catch the SIGCHLD from completed transmitters and do the */ X/* necessary cleanup. Also wait synchronously for a xmitter */ X/* to complete; also perform forced cleanup for an xmitter */ X/* ARGUMENTS : sig, > 0 if xmit_done is called by SIGCHLD */ X/* == 0 if called to check for a zombie w/NOHANG */ X/* ==-1 if called to sync wait for a transmitter */ X/* < -1 to perform a forced cleanup on a transmitter */ X/*************************************************************************/ X Xvoid Xxmit_done(sig) X int sig; /* sig will be nonzero if called by SIGCHLD */ X X{ Xstruct host *hostptr; Xstruct class *classptr; Xunion wait status; Xstruct rusage usage; Xextern char *sys_errlist[]; X Xint options, X loop, X pid; X X (void) sigsetmask(0); X X /* X * Try and catch any completed transmitters first and process them. X * Then, if xmit_done() was called to synchronously wait on a transmitter X * or to perform forced cleanup on a transmitter, do it. X */ X X Dprintf("(xmit_done) sig %d\n", sig); X X while (1) { X if (sig != -1) options = WNOHANG; X X pid = wait3(&status, options, &usage); X X if (pid <= 0) X if (sig < -1) { X pid = -sig; X sig = 0; X } else { X Dprintf("(xmit_done) no process! pid=%d, errno=%s\n", pid, X sys_errlist[errno]); X return; X } X X Dprintf("(xmit_done) pid %d\n", pid); X X for (loop = 0; loop < MAXXMITTERS; loop++) { X if (pidlist[loop] == pid) { X hostptr = pidmap[loop]; X pidlist[loop] = 0; X pidmap[loop] = (struct host *) NULL; X hostptr->pid = 0; X hostptr->whynot = WN_NOTMYTURN; X foreach (classptr, classlist) { X if (strcmp(hostptr->class, classptr->classname) == 0) { X classptr->curxmits--; X freeclassslot(classptr, hostptr->classslot); X dprintf("%s: transmission completed\n", hostptr->hostname); X return; X } X } X X /* X * The (sig < -1) check is here just in case a transmitter exitted X * normally just as run_queue tries to force a cleanup on it. If X * this isn't here, newsxd will abort with a corrupted data err. X */ X X if (sig < -1) return; X X /* Something is seriously wrong here -- quit somewhat gracefully */ X X logerr("CORRUPTED HOST/CLASS STRUCTURE!\n"); X kill_children(1); X } X } X } X} X X/*************************************************************************/ X/* FUNCTION : run_queue */ X/* PURPOSE : Go through the list of hosts to transmit to and start any */ X/* transmitter that meets all of the necessary conditions */ X/* ARGUMENTS : none */ X/*************************************************************************/ X Xvoid Xrun_queue() X X{ Xstruct host *hostptr; Xstruct class *classptr, *lastclass; Xstruct stat statbuf; Xstruct tm *curtime; X Xint clock, X pid, X hadtheirchance, X which, X which2, X loop, X loop2, X hostargc, X classargc; X Xchar fnbuf[MAXPATHLEN], X fnbuf2[MAXPATHLEN], X **hostargv, X **classargv; X X CONFIGCHANGED = 0; X hadtheirchance = 0; X lastclass = (struct class *) NULL; X X foreach (hostptr, hostlist) { X X /* X * If the configuration has been changed (possibly via SIGHUP), we X * need to assume that all of the current pointers and such are X * invalid since the current host MAY have been deleted... X * X * There is still a race condition, but this check lessens the chance X * of problems considerably. X * X */ X X if (CONFIGCHANGED) { X dprintf("Reconfigured during queue run -- aborting queue run\n"); X CONFIGCHANGED = 0; X return; X } X X (void) time(&clock); X curtime = localtime(&clock); X X classptr = getclass(hostptr->class); X X if (classptr != lastclass) { X if (lastclass) { X dprintf("class %s: members %d, hadtheirchance %d\n", X lastclass->classname, lastclass->members, hadtheirchance); X if (lastclass->members == hadtheirchance) { X lastclass->xmitsernum++; X dprintf("class %s: add 1 to xmitsernum, now %d\n", X lastclass->classname, lastclass->xmitsernum); X } X } X hadtheirchance = 0; X lastclass = classptr; X } X X /* X * Check and see if we somehow missed the SIGCHLD for a transmitter and X * it's really gone and we don't know it. If so, force a cleanup. X */ X X if ((hostptr->pid) && (kill(hostptr->pid, 0) == -1) && (errno == ESRCH)) { X xmit_done(-hostptr->pid); X hostptr->pid = 0; /* Just in case xmit_done() missed it! */ X } X X /* X * Check to see if the host has had a transmitter running for more X * than the ttl of its transmission class. If so, kill it. X */ X X which = (hostptr->options.ttl) ? X hostptr->options.ttl : classptr->options.ttl; X which2 = (hostptr->options.ttlpenalty) ? X hostptr->options.ttlpenalty : classptr->options.ttlpenalty; X X dprintf("%s: checking ttl (%d/%d)\n", hostptr->hostname, which, which2); X X if ((hostptr->pid > 0) && (clock > (hostptr->lasttime + which))) { X dprintf("%s: exceeded ttl, killing\n"); X hostptr->penaltytime = clock + which2; X if (kill(hostptr->pid, SIGTERM) != 0) xmit_done(-hostptr->pid); X hostptr->whynot = WN_TTL; X continue; /* skip this host to give others a chance */ X } X X /* X * If there is already a running transmitter for this host, skip it. We X * don't want more than one! X */ X X dprintf("%s: checking for active daemon (%d)\n", hostptr->hostname, X hostptr->pid); X X if (hostptr->pid > 0) { X hostptr->whynot = WN_RUNNING; X hadtheirchance++; X continue; X } X X /* X * Check to see if this host has already had a chance to start X * a transmitter. If so, let someone else have a chance. X */ X X dprintf("%s: xmitsernum is %d, class xmitsernum is %d\n", X hostptr->hostname, hostptr->xmitsernum, classptr->xmitsernum); X X/* X if ((hostptr->xmitsernum == classptr->xmitsernum) && X (classptr->maxxmits < classptr->members)) { X */ X X if (hostptr->xmitsernum == classptr->xmitsernum) { X hadtheirchance++; X/* hostptr->whynot = WN_NOTMYTURN; */ X continue; X } X X /* X * Check the current time against the last time an xmit was started X * for this class to make sure we don't start too many daemons at X * one time. X */ X X which = (hostptr->options.startint) ? X hostptr->options.startint : classptr->options.startint; X X dprintf("%s: checking class startup interval (clock is %d, start %d)\n", X hostptr->hostname, clock, (classptr->laststart + which)); X X if (clock < (classptr->laststart + which)) { X hostptr->whynot = WN_CLASSSTARTINT; X continue; X } X X /* X * Check the current load and compare against the maximum allowed X * load for starting new transmitters for this hosts's class. X */ X X which = (hostptr->options.maxload) ? X hostptr->options.maxload : classptr->options.maxload; X X dprintf("%s: checking maximum load (cur %d, max %d)\n", X hostptr->hostname, getla(), which); X X if (getla() > which) { X hostptr->whynot = WN_LOAD; X continue; X } X X /* X * Check the number of currently running transmitters for this host's X * transmission class. If we're already running at the limit, skip X * this host. X */ X X dprintf("%s (%s): checking running xmit count (%d of %d)\n", X hostptr->hostname, classptr->classname, classptr->curxmits, X classptr->maxxmits); X X if (classptr->curxmits >= classptr->maxxmits) { X hostptr->whynot = WN_MAXXMITS; X continue; X } X X /* X * All tests after this point should be tests for some characteristic X * of the host that would cause it to avoid its turn to start its X * transmitter. Things like: the host startup interval hasn't passed X * yet, or it's a bad time to send to that host, or there's no work X * to send to that host. X * X * Tests before this point should be tests for some characteristic not X * related to the particular host that prevents it from being able to X * start its transmitter. Things like: load too high, too many xmits X * already running... X */ X X /* X * This host had a chance to send news (and can possibly skip it). X */ X X hostptr->xmitsernum = classptr->xmitsernum; X hadtheirchance++; X X /* X * Check to see if the host had a transmitter killed because it ran X * longer than its class' ttl. If so, see if its penalty time is X * still unexpired. X */ X X dprintf("%s: checking ttl penalty\n", hostptr->hostname); X X if (hostptr->penaltytime > clock) { X hostptr->whynot = WN_PENALTYTIME; X continue; X } X X /* X * Check the current time against the last time an xmit was started X * for this class to make sure we don't start a transmitter for this X * host too often... X */ X X which = (hostptr->options.interval) ? X hostptr->options.interval : classptr->options.interval; X X dprintf("%s: checking host startup interval (%d)\n", hostptr->hostname, X which); X X if (clock < (hostptr->lasttime + which)) { X hostptr->whynot = WN_HOSTSTARTINT; X continue; X } X X /* X * Check to see that the current time is within the permitted range X * of transmission times. If not, skip this host. X */ X X dprintf("%s: checking valid transmission times (%s)\n", X hostptr->hostname, hostptr->times); X X if (!validtime(hostptr->times)) { X hostptr->whynot = WN_BADTIME; X continue; X } X X /* X * See if there is an outstanding work file for this host, if so we X * just need to run a transmitter, otherwise rename the batch file X * to the work file and run the transmitter. X */ X X (void) sprintf(fnbuf, workfile, hostptr->hostname); X X dprintf("%s: checking for workfile (%s)\n", X hostptr->hostname, fnbuf); X X if ((loop = stat(fnbuf, &statbuf)) != 0) { X Dprintf("%s: stat(%s) returned %d (errno is %d)\n", hostptr->hostname, X fnbuf, loop, errno); X X (void) sprintf(fnbuf2, batchfile, hostptr->hostname); X X dprintf("%s: checking for batchfile (%s)\n", X hostptr->hostname, fnbuf2); X X if (!classptr->flags[C_NOBATCH]) { X if ((loop = stat(fnbuf2, &statbuf)) != 0) { X Dprintf("%s: stat(%s) returned %d (errno is %d)\n", X hostptr->hostname, fnbuf, loop, errno); X X hostptr->whynot = WN_NOWORK; X continue; X } else { X hostptr->whynot = WN_RENAMEFAILED; X if (!classptr->flags[C_NOWORK] && rename(fnbuf2, fnbuf) != 0) { X dprintf("%s: rename failed (%s to %s)\n", X hostptr->hostname, fnbuf2, fnbuf); X continue; X } X } X } X } X X /* X * Fork off a transmitter for this host X */ X X hostptr->whynot = WN_RUNNING; X X if ((hostptr->classslot = getclassslot(classptr)) == -1) { X hostptr->whynot = WN_NOSLOT; X exit(1); X } X X if ((pid = fork()) == 0) { X /* Child. */ X int fd; X X /* Untrap all of the signals for newsxd; this is now a transmitter */ X X (void) signal(SIGCHLD, SIG_DFL); X (void) signal(SIGHUP, SIG_DFL); X (void) signal(SIGQUIT, SIG_DFL); X (void) signal(SIGTERM, SIG_DFL); X (void) signal(SIGUSR1, SIG_DFL); X (void) signal(SIGUSR2, SIG_DFL); X (void) signal(SIGIO, SIG_DFL); X (void) signal(SIGIOT, SIG_DFL); X (void) signal(SIGTRAP, SIG_DFL); X X fd = open(classptr->flags[C_NOWORK] ? fnbuf2 : fnbuf, O_RDONLY); X if (fd > 0) (void) flock(fd, LOCK_EX); X X#ifdef CNEWSLOCKING X if (!classptr->flags[C_NOWORK]) { X X /* If we can get a valid lock then we know that since the batch X * file has been renamed to WORKFILE, cnews will now be writing X * to a different file (BATCHFILE). X */ X (void) newslock(); X (void) newsunlock(); X } X#endif CNEWSLOCKING X X Dprintf("(%s): classslot is %d\n", hostptr->hostname, X hostptr->classslot); X X dprintf("%s: spawning transmitter\n", hostptr->hostname); X X if (!newsxdebug) { X (void) sprintf(fnbuf2, xmitlogs, hostptr->hostname); X (void) freopen(fnbuf2, "a", stdout); X (void) freopen(fnbuf2, "a", stderr); X } X X (void) fprintf(stderr, "%s: begin at %02d:%02d:%02d\n", X hostptr->hostname, curtime->tm_hour, curtime->tm_min, X curtime->tm_sec); X (void) fflush(stderr); X X which = (hostptr->options.deltanice) ? X hostptr->options.deltanice : classptr->options.deltanice; X X if (which != 0) { X Dprintf("Changing transmitter nice by %d for %s\n", X which, hostptr->hostname); X X (void) nice(which); X } X X if (classptr->xargc == 0) { X classptr = getclass("DEFAULT"); X if (classptr->xargc == 0) { X logerr("host %s: No DEFAULT xmitter defined -- aborting\n", X hostptr->hostname); X (void) _exit(1); X } X } X X Dprintf("classptr->xargc = %d\n", classptr->xargc); X X Dprintf("%s: EXECing %s with parameters:\n", X hostptr->hostname, classptr->xpath); X X for (loop = 0; loop <= classptr->xargc; loop++) { X if (classptr->xargv[loop] == NULL) { X Dprintf("arg[%d] = NULL\n", loop); X } else { X Dprintf("arg[%d] = %s\n", loop, classptr->xargv[loop]); X } X } X X classargc = classptr->xargc; X classargv = classptr->xargv; X X classargv[classargc] = NULL; X X for (loop = 0; loop < classargc; loop++) { X hostargv = NULL; X if (strcmp(classargv[loop], "%f") == NULL) { X hostargc = hostptr->xargc; X hostargv = hostptr->xargv; X } X X if (strcmp(classargv[loop], "%d") == NULL) { X struct class *myclassptr = getclass(hostptr->class); X hostargc = myclassptr->debugargc; X hostargv = myclassptr->debugargv; X Dprintf("debug argc = %d\n", hostargc); X } X X if (hostargv != NULL) { X if (hostargc == 0) { X classargc--; X for (loop2 = loop; loop2 < MAXEXECARGS-1; loop2++) X classargv[loop2] = classargv[loop2 + 1]; X } else { X classargc += hostargc - 1; X for (loop2 = MAXEXECARGS - hostargc; loop2 > loop; loop2--) X classargv[loop2 + hostargc - 1] = classargv[loop2]; X for (loop2 = loop; loop2 < loop + hostargc; loop2++) X classargv[loop2] = hostargv[loop2 - loop]; X loop += hostargc - 1; X } X } X } X X Dprintf("(after host flag insertion) classargc = %d\n", X classargc); X X for (loop = 0; loop <= classargc; loop++) { X /* WARNING: we are mangling the xargv array here! */ X processarg(loop, hostptr, classptr); X X if (classargv[loop] == NULL) { X Dprintf("arg[%d] = NULL\n", loop); X } else { X Dprintf("arg[%d] = %s\n", loop, classargv[loop]); X } X } X X (void) execvp(classptr->xpath, classargv); X X logerr("%s: can't exec %s\n", hostptr->hostname, classptr->xpath); X X (void) _exit(1); /* could not exec the news transmitter! */ X } else { X /* Parent. */ X if (pid == -1) { X logerr("host %s: Can't fork child.\n", hostptr->hostname); X continue; X } X for (loop = 0; loop < MAXXMITTERS; loop++) { X if (pidlist[loop] == 0) { X pidlist[loop] = pid; X pidmap[loop] = hostptr; X Dprintf("%s: entered into pidmap/list at %d\n", X hostptr->hostname, loop); X break; X } X } X hostptr->pid = pid; X hostptr->lasttime = clock; X classptr->laststart = clock; X classptr->curxmits++; X if (newsxdebug) xmit_done(-1); /* wait for xmitter to complete */ X } X } X X if (classptr->members == hadtheirchance) { X dprintf("class %s: add 1 to sernum (members %d, hadchance %d)\n", X classptr->classname, classptr->members, hadtheirchance); X classptr->xmitsernum++; X } X} END_OF_FILE if test 20001 -ne `wc -c <'process.c'`; then echo shar: \"'process.c'\" unpacked with wrong size! fi # end of 'process.c' fi if test -f 'simple.conf' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'simple.conf'\" else echo shar: Extracting \"'simple.conf'\" \(1073 characters\) sed "s/^X//" >'simple.conf' <<'END_OF_FILE' X# Define a service class that just runs an nntpxmit to each host X# every hour. Don't run nntpxmit if the load goes over 8. X Xclass normal maxxmits=1 interval=3600 maxload=8 X X# Define the default news transmitter Xxmit DEFAULT /usr/local/lib/news/nntpxmit nntpxmit %h:%w X X# Check the list of hosts every 10 minutes to see if we can send news to X# someone yet. Xqueueinterval 600 X X# In all of the following options, %s is replaced by the host name of the X# system being sent to. X X# File news places articles paths/ids in Xbatchfile /usr/spool/batch/%s X X# File a news transmitter wants articles paths/ids in Xworkfile /usr/spool/batch/%s.work X X# Hosts to send news to. Each line is of the format: X# CLASS VALID START X# host HOSTNAME NAME TIMES X Xhost dorothy normal Any Xhost toto normal Any Xhost wizard normal Any Xhost witch normal Any Xhost tinman normal Any Xhost lion normal Any END_OF_FILE if test 1073 -ne `wc -c <'simple.conf'`; then echo shar: \"'simple.conf'\" unpacked with wrong size! fi # end of 'simple.conf' fi if test -f 'util.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'util.c'\" else echo shar: Extracting \"'util.c'\" \(10869 characters\) sed "s/^X//" >'util.c' <<'END_OF_FILE' X/* X * #include <legal/bs.h> X > X > Copyright (c) 1989 Washington University in Saint Louis, Missouri and X > Chris Myers. All rights reserved. X > X > Permission is hereby granted to copy, reproduce, redistribute or X > otherwise use this software as long as: (1) there is no monetary X > profit gained specifically from the use or reproduction of this X > software, (2) it is not sold, rented, traded, or otherwise marketed, X > (3) the above copyright notice and this paragraph is included X > prominently in any copy made, and (4) that the name of the University X > is not used to endorse or promote products derived from this software X > without the specific prior written permission of the University. X > THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X > IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X > WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X > X */ X X#include "defs.h" X X/*************************************************************************/ X/* FUNCTION : daemon_start */ X/* PURPOSE : To disassociate newsxd from the calling process so it can */ X/* run as a daemon. */ X/* ARGUMENTS : none */ X/*************************************************************************/ X Xvoid Xdaemon_start() X X{ Xregister int childpid, fd; Xextern int errno; X X /* Ignore the terminal stop signals */ X (void) signal(SIGTTOU, SIG_IGN); X (void) signal(SIGTTIN, SIG_IGN); X (void) signal(SIGTSTP, SIG_IGN); X X /* Fork and let the parent process exit */ X if ((childpid = fork()) < 0) { X logerr("newsxd: can't fork to enter daemon mode\n"); X (void) exit(1); X } else if (childpid > 0) exit(0); X X /* Lose the process group */ X if (setpgrp(0, getpid()) == -1) { X logerr("newsxd: can't change pgrp to enter daemon mode\n"); X (void) exit(1); X } X X /* Lose the controlling terminal */ X if ((fd = open("/dev/tty", O_RDWR)) >= 0) { X (void) ioctl(fd, TIOCNOTTY, (char *) NULL); X (void) close(fd); X } X X /* Close any open files */ X for (fd = 0; fd < NOFILE; fd++) (void) close(fd); X errno = 0; X X /* Set a proper default umask */ X (void) umask(022); X} X X/*************************************************************************/ X/* FUNCTION : getla */ X/* PURPOSE : Return the current system load */ X/* ARGUMENTS : none */ X/* NOTES : this code stolen from sendmail 5.61 which stole it from */ X/* from something else... */ X/*************************************************************************/ X Xint Xgetla() X X{ X#if defined(sun) | defined(mips) Xlong avenrun[3]; X#else Xdouble avenrun[3]; X#endif Xextern off_t lseek(); Xstatic int kmem = -1; X Xstatic struct nlist Nl[] = X{ X { "_avenrun" }, X#define X_AVENRUN 0 X { 0 }, X}; X X X if (kmem < 0) { X kmem = open("/dev/kmem", 0, 0); X if (kmem < 0) return (-1); X (void) ioctl(kmem, (int) FIOCLEX, (char *) 0); X (void) nlist("/vmunix", Nl); X if (Nl[0].n_type == 0) return (-1); X } X X if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 || X read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun)) X { X /* thank you Ian */ X return (-1); X } X#ifdef sun X return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT); X#else X#ifdef mips X return (FIX_TO_INT(avenrun[0])); X#else X return ((int) (avenrun[0] + 0.5)); X#endif X#endif X} X X/*************************************************************************/ X/* FUNCTION : processarg */ X/* PURPOSE : Do %value substitutions in xargv */ X/* ARGUMENTS : which value of argv to modify, hostptr, classptr */ X/*************************************************************************/ X Xvoid Xprocessarg(which, hostptr, classptr) X int which; X struct host *hostptr; X struct class *classptr; X X{ Xchar buf1[MAXPATHLEN], X buf2[MAXPATHLEN], X charbuf[2], X *newxargv, X *strptr; X X if (classptr->xargv[which] == NULL) return; X X charbuf[1] = '\0'; X *buf1 = '\0'; X X for (strptr = classptr->xargv[which]; *strptr != '\0'; strptr++) { X if (*strptr != '%') { X charbuf[0] = *strptr; X (void) strcat(buf1, charbuf); X } else { X strptr++; X switch (*strptr) { X case 'h': (void) strcat(buf1, hostptr->hostname); X break; X case 'f': (void) strcat(buf1, "%f"); X /* We shouldn't have seen a %f at this point! */ X break; X case 'd': (void) strcat(buf1, "%d"); X /* We shouldn't have seen a %d at this point! */ X break; X case 'b': (void) sprintf(buf2, batchfile, hostptr->hostname); X (void) strcat(buf1, buf2); X break; X case 'w': if (classptr->flags[C_NOWORK]) X (void) sprintf(buf2, batchfile, hostptr->hostname); X else X (void) sprintf(buf2, workfile, hostptr->hostname); X (void) strcat(buf1, buf2); X break; X case 's': (void) sprintf(buf2, "%d", hostptr->classslot); X (void) strcat(buf1, buf2); X break; X case '%': (void) strcat(buf1, "%"); /* %% changes to % */ X break; X default : strptr--; X } X } X } X X newxargv = (char *) malloc(strlen(buf1) + 1); X (void) strcpy(newxargv, buf1); X classptr->xargv[which] = newxargv; X X} X X/*************************************************************************/ X/* FUNCTION : parse_time */ X/* PURPOSE : Check a single valid-time-string against the current time */ X/* and return whether or not a match occurs. */ X/* ARGUMENTS : a pointer to the time-string */ X/*************************************************************************/ X Xint Xparsetime(whattime) Xchar *whattime; X X{ Xstatic char *days[] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Wk" }; Xlong clock; Xstruct tm *curtime; Xint wday, start, stop, ltime, validday, loop, match; X X (void) time(&clock); X curtime = localtime(&clock); X wday = curtime->tm_wday; X validday = 0; X match = 1; X X while (match && isalpha(*whattime) && isupper(*whattime)) { X match = 0; X for (loop = 0; loop < 8; loop++) { X if (strncmp(days[loop], whattime, 2) == NULL) { X whattime += 2; X match = 1; X if ((wday == loop) | ((loop == 7) && wday && (wday < 6))) { X validday = 1; X } X } X } X } X X if (strncmp(whattime, "Any", 3) == NULL) { X validday = 1; X whattime += 3; X } X X if (!validday) return 0; X X if (sscanf(whattime, "%d-%d", &start, &stop) == 2) { X ltime = curtime->tm_min + 100 * curtime->tm_hour; X if ((start < stop) && ((ltime > start) & ltime < stop)) return 1; X if ((start > stop) && ((ltime > start) | ltime < stop)) return 1; X } else return 1; X X return 0; X} X X/*************************************************************************/ X/* FUNCTION : validtime */ X/* PURPOSE : Break apart a set of valid time-strings and pass them to */ X/* parse_time, returning whether or not ANY matches occurred */ X/* ARGUMENTS : a pointer to the time-string */ X/*************************************************************************/ X Xint Xvalidtime(ptr) Xchar *ptr; X X{ Xchar *nextptr; Xint good; X X while (1) { X nextptr = STRCHR(ptr, '|'); X if (STRCHR(ptr, '|') == NULL) return(parsetime(ptr)); X *nextptr = '\0'; X good = parsetime(ptr); X *nextptr++ = '|'; /* gotta restore the | or things get skipped! */ X if (good) return(1); X ptr = nextptr; X } X} X X/*************************************************************************/ X/* FUNCTION : getclassslot */ X/* PURPOSE : */ X/* ARGUMENTS : a pointer to the class structure */ X/* RETURNS : the slot number */ X/*************************************************************************/ X Xint getclassslot(classptr) Xstruct class *classptr; X X{ Xint loop; X X for (loop = 0; loop < MAXCLASSXMITTERS; loop++) X if (classptr->slots[loop] != 'X') { X classptr->slots[loop] = 'X'; X return loop; X } X X logerr("(getclassslot) Too many xmitters for class %s", classptr->classname); X return -1; X X} X X/*************************************************************************/ X/* FUNCTION : freeclassslot */ X/* PURPOSE : */ X/* ARGUMENTS : slot number to free and a pointer to the class structure */ X/*************************************************************************/ X Xvoid freeclassslot(classptr, slot) Xstruct class *classptr; Xint slot; X X{ X X classptr->slots[slot] = '.'; X X} X X/*************************************************************************/ X/* FUNCTION : idle */ X/* PURPOSE : Set newsxd to idle mode. Don't process xmitter queue... */ X/* ARGUMENTS : none */ X/*************************************************************************/ X Xvoid Xidle() X X{ X X daemon_idle = 1; X X} X X/*************************************************************************/ X/* FUNCTION : reset */ X/* PURPOSE : Kill all outstanding transmitters and idle newsxd. */ X/* ARGUMENTS : none */ X/*************************************************************************/ X Xvoid Xreset() X X{ X X kill_children(0); X daemon_idle = 1; X X} X X#ifdef CNEWSLOCKING X X/*************************************************************************/ X/* FUNCTION : unprivileged */ X/* PURPOSE : keep the cnews libraries happy. */ X/* ARGUMENTS : none used. */ X/*************************************************************************/ X Xint Xunprivileged(reason) Xchar *reason; X{ X X} X X#endif CNEWSLOCKING END_OF_FILE if test 10869 -ne `wc -c <'util.c'`; then echo shar: \"'util.c'\" unpacked with wrong size! fi # end of 'util.c' fi if test -f 'version.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'version.c'\" else echo shar: Extracting \"'version.c'\" \(10181 characters\) sed "s/^X//" >'version.c' <<'END_OF_FILE' X/* X * #include <legal/bs.h> X > X > Copyright (c) 1989 Washington University in Saint Louis, Missouri and X > Chris Myers. All rights reserved. X > X > Permission is hereby granted to copy, reproduce, redistribute or X > otherwise use this software as long as: (1) there is no monetary X > profit gained specifically from the use or reproduction of this X > software, (2) it is not sold, rented, traded, or otherwise marketed, X > (3) the above copyright notice and this paragraph is included X > prominently in any copy made, and (4) that the name of the University X > is not used to endorse or promote products derived from this software X > without the specific prior written permission of the University. X > THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR X > IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED X > WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. X > X * newsxd: a daemon for controlling the transmission of news X * X * Written by X * Chris Myers Internet: chris@wugate.wustl.edu X * Software Engineer UUCP: ...!uunet!wugate!chris X * Office of the Network Coordinator BITNET: chris@wunet.bitnet X * Washington University in Saint Louis X * X * HISTORY: X * Version 0.1: first written in perl. A grand experiment that just didn't X * work very reliably (no reflection on the quality of perl, it X * just wasn't meant to do this kind of thing). X * X * Version 1.0: first C version X * X * 1.1: added per-host locking X * 1.2: added maxload qualifier X * 1.2.1: added time to live qualifier X * X * SOURCE CODE INADVERTANTLY DESTROYED (SIGH) X * Oh well, it needed rewriting anyway. :-) X * X * Version 2.0: here we go again. No per-host locking right now :-( X * Removed all static limits on the number of transmission X * classes and hosts. Everything is now malloc'ed. X * 2.0.1: added penalty time to time-to-live qualifier to allow other X * hosts to get news transmitters started. X * 2.1: Added the capability to specify a different transmission X * program (and parameters) for each transmission class. This X * was the motive for the name change from nntpxd to newsxd. X * 2.1.1: fixed bug caused by defunct transmitters which finally X * overran the ttl (did not do proper cleanup). X * 2.1.2: added patches for Pyramid and BSD 4.3 support from Warren X * Lavallee and Jim Lowe. X * 2.1.3: fixed bugs in calculating the ttl penalty and start/stop X * time ranges X * 2.1.4: fixed bug in read_config where the loop went 1 past the end X * of the options for a transmission class. This could cause X * the last parameter on a longer preceeding class line to be X * carried over to the subsequent classes. X * 2.1.5: somehow the default value for nntplogs slipped away from the X * code. Put it back in where it belongs. X * 2.1.6: added inter-host startup interval for classes so that newsxd X * doesn't start up too many daemons at once X * 2.1.7: fix stupid bug created in earlier patch where I set argptr[x] X * to NULL (what it was supposed to be) and then blithly reset it X * to something else -- preventing EXECing the program specified X * in an XMIT option. ARGH. X * 2.1.8: ARGH! Left out the code to make sure a transmitter for each X * host wasn't started up more often than <interval> seconds. X * 2.2: Cut down on the size of the wishlist by adding a bunch of X * features (and hopefully no more bugs): X * - syslog logging X * - lots of range/value checking on inputs X * - sort host/class names when inserting into list X * - move all configuration parameters into newsxd.h X * - add more error messages X * - document code even more X * - allow reconfiguration without killing outstanding xmitters X * - add a 'nobatchfile' switch X * - allocation of alternate xmitter parameters is now dynamic X * - Per-host delta-nice values for transmitters X * - make code more readable X * - write a manpage X * 2.3: Add SIGUSR1, SIGUSR2 to {en,dis}able debugging while running X * Shift the order of syslog initialization and switch to daemon X * Fix a "go past end of the array sometimes" bug in getclass(). X * add "reason" to status display to show why xmitter not running X * in xmit options, %f=flags,%w=workfile,%b=batchfile,%h=hostname X * make transmission of news to hosts 'fair' X * minimize a race in run_queue if SIGQUIT causes reconfiguration X * change start/stop times to use UUCP-style permissible times X * 2.3.1: fix bug in incr xmitsernum when only one service class in use X * enable fair transmission code even if maxxmits > class members X * change use of strpbrk to strchr and make strchr #define'd X * 2.4: add per-host flags X * added check for multiply-defined host/class in add{host,class} X * zero classptr->members in addclass to allow reconfiguration X * 2.4.1: classptr->members++ in addhost was in the wrong spot X * buffer declaration for FAKESYSLOG had trailing ; should be , X * log a reinitialization message after getting SIGHUP X * add -v switch to show version and logging X * if debugging == ON in daemon mode, don't dump status to stderr X * was closing pidfile with close() rather than fclose() X * change comments in xmit_done() to show sig==0 is NOHANG X * validtime() was mangling the valid times string X * parsetime() sometimes said a good time was not valid X * addhost() now resorts host list on reconfiguration X * 2.4.2: modify xmit_done() to almost always use WNOHANG on wait calls X * add_host() was improperly decrementing classptr->members X * When debugging changes, log "On"/"Off" not the value (0/-1) X * get rid of remaining code for obsolete "nntpxmit" keyword X * finish updating comments, changing "nntpxmit" to "transmitter" X * 2.5: sigsetmask(0) to allow SIGCHLD in xmit_done(); don't lose kids X * modify "Why Not Running" messages slightly for readability X * xmit_done() now loops to catch all completed transmitters X * run_queue() now checks for MIA transmitters and cleans 'em up X * if no classes defined, dump_config() now returns immediately X * break the source apart into several files from one big'un X * class options are default, w/host options overriding X * [ttl, interval, nice, maxload] X * run_queue() was setting whynotrunning wrong for active xmits X * moved ttl penalty check into "host gave up chance" code to X * prevent a penalized host from blocking others X * added SIGIO to idle newsxd until SIGHUP is received X * added class slots X * added faster pid->host mapping data structure X * added SIGTRAP to dump internal data structures for debugging X * 2.5.1: apply "lint" fixes from Andrew Partan <asp@uunet.uu.net> X * close and reopen logfiles on SIGHUP <asp@uunet.uu.net> X * put Why Not reasons in static array, use everywhere X * process.c: in fork()ed child, call _exit() not exit() X * check for duplicate newsxd running X * add support for per-class transmitter debugging flags X * fix an open file leak in config.c:read_config() X * add SIGIOT to kill all transmitters and idle newsxd X * add setuid(geteuid()), setgid(getegid()) in main() X * fix some potential null-ptr dereferencing problems in config.c X * fix array-index overrun problem in config.c <asp@uunet.uu.net> X * remove pidfile and statusfile on shutdown unless debug|DEBUG X * flock() the work file before EXECing the transmitter X * support CNEWS-style locking to prevent races on the work file X * restore default signal action after fork() for transmitter X * X * SPECIAL THANKS TO: X * X * Alpha tester: John Coolidge <coolidge@brutus.cs.uiuc.edu> X * X * Beta testers: Warren Lavallee <warren@schizo.samsung.com> X * Don Thomson <thomson@macc.wisc.edu> X * Michael A. Cooper <mcooper@usc.edu> X * Jim Lowe <james@csd4.csd.uwm.edu> X * David C. Lawrence <tale@pawl.rpi.edu> X * John Coolidge <coolidge@brutus.cs.uiuc.edu> X * Lloyd W. Taylor <lloyd@aplcen.apl.jhu.edu> X * Dave Alden <alden@shape.mps.ohio-state.edu> X * Greg Hackney <root@texbell.sbc.com> X * Rick Adams <rick@uunet.uu.net> X * Andrew Partan <asp@uunet.uu.net> X * X * This software has been tested on the following systems: X * X * DECstation 3100, Ultrix 3.1 X * DEC VAX 8810, Ultrix 3.1 X * Sun 3/60, SunOS 4.0.1 X * Sun 3/180, SunOS 4.0.3 X * Pyramid DualPort OSx X * DEC MicroVAX-II, BSD 4.3 UNIX X * DEC MicroVAX-II, Mt Xinu MORE/BSD 4.3 X * X * Wishlist: X * X * - add logging of transmitter resource usage X * - Exponential backoff for failed transmission attempts X * - Per-host locking, using shlock-style locks X * - ability to specify min. # of articles queued before xmission starts X * - add per-class workfile and batchfile definitions X * - add per-host maximum delay between transmitter startups X * X */ END_OF_FILE if test 10181 -ne `wc -c <'version.c'`; then echo shar: \"'version.c'\" unpacked with wrong size! fi # end of 'version.c' fi echo shar: End of archive 2 \(of 3\). cp /dev/null ark2isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. rm -f ark[1-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.