/* ** Copyright (c) 1986, 1994, 1996, 2000, 2002, 2006 ** 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. ** ** System V support by Ric Anderson (ric@Opus1.COM) and Jeff Forys. */ #ifndef lint static char rcsid[] = "$Id: sys-5r4.c,v 1.23 2006/10/09 01:59:56 forys Exp $"; #endif #define NO_MEXTERN #include "conf.h" #undef NO_MEXTERN #include #include #if !defined(P_PID) #include #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. */ #ifdef sun # ifdef SIG2STR_MAX /* Solaris: handled in MdepInit() */ #define SIGMAPSIZE 128 char *SigMap[SIGMAPSIZE+1]; int NSig; # 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", "CLD", /* 13 - 18 */ "PWR", "WINCH", "URG", "POLL", "STOP", "TSTP", /* 19 - 24 */ "CONT", "TTIN", "TTOU", "VTALRM", "PROF", "XCPU", /* 25 - 30 */ "XFSZ", "WAITING", "LWP", "FREEZE", "THAW", "CANCEL", /* 31 - 36 */ "RTMIN", "RTMIN+1", "RTMIN+2", "RTMIN+3", "RTMAX-3", /* 37 - 41 */ "RTMAX-2", "RTMAX-1", "RTMAX" /* 42 - 44 */ }; int NSig = NSIG-1; # endif #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", "AIO" /* 31 - 34 */ }; int NSig = NSIG-1; #endif #define SETCMD(dst,src,maxlen) { \ if (maxlen > 0) src[maxlen] = '\0'; \ dst = (dst = strrchr(src, '/')) ? ++dst: src; \ } #define PRIO_SKEW 20 /* Skew SysVr4 priorities to match "nice" */ static char *TtyDevDir = "/dev"; static int setpriority_bsd(); 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 */ #ifdef sun int timeout; void alarmhdlr() { timeout = 1; } #endif /* * 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 *SysErr(); MyPid = (pid_T) getpid(); MyUid = (uid_T) getuid(); SETCMD(ProgName, pname, 0) #ifdef sun { struct sigaction avec; extern int SigsPerLine; int sig; SigsPerLine = 8; # ifdef SIG2STR_MAX /* * Set up signal info dynamically for binary compatibility * with future Solaris versions that may add more signals. */ NSig = _sys_nsig-1; if (SIGMAPSIZE < _sys_nsig) { fprintf(stderr, "%s: SIGMAPSIZE must be at least %d\n", ProgName, _sys_nsig); exit(EX_SERR); } for (sig = 0; sig <= NSig; sig++) { if ((SigMap[sig] = malloc(SIG2STR_MAX)) == NULL) Exceed("SigMap"); if (sig2str(sig, SigMap[sig]) == -1) sprintf(SigMap[sig], "%d", sig); } # endif sigemptyset (&avec.sa_mask); sigaddset (&avec.sa_mask, SIGALRM); avec.sa_flags = 0; /* no restart */ avec.sa_handler = alarmhdlr; (void) sigaction(SIGALRM, &avec, NULL); } #endif PrioMin = -PRIO_SKEW; PrioMax = PRIO_SKEW; /* * If we are running as root, raise our priority to better * catch runaway processes. */ if (MyUid == ROOTUID) (void) setpriority_bsd(MyPid, PrioMin); /* * 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); /* * 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); } 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((pid_t)pid, SigPri); else return setpriority_bsd((pid_t)pid, SigPri); } #ifdef SYSV_REGEX /* OS supports POSIX-style regular expressions */ #include REAL_REGEX_FUNCS #else NULL_REGEX_FUNCS #endif /* * Now, set up everything we need to write a GetProc() routine. */ #include #include static char *ProcDir = "/proc"; /* proc directory */ #ifdef PRMAXOPERAND /* SysVr4.2MP */ #define pr_lttydev pr_ttydev static char *ProcFil = "/proc/%s/psinfo"; /* proc info file */ #else static char *ProcFil = "/proc/%s"; /* proc images */ #endif /* * 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 char *SysErr(); static char *zombie = ""; static struct ProcInfo procinfo; static DIR *dirfp = NULL; #ifdef PRMAXOPERAND static struct psinfo pinfo; #else static struct prpsinfo pinfo; #endif struct dirent *dp; char flnm[FILENAME_MAX]; int fd; /* * 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); } while ((dp = readdir(dirfp)) != NULL) { if (strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0) continue; (void) sprintf(flnm, ProcFil, dp->d_name); #ifdef sun timeout = 0; alarm(2); #endif fd = open(flnm, O_RDONLY); #ifdef sun alarm(0); #endif if (fd < 0) { MissedProcCnt++; #ifdef sun if (Wflag && timeout) printf("Warning: open(%s) blocked\n", flnm); #endif continue; /* ignore procs we don't own */ } /* * Read process status (either read() or ioctl(PIOCPSINFO)). */ #ifdef PRMAXOPERAND if (read(fd, &pinfo, sizeof(pinfo)) != sizeof(pinfo)) #else if (ioctl(fd, PIOCPSINFO, &pinfo) == -1) #endif { (void) close(fd); if (Wflag) printf("Warning: can't read %s\n", flnm); continue; /* ignore these too */ } (void) close(fd); /* * Information about a process now resides in 'pinfo'. */ procinfo.pi_flags = 0; procinfo.pi_pid = pinfo.pr_pid; procinfo.pi_uid = pinfo.pr_uid; #ifndef PRMAXOPERAND /* cant handle SysVr4.2MP model (per thread zombies) */ if (pinfo.pr_zomb != 0) { procinfo.pi_flags |= PI_ZOMBIE; procinfo.pi_cmd = zombie; } else #endif { #ifdef sun if (pinfo.pr_pid < 4) /* low pids are special */ #else if (pinfo.pr_pid < 5) /* low pids are special */ #endif procinfo.pi_flags |= PI_ASKUSR; if (pinfo.pr_lttydev != PRNODEV) { procinfo.pi_flags |= PI_CTLTTY; procinfo.pi_tty = pinfo.pr_lttydev; } else { procinfo.pi_tty = (tty_T)-2; } procinfo.pi_cmd = pinfo.pr_fname; } return &procinfo; } (void) closedir(dirfp); dirfp = NULL; return (struct ProcInfo *)NULL; } /* * setpriority_bsd(pid_t pid, int prio) * * Set priority of a process. * * pid - target process id. * prio - priority adjustment (range: -PRIO_SKEW - +PRIO_SKEW). * * Returns 0 on success, -1 on error (setting errno). */ static int setpriority_bsd(pid, prio) pid_t pid; int prio; { static int firstime = 1; static short ts_maxupri; /* initialized once */ static id_t ts_id; /* initialized once */ pcparms_t parms; tsparms_t *tset_p; /* * Read default parameters for Time Share (TS) processes. */ if (firstime) { pcinfo_t pcinfo; (void)strcpy(pcinfo.pc_clname, "TS"); if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1) return -1; ts_maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri; ts_id = pcinfo.pc_cid; firstime = 0; } /* * Get scheduling parameters for this process. * * "If process specified does not belong to the specified * class, priocntl() returns -1 with errno set to ESRCH." */ parms.pc_cid = ts_id; if (priocntl(P_PID, pid, PC_GETPARMS, (caddr_t)&parms) == -1) return -1; /* * Set new priority. */ tset_p = (tsparms_t *)parms.pc_clparms; tset_p->ts_uprilim = tset_p->ts_upri = -(ts_maxupri * prio) / PRIO_SKEW; if (priocntl(P_PID, pid, PC_SETPARMS, (caddr_t)&parms) == -1) return -1; return 0; }