/*
** 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 <sys/priocntl.h>
#include <sys/tspriocntl.h>
#if !defined(P_PID)
#include <sys/procset.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
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 <regex.h>
REAL_REGEX_FUNCS
#else
NULL_REGEX_FUNCS
#endif
/*
* Now, set up everything we need to write a GetProc() routine.
*/
#include <sys/procfs.h>
#include <fcntl.h>
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 = "<defunct>";
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;
}
syntax highlighted by Code2HTML, v. 0.9.1