• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
print_info(void * data)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 
free_info(void * data)87 static void free_info(void *data)
88 {
89   free(((struct file_info *)data)->name);
90   free(data);
91 }
92 
fill_flags(struct file_info * fi)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 
scan_proc_net_file(char * path,int family,char type,void (* fn)(char *,int,char))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 
add_socket(ino_t inode,const char * type)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 
scan_unix(char * line,int af,char type)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 
scan_netlink(char * line,int af,char type)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 
scan_ip(char * line,int af,char type)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 
find_socket(struct file_info * fi,long inode)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 
fill_stat(struct file_info * fi,const char * path)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 
new_file_info(struct proc_info * pi,const char * fd)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 
visit_symlink(struct proc_info * pi,char * name,char * path)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 
visit_maps(struct proc_info * pi)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 
visit_fds(struct proc_info * pi)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 
lsof_pid(int pid)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 
scan_proc(struct dirtree * node)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 
lsof_main(void)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