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