Adjust 'after' so that it tries more rigorously to parse the input
[sgt/utils] / pid / pid.c
index 2fc5d5c..5d3245a 100644 (file)
--- a/pid/pid.c
+++ b/pid/pid.c
@@ -3,9 +3,11 @@
  *
  * Same basic idea as Debian's "pidof", in that you type 'pid command'
  * and it finds a process running that command and gives you the pid;
- * but souped up with various pragmatic features such as recognising
- * well known interpreters (so you can search for, say, 'pid
- * script.sh' as well as 'pid bash' and have it do what you meant).
+ * but differs in details, for example it will search for scripts by
+ * default rather than requiring pidof's -x option, and it will also
+ * look for command-line arguments ('pid make test') and try to find
+ * the parent process of a bunch of forks from the same shell script
+ * invocation.
  *
  * Currently tested only on Linux using /proc directly, but I've tried
  * to set it up so that the logic of what processes to choose is
@@ -351,20 +353,27 @@ static const struct procdata *get_proc(int pid)
  * Logic to pick out the set of processes we care about.
  */
 
-static int is_an_interpreter(const char *basename)
+static int is_an_interpreter(const char *basename, const char **stop_opt)
 {
     if (!strcmp(basename, "perl") ||
-        !strcmp(basename, "python") ||
-        !strcmp(basename, "ruby") ||
-        !strcmp(basename, "rep") ||
+        !strcmp(basename, "ruby")) {
+        *stop_opt = "-e";
+        return 1;
+    }
+    if (!strcmp(basename, "python") ||
         !strcmp(basename, "bash") ||
         !strcmp(basename, "sh") ||
-        !strcmp(basename, "dash") ||
+        !strcmp(basename, "dash")) {
+        *stop_opt = "-c";
+        return 1;
+    }
+    if (!strcmp(basename, "rep") ||
         !strcmp(basename, "lua") ||
-        !strcmp(basename, "java"))
+        !strcmp(basename, "java")) {
+        *stop_opt = NULL;
         return 1;
-    else
-        return 0;
+    }
+    return 0;
 }
 
 static const char *find_basename(const char *path)
@@ -385,7 +394,7 @@ static const char *find_basename(const char *path)
 static int find_command(int pid_argc, const char *const *pid_argv,
                         const char *cmd)
 {
-    const char *base;
+    const char *base, *stop_opt;
 
     base = pid_argv[0];
     if (*base == '-')
@@ -397,15 +406,23 @@ static int find_command(int pid_argc, const char *const *pid_argv,
          * argv[0] matches the supplied command name.
          */
         return 0;
-    } else if (is_an_interpreter(base)) {
+    } else if (is_an_interpreter(base, &stop_opt)) {
         /*
          * argv[0] is an interpreter program of some kind. Look
          * along its command line for the program it's running,
          * and see if _that_ matches the command name.
          */
         int i = 1;
-        while (i < pid_argc && pid_argv[i][0] == '-')
-            i++;                   /* skip interpreter options */
+        while (i < pid_argc && pid_argv[i][0] == '-') {
+            /*
+             * Skip interpreter options, unless they're things which
+             * make the next non-option argument not a script name
+             * (e.g. sh -c, perl -e).
+             */
+            if (stop_opt && !strncmp(pid_argv[i], stop_opt, strlen(stop_opt)))
+                return -1;             /* no match */
+            i++;
+        }
         if (i < pid_argc && !strcmp(find_basename(pid_argv[i]), cmd))
             return i;
     }