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