--- a
+++ b/pan/pan.c
@@ -0,0 +1,901 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
+ *
+ */
+/* $Id: pan.c,v 1.1 2000/09/14 21:54:44 nstraz Exp $ */
+
+#include <errno.h>
+#include <string.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>		/* log10() for subst_pcnt_f */
+#include <sys/times.h>
+
+#define NANNY
+#include "zoolib.h"
+
+struct coll_entry
+{
+    char *name;
+    char **argv;
+    int argc;
+    char *pcnt_f;
+    struct coll_entry *next;
+};
+
+struct collection
+{
+    int cnt;
+    struct coll_entry **ary;
+};
+
+struct active
+{
+    int pgrp;
+    int stopping;
+    time_t stime;
+    struct coll_entry *cmd;
+};
+
+struct orphan_pgrp
+{
+    int pgrp;
+    struct orphan_pgrp *next;
+};
+
+static pid_t run_child (struct coll_entry *colle, FILE * fp,
+			struct active *active);
+static char *slurp (char *file);
+static struct collection *get_collection (char *file, int optind, int argc,
+					  char **argv);
+static void pids_running (struct active *running, int keep_active);
+static int check_pids (struct active *running, int *num_active,
+		       int keep_active, FILE * fp, FILE * logfile,
+		       struct orphan_pgrp *orphans);
+static void propagate_signal (struct active *running, int keep_active,
+			      struct orphan_pgrp *orphans, FILE * fp);
+static void dump_coll (struct collection *coll);
+static char **subst_pcnt_f (struct coll_entry *colle);
+static void mark_orphan (struct orphan_pgrp *orphans, pid_t cpid);
+static void orphans_running (struct orphan_pgrp *orphans);
+static void check_orphans (struct orphan_pgrp *orphans, FILE * fp, int sig);
+
+char **splitstr (char *str, int *argc, char *sep);
+
+static char *panname = NULL;
+static char *errmsg;
+
+/* Debug Bits */
+int Debug = 0;
+#define	Dsetup		0x000200	/* one-time set-up */
+#define	Dshutdown	0x000100	/* killed by signal */
+#define	Dexit		0x000020	/* exit status */
+#define	Drunning	0x000010	/* current pids running */
+#define	Dstartup	0x000004	/* started command */
+#define	Dstart		0x000002	/* started command */
+#define Dwait		0x000001	/* wait interrupted */
+
+main (int argc, char **argv)
+{
+    extern char *optarg;
+    extern int optind;
+    int c;
+    char *active = NULL;
+    char *filename = "/dev/null";
+    char *logfilename = NULL;
+    FILE *logfile = NULL;
+    struct collection *coll = NULL;
+    FILE *fp;
+    pid_t cpid;
+    struct active *running;
+    struct orphan_pgrp *orphans, *orph;
+    int keep_active = 1;
+    int num_active = 0;
+    int err, i;
+    int starts = -1;
+    int stop;
+    int go_idle;
+    int has_brakes = 0;
+    int sequential = 0;
+    int fork_in_road = 0;
+    time_t t;
+    int exit_stat;
+    int track_exit_stats = 0;
+
+    while ((c = getopt (argc, argv, "s:x:n:a:f:Ad:hSl:ye")) != -1) {
+	switch (c) {
+	case 'A':
+	    has_brakes = 1;
+	    track_exit_stats = 1;
+	    break;
+	case 'x':
+	    keep_active = atoi (optarg);
+	    break;
+	case 's':
+	    starts = atoi (optarg);
+	    break;
+	case 'n':
+	    panname = (char *) malloc (strlen (optarg) + 1);
+	    strcpy (panname, optarg);
+	    break;
+	case 'a':
+	    active = (char *) malloc (strlen (optarg) + 1);
+	    strcpy (active, optarg);
+	    break;
+	case 'f':
+	    filename = (char *) malloc (strlen (optarg) + 1);
+	    strcpy (filename, optarg);
+	    break;
+	case 'd':
+	    sscanf (optarg, "%i", &Debug);
+	    break;
+	case 'S':
+	    sequential = 1;
+	    break;
+	case 'l':
+	    logfilename = optarg;
+	    break;
+	case 'y':
+	    fork_in_road = 1;
+	    break;
+	case 'e':
+	    track_exit_stats = 1;
+	    break;
+	case 'h':
+	    printf
+		("Usage: pan -n name [ -SyAeh ] [ -s starts ] [ -x nactive ] [ -l logfile ]\n\t[ -a active-file ] [ -f command-file ] [ -d debug-level ] [cmd]\n");
+	    exit (0);
+	}
+    }
+
+    if (panname == NULL) {
+	fprintf (stderr, "pan: Must supply -n\n");
+	exit (1);
+    }
+    if (active == NULL) {
+	active = zoo_active ();
+	if (active == NULL) {
+	    fprintf (stderr,
+		     "pan(%s): Must supply -a or set ZOO env variable\n",
+		     panname);
+	    exit (1);
+	}
+    }
+
+    if (logfilename != NULL) {
+	time_t startup;
+	char *s;
+
+	if (!strcmp (logfilename, "-")) {
+	    logfile = stdout;
+	} else {
+	    if ((logfile = fopen (logfilename, "a+")) == NULL) {
+		fprintf (stderr,
+			 "pan(%s): Error %s (%d) opening log file '%s'\n",
+			 panname, strerror(errno), errno, logfilename);
+		exit (1);
+	    }
+	}
+
+	time (&startup);
+	s = ctime (&startup);
+	*(s + strlen (s) - 1) = '\0';
+	fprintf (logfile, "startup='%s'\n", s);
+    }
+
+    coll = get_collection (filename, optind, argc, argv);
+    if (coll->cnt == 0) {
+	fprintf (stderr,
+		 "pan(%s): Must supply a file collection or a command\n",
+		 panname);
+	exit (1);
+    }
+
+    if (Debug & Dsetup)
+	dump_coll (coll);
+
+    /* a place to store the pgrps we're watching */
+    running =
+	(struct active *) malloc ((keep_active + 1) * sizeof (struct active));
+    memset (running, 0, keep_active * sizeof (struct active));
+    running[keep_active].pgrp = -1;	/* end sentinel */
+
+    /* a head to the orphaned pgrp list */
+    orphans = (struct orphan_pgrp *) malloc (sizeof (struct orphan_pgrp));
+    memset (orphans, 0, sizeof (struct orphan_pgrp));
+
+    srand48 (time (NULL) ^ (getpid () + (getpid () << 15)));
+
+    /* Supply a default for starts.  If we are in sequential mode, use
+     * the number of commands available; otherwise 1.
+     */
+
+    if (starts == -1)
+	if (sequential)
+	    starts = coll->cnt;
+	else
+	    starts = 1;
+
+    /* If starts is not infinite, but is less than keep_active,
+     * then bump it up to keep_active.
+     */
+
+    if ((starts > 0) && (starts < keep_active))
+	starts = keep_active;
+
+    if (starts == 0)		/* The user's view of infinite starts. */
+	starts = -1;		/* Our view of infinite starts. */
+
+    if ((fp = open_file (active, "r+", &errmsg)) == NULL) {
+	fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+	exit (1);
+    }
+    if (write_active_args (fp, getpid (), panname, argc, argv, &errmsg) == -1) {
+	fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+	exit (1);
+    }
+
+    /* Allocate N spaces for max-arg commands.
+     * this is an "active file cleanliness" thing
+     */
+    {
+	char *av[2], bigarg[82];
+	int t;
+
+	t = 1;
+	memset (bigarg, '.', 81);
+	bigarg[81] = '\0';
+	av[0] = bigarg;
+	av[1] = NULL;
+
+	for (c = 0; c < keep_active; c++) {
+	    if (write_active_args (fp, t, panname, 1, av, &errmsg) == -1) {
+		fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+		exit (1);
+	    }
+	}
+	for (c = 0; c < keep_active; c++) {
+	    if (clear_active (fp, t, &errmsg) != 1) {
+		fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+		exit (1);
+	    }
+	}
+    }
+
+    rec_signal = send_signal = 0;
+    signal (SIGINT, wait_handler);
+    signal (SIGTERM, wait_handler);
+    signal (SIGHUP, wait_handler);
+    signal (SIGUSR1, wait_handler);	/* ignore fork_in_road */
+    signal (SIGUSR2, wait_handler);	/* stop the scheduler */
+
+    c = 0;			/* in this loop, c is the command index */
+    stop = 0;
+    exit_stat = 0;
+    go_idle = 0;
+    while (1) {
+
+	while ((num_active < keep_active) && (starts != 0)) {
+	    if (stop || rec_signal || go_idle)
+		break;
+
+	    if (!sequential)
+		c = lrand48 () % coll->cnt;
+
+	    /* find a slot for the child */
+	    for (i = 0; i < keep_active; ++i) {
+		if (running[i].pgrp == 0)
+		    break;
+	    }
+	    if (i == keep_active) {
+		fprintf (stderr, "pan(%s): Aborting: i == keep_active = %d\n",
+			 panname, i);
+		wait_handler (SIGINT);
+		exit_stat++;
+		break;
+	    }
+
+	    cpid = run_child (coll->ary[c], fp, running + i);
+	    if (cpid != -1) {
+		++num_active;
+		if (starts > 0)
+		    --starts;
+	    }
+
+	    if (sequential)
+		if (++c >= coll->cnt)
+		    c = 0;
+
+	}			/* while( (num_active < keep_active) && (starts != 0) ) */
+
+	if (starts == 0)
+	    ++stop;
+
+	if (rec_signal) {
+	    /* propagate everything except sigusr2 */
+
+	    if (rec_signal == SIGUSR2) {
+		if (fork_in_road)
+		    ++go_idle;
+		else
+		    ++stop;
+		signal (rec_signal, wait_handler);
+		rec_signal = send_signal = 0;
+	    } else {
+		if (rec_signal == SIGUSR1)
+		    fork_in_road = 0;
+		propagate_signal (running, keep_active, orphans, fp);
+		if (fork_in_road)
+		    ++go_idle;
+		else
+		    ++stop;
+	    }
+	}
+
+	err = check_pids (running, &num_active, keep_active, fp,
+			  logfile, orphans);
+	if (Debug & Drunning) {
+	    pids_running (running, keep_active);
+	    orphans_running (orphans);
+	}
+	if (err) {
+	    if (fork_in_road)
+		++go_idle;
+	    if (track_exit_stats)
+		exit_stat++;
+	    if (has_brakes) {
+		printf ("pan(%s): All stop!%s\n", panname,
+			go_idle ? " (idling)" : "");
+		wait_handler (SIGINT);
+	    }
+	}
+
+	if (stop && (num_active == 0))
+	    break;
+
+	if (go_idle && (num_active == 0)) {
+	    go_idle = 0;	/* It is idle, now resume scheduling. */
+	    wait_handler (0);	/* Reset the signal ratchet. */
+	}
+    }
+
+    /* Wait for orphaned pgrps */
+    while (1) {
+	for (orph = orphans; orph != NULL; orph = orph->next) {
+	    if (orph->pgrp == 0)
+		continue;
+	    /* Yes, we have orphaned pgrps */
+	    sleep (5);
+	    if (!rec_signal) {
+		/* force an artificial signal, move us
+		 * through the signal ratchet.
+		 */
+		wait_handler (SIGINT);
+	    }
+	    propagate_signal (running, keep_active, orphans, fp);
+	    if (Debug & Drunning)
+		orphans_running (orphans);
+	    break;
+	}
+	if (orph == NULL)
+	    break;
+    }
+
+    signal (SIGINT, SIG_DFL);
+    if (clear_active (fp, getpid (), &errmsg) != 1) {
+	fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+	++exit_stat;
+    }
+    fclose (fp);
+
+    exit (exit_stat);
+}
+
+
+static void
+pids_running (struct active *running, int keep_active)
+{
+    int i;
+
+    printf ("pids still running: ");
+    for (i = 0; i < keep_active; ++i) {
+	if (running[i].pgrp != 0)
+	    printf ("%d ", running[i].pgrp);
+    }
+    printf ("\n");
+}
+
+
+static void
+propagate_signal (struct active *running, int keep_active,
+		  struct orphan_pgrp *orphans, FILE * fp)
+{
+    int i;
+
+    if (Debug & Dshutdown)
+	printf ("pan was signaled with sig %d...\n", rec_signal);
+
+    for (i = 0; i < keep_active; ++i) {
+	if (running[i].pgrp == 0)
+	    continue;
+
+	if (Debug & Dshutdown)
+	    printf ("  propagating sig %d to %d\n",
+		    send_signal, -running[i].pgrp);
+	if (kill (-running[i].pgrp, send_signal) != 0) {
+	    fprintf (stderr,
+		     "pan(%s): kill(%d,%d) failed on tag (%s).  errno:%d  %s\n",
+		     panname, -running[i].pgrp, send_signal,
+		     running[i].cmd->name, errno, SYSERR);
+	}
+	running[i].stopping = 1;
+    }
+
+    check_orphans (orphans, fp, send_signal);
+
+    signal (rec_signal, wait_handler);
+    rec_signal = send_signal = 0;
+}
+
+
+static int
+check_pids (struct active *running, int *num_active, int keep_active,
+	    FILE * fp, FILE * logfile, struct orphan_pgrp *orphans)
+{
+    int w;
+    pid_t cpid;
+    int stat_loc;
+    int ret = 0;
+    int i;
+    time_t t;
+    char *status;
+    int signaled = 0;
+    struct tms tms1, tms2;
+    clock_t tck;
+
+    check_orphans (orphans, fp, 0);
+
+    tck = times (&tms1);
+    if (tck == -1) {
+	fprintf (stderr, "pan(%s): times(&tms1) failed.  errno:%d  %s\n",
+		 panname, errno, SYSERR);
+    }
+    cpid = wait (&stat_loc);
+    tck = times (&tms2);
+    if (tck == -1) {
+	fprintf (stderr, "pan(%s): times(&tms2) failed.  errno:%d  %s\n",
+		 panname, errno, SYSERR);
+    }
+
+    if (cpid < 0) {
+	if (errno == EINTR) {
+	    if (Debug)
+		fprintf (stderr, "pan(%s): wait() interrupted\n", panname);
+	} else if (errno != ECHILD) {
+	    fprintf (stderr, "pan(%s): wait() failed.  errno:%d  %s\n",
+		     panname, errno, SYSERR);
+	}
+    } else if (cpid > 0) {
+
+	if (WIFSIGNALED (stat_loc)) {
+	    w = WTERMSIG (stat_loc);
+	    status = "signaled";
+	    if (Debug & Dexit)
+		printf ("child %d terminated with signal %d\n", cpid, w);
+	    --*num_active;
+	    signaled = 1;
+	} else if (WIFEXITED (stat_loc)) {
+	    w = WEXITSTATUS (stat_loc);
+	    status = "exited";
+	    if (Debug & Dexit)
+		printf ("child %d exited with status %d\n", cpid, w);
+	    --*num_active;
+	    if (w != 0)
+		ret++;
+	} else if (WIFSTOPPED (stat_loc)) {	/* should never happen */
+	    w = WSTOPSIG (stat_loc);
+	    status = "stopped";
+	    ret++;
+	} else {		/* should never happen */
+	    w = 0;
+	    status = "unknown";
+	    ret++;
+	}
+
+	for (i = 0; i < keep_active; ++i) {
+	    if (running[i].pgrp == cpid) {
+		if ((w == 130) && running[i].stopping &&
+		    (strcmp (status, "exited") == 0)) {
+		    /* The child received sigint, but
+		     * did not trap for it?  Compensate
+		     * for it here.
+		     */
+		    w = 0;
+		    ret--;	/* undo */
+		    if (Debug & Drunning)
+			printf
+			    ("pan(%s): tag=%s exited 130, known to be signaled; will give it an exit 0.\n",
+			     panname, running[i].cmd->name);
+		}
+		if (logfile != NULL) {
+		    time (&t);
+		    fprintf (logfile,
+			     "tag=%s stime=%d dur=%d exit=%s stat=%d core=%s cu=%d cs=%d\n",
+			     running[i].cmd->name, running[i].stime,
+			     t - running[i].stime, status, w,
+			     (stat_loc & 0200) ? "yes" : "no",
+			     tms2.tms_cutime - tms1.tms_cutime,
+			     tms2.tms_cstime - tms1.tms_cstime);
+		    fflush (logfile);
+		}
+		/* If signaled and we weren't expecting
+		 * this to be stopped then the proc
+		 * had a problem.
+		 */
+		if (signaled && !running[i].stopping)
+		    ret++;
+
+		running[i].pgrp = 0;
+		if (clear_active (fp, cpid, &errmsg) == -1) {
+		    fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+		    exit (1);
+		}
+
+		/* Check for orphaned pgrps */
+		if ((kill (-cpid, 0) == 0) || (errno == EPERM)) {
+		    if (write_active_args (fp, cpid, "panorphan",
+					   running[i].cmd->argc,
+					   running[i].cmd->argv,
+					   &errmsg) == -1) {
+			fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+			exit (1);
+		    }
+		    mark_orphan (orphans, cpid);
+		    /* status of kill doesn't matter */
+		    kill (-cpid, SIGTERM);
+		}
+
+		break;
+	    }
+	}
+    }
+    return ret;
+}
+
+
+static pid_t
+run_child (struct coll_entry *colle, FILE * fp, struct active *active)
+{
+    int cpid;
+
+    if ((cpid = fork ()) < 0) {
+	fprintf (stderr, "pan(%s): fork failed.  errno:%d  %s\n",
+		 panname, errno, SYSERR);
+	return -1;
+    } else if (cpid == 0) {
+	/* child */
+	char **eargv;
+
+	fclose (fp);
+	setpgrp ();
+
+	if (colle->pcnt_f != NULL) {
+	    eargv = subst_pcnt_f (colle);
+	} else {
+	    eargv = colle->argv;
+	}
+
+	/* execute command */
+	execvp (eargv[0], eargv);
+	fprintf (stderr,
+		 "pan(%s): execvp of '%s' (tag %s) failed.  errno:%d  %s\n",
+		 panname, eargv[0], colle->name, errno, SYSERR);
+	exit (errno);
+    }
+
+    /* parent */
+    time (&active->stime);
+    active->pgrp = cpid;
+    active->stopping = 0;
+    active->cmd = colle;
+
+    if (write_active_args
+	(fp, cpid, colle->name, colle->argc, colle->argv, &errmsg) == -1) {
+	fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+	exit (1);
+    }
+
+    if (Debug & Dstartup)
+	printf ("started %s cpid=%d at %s",
+		colle->name, cpid, ctime (&active->stime));
+
+    if (Debug & Dstart) {
+	int ac;
+	printf ("Executing test = %s as ", colle->name);
+	for (ac = 0; ac < colle->argc; ac++) {
+	    printf ("%s ", colle->argv[ac]);
+	}
+	printf ("\n");
+    }
+
+    return cpid;
+}
+
+
+static char **
+subst_pcnt_f (struct coll_entry *colle)
+{
+    char **eargv;
+    char *p;
+    int i;
+
+    eargv = (char **) malloc ((colle->argc + 1) * sizeof (char *));
+
+    for (i = 0; i < colle->argc; ++i) {
+	if ((p = strstr (colle->argv[i], "%f")) != NULL) {
+	    /* simple, for now */
+	    static int counter = 1;
+	    char *b, *p2;
+	    int pidlen, counterlen;
+
+	    *p = '\0';		/* cut off at % */
+	    p2 = p + 2;		/* stuff that follows %f */
+
+	    pidlen = 1 + (int) log10 ((double) getpid ());
+	    counterlen = 1 + (int) log10 ((double) counter);
+
+	    b = (char *) malloc (strlen (colle->argv[i]) +
+				 pidlen + 1 + counterlen + strlen (p2) + 1);
+	    sprintf (b, "%s%d_%d%s",
+		     colle->argv[i], getpid (), counter++, p2);
+	    *p = '%';		/* restore % */
+	    eargv[i] = b;
+	} else {
+	    eargv[i] = colle->argv[i];
+	}
+    }
+    eargv[i] = NULL;
+    return eargv;
+}
+
+static struct collection *
+get_collection (char *file, int optind, int argc, char **argv)
+{
+    char *buf, *a, *b;
+    struct coll_entry *head, *p, *n;
+    struct collection *coll;
+    int i;
+
+    buf = slurp (file);
+
+    coll = (struct collection *) malloc (sizeof (struct collection));
+    coll->cnt = 0;
+
+    head = p = n = NULL;
+    a = b = buf;
+    while (*b != '\0') {
+	if ((b = strchr (a, '\n')) != NULL)
+	    *b = '\0';
+
+	if ((*a != '#') && (*a != '\0') && (*a != ' ')) {
+	    if (head == NULL) {
+		head =
+		    (struct coll_entry *) malloc (sizeof (struct coll_entry));
+		head->pcnt_f = strstr (a, "%f");
+		head->argv = splitstr (a, &head->argc, 0);
+		head->name = head->argv[0];
+		head->argv++;	/* remove name from command */
+		head->argc--;
+		head->next = NULL;
+		p = head;
+	    } else {
+		n = (struct coll_entry *) malloc (sizeof (struct coll_entry));
+		p->next = n;
+		n->pcnt_f = strstr (a, "%f");
+		n->argv = splitstr (a, &n->argc, 0);
+		n->name = n->argv[0];
+		n->argv++;	/* remove name from command */
+		n->argc--;
+		n->next = NULL;
+		p = n;
+	    }
+	    coll->cnt++;
+	}
+	a += strlen (a) + 1;
+	b = a;
+    }
+    free (buf);
+
+    /* is there something on the commandline to be counted? */
+    if (optind < argc) {
+	char **args;
+	char *pcnt_f = NULL;
+
+	args = (char **) malloc ((argc - optind + 1) * sizeof (char *));
+	/* fill arg list */
+	for (i = 0; optind < argc; ++optind, ++i) {
+	    args[i] = argv[optind];
+	    if ((pcnt_f == NULL) && ((strstr (args[i], "%f")) != NULL)) {
+		pcnt_f = args[i];
+	    }
+	}
+	args[i] = NULL;
+
+	if (head == NULL) {
+	    head = (struct coll_entry *) malloc (sizeof (struct coll_entry));
+	    head->pcnt_f = pcnt_f;
+	    head->argv = args;
+	    head->name = "cmdln";
+	    head->argc = i;
+	    head->next = NULL;
+	} else {
+	    n = (struct coll_entry *) malloc (sizeof (struct coll_entry));
+	    p->next = n;
+	    n->pcnt_f = pcnt_f;
+	    n->argv = args;
+	    n->name = "cmdln";
+	    n->argc = i;
+	    n->next = NULL;
+	}
+	coll->cnt++;
+    }
+
+    /* get an array */
+    coll->ary = (struct coll_entry **) malloc (coll->cnt *
+					       sizeof (struct coll_entry *));
+
+    /* fill the array */
+    i = 0;
+    n = head;
+    while (n != NULL) {
+	coll->ary[i] = n;
+	n = n->next;
+	++i;
+    }
+    if (i != coll->cnt)
+	fprintf (stderr, "pan(%s): i doesn't match cnt\n", panname);
+
+    return coll;
+}
+
+
+static char *
+slurp (char *file)
+{
+    char *buf;
+    int fd;
+    struct stat sbuf;
+
+    if ((fd = open (file, O_RDONLY)) < 0) {
+	fprintf (stderr, "pan(%s): open(%s,O_RDONLY) failed.  errno:%d  %s\n",
+		 panname, file, errno, SYSERR);
+	exit (1);
+    }
+
+    if (fstat (fd, &sbuf) < 0) {
+	fprintf (stderr, "pan(%s): fstat(%s) failed.  errno:%d  %s\n",
+		 panname, file, errno, SYSERR);
+	exit (1);
+    }
+
+    buf = (char *) malloc (sbuf.st_size + 1);
+    if (read (fd, buf, sbuf.st_size) != sbuf.st_size) {
+	fprintf (stderr, "pan(%s): slurp failed.  errno:%d  %s\n",
+		 panname, errno, SYSERR);
+	exit (1);
+    }
+    buf[sbuf.st_size] = '\0';
+
+    close (fd);
+    return buf;
+}
+
+static void
+check_orphans (struct orphan_pgrp *orphans, FILE * fp, int sig)
+{
+    struct orphan_pgrp *orph;
+
+    for (orph = orphans; orph != NULL; orph = orph->next) {
+	if (orph->pgrp == 0)
+	    continue;
+
+	if (Debug & Dshutdown)
+	    printf ("  propagating sig %d to orphaned pgrp %d\n",
+		    sig, -(orph->pgrp));
+	if (kill (-(orph->pgrp), sig) != 0) {
+	    if (errno == ESRCH) {
+		/* This pgrp is now empty */
+		if (clear_active (fp, orph->pgrp, &errmsg) == -1) {
+		    fprintf (stderr, "pan(%s): %s\n", panname, errmsg);
+		}
+		orph->pgrp = 0;
+	    } else {
+		fprintf (stderr,
+			 "pan(%s): kill(%d,%d) on orphaned pgrp failed.  errno:%d  %s\n",
+			 panname, -(orph->pgrp), sig, errno, SYSERR);
+	    }
+	}
+    }
+}
+
+
+static void
+mark_orphan (struct orphan_pgrp *orphans, pid_t cpid)
+{
+    struct orphan_pgrp *orph;
+
+    for (orph = orphans; orph != NULL; orph = orph->next) {
+	if (orph->pgrp == 0)
+	    break;
+    }
+    if (orph == NULL) {
+	/* make a new struct */
+	orph = (struct orphan_pgrp *) malloc (sizeof (struct orphan_pgrp));
+
+	/* plug in the new struct just after the head */
+	orph->next = orphans->next;
+	orphans->next = orph;
+    }
+    orph->pgrp = cpid;
+}
+
+
+static void
+orphans_running (struct orphan_pgrp *orphans)
+{
+    struct orphan_pgrp *orph;
+
+    printf ("orphans still running: ");
+    for (orph = orphans; orph != NULL; orph = orph->next) {
+	if (orph->pgrp != 0)
+	    printf ("%d ", -(orph->pgrp));
+    }
+    printf ("\n");
+}
+
+static void
+dump_coll (struct collection *coll)
+{
+    int x, i;
+
+    for (i = 0; i < coll->cnt; ++i) {
+	printf ("coll %d\n", i);
+	printf ("  name=%s #args=%d\n", coll->ary[i]->name,
+		coll->ary[i]->argc);
+	for (x = 0; coll->ary[i]->argv[x]; ++x) {
+	    printf ("  argv[%d] = (%s)\n", x, coll->ary[i]->argv[x]);
+	}
+    }
+}