/* ** Copyright (c) 1986, 1994, 1996, 2000, 2002 ** Jeff Forys (jeffware@marjum.com). All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that: (1) Redistributions of ** source code must retain the above copyright notice, this list of ** conditions and the following disclaimer, (2) Redistributions in ** binary form must reproduce the above copyright notice, this list ** of conditions and the following disclaimer in the documentation ** and/or other materials provided with the distribution, (3) All ** advertising materials mentioning features or use of this software ** must display the following acknowledgment: ``This product includes ** software developed by Jeff Forys (jeffware@marjum.com).'', (4) ** The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED ** WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** Linux support by Chuck L. Blake (chuckb@Caltech.EDU) and Jeff Forys. */ #ifndef lint static char rcsid[] = "$Id: linux-1.c,v 1.15 2005/04/06 23:49:35 forys Exp $"; #endif #define NO_MEXTERN #include "conf.h" #undef NO_MEXTERN #include #include /* for [get|set]priority() */ #ifndef __CYGWIN__ #include /* for MINOR() macro */ #endif #include #include #include #include #include #include extern int MissedProcCnt; /* * 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 */ "BUS", "FPE", "KILL", "USR1", "SEGV", "USR2", /* 7 - 12 */ "PIPE", "ALRM", "TERM", "STKFLT", "CHLD", "CONT", /* 13 - 18 */ "STOP", "TSTP", "TTIN", "TTOU", "URG", "XCPU", /* 19 - 24 */ "XFSZ", "VTALRM", "PROF", "WINCH", "POLL", "PWR", /* 25 - 30 */ "UNUSED" }; int NSig = 31; #define SETCMD(dst,src,maxlen) { \ extern char *strrchr(); \ if (maxlen > 0) src[maxlen] = '\0'; \ dst = (dst = strrchr(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 */ static const char *nomemmsg = "%s: %s: out of memory (wanted %u bytes)\n"; /* * 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 skill_getpri(int *low, int *high); 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. */ #ifndef __CYGWIN__ if (MyUid == ROOTUID) (void) setpriority(PRIO_PROCESS, MyPid, PRIO_MIN); #endif /* * Determine what we are doing to processes we find. We will * either send them a signal (skill), or renice them (snice). */ Skill = (strstr(ProgName, "snice") == NULL); /* * Set up minimum and maximum process priorities. * Initialize SigPri to either default signal (`skill') or * default priority (`snice'). */ #ifndef __CYGWIN__ PrioMin = PRIO_MIN; PrioMax = PRIO_MAX; #else PrioMin = PrioMax = 0; #endif SigPri = Skill ? SIGTERM : 4; #ifndef __CYGWIN__ /* * 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); } #endif } /* * 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 #ifndef __CYGWIN__ return(setpriority(PRIO_PROCESS, (int)pid, SigPri)); #else { extern int errno; errno = ENOSYS; return -1; } #endif } /* OS supports POSIX-style regular expressions */ #include REAL_REGEX_FUNCS /* * Now, set up everything we need to write a GetProc() routine. */ #include char *SysErr(); static char *ProcDir = "/proc"; /* proc directory */ static char *ProcFil = "/proc/%s/stat"; /* name of only needed file */ #define MXBUF 1024 static char buf[MXBUF]; /* primary storage for file io */ /* * GetProc() * * Fill in and return a `struct ProcInfo' with information about the * next process. If no processes are left, return NULL. */ struct ProcInfo * GetProc() { static struct ProcInfo procinfo; static DIR *dirfp = NULL; struct dirent *dp; char flnm[FILENAME_MAX]; int fd; int n; /* first for length of buf[], then as a loop counter */ char* tmp; /* pointer into buf[] */ struct stat sb; /* stat() buffer, used to get UID */ /* * If this is our first time here, open the proc directory,... */ if (dirfp == NULL && (dirfp=opendir(ProcDir)) == NULL) { fprintf(stderr, "%s: %s: %s\n", ProgName, ProcDir, SysErr()); exit(EX_SERR); } /* * Read process-id's from /proc, open each processes' corresponding * "stat" file and parse it to fill in our 'procinfo' structure. */ while ((dp = readdir(dirfp)) != NULL) { if (dp->d_name[0] < '0' || dp->d_name[0] > '9') continue; /* * Try to open the corresponding "stat" file, if successful * then stat it (the file owner is the owner of the process). * If either fails, chalk it up as missed and try next one. */ (void) sprintf(flnm, ProcFil, dp->d_name); if ((fd = open(flnm, O_RDONLY)) < 0 || fstat(fd, &sb) < 0) { MissedProcCnt++; continue; } procinfo.pi_uid = sb.st_uid; /* read in the stat file and ensure it is NUL-terminated */ n = read(fd, buf, MXBUF-1); (void) close(fd); if (n == -1) { MissedProcCnt++; continue; } buf[n] = '\0'; /* * Parse the contents of the stat file. There is much here * we do not care about, and the contents seem to change * with each release. We want and from: * * () ... * * Since could potentially contain paren or whitespace, * start looking for the trailing paren from end of string. * If we find it, make it NUL to terminate the command. */ if ((tmp = strrchr(buf, ')')) == NULL) { badstatfmt: if (Wflag) printf("Warning: can't parse file: %s\n", flnm); continue; } *tmp = '\0'; /* * Grab the process id (atoi() will stop at first non-digit), * the command name, and whether this one is zombified. */ procinfo.pi_pid = atoi(buf); procinfo.pi_cmd = strchr(buf, '(') + 1; procinfo.pi_flags = 0; if (*(tmp + 2) == 'Z') procinfo.pi_flags |= PI_ZOMBIE; /* * All we need now is the tty's rdev. This is the fifth * field after . */ for (n = 0; n < 5; n++) while (*++tmp != ' ') /* land on a space */ if (*tmp == '\0') goto badstatfmt; if ((n = atoi(tmp)) == 0) { /* no controlling tty */ procinfo.pi_tty = (tty_T)-2; } else { procinfo.pi_tty = (tty_T)n; procinfo.pi_flags |= PI_CTLTTY; } return(&procinfo); } (void) closedir(dirfp); dirfp = NULL; return((struct ProcInfo *)NULL); }