Newsgroups: comp.sources.unix From: jeff@forys.cranbury.nj.us (Jeff Forys) Subject: v28i181: skill - signal or reprioritize specified processes, V3.6, Patch02 Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: jeff@forys.cranbury.nj.us (Jeff Forys) Posting-Number: Volume 28, Issue 181 Archive-Name: skill/patch02 Skill Version 3.6, Patch #2 [affects Solaris 5.3, OSF/1, Mach 3] - [Solaris] Fixes signal overrun problem in "skill -l". - Support for DEC OSF/1, and better Mach-3 support in general. - Adds support for varying numbers of sigs/line in "skill -l". - A question mark ("?") is now categorized as a tty, and will match processes without controlling ttys. Useful for killing orphaned "elm" processes, etc. Thanks to Lee Duncan of Sun for the Solaris and signal fixes, Allan Johannesen of WPI for the Mach-3 OSF/1 support, and "That Whispering Wolf" for the clever "?" idea. To apply this patch, "cd" to your skill source directory and run: patch -p0 0) { ! if ((FLAGS & PI_CTLTTY) == 0) ! continue; /* no controlling tty */ ! for (i = 0; i < TtyIndx && *(TtyList+i) != TTY; i++) ! ; if (i == TtyIndx) /* no matching tty */ continue; } --- 78,97 ---- while ((proc = GetProc()) != NULL) { if (TtyIndx > 0) { ! tty_T *ttyptr; ! /* ! * For tty's, "-t ?" specifies that you are looking ! * for processes without controlling terminals. ! * In this case, the controlling tty will be (-2). ! */ ! for (i = 0; i < TtyIndx; i++) { ! ttyptr = TtyList + i; ! if (*ttyptr == TTY || ! (*ttyptr == (tty_T)-2 && ! (FLAGS & PI_CTLTTY) == 0)) ! break; ! } if (i == TtyIndx) /* no matching tty */ continue; } *** /tmp/T0a003UV Mon Aug 22 22:45:11 1994 --- skill.1 Mon Aug 22 22:43:01 1994 *************** *** 1,5 **** .\" ! .\" $Header: skill.1,v 1.9 1994/07/12 15:02:35 forys Exp $ .\" .\" skill - send signals to processes by tty, user name, command or proc id. .\" skill - change process priorities by tty, user name, command or proc id. --- 1,5 ---- .\" ! .\" $Header: skill.1,v 1.10 1994/08/23 02:38:57 forys Exp $ .\" .\" skill - send signals to processes by tty, user name, command or proc id. .\" skill - change process priorities by tty, user name, command or proc id. *************** *** 9,15 **** .\" .\" Copyright 1994 by Jeff Forys (jeff@forys.cranbury.nj.us) .\" ! .TH SKILL 1 "July 15, 1994" "" "Local UNIX Programmer's Manual" .UC 4 .SH NAME skill, snice \- signal or reprioritize specified processes --- 9,15 ---- .\" .\" Copyright 1994 by Jeff Forys (jeff@forys.cranbury.nj.us) .\" ! .TH SKILL 1 "August 22, 1994" "" "Local UNIX Programmer's Manual" .UC 4 .SH NAME skill, snice \- signal or reprioritize specified processes *************** *** 117,122 **** --- 117,124 ---- .B forced to a particular type by preceding it with "-c" (command), "-u" (user), "-t" (tty), or "-p" (process id). + As a special case, a question mark ("?") is categorized as a tty, and + will match processes without controlling ttys. .PP Process id's are not known beforehand, so both kernel memory and the swap device must be searched for the required information. Alternately, *** /tmp/T0a003ZQ Tue Aug 23 01:28:47 1994 --- argparse.c Tue Aug 23 01:28:19 1994 *************** *** 1,5 **** #ifndef lint ! static char rcsid[] = "$Header: argparse.c,v 1.7 1994/06/26 04:09:17 forys Exp $"; #endif /* --- 1,5 ---- #ifndef lint ! static char rcsid[] = "$Header: argparse.c,v 1.10 1994/08/23 05:27:43 forys Exp $"; #endif /* *************** *** 28,33 **** --- 28,41 ---- int Fflag = 0, Iflag = 0, Nflag = 0, Vflag = 0, Wflag = 0; /* + * Because some vendors now have long symbolic signal names, "kill -l" may + * display fewer than 16 signals per line. Since 16 is the default, we set + * it here; the machine-dependent initialization routine -- MdepInit() -- + * may change it so that "skill -l" properly mimics "kill -l". + */ + int SigsPerLine = 16; + + /* * ArgParse(argc, argv) * * Destructively parse argv[] into appropriate global variables. *************** *** 178,184 **** Usage(E_USAGE); /* no arg following flag */ if (!help || help == 't') { /* tty? */ ! if (stat(*argv,&st)>=0&&(st.st_mode&S_IFMT)==S_IFCHR) { if (TtyIndx == 0) ALLOC(argcnt, tty_T *, TtyList, "tty"); *(TtyList + TtyIndx++) = st.st_rdev; --- 186,194 ---- Usage(E_USAGE); /* no arg following flag */ if (!help || help == 't') { /* tty? */ ! st.st_rdev = (dev_t)-2; /* default: no ctrling term */ ! if ((**argv == '?' && *(*argv + 1) == '\0') || ! (stat(*argv,&st)>=0&&(st.st_mode&S_IFMT)==S_IFCHR)){ if (TtyIndx == 0) ALLOC(argcnt, tty_T *, TtyList, "tty"); *(TtyList + TtyIndx++) = st.st_rdev; *************** *** 244,254 **** for (signo = 1; signo <= NSig; signo++) if (!isdigit(*SigMap[signo])) { ! printf("%s%c", SigMap[signo], /* 16 signals/line */ ! (++didprint % 16) == 0? '\n': ' '); } ! if ((didprint % 16) != 0) (void) putc('\n', stdout); exit(EX_OKAY); --- 254,264 ---- for (signo = 1; signo <= NSig; signo++) if (!isdigit(*SigMap[signo])) { ! printf("%s%c", SigMap[signo], /* signals/line */ ! (++didprint % SigsPerLine) == 0? '\n': ' '); } ! if ((didprint % SigsPerLine) != 0) (void) putc('\n', stdout); exit(EX_OKAY); *** /tmp/T0a002aZ Sun Aug 21 20:08:09 1994 --- Makefile Sun Aug 21 18:22:35 1994 *************** *** 1,5 **** # ! # $Header: Makefile,v 1.21 1994/07/14 03:20:37 forys Exp $ # # First, find the CONFIGURE comments and set up the "BIN*" and "MAN*" # defines according to local customs. Next, if your operating system --- 1,5 ---- # ! # $Header: Makefile,v 1.22 1994/08/21 22:18:21 forys Exp $ # # First, find the CONFIGURE comments and set up the "BIN*" and "MAN*" # defines according to local customs. Next, if your operating system *************** *** 17,23 **** # Berkeley 4.3BSD bsd-43 # Berkeley 4.4BSD bsd-44 LIBS= -lkvm # CMU Mach 2.6 mach-26 LIBS= -lsys ! # CMU Mach 3.0 mach-26 # DEC Ultrix 2.2 ultrix-22 # DEC Ultrix 4.2 ultrix-4 # Encore UMAX 4.2 umax-42 COPTS= -DNO_UID_T --- 17,24 ---- # Berkeley 4.3BSD bsd-43 # Berkeley 4.4BSD bsd-44 LIBS= -lkvm # CMU Mach 2.6 mach-26 LIBS= -lsys ! # CMU Mach 3.0 mach-30 install setuid root ! # DEC OSF/1 3.0 mach-30 install setuid root # DEC Ultrix 2.2 ultrix-22 # DEC Ultrix 4.2 ultrix-4 # Encore UMAX 4.2 umax-42 COPTS= -DNO_UID_T *** /tmp/T0a002ah Sun Aug 21 20:08:38 1994 --- README Sun Aug 21 18:29:57 1994 *************** *** 1,4 **** ! $Header: README,v 1.25 1994/07/14 03:19:56 forys Exp $ `skill' is a program which sends signals to processes given any combination of user names, ttys, commands, and pids. `snice' is --- 1,4 ---- ! $Header: README,v 1.26 1994/08/21 22:29:21 forys Exp $ `skill' is a program which sends signals to processes given any combination of user names, ttys, commands, and pids. `snice' is *************** *** 17,22 **** --- 17,23 ---- 4.2BSD, 4.3BSD, 4.4BSD (and many PC variants) Mach 2.6, 3.0 DEC Ultrix 2.2, 4.1-2 + DEC AXP OSF/1 Encore UMAX 4.2 HP-UX 6.5, 7.0, 8.0, 9.0-1 IBM AIX 3.1-2 *************** *** 43,48 **** --- 44,55 ---- both programs will still function effectively without it. At any rate, do not install either program setuid root unless you *really* trust your user community! + + The one exception to this rule is when installing on a Mach-3 based + Operating System. Here, you will likely need to install skill/snice + setuid root. Under this OS, the machine-dependent code will reset + the uid back to the "real" user before any processes are acted on. + In other words, for "mach-3", it is safe to install setuid root. Dale Wyttenbach noted that with a slight modification (strcmp() -> strstr()), one can use this simple *** /tmp/T0a002ap Sun Aug 21 20:09:09 1994 --- Config Sun Aug 21 18:21:31 1994 *************** *** 1,6 **** #!/bin/sh # ! # $Header: Config,v 1.14 1994/06/26 03:17:00 forys Exp $ # # Configure - edit skill Makefile filling in OSTYPE, COPTS, and LIBS. # Contributed by: Ric Anderson --- 1,6 ---- #!/bin/sh # ! # $Header: Config,v 1.15 1994/08/21 22:20:20 forys Exp $ # # Configure - edit skill Makefile filling in OSTYPE, COPTS, and LIBS. # Contributed by: Ric Anderson *************** *** 64,70 **** ;; mach3) ! OSTYPE=mach-26 ;; next1) --- 64,70 ---- ;; mach3) ! OSTYPE=mach-3 ;; next1) *** /tmp/T0a002ay Sun Aug 21 20:09:32 1994 --- GuessOS Sun Aug 21 18:21:55 1994 *************** *** 1,6 **** #!/bin/sh # ! # $Header: GuessOS,v 1.12 1994/06/26 03:17:24 forys Exp $ # # GuessOS - guess the OS type for skill. # Contributed by: Ric Anderson --- 1,6 ---- #!/bin/sh # ! # $Header: GuessOS,v 1.13 1994/08/21 22:19:48 forys Exp $ # # GuessOS - guess the OS type for skill. # Contributed by: Ric Anderson *************** *** 35,40 **** --- 35,43 ---- OS=next2 ;; *"Mach 3"*) + OS=mach3 + ;; + *"OSF/1"*) OS=mach3 ;; *"4.3 BSD UNIX"*) *** /tmp/T0a002bB Sun Aug 21 20:10:12 1994 --- machdep/sys-5r4.c Mon Aug 15 14:37:06 1994 *************** *** 1,5 **** #ifndef lint ! static char rcsid[] = "$Header: sys-5r4.c,v 1.10 1994/06/26 04:19:28 forys Exp $"; #endif /* --- 1,5 ---- #ifndef lint ! static char rcsid[] = "$Header: sys-5r4.c,v 1.11 1994/08/15 18:36:26 forys Exp $"; #endif /* *************** *** 36,50 **** * idea as to what NSIG should be. Here, `NSig' is the number of * signals available, not counting zero. */ char *SigMap[] = { "0", ! "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", /* 1 - 6 */ "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */ "PIPE", "ALRM", "TERM", "USR1", "USR2", "CHLD", /* 13 - 18 */ "PWR", "WINCH", "URG", "POLL", "STOP", "TSTP", /* 19 - 24 */ "CONT", "TTIN", "TTOU", "VTALRM", "PROF", "XCPU", /* 25 - 30 */ ! "XFS2", "WAITING", "LWP" /* 31 - 33 */ }; int NSig = NSIG-1; #define SETCMD(dst,src,maxlen) { \ extern char *strrchr(); \ --- 36,64 ---- * idea as to what NSIG should be. Here, `NSig' is the number of * signals available, not counting zero. */ + #ifdef sun char *SigMap[] = { "0", ! "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", /* 1 - 6 */ "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */ + "PIPE", "ALRM", "TERM", "USR1", "USR2", "CLD", /* 13 - 18 */ + "PWR", "WINCH", "URG", "POLL", "STOP", "TSTP", /* 19 - 24 */ + "CONT", "TTIN", "TTOU", "VTALRM", "PROF", "XCPU", /* 25 - 30 */ + "XFSZ", "WAITING", "LWP", "FREEZE", "THAW", "RTMIN", /* 31 - 36 */ + "RTMIN+1", "RTMIN+2", "RTMIN+3", "RTMAX-3", "RTMAX-2", /* 37 - 41 */ + "RTMAX-1", "RTMAX" /* 42 - 43 */ + }; + int NSig = NSIG-1; + #else + char *SigMap[] = { "0", + "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", /* 1 - 6 */ + "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */ "PIPE", "ALRM", "TERM", "USR1", "USR2", "CHLD", /* 13 - 18 */ "PWR", "WINCH", "URG", "POLL", "STOP", "TSTP", /* 19 - 24 */ "CONT", "TTIN", "TTOU", "VTALRM", "PROF", "XCPU", /* 25 - 30 */ ! "XFSZ", "WAITING", "LWP" /* 31 - 33 */ }; int NSig = NSIG-1; + #endif #define SETCMD(dst,src,maxlen) { \ extern char *strrchr(); \ *************** *** 79,84 **** --- 93,103 ---- char *pname; { extern char *rindex(), *SysErr(); + #ifdef sun + extern int SigsPerLine; + + SigsPerLine = 8; + #endif MyPid = (pid_T) getpid(); MyUid = (uid_T) getuid(); *** /dev/null Sun Aug 21 18:30:28 1994 --- machdep/mach-3.c Sun Aug 21 18:23:26 1994 *************** *** 0 **** --- 1,243 ---- + #ifndef lint + static char rcsid[] = "$Header: mach-3.c,v 1.1 1994/08/21 22:13:45 forys Exp $"; + #endif + + /* + ** This program may be freely redistributed for noncommercial purposes. + ** This entire comment MUST remain intact. + ** + ** Copyright 1994 by Jeff Forys (jeff@forys.cranbury.nj.us) + */ + + #define NO_MEXTERN + #include "conf.h" + #undef NO_MEXTERN + + #include + #include + + #include + + /* + * Define SigNames, NSig, and TtyDevDir here; they are used by other + * routines and must be global. Everyone seems to have their own + * idea as to what NSIG should be. Here, `NSig' is the number of + * signals available, not counting zero. + */ + char *SigMap[] = { "0", + "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", /* 1 - 6 */ + "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */ + "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", /* 13 - 18 */ + "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", /* 19 - 24 */ + "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", /* 25 - 30 */ + "USR2", "32", /* 31 - 32 */ + }; + int NSig = NSIG - 1; + + #define SETCMD(dst,src,maxlen) { \ + extern char *rindex(); \ + if (maxlen > 0) src[maxlen] = '\0'; \ + dst = (dst = rindex(src, '/')) ? ++dst: src; \ + } + + static char *TtyDevDir = "/dev"; + + int Skill; /* set 1 if running `skill', 0 if `snice' */ + int PrioMin, PrioMax; /* min and max process priorities */ + int SigPri; /* signal to send or priority to set */ + pid_T MyPid; /* pid of this process */ + uid_T MyUid; /* uid of this process */ + char *ProgName; /* program name */ + + /* + * This is the machine-dependent initialization routine. + * + * - The following global variables must be initialized: + * MyPid, MyUid, ProgName, Skill, PrioMin, PrioMax, SigPri + * - The working directory will be changed to that which contains the + * tty devices (`TtyDevDir'); this makes argument parsing go faster. + * - If possible, this routine should raise the priority of this process. + */ + void + MdepInit(pname) + char *pname; + { + extern char *rindex(), *SysErr(); + + MyPid = (pid_T) getpid(); + MyUid = (uid_T) getuid(); + SETCMD(ProgName, pname, 0) + + /* + * If we are running as root, raise our priority to better + * catch runaway processes. + */ + if (MyUid == ROOTUID) + (void) setpriority(PRIO_PROCESS, MyPid, PRIO_MIN); + + /* + * Determine what we are doing to processes we find. We will + * either send them a signal (skill), or renice them (snice). + */ + Skill = (strcmp(ProgName, "snice") != 0); + + /* + * chdir to `TtyDevDir' to speed up tty argument parsing. + */ + if (chdir(TtyDevDir) < 0) { + fprintf(stderr, "%s: chdir(%s): %s\n", ProgName, TtyDevDir, + SysErr()); + exit(EX_SERR); + } + + /* + * Set up minimum and maximum process priorities. + * Initialize SigPri to either default signal (`skill') or + * default priority (`snice'). + */ + PrioMin = PRIO_MIN; + PrioMax = PRIO_MAX; + SigPri = Skill? SIGTERM: 4; + } + + /* + * Carry out an action on a particular process. If this is `skill', + * then send the process a signal, otherwise this is `snice' so change + * it's priority. + * + * If 0 is returned, the operation was successful, otherwise -1 is + * returned and `errno' set. + */ + int + MdepAction(pid) + pid_T pid; + { + if (Skill) + return(kill((int)pid, SigPri)); + else + return(setpriority(PRIO_PROCESS, (int)pid, SigPri)); + } + + /* + * Now, set up everything we need to write a GetProc() routine. + */ + + #undef PI_ZOMBIE /* #define'd in "conf.h" *and* */ + #define PI_ZOMB 3 /* value from "conf.h" (it *wont* change) */ + + #include + + #define NPROCS 512 /* size of `procs' struct (if too small, use calloc) */ + + /* + * GetProc() + * + * Fill in and return a `struct ProcInfo' with information about the + * next process. If no processes are left, return NULL. + */ + struct ProcInfo * + GetProc() + { + extern int errno; + extern char *SysErr(); + static struct tbl_procinfo procs[NPROCS], *procsp, *procsidxp; + static struct ProcInfo procinfo; + register struct tbl_procinfo *aproc; + static int procindx = 0; + static int thisproc = 0; + static int maxnproc = 0; + + /* + * If this is the first time through, determine how many processes + * we will grab at one time. Hopefully, "all"... but, if the + * table() fails, or we have more than NPROCS and the calloc() + * fails, we'll have to resort to NPROCS at-a-time. + */ + if (maxnproc == 0) { + maxnproc = table(TBL_PROCINFO, 0, (char *)NULL, INT_MAX, 0); + if (maxnproc > NPROCS) { + /* + * We have more then NPROCS processes running + * on this machine. Ideally, one should bump + * NPROCS if they run into this problem. + * However, we'll quietly deal with it by + * grabbing a large block of memory. + * + * Note that, if we can not grab the memory, + * we will just have to make successive calls + * to table() reading NPROCS at-a-time. + */ + procsp = (struct tbl_procinfo *) + calloc(maxnproc, sizeof(struct tbl_procinfo)); + if (procsp == NULL) + maxnproc = NPROCS; + } else + maxnproc = NPROCS; + } + + /* + * Read in `maxnproc' structures at-a-time. Decrement `nproc' + * by the number of proc structures we have read; when it reaches + * zero, we are finished (return NULL). + */ + do { + if (thisproc == 0) { + if (maxnproc <= NPROCS) + procsp = procs; + + thisproc = table(TBL_PROCINFO, procindx, (char *)procsp, + maxnproc, sizeof(struct tbl_procinfo)); + + /* + * For now, we must be setuid root up to this point. + * Relinquish root here; this will screw us if the + * calloc() fails, but we have no choice. + */ + (void) seteuid(getuid()); + + if (thisproc <= 0) { + if (thisproc != 0 && errno != EINVAL) + fprintf(stderr, "%s: read proc: %s\n", + ProgName, SysErr()); + return((struct ProcInfo *)NULL); + } + procindx += thisproc; + } + + aproc = procsp++; + thisproc--; + } while (aproc->pi_status == PI_EMPTY); + + /* + * We now have a process (`aproc'). + * Fill in the rest of `procinfo'. + */ + procinfo.pi_flags = 0; + procinfo.pi_pid = (pid_T) aproc->pi_pid; + procinfo.pi_uid = (uid_T) aproc->pi_uid; + + /* + * Make sure this isn't a "zombie" or "exiting" + * process. If it is, we have all the information + * we need; fill in procinfo and return. + */ + if (aproc->pi_status == PI_ZOMBIE) { + static char *zombie = ""; + procinfo.pi_flags |= PI_ZOMB; + procinfo.pi_cmd = zombie; + } else if (aproc->pi_status == PI_EXITING) { + static char *exiting = ""; + procinfo.pi_flags |= PI_SWEXIT; + procinfo.pi_cmd = exiting; + } else { + if (aproc->pi_ttyd != -1) { /* has a controlling tty */ + procinfo.pi_flags |= PI_CTLTTY; + procinfo.pi_tty = (tty_T) aproc->pi_ttyd; + } + + /* set path-stripped command name */ + SETCMD(procinfo.pi_cmd, aproc->pi_comm, PI_COMLEN) + } + + return(&procinfo); + }