Home | History | Annotate | Download | only in pending
      1 /* lsof.c - list open files.
      2  *
      3  * Copyright 2015 The Android Open Source Project
      4 
      5 USE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
      6 
      7 config LSOF
      8   bool "lsof"
      9   default n
     10   help
     11     usage: lsof [-lt] [-p PID1,PID2,...] [FILE...]
     12 
     13     List all open files belonging to all active processes, or processes using
     14     listed FILE(s).
     15 
     16     -l	list uids numerically
     17     -p	for given comma-separated pids only (default all pids)
     18     -t	terse (pid only) output
     19 */
     20 
     21 #define FOR_lsof
     22 #include "toys.h"
     23 
     24 GLOBALS(
     25   struct arg_list *p;
     26 
     27   struct stat *sought_files;
     28   struct double_list *all_sockets, *files;
     29   int last_shown_pid, shown_header;
     30 )
     31 
     32 struct proc_info {
     33   char cmd[17];
     34   int pid, uid;
     35 };
     36 
     37 struct file_info {
     38   char *next, *prev;
     39 
     40   // For output.
     41   struct proc_info pi;
     42   char *name, fd[8], rw, locks, type[10], device[32], size_off[32], node[32];
     43 
     44   // For filtering.
     45   dev_t st_dev;
     46   ino_t st_ino;
     47 };
     48 
     49 static void print_info(void *data)
     50 {
     51   struct file_info *fi = data;
     52 
     53   // Filter matches
     54   if (toys.optc) {
     55     int i;
     56 
     57     for (i = 0; i<toys.optc; i++)
     58       if (TT.sought_files[i].st_dev==fi->st_dev)
     59         if (TT.sought_files[i].st_ino==fi->st_ino) break;
     60 
     61     if (i==toys.optc) return;
     62   }
     63 
     64   if (toys.optflags&FLAG_t) {
     65     if (fi->pi.pid != TT.last_shown_pid)
     66       printf("%d\n", TT.last_shown_pid = fi->pi.pid);
     67   } else {
     68     if (!TT.shown_header) {
     69       // TODO: llist_traverse to measure the columns first.
     70       printf("%-9s %5s %10.10s %4s   %7s %18s %9s %10s %s\n", "COMMAND", "PID",
     71         "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME");
     72       TT.shown_header = 1;
     73     }
     74 
     75     printf("%-9s %5d %10.10s %4s%c%c %7s %18s %9s %10s %s\n",
     76            fi->pi.cmd, fi->pi.pid, getusername(fi->pi.uid),
     77            fi->fd, fi->rw, fi->locks, fi->type, fi->device, fi->size_off,
     78            fi->node, fi->name);
     79   }
     80 }
     81 
     82 static void free_info(void *data)
     83 {
     84   free(((struct file_info *)data)->name);
     85   free(data);
     86 }
     87 
     88 static void fill_flags(struct file_info *fi)
     89 {
     90   FILE* fp;
     91   long long pos;
     92   unsigned flags;
     93 
     94   snprintf(toybuf, sizeof(toybuf), "/proc/%d/fdinfo/%s", fi->pi.pid, fi->fd);
     95   fp = fopen(toybuf, "r");
     96   if (!fp) return;
     97 
     98   if (fscanf(fp, "pos: %lld flags: %o", &pos, &flags) == 2) {
     99     flags &= O_ACCMODE;
    100     if (flags == O_RDONLY) fi->rw = 'r';
    101     else if (flags == O_WRONLY) fi->rw = 'w';
    102     else fi->rw = 'u';
    103 
    104     snprintf(fi->size_off, sizeof(fi->size_off), "0t%lld", pos);
    105   }
    106   fclose(fp);
    107 }
    108 
    109 static void scan_proc_net_file(char *path, int family, char type,
    110     void (*fn)(char *, int, char))
    111 {
    112   FILE *fp = fopen(path, "r");
    113   char *line = NULL;
    114   size_t line_length = 0;
    115 
    116   if (!fp) return;
    117 
    118   if (!getline(&line, &line_length, fp)) return; // Skip header.
    119 
    120   while (getline(&line, &line_length, fp) > 0) {
    121     fn(line, family, type);
    122   }
    123 
    124   free(line);
    125   fclose(fp);
    126 }
    127 
    128 static struct file_info *add_socket(ino_t inode, const char *type)
    129 {
    130   struct file_info *fi = xzalloc(sizeof(struct file_info));
    131 
    132   dlist_add_nomalloc(&TT.all_sockets, (struct double_list *)fi);
    133   fi->st_ino = inode;
    134   strcpy(fi->type, type);
    135   return fi;
    136 }
    137 
    138 static void scan_unix(char *line, int af, char type)
    139 {
    140   long inode;
    141   int path_pos;
    142 
    143   if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1) {
    144     struct file_info *fi = add_socket(inode, "unix");
    145     char *name = chomp(line + path_pos);
    146 
    147     fi->name = strdup(*name ? name : "socket");
    148   }
    149 }
    150 
    151 static void scan_netlink(char *line, int af, char type)
    152 {
    153   unsigned state;
    154   long inode;
    155   char *netlink_states[] = {
    156     "ROUTE", "UNUSED", "USERSOCK", "FIREWALL", "SOCK_DIAG", "NFLOG", "XFRM",
    157     "SELINUX", "ISCSI", "AUDIT", "FIB_LOOKUP", "CONNECTOR", "NETFILTER",
    158     "IP6_FW", "DNRTMSG", "KOBJECT_UEVENT", "GENERIC", "DM", "SCSITRANSPORT",
    159     "ENCRYPTFS", "RDMA", "CRYPTO"
    160   };
    161 
    162   if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu", &state, &inode)<2)
    163     return;
    164 
    165   struct file_info *fi = add_socket(inode, "netlink");
    166   fi->name =
    167       strdup(state < ARRAY_LEN(netlink_states) ? netlink_states[state] : "?");
    168 }
    169 
    170 static void scan_ip(char *line, int af, char type)
    171 {
    172   char *tcp_states[] = {
    173     "UNKNOWN", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
    174     "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING"
    175   };
    176   char local_ip[INET6_ADDRSTRLEN] = {0};
    177   char remote_ip[INET6_ADDRSTRLEN] = {0};
    178   struct in6_addr local, remote;
    179   int local_port, remote_port, state;
    180   long inode;
    181   int ok;
    182 
    183   if (af == 4) {
    184     ok = sscanf(line, " %*d: %x:%x %x:%x %x %*x:%*x %*X:%*X %*X %*d %*d %ld",
    185                 &(local.s6_addr32[0]), &local_port,
    186                 &(remote.s6_addr32[0]), &remote_port,
    187                 &state, &inode) == 6;
    188   } else {
    189     ok = sscanf(line, " %*d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x "
    190                 "%*x:%*x %*X:%*X %*X %*d %*d %ld",
    191                 &(local.s6_addr32[0]), &(local.s6_addr32[1]),
    192                 &(local.s6_addr32[2]), &(local.s6_addr32[3]),
    193                 &local_port,
    194                 &(remote.s6_addr32[0]), &(remote.s6_addr32[1]),
    195                 &(remote.s6_addr32[2]), &(remote.s6_addr32[3]),
    196                 &remote_port, &state, &inode) == 12;
    197   }
    198   if (!ok) return;
    199 
    200   struct file_info *fi = add_socket(inode, af == 4 ? "IPv4" : "IPv6");
    201   inet_ntop(af, &local, local_ip, sizeof(local_ip));
    202   inet_ntop(af, &remote, remote_ip, sizeof(remote_ip));
    203   if (type == 't') {
    204     if (state < 0 || state > TCP_CLOSING) state = 0;
    205     fi->name = xmprintf(af == 4 ?
    206                         "TCP %s:%d->%s:%d (%s)" :
    207                         "TCP [%s]:%d->[%s]:%d (%s)",
    208                         local_ip, local_port, remote_ip, remote_port,
    209                         tcp_states[state]);
    210   } else {
    211     fi->name = xmprintf(af == 4 ? "%s %s:%d->%s:%d" : "%s [%s]:%d->[%s]:%d",
    212                         type == 'u' ? "UDP" : "RAW",
    213                         local_ip, local_port, remote_ip, remote_port);
    214   }
    215 }
    216 
    217 static int find_socket(struct file_info *fi, long inode)
    218 {
    219   static int cached;
    220   if (!cached) {
    221     scan_proc_net_file("/proc/net/tcp", 4, 't', scan_ip);
    222     scan_proc_net_file("/proc/net/tcp6", 6, 't', scan_ip);
    223     scan_proc_net_file("/proc/net/udp", 4, 'u', scan_ip);
    224     scan_proc_net_file("/proc/net/udp6", 6, 'u', scan_ip);
    225     scan_proc_net_file("/proc/net/raw", 4, 'r', scan_ip);
    226     scan_proc_net_file("/proc/net/raw6", 6, 'r', scan_ip);
    227     scan_proc_net_file("/proc/net/unix", 0, 0, scan_unix);
    228     scan_proc_net_file("/proc/net/netlink", 0, 0, scan_netlink);
    229     cached = 1;
    230   }
    231   void* list = TT.all_sockets;
    232 
    233   while (list) {
    234     struct file_info *s = (struct file_info*) llist_pop(&list);
    235 
    236     if (s->st_ino == inode) {
    237       fi->name = s->name ? strdup(s->name) : NULL;
    238       strcpy(fi->type, s->type);
    239       return 1;
    240     }
    241     if (list == TT.all_sockets) break;
    242   }
    243 
    244   return 0;
    245 }
    246 
    247 static void fill_stat(struct file_info *fi, const char *path)
    248 {
    249   struct stat sb;
    250   long dev;
    251 
    252   if (stat(path, &sb)) return;
    253 
    254   // Fill TYPE.
    255   switch ((sb.st_mode & S_IFMT)) {
    256     case S_IFBLK: strcpy(fi->type, "BLK"); break;
    257     case S_IFCHR: strcpy(fi->type, "CHR"); break;
    258     case S_IFDIR: strcpy(fi->type, "DIR"); break;
    259     case S_IFIFO: strcpy(fi->type, "FIFO"); break;
    260     case S_IFLNK: strcpy(fi->type, "LINK"); break;
    261     case S_IFREG: strcpy(fi->type, "REG"); break;
    262     case S_IFSOCK: strcpy(fi->type, "sock"); break;
    263     default:
    264       snprintf(fi->type, sizeof(fi->type), "0%03o", sb.st_mode & S_IFMT);
    265       break;
    266   }
    267 
    268   if (S_ISSOCK(sb.st_mode)) find_socket(fi, sb.st_ino);
    269 
    270   // Fill DEVICE.
    271   dev = (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) ? sb.st_rdev : sb.st_dev;
    272   if (!S_ISSOCK(sb.st_mode))
    273     snprintf(fi->device, sizeof(fi->device), "%d,%d",
    274              dev_major(dev), dev_minor(dev));
    275 
    276   // Fill SIZE/OFF.
    277   if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
    278     snprintf(fi->size_off, sizeof(fi->size_off), "%lld",
    279              (long long)sb.st_size);
    280 
    281   // Fill NODE.
    282   snprintf(fi->node, sizeof(fi->node), "%ld", (long)sb.st_ino);
    283 
    284   // Stash st_dev and st_ino for filtering.
    285   fi->st_dev = sb.st_dev;
    286   fi->st_ino = sb.st_ino;
    287 }
    288 
    289 struct file_info *new_file_info(struct proc_info *pi, const char *fd)
    290 {
    291   struct file_info *fi = xzalloc(sizeof(struct file_info));
    292 
    293   dlist_add_nomalloc(&TT.files, (struct double_list *)fi);
    294 
    295   fi->pi = *pi;
    296 
    297   // Defaults.
    298   strcpy(fi->fd, fd);
    299   strcpy(fi->type, "unknown");
    300   fi->rw = fi->locks = ' ';
    301 
    302   return fi;
    303 }
    304 
    305 static void visit_symlink(struct proc_info *pi, char *name, char *path)
    306 {
    307   struct file_info *fi = new_file_info(pi, "");
    308 
    309   // Get NAME.
    310   if (name) { // "/proc/pid/[cwd]".
    311     snprintf(fi->fd, sizeof(fi->fd), "%s", name);
    312     snprintf(toybuf, sizeof(toybuf), "/proc/%d/%s", pi->pid, path);
    313   } else { // "/proc/pid/fd/[3]"
    314     snprintf(fi->fd, sizeof(fi->fd), "%s", path);
    315     fill_flags(fi); // Clobbers toybuf.
    316     snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd/%s", pi->pid, path);
    317   }
    318   // TODO: code called by fill_stat would be easier to write if we didn't
    319   // rely on toybuf being preserved here.
    320   fill_stat(fi, toybuf);
    321   if (!fi->name) { // We already have a name for things like sockets.
    322     fi->name = xreadlink(toybuf);
    323     if (!fi->name) {
    324       fi->name = xmprintf("%s (readlink: %s)", toybuf, strerror(errno));
    325     }
    326   }
    327 }
    328 
    329 static void visit_maps(struct proc_info *pi)
    330 {
    331   FILE *fp;
    332   unsigned long long offset;
    333   char device[10];
    334   long inode;
    335   char *line = NULL;
    336   size_t line_length = 0;
    337 
    338   snprintf(toybuf, sizeof(toybuf), "/proc/%d/maps", pi->pid);
    339   fp = fopen(toybuf, "r");
    340   if (!fp) return;
    341 
    342   while (getline(&line, &line_length, fp) > 0) {
    343     int name_pos;
    344 
    345     if (sscanf(line, "%*x-%*x %*s %llx %s %ld %n",
    346                &offset, device, &inode, &name_pos) >= 3) {
    347       struct file_info *fi;
    348 
    349       // Ignore non-file maps.
    350       if (inode == 0 || !strcmp(device, "00:00")) continue;
    351       // TODO: show unique maps even if they have a non-zero offset?
    352       if (offset != 0) continue;
    353 
    354       fi = new_file_info(pi, "mem");
    355       fi->name = strdup(chomp(line + name_pos));
    356       fill_stat(fi, fi->name);
    357     }
    358   }
    359   free(line);
    360   fclose(fp);
    361 }
    362 
    363 static void visit_fds(struct proc_info *pi)
    364 {
    365   DIR *dir;
    366   struct dirent *de;
    367 
    368   snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd", pi->pid);
    369   if (!(dir = opendir(toybuf))) {
    370     struct file_info *fi = new_file_info(pi, "NOFD");
    371 
    372     fi->name = xmprintf("%s (opendir: %s)", toybuf, strerror(errno));
    373     return;
    374   }
    375 
    376   while ((de = readdir(dir))) {
    377     if (*de->d_name == '.') continue;
    378     visit_symlink(pi, NULL, de->d_name);
    379   }
    380 
    381   closedir(dir);
    382 }
    383 
    384 static void lsof_pid(int pid, struct stat *st)
    385 {
    386   struct proc_info pi;
    387   struct stat sb;
    388   char *s;
    389 
    390   pi.pid = pid;
    391 
    392   // Skip nonexistent pids
    393   sprintf(toybuf, "/proc/%d/stat", pid);
    394   if (!readfile(toybuf, toybuf, sizeof(toybuf)-1) || !(s = strchr(toybuf, '(')))
    395     return;
    396   memcpy(pi.cmd, s+1, sizeof(pi.cmd)-1);
    397   pi.cmd[sizeof(pi.cmd)-1] = 0;
    398   if ((s = strrchr(pi.cmd, ')'))) *s = 0;
    399 
    400   // Get USER.
    401   if (!st) {
    402     snprintf(toybuf, sizeof(toybuf), "/proc/%d", pid);
    403     if (stat(toybuf, st = &sb)) return;
    404   }
    405   pi.uid = st->st_uid;
    406 
    407   visit_symlink(&pi, "cwd", "cwd");
    408   visit_symlink(&pi, "rtd", "root");
    409   visit_symlink(&pi, "txt", "exe");
    410   visit_maps(&pi);
    411   visit_fds(&pi);
    412 }
    413 
    414 static int scan_proc(struct dirtree *node)
    415 {
    416   int pid;
    417 
    418   if (!node->parent) return DIRTREE_RECURSE|DIRTREE_SHUTUP;
    419   if ((pid = atol(node->name))) lsof_pid(pid, &node->st);
    420 
    421   return 0;
    422 }
    423 
    424 void lsof_main(void)
    425 {
    426   struct arg_list *pp;
    427   int i, pid;
    428 
    429   // lsof will only filter on paths it can stat (because it filters by inode).
    430   if (toys.optc) {
    431     TT.sought_files = xmalloc(toys.optc*sizeof(struct stat));
    432     for (i = 0; i<toys.optc; ++i) xstat(toys.optargs[i], TT.sought_files+i);
    433   }
    434 
    435   if (!TT.p) dirtree_read("/proc", scan_proc);
    436   else for (pp = TT.p; pp; pp = pp->next) {
    437     char *start, *end, *next = pp->arg;
    438 
    439     while ((start = comma_iterate(&next, &i))) {
    440       if ((pid = strtol(start, &end, 10))<1 || (*end && *end!=','))
    441         error_msg("bad -p '%.*s'", (int)(end-start), start);
    442       lsof_pid(pid, 0);
    443     }
    444   }
    445 
    446   llist_traverse(TT.files, print_info);
    447 
    448   if (CFG_TOYBOX_FREE) {
    449     llist_traverse(TT.files, free_info);
    450     llist_traverse(TT.all_sockets, free_info);
    451   }
    452 }
    453