]> granicus.if.org Git - psmisc/commitdiff
killall younger and older flags
authorCraig Small <csmall@enc.com.au>
Tue, 23 Dec 2014 21:35:34 +0000 (08:35 +1100)
committerCraig Small <csmall@enc.com.au>
Tue, 23 Dec 2014 21:35:34 +0000 (08:35 +1100)
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
src/killall.c

index f2a01f61cd574ceafe1bb2f822fb69187749860c..14e8df656c2758cb2b82e2a229c2beed398212c0 100644 (file)
--- 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
 ================
index f8616495069d7763336e6576b47d5d8b18f92ab2..31cc412f7f703fa758fc60944b7d0d42b6191364 100644 (file)
@@ -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");
 }