]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/lsfd.c
Merge branch 'lsfd--misc-fix' of https://github.com/masatake/util-linux
[thirdparty/util-linux.git] / misc-utils / lsfd.c
1 /*
2 * lsfd(1) - list file descriptors
3 *
4 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 * Karel Zak <kzak@redhat.com>
7 *
8 * Very generally based on lsof(8) by Victor A. Abell <abe@purdue.edu>
9 * It supports multiple OSes. lsfd specializes to Linux.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it would be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <inttypes.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <getopt.h>
32 #include <ctype.h>
33 #include <search.h>
34
35 #include <linux/sched.h>
36 #include <sys/syscall.h>
37 #include <linux/kcmp.h>
38 static int kcmp(pid_t pid1, pid_t pid2, int type,
39 unsigned long idx1, unsigned long idx2)
40 {
41 return syscall(SYS_kcmp, pid1, pid2, type, idx1, idx2);
42 }
43
44 /* See proc(5).
45 * Defined in linux/include/linux/sched.h private header file. */
46 #define PF_KTHREAD 0x00200000 /* I am a kernel thread */
47
48 #include "c.h"
49 #include "nls.h"
50 #include "xalloc.h"
51 #include "list.h"
52 #include "closestream.h"
53 #include "strutils.h"
54 #include "procfs.h"
55 #include "fileutils.h"
56 #include "idcache.h"
57 #include "pathnames.h"
58
59 #include "libsmartcols.h"
60
61 #include "lsfd.h"
62 #include "lsfd-filter.h"
63 #include "lsfd-counter.h"
64
65 /*
66 * /proc/$pid/mountinfo entries
67 */
68 struct nodev {
69 struct list_head nodevs;
70 unsigned long minor;
71 char *filesystem;
72 };
73
74 struct nodev_table {
75 #define NODEV_TABLE_SIZE 97
76 struct list_head tables[NODEV_TABLE_SIZE];
77 };
78 static struct nodev_table nodev_table;
79
80 struct name_manager {
81 struct idcache *cache;
82 unsigned long next_id;
83 };
84
85 /*
86 * /proc/devices entries
87 */
88 struct devdrv {
89 struct list_head devdrvs;
90 unsigned long major;
91 char *name;
92 };
93
94 static struct list_head chrdrvs;
95 static struct list_head blkdrvs;
96
97 /*
98 * IPC table
99 */
100
101 #define IPC_TABLE_SIZE 997
102 struct ipc_table {
103 struct list_head tables[IPC_TABLE_SIZE];
104 };
105
106 static struct ipc_table ipc_table;
107
108 /*
109 * Column related stuffs
110 */
111
112 /* column names */
113 struct colinfo {
114 const char *name;
115 double whint;
116 int flags;
117 int json_type;
118 const char *help;
119 };
120
121 /* columns descriptions */
122 static const struct colinfo infos[] = {
123 [COL_AINODECLASS] = { "AINODECLASS",
124 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
125 N_("class of anonymous inode") },
126 [COL_ASSOC] = { "ASSOC",
127 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
128 N_("association between file and process") },
129 [COL_BLKDRV] = { "BLKDRV",
130 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
131 N_("block device driver name resolved by /proc/devices") },
132 [COL_CHRDRV] = { "CHRDRV",
133 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
134 N_("character device driver name resolved by /proc/devices") },
135 [COL_COMMAND] = { "COMMAND",
136 0.3, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
137 N_("command of the process opening the file") },
138 [COL_DELETED] = { "DELETED",
139 0, SCOLS_FL_RIGHT, SCOLS_JSON_BOOLEAN,
140 N_("reachability from the file system") },
141 [COL_DEV] = { "DEV",
142 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
143 N_("ID of device containing file") },
144 [COL_DEVTYPE] = { "DEVTYPE",
145 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
146 N_("device type (blk, char, or nodev)") },
147 [COL_ENDPOINTS] = { "ENDPOINTS",
148 0, SCOLS_FL_WRAP, SCOLS_JSON_STRING,
149 N_("IPC endpoints information communicated with the fd") },
150 [COL_EVENTFD_ID] = {"EVENTFD.ID",
151 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
152 N_("eventfd ID") },
153 [COL_EVENTPOLL_TFDS] = {"EVENTPOLL.TFDS",
154 0, SCOLS_FL_WRAP, SCOLS_JSON_STRING,
155 N_("file descriptors targeted by the eventpoll file") },
156 [COL_FD] = { "FD",
157 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
158 N_("file descriptor for the file") },
159 [COL_FLAGS] = { "FLAGS",
160 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
161 N_("flags specified when opening the file") },
162 [COL_FUID] = { "FUID",
163 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
164 N_("user ID number of the file's owner") },
165 [COL_INET_LADDR] = { "INET.LADDR",
166 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
167 N_("local IP address") },
168 [COL_INET_RADDR] = { "INET.RADDR",
169 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
170 N_("remote IP address") },
171 [COL_INET6_LADDR] = { "INET6.LADDR",
172 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
173 N_("local IPv6 address") },
174 [COL_INET6_RADDR] = { "INET6.RADDR",
175 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
176 N_("remote IPv6 address") },
177 [COL_INODE] = { "INODE",
178 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
179 N_("inode number") },
180 [COL_KNAME] = { "KNAME",
181 0.4, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
182 N_("name of the file (raw)") },
183 [COL_KTHREAD] = { "KTHREAD",
184 0, SCOLS_FL_RIGHT, SCOLS_JSON_BOOLEAN,
185 N_("opened by a kernel thread") },
186 [COL_MAJMIN] = { "MAJ:MIN",
187 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
188 N_("device ID for special, or ID of device containing file") },
189 [COL_MAPLEN] = { "MAPLEN",
190 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
191 N_("length of file mapping (in page)") },
192 [COL_MISCDEV] = { "MISCDEV",
193 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
194 N_("misc character device name resolved by /proc/misc") },
195 [COL_MNT_ID] = { "MNTID",
196 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
197 N_("mount id") },
198 [COL_MODE] = { "MODE",
199 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
200 N_("access mode (rwx)") },
201 [COL_NAME] = { "NAME",
202 0.4, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
203 N_("name of the file (cooked)") },
204 [COL_NETLINK_GROUPS] = { "NETLINK.GROUPS",
205 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
206 N_("netlink multicast groups") },
207 [COL_NETLINK_LPORT] = { "NETLINK.LPORT",
208 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
209 N_("netlink local port id") },
210 [COL_NETLINK_PROTOCOL] = { "NETLINK.PROTOCOL",
211 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
212 N_("netlink protocol") },
213 [COL_NLINK] = { "NLINK",
214 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
215 N_("link count") },
216 [COL_NS_NAME] = { "NS.NAME",
217 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
218 N_("name of the namespace (NS.TYPE:[INODE])") },
219 [COL_NS_TYPE] = { "NS.TYPE",
220 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
221 N_("type of the namespace") },
222 [COL_OWNER] = { "OWNER",
223 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
224 N_("owner of the file") },
225 [COL_PACKET_IFACE] = { "PACKET.IFACE",
226 0, SCOLS_FL_RIGHT,SCOLS_JSON_STRING,
227 N_("net interface associated with the packet socket") },
228 [COL_PACKET_PROTOCOL] = { "PACKET.PROTOCOL",
229 0, SCOLS_FL_RIGHT,SCOLS_JSON_STRING,
230 N_("L3 protocol associated with the packet socket") },
231 [COL_PARTITION] = { "PARTITION",
232 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
233 N_("block device name resolved by /proc/partition") },
234 [COL_PID] = { "PID",
235 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
236 N_("PID of the process opening the file") },
237 [COL_PIDFD_COMM] = { "PIDFD.COMM",
238 0.2, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
239 N_("command of the process targeted by the pidfd") },
240 [COL_PIDFD_NSPID] = { "PIDFD.NSPID",
241 0.2, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
242 N_("NSpid field in fdinfo of the pidfd") },
243 [COL_PIDFD_PID] = { "PIDFD.PID",
244 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
245 N_("PID of the process targeted by the pidfd") },
246 [COL_PING_ID] = { "PING.ID",
247 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
248 N_("ICMP echo request ID") },
249 [COL_POS] = { "POS",
250 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
251 N_("file position") },
252 [COL_RAW_PROTOCOL] = { "RAW.PROTOCOL",
253 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
254 N_("protocol number of the raw socket") },
255 [COL_RDEV] = { "RDEV",
256 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
257 N_("device ID (if special file)") },
258 [COL_SIZE] = { "SIZE",
259 4, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
260 N_("file size"), },
261 [COL_SOCK_LISTENING] = { "SOCK.LISTENING",
262 0, SCOLS_FL_RIGHT, SCOLS_JSON_BOOLEAN,
263 N_("listening socket") },
264 [COL_SOCK_NETNS] = { "SOCK.NETNS",
265 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
266 N_("inode identifying network namespace where the socket belongs to") },
267 [COL_SOCK_PROTONAME] = { "SOCK.PROTONAME",
268 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
269 N_("protocol name") },
270 [COL_SOCK_STATE] = { "SOCK.STATE",
271 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
272 N_("State of socket") },
273 [COL_SOCK_TYPE] = { "SOCK.TYPE",
274 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
275 N_("Type of socket") },
276 [COL_SOURCE] = { "SOURCE",
277 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
278 N_("file system, partition, or device containing file") },
279 [COL_STTYPE] = { "STTYPE",
280 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
281 N_("file type (raw)") },
282 [COL_TCP_LADDR] = { "TCP.LADDR",
283 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
284 N_("local TCP address (INET address:TCP port)") },
285 [COL_TCP_RADDR] = { "TCP.RADDR",
286 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
287 N_("remote TCP address (INET address:TCP port)") },
288 [COL_TCP_LPORT] = { "TCP.LPORT",
289 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
290 N_("local TCP port") },
291 [COL_TCP_RPORT] = { "TCP.RPORT",
292 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
293 N_("remote TCP port") },
294 [COL_TID] = { "TID",
295 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
296 N_("thread ID of the process opening the file") },
297 [COL_TIMERFD_CLOCKID] = { "TIMERFD.CLOCKID",
298 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
299 N_("clockid") },
300 [COL_TIMERFD_INTERVAL] = { "TIMERFD.INTERVAL",
301 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
302 N_("interval") },
303 [COL_TIMERFD_REMAINING]= { "TIMERFD.REMAINING",
304 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
305 N_("remaining time") },
306 [COL_TYPE] = { "TYPE",
307 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
308 N_("file type (cooked)") },
309 [COL_UDP_LADDR] = { "UDP.LADDR",
310 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
311 N_("local UDP address (INET address:UDP port)") },
312 [COL_UDP_RADDR] = { "UDP.RADDR",
313 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
314 N_("remote UDP address (INET address:UDP port)") },
315 [COL_UDP_LPORT] = { "UDP.LPORT",
316 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
317 N_("local UDP port") },
318 [COL_UDP_RPORT] = { "UDP.RPORT",
319 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
320 N_("remote UDP port") },
321 [COL_UDPLITE_LADDR] = { "UDPLITE.LADDR",
322 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
323 N_("local UDPLite address (INET address:UDPLite port)") },
324 [COL_UDPLITE_RADDR] = { "UDPLITE.RADDR",
325 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
326 N_("remote UDPLite address (INET address:UDPLite port)") },
327 [COL_UDPLITE_LPORT] = { "UDPLITE.LPORT",
328 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
329 N_("local UDPLite port") },
330 [COL_UDPLITE_RPORT] = { "UDPLITE.RPORT",
331 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
332 N_("remote UDPLite port") },
333 [COL_UID] = { "UID",
334 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
335 N_("user ID number of the process") },
336 [COL_UNIX_PATH] = { "UNIX.PATH",
337 0.4, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
338 N_("filesystem pathname for UNIX domain socket") },
339 [COL_USER] = { "USER",
340 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
341 N_("user of the process") },
342 };
343
344 static const int default_columns[] = {
345 COL_COMMAND,
346 COL_PID,
347 COL_USER,
348 COL_ASSOC,
349 COL_MODE,
350 COL_TYPE,
351 COL_SOURCE,
352 COL_MNT_ID,
353 COL_INODE,
354 COL_NAME,
355 };
356
357 static const int default_threads_columns[] = {
358 COL_COMMAND,
359 COL_PID,
360 COL_TID,
361 COL_USER,
362 COL_ASSOC,
363 COL_MODE,
364 COL_TYPE,
365 COL_SOURCE,
366 COL_MNT_ID,
367 COL_INODE,
368 COL_NAME,
369 };
370
371 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
372 static size_t ncolumns;
373
374 static ino_t *mnt_namespaces;
375 static size_t nspaces;
376
377 struct counter_spec {
378 struct list_head specs;
379 const char *name;
380 const char *expr;
381 };
382
383 static const struct counter_spec default_counter_specs[] = {
384 {
385 .name = N_("processes"),
386 .expr = "ASSOC == 'cwd'",
387 },
388 {
389 .name = N_("root owned processes"),
390 .expr = "(ASSOC == 'cwd') && (UID == 0)",
391 },
392 {
393 .name = N_("kernel threads"),
394 .expr = "(ASSOC == 'cwd') && KTHREAD",
395 },
396 {
397 .name = N_("open files"),
398 .expr = "FD >= 0",
399 },
400 {
401 .name = N_("RO open files"),
402 .expr = "(FD >= 0) and (MODE == 'r--')",
403 },
404 {
405 .name = N_("WO open files"),
406 .expr = "(FD >= 0) and (MODE == '-w-')",
407 },
408 {
409 .name = N_("shared mappings"),
410 .expr = "ASSOC == 'shm'",
411 },
412 {
413 .name = N_("RO shared mappings"),
414 .expr = "(ASSOC == 'shm') and (MODE == 'r--')",
415 },
416 {
417 .name = N_("WO shared mappings"),
418 .expr = "(ASSOC == 'shm') and (MODE == '-w-')",
419 },
420 {
421 .name = N_("regular files"),
422 .expr = "(FD >= 0) && (STTYPE == 'REG')",
423 },
424 {
425 .name = N_("directories"),
426 .expr = "(FD >= 0) && (STTYPE == 'DIR')",
427 },
428 {
429 .name = N_("sockets"),
430 .expr = "(FD >= 0) && (STTYPE == 'SOCK')",
431 },
432 {
433 .name = N_("fifos/pipes"),
434 .expr = "(FD >= 0) && (STTYPE == 'FIFO')",
435 },
436 {
437 .name = N_("character devices"),
438 .expr = "(FD >= 0) && (STTYPE == 'CHR')",
439 },
440 {
441 .name = N_("block devices"),
442 .expr = "(FD >= 0) && (STTYPE == 'BLK')",
443 },
444 {
445 .name = N_("unknown types"),
446 .expr = "(FD >= 0) && (STTYPE == 'UNKN')",
447 }
448 };
449
450 struct lsfd_control {
451 struct libscols_table *tb; /* output */
452 struct list_head procs; /* list of all processes */
453
454 unsigned int noheadings : 1,
455 raw : 1,
456 json : 1,
457 notrunc : 1,
458 threads : 1,
459 show_main : 1, /* print main table */
460 show_summary : 1, /* print summary/counters */
461 sockets_only : 1; /* display only SOCKETS */
462
463 struct lsfd_filter *filter;
464 struct lsfd_counter **counters; /* NULL terminated array. */
465 };
466
467 static void *proc_tree; /* for tsearch/tfind */
468
469 static int proc_tree_compare(const void *a, const void *b)
470 {
471 return ((struct proc *)a)->pid - ((struct proc *)b)->pid;
472 }
473
474 struct proc *get_proc(pid_t pid)
475 {
476 struct proc **node = tfind(&pid, &proc_tree, proc_tree_compare);
477 if (node)
478 return *node;
479 return NULL;
480 }
481
482 static int column_name_to_id(const char *name, size_t namesz)
483 {
484 size_t i;
485
486 for (i = 0; i < ARRAY_SIZE(infos); i++) {
487 const char *cn = infos[i].name;
488
489 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
490 return i;
491 }
492 warnx(_("unknown column: %s"), name);
493
494 return LSFD_FILTER_UNKNOWN_COL_ID;
495 }
496
497 static int column_name_to_id_cb(const char *name, void *data __attribute__((__unused__)))
498 {
499 return column_name_to_id(name, strlen(name));
500 }
501
502 static int get_column_id(int num)
503 {
504 assert(num >= 0);
505 assert((size_t) num < ncolumns);
506 assert(columns[num] < (int) ARRAY_SIZE(infos));
507
508 return columns[num];
509 }
510
511 static const struct colinfo *get_column_info(int num)
512 {
513 return &infos[ get_column_id(num) ];
514 }
515
516 static struct libscols_column *add_column(struct libscols_table *tb, const struct colinfo *col)
517 {
518 struct libscols_column *cl;
519 int flags = col->flags;
520
521 cl = scols_table_new_column(tb, col->name, col->whint, flags);
522 if (cl) {
523 scols_column_set_json_type(cl, col->json_type);
524 if (col->flags & SCOLS_FL_WRAP) {
525 scols_column_set_wrapfunc(cl,
526 scols_wrapnl_chunksize,
527 scols_wrapnl_nextchunk,
528 NULL);
529 scols_column_set_safechars(cl, "\n");
530 }
531 }
532
533 return cl;
534 }
535
536 static struct libscols_column *add_column_by_id_cb(struct libscols_table *tb, int colid, void *data)
537 {
538 struct libscols_column *cl;
539
540 if (ncolumns >= ARRAY_SIZE(columns))
541 errx(EXIT_FAILURE, _("too many columns are added via filter expression"));
542
543 assert(colid < LSFD_N_COLS);
544
545 cl = add_column(tb, infos + colid);
546 if (!cl)
547 err(EXIT_FAILURE, _("failed to allocate output column"));
548 columns[ncolumns++] = colid;
549
550 if (colid == COL_TID) {
551 struct lsfd_control *ctl = data;
552 ctl->threads = 1;
553 }
554
555 return cl;
556 }
557
558 static int has_mnt_ns(ino_t id)
559 {
560 size_t i;
561
562 for (i = 0; i < nspaces; i++) {
563 if (mnt_namespaces[i] == id)
564 return 1;
565 }
566 return 0;
567 }
568
569 static void add_mnt_ns(ino_t id)
570 {
571 size_t nmax = 0;
572
573 if (nspaces)
574 nmax = (nspaces + 16) / 16 * 16;
575 if (nmax <= nspaces + 1) {
576 nmax += 16;
577 mnt_namespaces = xrealloc(mnt_namespaces,
578 sizeof(ino_t) * nmax);
579 }
580 mnt_namespaces[nspaces++] = id;
581 }
582
583 static const struct file_class *stat2class(struct stat *sb)
584 {
585 dev_t dev;
586
587 assert(sb);
588
589 switch (sb->st_mode & S_IFMT) {
590 case S_IFCHR:
591 return &cdev_class;
592 case S_IFBLK:
593 return &bdev_class;
594 case S_IFSOCK:
595 return &sock_class;
596 case S_IFIFO:
597 return &fifo_class;
598 case S_IFLNK:
599 case S_IFDIR:
600 return &file_class;
601 case S_IFREG:
602 dev = sb->st_dev;
603 if (major(dev) != 0)
604 return &file_class;
605
606 if (is_nsfs_dev(dev))
607 return &nsfs_file_class;
608
609 if (is_mqueue_dev(dev))
610 return &mqueue_file_class;
611
612 return &file_class;
613 default:
614 break;
615 }
616
617 return &unkn_class;
618 }
619
620 static struct file *new_file(struct proc *proc, const struct file_class *class)
621 {
622 struct file *file;
623
624 assert(class);
625 file = xcalloc(1, class->size);
626 file->class = class;
627
628 file->proc = proc;
629
630 INIT_LIST_HEAD(&file->files);
631 list_add_tail(&file->files, &proc->files);
632
633 return file;
634 }
635
636 static struct file *copy_file(struct file *old)
637 {
638 struct file *file = xcalloc(1, old->class->size);
639
640 INIT_LIST_HEAD(&file->files);
641 file->proc = old->proc;
642 list_add_tail(&file->files, &old->proc->files);
643
644 file->class = old->class;
645 file->association = old->association;
646 file->name = xstrdup(old->name);
647 file->stat = old->stat;
648
649 return file;
650 }
651
652 static void file_set_path(struct file *file, struct stat *sb, const char *name, int association)
653 {
654 file->association = association;
655 file->name = xstrdup(name);
656 file->stat = *sb;
657 }
658
659 static void file_init_content(struct file *file)
660 {
661 if (file->class && file->class->initialize_content)
662 file->class->initialize_content(file);
663 }
664
665 static void free_file(struct file *file)
666 {
667 const struct file_class *class = file->class;
668
669 while (class) {
670 if (class->free_content)
671 class->free_content(file);
672 class = class->super;
673 }
674 free(file);
675 }
676
677
678 static struct proc *new_process(pid_t pid, struct proc *leader)
679 {
680 struct proc *proc = xcalloc(1, sizeof(*proc));
681
682 proc->pid = pid;
683 proc->leader = leader? leader: proc;
684 proc->command = NULL;
685
686 INIT_LIST_HEAD(&proc->files);
687 INIT_LIST_HEAD(&proc->procs);
688
689 proc->kthread = 0;
690 return proc;
691 }
692
693 static void free_proc(struct proc *proc)
694 {
695 list_free(&proc->files, struct file, files, free_file);
696
697 free(proc->command);
698 free(proc);
699 }
700
701 static void read_fdinfo(struct file *file, FILE *fdinfo)
702 {
703 char buf[1024];
704
705 while (fgets(buf, sizeof(buf), fdinfo)) {
706 const struct file_class *class;
707 char *val = strchr(buf, ':');
708
709 if (!val)
710 continue;
711 *val++ = '\0'; /* terminate key */
712
713 val = (char *) skip_space(val);
714 rtrim_whitespace((unsigned char *) val);
715
716 class = file->class;
717 while (class) {
718 if (class->handle_fdinfo
719 && class->handle_fdinfo(file, buf, val))
720 break;
721 class = class->super;
722 }
723 }
724 }
725
726 static struct file *collect_file_symlink(struct path_cxt *pc,
727 struct proc *proc,
728 const char *name,
729 int assoc,
730 bool sockets_only)
731 {
732 char sym[PATH_MAX] = { '\0' };
733 struct stat sb;
734 struct file *f, *prev;
735
736 if (ul_path_readlink(pc, sym, sizeof(sym), name) < 0)
737 return NULL;
738
739 /* The /proc/#/{fd,ns} often contains the same file (e.g. /dev/tty)
740 * more than once. Let's try to reuse the previous file if the real
741 * path is the same to save stat() call.
742 */
743 prev = list_last_entry(&proc->files, struct file, files);
744 if (prev && prev->name && strcmp(prev->name, sym) == 0) {
745 f = copy_file(prev);
746 f->association = assoc;
747 } else {
748 const struct file_class *class;
749
750 if (ul_path_stat(pc, &sb, 0, name) < 0)
751 return NULL;
752
753 class = stat2class(&sb);
754 if (sockets_only
755 /* A nsfs file is not a socket but the nsfs file can
756 * be used as a entry point to collect information from
757 * other network namespaces. Besed on the information,
758 * various columns of sockets can be filled.
759 */
760 && (class != &sock_class) && (class != &nsfs_file_class))
761 return NULL;
762 f = new_file(proc, class);
763 file_set_path(f, &sb, sym, assoc);
764 }
765
766 file_init_content(f);
767
768 if (is_association(f, NS_MNT))
769 proc->ns_mnt = f->stat.st_ino;
770 else if (is_association(f, NS_NET))
771 load_sock_xinfo(pc, name, f->stat.st_ino);
772
773 else if (assoc >= 0) {
774 /* file-descriptor based association */
775 FILE *fdinfo;
776
777 if (ul_path_stat(pc, &sb, AT_SYMLINK_NOFOLLOW, name) == 0)
778 f->mode = sb.st_mode;
779
780 if (is_nsfs_dev(f->stat.st_dev))
781 load_sock_xinfo(pc, name, f->stat.st_ino);
782
783 fdinfo = ul_path_fopenf(pc, "r", "fdinfo/%d", assoc);
784 if (fdinfo) {
785 read_fdinfo(f, fdinfo);
786 fclose(fdinfo);
787 }
788 }
789
790 return f;
791 }
792
793 /* read symlinks from /proc/#/fd
794 */
795 static void collect_fd_files(struct path_cxt *pc, struct proc *proc,
796 bool sockets_only)
797 {
798 DIR *sub = NULL;
799 struct dirent *d = NULL;
800 char path[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX))];
801
802 while (ul_path_next_dirent(pc, &sub, "fd", &d) == 0) {
803 uint64_t num;
804
805 if (ul_strtou64(d->d_name, &num, 10) != 0) /* only numbers */
806 continue;
807
808 snprintf(path, sizeof(path), "fd/%ju", (uintmax_t) num);
809 collect_file_symlink(pc, proc, path, num, sockets_only);
810 }
811 }
812
813 static void parse_maps_line(struct path_cxt *pc, char *buf, struct proc *proc)
814 {
815 uint64_t start, end, offset, ino;
816 unsigned long major, minor;
817 enum association assoc = ASSOC_MEM;
818 struct stat sb;
819 struct file *f, *prev;
820 char *path, modestr[5];
821 dev_t devno;
822
823 /* read rest of the map */
824 if (sscanf(buf, "%"SCNx64 /* start */
825 "-%"SCNx64 /* end */
826 " %4[^ ]" /* mode */
827 " %"SCNx64 /* offset */
828 " %lx:%lx" /* maj:min */
829 " %"SCNu64, /* inode */
830
831 &start, &end, modestr, &offset,
832 &major, &minor, &ino) != 7)
833 return;
834
835 /* Skip private anonymous mappings. */
836 if (major == 0 && minor == 0 && ino == 0)
837 return;
838
839 devno = makedev(major, minor);
840
841 if (modestr[3] == 's')
842 assoc = ASSOC_SHM;
843
844 /* The map usually contains the same file more than once, try to reuse
845 * the previous file (if devno and ino are the same) to save stat() call.
846 */
847 prev = list_last_entry(&proc->files, struct file, files);
848
849 if (prev && prev->stat.st_dev == devno && prev->stat.st_ino == ino) {
850 f = copy_file(prev);
851 f->association = -assoc;
852 } else if ((path = strchr(buf, '/'))) {
853 rtrim_whitespace((unsigned char *) path);
854 if (stat(path, &sb) < 0)
855 /* If a file is mapped but deleted from the file system,
856 * "stat by the file name" may not work. In that case,
857 */
858 goto try_map_files;
859 f = new_file(proc, stat2class(&sb));
860 file_set_path(f, &sb, path, -assoc);
861 } else {
862 /* As used in tcpdump, AF_PACKET socket can be mmap'ed. */
863 char map_file[sizeof("map_files/0000000000000000-ffffffffffffffff")];
864 char sym[PATH_MAX] = { '\0' };
865
866 try_map_files:
867 snprintf(map_file, sizeof(map_file), "map_files/%"PRIx64"-%"PRIx64, start, end);
868 if (ul_path_stat(pc, &sb, 0, map_file) < 0)
869 return;
870 if (ul_path_readlink(pc, sym, sizeof(sym), map_file) < 0)
871 return;
872 f = new_file(proc, stat2class(&sb));
873 file_set_path(f, &sb, sym, -assoc);
874 }
875
876 if (modestr[0] == 'r')
877 f->mode |= S_IRUSR;
878 if (modestr[1] == 'w')
879 f->mode |= S_IWUSR;
880 if (modestr[2] == 'x')
881 f->mode |= S_IXUSR;
882
883 f->map_start = start;
884 f->map_end = end;
885 f->pos = offset;
886
887 file_init_content(f);
888 }
889
890 static void collect_mem_files(struct path_cxt *pc, struct proc *proc)
891 {
892 FILE *fp;
893 char buf[BUFSIZ];
894
895 fp = ul_path_fopen(pc, "r", "maps");
896 if (!fp)
897 return;
898
899 while (fgets(buf, sizeof(buf), fp))
900 parse_maps_line(pc, buf, proc);
901
902 fclose(fp);
903 }
904
905 static void collect_outofbox_files(struct path_cxt *pc,
906 struct proc *proc,
907 enum association assocs[],
908 const char *names[],
909 size_t count,
910 bool sockets_only)
911 {
912 size_t i;
913
914 for (i = 0; i < count; i++)
915 collect_file_symlink(pc, proc, names[assocs[i]], assocs[i] * -1,
916 sockets_only);
917 }
918
919 static void collect_execve_file(struct path_cxt *pc, struct proc *proc,
920 bool sockets_only)
921 {
922 enum association assocs[] = { ASSOC_EXE };
923 const char *names[] = {
924 [ASSOC_EXE] = "exe",
925 };
926 collect_outofbox_files(pc, proc, assocs, names, ARRAY_SIZE(assocs),
927 sockets_only);
928 }
929
930 static void collect_fs_files(struct path_cxt *pc, struct proc *proc,
931 bool sockets_only)
932 {
933 enum association assocs[] = { ASSOC_EXE, ASSOC_CWD, ASSOC_ROOT };
934 const char *names[] = {
935 [ASSOC_CWD] = "cwd",
936 [ASSOC_ROOT] = "root",
937 };
938 collect_outofbox_files(pc, proc, assocs, names, ARRAY_SIZE(assocs),
939 sockets_only);
940 }
941
942 static void collect_namespace_files(struct path_cxt *pc, struct proc *proc)
943 {
944 enum association assocs[] = {
945 ASSOC_NS_CGROUP,
946 ASSOC_NS_IPC,
947 ASSOC_NS_MNT,
948 ASSOC_NS_NET,
949 ASSOC_NS_PID,
950 ASSOC_NS_PID4C,
951 ASSOC_NS_TIME,
952 ASSOC_NS_TIME4C,
953 ASSOC_NS_USER,
954 ASSOC_NS_UTS,
955 };
956 const char *names[] = {
957 [ASSOC_NS_CGROUP] = "ns/cgroup",
958 [ASSOC_NS_IPC] = "ns/ipc",
959 [ASSOC_NS_MNT] = "ns/mnt",
960 [ASSOC_NS_NET] = "ns/net",
961 [ASSOC_NS_PID] = "ns/pid",
962 [ASSOC_NS_PID4C] = "ns/pid_for_children",
963 [ASSOC_NS_TIME] = "ns/time",
964 [ASSOC_NS_TIME4C] = "ns/time_for_children",
965 [ASSOC_NS_USER] = "ns/user",
966 [ASSOC_NS_UTS] = "ns/uts",
967 };
968 collect_outofbox_files(pc, proc, assocs, names, ARRAY_SIZE(assocs),
969 /* Namespace information is alwasys needed. */
970 false);
971 }
972
973 static struct nodev *new_nodev(unsigned long minor, const char *filesystem)
974 {
975 struct nodev *nodev = xcalloc(1, sizeof(*nodev));
976
977 INIT_LIST_HEAD(&nodev->nodevs);
978 nodev->minor = minor;
979 nodev->filesystem = xstrdup(filesystem);
980
981 return nodev;
982 }
983
984 static void free_nodev(struct nodev *nodev)
985 {
986 free(nodev->filesystem);
987 free(nodev);
988 }
989
990 void add_nodev(unsigned long minor, const char *filesystem)
991 {
992 struct nodev *nodev = new_nodev(minor, filesystem);
993 unsigned long slot = nodev->minor % NODEV_TABLE_SIZE;
994
995 list_add_tail(&nodev->nodevs, &nodev_table.tables[slot]);
996 }
997
998 static void initialize_nodevs(void)
999 {
1000 int i;
1001
1002 for (i = 0; i < NODEV_TABLE_SIZE; i++)
1003 INIT_LIST_HEAD(&nodev_table.tables[i]);
1004 }
1005
1006 static void finalize_nodevs(void)
1007 {
1008 int i;
1009
1010 for (i = 0; i < NODEV_TABLE_SIZE; i++)
1011 list_free(&nodev_table.tables[i], struct nodev, nodevs, free_nodev);
1012
1013 free(mnt_namespaces);
1014 }
1015
1016 const char *get_nodev_filesystem(unsigned long minor)
1017 {
1018 struct list_head *n;
1019 int slot = minor % NODEV_TABLE_SIZE;
1020
1021 list_for_each (n, &nodev_table.tables[slot]) {
1022 struct nodev *nodev = list_entry(n, struct nodev, nodevs);
1023 if (nodev->minor == minor)
1024 return nodev->filesystem;
1025 }
1026 return NULL;
1027 }
1028
1029 static void add_nodevs(FILE *mnt)
1030 {
1031 /* This can be very long. A line in mountinfo can have more than 3
1032 * paths. */
1033 char line[PATH_MAX * 3 + 256];
1034
1035 while (fgets(line, sizeof(line), mnt)) {
1036 unsigned long major, minor;
1037 char filesystem[256];
1038
1039 /* 23 61 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel */
1040 if(sscanf(line, "%*d %*d %lu:%lu %*s %*s %*s %*[^-] - %s %*[^\n]",
1041 &major, &minor, filesystem) != 3)
1042 /* 1600 1458 0:55 / / rw,nodev,relatime - overlay overlay rw,context="s... */
1043 if (sscanf(line, "%*d %*d %lu:%lu %*s %*s %*s - %s %*[^\n]",
1044 &major, &minor, filesystem) != 3)
1045 continue;
1046
1047 if (major != 0)
1048 continue;
1049 if (get_nodev_filesystem(minor))
1050 continue;
1051
1052 add_nodev(minor, filesystem);
1053 }
1054 }
1055
1056 static void initialize_ipc_table(void)
1057 {
1058 for (int i = 0; i < IPC_TABLE_SIZE; i++)
1059 INIT_LIST_HEAD(ipc_table.tables + i);
1060 }
1061
1062 static void free_ipc(struct ipc *ipc)
1063 {
1064 if (ipc->class->free)
1065 ipc->class->free(ipc);
1066 free(ipc);
1067 }
1068
1069 static void finalize_ipc_table(void)
1070 {
1071 for (int i = 0; i < IPC_TABLE_SIZE; i++)
1072 list_free(&ipc_table.tables[i], struct ipc, ipcs, free_ipc);
1073 }
1074
1075 struct ipc *new_ipc(const struct ipc_class *class)
1076 {
1077 struct ipc *ipc = xcalloc(1, class->size);
1078 ipc->class = class;
1079 INIT_LIST_HEAD(&ipc->endpoints);
1080 INIT_LIST_HEAD(&ipc->ipcs);
1081 return ipc;
1082 }
1083
1084 struct ipc *get_ipc(struct file *file)
1085 {
1086 int slot;
1087 struct list_head *e;
1088 const struct ipc_class *ipc_class;
1089
1090 if (!file->class->get_ipc_class)
1091 return NULL;
1092
1093 ipc_class = file->class->get_ipc_class(file);
1094 if (!ipc_class)
1095 return NULL;
1096
1097 slot = ipc_class->get_hash(file) % IPC_TABLE_SIZE;
1098 list_for_each (e, &ipc_table.tables[slot]) {
1099 struct ipc *ipc = list_entry(e, struct ipc, ipcs);
1100 if (ipc->class != ipc_class)
1101 continue;
1102 if (ipc_class->is_suitable_ipc(ipc, file))
1103 return ipc;
1104 }
1105 return NULL;
1106 }
1107
1108 void add_ipc(struct ipc *ipc, unsigned int hash)
1109 {
1110 int slot = hash % IPC_TABLE_SIZE;
1111 list_add(&ipc->ipcs, &ipc_table.tables[slot]);
1112 }
1113
1114 void init_endpoint(struct ipc_endpoint *endpoint)
1115 {
1116 INIT_LIST_HEAD(&endpoint->endpoints);
1117 }
1118
1119 void add_endpoint(struct ipc_endpoint *endpoint, struct ipc *ipc)
1120 {
1121 endpoint->ipc = ipc;
1122 list_add(&endpoint->endpoints, &ipc->endpoints);
1123 }
1124
1125 static void fill_column(struct proc *proc,
1126 struct file *file,
1127 struct libscols_line *ln,
1128 int column_id,
1129 size_t column_index)
1130 {
1131 const struct file_class *class = file->class;
1132
1133 while (class) {
1134 if (class->fill_column
1135 && class->fill_column(proc, file, ln,
1136 column_id, column_index))
1137 break;
1138 class = class->super;
1139 }
1140 }
1141
1142 static void convert_file(struct proc *proc,
1143 struct file *file,
1144 struct libscols_line *ln)
1145
1146 {
1147 size_t i;
1148
1149 for (i = 0; i < ncolumns; i++)
1150 fill_column(proc, file, ln, get_column_id(i), i);
1151 }
1152
1153 static void convert(struct list_head *procs, struct lsfd_control *ctl)
1154 {
1155 struct list_head *p;
1156
1157 list_for_each (p, procs) {
1158 struct proc *proc = list_entry(p, struct proc, procs);
1159 struct list_head *f;
1160
1161 list_for_each (f, &proc->files) {
1162 struct file *file = list_entry(f, struct file, files);
1163 struct libscols_line *ln = scols_table_new_line(ctl->tb, NULL);
1164 struct lsfd_counter **counter = NULL;
1165
1166 if (!ln)
1167 err(EXIT_FAILURE, _("failed to allocate output line"));
1168
1169 convert_file(proc, file, ln);
1170
1171 if (!lsfd_filter_apply(ctl->filter, ln)) {
1172 scols_table_remove_line(ctl->tb, ln);
1173 continue;
1174 }
1175
1176 if (!ctl->counters)
1177 continue;
1178
1179 for (counter = ctl->counters; *counter; counter++)
1180 lsfd_counter_accumulate(*counter, ln);
1181 }
1182 }
1183 }
1184
1185 static void delete(struct list_head *procs, struct lsfd_control *ctl)
1186 {
1187 struct list_head *p;
1188
1189 list_for_each (p, procs) {
1190 struct proc *proc = list_entry(p, struct proc, procs);
1191 tdelete(proc, &proc_tree, proc_tree_compare);
1192 }
1193 list_free(procs, struct proc, procs, free_proc);
1194
1195 scols_unref_table(ctl->tb);
1196 lsfd_filter_free(ctl->filter);
1197 if (ctl->counters) {
1198 struct lsfd_counter **counter;
1199 for (counter = ctl->counters; *counter; counter++)
1200 lsfd_counter_free(*counter);
1201 free(ctl->counters);
1202 }
1203 }
1204
1205 static void emit(struct lsfd_control *ctl)
1206 {
1207 scols_print_table(ctl->tb);
1208 }
1209
1210
1211 static void initialize_class(const struct file_class *class)
1212 {
1213 if (class->initialize_class)
1214 class->initialize_class();
1215 }
1216
1217 static void initialize_classes(void)
1218 {
1219 initialize_class(&file_class);
1220 initialize_class(&cdev_class);
1221 initialize_class(&bdev_class);
1222 initialize_class(&sock_class);
1223 initialize_class(&unkn_class);
1224 }
1225
1226 static void finalize_class(const struct file_class *class)
1227 {
1228 if (class->finalize_class)
1229 class->finalize_class();
1230 }
1231
1232 static void finalize_classes(void)
1233 {
1234 finalize_class(&file_class);
1235 finalize_class(&cdev_class);
1236 finalize_class(&bdev_class);
1237 finalize_class(&sock_class);
1238 finalize_class(&unkn_class);
1239 }
1240
1241 static struct devdrv *new_devdrv(unsigned long major, const char *name)
1242 {
1243 struct devdrv *devdrv = xcalloc(1, sizeof(*devdrv));
1244
1245 INIT_LIST_HEAD(&devdrv->devdrvs);
1246
1247 devdrv->major = major;
1248 devdrv->name = xstrdup(name);
1249
1250 return devdrv;
1251 }
1252
1253 static void free_devdrv(struct devdrv *devdrv)
1254 {
1255 free(devdrv->name);
1256 free(devdrv);
1257 }
1258
1259 #define READ_DEVICES_LINE_LEN 256
1260 static struct devdrv *read_devdrv(const char *line)
1261 {
1262 unsigned long major;
1263 char name[READ_DEVICES_LINE_LEN];
1264
1265 if (sscanf(line, "%lu %s", &major, name) != 2)
1266 return NULL;
1267
1268 return new_devdrv(major, name);
1269 }
1270
1271 static void read_devices(struct list_head *chrdrvs_list,
1272 struct list_head *blkdrvs_list, FILE *devices_fp)
1273 {
1274 char line[READ_DEVICES_LINE_LEN];
1275
1276 /* Skip to the line "Character devices:". */
1277 while (fgets(line, sizeof(line), devices_fp)) {
1278 if (line[0] == 'C')
1279 break;
1280 continue;
1281 }
1282
1283 while (fgets(line, sizeof(line), devices_fp)) {
1284 /* Find the blank line before "Block devices:" line. */
1285 if (line[0] == '\n')
1286 break;
1287
1288 /* Read the character device drivers */
1289 struct devdrv *devdrv = read_devdrv(line);
1290 if (devdrv)
1291 list_add_tail(&devdrv->devdrvs, chrdrvs_list);
1292 }
1293
1294 /* Skip to the line "Block devices:". */
1295 while (fgets(line, sizeof(line), devices_fp)) {
1296 if (line[0] == 'B')
1297 break;
1298 continue;
1299 }
1300
1301 /* Read the block device drivers */
1302 while (fgets(line, sizeof(line), devices_fp)) {
1303 struct devdrv *devdrv = read_devdrv(line);
1304 if (devdrv)
1305 list_add_tail(&devdrv->devdrvs, blkdrvs_list);
1306 }
1307 }
1308
1309 static void initialize_devdrvs(void)
1310 {
1311 FILE *devices_fp;
1312
1313 INIT_LIST_HEAD(&chrdrvs);
1314 INIT_LIST_HEAD(&blkdrvs);
1315
1316 devices_fp = fopen("/proc/devices", "r");
1317 if (devices_fp) {
1318 read_devices(&chrdrvs, &blkdrvs, devices_fp);
1319 fclose(devices_fp);
1320 }
1321 }
1322
1323 static void finalize_devdrvs(void)
1324 {
1325 list_free(&blkdrvs, struct devdrv, devdrvs, free_devdrv);
1326 list_free(&chrdrvs, struct devdrv, devdrvs, free_devdrv);
1327 }
1328
1329 static const char *get_devdrv(struct list_head *devdrvs_list, unsigned long major)
1330 {
1331 struct list_head *c;
1332 list_for_each(c, devdrvs_list) {
1333 struct devdrv *devdrv = list_entry(c, struct devdrv, devdrvs);
1334 if (devdrv->major == major)
1335 return devdrv->name;
1336 }
1337 return NULL;
1338 }
1339
1340 const char *get_chrdrv(unsigned long major)
1341 {
1342 return get_devdrv(&chrdrvs, major);
1343 }
1344
1345 const char *get_blkdrv(unsigned long major)
1346 {
1347 return get_devdrv(&blkdrvs, major);
1348 }
1349
1350 struct name_manager *new_name_manager(void)
1351 {
1352 struct name_manager *nm = xcalloc(1, sizeof(struct name_manager));
1353
1354 nm->cache = new_idcache();
1355 if (!nm->cache)
1356 err(EXIT_FAILURE, _("failed to allocate an idcache"));
1357
1358 nm->next_id = 1; /* 0 is never issued as id. */
1359 return nm;
1360 }
1361
1362 void free_name_manager(struct name_manager *nm)
1363 {
1364 free_idcache(nm->cache);
1365 free(nm);
1366 }
1367
1368 const char *get_name(struct name_manager *nm, unsigned long id)
1369 {
1370 struct identry *e;
1371
1372 e = get_id(nm->cache, id);
1373
1374 return e? e->name: NULL;
1375 }
1376
1377 unsigned long add_name(struct name_manager *nm, const char *name)
1378 {
1379 struct identry *e = NULL, *tmp;
1380
1381 for (tmp = nm->cache->ent; tmp; tmp = tmp->next) {
1382 if (strcmp(tmp->name, name) == 0) {
1383 e = tmp;
1384 break;
1385 }
1386 }
1387
1388 if (e)
1389 return e->id;
1390
1391 e = xmalloc(sizeof(struct identry));
1392 e->name = xstrdup(name);
1393 e->id = nm->next_id++;
1394 e->next = nm->cache->ent;
1395 nm->cache->ent = e;
1396
1397 return e->id;
1398 }
1399
1400 static void read_process(struct lsfd_control *ctl, struct path_cxt *pc,
1401 pid_t pid, struct proc *leader)
1402 {
1403 char buf[BUFSIZ];
1404 struct proc *proc;
1405
1406 if (procfs_process_init_path(pc, pid) != 0)
1407 return;
1408
1409 proc = new_process(pid, leader);
1410 proc->command = procfs_process_get_cmdname(pc, buf, sizeof(buf)) > 0 ?
1411 xstrdup(buf) : xstrdup(_("(unknown)"));
1412 procfs_process_get_uid(pc, &proc->uid);
1413
1414 if (procfs_process_get_stat(pc, buf, sizeof(buf)) > 0) {
1415 char *p;
1416 unsigned int flags;
1417 char *pat = NULL;
1418
1419 /* See proc(5) about the column in the line. */
1420 xstrappend(&pat, "%*d (");
1421 for (p = proc->command; *p != '\0'; p++) {
1422 if (*p == '%')
1423 xstrappend(&pat, "%%");
1424 else
1425 xstrputc(&pat, *p);
1426 }
1427 xstrappend(&pat, ") %*c %*d %*d %*d %*d %*d %u %*[^\n]");
1428 if (sscanf(buf, pat, &flags) == 1)
1429 proc->kthread = !!(flags & PF_KTHREAD);
1430 free(pat);
1431 }
1432
1433 collect_execve_file(pc, proc, ctl->sockets_only);
1434
1435 if (proc->pid == proc->leader->pid
1436 || kcmp(proc->leader->pid, proc->pid, KCMP_FS, 0, 0) != 0)
1437 collect_fs_files(pc, proc, ctl->sockets_only);
1438
1439 if (proc->ns_mnt == 0 || !has_mnt_ns(proc->ns_mnt)) {
1440 FILE *mnt = ul_path_fopen(pc, "r", "mountinfo");
1441 if (mnt) {
1442 add_nodevs(mnt);
1443 if (proc->ns_mnt)
1444 add_mnt_ns(proc->ns_mnt);
1445 fclose(mnt);
1446 }
1447 }
1448
1449 collect_namespace_files(pc, proc);
1450
1451 /* If kcmp is not available,
1452 * there is no way to no whether threads share resources.
1453 * In such cases, we must pay the costs: call collect_mem_files()
1454 * and collect_fd_files().
1455 */
1456 if ((!ctl->sockets_only)
1457 && (proc->pid == proc->leader->pid
1458 || kcmp(proc->leader->pid, proc->pid, KCMP_VM, 0, 0) != 0))
1459 collect_mem_files(pc, proc);
1460
1461 if (proc->pid == proc->leader->pid
1462 || kcmp(proc->leader->pid, proc->pid, KCMP_FILES, 0, 0) != 0)
1463 collect_fd_files(pc, proc, ctl->sockets_only);
1464
1465 list_add_tail(&proc->procs, &ctl->procs);
1466 if (tsearch(proc, &proc_tree, proc_tree_compare) == NULL)
1467 errx(EXIT_FAILURE, _("failed to allocate memory"));
1468
1469 /* The tasks collecting overwrites @pc by /proc/<task-pid>/. Keep it as
1470 * the last path based operation in read_process()
1471 */
1472 if (ctl->threads && leader == NULL) {
1473 DIR *sub = NULL;
1474 pid_t tid = 0;
1475
1476 while (procfs_process_next_tid(pc, &sub, &tid) == 0) {
1477 if (tid == pid)
1478 continue;
1479 read_process(ctl, pc, tid, proc);
1480 }
1481 }
1482
1483 /* Let's be careful with number of open files */
1484 ul_path_close_dirfd(pc);
1485 }
1486
1487 static void parse_pids(const char *str, pid_t **pids, int *count)
1488 {
1489 long v;
1490 char *next = NULL;
1491
1492 if (*str == '\0')
1493 return;
1494
1495 errno = 0;
1496 v = strtol(str, &next, 10);
1497 if (errno)
1498 err(EXIT_FAILURE, _("unexpected value for pid specification: %s"), str);
1499 if (next == str)
1500 errx(EXIT_FAILURE, _("garbage at the end of pid specification: %s"), str);
1501 if (v < 0)
1502 errx(EXIT_FAILURE, _("out of range value for pid specification: %ld"), v);
1503
1504 (*count)++;
1505 *pids = xrealloc(*pids, (*count) * sizeof(**pids));
1506 (*pids)[*count - 1] = (pid_t)v;
1507
1508 while (next && *next != '\0'
1509 && (isspace((unsigned char)*next) || *next == ','))
1510 next++;
1511 if (*next != '\0')
1512 parse_pids(next, pids, count);
1513 }
1514
1515 static int pidcmp(const void *a, const void *b)
1516 {
1517 pid_t pa = *(pid_t *)a;
1518 pid_t pb = *(pid_t *)b;
1519
1520 if (pa < pb)
1521 return -1;
1522 else if (pa == pb)
1523 return 0;
1524 else
1525 return 1;
1526 }
1527
1528 static void sort_pids(pid_t pids[], const int count)
1529 {
1530 qsort(pids, count, sizeof(pid_t), pidcmp);
1531 }
1532
1533 static bool member_pids(const pid_t pid, const pid_t pids[], const int count)
1534 {
1535 return bsearch(&pid, pids, count, sizeof(pid_t), pidcmp)? true: false;
1536 }
1537
1538 static void collect_processes(struct lsfd_control *ctl, const pid_t pids[], int n_pids)
1539 {
1540 DIR *dir;
1541 struct dirent *d;
1542 struct path_cxt *pc = NULL;
1543
1544 pc = ul_new_path(NULL);
1545 if (!pc)
1546 err(EXIT_FAILURE, _("failed to alloc procfs handler"));
1547
1548 dir = opendir(_PATH_PROC);
1549 if (!dir)
1550 err(EXIT_FAILURE, _("failed to open /proc"));
1551
1552 while ((d = readdir(dir))) {
1553 pid_t pid;
1554
1555 if (procfs_dirent_get_pid(d, &pid) != 0)
1556 continue;
1557 if (n_pids == 0 || member_pids(pid, pids, n_pids))
1558 read_process(ctl, pc, pid, 0);
1559 }
1560
1561 closedir(dir);
1562 ul_unref_path(pc);
1563 }
1564
1565 static void __attribute__((__noreturn__)) usage(void)
1566 {
1567 FILE *out = stdout;
1568 size_t i;
1569
1570 fputs(USAGE_HEADER, out);
1571 fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
1572
1573 fputs(USAGE_OPTIONS, out);
1574 fputs(_(" -l, --threads list in threads level\n"), out);
1575 fputs(_(" -J, --json use JSON output format\n"), out);
1576 fputs(_(" -n, --noheadings don't print headings\n"), out);
1577 fputs(_(" -o, --output <list> output columns\n"), out);
1578 fputs(_(" -r, --raw use raw output format\n"), out);
1579 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out);
1580 fputs(_(" -p, --pid <pid(s)> collect information only specified processes\n"), out);
1581 fputs(_(" -i[4|6], --inet[=4|6] list only IPv4 and/or IPv6 sockets\n"), out);
1582 fputs(_(" -Q, --filter <expr> apply display filter\n"), out);
1583 fputs(_(" --debug-filter dump the internal data structure of filter and exit\n"), out);
1584 fputs(_(" -C, --counter <name>:<expr>\n"
1585 " define custom counter for --summary output\n"), out);
1586 fputs(_(" --dump-counters dump counter definitions\n"), out);
1587 fputs(_(" --summary[=<when>] print summary information (only, append, or never)\n"), out);
1588
1589 fputs(USAGE_SEPARATOR, out);
1590 printf(USAGE_HELP_OPTIONS(30));
1591
1592 fprintf(out, USAGE_COLUMNS);
1593
1594 for (i = 0; i < ARRAY_SIZE(infos); i++)
1595 fprintf(out, " %16s %-10s%s\n", infos[i].name,
1596 infos[i].json_type == SCOLS_JSON_STRING? "<string>":
1597 infos[i].json_type == SCOLS_JSON_NUMBER? "<number>":
1598 "<boolean>",
1599 _(infos[i].help));
1600
1601 printf(USAGE_MAN_TAIL("lsfd(1)"));
1602
1603 exit(EXIT_SUCCESS);
1604 }
1605
1606 static void append_filter_expr(char **a, const char *b, bool and)
1607 {
1608 if (*a == NULL) {
1609 *a = xstrdup(b);
1610 return;
1611 }
1612
1613 char *tmp = *a;
1614 *a = NULL;
1615
1616 xstrappend(a, "(");
1617 xstrappend(a, tmp);
1618 xstrappend(a, ")");
1619 if (and)
1620 xstrappend(a, "and(");
1621 else
1622 xstrappend(a, "or(");
1623 xstrappend(a, b);
1624 xstrappend(a, ")");
1625 }
1626
1627 static struct lsfd_filter *new_filter(const char *expr, bool debug, const char *err_prefix, struct lsfd_control *ctl)
1628 {
1629 struct lsfd_filter *filter;
1630 const char *errmsg;
1631
1632 filter = lsfd_filter_new(expr, ctl->tb,
1633 LSFD_N_COLS,
1634 column_name_to_id_cb,
1635 add_column_by_id_cb, ctl);
1636 errmsg = lsfd_filter_get_errmsg(filter);
1637 if (errmsg)
1638 errx(EXIT_FAILURE, "%s%s", err_prefix, errmsg);
1639 if (debug) {
1640 lsfd_filter_dump(filter, stdout);
1641 exit(EXIT_SUCCESS);
1642 }
1643
1644 return filter;
1645 }
1646
1647 static struct counter_spec *new_counter_spec(const char *spec_str)
1648 {
1649 char *sep;
1650 struct counter_spec *spec;
1651
1652 if (spec_str[0] == '\0')
1653 errx(EXIT_FAILURE,
1654 _("too short counter specification: -C/--counter %s"),
1655 spec_str);
1656 if (spec_str[0] == ':')
1657 errx(EXIT_FAILURE,
1658 _("no name for counter: -C/--counter %s"),
1659 spec_str);
1660
1661 sep = strchr(spec_str, ':');
1662 if (sep == NULL)
1663 errx(EXIT_FAILURE,
1664 _("no name for counter: -C/--counter %s"),
1665 spec_str);
1666 if (sep[1] == '\0')
1667 errx(EXIT_FAILURE,
1668 _("empty counter expression given: -C/--counter %s"),
1669 spec_str);
1670
1671 /* Split the spec_str in to name and expr. */
1672 *sep = '\0';
1673
1674 if (strchr(spec_str, '{'))
1675 errx(EXIT_FAILURE,
1676 _("don't use `{' in the name of a counter: %s"),
1677 spec_str);
1678
1679 spec = xmalloc(sizeof(struct counter_spec));
1680 INIT_LIST_HEAD(&spec->specs);
1681 spec->name = spec_str;
1682 spec->expr = sep + 1;
1683
1684 return spec;
1685 }
1686
1687 static void free_counter_spec(struct counter_spec *counter_spec)
1688 {
1689 free(counter_spec);
1690 }
1691
1692 static struct lsfd_counter *new_counter(const struct counter_spec *spec, struct lsfd_control *ctl)
1693 {
1694 struct lsfd_filter *filter;
1695
1696 filter = new_filter(spec->expr, false,
1697 _("failed in making filter for a counter: "),
1698 ctl);
1699 return lsfd_counter_new(spec->name, filter);
1700 }
1701
1702 static struct lsfd_counter **new_counters(struct list_head *specs, struct lsfd_control *ctl)
1703 {
1704 struct lsfd_counter **counters;
1705 size_t len = list_count_entries(specs);
1706 size_t i = 0;
1707 struct list_head *s;
1708
1709 counters = xcalloc(len + 1, sizeof(struct lsfd_counter *));
1710 list_for_each(s, specs) {
1711 struct counter_spec *spec = list_entry(s, struct counter_spec, specs);
1712 counters[i++] = new_counter(spec, ctl);
1713 }
1714 assert(counters[len] == NULL);
1715
1716 return counters;
1717 }
1718
1719 static struct lsfd_counter **new_default_counters(struct lsfd_control *ctl)
1720 {
1721 struct lsfd_counter **counters;
1722 size_t len = ARRAY_SIZE(default_counter_specs);
1723 size_t i;
1724
1725 counters = xcalloc(len + 1, sizeof(struct lsfd_counter *));
1726 for (i = 0; i < len; i++) {
1727 const struct counter_spec *spec = default_counter_specs + i;
1728 counters[i] = new_counter(spec, ctl);
1729 }
1730 assert(counters[len] == NULL);
1731
1732 return counters;
1733 }
1734
1735 static void dump_default_counter_specs(void)
1736 {
1737 size_t len = ARRAY_SIZE(default_counter_specs);
1738 size_t i;
1739
1740 puts("default counter specs:");
1741 for (i = 0; i < len; i++) {
1742 const struct counter_spec *spec = default_counter_specs + i;
1743 printf("\t%s:%s\n", spec->name, spec->expr);
1744 }
1745 }
1746
1747 static void dump_counter_specs(struct list_head *specs)
1748 {
1749 struct list_head *s;
1750
1751 puts("custom counter specs:");
1752 list_for_each(s, specs) {
1753 struct counter_spec *spec = list_entry(s, struct counter_spec, specs);
1754 printf("\t%s:%s\n", spec->name, spec->expr);
1755 }
1756 }
1757
1758 static struct libscols_table *new_summary_table(struct lsfd_control *ctl)
1759 {
1760 struct libscols_table *tb = scols_new_table();
1761
1762 struct libscols_column *name_cl, *value_cl;
1763
1764 if (!tb)
1765 err(EXIT_FAILURE, _("failed to allocate summary table"));
1766
1767 scols_table_enable_noheadings(tb, ctl->noheadings);
1768 scols_table_enable_raw(tb, ctl->raw);
1769 scols_table_enable_json(tb, ctl->json);
1770
1771 if(ctl->json)
1772 scols_table_set_name(tb, "lsfd-summary");
1773
1774
1775 value_cl = scols_table_new_column(tb, _("VALUE"), 0, SCOLS_FL_RIGHT);
1776 if (!value_cl)
1777 err(EXIT_FAILURE, _("failed to allocate summary column"));
1778 if (ctl->json)
1779 scols_column_set_json_type(value_cl, SCOLS_JSON_NUMBER);
1780
1781 name_cl = scols_table_new_column(tb, _("COUNTER"), 0, 0);
1782 if (!name_cl)
1783 err(EXIT_FAILURE, _("failed to allocate summary column"));
1784 if (ctl->json)
1785 scols_column_set_json_type(name_cl, SCOLS_JSON_STRING);
1786
1787 return tb;
1788 }
1789
1790 static void fill_summary_line(struct libscols_line *ln, struct lsfd_counter *counter)
1791 {
1792 char *str = NULL;
1793
1794 xasprintf(&str, "%llu", (unsigned long long)lsfd_counter_value(counter));
1795 if (!str)
1796 err(EXIT_FAILURE, _("failed to add summary data"));
1797 if (scols_line_refer_data(ln, 0, str))
1798 err(EXIT_FAILURE, _("failed to add summary data"));
1799
1800 if (scols_line_set_data(ln, 1, lsfd_counter_name(counter)))
1801 err(EXIT_FAILURE, _("failed to add summary data"));
1802 }
1803
1804 static void emit_summary(struct lsfd_control *ctl, struct lsfd_counter **counter)
1805 {
1806 struct libscols_table *tb = new_summary_table(ctl);
1807
1808 for (; *counter; counter++) {
1809 struct libscols_line *ln = scols_table_new_line(tb, NULL);
1810 fill_summary_line(ln, *counter);
1811 }
1812 scols_print_table(tb);
1813
1814 scols_unref_table(tb);
1815 }
1816
1817 static void attach_xinfos(struct list_head *procs)
1818 {
1819 struct list_head *p;
1820
1821 list_for_each (p, procs) {
1822 struct proc *proc = list_entry(p, struct proc, procs);
1823 struct list_head *f;
1824
1825 list_for_each (f, &proc->files) {
1826 struct file *file = list_entry(f, struct file, files);
1827 if (file->class->attach_xinfo)
1828 file->class->attach_xinfo(file);
1829 }
1830 }
1831 }
1832
1833 /* Filter expressions for implementing -i option.
1834 *
1835 * To list up the protocol names, use the following command line
1836 *
1837 * cd linux/net;
1838 * find . -type f -exec grep -A 1 --color=auto -nH --null -e 'struct proto .*{' \{\} +
1839 *
1840 */
1841 #define INET_SUBEXP_BEGIN "(SOCK.PROTONAME =~ \"^("
1842 #define INET4_REG "TCP|UDP|RAW|PING|UDP-Lite|SCTP|DCCP|L2TP/IP|SMC"
1843 #define INET6_REG "TCPv6|UDPv6|RAWv6|PINGv6|UDPLITEv6|SCTPv6|DCCPv6|L2TP/IPv6|SMC6"
1844 #define INET_SUBEXP_END ")$\")"
1845
1846 static const char *inet4_subexpr = INET_SUBEXP_BEGIN
1847 INET4_REG
1848 INET_SUBEXP_END;
1849 static const char *inet6_subexpr = INET_SUBEXP_BEGIN
1850 INET6_REG
1851 INET_SUBEXP_END;
1852 static const char *inet46_subexpr = INET_SUBEXP_BEGIN
1853 INET4_REG "|" INET6_REG
1854 INET_SUBEXP_END;
1855
1856 int main(int argc, char *argv[])
1857 {
1858 int c;
1859 size_t i;
1860 char *outarg = NULL;
1861 char *filter_expr = NULL;
1862 bool debug_filter = false;
1863 bool dump_counters = false;
1864 pid_t *pids = NULL;
1865 int n_pids = 0;
1866 struct list_head counter_specs;
1867
1868 struct lsfd_control ctl = {
1869 .show_main = 1
1870 };
1871
1872 INIT_LIST_HEAD(&counter_specs);
1873
1874 enum {
1875 OPT_DEBUG_FILTER = CHAR_MAX + 1,
1876 OPT_SUMMARY,
1877 OPT_DUMP_COUNTERS,
1878 };
1879 static const struct option longopts[] = {
1880 { "noheadings", no_argument, NULL, 'n' },
1881 { "output", required_argument, NULL, 'o' },
1882 { "version", no_argument, NULL, 'V' },
1883 { "help", no_argument, NULL, 'h' },
1884 { "json", no_argument, NULL, 'J' },
1885 { "raw", no_argument, NULL, 'r' },
1886 { "threads", no_argument, NULL, 'l' },
1887 { "notruncate", no_argument, NULL, 'u' },
1888 { "pid", required_argument, NULL, 'p' },
1889 { "inet", optional_argument, NULL, 'i' },
1890 { "filter", required_argument, NULL, 'Q' },
1891 { "debug-filter",no_argument, NULL, OPT_DEBUG_FILTER },
1892 { "summary", optional_argument, NULL, OPT_SUMMARY },
1893 { "counter", required_argument, NULL, 'C' },
1894 { "dump-counters",no_argument, NULL, OPT_DUMP_COUNTERS },
1895 { NULL, 0, NULL, 0 },
1896 };
1897
1898 setlocale(LC_ALL, "");
1899 bindtextdomain(PACKAGE, LOCALEDIR);
1900 textdomain(PACKAGE);
1901 close_stdout_atexit();
1902
1903 while ((c = getopt_long(argc, argv, "no:JrVhluQ:p:i::C:s", longopts, NULL)) != -1) {
1904 switch (c) {
1905 case 'n':
1906 ctl.noheadings = 1;
1907 break;
1908 case 'o':
1909 outarg = optarg;
1910 break;
1911 case 'J':
1912 ctl.json = 1;
1913 break;
1914 case 'r':
1915 ctl.raw = 1;
1916 break;
1917 case 'l':
1918 ctl.threads = 1;
1919 break;
1920 case 'u':
1921 ctl.notrunc = 1;
1922 break;
1923 case 'p':
1924 parse_pids(optarg, &pids, &n_pids);
1925 break;
1926 case 'i': {
1927 const char *subexpr = NULL;
1928
1929 ctl.sockets_only = 1;
1930 if (optarg == NULL)
1931 subexpr = inet46_subexpr;
1932 else if (strcmp(optarg, "4") == 0)
1933 subexpr = inet4_subexpr;
1934 else if (strcmp(optarg, "6") == 0)
1935 subexpr = inet6_subexpr;
1936 else
1937 errx(EXIT_FAILURE,
1938 _("unknown -i/--inet argument: %s"),
1939 optarg);
1940
1941 append_filter_expr(&filter_expr, subexpr, true);
1942 break;
1943 }
1944 case 'Q':
1945 append_filter_expr(&filter_expr, optarg, true);
1946 break;
1947 case 'C': {
1948 struct counter_spec *c = new_counter_spec(optarg);
1949 list_add_tail(&c->specs, &counter_specs);
1950 break;
1951 }
1952 case OPT_DEBUG_FILTER:
1953 debug_filter = true;
1954 break;
1955 case OPT_SUMMARY:
1956 if (optarg) {
1957 if (strcmp(optarg, "never") == 0)
1958 ctl.show_summary = 0, ctl.show_main = 1;
1959 else if (strcmp(optarg, "only") == 0)
1960 ctl.show_summary = 1, ctl.show_main = 0;
1961 else if (strcmp(optarg, "append") == 0)
1962 ctl.show_summary = 1, ctl.show_main = 1;
1963 else
1964 errx(EXIT_FAILURE, _("unsupported --summary argument"));
1965 } else
1966 ctl.show_summary = 1, ctl.show_main = 0;
1967 break;
1968 case OPT_DUMP_COUNTERS:
1969 dump_counters = true;
1970 break;
1971 case 'V':
1972 print_version(EXIT_SUCCESS);
1973 case 'h':
1974 usage();
1975 default:
1976 errtryhelp(EXIT_FAILURE);
1977 }
1978 }
1979 if (argv[optind])
1980 errtryhelp(EXIT_FAILURE);
1981
1982 #define INITIALIZE_COLUMNS(COLUMN_SPEC) \
1983 for (i = 0; i < ARRAY_SIZE(COLUMN_SPEC); i++) \
1984 columns[ncolumns++] = COLUMN_SPEC[i]
1985 if (!ncolumns) {
1986 if (ctl.threads)
1987 INITIALIZE_COLUMNS(default_threads_columns);
1988 else
1989 INITIALIZE_COLUMNS(default_columns);
1990 }
1991
1992 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
1993 &ncolumns, column_name_to_id) < 0)
1994 return EXIT_FAILURE;
1995
1996 scols_init_debug(0);
1997
1998 INIT_LIST_HEAD(&ctl.procs);
1999
2000 /* inilialize scols table */
2001 ctl.tb = scols_new_table();
2002 if (!ctl.tb)
2003 err(EXIT_FAILURE, _("failed to allocate output table"));
2004
2005 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
2006 scols_table_enable_raw(ctl.tb, ctl.raw);
2007 scols_table_enable_json(ctl.tb, ctl.json);
2008 if (ctl.json)
2009 scols_table_set_name(ctl.tb, "lsfd");
2010
2011 /* create output columns */
2012 for (i = 0; i < ncolumns; i++) {
2013 const struct colinfo *col = get_column_info(i);
2014 struct libscols_column *cl = add_column(ctl.tb, col);
2015
2016 if (!cl)
2017 err(EXIT_FAILURE, _("failed to allocate output column"));
2018
2019 if (ctl.notrunc) {
2020 int flags = scols_column_get_flags(cl);
2021 flags &= ~SCOLS_FL_TRUNC;
2022 scols_column_set_flags(cl, flags);
2023 }
2024 }
2025
2026 /* make fitler */
2027 if (filter_expr) {
2028 ctl.filter = new_filter(filter_expr, debug_filter, "", &ctl);
2029 free(filter_expr);
2030 }
2031
2032 if (dump_counters) {
2033 if (list_empty(&counter_specs))
2034 dump_default_counter_specs();
2035 else
2036 dump_counter_specs(&counter_specs);
2037 return 0;
2038 }
2039
2040 /* make counters */
2041 if (ctl.show_summary) {
2042 if (list_empty(&counter_specs))
2043 ctl.counters = new_default_counters(&ctl);
2044 else {
2045 ctl.counters = new_counters(&counter_specs, &ctl);
2046 list_free(&counter_specs, struct counter_spec, specs,
2047 free_counter_spec);
2048 }
2049 }
2050
2051 if (n_pids > 0)
2052 sort_pids(pids, n_pids);
2053
2054 /* collect data */
2055 initialize_nodevs();
2056 initialize_classes();
2057 initialize_devdrvs();
2058 initialize_ipc_table();
2059
2060 collect_processes(&ctl, pids, n_pids);
2061 free(pids);
2062
2063 attach_xinfos(&ctl.procs);
2064
2065 convert(&ctl.procs, &ctl);
2066
2067 /* print */
2068 if (ctl.show_main)
2069 emit(&ctl);
2070
2071 if (ctl.show_summary && ctl.counters)
2072 emit_summary(&ctl, ctl.counters);
2073
2074 /* cleanup */
2075 delete(&ctl.procs, &ctl);
2076
2077 finalize_ipc_table();
2078 finalize_devdrvs();
2079 finalize_classes();
2080 finalize_nodevs();
2081
2082 return 0;
2083 }