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