10f66f451Sopenharmony_ci/* lsof.c - list open files.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2015 The Android Open Source Project
40f66f451Sopenharmony_ci
50f66f451Sopenharmony_ciUSE_LSOF(NEWTOY(lsof, "lp*t", TOYFLAG_USR|TOYFLAG_BIN))
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciconfig LSOF
80f66f451Sopenharmony_ci  bool "lsof"
90f66f451Sopenharmony_ci  default n
100f66f451Sopenharmony_ci  help
110f66f451Sopenharmony_ci    usage: lsof [-lt] [-p PID1,PID2,...] [FILE...]
120f66f451Sopenharmony_ci
130f66f451Sopenharmony_ci    List all open files belonging to all active processes, or processes using
140f66f451Sopenharmony_ci    listed FILE(s).
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ci    -l	list uids numerically
170f66f451Sopenharmony_ci    -p	for given comma-separated pids only (default all pids)
180f66f451Sopenharmony_ci    -t	terse (pid only) output
190f66f451Sopenharmony_ci*/
200f66f451Sopenharmony_ci
210f66f451Sopenharmony_ci#define FOR_lsof
220f66f451Sopenharmony_ci#include "toys.h"
230f66f451Sopenharmony_ci
240f66f451Sopenharmony_ciGLOBALS(
250f66f451Sopenharmony_ci  struct arg_list *p;
260f66f451Sopenharmony_ci
270f66f451Sopenharmony_ci  struct stat *sought_files;
280f66f451Sopenharmony_ci  struct double_list *all_sockets, *files;
290f66f451Sopenharmony_ci  int last_shown_pid, shown_header;
300f66f451Sopenharmony_ci)
310f66f451Sopenharmony_ci
320f66f451Sopenharmony_cistruct proc_info {
330f66f451Sopenharmony_ci  char cmd[17];
340f66f451Sopenharmony_ci  int pid, uid;
350f66f451Sopenharmony_ci};
360f66f451Sopenharmony_ci
370f66f451Sopenharmony_cistruct file_info {
380f66f451Sopenharmony_ci  char *next, *prev;
390f66f451Sopenharmony_ci
400f66f451Sopenharmony_ci  // For output.
410f66f451Sopenharmony_ci  struct proc_info pi;
420f66f451Sopenharmony_ci  char *name, fd[8], rw, locks, type[10], device[32], size_off[32], node[32];
430f66f451Sopenharmony_ci
440f66f451Sopenharmony_ci  // For filtering.
450f66f451Sopenharmony_ci  dev_t st_dev;
460f66f451Sopenharmony_ci  ino_t st_ino;
470f66f451Sopenharmony_ci};
480f66f451Sopenharmony_ci
490f66f451Sopenharmony_cistatic void print_info(void *data)
500f66f451Sopenharmony_ci{
510f66f451Sopenharmony_ci  struct file_info *fi = data;
520f66f451Sopenharmony_ci
530f66f451Sopenharmony_ci  // Filter matches
540f66f451Sopenharmony_ci  if (toys.optc) {
550f66f451Sopenharmony_ci    int i;
560f66f451Sopenharmony_ci
570f66f451Sopenharmony_ci    for (i = 0; i<toys.optc; i++)
580f66f451Sopenharmony_ci      if (TT.sought_files[i].st_dev==fi->st_dev)
590f66f451Sopenharmony_ci        if (TT.sought_files[i].st_ino==fi->st_ino) break;
600f66f451Sopenharmony_ci
610f66f451Sopenharmony_ci    if (i==toys.optc) return;
620f66f451Sopenharmony_ci  }
630f66f451Sopenharmony_ci
640f66f451Sopenharmony_ci  if (toys.optflags&FLAG_t) {
650f66f451Sopenharmony_ci    if (fi->pi.pid != TT.last_shown_pid)
660f66f451Sopenharmony_ci      printf("%d\n", TT.last_shown_pid = fi->pi.pid);
670f66f451Sopenharmony_ci  } else {
680f66f451Sopenharmony_ci    if (!TT.shown_header) {
690f66f451Sopenharmony_ci      // TODO: llist_traverse to measure the columns first.
700f66f451Sopenharmony_ci      printf("%-9s %5s %10.10s %4s   %7s %18s %9s %10s %s\n", "COMMAND", "PID",
710f66f451Sopenharmony_ci        "USER", "FD", "TYPE", "DEVICE", "SIZE/OFF", "NODE", "NAME");
720f66f451Sopenharmony_ci      TT.shown_header = 1;
730f66f451Sopenharmony_ci    }
740f66f451Sopenharmony_ci
750f66f451Sopenharmony_ci    printf("%-9s %5d %10.10s %4s%c%c %7s %18s %9s %10s %s\n",
760f66f451Sopenharmony_ci           fi->pi.cmd, fi->pi.pid, getusername(fi->pi.uid),
770f66f451Sopenharmony_ci           fi->fd, fi->rw, fi->locks, fi->type, fi->device, fi->size_off,
780f66f451Sopenharmony_ci           fi->node, fi->name);
790f66f451Sopenharmony_ci  }
800f66f451Sopenharmony_ci}
810f66f451Sopenharmony_ci
820f66f451Sopenharmony_cistatic void free_info(void *data)
830f66f451Sopenharmony_ci{
840f66f451Sopenharmony_ci  free(((struct file_info *)data)->name);
850f66f451Sopenharmony_ci  free(data);
860f66f451Sopenharmony_ci}
870f66f451Sopenharmony_ci
880f66f451Sopenharmony_cistatic void fill_flags(struct file_info *fi)
890f66f451Sopenharmony_ci{
900f66f451Sopenharmony_ci  FILE* fp;
910f66f451Sopenharmony_ci  long long pos;
920f66f451Sopenharmony_ci  unsigned flags;
930f66f451Sopenharmony_ci
940f66f451Sopenharmony_ci  snprintf(toybuf, sizeof(toybuf), "/proc/%d/fdinfo/%s", fi->pi.pid, fi->fd);
950f66f451Sopenharmony_ci  fp = fopen(toybuf, "r");
960f66f451Sopenharmony_ci  if (!fp) return;
970f66f451Sopenharmony_ci
980f66f451Sopenharmony_ci  if (fscanf(fp, "pos: %lld flags: %o", &pos, &flags) == 2) {
990f66f451Sopenharmony_ci    flags &= O_ACCMODE;
1000f66f451Sopenharmony_ci    if (flags == O_RDONLY) fi->rw = 'r';
1010f66f451Sopenharmony_ci    else if (flags == O_WRONLY) fi->rw = 'w';
1020f66f451Sopenharmony_ci    else fi->rw = 'u';
1030f66f451Sopenharmony_ci
1040f66f451Sopenharmony_ci    snprintf(fi->size_off, sizeof(fi->size_off), "0t%lld", pos);
1050f66f451Sopenharmony_ci  }
1060f66f451Sopenharmony_ci  fclose(fp);
1070f66f451Sopenharmony_ci}
1080f66f451Sopenharmony_ci
1090f66f451Sopenharmony_cistatic void scan_proc_net_file(char *path, int family, char type,
1100f66f451Sopenharmony_ci    void (*fn)(char *, int, char))
1110f66f451Sopenharmony_ci{
1120f66f451Sopenharmony_ci  FILE *fp = fopen(path, "r");
1130f66f451Sopenharmony_ci  char *line = NULL;
1140f66f451Sopenharmony_ci  size_t line_length = 0;
1150f66f451Sopenharmony_ci
1160f66f451Sopenharmony_ci  if (!fp) return;
1170f66f451Sopenharmony_ci
1180f66f451Sopenharmony_ci  if (getline(&line, &line_length, fp) <= 0) return; // Skip header.
1190f66f451Sopenharmony_ci
1200f66f451Sopenharmony_ci  while (getline(&line, &line_length, fp) > 0) {
1210f66f451Sopenharmony_ci    fn(line, family, type);
1220f66f451Sopenharmony_ci  }
1230f66f451Sopenharmony_ci
1240f66f451Sopenharmony_ci  free(line);
1250f66f451Sopenharmony_ci  fclose(fp);
1260f66f451Sopenharmony_ci}
1270f66f451Sopenharmony_ci
1280f66f451Sopenharmony_cistatic struct file_info *add_socket(ino_t inode, const char *type)
1290f66f451Sopenharmony_ci{
1300f66f451Sopenharmony_ci  struct file_info *fi = xzalloc(sizeof(struct file_info));
1310f66f451Sopenharmony_ci
1320f66f451Sopenharmony_ci  dlist_add_nomalloc(&TT.all_sockets, (struct double_list *)fi);
1330f66f451Sopenharmony_ci  fi->st_ino = inode;
1340f66f451Sopenharmony_ci  strcpy(fi->type, type);
1350f66f451Sopenharmony_ci  return fi;
1360f66f451Sopenharmony_ci}
1370f66f451Sopenharmony_ci
1380f66f451Sopenharmony_cistatic void scan_unix(char *line, int af, char type)
1390f66f451Sopenharmony_ci{
1400f66f451Sopenharmony_ci  long inode;
1410f66f451Sopenharmony_ci  int path_pos;
1420f66f451Sopenharmony_ci
1430f66f451Sopenharmony_ci  if (sscanf(line, "%*p: %*X %*X %*X %*X %*X %lu %n", &inode, &path_pos) >= 1) {
1440f66f451Sopenharmony_ci    struct file_info *fi = add_socket(inode, "unix");
1450f66f451Sopenharmony_ci    char *name = chomp(line + path_pos);
1460f66f451Sopenharmony_ci
1470f66f451Sopenharmony_ci    fi->name = strdup(*name ? name : "socket");
1480f66f451Sopenharmony_ci  }
1490f66f451Sopenharmony_ci}
1500f66f451Sopenharmony_ci
1510f66f451Sopenharmony_cistatic void scan_netlink(char *line, int af, char type)
1520f66f451Sopenharmony_ci{
1530f66f451Sopenharmony_ci  unsigned state;
1540f66f451Sopenharmony_ci  long inode;
1550f66f451Sopenharmony_ci  char *netlink_states[] = {
1560f66f451Sopenharmony_ci    "ROUTE", "UNUSED", "USERSOCK", "FIREWALL", "SOCK_DIAG", "NFLOG", "XFRM",
1570f66f451Sopenharmony_ci    "SELINUX", "ISCSI", "AUDIT", "FIB_LOOKUP", "CONNECTOR", "NETFILTER",
1580f66f451Sopenharmony_ci    "IP6_FW", "DNRTMSG", "KOBJECT_UEVENT", "GENERIC", "DM", "SCSITRANSPORT",
1590f66f451Sopenharmony_ci    "ENCRYPTFS", "RDMA", "CRYPTO"
1600f66f451Sopenharmony_ci  };
1610f66f451Sopenharmony_ci
1620f66f451Sopenharmony_ci  if (sscanf(line, "%*p %u %*u %*x %*u %*u %*u %*u %*u %lu", &state, &inode)<2)
1630f66f451Sopenharmony_ci    return;
1640f66f451Sopenharmony_ci
1650f66f451Sopenharmony_ci  struct file_info *fi = add_socket(inode, "netlink");
1660f66f451Sopenharmony_ci  fi->name =
1670f66f451Sopenharmony_ci      strdup(state < ARRAY_LEN(netlink_states) ? netlink_states[state] : "?");
1680f66f451Sopenharmony_ci}
1690f66f451Sopenharmony_ci
1700f66f451Sopenharmony_cistatic void scan_ip(char *line, int af, char type)
1710f66f451Sopenharmony_ci{
1720f66f451Sopenharmony_ci  char *tcp_states[] = {
1730f66f451Sopenharmony_ci    "UNKNOWN", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2",
1740f66f451Sopenharmony_ci    "TIME_WAIT", "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", "CLOSING"
1750f66f451Sopenharmony_ci  };
1760f66f451Sopenharmony_ci  char local_ip[INET6_ADDRSTRLEN] = {0};
1770f66f451Sopenharmony_ci  char remote_ip[INET6_ADDRSTRLEN] = {0};
1780f66f451Sopenharmony_ci  struct in6_addr local, remote;
1790f66f451Sopenharmony_ci  int local_port, remote_port, state;
1800f66f451Sopenharmony_ci  long inode;
1810f66f451Sopenharmony_ci  int ok;
1820f66f451Sopenharmony_ci
1830f66f451Sopenharmony_ci  if (af == 4) {
1840f66f451Sopenharmony_ci    ok = sscanf(line, " %*d: %x:%x %x:%x %x %*x:%*x %*X:%*X %*X %*d %*d %ld",
1850f66f451Sopenharmony_ci                &(local.s6_addr32[0]), &local_port,
1860f66f451Sopenharmony_ci                &(remote.s6_addr32[0]), &remote_port,
1870f66f451Sopenharmony_ci                &state, &inode) == 6;
1880f66f451Sopenharmony_ci  } else {
1890f66f451Sopenharmony_ci    ok = sscanf(line, " %*d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x "
1900f66f451Sopenharmony_ci                "%*x:%*x %*X:%*X %*X %*d %*d %ld",
1910f66f451Sopenharmony_ci                &(local.s6_addr32[0]), &(local.s6_addr32[1]),
1920f66f451Sopenharmony_ci                &(local.s6_addr32[2]), &(local.s6_addr32[3]),
1930f66f451Sopenharmony_ci                &local_port,
1940f66f451Sopenharmony_ci                &(remote.s6_addr32[0]), &(remote.s6_addr32[1]),
1950f66f451Sopenharmony_ci                &(remote.s6_addr32[2]), &(remote.s6_addr32[3]),
1960f66f451Sopenharmony_ci                &remote_port, &state, &inode) == 12;
1970f66f451Sopenharmony_ci  }
1980f66f451Sopenharmony_ci  if (!ok) return;
1990f66f451Sopenharmony_ci
2000f66f451Sopenharmony_ci  struct file_info *fi = add_socket(inode, af == 4 ? "IPv4" : "IPv6");
2010f66f451Sopenharmony_ci  inet_ntop(af, &local, local_ip, sizeof(local_ip));
2020f66f451Sopenharmony_ci  inet_ntop(af, &remote, remote_ip, sizeof(remote_ip));
2030f66f451Sopenharmony_ci  if (type == 't') {
2040f66f451Sopenharmony_ci    if (state < 0 || state > TCP_CLOSING) state = 0;
2050f66f451Sopenharmony_ci    fi->name = xmprintf(af == 4 ?
2060f66f451Sopenharmony_ci                        "TCP %s:%d->%s:%d (%s)" :
2070f66f451Sopenharmony_ci                        "TCP [%s]:%d->[%s]:%d (%s)",
2080f66f451Sopenharmony_ci                        local_ip, local_port, remote_ip, remote_port,
2090f66f451Sopenharmony_ci                        tcp_states[state]);
2100f66f451Sopenharmony_ci  } else {
2110f66f451Sopenharmony_ci    fi->name = xmprintf(af == 4 ? "%s %s:%d->%s:%d" : "%s [%s]:%d->[%s]:%d",
2120f66f451Sopenharmony_ci                        type == 'u' ? "UDP" : "RAW",
2130f66f451Sopenharmony_ci                        local_ip, local_port, remote_ip, remote_port);
2140f66f451Sopenharmony_ci  }
2150f66f451Sopenharmony_ci}
2160f66f451Sopenharmony_ci
2170f66f451Sopenharmony_cistatic int find_socket(struct file_info *fi, long inode)
2180f66f451Sopenharmony_ci{
2190f66f451Sopenharmony_ci  static int cached;
2200f66f451Sopenharmony_ci  if (!cached) {
2210f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/tcp", 4, 't', scan_ip);
2220f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/tcp6", 6, 't', scan_ip);
2230f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/udp", 4, 'u', scan_ip);
2240f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/udp6", 6, 'u', scan_ip);
2250f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/raw", 4, 'r', scan_ip);
2260f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/raw6", 6, 'r', scan_ip);
2270f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/unix", 0, 0, scan_unix);
2280f66f451Sopenharmony_ci    scan_proc_net_file("/proc/net/netlink", 0, 0, scan_netlink);
2290f66f451Sopenharmony_ci    cached = 1;
2300f66f451Sopenharmony_ci  }
2310f66f451Sopenharmony_ci  void* list = TT.all_sockets;
2320f66f451Sopenharmony_ci
2330f66f451Sopenharmony_ci  while (list) {
2340f66f451Sopenharmony_ci    struct file_info *s = (struct file_info*) llist_pop(&list);
2350f66f451Sopenharmony_ci
2360f66f451Sopenharmony_ci    if (s->st_ino == inode) {
2370f66f451Sopenharmony_ci      fi->name = s->name ? strdup(s->name) : NULL;
2380f66f451Sopenharmony_ci      strcpy(fi->type, s->type);
2390f66f451Sopenharmony_ci      return 1;
2400f66f451Sopenharmony_ci    }
2410f66f451Sopenharmony_ci    if (list == TT.all_sockets) break;
2420f66f451Sopenharmony_ci  }
2430f66f451Sopenharmony_ci
2440f66f451Sopenharmony_ci  return 0;
2450f66f451Sopenharmony_ci}
2460f66f451Sopenharmony_ci
2470f66f451Sopenharmony_cistatic void fill_stat(struct file_info *fi, const char *path)
2480f66f451Sopenharmony_ci{
2490f66f451Sopenharmony_ci  struct stat sb;
2500f66f451Sopenharmony_ci  long dev;
2510f66f451Sopenharmony_ci
2520f66f451Sopenharmony_ci  if (stat(path, &sb)) return;
2530f66f451Sopenharmony_ci
2540f66f451Sopenharmony_ci  // Fill TYPE.
2550f66f451Sopenharmony_ci  switch ((sb.st_mode & S_IFMT)) {
2560f66f451Sopenharmony_ci    case S_IFBLK: strcpy(fi->type, "BLK"); break;
2570f66f451Sopenharmony_ci    case S_IFCHR: strcpy(fi->type, "CHR"); break;
2580f66f451Sopenharmony_ci    case S_IFDIR: strcpy(fi->type, "DIR"); break;
2590f66f451Sopenharmony_ci    case S_IFIFO: strcpy(fi->type, "FIFO"); break;
2600f66f451Sopenharmony_ci    case S_IFLNK: strcpy(fi->type, "LINK"); break;
2610f66f451Sopenharmony_ci    case S_IFREG: strcpy(fi->type, "REG"); break;
2620f66f451Sopenharmony_ci    case S_IFSOCK: strcpy(fi->type, "sock"); break;
2630f66f451Sopenharmony_ci    default:
2640f66f451Sopenharmony_ci      snprintf(fi->type, sizeof(fi->type), "%04o", sb.st_mode & S_IFMT);
2650f66f451Sopenharmony_ci      break;
2660f66f451Sopenharmony_ci  }
2670f66f451Sopenharmony_ci
2680f66f451Sopenharmony_ci  if (S_ISSOCK(sb.st_mode)) find_socket(fi, sb.st_ino);
2690f66f451Sopenharmony_ci
2700f66f451Sopenharmony_ci  // Fill DEVICE.
2710f66f451Sopenharmony_ci  dev = (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) ? sb.st_rdev : sb.st_dev;
2720f66f451Sopenharmony_ci  if (!S_ISSOCK(sb.st_mode))
2730f66f451Sopenharmony_ci    snprintf(fi->device, sizeof(fi->device), "%d,%d",
2740f66f451Sopenharmony_ci             dev_major(dev), dev_minor(dev));
2750f66f451Sopenharmony_ci
2760f66f451Sopenharmony_ci  // Fill SIZE/OFF.
2770f66f451Sopenharmony_ci  if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
2780f66f451Sopenharmony_ci    snprintf(fi->size_off, sizeof(fi->size_off), "%lld",
2790f66f451Sopenharmony_ci             (long long)sb.st_size);
2800f66f451Sopenharmony_ci
2810f66f451Sopenharmony_ci  // Fill NODE.
2820f66f451Sopenharmony_ci  snprintf(fi->node, sizeof(fi->node), "%ld", (long)sb.st_ino);
2830f66f451Sopenharmony_ci
2840f66f451Sopenharmony_ci  // Stash st_dev and st_ino for filtering.
2850f66f451Sopenharmony_ci  fi->st_dev = sb.st_dev;
2860f66f451Sopenharmony_ci  fi->st_ino = sb.st_ino;
2870f66f451Sopenharmony_ci}
2880f66f451Sopenharmony_ci
2890f66f451Sopenharmony_cistruct file_info *new_file_info(struct proc_info *pi, const char *fd)
2900f66f451Sopenharmony_ci{
2910f66f451Sopenharmony_ci  struct file_info *fi = xzalloc(sizeof(struct file_info));
2920f66f451Sopenharmony_ci
2930f66f451Sopenharmony_ci  dlist_add_nomalloc(&TT.files, (struct double_list *)fi);
2940f66f451Sopenharmony_ci
2950f66f451Sopenharmony_ci  fi->pi = *pi;
2960f66f451Sopenharmony_ci
2970f66f451Sopenharmony_ci  // Defaults.
2980f66f451Sopenharmony_ci  strcpy(fi->fd, fd);
2990f66f451Sopenharmony_ci  strcpy(fi->type, "unknown");
3000f66f451Sopenharmony_ci  fi->rw = fi->locks = ' ';
3010f66f451Sopenharmony_ci
3020f66f451Sopenharmony_ci  return fi;
3030f66f451Sopenharmony_ci}
3040f66f451Sopenharmony_ci
3050f66f451Sopenharmony_cistatic void visit_symlink(struct proc_info *pi, char *name, char *path)
3060f66f451Sopenharmony_ci{
3070f66f451Sopenharmony_ci  struct file_info *fi = new_file_info(pi, "");
3080f66f451Sopenharmony_ci
3090f66f451Sopenharmony_ci  // Get NAME.
3100f66f451Sopenharmony_ci  if (name) { // "/proc/pid/[cwd]".
3110f66f451Sopenharmony_ci    snprintf(fi->fd, sizeof(fi->fd), "%s", name);
3120f66f451Sopenharmony_ci    snprintf(toybuf, sizeof(toybuf), "/proc/%d/%s", pi->pid, path);
3130f66f451Sopenharmony_ci  } else { // "/proc/pid/fd/[3]"
3140f66f451Sopenharmony_ci    snprintf(fi->fd, sizeof(fi->fd), "%s", path);
3150f66f451Sopenharmony_ci    fill_flags(fi); // Clobbers toybuf.
3160f66f451Sopenharmony_ci    snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd/%s", pi->pid, path);
3170f66f451Sopenharmony_ci  }
3180f66f451Sopenharmony_ci  // TODO: code called by fill_stat would be easier to write if we didn't
3190f66f451Sopenharmony_ci  // rely on toybuf being preserved here.
3200f66f451Sopenharmony_ci  fill_stat(fi, toybuf);
3210f66f451Sopenharmony_ci  if (!fi->name) { // We already have a name for things like sockets.
3220f66f451Sopenharmony_ci    fi->name = xreadlink(toybuf);
3230f66f451Sopenharmony_ci    if (!fi->name) {
3240f66f451Sopenharmony_ci      fi->name = xmprintf("%s (readlink: %s)", toybuf, strerror(errno));
3250f66f451Sopenharmony_ci    }
3260f66f451Sopenharmony_ci  }
3270f66f451Sopenharmony_ci}
3280f66f451Sopenharmony_ci
3290f66f451Sopenharmony_cistatic void visit_maps(struct proc_info *pi)
3300f66f451Sopenharmony_ci{
3310f66f451Sopenharmony_ci  FILE *fp;
3320f66f451Sopenharmony_ci  unsigned long long offset;
3330f66f451Sopenharmony_ci  long inode;
3340f66f451Sopenharmony_ci  char *line = NULL, device[10]; // xxx:xxxxx\0
3350f66f451Sopenharmony_ci  size_t line_length = 0;
3360f66f451Sopenharmony_ci  struct file_info *fi;
3370f66f451Sopenharmony_ci
3380f66f451Sopenharmony_ci  snprintf(toybuf, sizeof(toybuf), "/proc/%d/maps", pi->pid);
3390f66f451Sopenharmony_ci  fp = fopen(toybuf, "r");
3400f66f451Sopenharmony_ci  if (!fp) return;
3410f66f451Sopenharmony_ci
3420f66f451Sopenharmony_ci  while (getline(&line, &line_length, fp) > 0) {
3430f66f451Sopenharmony_ci    int name_pos;
3440f66f451Sopenharmony_ci
3450f66f451Sopenharmony_ci    if (sscanf(line, "%*x-%*x %*s %llx %9s %ld %n",
3460f66f451Sopenharmony_ci               &offset, device, &inode, &name_pos) >= 3) {
3470f66f451Sopenharmony_ci      // Ignore non-file maps.
3480f66f451Sopenharmony_ci      if (inode == 0 || !strcmp(device, "00:00")) continue;
3490f66f451Sopenharmony_ci      // TODO: show unique maps even if they have a non-zero offset?
3500f66f451Sopenharmony_ci      if (offset != 0) continue;
3510f66f451Sopenharmony_ci
3520f66f451Sopenharmony_ci      fi = new_file_info(pi, "mem");
3530f66f451Sopenharmony_ci      fi->name = strdup(chomp(line + name_pos));
3540f66f451Sopenharmony_ci      fill_stat(fi, fi->name);
3550f66f451Sopenharmony_ci    }
3560f66f451Sopenharmony_ci  }
3570f66f451Sopenharmony_ci  free(line);
3580f66f451Sopenharmony_ci  fclose(fp);
3590f66f451Sopenharmony_ci}
3600f66f451Sopenharmony_ci
3610f66f451Sopenharmony_cistatic void visit_fds(struct proc_info *pi)
3620f66f451Sopenharmony_ci{
3630f66f451Sopenharmony_ci  DIR *dir;
3640f66f451Sopenharmony_ci  struct dirent *de;
3650f66f451Sopenharmony_ci
3660f66f451Sopenharmony_ci  snprintf(toybuf, sizeof(toybuf), "/proc/%d/fd", pi->pid);
3670f66f451Sopenharmony_ci  if (!(dir = opendir(toybuf))) {
3680f66f451Sopenharmony_ci    struct file_info *fi = new_file_info(pi, "NOFD");
3690f66f451Sopenharmony_ci
3700f66f451Sopenharmony_ci    fi->name = xmprintf("%s (opendir: %s)", toybuf, strerror(errno));
3710f66f451Sopenharmony_ci    return;
3720f66f451Sopenharmony_ci  }
3730f66f451Sopenharmony_ci
3740f66f451Sopenharmony_ci  while ((de = readdir(dir))) {
3750f66f451Sopenharmony_ci    if (*de->d_name == '.') continue;
3760f66f451Sopenharmony_ci    visit_symlink(pi, NULL, de->d_name);
3770f66f451Sopenharmony_ci  }
3780f66f451Sopenharmony_ci
3790f66f451Sopenharmony_ci  closedir(dir);
3800f66f451Sopenharmony_ci}
3810f66f451Sopenharmony_ci
3820f66f451Sopenharmony_cistatic void lsof_pid(int pid, struct stat *st)
3830f66f451Sopenharmony_ci{
3840f66f451Sopenharmony_ci  struct proc_info pi;
3850f66f451Sopenharmony_ci  struct stat sb;
3860f66f451Sopenharmony_ci  char *s;
3870f66f451Sopenharmony_ci
3880f66f451Sopenharmony_ci  pi.pid = pid;
3890f66f451Sopenharmony_ci
3900f66f451Sopenharmony_ci  // Skip nonexistent pids
3910f66f451Sopenharmony_ci  sprintf(toybuf, "/proc/%d/stat", pid);
3920f66f451Sopenharmony_ci  if (!readfile(toybuf, toybuf, sizeof(toybuf)-1) || !(s = strchr(toybuf, '(')))
3930f66f451Sopenharmony_ci    return;
3940f66f451Sopenharmony_ci  memcpy(pi.cmd, s+1, sizeof(pi.cmd)-1);
3950f66f451Sopenharmony_ci  pi.cmd[sizeof(pi.cmd)-1] = 0;
3960f66f451Sopenharmony_ci  if ((s = strrchr(pi.cmd, ')'))) *s = 0;
3970f66f451Sopenharmony_ci
3980f66f451Sopenharmony_ci  // Get USER.
3990f66f451Sopenharmony_ci  if (!st) {
4000f66f451Sopenharmony_ci    snprintf(toybuf, sizeof(toybuf), "/proc/%d", pid);
4010f66f451Sopenharmony_ci    if (stat(toybuf, st = &sb)) return;
4020f66f451Sopenharmony_ci  }
4030f66f451Sopenharmony_ci  pi.uid = st->st_uid;
4040f66f451Sopenharmony_ci
4050f66f451Sopenharmony_ci  visit_symlink(&pi, "cwd", "cwd");
4060f66f451Sopenharmony_ci  visit_symlink(&pi, "rtd", "root");
4070f66f451Sopenharmony_ci  visit_symlink(&pi, "txt", "exe");
4080f66f451Sopenharmony_ci  visit_maps(&pi);
4090f66f451Sopenharmony_ci  visit_fds(&pi);
4100f66f451Sopenharmony_ci}
4110f66f451Sopenharmony_ci
4120f66f451Sopenharmony_cistatic int scan_proc(struct dirtree *node)
4130f66f451Sopenharmony_ci{
4140f66f451Sopenharmony_ci  int pid;
4150f66f451Sopenharmony_ci
4160f66f451Sopenharmony_ci  if (!node->parent) return DIRTREE_RECURSE|DIRTREE_SHUTUP;
4170f66f451Sopenharmony_ci  if ((pid = atol(node->name))) lsof_pid(pid, &node->st);
4180f66f451Sopenharmony_ci
4190f66f451Sopenharmony_ci  return 0;
4200f66f451Sopenharmony_ci}
4210f66f451Sopenharmony_ci
4220f66f451Sopenharmony_civoid lsof_main(void)
4230f66f451Sopenharmony_ci{
4240f66f451Sopenharmony_ci  struct arg_list *pp;
4250f66f451Sopenharmony_ci  int i, pid;
4260f66f451Sopenharmony_ci
4270f66f451Sopenharmony_ci  // lsof will only filter on paths it can stat (because it filters by inode).
4280f66f451Sopenharmony_ci  if (toys.optc) {
4290f66f451Sopenharmony_ci    TT.sought_files = xmalloc(toys.optc*sizeof(struct stat));
4300f66f451Sopenharmony_ci    for (i = 0; i<toys.optc; ++i) xstat(toys.optargs[i], TT.sought_files+i);
4310f66f451Sopenharmony_ci  }
4320f66f451Sopenharmony_ci
4330f66f451Sopenharmony_ci  if (!TT.p) dirtree_read("/proc", scan_proc);
4340f66f451Sopenharmony_ci  else for (pp = TT.p; pp; pp = pp->next) {
4350f66f451Sopenharmony_ci    char *start, *end, *next = pp->arg;
4360f66f451Sopenharmony_ci
4370f66f451Sopenharmony_ci    while ((start = comma_iterate(&next, &i))) {
4380f66f451Sopenharmony_ci      if ((pid = strtol(start, &end, 10))<1 || (*end && *end!=','))
4390f66f451Sopenharmony_ci        error_msg("bad -p '%.*s'", (int)(end-start), start);
4400f66f451Sopenharmony_ci      lsof_pid(pid, 0);
4410f66f451Sopenharmony_ci    }
4420f66f451Sopenharmony_ci  }
4430f66f451Sopenharmony_ci
4440f66f451Sopenharmony_ci  llist_traverse(TT.files, print_info);
4450f66f451Sopenharmony_ci
4460f66f451Sopenharmony_ci  if (CFG_TOYBOX_FREE) {
4470f66f451Sopenharmony_ci    llist_traverse(TT.files, free_info);
4480f66f451Sopenharmony_ci    llist_traverse(TT.all_sockets, free_info);
4490f66f451Sopenharmony_ci  }
4500f66f451Sopenharmony_ci}
451