[Toybox] [PATCH] Fix netstat -p.

enh enh at google.com
Mon Sep 7 10:46:17 PDT 2015


Fix netstat -p.

netstat -p was failing for any cmdline longer than 21 characters. (A
typical Chrome render process has about half a kilobyte of cmdline.)

There's still a lot of cleanup to be done later, but this is enough to
fix -p and remove a fair amount of unnecessary custom code into the
bargain.

diff --git a/toys/pending/netstat.c b/toys/pending/netstat.c
index fbb9eb1..98db54d 100644
--- a/toys/pending/netstat.c
+++ b/toys/pending/netstat.c
@@ -29,8 +29,14 @@ config NETSTAT

 #define FOR_netstat
 #include "toys.h"
+
 #include <net/route.h>

+GLOBALS(
+  char current_name[21];
+  int some_process_unidentified;
+);
+
 typedef union _iaddr {
   unsigned u;
   unsigned char b[4];
@@ -56,28 +62,15 @@ enum {
 };

 #define SOCK_NOT_CONNECTED 1
-//For PID/Progrma Name
-#define PROGRAM_NAME "PID/Program Name"
-#define PROGNAME_LEN 21

 typedef struct _pidlist {
   struct _pidlist *next;
   long inode;
-  char name[PROGNAME_LEN];
+  char name[21];
 } PID_LIST;
 PID_LIST *pid_list = NULL;

 /*
- * Get base name from the input name.
- */
-static const char *get_basename(char *name)
-{
-  const char *c = strrchr(name, '/');
-  if (c) return c + 1;
-  return name;
-}
-
-/*
  * locate character in string.
  */
 static char *strchr_nul(char *s, int c)
@@ -86,39 +79,6 @@ static char *strchr_nul(char *s, int c)
   return (char*)s;
 }

-// Find out if the last character of a string matches with the given one.
-// Don't underrun the buffer if the string length is 0.
-static char *find_last_char(char *str, int c)
-{
-  if (str && *str) {
-    size_t sz = strlen(str) - 1;
-    str += sz;
-    if ( (unsigned char)*str == c) return (char*)str;
-  }
-  return NULL;
-}
-/*
- * Concat path and the file name.
- */
-static char *append_pathandfile(char *path, char *fname)
-{
-  char *c;
-  if (!path) path = "";
-  c = find_last_char(path, '/');
-  while (*fname == '/') fname++;
-  return xmprintf("%s%s%s", path, (c)? "" : "/", fname);
-}
-/*
- * Concat sub-path and the file name.
- */
-static char *append_subpathandfile(char *path, char *fname)
-{
-#define ISDOTORDOTDOT(s) ((s)[0] == '.' && (!(s)[1] || ((s)[1] == '.'
&& !(s)[2])))
-  if(!fname) return NULL;
-  if(ISDOTORDOTDOT(fname)) return NULL;
-  return append_pathandfile(path, fname);
-#undef ISDOTORDOTDOT
-}
 /*
  * used to converts string into int and validate the input str for
invalid int value or out-of-range.
  */
@@ -361,7 +321,7 @@ static void show_unix_sockets(char *fname, char *label)
       if (flags & SOCK_NO_SPACE) strcat(sock_flags, "N ");
       strcat(sock_flags, "]");
     }
-    xprintf("%-5s %-6ld %-11s %-10s %-13s %6lu ", (!label ? "unix" :
"??"), refcount, sock_flags, sock_type, sock_state, inode);
+    xprintf("%-5s %-6ld %-11s %-10s %-13s %8lu ", (!label ? "unix" :
"??"), refcount, sock_flags, sock_type, sock_state, inode);
     if (toys.optflags & FLAG_p) xprintf("%-20s", get_pid_name(inode));

     bptr += path_offset;
@@ -392,7 +352,7 @@ static long ss_inode(char *link)
 /*
  * add inode and progname in the pid list.
  */
-static void add2list(long inode, char *progname)
+static void add2list(long inode)
 {
   PID_LIST *node = pid_list;
   for(; node; node = node->next) {
@@ -401,71 +361,61 @@ static void add2list(long inode, char *progname)
   }
   PID_LIST *new = (PID_LIST *)xzalloc(sizeof(PID_LIST));
   new->inode = inode;
-  xstrncpy(new->name, progname, PROGNAME_LEN);
+  xstrncpy(new->name, TT.current_name, sizeof(new->name));
   new->next = pid_list;
   pid_list = new;
 }
-/*
- * add pid info in the list.
- */
-static void extract_inode(char *path, char *progname)
+
+static void scan_pid_inodes(char *path)
 {
   DIR *dp;
   struct dirent *entry;

   if (!(dp = opendir(path))) {
-    if (errno == EACCES) return;
-    else perror_exit("%s", path);
+    if (errno == EACCES) {
+      TT.some_process_unidentified = 1;
+      return;
+    } else perror_exit("%s", path);
   }
   while ((entry = readdir(dp))) {
-    char *link = NULL, *fname = append_subpathandfile(path, entry->d_name);
-    if (!fname) continue;
-    link = xreadlink(fname);
-    if (link) {
-      long inode = ss_inode(link);
-      free(link);
-      if (inode != -1) add2list(inode, progname);
-    }
-    free(fname);
-  }//end of while.
+    char link_name[64], *link;
+    long inode;
+
+    if (!isdigit(entry->d_name[0])) continue;
+    snprintf(link_name, sizeof(link_name), "%s/%s", path, entry->d_name);
+    link = xreadlink(link_name);
+    if ((inode = ss_inode(link)) != -1) add2list(inode);
+    free(link);
+  }
   closedir(dp);
 }
