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