v23i012: Netnews transmission daemon, Part02/03
Rich Salz
rsalz at bbn.com
Fri Aug 31 23:42:56 AEST 1990
Submitted-by: Chris Myers <chris at 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 at uunet.uu.net if you want that tool.
# Contents: defs.h patchlevel.h process.c simple.conf util.c version.c
# Wrapped by rsalz at 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 at wugate.wustl.edu
X * Software Engineer UUCP: ...!uunet!wugate!chris
X * Office of the Network Coordinator BITNET: chris at 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 at uunet.uu.net>
X * close and reopen logfiles on SIGHUP <asp at 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 at 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 at brutus.cs.uiuc.edu>
X *
X * Beta testers: Warren Lavallee <warren at schizo.samsung.com>
X * Don Thomson <thomson at macc.wisc.edu>
X * Michael A. Cooper <mcooper at usc.edu>
X * Jim Lowe <james at csd4.csd.uwm.edu>
X * David C. Lawrence <tale at pawl.rpi.edu>
X * John Coolidge <coolidge at brutus.cs.uiuc.edu>
X * Lloyd W. Taylor <lloyd at aplcen.apl.jhu.edu>
X * Dave Alden <alden at shape.mps.ohio-state.edu>
X * Greg Hackney <root at texbell.sbc.com>
X * Rick Adams <rick at uunet.uu.net>
X * Andrew Partan <asp at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list