OSDN Git Service

Redo namestopid to handle more cases.
authorRob Landley <rob@landley.net>
Fri, 22 Dec 2017 20:56:37 +0000 (14:56 -0600)
committerRob Landley <rob@landley.net>
Fri, 22 Dec 2017 20:56:37 +0000 (14:56 -0600)
lib/lib.c

index 44a7cc7..c4c4a14 100644 (file)
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -1010,37 +1010,64 @@ char *getbasename(char *name)
   return name;
 }
 
-static int argv0_match(char *cmd, char *name)
-{
-  return (*name == '/' ? !strcmp(cmd, name)
-      : !strcmp(getbasename(cmd), getbasename(name)));
-}
-
 // Execute a callback for each PID that matches a process name from a list.
 void names_to_pid(char **names, int (*callback)(pid_t pid, char *name))
 {
   DIR *dp;
   struct dirent *entry;
 
-  if (!(dp = opendir("/proc"))) perror_exit("opendir");
+  if (!(dp = opendir("/proc"))) perror_exit("no /proc");
 
   while ((entry = readdir(dp))) {
-    unsigned u;
-    char *cmd, *comm, **cur;
+    unsigned u = atoi(entry->d_name);
+    char *cmd = 0, *comm, **cur;
+    off_t len;
 
-    if (!(u = atoi(entry->d_name))) continue;
+    if (!u) continue;
 
-    // For a script, comm and argv[1] will match (argv[0] will be the interp).
+    // Comm is original name of executable (argv[0] could be #! interpreter)
+    // but it's limited to 15 characters
     sprintf(libbuf, "/proc/%u/comm", u);
-    if (!(comm = readfile(libbuf, libbuf, sizeof(libbuf)))) continue;
-    sprintf(libbuf+16, "/proc/%u/cmdline", u);
-    if (!(cmd = readfile(libbuf+16, libbuf+16, sizeof(libbuf)-16))) continue;
-
-    for (cur = names; *cur; cur++)
-      if (argv0_match(cmd, *cur) ||
-          (!strncmp(comm, *cur, 15) && argv0_match(cmd+strlen(cmd)+1, *cur)))
-        if (callback(u, *cur)) break;
-    if (*cur) break;
+    len = sizeof(libbuf);
+    if (!(comm = readfileat(AT_FDCWD, libbuf, libbuf, &len)) || !len)
+      continue;
+    if (libbuf[len-1] == '\n') libbuf[--len] = 0;
+
+    for (cur = names; *cur; cur++) {
+      struct stat st1, st2;
+      char *bb = basename(*cur);
+      off_t len;
+
+      // fast path: only matching a filename (no path) that fits in comm
+      if (strncmp(comm, bb, 15)) continue;
+      len = strlen(bb);
+      if (bb==*cur && len<16) goto match;
+
+      // If we have a path to existing file only match if same inode
+      if (bb!=*cur && !stat(*cur, &st1)) {
+        char buf[32];
+
+        sprintf(buf, "/proc/%u/exe", u);
+        if (stat(buf, &st1)) continue;
+        if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) continue;
+        goto match;
+      }
+
+      // Nope, gotta read command line to confirm
+      if (!cmd) {
+        sprintf(cmd = libbuf+16, "/proc/%u/cmdline", u);
+        len = sizeof(libbuf)-17;
+        if (!(cmd = readfileat(AT_FDCWD, cmd, cmd, &len))) continue;
+        // readfile only guarnatees one null terminator and we need two
+        // (yes the kernel should do this for us, don't care)
+        cmd[len] = 0;
+      }
+      if (!strcmp(bb, basename(cmd))) goto match;
+      if (bb!=*cur && !strcmp(bb, basename(cmd+strlen(cmd)+1))) goto match;
+      continue;
+match:
+      if (callback(u, *cur)) break;
+    }
   }
   closedir(dp);
 }