From 86e8d8cf2e16fcf3beae6ce877d8a1961f658f0b Mon Sep 17 00:00:00 2001 From: Craig Small Date: Wed, 24 Dec 2014 08:35:34 +1100 Subject: killall younger and older flags The -y and -o flags were not being used if you used the regex (-r) flag. I initially moved those flags to the right part of the loop. However looking at that giant loop of code it was very difficult to debug so I have also taken the opportunity to re-work it with some functions, so its clearer what is going on. --- ChangeLog | 11 +- src/killall.c | 594 +++++++++++++++++++++++++++++++--------------------------- 2 files changed, 324 insertions(+), 281 deletions(-) diff --git a/ChangeLog b/ChangeLog index f2a01f6..14e8df6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,10 +1,11 @@ Changes in 22.22 ================ - * Fixed typo in fuser which has -M on Debian #740275 - * pstree by default doesn't show threadnames, use -t to show - as it disables compaction. SF Patch#33 - * PATH_MAX defined in pstree for FreeBSD Debian #750405 - * pstree ignores disappeared processes SF Patch#34 + * fuser: Fixed typo for -M flag. Debian #740275 + * pstree: by default doesn't show threadnames, use -t to show + as it disables compaction. SF [#33] + * pstree: PATH_MAX defined for FreeBSD. Debian #750405 + * pstree: ignores disappeared processes. SF [#34] + * killall: -o and -y work with -r flags. SF [#64] Changes in 22.21 ================ diff --git a/src/killall.c b/src/killall.c index f861649..31cc412 100644 --- a/src/killall.c +++ b/src/killall.c @@ -85,6 +85,12 @@ static int verbose = 0, exact = 0, interactive = 0, reg = 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) { @@ -240,89 +246,215 @@ build_regexp_list(int names, char **namelist) return reglist; } -#ifdef WITH_SELINUX +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 -kill_all(int signal, int names, char **namelist, struct passwd *pwent, - regex_t *scontext ) -#else /*WITH_SELINUX*/ +load_process_name_and_age(char *comm, double *process_age_sec, + const pid_t pid, int load_age) +{ + FILE *file; + char *path; + *process_age_sec = 0; + + if (asprintf (&path, PROC_BASE "/%d/stat", pid) < 0) + return -1; + if (!(file = fopen (path, "r"))) + { + free(path); + return -1; + } + free (path); + if (fscanf (file, "%*d (%15[^)]", comm) != 1) + { + fclose(file); + return -1; + } + + if (load_age) + { + rewind(file); + unsigned long long proc_stt_jf = 0; + if (fscanf(file, "%*d %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %Lu", + &proc_stt_jf) != 1) + { + fclose(file); + return -1; + } + *process_age_sec = process_age(proc_stt_jf); + } + (void) fclose (file); + return strlen(comm); +} + static int -kill_all (int signal, int names, char **namelist, struct passwd *pwent) -#endif /*WITH_SELINUX*/ +load_proc_cmdline(const pid_t pid, const char *comm, char **command, int *got_long) { - DIR *dir; - struct dirent *de; - FILE *file; - struct stat st, sts[MAX_NAMES]; - int *name_len = NULL; - char *path, comm[COMM_LEN]; - char *command_buf; - char *command; - pid_t *pid_table, pid, self, *pid_killed; - pid_t *pgids = NULL; - int i, j, okay, length, got_long, error; - int pids, max_pids, pids_killed; - unsigned long found; - regex_t *reglist = NULL;; -#ifdef WITH_SELINUX - security_context_t lcontext=NULL; -#endif /*WITH_SELINUX*/ + FILE *file; + char *path, *p, *command_buf; + int cmd_size = 128; + int okay; + + if (asprintf (&path, PROC_BASE "/%d/cmdline", pid) < 0) + return -1; + if (!(file = fopen (path, "r"))) + { + free (path); + return -1; + } + free(path); - if (names && reg) - reglist = build_regexp_list(names, namelist); - else if (names) - { - if (!(name_len = malloc (sizeof (int) * names))) - { - perror ("malloc"); - exit (1); - } - for (i = 0; i < names; i++) - { - if (!strchr (namelist[i], '/')) - { - sts[i].st_dev = 0; - name_len[i] = strlen (namelist[i]); - } - else if (stat (namelist[i], &sts[i]) < 0) - { - perror (namelist[i]); - exit (1); - } - } - } - self = getpid (); - found = 0; - if (!(dir = opendir (PROC_BASE))) + if ( (command_buf = (char *)malloc (cmd_size)) == NULL) + exit(1); + + while (1) { - perror (PROC_BASE); - exit (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)) + { + int cur_size = cmd_size; + cmd_size *= 2; + command_buf = (char *)realloc(command_buf, cmd_size); + if (!command_buf) + exit (1); + 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, COMM_LEN-1) == 0) { + okay = 1; + *command = p; + break; + } } - max_pids = 256; - pid_table = malloc (max_pids * sizeof (pid_t)); - if (!pid_table) + (void) fclose(file); + + if (exact && !okay) { - perror ("malloc"); - exit (1); + if (verbose) + fprintf (stderr, _("killall: skipping partial match %s(%d)\n"), + comm, pid); + return -1; + *got_long = okay; + } + return 0; +} + +static pid_t * +create_pid_table(int *max_pids, int *pids) +{ + pid_t self, *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) + *pids = 0; + while ( (de = readdir (dir)) != NULL) { - if (!(pid = (pid_t) atoi (de->d_name)) || pid == self) - continue; - if (pids == max_pids) + if (!(pid = (pid_t) atoi (de->d_name)) || pid == self) + continue; + if (*pids == *max_pids) { - if (!(pid_table = realloc (pid_table, 2 * pids * sizeof (pid_t)))) + if (!(pid_table = realloc (pid_table, 2 * *pids * sizeof (pid_t)))) { perror ("realloc"); exit (1); } - max_pids *= 2; + *max_pids *= 2; } - pid_table[pids++] = pid; + pid_table[(*pids)++] = pid; } (void) closedir (dir); - pids_killed = 0; - pid_killed = malloc (max_pids * sizeof (pid_t)); + return pid_table; +} + +#ifdef WITH_SELINUX +static int +kill_all(int signal, int name_count, char **namelist, struct passwd *pwent, + regex_t *scontext ) +#else /*WITH_SELINUX*/ +static int +kill_all (int signal, int name_count, char **namelist, struct passwd *pwent) +#endif /*WITH_SELINUX*/ +{ + struct stat st; + NAMEINFO *name_info = NULL; + char *path, comm[COMM_LEN]; + char *command; + pid_t *pid_table, *pid_killed; + pid_t *pgids = NULL; + int i, j, length, got_long, error; + int pids, max_pids, pids_killed; + unsigned long found; + regex_t *reglist = NULL;; +#ifdef WITH_SELINUX + security_context_t lcontext=NULL; +#endif /*WITH_SELINUX*/ + + 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"); @@ -337,273 +469,182 @@ kill_all (int signal, int names, char **namelist, struct passwd *pwent) exit (1); } } - for (i = 0; i < pids; i++) + for (i = 0; i < pids; i++) { - pid_t id; - int found_name = -1; - double process_age_sec = 0; - /* match by UID */ - if (pwent && match_process_uid(pid_table[i], pwent->pw_uid)==0) - continue; + pid_t id; + int found_name = -1; + double process_age_sec = 0; + /* match by UID */ + if (pwent && match_process_uid(pid_table[i], pwent->pw_uid)==0) + continue; + #ifdef WITH_SELINUX - /* match by SELinux context */ - if (scontext) + /* match by SELinux context */ + if (scontext) { - if (getpidcon(pid_table[i], &lcontext) < 0) - continue; - if (regexec(scontext, lcontext, 0, NULL, 0) != 0) { + if (getpidcon(pid_table[i], &lcontext) < 0) + continue; + if (regexec(scontext, lcontext, 0, NULL, 0) != 0) { + freecon(lcontext); + continue; + } freecon(lcontext); - continue; - } - freecon(lcontext); } #endif /*WITH_SELINUX*/ - /* load process name */ - if (asprintf (&path, PROC_BASE "/%d/stat", pid_table[i]) < 0) - continue; - if (!(file = fopen (path, "r"))) - { - free (path); - continue; - } - free (path); - okay = fscanf (file, "%*d (%15[^)]", comm) == 1; - if (!okay) { - fclose(file); - continue; - } - if ( younger_than || older_than ) { - rewind(file); - unsigned long long proc_stt_jf = 0; - okay = fscanf(file, "%*d %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %Lu", - &proc_stt_jf) == 1; - if (!okay) { - fclose(file); + length = load_process_name_and_age(comm, &process_age_sec, pid_table[i], (younger_than||older_than)); + if (length < 0) continue; - } - process_age_sec = process_age(proc_stt_jf); - } - (void) fclose (file); - - got_long = 0; - command = NULL; /* make gcc happy */ - length = strlen (comm); - if (length == COMM_LEN - 1) - { - if (asprintf (&path, PROC_BASE "/%d/cmdline", pid_table[i]) < 0) + + /* test for process age, if required */ + if ( younger_than && process_age_sec && (process_age_sec > younger_than ) ) continue; - if (!(file = fopen (path, "r"))) { - free (path); + if ( older_than && process_age_sec && (process_age_sec < older_than ) ) continue; - } - free (path); - int cmd_size = 128; - command_buf = (char *)malloc (cmd_size); - if (!command_buf) - exit (1); - while (1) { - /* look for actual command so we skip over initial "sh" if any */ - char *p; - - /* 'cmdline' has arguments separated by nulls */ - for (p=command_buf; ; p++) { - int c; - if (p == (command_buf + cmd_size)) - { - int cur_size = cmd_size; - cmd_size *= 2; - command_buf = (char *)realloc(command_buf, cmd_size); - if (!command_buf) - exit (1); - 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, COMM_LEN-1) == 0) { - okay = 1; - command = p; - break; - } - } - (void) fclose(file); - if (exact && !okay) - { - if (verbose) - fprintf (stderr, _("killall: skipping partial match %s(%d)\n"), - comm, pid_table[i]); - continue; - } - got_long = okay; - } - /* mach by process name */ - for (j = 0; j < names; j++) + + got_long = 0; + command = NULL; /* make gcc happy */ + if (length == COMM_LEN - 1) + if (load_proc_cmdline(pid_table[i], comm, &command, &got_long) < 0) + continue; + + /* match by process name */ + for (j = 0; j < name_count; j++) { - if (reg) + if (reg) { - if (regexec (®list[j], got_long ? command : comm, 0, NULL, 0) != 0) - continue; + if (regexec (®list[j], got_long ? command : comm, 0, NULL, 0) != 0) + continue; } - else /* non-regex */ + else /* non-regex */ { - if ( younger_than && process_age_sec && (process_age_sec > younger_than ) ) - continue; - if ( older_than && process_age_sec && (process_age_sec < older_than ) ) - continue; - - if (!sts[j].st_dev) + if (!name_info[j].st.st_dev) { - if (length != COMM_LEN - 1 || name_len[j] < COMM_LEN - 1) + if (length != COMM_LEN - 1 || name_info[j].name_length < COMM_LEN - 1) { - if (ignore_case == 1) - { - if (strcasecmp (namelist[j], comm)) - continue; - } - else + if (ignore_case == 1) { - if (strcmp(namelist[j], comm)) - continue; + if (strcasecmp (namelist[j], comm)) + continue; + } else { + if (strcmp(namelist[j], comm)) + continue; } - } - else - { - if (ignore_case == 1) + } else { + if (ignore_case == 1) { - if (got_long ? strcasecmp (namelist[j], command) : + if (got_long ? strcasecmp (namelist[j], command) : strncasecmp (namelist[j], comm, COMM_LEN - 1)) - continue; - } - else - { - if (got_long ? strcmp (namelist[j], command) : + continue; + } else { + if (got_long ? strcmp (namelist[j], command) : strncmp (namelist[j], comm, COMM_LEN - 1)) - continue; + continue; } } - } - else - { - int ok = 1; - - if (asprintf (&path, PROC_BASE "/%d/exe", pid_table[i]) < 0) - continue; - - if (stat (path, &st) < 0) - ok = 0; - - else if (sts[j].st_dev != st.st_dev || - sts[j].st_ino != st.st_ino) + } else { + int ok = 1; + if (asprintf (&path, PROC_BASE "/%d/exe", pid_table[i]) < 0) + continue; + if (stat (path, &st) < 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); + /* 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 || + if (!linkbuf || readlink(path, linkbuf, len + 1) != len || memcmp(namelist[j], linkbuf, len)) - ok = 0; - free(linkbuf); + ok = 0; + free(linkbuf); } - - free(path); - if (!ok) - continue; - } + free(path); + if (!ok) + continue; + } } /* non-regex */ - found_name = j; - break; + found_name = j; + break; } - - if (names && found_name==-1) - continue; /* match by process name faild */ + 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 (pgids[j] == id) + break; if (j < i) continue; - } + } if (interactive && !ask (comm, id, signal)) - continue; + continue; if (kill (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); + 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 |= 1 << 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)); + fprintf (stderr, "%s(%d): %s\n", got_long ? command : + comm, id, strerror (errno)); } - free(reglist); - free(pgids); - free(name_len); - if (!quiet) - for (i = 0; i < names; i++) - if (!(found & (1 << i))) - fprintf (stderr, _("%s: no process found\n"), namelist[i]); - if (names) - /* killall returns a zero return code if at least one process has - * been killed for each listed command. */ - error = found == ((1 << (names - 1)) | ((1 << (names - 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) + free(reglist); + free(pgids); + if (!quiet) + for (i = 0; i < name_count; i++) + if (!(found & (1 << 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 == ((1 << (name_count - 1)) | ((1 << (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;) + for (i = 0; i < pids_killed;) { - if (kill (process_group ? -pid_killed[i] : pid_killed[i], 0) < 0 && - errno == ESRCH) + if (kill (process_group ? -pid_killed[i] : pid_killed[i], 0) < 0 && + errno == ESRCH) { - pid_killed[i] = pid_killed[--pids_killed]; - continue; + pid_killed[i] = pid_killed[--pids_killed]; + continue; } - i++; + i++; } - sleep (1); /* wait a bit longer */ + sleep (1); /* wait a bit longer */ } - free(pid_killed); - free(pid_table); - return error; + free(pid_killed); + free(pid_table); + return error; } @@ -650,7 +691,7 @@ void print_version() { fprintf(stderr, "killall (PSmisc) %s\n", VERSION); fprintf(stderr, _( - "Copyright (C) 1993-2012 Werner Almesberger and Craig Small\n\n")); + "Copyright (C) 1993-2014 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" @@ -839,11 +880,12 @@ main (int argc, char **argv) exit (1); } argv = argv + myoptind; - /*printf("sending signal %d to procs\n", sig_num);*/ + printf("sending signal %d to procs\n", sig_num); #ifdef WITH_SELINUX return kill_all(sig_num,argc - myoptind, argv, pwent, scontext ? &scontext_reg : NULL); #else /*WITH_SELINUX*/ return kill_all(sig_num,argc - myoptind, argv, pwent); #endif /*WITH_SELINUX*/ + printf("done\n"); } -- cgit v1.2.3