summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCraig Small <csmall@enc.com.au>2014-12-24 08:35:34 +1100
committerCraig Small <csmall@enc.com.au>2014-12-24 08:35:34 +1100
commit86e8d8cf2e16fcf3beae6ce877d8a1961f658f0b (patch)
treea0821445b82bf57c56065946ba97731e61ba9d7f
parent9e870ae6e732c85e565cbd7137dd68d123ef4f06 (diff)
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.
-rw-r--r--ChangeLog11
-rw-r--r--src/killall.c594
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 (&reglist[j], got_long ? command : comm, 0, NULL, 0) != 0)
- continue;
+ if (regexec (&reglist[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");
}