[net.sources] A version of zap . . .

paws@sphinx.UChicago.UUCP (Randy Smith) (05/05/85)

    I figured that as long as people were posting new versions of K&P program
  I'd post a version of zap which I recently finished writing.  It has a few
  new features:

    1) It allows the super-user to look at all processes on the system,
    2) If a string argument is provided it only gives processes with that
       string in their names.
    3) By default it doesnot show shells, the zap process itself, or the
       ps process that zap runs to find out what to kill.
    4) You can force zap to kill everything you've selected without asking,
       although this is not recommended.

    If anyone finds any bugs, please tell me about them.  Much thanks.

                                -- Randy Smith
#:==============================================================
#: To unbundle, "sh" this file -- DO NOT use csh
#: The files are in SHAR archive format.
#: Archive created by paws Sat May 4 18:16:53 CDT 1985
echo x - zap.1
sed 's/^X//' > zap.1 <<'+FUNKY+STUFF+'
X.TH ZAP 1-ucb
X.UC 4
X.SH NAME
Xzap \- interactive process killer
X.SH SYNOPSIS
X.B zap
X[
X.B \-# \-svf
X]
X[process_string]
X.SH DESCRIPTION
X.I Zap 
Xinteractively `zaps' processes with the
Xspecified string in their names, using the specified signal (if given). The
Xdefault signal is the SIGTERM (terminate) signal.  The processes
Xdisplayed are all of the process owned by the user that match the string
Xexcept for shells and
Xprocesses used by zap itself.
XIf you are the super-user, all processes on the system that match the specified
Xstring are offered.
X.PP
XTo select a particular
Xprocess for termination, the user enters 'y' to the prompt.
XAny other response skips that process and continues to the next one.
X.PP
XThe options for this command are:
X.TP 5
X.B #
Xis the signal number to send to those processes you select.  The default
Xis SIGTERM.
X.TP 5
X.B s
Xasks for the shells owned by the user to be shown also.
X.TP 5
X.B v
Xasks that all processes
X.I except
Xthose matching the specified string be displayed.
X.TP 5
X.B f
Xasks that all process selected be killed without confirmation (dangerous).
X.SH "SEE ALSO"
Xcsh(1), ps(1), kill(1), kill(2), sigvec(2)
X.SH BUGS
XTo be found.  Send bug reports to ...ihnp4!gargoyle!sphinx!paws
X
X
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws         1218 May  4 18:16 zap.1    (as sent)'
chmod u=rw,g=r,o=r zap.1
ls -l zap.1
echo x - Makefile
sed 's/^X//' > Makefile <<'+FUNKY+STUFF+'
XCFILES = zap.c ttyin.c strindex.c efopen.c extpopen.c
X
Xzap:	zap.o ttyin.o strindex.o efopen.o extpopen.o Makefile
X	cc -o zap zap.o ttyin.o efopen.o strindex.o extpopen.o
X	chmod a+x zap
X
Xprint:	${CFILES} Makefile
X	qprint $? &
X	touch print
X
Xinstall: zap
X	 mv zap /u1/paws/bin
X	 nroff -man  zap.1 > /u1/paws/man/zap
X	 chmod a+x /u1/paws/bin/zap
X
X
X
X
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws          346 May  3 15:11 Makefile    (as sent)'
chmod u=rw,g=r,o=r Makefile
ls -l Makefile
echo x - efopen.c
sed 's/^X//' > efopen.c <<'+FUNKY+STUFF+'
X/* efopen.c 
X * Taken directly from Kernighan and Pike
X */
X
X#include <stdio.h>
X
XFILE *efopen(file,mode)
Xchar *file,*mode;
X{
X   FILE *fp, *fopen();
X   extern char *progname;
X
X   if ((fp = fopen(file,mode)) != NULL)
X      return fp;
X   fprintf(stderr,"%s: can't open file %s mode %s\n",
X      progname,file,mode);
X   exit(1);
X}
X
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws          327 May  4 18:06 efopen.c    (as sent)'
chmod u=rw,g=r,o=r efopen.c
ls -l efopen.c
echo x - extpopen.c
sed 's/^X//' > extpopen.c <<'+FUNKY+STUFF+'
X#include <stdio.h>
X
X/*
X * A subroutine to create a process and connect either its output
X * (mode="r") or its input (mode="w") to the file pointer returned by
X * the routine.  
X * It also stores the process id of the process created in the location
X * pointed to by pid.
X */
X
XFILE *extpopen(efile,args,mode,pid)
Xchar *efile;		/* File to be execed by child process */
Xchar *args[];		/* Arguments to be passed to child process */
Xchar *mode;		/* Mode of opening of process; "r" is read from
X			 * process created, "w" is to write to process
X			 * created
X			 */
Xint *pid;		/* Location to store process id of child in */
X{
X    int pipfd[1];	/* holder for file descriptors returned by pipe */
X    int redif;		/* stdio file descriptor to be redifined */
X    FILE *fdopen();	/* Declare funtions used. */
X    
X    if (0 != pipe(pipfd)) 	/* Create pipeline */
X        return NULL;
X
X    if (strcmp(mode,"r") && strcmp(mode,"w"))
X        return NULL;			/* If mode is out of bounds, return */
X    
X    redif = !strcmp(mode,"r");		/* Get mode to be reset. */
X					/* 0=stdin, 1=stdout	 */
X
X    if (*pid = fork()) {		/* Create child */
X    					/*
X					 * In parent, close child's side
X					 * of pipe and return a file pointer
X					 * off of parents side of pipe.
X					 */
X        close(redif ? pipfd[1] : pipfd [0]);
X        return fdopen((redif ? pipfd[0] : pipfd[1]),mode);
X    }
X    else {				/* In child, redifine std{in,out} */
X	if (!dup2((redif ? pipfd[1] : pipfd[0]),redif))	
X	    exit(1);
X	close(pipfd[0]); close(pipfd[1]);	/* Close loose ends */
X	execv(efile,args);			/* Exec new program */
X	return(NULL);		/* To satisy lint; will never reach */
X    }
X}
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws         1652 May  4 16:12 extpopen.c    (as sent)'
chmod u=rw,g=r,o=r extpopen.c
ls -l extpopen.c
echo x - strindex.c
sed 's/^X//' > strindex.c <<'+FUNKY+STUFF+'
X/* strindex -- return index of t in s, -1 if none */
X/* Taken directly from Kerrnigan and Pike */
X
Xstrindex(s,t)
Xchar *s,*t;
X{
X    int i, n;
X
X    n = strlen(t);
X    for (i = 0; s[i] != '\0'; i++)
X	if (strncmp(s+i,t,n) == 0)
X  	    return i;
X    return -1;
X}
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws          258 May  4 18:06 strindex.c    (as sent)'
chmod u=rw,g=r,o=r strindex.c
ls -l strindex.c
echo x - ttyin.c
sed 's/^X//' > ttyin.c <<'+FUNKY+STUFF+'
X/* ttyin.c 
X * Taken directly from Kernighan and Pike
X */
X
X#include <stdio.h>
X
Xttyin() /* process response from /dev/tty */
X{
X   char buf[BUFSIZ];
X   FILE *efopen();
X   static FILE *tty = NULL;
X
X   if (tty == NULL)
X      tty = efopen("/dev/tty","r");
X   for (;;) {
X      if (fgets(buf,BUFSIZ,tty) == NULL || buf[0] == 'q')
X	 exit(0);
X      else if (buf[0] == '!') {
X	 system(buf+1); /* possible bug here */
X	 fprintf(stderr,"!\n");
X      }
X      else  /* ordinary line */
X         return buf[0];
X   }
X}
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws          503 May  4 18:06 ttyin.c    (as sent)'
chmod u=rw,g=r,o=r ttyin.c
ls -l ttyin.c
echo x - zap.c
sed 's/^X//' > zap.c <<'+FUNKY+STUFF+'
X/* zap: interactive process killer
X * Lovingly stolen from Kernighan and Pike, then
X *   slowly subjected to creeping featurism by Randy Smith.
X */
X
X#define TRUE 1
X#define FALSE 0
X
X/* The beginning of the program's name in a 'ps -g' format output */
X#define COMFIELD 21
X
X#include <stdio.h>
X#include <signal.h>
X#include <ctype.h>
X
Xchar *progname;	      /* Name of this program */
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X   FILE *fin, *extpopen();    /* pointer to ps output and function called
X                                 for creating ps process and getting pointer */
X   char buf[1024];	      /* Buffer to store each line of output from ps */
X   char *ps = "/bin/ps";      /* Location of ps */
X   char *ps_args ;	      /* Pointer to argument for ps */
X   char *process_args[2] ;    /* Pointer to arguments in correct format for
X                                 extpopen()  */
X   int mysignal = SIGTERM;    /* Signal number; default = Terminate */
X   int pid, mypid, ps_pid;    /* Current process being examined, me, ps */
X			      /* Flags for:  */
X   int shell=FALSE;	      /*      Show shells also? */
X   int noask=FALSE;	      /*      Don't ask before killing? */
X   int revshow=FALSE;         /*      Show processes that DON'T match arg? */
X
X   mypid = getpid();
X   progname = argv[0];
X
X   if (geteuid() == 0) /* Root can kill any process. Others can't */
X	ps_args = "-ag";
X   else
X	ps_args = "-g";
X
X   process_args[0] = "ps";	/* Set args for extpopen() */
X   process_args[1] = ps_args;
X   process_args[2] = NULL;
X
X   argc--; argv++;
X
X   while (**argv == '-') {
X       (*argv)++;
X       if isdigit(**argv) {		/* Set signal number */
X	  sscanf(*argv,"%d",&mysignal);
X          if ((mysignal <= 0) || (mysignal > 27)) {  /* bad signal number */
X	      fprintf(stderr,"%s: bad signal number %d\n",progname,mysignal);
X              exit(1);
X          }
X       }
X       else				/* Set other flags */
X           while (**argv != '\0')
X	       switch (* (*argv)++) {
X		   case 's': shell=TRUE;
X		             break;
X		   case 'v': revshow=TRUE;
X		             break;
X		   case 'f': noask=TRUE;
X		             break;
X		   default:  fprintf(stderr,"invalid option in %s\n",progname);
X		             exit(1);
X	       }
X	   argv++; argc--;
X   }
X
X   if ((fin = extpopen(ps,process_args,"r",&ps_pid)) == NULL) {  /* open ps */
X      fprintf(stderr,"%s: can't run %s in process id #%d\n",progname, ps,ps_pid);
X      exit(1);
X   }
X
X   fgets(buf,sizeof(buf),fin); /* get rid of header line from ps */
X   if (!noask)
X      fprintf(stderr,"%s",buf);
X   while (fgets(buf,sizeof(buf),fin) != NULL) {
X      sscanf(buf,"%d",&pid);
X
X/*
X * This next little logical quagmire offers a process up if:
X *    1) It is not zap itself, and
X *    2) It is not the ps run by zap, and
X *    3)   a) There are no arguments left in the string, or
X *         b) revshow is TRUE and the first argument isn't in the ps string, or
X *         c) revshow is FALSE and the first argument is in the ps string.
X *  
X * In testing to see whether an argument is in the ps string, we check it 
X * against only the last field of the output from ps, which starts at
X * location COMFIELD.
X */
X
X      if ((pid != mypid) &&
X          (pid != ps_pid) && 
X          (!argc ||
X	   (revshow && !(strindex(buf+COMFIELD,*argv) >= 0))||
X	   (!revshow && (strindex(buf+COMFIELD,*argv) >= 0))) &&  
X	  (shell || !isshell(buf))                                ) {
X	 buf[strlen(buf)-1] = '\0'; /* suppress '\n' */
X         if (!noask)
X               fprintf(stderr,"%s? ",buf);
X	 if (noask || (ttyin() == 'y'))
X 	       kill(pid,mysignal);
X      }
X   }
X   exit(0);
X}
X
X/*
X * A short subroutine to find out if the program is a shell or not
X */
X
Xint isshell(program)
Xchar *program;
X{
X    return((strindex(program,"(tcsh)") >= 0)  ||
X           (strindex(program,"(csh)" ) >= 0)  ||
X           (strindex(program,"(sh)"  ) >= 0)    );
X}
X
X	  
+FUNKY+STUFF+
echo '-rw-r--r--  1 paws         3884 May  4 16:17 zap.c    (as sent)'
chmod u=rw,g=r,o=r zap.c
ls -l zap.c
exit 0