2 * Copyright 1998-2002 by Albert Cahalan; all rights resered.
3 * This file may be used subject to the terms and conditions of the
4 * GNU Library General Public License Version 2, or any later version
5 * at your option, as published by the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU Library General Public License for more details.
19 #include <sys/resource.h>
22 #include <sys/types.h>
24 #include "proc/pwcache.h"
26 #include "proc/devname.h"
27 #include "proc/procps.h" /* char *user_from_uid(uid_t uid) */
28 #include "proc/version.h" /* procps_version */
30 static int f_flag, i_flag, v_flag, w_flag, n_flag;
32 static int tty_count, uid_count, cmd_count, pid_count;
35 static const char **cmds;
38 #define ENLIST(thing,addme) do{ \
39 if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \
40 if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \
41 thing##s[thing##_count++] = addme; \
45 static int saved_argc;
47 static int sig_or_pri;
50 #define PROG_GARBAGE 0 /* keep this 0 */
53 /* #define PROG_NICE 3 */ /* easy, but the old one isn't broken */
57 /********************************************************************/
59 static void display_kill_version(void){
62 fprintf(stdout, "kill (%s)\n",procps_version);
65 fprintf(stdout, "skill (%s)\n",procps_version);
68 fprintf(stdout, "snice (%s)\n",procps_version);
71 fprintf(stdout, "unknown (%s)\n",procps_version);
76 /***** kill or nice a process */
77 static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd){
81 dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
84 fprintf(stderr, "%-8s %-8s %5d %-16.16s ? ",
85 (char*)dn_buf,user_from_uid(uid),pid,cmd
87 if(!fgets(buf,7,stdin)){
91 if(*buf!='y' && *buf!='Y') return;
93 /* do the actual work */
94 if(program==PROG_SKILL) failed=kill(pid,sig_or_pri);
95 else failed=setpriority(PRIO_PROCESS,pid,sig_or_pri);
98 fprintf(stderr, "%-8s %-8s %5d %-16.16s ",
99 (char*)dn_buf,user_from_uid(uid),pid,cmd
107 printf("%-8s %-8s %5d %-16.16s\n",
108 (char*)dn_buf,user_from_uid(uid),pid,cmd
119 /***** check one process */
120 static void check_proc(int pid){
127 if(pid==my_pid) return;
128 sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
129 fd = open(buf,O_RDONLY);
130 if(fd==-1){ /* process exited maybe */
131 if(pids && w_flag) printf("WARNING: process %d could not be found.",pid);
135 if(uids){ /* check the EUID */
137 while(i--) if(uids[i]==statbuf.st_uid) break;
138 if(i==-1) goto closure;
142 tmp = strrchr(buf, ')');
144 i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */
148 while(i--) if(ttys[i]==tty) break;
149 if(i==-1) goto closure;
151 tmp = strchr(buf, '(') + 1;
154 /* fast comparison trick -- useful? */
155 while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break;
156 if(i==-1) goto closure;
158 /* This is where we kill/nice something. */
159 /* fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
160 pid, statbuf.st_uid, tty>>8, tty&0xf, tmp
163 hurt_proc(tty, statbuf.st_uid, pid, tmp);
165 close(fd); /* kill/nice _first_ to avoid PID reuse */
169 /***** debug function */
171 static void show_lists(void){
174 fprintf(stderr, "%d TTY: ", tty_count);
178 fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n');
180 }else fprintf(stderr, "\n");
182 fprintf(stderr, "%d UID: ", uid_count);
185 while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n');
186 }else fprintf(stderr, "\n");
188 fprintf(stderr, "%d PID: ", pid_count);
191 while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n');
192 }else fprintf(stderr, "\n");
194 fprintf(stderr, "%d CMD: ", cmd_count);
197 while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n');
198 }else fprintf(stderr, "\n");
203 /***** iterate over all PIDs */
204 static void iterate(void){
210 while(pid--) check_proc(pids[pid]);
214 /* could setuid() and kill -1 to have the kernel wipe out a user */
215 if(!ttys && !cmds && !pids && !i_flag){
218 d = opendir("/proc");
223 while(( de = readdir(d) )){
224 if(de->d_name[0] > '9') continue;
225 if(de->d_name[0] < '1') continue;
226 pid = atoi(de->d_name);
227 if(pid) check_proc(pid);
233 static void kill_usage(void) NORETURN;
234 static void kill_usage(void){
237 " kill pid ... Send SIGTERM to every process listed.\n"
238 " kill signal pid ... Send a signal to every process listed.\n"
239 " kill -s signal pid ... Send a signal to every process listed.\n"
240 " kill -l List all signal names.\n"
241 " kill -L List all signal names in a nice table.\n"
242 " kill -l signal Convert between signal numbers and names.\n"
248 static void kill_main(int argc, const char *restrict const *restrict argv) NORETURN;
249 static void kill_main(int argc, const char *restrict const *restrict argv){
253 if(argc<2) kill_usage();
254 if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
255 display_kill_version();
264 /* The -l option prints out signal names. */
265 if(argv[1][1]=='l' && argv[1][2]=='\0'){
267 unix_print_signals();
270 /* at this point, argc must be 3 or more */
271 if(argc>128 || argv[2][0] == '-') kill_usage();
272 exit(print_given_signals(argc-2, argv+2, 80));
275 /* The -L option prints out signal names in a nice table. */
276 if(argv[1][1]=='L' && argv[1][2]=='\0'){
278 pretty_print_signals();
283 if(argv[1][1]=='-' && argv[1][2]=='\0'){
288 if(argv[1][1]=='-') kill_usage(); /* likely --help */
289 // FIXME: "kill -sWINCH $$" not handled
290 if(argv[1][2]=='\0' && (argv[1][1]=='s' || argv[1][1]=='n')){
299 signo = signal_name_to_number(sigptr);
301 fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr);
305 if(!argc) kill_usage(); /* nothing to kill? */
309 pid = strtol(argv[argc],&endp,10);
311 if(!kill((pid_t)pid,signo)) continue;
312 // The UNIX standard contradicts itself. If at least one process
313 // is matched for each PID (as if processes could share PID!) and
314 // "the specified signal was successfully processed" (the systcall
315 // returned zero?) for at least one of those processes, then we must
316 // exit with zero. Note that an error might have also occured.
317 // The standard says we return non-zero if an error occurs. Thus if
318 // killing two processes gives 0 for one and EPERM for the other,
319 // we are required to return both zero and non-zero. Quantum kill???
323 fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]);
329 /***** skill/snice help */
330 static void skillsnice_usage(void) NORETURN;
331 static void skillsnice_usage(void){
332 if(program==PROG_SKILL){
334 "Usage: skill [signal to send] [options] process selection criteria\n"
335 "Example: skill -KILL -v pts/*\n"
337 "The default signal is TERM. Use -l or -L to list available signals.\n"
338 "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
339 "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"
343 "Usage: snice [new priority] [options] process selection criteria\n"
344 "Example: snice netscape crack +7\n"
346 "The default priority is +4. (snice +4 ...)\n"
347 "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
348 "Negative priority numbers are restricted to administrative users.\n"
354 "-f fast mode This is not currently useful.\n"
355 "-i interactive use You will be asked to approve each action.\n"
356 "-v verbose output Display information about selected processes.\n"
357 "-w warnings enabled This is not currently useful.\n"
358 "-n no action This only displays the process ID.\n"
360 "Selection criteria can be: terminal, user, pid, command.\n"
361 "The options below may be used to ensure correct interpretation.\n"
362 "-t The next argument is a terminal (tty or pty).\n"
363 "-u The next argument is a username.\n"
364 "-p The next argument is a process ID number.\n"
365 "-c The next argument is a command name.\n"
371 static void _skillsnice_usage(int line){
372 fprintf(stderr,"Something at line %d.\n", line);
375 #define skillsnice_usage() _skillsnice_usage(__LINE__)
378 #define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL)
380 /***** common skill/snice argument parsing code */
381 #define NO_PRI_VAL ((int)0xdeafbeef)
382 static void skillsnice_parse(int argc, const char *restrict const *restrict argv){
384 int prino = NO_PRI_VAL;
387 const char *restrict argptr;
388 if(argc<2) skillsnice_usage();
389 if(argc==2 && argv[1][0]=='-'){
390 if(!strcmp(argv[1],"-L")){
391 pretty_print_signals();
394 if(!strcmp(argv[1],"-l")){
395 unix_print_signals();
398 if(!strcmp(argv[1],"-V")|| !strcmp(argv[1],"--version")){
399 display_kill_version();
405 /* Time for serious parsing. What does "skill -int 123 456" mean? */
407 if(force && !num_found){ /* if forced, _must_ find something */
408 fprintf(stderr,"ERROR: -%c used with bad data.\n", force);
412 if(program==PROG_SKILL && signo<0 && *argptr=='-'){
413 signo = signal_name_to_number(argptr+1);
414 if(signo>=0){ /* found a signal */
419 if(program==PROG_SNICE && prino==NO_PRI_VAL
420 && (*argptr=='+' || *argptr=='-') && argptr[1]){
423 val = strtol(argptr,&endp,10);
424 if(!*endp && val<=999 && val>=-999){
430 /* If '-' found, collect any flags. (but lone "-" is a tty) */
431 if(*argptr=='-' && argptr[1]){
434 switch(( force = *argptr++ )){
435 default: skillsnice_usage();
440 if(!*argptr){ /* nothing left here, *argptr is '\0' */
442 fprintf(stderr,"ERROR: -%c with nothing after it.\n", force);
446 goto selection_collection;
447 case 'f': f_flag++; break;
448 case 'i': i_flag++; break;
449 case 'v': v_flag++; break;
450 case 'w': w_flag++; break;
451 case 'n': n_flag++; break;
455 * If no more arguments, all the "if(argc)..." tests will fail
456 * and the big loop will exit.
458 } /* END OF SWITCH */
461 selection_collection:
462 num_found = 0; /* we should find at least one thing */
463 switch(force){ /* fall through each data type */
464 default: skillsnice_usage();
465 case 0: /* not forced */
470 if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */
471 snprintf(path,32,"/dev/%s",argptr);
472 if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){
474 ENLIST(tty,sbuf.st_rdev);
476 }else if(!(argptr[1])){ /* if only 1 character */
479 if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */
491 struct passwd *passwd_data;
492 passwd_data = getpwnam(argptr);
495 ENLIST(uid,passwd_data->pw_uid);
501 if(argc && *argptr>='0' && *argptr<='9'){
504 num = strtol(argptr, &endp, 0);
512 if(num_found) continue; /* could still be an option */
519 } /* END OF SWITCH */
521 /* No more arguments to process. Must sanity check. */
522 if(!tty_count && !uid_count && !cmd_count && !pid_count){
523 fprintf(stderr,"ERROR: no process selection criteria.\n");
526 if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){
527 fprintf(stderr,"ERROR: general flags may not be repeated.\n");
530 if(i_flag && (v_flag|f_flag|n_flag)){
531 fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n");
534 if(v_flag && (i_flag|f_flag)){
535 fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n");
538 /* OK, set up defaults */
539 if(prino==NO_PRI_VAL) prino=4;
540 if(signo<0) signo=SIGTERM;
543 signo=0; /* harmless */
545 if(program==PROG_SKILL) sig_or_pri = signo;
546 else sig_or_pri = prino;
550 int main(int argc, const char *argv[]){
555 fprintf(stderr,"ERROR: could not determine own name.\n");
558 tmpstr=strrchr(*argv,'/');
560 if(!tmpstr) tmpstr=*argv;
561 program = PROG_GARBAGE;
563 setpriority(PRIO_PROCESS,my_pid,-20);
564 if(!strcmp(tmpstr,"snice")) program = PROG_SNICE;
565 if(!strcmp(tmpstr,"skill")) program = PROG_SKILL;
567 if(!strcmp(tmpstr,"kill")) program = PROG_KILL;
572 skillsnice_parse(argc, argv);
574 iterate(); /* this is it, go get them */
577 kill_main(argc, argv);
580 fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr);