/* * Copyright 1998-2002 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "c.h" #include "nls.h" #include "xalloc.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 */ struct run_time_conf_t { int fast; int interactive; int verbose; int warnings; int noaction; int debugging; }; 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 = xmalloc(sizeof(*thing##s)*saved_argc); \ thing##s[thing##_count++] = addme; \ }while(0) static int my_pid; static int saved_argc; static int sig_or_pri; enum { PROG_UNKNOWN, PROG_KILL, PROG_SKILL, PROG_SNICE }; static int program = PROG_UNKNOWN; static void display_kill_version(void) { fprintf(stdout, PROCPS_NG_VERSION); } /* kill or nice a process */ static void hurt_proc(int tty, int uid, int pid, const char *restrict const cmd, struct run_time_conf_t *run_time) { int failed; int saved_errno; char dn_buf[1000]; dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV); if (run_time->interactive) { char buf[8]; fprintf(stderr, "%-8s %-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 (run_time->warnings && failed) { fprintf(stderr, "%-8s %-8s %5d %-16.16s ", (char *)dn_buf, user_from_uid(uid), pid, cmd); errno = saved_errno; perror(""); return; } if (run_time->interactive) return; if (run_time->verbose) { printf("%-8s %-8s %5d %-16.16s\n", (char *)dn_buf, user_from_uid(uid), pid, cmd); return; } if (run_time->noaction) { printf("%d\n", pid); return; } } /* check one process */ static void check_proc(int pid, struct run_time_conf_t *run_time) { char buf[128]; struct stat statbuf; char *tmp; int tty; int fd; int i; if (pid == my_pid) return; /* pid (cmd) state ppid pgrp session tty */ sprintf(buf, "/proc/%d/stat", pid); fd = open(buf, O_RDONLY); if (fd == -1) { /* process exited maybe */ if (pids && run_time->warnings) printf(_("WARNING: process %d could not be found.\n"), 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. */ /* for debugging purposes? 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, run_time); closure: /* kill/nice _first_ to avoid PID reuse */ close(fd); } /* debug function */ 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"); } /* iterate over all PIDs */ static void iterate(struct run_time_conf_t *run_time) { int pid; DIR *d; struct dirent *de; if (pids) { pid = pid_count; while (pid--) check_proc(pids[pid], run_time); return; } #if 0 /* could setuid() and kill -1 to have the kernel wipe out a user */ if (!ttys && !cmds && !pids && !run_time->interactive) { } #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, run_time); } closedir(d); } /* kill help */ static void __attribute__ ((__noreturn__)) kill_usage(void) { fputs(USAGE_HEADER, stderr); fprintf(stderr, " %s [options] [...]\n", program_invocation_short_name); fputs(USAGE_OPTIONS, stderr); fputs(_(" [...] send SIGTERM to every listed\n"), stderr); fputs(_(" - specify the to be sent\n"), stderr); fputs(_(" -s specify the to be sent\n"), stderr); fputs(_(" -l list all signal names\n"), stderr); fputs(_(" -L list all signal names in a nice table\n"), stderr); fputs(_(" -l convert between signal numbers and names\n"), stderr); fputs(USAGE_SEPARATOR, stderr); fprintf(stderr, USAGE_MAN_TAIL("skill(1)")); exit(1); } /* skill and snice help */ static void __attribute__ ((__noreturn__)) skillsnice_usage(void) { fputs(USAGE_HEADER, stderr); if (program == PROG_SKILL) { fprintf(stderr, " %s [signal] [options] \n", program_invocation_short_name); } else { fprintf(stderr, " %s [new priority] [options] \n", program_invocation_short_name); } fputs(USAGE_OPTIONS, stderr); fputs(_(" -f fast mode (not implemented)\n"), stderr); fputs(_(" -i interactive\n"), stderr); fputs(_(" -l list all signal names\n"), stderr); fputs(_(" -L list all signal names in a nice table\n"), stderr); fputs(_(" -n no action\n"), stderr); fputs(_(" -v explain what is being done\n"), stderr); fputs(_(" -w enable warnings (not implemented)\n"), stderr); fputs(USAGE_VERSION, stderr); fputs(_("\n"), stderr); fputs(_("Expression can be: terminal, user, pid, command.\n"), stderr); fputs(_("The options below may be used to ensure correct interpretation.\n"), stderr); fputs(_(" -c expression is a command name\n"), stderr); fputs(_(" -p expression is a process id number\n"), stderr); fputs(_(" -t expression is a terminal\n"), stderr); fputs(_(" -u expression is a username\n"), stderr); if (program == PROG_SKILL) { fprintf(stderr, _("\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")); fprintf(stderr, USAGE_MAN_TAIL("skill(1)")); } else { fprintf(stderr, _("\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, USAGE_MAN_TAIL("snice(1)")); } exit(1); } /* kill */ static void __attribute__ ((__noreturn__)) kill_main(int argc, const char *restrict const *restrict argv, struct run_time_conf_t *run_time) { 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 */ // FIXME: "kill -sWINCH $$" not handled if (argv[1][2] == '\0' && (argv[1][1] == 's' || argv[1][1] == 'n')) { 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 && (endp != argv[argc])) { if (!kill((pid_t) pid, signo)) continue; /* * The UNIX standard contradicts itself. If at least * one process is matched for each PID (as if * processes could share PID!) and "the specified * signal was successfully processed" (the systcall * returned zero?) for at least one of those * processes, then we must exit with zero. Note that * an error might have also occured. The standard * says we return non-zero if an error occurs. Thus * if killing two processes gives 0 for one and EPERM * for the other, we are required to return both zero * and non-zero. Quantum kill??? */ perror("kill"); exitvalue = 1; continue; } fprintf(stderr, _("ERROR: garbage process ID \"%s\".\n"), argv[argc]); kill_usage(); } exit(exitvalue); } #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, struct run_time_conf_t *run_time) { 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': run_time->fast++; break; case 'i': run_time->interactive++; break; case 'v': run_time->verbose++; break; case 'w': run_time->warnings++; break; case 'n': run_time->noaction++; 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 */ if (argptr && argptr[0] == '-') /* its the next argument not a parameter */ continue; case 't': if (argc) { struct stat sbuf; char path[32]; if (!argptr) /* Huh? Maybe "skill -t ''". */ skillsnice_usage(); 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) /* the shell eats '?' */ break; 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) /* could still be an option */ continue; 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 ((run_time->fast | run_time->interactive | run_time->verbose | run_time->warnings | run_time->noaction) & ~1) { fprintf(stderr, _("ERROR: general flags may not be repeated.\n")); skillsnice_usage(); } if (run_time->interactive && (run_time->verbose | run_time->fast | run_time->noaction)) { fprintf(stderr, _("ERROR: -i makes no sense with -v, -f, and -n.\n")); skillsnice_usage(); } if (run_time->verbose && (run_time->interactive | run_time->fast)) { 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 (run_time->noaction) { program = PROG_SKILL; /* harmless */ signo = 0; } if (program == PROG_SKILL) sig_or_pri = signo; else sig_or_pri = prino; } /* main body */ int main(int argc, const char *argv[]) { struct run_time_conf_t run_time; memset(&run_time, 0, sizeof(struct run_time_conf_t)); my_pid = getpid(); if (strcmp(program_invocation_short_name, "kill") == 0 || strcmp(program_invocation_short_name, "lt-kill") == 0) program = PROG_KILL; else if (strcmp(program_invocation_short_name, "skill") == 0 || strcmp(program_invocation_short_name, "lt-skill") == 0) program = PROG_SKILL; else if (strcmp(program_invocation_short_name, "snice") == 0 || strcmp(program_invocation_short_name, "lt-snice") == 0) program = PROG_SNICE; switch (program) { case PROG_SNICE: case PROG_SKILL: setpriority(PRIO_PROCESS, my_pid, -20); skillsnice_parse(argc, argv, &run_time); if (run_time.debugging) show_lists(); iterate(&run_time); break; case PROG_KILL: kill_main(argc, argv, &run_time); break; default: fprintf(stderr, _("skill: \"%s\" is not support\n"), program_invocation_short_name); fprintf(stderr, USAGE_MAN_TAIL("skill(1)")); return 1; } return 0; }