diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 13:17:26 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 13:17:26 +0000 |
commit | a4dc4fbcc1b131061dcccd5cdcc2395d9a2ba784 (patch) | |
tree | bc531e7e083fff1668bb4e79e9692d8cc47aea95 /src/killall.c | |
parent | Initial commit. (diff) | |
download | psmisc-upstream.tar.xz psmisc-upstream.zip |
Adding upstream version 23.7.upstream/23.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/killall.c')
-rw-r--r-- | src/killall.c | 1069 |
1 files changed, 1069 insertions, 0 deletions
diff --git a/src/killall.c b/src/killall.c new file mode 100644 index 0000000..81dcc4b --- /dev/null +++ b/src/killall.c @@ -0,0 +1,1069 @@ +/* + * killall.c - kill processes by name or list PIDs + * + * Copyright (C) 1993-2002 Werner Almesberger + * Copyright (C) 2002-2024 Craig Small <csmall@dropbear.xyz> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <signal.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <fcntl.h> +#include <getopt.h> +#include <pwd.h> +#include <regex.h> +#include <ctype.h> +#include <assert.h> + +#ifdef WITH_SELINUX +#include <dlfcn.h> +#include <selinux/selinux.h> +#endif /*WITH_SELINUX*/ + +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif /* HAVE_LOCALE_H */ + +#include "i18n.h" +#include "comm.h" +#include "signals.h" + +#define PROC_BASE "/proc" +#define MAX_NAMES (int)(sizeof(unsigned long)*8) + +#define TSECOND "s" +#define TMINUTE "m" +#define THOUR "h" +#define TDAY "d" +#define TWEEK "w" +#define TMONTH "M" +#define TYEAR "y" + +#define TMAX_SECOND 31536000 +#define TMAX_MINUTE 525600 +#define TMAX_HOUR 8760 +#define TMAX_DAY 365 +#define TMAX_WEEK 48 +#define TMAX_MONTH 12 +#define TMAX_YEAR 1 + +#define ER_REGFAIL -1 +#define ER_NOMEM -2 +#define ER_UNKWN -3 +#define ER_OOFRA -4 + +static pid_t opt_ns_pid = 0; + +static int verbose = 0, exact = 0, interactive = 0, reg = 0, + quiet = 0, wait_until_dead = 0, process_group = 0, + ignore_case = 0; +static long younger_than = 0, older_than = 0; + +typedef struct NAMEINFO { + const char *name; + int name_length; + struct stat st; +} NAMEINFO; + +static int +ask (char *name, pid_t pid, const int signal) +{ + int res; + size_t len; + char *line; + + line = NULL; + len = 0; + + do { + if (signal == SIGTERM) + printf (_("Kill %s(%s%d) ? (y/N) "), name, process_group ? "pgid " : "", + pid); + else + printf (_("Signal %s(%s%d) ? (y/N) "), name, process_group ? "pgid " : "", + pid); + + fflush (stdout); + + if (getline (&line, &len, stdin) < 0) + return 0; + /* Check for default */ + if (line[0] == '\n') { + free(line); + return 0; + } + res = rpmatch(line); + if (res >= 0) { + free(line); + return res; + } + } while(1); + /* Never should get here */ +} + +static double +uptime() +{ + char * savelocale; + char buf[2048]; + FILE* file; + if (!(file=fopen( PROC_BASE "/uptime", "r"))) { + fprintf(stderr, "killall: error opening uptime file\n"); + exit(1); + } + savelocale = setlocale(LC_NUMERIC,"C"); + if (fscanf(file, "%2047s", buf) == EOF) perror("uptime"); + fclose(file); + setlocale(LC_NUMERIC,savelocale); + return atof(buf); +} + +/* process age from jiffies to seconds via uptime */ +static double process_age(const unsigned long long jf) +{ + double age; + double sc_clk_tck = sysconf(_SC_CLK_TCK); + assert(sc_clk_tck > 0); + age = uptime() - jf / sc_clk_tck; + if (age < 0L) + return 0L; + return age; +} + +/* returns requested time interval in seconds, + negative indicates error has occurred + */ +static long +parse_time_units(const char* age) +{ + char *unit; + long num; + + num = strtol(age,&unit,10); + if (age == unit) /* no digits found */ + return -1; + if (unit[0] == '\0') /* no units found */ + return -1; + + switch(unit[0]) { + case 's': + return num; + case 'm': + return (num * 60); + case 'h': + return (num * 60 * 60); + case 'd': + return (num * 60 * 60 * 24); + case 'w': + return (num * 60 * 60 * 24 * 7); + case 'M': + return (num * 60 * 60 * 24 * 7 * 4); + case 'y': + return (num * 60 * 60 * 24 * 7 * 4 * 12); + } + return -1; +} + +enum ns_type { + IPCNS = 0, + MNTNS, + NETNS, + PIDNS, + USERNS, + UTSNS +}; + +static const char *ns_names[] = { + [IPCNS] = "ipc", + [MNTNS] = "mnt", + [NETNS] = "net", + [PIDNS] = "pid", + [USERNS] = "user", + [UTSNS] = "uts", +}; + +#define NUM_NS 6 + +const char *get_ns_name(int id) { + if (id >= NUM_NS) + return NULL; + return ns_names[id]; +} + +static int get_ns(pid_t pid, int id) { + struct stat st; + char buff[50]; + snprintf(buff, sizeof(buff), "/proc/%i/ns/%s", pid, get_ns_name(id)); + if (stat(buff, &st)) + return 0; + else + return st.st_ino; +} + +static int +match_process_uid(const int pidfd, uid_t uid) +{ + char buf[128]; + uid_t puid; + FILE *f; + int fd; + int re = -1; + + if ( (fd = openat(pidfd, "status", O_RDONLY, 0)) < 0) + return 0; + if (!(f = fdopen (fd, "r"))) + { + close(fd); + return 0; + } + + while (fgets(buf, sizeof buf, f)) + { + if (sscanf (buf, "Uid:\t%d", &puid)) + { + re = uid==puid; + break; + } + } + close(fd); + if (re==-1) + { + fprintf(stderr, _("killall: Cannot get UID from process status\n")); + exit(1); + } + return re; +} + +/* Match on the given scontext to the process context + * Return 0 on a match + */ +static int +match_process_context(const pid_t pid, const regex_t *scontext) +{ + static void (*my_freecon)(char*) = NULL; + static int (*my_getpidcon)(pid_t pid, char **context) = NULL; + static int selinux_enabled = 0; + char *lcontext; + int retval = 1; + +#ifdef WITH_SELINUX + static int tried_load = 0; + static int (*my_is_selinux_enabled)(void) = NULL; + + if(!my_getpidcon && !tried_load){ + void *handle = dlopen("libselinux.so.1", RTLD_NOW); + if(handle) { + my_freecon = dlsym(handle, "freecon"); + if(dlerror()) + my_freecon = NULL; + my_getpidcon = dlsym(handle, "getpidcon"); + if(dlerror()) + my_getpidcon = NULL; + my_is_selinux_enabled = dlsym(handle, "is_selinux_enabled"); + if(dlerror()) + my_is_selinux_enabled = 0; + else + selinux_enabled = my_is_selinux_enabled(); + } + tried_load++; + } +#endif /* WITH_SELINUX */ + + if (my_getpidcon && selinux_enabled && !my_getpidcon(pid, &lcontext)) { + retval = (regexec(scontext, lcontext, 0, NULL, 0) ==0); + my_freecon(lcontext); + } else { + FILE *file; + char path[50]; + char readbuf[BUFSIZ+1]; + snprintf(path, sizeof path, "/proc/%d/attr/current", pid); + if ( (file = fopen(path, "r")) != NULL) { + if (fgets(readbuf, BUFSIZ, file) != NULL) { + retval = (regexec(scontext, readbuf, 0, NULL, 0)==0); + } + fclose(file); + } + } + return retval; +} + +static int +my_send_signal( + const int pidfd, + const pid_t pid, + const int sig) +{ +#ifdef __NR_pidfd_send_signal + if (pid > 0) /* Not PGID */ + { + int ret = syscall(__NR_pidfd_send_signal, pidfd, sig, NULL, 0); + if (ret >= 0 || errno != ENOSYS) + return ret; + // fall through if no such syscall + } +#endif + return kill(pid, sig); +} + +static void +free_regexp_list(regex_t *reglist, int names) +{ + int i; + for (i = 0; i < names; i++) + regfree(®list[i]); + free(reglist); +} + +static regex_t * +build_regexp_list(int names, char **namelist) +{ + int i; + regex_t *reglist; + int flag = REG_EXTENDED|REG_NOSUB; + + if (!(reglist = malloc (sizeof (regex_t) * names))) + { + perror ("malloc"); + exit (1); + } + + if (ignore_case) + flag |= REG_ICASE; + + for (i = 0; i < names; i++) + { + if (regcomp(®list[i], namelist[i], flag) != 0) + { + fprintf(stderr, _("killall: Bad regular expression: %s\n"), namelist[i]); + free_regexp_list(reglist, i); + exit (1); + } + } + return reglist; +} + +static NAMEINFO * +build_nameinfo(const int names, char **namelist) +{ + int i; + NAMEINFO *ni = NULL; + if ( (ni = malloc(sizeof(NAMEINFO) * names)) == NULL) + return NULL; + + for (i = 0; i < names; i++) + { + ni[i].name = namelist[i]; + ni[i].st.st_dev = 0; + if (!strchr (namelist[i], '/')) + { + ni[i].name_length = strlen (namelist[i]); + } + else if (stat (namelist[i], &(ni[i].st)) < 0) + { + perror (namelist[i]); + free(ni); + return NULL; + } + } + return ni; +} + +static int +load_process_name_and_age(char *comm, double *process_age_sec, + const int pidfd, int load_age) +{ + int fd; + FILE *file; + char buf[1024]; + char *startcomm, *endcomm; + unsigned lencomm; + *process_age_sec = 0; + + if ( (fd = openat(pidfd, "stat", O_RDONLY, 0)) < 0) + return -1; + if (!(file = fdopen (fd, "r"))) + { + close(fd); + return -1; + } + if (fgets(buf, 1024, file) == NULL) + { + close(fd); + return -1; + } + close(fd); + if ( NULL == ( startcomm = strchr(buf, '('))) + return -1; + startcomm++; + if ( NULL == ( endcomm = strrchr(startcomm, ')'))) + return -1; + lencomm = endcomm - startcomm; + if (lencomm > COMM_LEN -1) + lencomm = COMM_LEN -1; + strncpy(comm, startcomm, lencomm); + comm[lencomm] = '\0'; + + endcomm += 2; // skip ") " + if (load_age) + { + unsigned long long proc_stt_jf = 0; + if (sscanf(endcomm, "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %Lu", + &proc_stt_jf) != 1) + { + return -1; + } + *process_age_sec = process_age(proc_stt_jf); + } + return lencomm; +} + +static int +load_proc_cmdline(const int pidfd, const pid_t pid, const char *comm, const int check_comm_length, char **command, int *got_long) +{ + FILE *file; + int fp; + char *p, *command_buf; + int cmd_size = 128; + int okay; + + if ( (fp = openat(pidfd, "cmdline", O_RDONLY, 0)) < 0) + return -1; + + if (!(file = fdopen (fp, "r"))) + { + close(fp); + return -1; + } + + if ( (command_buf = (char *)malloc (cmd_size)) == NULL) + exit(1); + + while (1) + { + /* look for actual command so we skip over initial "sh" if any */ + + /* 'cmdline' has arguments separated by nulls */ + for (p=command_buf; ; p++) + { + int c; + if (p == (command_buf + cmd_size)) + { + char *new_command_buf; + int cur_size = cmd_size; + cmd_size *= 2; + new_command_buf = (char *)realloc(command_buf, cmd_size); + if (!new_command_buf) { + if (command_buf) + free(command_buf); + exit (1); + } + command_buf = new_command_buf; + p = command_buf + cur_size; + } + c = fgetc(file); + if (c == EOF || c == '\0') + { + *p = '\0'; + break; + } else { + *p = c; + } + } + if (strlen(command_buf) == 0) { + okay = 0; + break; + } + p = strrchr(command_buf,'/'); + p = p ? p+1 : command_buf; + if (strncmp(p, comm, check_comm_length) == 0) { + okay = 1; + if (!(*command = strdup(p))) { + free(command_buf); + exit(1); + } + break; + } + } + (void) close(fp); + free(command_buf); + command_buf = NULL; + + if (exact && !okay) + { + if (verbose) + fprintf (stderr, _("killall: skipping partial match %s(%d)\n"), + comm, pid); + *got_long = okay; + return -1; + } + *got_long = okay; + return 0; +} + +static pid_t * +create_pid_table(int *max_pids, int *pids) +{ + pid_t self, *pid_table, *realloc_pid_table; + int pid; + DIR *dir; + struct dirent *de; + + self = getpid (); + if (!(dir = opendir (PROC_BASE))) + { + perror (PROC_BASE); + exit (1); + } + *max_pids = 256; + pid_table = malloc (*max_pids * sizeof (pid_t)); + if (!pid_table) + { + perror ("malloc"); + exit (1); + } + *pids = 0; + while ( (de = readdir (dir)) != NULL) + { + if (!(pid = (pid_t) atoi (de->d_name)) || pid == self) + continue; + if (*pids == *max_pids) + { + if (!(realloc_pid_table = realloc (pid_table, 2 * *pids * sizeof (pid_t)))) + { + perror ("realloc"); + free(pid_table); + exit (1); + } + pid_table = realloc_pid_table; + *max_pids *= 2; + } + pid_table[(*pids)++] = pid; + } + (void) closedir (dir); + return pid_table; +} + +#define strcmp2(A,B,I) (I? strcasecmp((A),(B)):strcmp((A),(B))) +#define strncmp2(A,B,L,I) (I? strncasecmp((A),(B),(L)):strncmp((A),(B),(L))) +static int match_process_name( + const char *proc_comm, + const int comm_len, + const char *proc_cmdline, + const char *match_name, + const int match_len, + const int got_long + ) +{ + /* process is old length but matching longer */ + if (comm_len == OLD_COMM_LEN - 1 && match_len >= OLD_COMM_LEN - 1) + { + if (got_long) + { + return (0 == strcmp2 (match_name, proc_cmdline, ignore_case)); + } else { + return (0 == strncmp2 (match_name, proc_comm, OLD_COMM_LEN - 1, + ignore_case)); + } + } + + if (comm_len == COMM_LEN - 1 && match_len >= COMM_LEN - 1) + { + if (got_long) + { + return (0 == strcmp2 (match_name, proc_cmdline, ignore_case)); + } else { + return (0 == strncmp2 (match_name, proc_comm, COMM_LEN - 1, + ignore_case)); + } + } + /* Not old new COMM_LEN so we match all of it */ + if (got_long) + { + return (0 == strcmp2 (match_name, proc_cmdline, ignore_case)); + } + return (0 == strcmp2 (match_name, proc_comm, ignore_case)); +} + +static int +kill_all(int signal, int name_count, char **namelist, struct passwd *pwent, + regex_t *scontext ) +{ + struct stat st; + NAMEINFO *name_info = NULL; + char comm[COMM_LEN]; + char *command = NULL; + pid_t *pid_table, *pid_killed; + pid_t *pgids = NULL; + int i, j, length, got_long, error; + int pids, max_pids, pids_killed; + int pidfd = 0; + unsigned long found; + regex_t *reglist = NULL; + long ns_ino = 0; + + if (opt_ns_pid) + ns_ino = get_ns(opt_ns_pid, PIDNS); + + if (name_count && reg) + reglist = build_regexp_list(name_count, namelist); + else + if ( (name_info = build_nameinfo(name_count, namelist)) == NULL) + exit(1); + + pid_table = create_pid_table(&max_pids, &pids); + found = 0; + pids_killed = 0; + pid_killed = malloc (max_pids * sizeof (pid_t)); + if (!pid_killed) + { + perror ("malloc"); + exit (1); + } + if (process_group) + { + pgids = calloc (pids, sizeof (pid_t)); + if (!pgids) + { + perror ("malloc"); + exit (1); + } + } + got_long = 0; + for (i = 0; i < pids; i++) + { + pid_t id; + int found_name = -1; + double process_age_sec = 0; + char pidpath[256]; + + /* Open PID directory */ + if (pidfd > 0) + close(pidfd); + snprintf (pidpath, sizeof pidpath, PROC_BASE "/%d", pid_table[i]); + if ( (pidfd = open(pidpath, O_RDONLY|O_DIRECTORY)) < 0) + continue; + /* match by UID */ + if (pwent && match_process_uid(pidfd, pwent->pw_uid)==0) + continue; + if (opt_ns_pid && ns_ino && ns_ino != get_ns(pid_table[i], PIDNS)) + continue; + + if (scontext && match_process_context(pid_table[i], scontext) == 0) + continue; + + length = load_process_name_and_age(comm, &process_age_sec, pidfd, (younger_than||older_than)); + if (length < 0) + continue; + + /* test for process age, if required */ + if ( younger_than && (process_age_sec > younger_than ) ) + continue; + if ( older_than && (process_age_sec < older_than ) ) + continue; + + got_long = 0; + if (command) { + free(command); + command = NULL; + } + + if (length == COMM_LEN - 1 || length == OLD_COMM_LEN - 1) + if (load_proc_cmdline(pidfd, pid_table[i], comm, length, &command, &got_long) < 0) + continue; + + /* match by process name */ + for (j = 0; j < name_count; j++) + { + if (reg) + { + if (regexec (®list[j], got_long ? command : comm, 0, NULL, 0) != 0) + continue; + } + else /* non-regex */ + { + if (!name_info[j].st.st_dev) + { + if (!match_process_name(comm, length, command, namelist[j], + name_info[j].name_length, got_long)) + continue; + + } else { + int ok = 1; + if (fstatat(pidfd, "exe", &st, 0) < 0) + ok = 0; + else if (name_info[j].st.st_dev != st.st_dev || + name_info[j].st.st_ino != st.st_ino) + { + /* maybe the binary has been modified and std[j].st_ino + * is not reliable anymore. We need to compare paths. + */ + size_t len = strlen(namelist[j]); + char *linkbuf = malloc(len + 1); + + if (!linkbuf || + readlinkat(pidfd, "exe", linkbuf, len + 1) != (ssize_t)len || + memcmp(namelist[j], linkbuf, len)) + ok = 0; + free(linkbuf); + } + if (!ok) + continue; + } + } /* non-regex */ + found_name = j; + break; + } + if (name_count && found_name==-1) + continue; /* match by process name faild */ + + /* check for process group */ + if (!process_group) + id = pid_table[i]; + else + { + int j; + + id = getpgid (pid_table[i]); + pgids[i] = id; + if (id < 0) + { + fprintf (stderr, "killall: getpgid(%d): %s\n", + pid_table[i], strerror (errno)); + } + for (j = 0; j < i; j++) + if (pgids[j] == id) + break; + if (j < i) + continue; + } + if (interactive && !ask (comm, id, signal)) + continue; + if (my_send_signal (pidfd, process_group ? -id : id, signal) >= 0) + { + if (verbose) + fprintf (stderr, _("Killed %s(%s%d) with signal %d\n"), got_long ? command : + comm, process_group ? "pgid " : "", id, signal); + if (found_name >= 0) + /* mark item of namelist */ + found |= 1UL << found_name; + pid_killed[pids_killed++] = id; + } + else if (errno != ESRCH || interactive) + fprintf (stderr, "%s(%d): %s\n", got_long ? command : + comm, id, strerror (errno)); + } + if (command) + free(command); + if (reglist) + free_regexp_list(reglist, name_count); + free(pgids); + if (pidfd > 0) + close(pidfd); + if (!quiet) + for (i = 0; i < name_count; i++) + if (!(found & (1UL << i))) + fprintf (stderr, _("%s: no process found\n"), namelist[i]); + if (name_count) + /* killall returns a zero return code if at least one process has + * been killed for each listed command. */ + error = found == ((1UL << (name_count - 1)) | ((1UL << (name_count - 1)) - 1)) ? 0 : 1; + else + /* in nameless mode killall returns a zero return code if at least + * one process has killed */ + error = pids_killed ? 0 : 1; + /* + * We scan all (supposedly) killed processes every second to detect dead + * processes as soon as possible in order to limit problems of race with + * PID re-use. + */ + while (pids_killed && wait_until_dead) + { + for (i = 0; i < pids_killed;) + { + if (kill (process_group ? -pid_killed[i] : pid_killed[i], 0) < 0 && + errno == ESRCH) + { + pid_killed[i] = pid_killed[--pids_killed]; + continue; + } + i++; + } + sleep (1); /* wait a bit longer */ + } + free(pid_killed); + free(pid_table); + free(name_info); + return error; +} + + +static void +usage (const char *msg) +{ + if (msg != NULL) + fprintf(stderr, "%s\n", msg); + fprintf(stderr, _( + "Usage: killall [OPTION]... [--] NAME...\n")); + fprintf(stderr, _( + " killall -l, --list\n" + " killall -V, --version\n\n" + " -e,--exact require exact match for very long names\n" + " -I,--ignore-case case insensitive process name match\n" + " -g,--process-group kill process group instead of process\n" + " -y,--younger-than kill processes younger than TIME\n" + " -o,--older-than kill processes older than TIME\n" + " -i,--interactive ask for confirmation before killing\n" + " -l,--list list all known signal names\n" + " -q,--quiet don't print complaints\n" + " -r,--regexp interpret NAME as an extended regular expression\n" + " -s,--signal SIGNAL send this signal instead of SIGTERM\n" + " -u,--user USER kill only process(es) running as USER\n" + " -v,--verbose report if the signal was successfully sent\n" + " -V,--version display version information\n" + " -w,--wait wait for processes to die\n" + " -n,--ns PID match processes that belong to the same namespaces\n" + " as PID\n")); + + fprintf(stderr, _( + " -Z,--context REGEXP kill only process(es) having context\n" + " (must precede other arguments)\n")); + fputc('\n', stderr); + exit(1); +} + + +void print_version() +{ + fprintf(stderr, "killall (PSmisc) %s\n", VERSION); + fprintf(stderr, _( + "Copyright (C) 1993-2024 Werner Almesberger and Craig Small\n\n")); + fprintf(stderr, _( + "PSmisc comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it under\n" + "the terms of the GNU General Public License.\n" + "For more information about these matters, see the files named COPYING.\n")); +} + +static int +have_proc_self_stat (void) +{ + char filename[128]; + struct stat isproc; + pid_t pid = getpid(); + + snprintf(filename, sizeof(filename), PROC_BASE"/%d/stat", (int) pid); + return stat(filename, &isproc) == 0; +} + +int +main (int argc, char **argv) +{ + char *name; + int sig_num; + int optc; + int myoptind; + int skip_error=0; + struct passwd *pwent = NULL; + char yt[COMM_LEN]; + char ot[COMM_LEN]; + + //int optsig = 0; + + struct option options[] = { + {"exact", 0, NULL, 'e'}, + {"ignore-case", 0, NULL, 'I'}, + {"process-group", 0, NULL, 'g'}, + {"younger-than", 1, NULL, 'y'}, + {"older-than", 1, NULL, 'o'}, + {"interactive", 0, NULL, 'i'}, + {"list-signals", 0, NULL, 'l'}, + {"quiet", 0, NULL, 'q'}, + {"regexp", 0, NULL, 'r'}, + {"signal", 1, NULL, 's'}, + {"user", 1, NULL, 'u'}, + {"verbose", 0, NULL, 'v'}, + {"wait", 0, NULL, 'w'}, + {"ns", 1, NULL, 'n' }, + {"context", 1, NULL, 'Z'}, + {"version", 0, NULL, 'V'}, + {0,0,0,0 }}; + + + /* Setup the i18n */ +#ifdef ENABLE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + char *scontext = NULL; + regex_t scontext_reg; + + if ( argc < 2 ) usage(NULL); /* do the obvious thing... */ + + name = strrchr (*argv, '/'); + if (name) + name++; + else + name = *argv; + sig_num = SIGTERM; + + + opterr = 0; + while ( (optc = getopt_long_only(argc,argv,"egy:o:ilqrs:u:vwZ:VIn:",options,NULL)) != -1) { + switch (optc) { + case 'e': + exact = 1; + break; + case 'g': + process_group = 1; + break; + case 'y': + strncpy(yt, optarg, sizeof yt -1); + yt[sizeof yt -1] = '\0'; + if ( 0 >= (younger_than = parse_time_units(yt) ) ) + usage(_("Invalid time format")); + break; + case 'o': + strncpy(ot, optarg, sizeof ot - 1); + ot[sizeof ot -1] = '\0'; + if ( 0 >= (older_than = parse_time_units(ot) ) ) + usage(_("Invalid time format")); + break; + case 'i': + interactive = 1; + break; + case 'l': + list_signals(); + return 0; + break; + case 'q': + quiet = 1; + break; + case 'r': + reg = 1; + break; + case 's': + sig_num = get_signal (optarg, "killall"); + break; + case 'u': + if (!(pwent = getpwnam(optarg))) { + fprintf (stderr, _("Cannot find user %s\n"), optarg); + exit (1); + } + break; + case 'v': + verbose = 1; + break; + case 'w': + wait_until_dead = 1; + break; + case 'I': + /* option check is optind-1 but sig name is optind */ + if (strcmp(argv[optind-1],"-I") == 0 || strncmp(argv[optind-1],"--",2) == 0) { + ignore_case = 1; + } else { + sig_num = get_signal (argv[optind]+1, "killall"); + skip_error=optind; + } + break; + case 'V': + /* option check is optind-1 but sig name is optind */ + if (strcmp(argv[optind-1],"-V") == 0 || strncmp(argv[optind-1],"--",2) == 0) { + print_version(); + return 0; + } else { + sig_num = get_signal (argv[optind]+1, "killall"); + skip_error=optind; + } + break; + case 'n': { + long num; + char *end = NULL; + errno = 0; + num = strtol(optarg, &end, 10); + if (errno != 0 || optarg == end || end == NULL) + usage(_("Invalid namespace PID")); + opt_ns_pid = (pid_t) num; + } + break; + case 'Z': + scontext=optarg; + if (regcomp(&scontext_reg, scontext, REG_EXTENDED|REG_NOSUB) != 0) { + fprintf(stderr, _("Bad regular expression: %s\n"), scontext); + exit (1); + } + break; + case '?': + if (skip_error == optind) + break; + /* Sigh, this is a hack because -ve could be -version or + * -verbose */ + if (strncmp(argv[optind-1], "-ve", 3) == 0) { + verbose=1; + exact=1; + break; + } + /* Signal names are in uppercase, so check to see if the argv + * is upper case */ + if (argv[optind-1][1] >= 'A' && argv[optind-1][1] <= 'Z') { + sig_num = get_signal (argv[optind-1]+1, "killall"); + } else { + /* Might also be a -## signal too */ + if (argv[optind-1][1] >= '0' && argv[optind-1][1] <= '9') { + sig_num = atoi(argv[optind-1]+1); + } else { + usage(NULL); + } + } + break; + } + } + myoptind = optind; + if ((argc - myoptind < 1) && pwent==NULL && scontext==NULL) + usage(NULL); + + if (argc - myoptind > MAX_NAMES) { + fprintf (stderr, _("killall: Maximum number of names is %d\n"), + MAX_NAMES); + exit (1); + } + if (!have_proc_self_stat()) { + fprintf (stderr, _("killall: %s lacks process entries (not mounted ?)\n"), + PROC_BASE); + exit (1); + } + argv = argv + myoptind; + return kill_all(sig_num,argc - myoptind, argv, pwent, + scontext ? &scontext_reg : NULL); + } |