/*
** 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 <sys/user.h>
#include <sys/proc.h>
#include <stdio.h>
#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 <sys/tty.h>
#include <sys/file.h>
#include <sys/vm.h>
#include <machine/pte.h>
#include <nlist.h>
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 = "<defunct>";
procinfo.pi_flags |= PI_ZOMBIE;
procinfo.pi_cmd = zombie;
} else if (aproc->p_type & SWEXIT) { /* exiting */
static char *exiting = "<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 */
syntax highlighted by Code2HTML, v. 0.9.1