578 lines
16 KiB
C
578 lines
16 KiB
C
/*
|
|
* Copyright 1998 by Albert Cahalan; all rights resered.
|
|
* This file may be used subject to the terms and conditions of the
|
|
* GNU Library General Public License Version 2, or any later version
|
|
* at your option, as published by the Free Software Foundation.
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Library General Public License for more details.
|
|
*/
|
|
#include <fcntl.h>
|
|
#include <pwd.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include "proc/pwcache.h"
|
|
#include "proc/sig.h"
|
|
#include "proc/devname.h"
|
|
#include "proc/procps.h" /* char *user_from_uid(uid_t uid) */
|
|
#include "proc/version.h" /* procps_version */
|
|
|
|
static int f_flag, i_flag, v_flag, w_flag, n_flag;
|
|
|
|
static int tty_count, uid_count, cmd_count, pid_count;
|
|
static int *ttys;
|
|
static uid_t *uids;
|
|
static const char **cmds;
|
|
static int *pids;
|
|
|
|
#define ENLIST(thing,addme) do{ \
|
|
if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \
|
|
if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \
|
|
thing##s[thing##_count++] = addme; \
|
|
}while(0)
|
|
|
|
static int my_pid;
|
|
static int saved_argc;
|
|
|
|
static int sig_or_pri;
|
|
|
|
static int program;
|
|
#define PROG_GARBAGE 0 /* keep this 0 */
|
|
#define PROG_KILL 1
|
|
#define PROG_SKILL 2
|
|
/* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */
|
|
#define PROG_SNICE 4
|
|
|
|
|
|
/********************************************************************/
|
|
|
|
static void display_kill_version(){
|
|
|
|
switch(program) {
|
|
case PROG_KILL:
|
|
fprintf(stdout, "kill (%s)\n",procps_version);
|
|
return;
|
|
case PROG_SKILL:
|
|
fprintf(stdout, "skill (%s)\n",procps_version);
|
|
return;
|
|
case PROG_SNICE:
|
|
fprintf(stdout, "snice (%s)\n",procps_version);
|
|
return;
|
|
default:
|
|
fprintf(stdout, "unknown (%s)\n",procps_version);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/***** kill or nice a process */
|
|
static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd){
|
|
int failed;
|
|
int saved_errno;
|
|
char dn_buf[1000];
|
|
dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
|
|
if(i_flag){
|
|
char buf[8];
|
|
fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s ? ",
|
|
(char*)dn_buf,user_from_uid(uid),pid,cmd
|
|
);
|
|
if(!fgets(buf,7,stdin)){
|
|
printf("\n");
|
|
exit(0);
|
|
}
|
|
if(*buf!='y' && *buf!='Y') return;
|
|
}
|
|
/* do the actual work */
|
|
if(program==PROG_SKILL) failed=kill(pid,sig_or_pri);
|
|
else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri);
|
|
saved_errno = errno;
|
|
if(w_flag && failed){
|
|
fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s ",
|
|
(char*)dn_buf,user_from_uid(uid),pid,cmd
|
|
);
|
|
errno = saved_errno;
|
|
perror("");
|
|
return;
|
|
}
|
|
if(i_flag) return;
|
|
if(v_flag){
|
|
printf("%-8.8s %-8.8s %5d %-16.16s\n",
|
|
(char*)dn_buf,user_from_uid(uid),pid,cmd
|
|
);
|
|
return;
|
|
}
|
|
if(n_flag){
|
|
printf("%d\n",pid);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/***** check one process */
|
|
static void check_proc(int pid){
|
|
char buf[128];
|
|
struct stat statbuf;
|
|
char *tmp;
|
|
int tty;
|
|
int fd;
|
|
int i;
|
|
if(pid==my_pid) return;
|
|
sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
|
|
fd = open(buf,O_RDONLY);
|
|
if(fd==-1){ /* process exited maybe */
|
|
if(pids && w_flag) printf("WARNING: process %d could not be found.",pid);
|
|
return;
|
|
}
|
|
fstat(fd, &statbuf);
|
|
if(uids){ /* check the EUID */
|
|
i=uid_count;
|
|
while(i--) if(uids[i]==statbuf.st_uid) break;
|
|
if(i==-1) goto closure;
|
|
}
|
|
read(fd,buf,128);
|
|
buf[127] = '\0';
|
|
tmp = strrchr(buf, ')');
|
|
*tmp++ = '\0';
|
|
i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */
|
|
tty = atoi(tmp);
|
|
if(ttys){
|
|
i=tty_count;
|
|
while(i--) if(ttys[i]==tty) break;
|
|
if(i==-1) goto closure;
|
|
}
|
|
tmp = strchr(buf, '(') + 1;
|
|
if(cmds){
|
|
i=cmd_count;
|
|
/* fast comparison trick -- useful? */
|
|
while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break;
|
|
if(i==-1) goto closure;
|
|
}
|
|
/* This is where we kill/nice something. */
|
|
/* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
|
|
pid, statbuf.st_uid, tty>>8, tty&0xf, tmp
|
|
);
|
|
*/
|
|
hurt_proc(tty, statbuf.st_uid, pid, tmp);
|
|
closure:
|
|
close(fd); /* kill/nice _first_ to avoid PID reuse */
|
|
}
|
|
|
|
|
|
/***** debug function */
|
|
#if 0
|
|
static void show_lists(void){
|
|
int i;
|
|
|
|
fprintf(stderr, "%d TTY: ", tty_count);
|
|
if(ttys){
|
|
i=tty_count;
|
|
while(i--){
|
|
fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n');
|
|
}
|
|
}else fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "%d UID: ", uid_count);
|
|
if(uids){
|
|
i=uid_count;
|
|
while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n');
|
|
}else fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "%d PID: ", pid_count);
|
|
if(pids){
|
|
i=pid_count;
|
|
while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n');
|
|
}else fprintf(stderr, "\n");
|
|
|
|
fprintf(stderr, "%d CMD: ", cmd_count);
|
|
if(cmds){
|
|
i=cmd_count;
|
|
while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n');
|
|
}else fprintf(stderr, "\n");
|
|
}
|
|
#endif
|
|
|
|
|
|
/***** iterate over all PIDs */
|
|
static void iterate(void){
|
|
int pid;
|
|
DIR *d;
|
|
struct dirent *de;
|
|
if(pids){
|
|
pid = pid_count;
|
|
while(pid--) check_proc(pids[pid]);
|
|
return;
|
|
}
|
|
#if 0
|
|
/* could setuid() and kill -1 to have the kernel wipe out a user */
|
|
if(!ttys && !cmds && !pids && !i_flag){
|
|
}
|
|
#endif
|
|
d = opendir("/proc");
|
|
if(!d){
|
|
perror("/proc");
|
|
exit(1);
|
|
}
|
|
while(( de = readdir(d) )){
|
|
if(de->d_name[0] > '9') continue;
|
|
if(de->d_name[0] < '1') continue;
|
|
pid = atoi(de->d_name);
|
|
if(pid) check_proc(pid);
|
|
}
|
|
closedir (d);
|
|
}
|
|
|
|
/***** kill help */
|
|
static void kill_usage(void) NORETURN;
|
|
static void kill_usage(void){
|
|
fprintf(stderr,
|
|
"Usage:\n"
|
|
" kill pid ... Send SIGTERM to every process listed.\n"
|
|
" kill signal pid ... Send a signal to every process listed.\n"
|
|
" kill -s signal pid ... Send a signal to every process listed.\n"
|
|
" kill -l List all signal names.\n"
|
|
" kill -L List all signal names in a nice table.\n"
|
|
" kill -l signal Convert between signal numbers and names.\n"
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
/***** kill */
|
|
static void kill_main(int argc, const char *restrict const *restrict argv) NORETURN;
|
|
static void kill_main(int argc, const char *restrict const *restrict argv){
|
|
const char *sigptr;
|
|
int signo = SIGTERM;
|
|
int exitvalue = 0;
|
|
if(argc<2) kill_usage();
|
|
if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
|
|
display_kill_version();
|
|
exit(0);
|
|
}
|
|
if(argv[1][0]!='-'){
|
|
argv++;
|
|
argc--;
|
|
goto no_more_args;
|
|
}
|
|
|
|
/* The -l option prints out signal names. */
|
|
if(argv[1][1]=='l' && argv[1][2]=='\0'){
|
|
if(argc==2){
|
|
unix_print_signals();
|
|
exit(0);
|
|
}
|
|
/* at this point, argc must be 3 or more */
|
|
if(argc>128 || argv[2][0] == '-') kill_usage();
|
|
exit(print_given_signals(argc-2, argv+2, 80));
|
|
}
|
|
|
|
/* The -L option prints out signal names in a nice table. */
|
|
if(argv[1][1]=='L' && argv[1][2]=='\0'){
|
|
if(argc==2){
|
|
pretty_print_signals();
|
|
exit(0);
|
|
}
|
|
kill_usage();
|
|
}
|
|
if(argv[1][1]=='-' && argv[1][2]=='\0'){
|
|
argv+=2;
|
|
argc-=2;
|
|
goto no_more_args;
|
|
}
|
|
if(argv[1][1]=='-') kill_usage(); /* likely --help */
|
|
if(argv[1][1]=='s' && argv[1][2]=='\0'){
|
|
sigptr = argv[2];
|
|
argv+=3;
|
|
argc-=3;
|
|
}else{
|
|
sigptr = argv[1]+1;
|
|
argv+=2;
|
|
argc-=2;
|
|
}
|
|
signo = signal_name_to_number(sigptr);
|
|
if(signo<0){
|
|
fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr);
|
|
kill_usage();
|
|
}
|
|
no_more_args:
|
|
if(!argc) kill_usage(); /* nothing to kill? */
|
|
while(argc--){
|
|
long pid;
|
|
char *endp;
|
|
pid = strtol(argv[argc],&endp,10);
|
|
if(!*endp){
|
|
if(!kill((pid_t)pid,signo)) continue;
|
|
exitvalue = 1;
|
|
continue;
|
|
}
|
|
fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]);
|
|
kill_usage();
|
|
}
|
|
exit(exitvalue);
|
|
}
|
|
|
|
/***** skill/snice help */
|
|
static void skillsnice_usage(void) NORETURN;
|
|
static void skillsnice_usage(void){
|
|
if(program==PROG_SKILL){
|
|
fprintf(stderr,
|
|
"Usage: skill [signal to send] [options] process selection criteria\n"
|
|
"Example: skill -KILL -v pts/*\n"
|
|
"\n"
|
|
"The default signal is TERM. Use -l or -L to list available signals.\n"
|
|
"Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
|
|
"Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"
|
|
);
|
|
}else{
|
|
fprintf(stderr,
|
|
"Usage: snice [new priority] [options] process selection criteria\n"
|
|
"Example: snice netscape crack +7\n"
|
|
"\n"
|
|
"The default priority is +4. (snice +4 ...)\n"
|
|
"Priority numbers range from +20 (slowest) to -20 (fastest).\n"
|
|
"Negative priority numbers are restricted to administrative users.\n"
|
|
);
|
|
}
|
|
fprintf(stderr,
|
|
"\n"
|
|
"General options:\n"
|
|
"-f fast mode This is not currently useful.\n"
|
|
"-i interactive use You will be asked to approve each action.\n"
|
|
"-v verbose output Display information about selected processes.\n"
|
|
"-w warnings enabled This is not currently useful.\n"
|
|
"-n no action This only displays the process ID.\n"
|
|
"\n"
|
|
"Selection criteria can be: terminal, user, pid, command.\n"
|
|
"The options below may be used to ensure correct interpretation.\n"
|
|
"-t The next argument is a terminal (tty or pty).\n"
|
|
"-u The next argument is a username.\n"
|
|
"-p The next argument is a process ID number.\n"
|
|
"-c The next argument is a command name.\n"
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
#if 0
|
|
static void _skillsnice_usage(int line){
|
|
fprintf(stderr,"Something at line %d.\n", line);
|
|
skillsnice_usage();
|
|
}
|
|
#define skillsnice_usage() _skillsnice_usage(__LINE__)
|
|
#endif
|
|
|
|
#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL)
|
|
|
|
/***** common skill/snice argument parsing code */
|
|
#define NO_PRI_VAL ((int)0xdeafbeef)
|
|
static void skillsnice_parse(int argc, const char *restrict const *restrict argv){
|
|
int signo = -1;
|
|
int prino = NO_PRI_VAL;
|
|
int force = 0;
|
|
int num_found = 0;
|
|
const char *restrict argptr;
|
|
if(argc<2) skillsnice_usage();
|
|
if(argc==2 && argv[1][0]=='-'){
|
|
if(!strcmp(argv[1],"-L")){
|
|
pretty_print_signals();
|
|
exit(0);
|
|
}
|
|
if(!strcmp(argv[1],"-l")){
|
|
unix_print_signals();
|
|
exit(0);
|
|
}
|
|
if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
|
|
display_kill_version();
|
|
exit(0);
|
|
}
|
|
skillsnice_usage();
|
|
}
|
|
NEXTARG;
|
|
/* Time for serious parsing. What does "skill -int 123 456" mean? */
|
|
while(argc){
|
|
if(force && !num_found){ /* if forced, _must_ find something */
|
|
fprintf(stderr,"ERROR: -%c used with bad data.\n", force);
|
|
skillsnice_usage();
|
|
}
|
|
force = 0;
|
|
if(program==PROG_SKILL && signo<0 && *argptr=='-'){
|
|
signo = signal_name_to_number(argptr+1);
|
|
if(signo>=0){ /* found a signal */
|
|
if(!NEXTARG) break;
|
|
continue;
|
|
}
|
|
}
|
|
if(program==PROG_SNICE && prino==NO_PRI_VAL
|
|
&& (*argptr=='+' || *argptr=='-') && argptr[1]){
|
|
long val;
|
|
char *endp;
|
|
val = strtol(argptr,&endp,10);
|
|
if(!*endp && val<=999 && val>=-999){
|
|
prino=val;
|
|
if(!NEXTARG) break;
|
|
continue;
|
|
}
|
|
}
|
|
/* If '-' found, collect any flags. (but lone "-" is a tty) */
|
|
if(*argptr=='-' && argptr[1]){
|
|
argptr++;
|
|
do{
|
|
switch(( force = *argptr++ )){
|
|
default: skillsnice_usage();
|
|
case 't':
|
|
case 'u':
|
|
case 'p':
|
|
case 'c':
|
|
if(!*argptr){ /* nothing left here, *argptr is '\0' */
|
|
if(!NEXTARG){
|
|
fprintf(stderr,"ERROR: -%c with nothing after it.\n", force);
|
|
skillsnice_usage();
|
|
}
|
|
}
|
|
goto selection_collection;
|
|
case 'f': f_flag++; break;
|
|
case 'i': i_flag++; break;
|
|
case 'v': v_flag++; break;
|
|
case 'w': w_flag++; break;
|
|
case 'n': n_flag++; break;
|
|
case 0:
|
|
NEXTARG;
|
|
/*
|
|
* If no more arguments, all the "if(argc)..." tests will fail
|
|
* and the big loop will exit.
|
|
*/
|
|
} /* END OF SWITCH */
|
|
}while(force);
|
|
} /* END OF IF */
|
|
selection_collection:
|
|
num_found = 0; /* we should find at least one thing */
|
|
switch(force){ /* fall through each data type */
|
|
default: skillsnice_usage();
|
|
case 0: /* not forced */
|
|
case 't':
|
|
if(argc){
|
|
struct stat sbuf;
|
|
char path[32];
|
|
if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */
|
|
snprintf(path,32,"/dev/%s",argptr);
|
|
if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){
|
|
num_found++;
|
|
ENLIST(tty,sbuf.st_rdev);
|
|
if(!NEXTARG) break;
|
|
}else if(!(argptr[1])){ /* if only 1 character */
|
|
switch(*argptr){
|
|
default:
|
|
if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */
|
|
case '-':
|
|
case '?':
|
|
num_found++;
|
|
ENLIST(tty,0);
|
|
if(!NEXTARG) break;
|
|
}
|
|
}
|
|
}
|
|
if(force) continue;
|
|
case 'u':
|
|
if(argc){
|
|
struct passwd *passwd_data;
|
|
passwd_data = getpwnam(argptr);
|
|
if(passwd_data){
|
|
num_found++;
|
|
ENLIST(uid,passwd_data->pw_uid);
|
|
if(!NEXTARG) break;
|
|
}
|
|
}
|
|
if(force) continue;
|
|
case 'p':
|
|
if(argc && *argptr>='0' && *argptr<='9'){
|
|
char *endp;
|
|
int num;
|
|
num = strtol(argptr, &endp, 0);
|
|
if(*endp == '\0'){
|
|
num_found++;
|
|
ENLIST(pid,num);
|
|
if(!NEXTARG) break;
|
|
}
|
|
}
|
|
if(force) continue;
|
|
if(num_found) continue; /* could still be an option */
|
|
case 'c':
|
|
if(argc){
|
|
num_found++;
|
|
ENLIST(cmd,argptr);
|
|
if(!NEXTARG) break;
|
|
}
|
|
} /* END OF SWITCH */
|
|
} /* END OF WHILE */
|
|
/* No more arguments to process. Must sanity check. */
|
|
if(!tty_count && !uid_count && !cmd_count && !pid_count){
|
|
fprintf(stderr,"ERROR: no process selection criteria.\n");
|
|
skillsnice_usage();
|
|
}
|
|
if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){
|
|
fprintf(stderr,"ERROR: general flags may not be repeated.\n");
|
|
skillsnice_usage();
|
|
}
|
|
if(i_flag && (v_flag|f_flag|n_flag)){
|
|
fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n");
|
|
skillsnice_usage();
|
|
}
|
|
if(v_flag && (i_flag|f_flag)){
|
|
fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n");
|
|
skillsnice_usage();
|
|
}
|
|
/* OK, set up defaults */
|
|
if(prino==NO_PRI_VAL) prino=4;
|
|
if(signo<0) signo=SIGTERM;
|
|
if(n_flag){
|
|
program=PROG_SKILL;
|
|
signo=0; /* harmless */
|
|
}
|
|
if(program==PROG_SKILL) sig_or_pri = signo;
|
|
else sig_or_pri = prino;
|
|
}
|
|
|
|
/***** main body */
|
|
int main(int argc, const char *argv[]){
|
|
const char *tmpstr;
|
|
my_pid = getpid();
|
|
saved_argc = argc;
|
|
if(!argc){
|
|
fprintf(stderr,"ERROR: could not determine own name.\n");
|
|
exit(1);
|
|
}
|
|
tmpstr=strrchr(*argv,'/');
|
|
if(tmpstr) tmpstr++;
|
|
if(!tmpstr) tmpstr=*argv;
|
|
program = PROG_GARBAGE;
|
|
if(*tmpstr=='s'){
|
|
setpriority(PRIO_PROCESS,my_pid,-20);
|
|
if(!strcmp(tmpstr,"snice")) program = PROG_SNICE;
|
|
if(!strcmp(tmpstr,"skill")) program = PROG_SKILL;
|
|
}else{
|
|
if(!strcmp(tmpstr,"kill")) program = PROG_KILL;
|
|
}
|
|
switch(program){
|
|
case PROG_SNICE:
|
|
case PROG_SKILL:
|
|
skillsnice_parse(argc, argv);
|
|
/* show_lists(); */
|
|
iterate(); /* this is it, go get them */
|
|
break;
|
|
case PROG_KILL:
|
|
kill_main(argc, argv);
|
|
break;
|
|
default:
|
|
fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|