-/*
- * prepare the list for all pids in /proc directory.
- */
-static void get_pid_list(void)
+
+static void scan_pid(int pid)
 {
-  DIR *dp;
-  struct dirent *entry;
-  char path[64] = {0,};
-  uid_t uid = geteuid();
+  char *line, *p, *fd_dir;

-  if (!(dp = opendir("/proc"))) perror_exit("opendir");
+  snprintf(toybuf, sizeof(toybuf), "/proc/%d/cmdline", pid);
+  line = xreadfile(toybuf, 0, 0);

-  while ((entry = readdir(dp))) {
-    int fd, nitems = 0, length = 0;
-    char *pid, *progname;
-
-    if (!isdigit(*entry->d_name)) continue;
-    pid = entry->d_name;
-    length = snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name);
-    if (sizeof(path) <= length) continue;
-
-    fd = xopen(path, O_RDONLY);
-    nitems = readall(fd, toybuf, sizeof(toybuf) - 1);
-    xclose(fd);
-    if (nitems < 1) continue;
-    toybuf[nitems] = '\0';
-    strcpy(path + length - (sizeof("cmdline")-1), "fd");
-    progname = append_pathandfile(pid, (char *)get_basename(toybuf));
//e.g. progname = 2054/gnome-keyring-daemon
-    extract_inode(path, progname);
-    free(progname);
-  }//end of while.
-  closedir(dp);
+  if ((p = strchr(line, ' '))) *p = 0; // "/bin/netstat -ntp" -> "/bin/netstat"
+  snprintf(TT.current_name, sizeof(TT.current_name), "%d/%s",
+           pid, basename_r(line)); // "584/netstat"
+  free(line);

-  if (uid) fprintf(stderr, "(Not all processes could be identified,
non-owned process info "
-      "will not be shown, you would have to be root to see it all.)\n");
+  fd_dir = xmprintf("/proc/%d/fd", pid);
+  scan_pid_inodes(fd_dir);
+  free(fd_dir);
 }
+
+static int scan_pids(struct dirtree *node)
+{
+  int pid;
+
+  if (!node->parent) return DIRTREE_RECURSE;
+  if ((pid = atol(node->name))) scan_pid(pid);
+  return 0;
+}
+
 /*
  * Dealloc pid list.
  */
@@ -484,9 +434,9 @@ static void clean_pid_list(void)
 static void show_header(void)
 {
   if ((toys.optflags & FLAG_W) && (toys.optflags & FLAG_p))
-    xprintf("\nProto Recv-Q Send-Q %-51s %-51s %-12s%s\n", "Local
Address", "Foreign Address", "State", PROGRAM_NAME);
+    xprintf("\nProto Recv-Q Send-Q %-51s %-51s %-12s%s\n", "Local
Address", "Foreign Address", "State", "PID/Program Name");
   else if (toys.optflags & FLAG_p)
-    xprintf("\nProto Recv-Q Send-Q %-23s %-23s %-12s%s\n", "Local
Address", "Foreign Address", "State", PROGRAM_NAME);
+    xprintf("\nProto Recv-Q Send-Q %-23s %-23s %-12s%s\n", "Local
Address", "Foreign Address", "State", "PID/Program Name");
   else if (toys.optflags & FLAG_W)
 	  xprintf("\nProto Recv-Q Send-Q %-51s %-51s State     \n", "Local
Address", "Foreign Address");
   else xprintf("\nProto Recv-Q Send-Q %-23s %-23s State     \n",
"Local Address", "Foreign Address");
@@ -610,7 +560,15 @@ void netstat_main(void)
     return;
   }

-  if (toys.optflags & FLAG_p) get_pid_list();
+  if (toys.optflags & FLAG_p) {
+    dirtree_read("/proc", scan_pids);
+    // TODO: we probably shouldn't warn if all the processes we're going to
+    // list were identified.
+    if (TT.some_process_unidentified)
+      fprintf(stderr,
+        "(Not all processes could be identified, non-owned process info\n"
+        " will not be shown, you would have to be root to see it all.)\n");
+  }

   //For TCP/UDP/RAW.
   if ( (toys.optflags & FLAG_t) || (toys.optflags & FLAG_u) ||
(toys.optflags & FLAG_w) ) {
@@ -640,8 +598,8 @@ void netstat_main(void)
     else if (toys.optflags & FLAG_l) xprintf("(only servers)");
     else xprintf("(w/o servers)");

-    if (toys.optflags & FLAG_p) xprintf("\nProto RefCnt Flags
Type       State         I-Node %s    Path\n", PROGRAM_NAME);
-    else xprintf("\nProto RefCnt Flags       Type       State
I-Node Path\n");
+    if (toys.optflags & FLAG_p) xprintf("\nProto RefCnt Flags
Type       State           I-Node PID/Program Name    Path\n");
+    else xprintf("\nProto RefCnt Flags       Type       State
  I-Node Path\n");
     show_unix_sockets("/proc/net/unix", "unix");
   }
   if (toys.optflags & FLAG_p) clean_pid_list();


More information about the Toybox mailing list