/* ** 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. ** ** Ultrix 4 support by Ric Anderson (ric@Opus1.COM) */ #ifndef lint static char rcsid[] = "$Id: ultrix-4.c,v 1.12 2005/04/06 23:49:35 forys Exp $"; #endif #define NO_MEXTERN #include "conf.h" #undef NO_MEXTERN #include #include #include #define CRIT_PID 4 /* Query kills for pids .LE. this number */ /* * 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", "IOT", /* 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", "LOST", "USR1", /* 25 - 30 */ "USR2", "32", /* 31 - 32 */ }; 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)); } /* disable regular expresions: regex(3) availability unknown */ NULL_REGEX_FUNCS /* * Now, set up everything we need to write a GetProc() routine. */ #include #include #include #include #include static char *kmemf = "/dev/kmem"; /* window into kernel virtual memory */ static char *memf = "/dev/mem"; /* window into physical memory */ static char *swapf = "/dev/drum"; /* paging device */ static char *kernf = "/vmunix"; /* kernel image */ static int kmem = 0, mem = 0, swap = 0; static struct nlist nl[] = { { "_nproc" }, #define X_NPROC 0 { "_proc" }, #define X_PROC 1 { "_proc_bitmap" }, #define X_PROC_BITMAP 2 #ifdef vax { "_Usrptmap" }, #define X_USRPTMAP 3 { "_usrpt" }, #define X_USRPT 4 { "" }, #define X_LAST 5 #else { "" }, #define X_LAST 3 #endif }; static int nproc = -1; static int absolute_proc_number = -1; static struct proc *procp; #ifdef vax static struct pte *usrptmap, *usrpt; #endif #define NPROCS 32 /* number of procs to read at once */ extern off_t lseek(); /* * GetProc() * * Fill in and return a `struct ProcInfo' with information about the * next process. If no processes are left, return NULL. */ struct ProcInfo * GetProc() { int proc_active; extern char *SysErr(); static struct user *GetUser(); static unsigned bitmap; static struct proc procs[NPROCS], *procsp; static struct ProcInfo procinfo; static struct tty tty; register struct user *auser; register struct proc *aproc; static int thisproc = 0; /* * If this is our first time here, open various files, * and set up the nlist. */ if (nproc == -1) { char *errstr = "%s: %s: %s\n"; int nfound; if ((kmem=open(kmemf, 0)) < 0) { /* open kmem */ fprintf(stderr, errstr, ProgName, kmemf, SysErr()); exit(EX_SERR); } if ((mem=open(memf, 0)) < 0) { /* open mem */ fprintf(stderr, errstr, ProgName, memf, SysErr()); exit(EX_SERR); } if ((swap=open(swapf, 0)) < 0) { /* open swap device */ fprintf(stderr, errstr, ProgName, swapf, SysErr()); exit(EX_SERR); } if ((nfound=nlist(kernf, nl)) < 0) { /* kernel name list */ fprintf(stderr, errstr, ProgName, kernf,"no name list"); exit(EX_SERR); } if (nfound != 0) { register int i; fprintf(stderr, "%s: nlist: unresolved symbols:", ProgName); for (i = 0; i < X_LAST; i++) if (nl[i].n_type == 0) fprintf(stderr, " %s", nl[i].n_name); (void) putc('\n', stderr); exit(EX_SERR); } #ifdef vax usrpt = (struct pte *) nl[X_USRPT].n_value; usrptmap = (struct pte *) nl[X_USRPTMAP].n_value; #endif procp = (struct proc *)GetWord((off_t)nl[X_PROC].n_value); nproc = GetWord((off_t)nl[X_NPROC].n_value); } /* * Read in NPROCS proc 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 { while (thisproc == 0) { int nread; int psize; if (nproc == 0) return((struct ProcInfo *)NULL); thisproc = MIN(NPROCS, nproc); psize = thisproc * sizeof(struct proc); nproc -= thisproc; if (lseek(kmem, (off_t)procp, L_SET) == -1 || (nread = read(kmem, (char *)procs, psize)) < 0) { fprintf(stderr, "%s: read proc: %s\n", ProgName, SysErr()); return((struct ProcInfo *)NULL); } else if (nread != psize) { thisproc = nread / sizeof(struct proc); nproc = 0; fprintf(stderr, "%s: read proc: short read\n", ProgName); } procsp = procs; procp += thisproc; } aproc = procsp++; thisproc--; /* Ultrix 4.x uses a bitmap to tell if a proc structure is */ /* valid. We read and check the bitmap. The code here is */ /* structured around 32 bits/entry, and 4 bytes/entry. */ absolute_proc_number++; if((absolute_proc_number % 32) == 0) bitmap = GetWord((unsigned int)nl[X_PROC_BITMAP].n_value + ((absolute_proc_number / 32) * 4)); proc_active = (bitmap & (1 << (absolute_proc_number % 32))) != 0; if (proc_active) { /* * Before we go through the trouble of reading * in the user struct, let's 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->p_pid; procinfo.pi_uid = (uid_T) aproc->p_uid; if (aproc->p_stat == SZOMB) { /* zombie */ static char *zombie = ""; procinfo.pi_flags |= PI_ZOMBIE; procinfo.pi_cmd = zombie; } else if (aproc->p_type & SWEXIT) { /* exiting */ static char *exiting = ""; procinfo.pi_flags |= PI_SWEXIT; procinfo.pi_cmd = exiting; } if (procinfo.pi_flags) return(&procinfo); else auser = GetUser(aproc); } } while (!proc_active || auser == NULL); /* * We now have a process (`aproc') and a user (`auser'). * Fill in the rest of `procinfo'. */ if (aproc->p_ttyp != 0 && /* has a controlling terminal? */ lseek(kmem, (off_t)aproc->p_ttyp, L_SET) != -1 && read(kmem, (char *)&tty, sizeof(tty)) == sizeof(tty)) { procinfo.pi_flags |= PI_CTLTTY; procinfo.pi_tty = (tty_T) tty.t_dev; } if (aproc->p_pid <= CRIT_PID) /* special */ procinfo.pi_flags |= PI_ASKUSR; SETCMD(procinfo.pi_cmd, auser->u_comm, MAXCOMLEN) return(&procinfo); } #define SKRD(file, src, dst, size) \ (lseek(file, (off_t)(src), L_SET) == -1) || \ (read(file, (char *)(dst), (size)) != (size)) /* * GetStruct(loc,name,dest,size) * * Read in structure at `loc' from kernel virtual memory. * If an error occurs, return non-zero. */ static int GetStruct(loc,name,dest,size) char *name; int size; off_t dest, loc; { if(SKRD(kmem,loc,dest,size)) return(0); return(1); } /* * GetWord(loc) * * Read in word at `loc' from kernel virtual memory. * If an error occurs, call exit(2) with EX_SERR. */ int GetWord(loc) off_t loc; { int val; if (SKRD(kmem, loc, &val, sizeof(val))) { fprintf(stderr, "%s: can't read word at %lx in %s\n", ProgName, (u_long)loc, kmemf); exit(EX_SERR); } return (val); } #ifdef mips #define SW_UADDR dtob(GetWord(dmap.dm_ptdaddr)) #define SW_UBYTES sizeof(struct user) /* * GetUser(aproc) * * Read in the user struct for `aproc' and return a pointer to it. * If an error occurs, return NULL. */ static struct user * GetUser(aproc) struct proc *aproc; { static char *WarnMsg = "Warning: can't read "; static union { struct user user; char upgs[UPAGES][NBPG]; } u; static struct pte uptes[UPAGES]; static struct dmap dmap; register int i, nbytes; /* * If process is not in core, we simply snarf it's user struct * from the swap device. */ if ((aproc->p_sched & SLOAD) == 0) { if(!GetStruct((off_t)aproc->p_smap,"aproc->p_smap", &dmap,sizeof(dmap))) { if (Wflag) printf("%s dmap for pid %d from %s\n", WarnMsg, aproc->p_pid, swapf); return ((struct user *)NULL); } if (SKRD(swap, SW_UADDR, &u.user, SW_UBYTES)) { if (Wflag) printf("%su for pid %d from %s\n", WarnMsg, aproc->p_pid, swapf); return ((struct user *)NULL); } return (&u.user); } /* * Process is in core. Follow p_addr to read in the page * table entries that map the u-area and then read in the * physical pages that comprise the u-area. * * If at any time, an lseek() or read() fails, print a warning * message (if `Wflag' is set) and return NULL. */ if (SKRD(kmem, aproc->p_addr, uptes, sizeof(uptes))) { if (Wflag) printf("%suser pt for pid %d from %s\n", WarnMsg, aproc->p_pid, kmemf); return ((struct user *)NULL); } nbytes = sizeof(struct user); for (i = 0; i < UPAGES && nbytes > 0; i++) { if (SKRD(mem, ptob(uptes[i].pg_pfnum), u.upgs[i], NBPG)) { if (Wflag) printf("%suser page %u for pid %d from %s\n", WarnMsg, uptes[i].pg_pfnum, aproc->p_pid, memf); return((struct user *)NULL); } nbytes -= NBPG; } return (&u.user); } #endif /* mips */ #ifdef vax /**** The following code is uncompiled, and untested. if you have * an Ultrix 4.x vax to compile it on, please do so and send * mods to jeff forys. */ #define SW_UADDR dtob(GetWord(dmap.dm_ptdaddr)) #define SW_UBYTES sizeof(struct user) /* * GetUser(aproc) * * Read in the user struct for `aproc' and return a pointer to it. * If an error occurs, return NULL. */ static struct user * GetUser(aproc) struct proc *aproc; { static char *WarnMsg = "Warning: can't read "; static union { struct user user; char upgs[UPAGES][NBPG]; } u; static struct pte apte, uptes[CLSIZE+HIGHPAGES]; static struct dmap dmap; register int ncl, i, nbytes; /* * If process is not in core, we simply snarf it's user struct * from the swap device. */ if ((aproc->p_sched & SLOAD) == 0) { if(!GetStruct((off_t)aproc->p_smap,"aproc->p_smap", &dmap,sizeof(dmap))) { if (Wflag) printf("%s dmap for pid %d from %s\n", WarnMsg, aproc->p_pid, swapf); return ((struct user *)NULL); } if (SKRD(swap, SW_UADDR, &u.user, SW_UBYTES)) { if (Wflag) printf("%su for pid %d from %s\n", WarnMsg, aproc->p_pid, swapf); return ((struct user *)NULL); } return (&u.user); } /* Process is in core. First read from the user page-table */ /* map for this process to get the u-area page table entry. */ /* Next, read in the page table entries that map the u-area. */ /* Finally, read in the physical pages that comprise the */ /* u-area. */ /* */ /* If at any time, an lseek() or read() fails, print a warning */ /* message (if `Wflag' is set) and return NULL. */ if (SKRD(kmem, &usrptmap[btokmx(aproc->p_p0br) + aproc->p_szpt - 1], &apte, sizeof(apte))) { if (Wflag) printf("%sindir pte of user pt for pid %d from %s\n", WarnMsg, aproc->p_pid, kmemf); return ((struct user *)NULL); } if (SKRD(mem, ctob(apte.pg_pfnum+1) - sizeof(uptes), uptes, sizeof(uptes))) { if (Wflag) printf("%suser pt for pid %d from %s\n", WarnMsg, aproc->p_pid, memf); return ((struct user *)NULL); } /* * Set `ncl' to the (rounded up) number of u-area page clusters * to read, and then read in this user struct (in reverse). */ ncl = (sizeof(struct user) + CLBYTES - 1) / CLBYTES; while (--ncl >= 0) { i = ncl * CLSIZE; if (SKRD(mem,ctob(uptes[CLSIZE+i].pg_pfnum),u.upgs[i],CLBYTES)){ if (Wflag) printf("%suser page %u for pid %d from %s\n", WarnMsg, uptes[CLSIZE+i].pg_pfnum, aproc->p_pid, memf); return((struct user *)NULL); } } return (&u.user); } #endif /* vax */