/*
** 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.
*/
#ifndef lint
static char rcsid[] = "$Id: mach-3.c,v 1.9 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 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", "VTALRM", "PROF", "WINCH", "INFO", "USR1", /* 25 - 30 */
#ifndef __alpha
"USR2", "32" /* 31 - 32 */
#else
"USR2", "RESV", "RTMIN", "RTMIN+1", /* 31 - 34 */
"RTMIN+2", "RTMIN+3", "RTMIN+4", "RTMIN+5", /* 35 - 38 */
"RTMIN+6", "RTMIN+7", "RTMAX-7", "RTMAX-6", /* 39 - 42 */
"RTMAX-5", "RTMAX-4", "RTMAX-3", "RTMAX-2", /* 43 - 46 */
"RTMAX-1", "RTMAX" /* 47 - 48 */
#endif
};
int NSig = NSIG - 1;
#define SETCMD(dst,src,maxlen) { \
extern char *rindex(); \
if (maxlen > 0) src[maxlen] = '\0'; \
if ((dst = rindex(src, '/')) != NULL) dst++; else 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();
#ifdef __alpha
/* Tru64 actually does 16 + 16 + 8 + 8; we'll settle on 8 */
extern int SigsPerLine;
SigsPerLine = 8;
#endif
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));
}
/* IMPORTANT NOTE REGARDING REGULAR EXPRESSION ON THIS SYSTEM
*
* On this operating system, skill/snice run setuid root. While regular
* expressions are likely supported, enabling them will cause them to be
* compiled while running as the super-user; that is, the regcomp(3)
* invocation will happen as root. If there are any buffer overflow
* problems with this function, enabling regular expressions could open
* a security hole.
*
* While I do not know of any such problems on this operating system, by
* default, regular expression support is disabled. To enable support,
* simply define TRUST_REGEX and recompile.
*/
#ifdef TRUST_REGEX
#include <regex.h>
REAL_REGEX_FUNCS
#else
NULL_REGEX_FUNCS
#endif
/*
* Now, set up everything we need to write a GetProc() routine.
*/
#undef PI_ZOMBIE /* #define'd in "conf.h" *and* <sys/table.h> */
#define PI_ZOMB 3 /* value from "conf.h" (it *wont* change) */
#include <sys/table.h>
#define PROCSLOP 32 /* as an aid in catching run-away processes */
#define NPROCS (480+PROCSLOP) /* size of `procs' struct (or use calloc()) */
/*
* 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 int errno;
extern char *SysErr();
static struct tbl_procinfo procs[NPROCS], *procsp;
static struct ProcInfo procinfo;
register struct tbl_procinfo *aproc;
static int procindx = 0;
static int thisproc = 0;
static int maxnproc = 0;
static int readonce = 1;
/*
* If this is the first time through, determine how many processes
* we will grab at one time. Hopefully, "all"... but, if the
* table() fails, or we have more than NPROCS and the calloc()
* fails, we'll have to resort to NPROCS at-a-time.
*/
if (maxnproc == 0) {
if ((maxnproc = table(TBL_PROCINFO, 0, (char *)NULL, INT_MAX,
0) + PROCSLOP) > NPROCS) {
/*
* We have more then NPROCS processes running
* on this machine. Ideally, one should bump
* NPROCS if they run into this problem.
* However, we'll quietly deal with it by
* grabbing a large block of memory.
*
* Note that, if we can not grab the memory,
* we will just have to make successive calls
* to table() reading NPROCS at-a-time.
*/
procsp = (struct tbl_procinfo *)
calloc(maxnproc, sizeof(struct tbl_procinfo));
if (procsp == NULL) {
maxnproc = NPROCS;
readonce = 0;
}
} else
maxnproc = NPROCS;
}
/*
* This is a little convoluted due to the table() system call.
* We have the total number of processes we can read at-a-time
* in `maxnproc'. We do not care about the actual count of
* existing processes because:
* 1) The count may change by time we actually read them,
* 2) We may be unable to read them all at once, anyway.
*
* So, if we *know* we have enough memory ("readonce" is set),
* just do the read once and hope we get everything; we have
* provided for some additional processes (via PROCSLOP).
* Alternately, continually read in blocks of processes until
* table() returns EINVAL (since we can never be sure of the
* actual number of active processes in this situation).
*
* At any rate, return NULL when all processes have been read.
*/
do {
if (thisproc == 0) {
if (maxnproc <= NPROCS)
procsp = procs;
if (readonce && ++readonce == 3)
return((struct ProcInfo *)NULL);
thisproc = table(TBL_PROCINFO, procindx, (char *)procsp,
maxnproc, sizeof(struct tbl_procinfo));
/*
* For now, we must be setuid root up to this point.
* Relinquish root here; this will screw us if the
* calloc() fails and we are not the superuser, but
* we have no choice.
*/
(void) seteuid(getuid());
if (thisproc <= 0) {
if (thisproc != 0 && errno != EINVAL)
fprintf(stderr, "%s: read proc: %s\n",
ProgName, SysErr());
return((struct ProcInfo *)NULL);
}
procindx += thisproc;
}
aproc = procsp++;
thisproc--;
} while (aproc->pi_status == PI_EMPTY);
/*
* We now have a process (`aproc').
* Fill in the rest of `procinfo'.
*/
procinfo.pi_flags = 0;
procinfo.pi_pid = (pid_T) aproc->pi_pid;
procinfo.pi_uid = (uid_T) aproc->pi_uid;
/*
* 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.
*/
if (aproc->pi_status == PI_ZOMBIE) {
static char *zombie = "<defunct>";
procinfo.pi_flags |= PI_ZOMB;
procinfo.pi_cmd = zombie;
} else if (aproc->pi_status == PI_EXITING) {
static char *exiting = "<exiting>";
procinfo.pi_flags |= PI_SWEXIT;
procinfo.pi_cmd = exiting;
} else {
if (aproc->pi_ttyd != -1) { /* has a controlling tty */
procinfo.pi_flags |= PI_CTLTTY;
procinfo.pi_tty = (tty_T) aproc->pi_ttyd;
}
/* set path-stripped command name */
SETCMD(procinfo.pi_cmd, aproc->pi_comm, PI_COMLEN)
}
return(&procinfo);
}
syntax highlighted by Code2HTML, v. 0.9.1