/*
** 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.
*/
/*
* skill - send signals to processes by tty, user name, command or proc id.
* snice - reprioritize processes by tty, user name, command or proc id.
*
* Version 4.1.3
*/
#ifndef lint
static char rcsid[] = "$Id: main.c,v 1.41 2006/10/09 02:02:02 forys Exp $";
/*
* SCCS version release number is manually updated (for what(1), etc).
* If you use SCCS, please use last extension for version (e.g. "4.1.3.1").
*/
static char sccsid[] =
"@(#)skill 4.1.3.0 (jeffware@marjum.com) 10/08/06";
#endif
const char *Version = "4.1.3 20061008";
const char *CopyrightVersion = "%s %s\n\nCopyright (c) 1986, 1994, 1996, 2000, 2002 Jeff Forys. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, is permitted under the terms specified in the source\ncode (and manual pages).\n";
#include "conf.h"
#include <stdio.h>
#include <errno.h>
#include <pwd.h>
/*
* Processes which could not be checked -- usually due to permission
* problems (SunOS 4.1, SysVR4) -- are tallied in "MissedProcCnt".
* The machine dependent code is responsible for incrementing this;
* if it does not, we simply assume that all reasonable processes
* have been investigated (i.e. zombies are not reasonable).
*/
int MissedProcCnt = 0;
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *SysErr(), *WhoIs();
register struct ProcInfo *proc; /* process we are examining */
int reterr = -1; /* -1:matchless, 0:okay, 1:error(s) */
register int i;
int eofstdin = 0;
/*
* Ignore SIGHUP (in case we kill our shell).
*/
(void) signal(SIGHUP, SIG_IGN);
/*
* Call machine-dependent initialization routine; besides setting
* up many global variables, this routine will change our working
* directory to where tty devs reside and may raise our priority.
*
* When machine-dependent initialization is complete, parse the
* argument list.
*/
MdepInit(argv[0]);
ArgParse(argc, argv);
/*
* Our friend ArgParse() has painfully categorized the arguments
* as ttys, users, commands, or pids. Loop through the list of
* currently running processes and find things that match. In
* order for a match, we must have 1 item from each category
* (unless a category is empty). When a match is found, the
* process is either sent signal `SigPri' (`Skill' == 1) or has
* it's priority is changed to `SigPri' (`Skill' == 0).
*/
#define PID proc->pi_pid /* process id */
#define UID proc->pi_uid /* process owner */
#define TTY proc->pi_tty /* controlling tty */
#define CMD proc->pi_cmd /* command being executed */
#define FLAGS proc->pi_flags /* various flags (see conf.h) */
while ((proc = GetProc()) != NULL) {
if (TtyIndx > 0) {
tty_T *ttyptr;
/*
* For tty's, "-t ?" specifies that you are looking
* for processes without controlling terminals.
* In this case, the controlling tty will be (-2).
*/
for (i = 0; i < TtyIndx; i++) {
ttyptr = TtyList + i;
if (*ttyptr == TTY ||
(*ttyptr == (tty_T)-2 &&
(FLAGS & PI_CTLTTY) == 0))
break;
}
if (i == TtyIndx) /* no matching tty */
continue;
}
if (UidIndx > 0) {
for (i = 0; i < UidIndx && *(UidList+i) != UID; i++)
;
if (i == UidIndx) /* no matching uid */
continue;
}
if (PidIndx > 0) {
for (i = 0; i < PidIndx && *(PidList+i) != PID; i++)
;
if (i == PidIndx) /* no matching pid */
continue;
}
if (CmdIndx > 0) {
cmd_T *cmdp;
for (i = 0; i < CmdIndx; i++) {
cmdp = (CmdList + i);
if ((cmdp->flags & CMD_FLAG_EXACT) != 0 &&
STREQU(CMD, cmdp->cmd.cmdstr))
break;
else if ((cmdp->flags & CMD_FLAG_REGEX) != 0 &&
RegexMatch(CMD, cmdp) != 0)
break;
}
if (i == CmdIndx) /* no matching cmd */
continue;
}
if (PID == MyPid || PID == 0) /* ignore self */
continue;
if (Iflag || (!Nflag && (FLAGS & PI_ASKUSR))) { /* ask user */
static char yesno[10];
(void) fseek(stdin, 0L, 0);
fputs(ProgName, stdout);
if (Skill)
printf(": send #%d a %s", PID, SigMap[SigPri]);
else
printf(": renice #%d to %s%d", PID,
(SigPri >= 0)? "+": "", SigPri);
printf(" (%s executing %s)? ", WhoIs(UID), CMD);
(void) fflush(stdout);
/*
* Read user response. If not interactive, we are
* here because of PI_ASKUSR. In that case, if EOF
* is detected, continue on; do not ask user again.
* In the interactive case, exit on EOF.
*
* The intent here is to not stop a search early
* for root processes when STDIN is closed.
*/
if (eofstdin || fgets(yesno, 10, stdin) == NULL) {
(void) putc('\n', stdout);
if (Iflag)
return(EX_OKAY);
eofstdin = 1;
}
if (reterr < 0)
reterr = 0;
if (*yesno != 'y' && *yesno != 'Y')
continue;
}
if (FLAGS & PI_ZOMBIE) { /* zombie process */
fprintf(stderr, "%d: zombie process\n", PID);
continue;
} else if (FLAGS & PI_SWEXIT) { /* process is exiting */
fprintf(stderr, "%d: exiting process\n", PID);
continue;
}
/*
* Finally do what we came here to do. First, if
* we are only displaying process id's, then do so.
* If `Skill' is set, send signal `SigPri' to the
* process, otherwise, set priority of process to
* `SigPri'. If either setpriority(2) or kill(2)
* return -1, display the system error message.
*/
if (Nflag) {
printf("%d", (int)PID);
if (Vflag)
printf(" (%s executing %s)", WhoIs(UID), CMD);
(void) putc('\n', stdout);
reterr = 0;
} else if (MdepAction(PID) < 0) {
char *errmsg = SysErr();
fprintf(stderr, "%d: %s (%s executing %s)\n",
PID, errmsg, WhoIs(UID), CMD);
reterr = 1;
} else { /* success! */
if (reterr < 0)
reterr = 0;
if (Vflag) {
if (Skill)
printf("> sent #%d a %s",
PID, SigMap[SigPri]);
else
printf("> reniced #%d to %s%d", PID,
(SigPri >= 0)? "+": "", SigPri);
if (!Iflag)
printf(" (%s executing %s)",
WhoIs(UID), CMD);
(void) putc('\n', stdout);
(void) fflush(stdout);
}
}
}
if (reterr == -1) {
fprintf(stderr, "%s: no matching processes", ProgName);
if (MissedProcCnt > 0)
fprintf(stderr, " (but %d could not be checked)",
MissedProcCnt);
(void) putc('\n', stderr);
reterr = 1;
}
return(reterr? EX_UERR: EX_OKAY);
}
/*
* Whois(uid)
*
* Given a user id, return its associated user name.
*/
char *
WhoIs(uid)
uid_T uid;
{
extern char *strncpy(); /* avoid <string/strings> war */
static char usrcache[MAXUSR+1]=ROOTUSR; /* user name */
static uid_T uidcache = ROOTUID; /* user id */
struct passwd *pp;
if (uid == ROOTUID) /* be consistent w/ROOTUID */
return(ROOTUSR);
if (uid == uidcache) /* lucky break: same person */
return(usrcache);
if ((pp=getpwuid((int) uid)) == NULL) /* entry is gone? */
(void) sprintf(usrcache, "<uid:%d>", (int)uid);
else {
(void) strncpy(usrcache, pp->pw_name, MAXUSR);
usrcache[MAXUSR] = '\0';
}
uidcache = uid;
return(usrcache);
}
/*
* Usage(error)
*
* The user typed something incorrect; explain their mistake (encoded
* in `error'), display usage information, and exit.
*/
void
Usage(error)
int error;
{
switch (error) {
case E_USAGE:
fprintf(stderr,
"Usage: %s [%s] [-afinvw] {<tty> <user> <pid> <cmd>}\n",
ProgName, Skill? "-<signal>": "(+|-)<priority>");
break;
case E_PRIOR: /* unused... remains for posterity? */
fprintf(stderr, "%s: no such priority (%d)\n",
ProgName, SigPri);
break;
case E_SIGNO:
fprintf(stderr, "%s: bad signal number (%d)\n",
ProgName, SigPri);
break;
case E_VERS:
printf(CopyrightVersion, ProgName, Version);
exit(EX_OKAY);
break;
default:
fprintf(stderr, "%s: internal error: Usage(%d)\n",
ProgName, error);
break;
}
exit(EX_UERR);
/*NOTREACHED*/
}
/*
* _LP64: 64-bit ABIs (e.g. SPARC V9) where sys_errlist[] not visible.
*/
#if defined(HAS_STRERROR) || defined(_LP64)
#include <string.h>
#endif
/*
* SysErr()
*
* Return the error message described by `errno'.
*/
char *
SysErr()
{
extern int errno;
#if defined(HAS_STRERROR) || defined(_LP64)
char *se = strerror(errno);
return((se==NULL)? "unknown error": se);
#else
#if !defined(__USE_BSD) && !defined(__USE_GNU) && \
(!defined(_FSTDIO) || defined(_ANSI_SOURCE) || defined(__STRICT_ANSI__))
extern char *sys_errlist[];
extern int sys_nerr;
#endif
return((errno > sys_nerr)? "unknown error": (char *)sys_errlist[errno]);
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1