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