Add pan and associated files. This is a lightweight test harness. It works a
lot like runtests.py did, but it is more powerful. See the man page for
details.
diff --git a/Makefile b/Makefile
index 926df0b..73ca7fd 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@
all: libltp.a
@$(MAKE) -C doio $@
@$(MAKE) -C tests $@
+ @$(MAKE) -C pan $@
libltp.a:
@$(MAKE) -C lib $@
@@ -13,3 +14,4 @@
@$(MAKE) -C lib $@
@$(MAKE) -C doio $@
@$(MAKE) -C tests $@
+ @$(MAKE) -C pan $@
diff --git a/README b/README
index 6743330..c7e2f8b 100644
--- a/README
+++ b/README
@@ -26,11 +26,11 @@
structure is simple.
doio/
- The doio directory contains three tools: doio, iogen, and growfiles.
- These are elaborate filesystem tests for stressing and testing the
- functionality of the filesystem. There is also a wrapper for doio
- and iogen called rwtest. Command examples for these tools can be found
- in the cmdlines in the root of the ltp directory.
+ The doio directory contains three tools: doio, iogen, and growfiles.
+ These are elaborate filesystem tests for stressing and testing the
+ functionality of the filesystem. There is also a wrapper for doio
+ and iogen called rwtest. Command examples for these tools can be
+ found in the cmdlines in the root of the ltp directory.
include/
lib/
@@ -38,13 +38,24 @@
routines used by many of the tests.
tests/
- To date the tests directory contains a number of simple tests called
- 'quickhitters'. These tests are designed to be simple and quick and be
- run in conjunction with each other. They have some use as stand alone
- tests, but when run many-at-a-time, interesting issues can come up.
+ To date the tests directory contains a number of simple tests called
+ 'quickhitters'. These tests are designed to be simple and quick and
+ be run in conjunction with each other. They have some use as stand
+ alone tests, but when run many-at-a-time, interesting issues can come
+ up.
doc/
- The doc directory contains mainly man pages for mainly the library codes.
+ The doc directory contains mainly man pages for mainly the library
+ codes.
+
+runtest/
+ The runtest directory contains a simple test harness that Aaron
+ Laffin hacked up in Python. It runs tests from a file in sequential
+ order. It is meant to be an example of a simple test harness.
+pan/
+ The pan directory contains a simple, lightweight test harness. pan
+ has the ability to run tests randomly and in parallel. See pan's man
+ page for more information.
Notes
diff --git a/doc/man1/bump.1 b/doc/man1/bump.1
new file mode 100644
index 0000000..744eb6a
--- /dev/null
+++ b/doc/man1/bump.1
@@ -0,0 +1,80 @@
+.\"
+.\" $Id: bump.1,v 1.1 2000/09/14 21:54:44 nstraz Exp $
+.\"
+.\" 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/
+.\"
+.TH BUMP 1 "14 Sep 2000" "LTP" "Linux Test Project"
+.SH NAME
+bump \- send signal to tags run by pan
+.SH SYNOPSIS
+\fBbump [-1] [-s \fIsig\fB] [\fI-a active-file\fB] [tags...]
+.SH DESCRIPTION
+
+Bump will send a SIGINT signal to processes, given that each process has a
+corresponding tag in an active-file. The active-file is the same one that is
+used by the pan to start the processes.
+
+If the active file has multiple occurrences of a single tag name then only the
+first process will be signaled. You may specify the tag name multiple times
+on the commandline if necessary.
+
+.TP 1i
+\fB-1\fP
+Send a SIGUSR1 signal. By default a SIGINT will be sent.
+.TP 1i
+\fB-a \fIactive_file\fB
+A file containing the tagnames, pids, and commands being run by a pan. If this
+is not specified then the ZOO environment variable will be read for the name of
+the directory where the active file can be found.
+.TP 1i
+\fB-s \fIsig\fB
+Used to specify a signal number to send. By default a SIGINT will be sent.
+
+.in -1i
+
+.SH ENVIRONMENT
+.TP
+ZOO
+If set, should name the directory where the active file can be found.
+This is ignored if \fI-a\fP is specified.
+
+.SH FILES
+.TP
+active
+Default name of active file if \fI-a\fP is not specified. This is prefixed
+by the directory name found in the ZOO environment variable.
+
+.SH "SEE ALSO"
+Zoo tools - pan(1)
+
+.SH DIAGNOSTICS
+Exits zero, unless it cannot find the active file or if there were no tags
+listed on the commandline.
diff --git a/doc/man1/pan.1 b/doc/man1/pan.1
new file mode 100644
index 0000000..227ed8a
--- /dev/null
+++ b/doc/man1/pan.1
@@ -0,0 +1,226 @@
+.\"
+.\" $Id: pan.1,v 1.1 2000/09/14 21:54:44 nstraz Exp $
+.\"
+.\" 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/
+.TH PAN 1 "14 Sep 2000" "LTP" "Linux Test Project"
+.SH NAME
+pan \- A light-weight driver to run tests and clean up their pgrps
+.SH SYNOPSIS
+\fBpan -n tagname [-SyAeh] [-s \fIstarts\fB] [\fI-x nactive\fB] [\fI-l logfile\fB] [\fI-a active-file\fB] [\fI-f command-file\fB] [\fI-d debug-level\fB] [cmd]
+.SH DESCRIPTION
+
+Pan will run a command, as specified on the commandline, or collection of
+commands from a command-file. By default pan runs one command, choosing it at
+random from the whole set of commands available to it. The pan's name in the
+active file is specified by the tagname. When a command terminates pan will
+kill any orphans that may have been left behind in its pgrp. If pan is
+signaled it will kill any active commands and, again, clean up any orphans.
+
+Pan uses the signal ratchet found in other zoo tools. The first time pan is
+signaled it sends a SIGTERM to the active pgrps; the second time it sends
+SIGHUP; the third time a SIGINT; after that it always sends SIGKILL.
+
+Pan will not terminate until all the active commands and everything in their
+pgrps is dead. It will loop around at 5 second intervals, triggering its own
+signal ratchet, until it succeeds in killing the pgrps.
+
+When the pan starts up it places its own tagname and commandline in the active
+file and begins scheduling commands. After a command is started pan puts an
+entry for it into the active file with its indicated tagname. If the command
+was specified on the command line, rather than in the command-file, then its
+tagname will be "cmdln". When a process terminates pan frees the active file
+entry. If a command terminates and leaves an orphaned pgrp then pan will put
+an entry into the active file called "panorphan" which will be removed only
+when the orphaned pgrp is cleaned up. Before pan exits it will ensure that
+all orphaned pgrps are dead (see above) and then it will remove its own
+tagname from the active file.
+
+The command-file is a file containing tag/command pairs. Each line in the
+file begins with a tag identifying the command, followed by white space, and
+then the command and its arguments. A line beginning with the # character is
+a comment. Pan recognizes the token "%f" in a command's arguments and
+replaces it with a unique identifier--add this to filename arguments to
+prevent two instances of the command from interfering with each other.
+
+When pan receives a SIGUSR2 it stops scheduling new tests and waits for the
+active tests to terminate. If the \fB-y\fP option was used then it will begin
+scheduling again, otherwise it will exit. It does not propagate the SIGUSR2.
+
+.TP 1i
+\fB-A\fP
+The all-stop flag. If any command exits non-zero pan will shutdown its
+scheduler and signal any active pgrps. The pan will exit non-zero after
+everything is shut down. By default pan ignores command exit statuses.
+The \fI-e\fP option is implied when this option is used.
+.TP 1i
+\fB-a \fIactive_file\fB
+A file containing the tagnames, pids, and commands being run. If this is
+not specified then the ZOO environment variable will be read for the name
+of a directory where the active file will be placed, and in this case the
+active file's name will be "active". A single active file may be shared
+by any number of Zoo tools.
+.TP 1i
+\fB-d \fIdebug-level\fB
+See the source for settings.
+.TP 1i
+\fB-e\fP
+Pan will exit non-zero if any of its commands exited non-zero. By default
+pan ignores command exit statuses.
+.TP 1i
+\fB-h\fP
+Print some simple help.
+.TP 1i
+\fB-l \fIlogfile\fB
+Name of a log file to be used to store exit information for each of the
+commands (tags) that are run. This log file may not be shared with other Zoo
+tools or other pan processes.
+.TP 1i
+\fB-n \fItagname\fB
+The tagname by which this pan process will be known by the zoo tools. This
+is a required argument.
+.TP 1i
+\fB-S\fP
+Causes pan to run commands (tags) sequentially, as they are listed in the
+command-file. By default it chooses tags randomly. If a command is specified
+on the commandline and a command-file is also specified, then the commandline
+tag will be the last command. If this is specified and \fI-s\fP is not
+specified then the default setting for \fI-s\fP is equal to the total number
+of commands.
+.TP 1i
+\fB-s \fIstarts\fB
+Indicates the number of commands (tags) that should be run before terminating.
+Set this to zero to run forever. By default this is set to 1 (but see
+\fI-S\fP for an exception). If this is specified and is less than the value
+specified for \fI-x\fP then it is bumped up to be equal to the value of
+\fI-x\fP (in other words, \fI-x\fP is always satisfied).
+.TP 1i
+\fB-x \fInactive\fB
+Indicates the number of commands (tags) that should be kept active at any one
+time. If this is greater than 1 then it is possible to have multiple
+instances of the same tag active at once. By default this is 1.
+.TP 1i
+\fB-y\fP
+Causes the pan scheduler to go idle if a signal is received or if a command
+exits non-zero. All active commands and their pgrps will be killed. After
+everything is dead the scheduler will restart again where it left off. If the
+signal is SIGUSR1 then pan will behave as if \fI-y\fP had not been specified.
+
+.in -1i
+
+.SH EXAMPLES
+
+In practice, the ZOO environment variable is generally prefered over the
+\fI-a\fP option. All examples assume this is being set.
+
+The following creates a pan named "ex1" with an active file in /tmp/active.
+It runs the command "echo hello", keeping 3 copies running at all times,
+running 10 copies before terminating.
+
+$ export ZOO=/tmp
+.br
+$ pan -n ex1 -s 10 -x 3 echo hello
+
+The next example will use this command file. Call this /tmp/cmds1.
+.br
+----------cut------
+.br
+fido ls /bin
+.br
+rover echo hello wally
+.br
+gidget sleep 2
+.br
+lassie ls /etc
+.br
+----------cut------
+.br
+
+Using the above command file, /tmp/cmds1, run one command at a time,
+sequentially, running each command only once. If one command should fail then
+terminate immediately. An exit log is kept for all the commands.
+
+$ pan -n ex3 -S -A -f /tmp/cmds1 -l ex3.log
+
+
+.SH LAYERING
+
+Pan is often used in layers. This section extends the above examples to show
+how this is done.
+
+The next example will use this command file. Call this /tmp/cmds2. Note that
+the embedded pans inside this file have exit logs, and that %f is used to give
+each pan a unique log file name.
+.br
+----------cut------
+.br
+larry pan -n ex4b -s10 -A -l ex4_%f.log echo hello
+.br
+curly pan -n ex4c -S -A -f /tmp/cmds1 -l ex4_%f.log
+.br
+moe echo done here
+.br
+----------cut------
+.br
+
+The following will run commands from the command file, keeping two at a time
+running, choosing them sequentially, and terminating if any of them exits
+non-zero.
+
+$ pan -n ex4 -x2 -A -S -f /tmp/cmds2
+
+Now run the commands in /tmp/cmds2, but this time we want to recover if one of
+the commands should exit non-zero. In this example it is possible for the
+"larry" or "curly" tags to exit non-zero. When this happens the pan will kill
+all active tags, making sure both larry and curly are dead, and then will
+continue scheduling--ensuring that our "done here" message comes out no matter
+what.
+
+$ pan -n ex5 -x2 -A -S -y -f /tmp/cmds2
+
+.SH ENVIRONMENT
+.TP
+ZOO
+If set, should name the directory where the active file should be placed.
+This is ignored if \fI-a\fP is specified.
+
+.SH FILES
+.TP
+active
+Default name of active file if \fI-a\fP is not specified. This is prefixed
+by the directory name found in the ZOO environment variable.
+
+.SH "SEE ALSO"
+Zoo tools - bump(1)
+
+.SH DIAGNOSTICS
+By default it exits zero unless signaled, regardless of the exit status of any
+of the commands it is running. If \fI-A\fP or \fI-e\fP are specified it exits non-zero if
+it is signaled or if any of the commands it is running should exit non-zero.
diff --git a/pan/Makefile b/pan/Makefile
new file mode 100644
index 0000000..a6fc395
--- /dev/null
+++ b/pan/Makefile
@@ -0,0 +1,12 @@
+
+CC = gcc
+LDFLAGS = -lm
+
+all: pan bump
+
+pan: pan.o zoolib.o splitstr.o
+
+bump: bump.o zoolib.o
+
+clean:
+ rm *.o pan bump
diff --git a/pan/bump.c b/pan/bump.c
new file mode 100644
index 0000000..2b83f97
--- /dev/null
+++ b/pan/bump.c
@@ -0,0 +1,170 @@
+/*
+ * 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: bump.c,v 1.1 2000/09/14 21:54:44 nstraz Exp $ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <string.h>
+
+#define NANNY /* not really */
+#include "zoolib.h"
+
+pid_t
+read_active( FILE *fp, char *name );
+
+static char *errmsg;
+
+main( int argc, char **argv ){
+ extern char *optarg;
+ extern int optind;
+ int c;
+ int x;
+ char *active = NULL;
+ pid_t nanny;
+ FILE *fp;
+ int sig = SIGINT;
+
+ while( (c = getopt(argc, argv, "a:s:12")) != -1 ){
+ switch(c){
+ case 'a':
+ active = (char*)malloc(strlen(optarg)+1);
+ strcpy( active, optarg );
+ break;
+ case 's':
+ sig = atoi( optarg );
+ break;
+ case '1':
+ sig = SIGUSR1;
+ break;
+ case '2':
+ sig = SIGUSR2;
+ break;
+ }
+ }
+
+ if( active == NULL ){
+ active = zoo_active();
+ if( active == NULL ){
+ fprintf(stderr, "bump: Must supply -a or set ZOO env variable\n");
+ exit(1);
+ }
+ }
+ if( optind == argc ){
+ fprintf( stderr, "bump: Must supply names\n");
+ exit(1);
+ }
+
+ /* need r+ here because we're using write-locks */
+ if( (fp = open_file( active, "r+", &errmsg )) == NULL ){
+ fprintf(stderr, "bump: %s\n", errmsg);
+ exit(1);
+ }
+ while( optind < argc ){
+ /*printf("argv[%d] = (%s)\n", optind, argv[optind] );*/
+ nanny = read_active( fp, argv[optind] );
+ if( nanny == -1 ){
+ fprintf(stderr, "bump: Did not find name '%s'\n",
+ argv[optind] );
+ }
+ else{
+ if( kill( nanny, sig ) == -1 ){
+ if( errno == ESRCH ){
+ fprintf(stderr,"bump: Did not find nanny '%s', pid %d\n",
+ argv[optind], nanny );
+ if( clear_active( fp, nanny, &errmsg ) != 1 )
+ fprintf(stderr,"bump: %s\n", errmsg);
+ }
+ }
+ }
+ ++optind;
+ }
+ fclose( fp );
+
+ exit(0);
+}
+
+
+
+
+pid_t
+read_active( FILE *fp, char *name )
+{
+ char buf[1024];
+ pid_t nanny = -1;
+ char *n;
+ int len;
+
+ if( lock_file( fp, F_WRLCK, &errmsg ) == -1 ){
+ fprintf(stderr, "bump: %s\n", errmsg );
+ return(-1);
+ }
+ if( seek_file( fp, 0, SEEK_SET, &errmsg ) == -1 ){
+ fprintf(stderr, "bump: %s\n", errmsg );
+ return(-1);
+ }
+
+ len = strlen( name );
+ while(1){
+ if( fgets( buf, 1023, fp ) == NULL ){
+ /*printf("end of file\n");*/
+ break;
+ }
+
+ if( buf[0] == '#' ){
+ /*printf("skip line (%s)\n", buf );*/
+ continue;
+ }
+
+ /* get name */
+ if( (n = strchr( buf, ',' )) == NULL ){
+ /*printf("no comma (%s)\n", buf );*/
+ continue;
+ }
+
+ if( strncmp( n + 1, name, len ) != 0 ){
+ /*printf("not matching (%s) and (%s)\n",
+ n+1, name );*/
+ continue;
+ }
+
+ nanny = atoi( buf );
+ break;
+ }/* while */
+
+ if( lock_file( fp, F_UNLCK, &errmsg ) == -1 ){
+ fprintf(stderr, "bump: %s\n", errmsg );
+ return(-1);
+ }
+ return nanny;
+}
+
diff --git a/pan/pan.c b/pan/pan.c
new file mode 100644
index 0000000..c135d6e
--- /dev/null
+++ 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]);
+ }
+ }
+}
diff --git a/pan/splitstr.c b/pan/splitstr.c
new file mode 100644
index 0000000..4a63dff
--- /dev/null
+++ b/pan/splitstr.c
@@ -0,0 +1,91 @@
+/*
+ * 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: splitstr.c,v 1.1 2000/09/14 21:54:44 nstraz Exp $ */
+/*
+ * This is a heavily modified version of USC_parse_arg
+ *
+ * Synopsis
+ *
+ * char **splitstr(char *str, int *argcount, char *separator)
+ *
+ * Description
+ * This function splits a string (str) into components that are separated by
+ * one or more of the characters in the (separator) string. An array of
+ * strings is returned, along with argcount being set to the number of strings
+ * found.
+ *
+ * To rid yourself of the memory allocated for the string:
+ * free( SplitstrReturn[0] );
+ * free( SplitstrReturn );
+ *
+ *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/
+#include <stdio.h>
+#include <string.h> /* for string functions */
+
+#define ARG_ARRAY_SIZE 1000
+
+char **
+splitstr(char *str, int *argcount, char *separator)
+{
+ char *arg_string;
+ char *arg_array[ARG_ARRAY_SIZE+1], **aa;
+
+ int num_toks = 0; /* number of tokens found */
+
+ /* copy str to not destroy the original */
+ arg_string = (char*)malloc( strlen(str) + 1 );
+ strcpy( arg_string, str );
+
+ if(separator==NULL)
+ separator = " \t";
+
+ /*
+ * Use strtok() to parse 'arg_string', placing pointers to the
+ * individual tokens into the elements of 'arg_array'.
+ */
+ arg_array[num_toks] = strtok(arg_string, separator);
+ while ( num_toks < ARG_ARRAY_SIZE &&
+ (arg_array[++num_toks] = strtok(NULL, separator)) != NULL )
+ ;
+
+ arg_array[++num_toks] = NULL;
+
+ *argcount = num_toks-1;
+ aa = (char **) malloc(sizeof(char *) * num_toks);
+ memcpy(aa, arg_array, sizeof(char *) * num_toks);
+
+ /*
+ * Return the argument array.
+ */
+ return(aa);
+}
diff --git a/pan/zoolib.c b/pan/zoolib.c
new file mode 100644
index 0000000..174f338
--- /dev/null
+++ b/pan/zoolib.c
@@ -0,0 +1,352 @@
+/*
+ * 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: zoolib.c,v 1.1 2000/09/14 21:54:44 nstraz Exp $ */
+#include <stdlib.h> /* for getenv */
+#include "zoolib.h"
+
+#define A_BUF_SZ 100
+
+static char Errmsg[500];
+
+char *
+zoo_active()
+{
+ char *fname = "active";
+ static char *active = NULL;
+ char *zoo;
+
+ if( active == NULL ){
+ zoo = getenv( "ZOO" );
+ if( zoo != NULL ){
+ active = (char*)malloc( strlen(zoo) + 1 +
+ strlen(fname) + 1 );
+ sprintf( active, "%s/%s", zoo, fname );
+ }
+ }
+ return active;
+}
+
+
+FILE *
+open_file( char *file, char *mode, char **errmsg )
+{
+ FILE *fp;
+
+ if( errmsg != NULL )
+ *errmsg = Errmsg;
+
+ if( strcmp( mode, "r+" ) == 0 ){
+ /* make sure there's a file */
+ if( (fp = fopen( file, "a" )) == NULL ){
+ sprintf(Errmsg, "Unable to create file '%s'. %s/%d errno:%d %s\n",
+ file, __FILE__, __LINE__, errno, SYSERR );
+ return(NULL);
+ }
+ else
+ fclose( fp );
+ }
+
+
+ if( (fp = fopen( file, mode )) == NULL ){
+ sprintf(Errmsg, "Unable to open(%s,%s). %s/%d errno:%d %s\n",
+ file, mode, __FILE__, __LINE__, errno, SYSERR );
+ return(NULL);
+ }
+ return fp;
+}
+
+int
+seek_file( FILE *fp, long int offset, int whence, char **errmsg )
+{
+ if( errmsg != NULL )
+ *errmsg = Errmsg;
+
+ if( (whence != SEEK_SET) && (whence != SEEK_END) ){
+ sprintf(Errmsg, "whence is bad. %s/%d\n", __FILE__, __LINE__ );
+ return(-1);
+ }
+
+ if( fseek( fp, offset, whence ) != 0 ){
+ sprintf(Errmsg,"fseek(%ld,%s) failed. %s/%d errno:%d %s\n",
+ offset,
+ whence == SEEK_SET ? "SEEK_SET" : "SEEK_END",
+ __FILE__, __LINE__,
+ errno, SYSERR );
+ return(-1);
+ }
+ return whence;
+}
+
+
+int
+lock_file( FILE *fp, short ltype, char **errmsg )
+{
+ struct flock flk;
+ int ret=1;
+
+ if( errmsg != NULL )
+ *errmsg = Errmsg;
+
+ if( (ltype != F_WRLCK) && (ltype != F_UNLCK) ){
+ sprintf(Errmsg, "bad ltype, %s/%d\n", __FILE__, __LINE__ );
+ return(-1);
+ }
+
+ flk.l_whence = flk.l_start = flk.l_len = 0;
+ flk.l_type = ltype;
+/* XXX it's time to upgrade this code for sigaction */
+ sighold(SIGINT);
+ sighold(SIGTERM);
+ sighold(SIGHUP);
+ sighold(SIGUSR1);
+ sighold(SIGUSR2);
+ if( fcntl( fileno(fp), F_SETLKW, &flk ) == -1 ){
+ sprintf(Errmsg, "fcntl(%s) failed. %s/%d errno:%d %s\n",
+ ltype == F_WRLCK ? "F_WRLCK" : "F_UNLCK",
+ __FILE__, __LINE__,
+ errno, SYSERR );
+ ret=-1;
+ }
+ sigrelse(SIGINT);
+ sigrelse(SIGTERM);
+ sigrelse(SIGHUP);
+ sigrelse(SIGUSR1);
+ sigrelse(SIGUSR2);
+ return(ret);
+}
+
+
+int
+write_active( FILE *fp, char *name, char **errmsg )
+{
+ pid_t pid = getpid();
+ struct flock flk;
+ char *emsg;
+
+ if( fp == NULL )
+ return(1);
+
+ if( errmsg != NULL )
+ *errmsg = Errmsg;
+
+ if( lock_file( fp, F_WRLCK, &emsg ) == -1 )
+ return(-1);
+ if( seek_file( fp, 0, SEEK_END, &emsg ) == -1 )
+ return(-1);
+
+ /* Write pid first so update_active() can find it quickly.
+ * update_active() will probably be used far more often than any
+ * process that needs to search by name anyway.
+ */
+ fprintf( fp, "%d,%s\n", pid, name );
+ fflush( fp );
+
+ if( lock_file( fp, F_UNLCK, &emsg ) == -1 )
+ return(-1);
+ return(1);
+}
+
+/*
+ * Write a command to the active file, with arguments.
+ * Uses a "first fit" algorithm to take the first available line
+ * Inactive commandlines are assumed to start with a "#"
+ */
+int
+write_active_args( FILE *fp, pid_t pid, char *name, int argc,
+ char **argv, char **errmsg )
+{
+ struct flock flk;
+ char *args, *cat_args();
+ char active[81]; /* max length.. */
+ int len, l2, found;
+ long int pos;
+ char buf[A_BUF_SZ];
+ char *emsg;
+
+ if( fp == NULL )
+ return(1);
+
+ if( errmsg != NULL )
+ *errmsg = Errmsg;
+
+ if( (args = cat_args(argc, argv, &emsg)) == NULL )
+ return(-1);
+ len = sprintf(active, "%d,%s,", pid, name);
+ strncat(active, args, 80 - len);
+ len = strlen(active);
+ free(args);
+
+ if( lock_file( fp, F_WRLCK, &emsg ) == -1 )
+ return(-1);
+ if( seek_file( fp, 0, SEEK_SET, &emsg ) == -1 )
+ return(-1);
+
+ found=0;
+ while(1) {
+ pos = ftell( fp );
+ if( fgets( buf, A_BUF_SZ - 1, fp ) == NULL )
+ break;
+
+ if( buf[0] == '#' ) {
+ /* "First Fit" */
+ if( (l2=strlen(buf)) > len ) { /* can rewrite this one */
+ if( seek_file( fp, pos, SEEK_SET, &emsg ) == -1 )
+ return(-1);
+ fprintf( fp, "%s%*.*s\n", active, l2-len-1, l2-len-1,
+ "|");
+ found=1;
+ break;
+ }
+ }
+ }/* while */
+
+ if(!found) {
+ if( seek_file( fp, 0, SEEK_END, &emsg ) == -1 )
+ return(-1);
+
+ /* Write pid first so update_active() can find it quickly.
+ * update_active() will probably be used far more often than any
+ * process that needs to search by name anyway.
+ */
+ fprintf( fp, "%s\n", active );
+ }
+
+ fflush( fp );
+ if( lock_file( fp, F_UNLCK, &emsg ) == -1 )
+ return(-1);
+
+ return(1);
+}
+
+
+int
+clear_active( FILE *fp, pid_t me, char **errmsg )
+{
+ char buf[A_BUF_SZ];
+ int pid;
+ long int pos;
+ int found = 0;
+ char *emsg;
+
+ if( fp == NULL )
+ return(1);
+
+ if( lock_file( fp, F_WRLCK, &emsg ) == -1 )
+ return(-1);
+ if( seek_file( fp, 0, SEEK_SET, &emsg ) == -1 )
+ return(-1);
+
+ while(1){
+ pos = ftell( fp );
+ if( fgets( buf, A_BUF_SZ - 1, fp ) == NULL )
+ break;
+
+ if( buf[0] == '#' )
+ continue;
+
+ /* is pid me? */
+ pid = atoi( buf );
+ if( pid != me )
+ continue;
+
+ if( seek_file( fp, pos, SEEK_SET, &emsg ) == -1 )
+ return(-1);
+ fprintf( fp, "#" );
+ found++;
+ break;
+ }/* while */
+
+ fflush( fp );
+ if( lock_file( fp, F_UNLCK, &emsg ) == -1 )
+ return(-1);
+
+ if( ! found ){
+ sprintf(Errmsg, "clear_active() did not find pid(%d). %s/%d\n",
+ me, __FILE__, __LINE__ );
+ return(0);
+ }
+ return(1);
+}
+
+
+void
+wait_handler( int sig )
+{
+ static int lastsent = 0;
+
+ if( sig == 0 ){
+ lastsent = 0;
+ }
+ else {
+ rec_signal = sig;
+ if( sig == SIGUSR2 )
+ return;
+ if( lastsent == 0 )
+ send_signal = sig;
+ else if( lastsent == SIGUSR1 )
+ send_signal = SIGINT;
+ else if( lastsent == sig )
+ send_signal = SIGTERM;
+ else if( lastsent == SIGTERM )
+ send_signal = SIGHUP;
+ else if( lastsent == SIGHUP )
+ send_signal = SIGKILL;
+ lastsent = send_signal;
+ }
+}
+
+char *
+cat_args(int argc, char **argv, char **errmsg)
+{
+ int a, size;
+ char *cmd;
+
+ for(size=a=0;a<argc;a++) {
+ size += strlen(argv[a]);
+ size++;
+ }
+
+ if( (cmd = (char *)malloc(size)) == NULL ) {
+ sprintf(Errmsg, "Malloc Error, %s/%d", __FILE__, __LINE__);
+ return(NULL);
+ }
+
+ *cmd='\0';
+ for(a=0;a<argc;a++) {
+ if(a != 0)
+ strcat(cmd, " ");
+ strcat(cmd, argv[a]);
+ }
+
+ return(cmd);
+}
diff --git a/pan/zoolib.h b/pan/zoolib.h
new file mode 100644
index 0000000..6c76a9c
--- /dev/null
+++ b/pan/zoolib.h
@@ -0,0 +1,71 @@
+/*
+ * 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: zoolib.h,v 1.1 2000/09/14 21:54:44 nstraz Exp $ */
+#ifndef ZOOLIB_H
+#define ZOOLIB_H
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/signal.h>
+
+#ifdef NANNY
+#define EXTERN
+#else
+#define EXTERN extern
+#endif
+
+EXTERN int rec_signal; /* received signal */
+EXTERN int send_signal; /* signal to send */
+
+extern int errno;
+#ifndef linux
+extern char *sys_errlist[];
+#endif
+#define SYSERR sys_errlist[errno]
+
+int lock_file( FILE *fp, short ltype, char **errmsg );
+FILE *open_file( char *file, char *mode, char **errmsg );
+
+void wait_handler();
+
+char *zoo_active( void );
+int write_active( FILE *fp, char *name, char **errmsg );
+int clear_active( FILE *fp, pid_t me, char **errmsg );
+int write_active_args( FILE *fp, pid_t pid, char *name, int argc, char **argv, char **errmsg );
+int seek_file( FILE *fp, long int offset, int whence, char **errmsg );
+char *cat_args(int argc, char **argv, char **errmsg);
+
+#endif /* ZOOLIB_H */
diff --git a/runpan.sh b/runpan.sh
new file mode 100755
index 0000000..8bdeadf
--- /dev/null
+++ b/runpan.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+# This will only run the quickhit tests.
+cd `dirname $0`
+LTPROOT=${PWD}
+
+mkdir /tmp/runalltests-$$
+cd /tmp/runalltests-$$
+
+export PATH="${PATH}:${LTPROOT}/doio:${LTPROOT}/tests"
+
+${LTPROOT}/pan/pan -e -S -a $$ -n $$ -f ${LTPROOT}/runtest/quickhit
+
+if [ $? -eq "0" ]; then
+ echo pan reported PASS
+else
+ echo pan reported FAIL
+fi
+
+rm -rf /tmp/runalltests-$$
diff --git a/tests/alarm02.c b/tests/alarm02.c
index e5cb0bb..425fb80 100644
--- a/tests/alarm02.c
+++ b/tests/alarm02.c
@@ -29,7 +29,7 @@
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*/
-/* $Id: alarm02.c,v 1.3 2000/08/30 18:43:38 nstraz Exp $ */
+/* $Id: alarm02.c,v 1.4 2000/09/14 21:54:44 nstraz Exp $ */
/**********************************************************
*
* OS Test - Silicon Graphics, Inc.
@@ -46,10 +46,6 @@
*
* CPU TYPES : ALL
*
- * BINARY LOCATION : CUTS_BIN/rf_tests/sys
- *
- * SOURCE LOCATION : CUTS_SRC/src/tests/sys
- *
* AUTHOR : Billy Jean Horne
*
* CO-PILOT : Kathy Olmsted
@@ -82,7 +78,7 @@
* Loop for each test case.
* Execute alarm (0) system call to clear previous alarm.
* Check return code, if system call failed (return=-1)
- * Issue a BROK message and exit the test.
+ * Issue a FAIL message and exit the test.
* Call alarm() with boundary values for seconds.
* Verify that returned value is as expected.
* Report results.