/* ** 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. */ #ifndef lint static char rcsid[] = "$Id: aix-3.c,v 1.10 2006/10/09 01:58:11 forys Exp $"; #endif #define NO_MEXTERN #include "conf.h" #undef NO_MEXTERN #include #include #include /* * For AIX 3 and AIX 4, #define the SKILL_AIX34 macro. * For AIX 5 (and beyond), this file should compile correctly. */ #ifdef SKILL_AIX34 #define SKILL_P_STRUCT struct procinfo #define SKILL_P_STATE(p) (p)->pi_stat #define SKILL_P_FLAGS(p) (p)->pi_flag #define SKILL_PU_TTYP(p,u) (u)->ui_ttyp #define SKILL_PU_TTYD(p,u) (u)->ui_ttyd #define SKILL_PU_COMM(p,u) (u)->ui_comm #define SKILL_NOTTY (-1) #else #define SKILL_P_STRUCT struct procentry64 #define SKILL_P_STATE(p) (p)->pi_state #define SKILL_P_FLAGS(p) (p)->pi_flags #define SKILL_PU_TTYP(p,u) (p)->pi_ttyp #define SKILL_PU_TTYD(p,u) (p)->pi_ttyd #define SKILL_PU_COMM(p,u) (p)->pi_comm #define SKILL_NOTTY (0) #include #endif /* * 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", "26", "MSG", "WINCH", "PWR", "USR1", /* 25 - 30 */ "USR2", "PROF", "DANGER", "VTALRM", "MIGRATE", "PRE", /* 31 - 36 */ "37", "38", "39", "40", "41", "42", /* 37 - 42 */ "43", "44", "45", "46", "47", "48", /* 43 - 48 */ "49", "50", "51", "52", "53", "54", /* 49 - 54 */ #ifdef SKILL_AIX34 "55", "56", "57", "58", "59", "GRANT", /* 55 - 60 */ #else "55", "56", "57", "58", "CPUFAIL", "GRANT", /* 55 - 60 */ #endif "RETRACT", "SOUND", "SAK", "64", /* 61 - 64 */ }; int NSig = NSIG; #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)); } #ifdef SKILL_AIX34 /* disable regular expresions: regex(3) availability unknown */ NULL_REGEX_FUNCS #else /* OS supports POSIX-style regular expressions */ #include REAL_REGEX_FUNCS #endif /* * Now, set up everything we need to write a GetProc() routine. * * IMPORTANT NOTE TO PEOPLE SNARFING THIS CODE: * * The AIX getproc() and getuser() routines are unsupported (not to * mention, undocumented). They are liable to be changed, or replaced * in future versions of AIX. Please include this comment in any * code that uses these routines. Thanks. * * With that said, here's a brief (incomplete) description of each: * * #include * * struct procinfo pinfo[N]; * getproc(pinfo, N, sizeof(struct procinfo)) * RETURN: number of procs read (must be all), or -1 on error. * [If N is too small, -1 is returned with errno == ENOSPC] * * struct userinfo uinfo; * struct procinfo *aproc; * getuser(aproc, sizeof(struct procinfo), &uinfo, sizeof(uinfo)) * RETURN: 0 on success, -1 on error. */ #include #include #include #include extern off_t lseek(); #define NPROCS 1024 /* 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. * * Fflag support: * If Fflag is set we will try to avoid reading in the user struct. * We can do this only if Iflag, TtyIndx, and CmdIndx are zero. */ struct ProcInfo * GetProc() { extern int errno; extern char *SysErr(); static SKILL_P_STRUCT pinfo[NPROCS], *procsp; register SKILL_P_STRUCT *aproc; static struct ProcInfo procinfo; static int thisproc = 0; static int initialized = 0; static int needuser = 1; /* Fflag support */ #ifdef SKILL_AIX34 register struct userinfo *auser; #else pid_t procsidx; #endif /* * Read in all the processes at once, into a large block of memory; * if `pinfo' is big enough, we'll use that, o/w we will calloc() * what we need. Either way, `procsp' will point to the next proc. * * The first time thisproc == 0, we do the `procsp' initialization. * The second time thisproc == 0, we are finished and return NULL. * The following `while' is for sanity; it could be an `if'. */ while (thisproc == 0) { char *errstr = "%s: %s: %s\n"; register u_int nprocs, maxnprocs, pisize; if (initialized) return((struct ProcInfo *)NULL); pisize = sizeof(SKILL_P_STRUCT); #ifdef SKILL_AIX34 if ((thisproc = getproc(pinfo, (u_int)NPROCS, pisize)) < 0) #else procsidx = 0; if (((thisproc = getprocs64(pinfo, pisize, NULL, 0, &procsidx, (u_int)NPROCS)) < 0) || thisproc == NPROCS) #endif { /* * We apparently have more then NPROCS processes * running on this machine. Ideally, one should * raise NPROCS if they run into this problem. * However, we'll quietly deal with it by grabbing * a sufficiently large block of memory. * * N.B. `nprocs' is temporarily used to hold `errno' * across the call to sysconfig(), after which, it * will assume it's real duty. */ struct var v; nprocs = errno; if (sysconfig(SYS_GETPARMS, &v, sizeof(struct var)) < 0) maxnprocs = 1024 * 1024; /* very big */ else maxnprocs = v.v_proc; errno = nprocs; nprocs = NPROCS; while (nprocs <= maxnprocs) { nprocs <<= 1; if ((procsp = (SKILL_P_STRUCT *) calloc(nprocs, pisize)) == NULL) { fprintf(stderr, errstr, ProgName, "getproc", "out of memory"); exit(EX_SERR); } errno = 0; #ifdef SKILL_AIX34 thisproc = getproc(procsp, nprocs, pisize); #else procsidx = 0; thisproc = getprocs64(procsp, pisize, NULL, 0, &procsidx, nprocs); #endif if (thisproc < 0 && errno != ENOSPC) { fprintf(stderr, errstr, ProgName, "getproc", SysErr()); exit(EX_SERR); } else if (thisproc < nprocs) break; free((void *)procsp); } if (nprocs > maxnprocs) { fprintf(stderr, errstr, ProgName, "GetProc", "maxnproc exceeded"); exit(EX_SERR); } } else procsp = pinfo; /* * We run a little faster without reading in the user struct; * the price is incomplete information for errors (no cmd). */ if (Fflag && Iflag == 0 && TtyIndx == 0 && CmdIndx == 0) needuser = 0; initialized = 1; } /* * Trudge thru `procsp'. Decrement `thisproc' as we go. */ do { aproc = procsp++; thisproc--; if (SKILL_P_STATE(aproc) != SNONE) { /* * 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. */ procinfo.pi_flags = 0; procinfo.pi_pid = (pid_T) aproc->pi_pid; procinfo.pi_uid = (uid_T) aproc->pi_uid; if (SKILL_P_STATE(aproc) == SZOMB) { /* zombie */ static char *zombie = ""; procinfo.pi_flags |= PI_ZOMBIE; procinfo.pi_cmd = zombie; } else if (SKILL_P_FLAGS(aproc) & SEXIT) { static char *exiting = ""; procinfo.pi_flags |= PI_SWEXIT; procinfo.pi_cmd = exiting; } else if (!needuser) { static char *fflagcmd = "<-f>"; procinfo.pi_cmd = fflagcmd; } if (procinfo.pi_flags || !needuser) return(&procinfo); #ifdef SKILL_AIX34 else { static struct userinfo uinfo; if (getuser(aproc, sizeof(SKILL_P_STRUCT), &uinfo,sizeof(struct userinfo)) < 0) auser = NULL; else auser = &uinfo; } #endif } } #ifdef SKILL_AIX34 while (SKILL_P_STATE(aproc) == SNONE || auser == NULL); #else while (SKILL_P_STATE(aproc) == SNONE); #endif /* * We now have a process (`aproc'). * Fill in the rest of `procinfo'. */ if (SKILL_PU_TTYP(aproc,auser) != SKILL_NOTTY) { /* controlling tty */ procinfo.pi_flags |= PI_CTLTTY; procinfo.pi_tty = (tty_T) SKILL_PU_TTYD(aproc,auser); } else { procinfo.pi_tty = (tty_T)-2; } /* set path-stripped command name */ SETCMD(procinfo.pi_cmd, SKILL_PU_COMM(aproc,auser), MAXCOMLEN) return(&procinfo); }