2 * lsfd(1) - list file descriptors
4 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 * Karel Zak <kzak@redhat.com>
8 * Very generally based on lsof(8) by Victor A. Abell <abe@purdue.edu>
9 * It supports multiple OSes. lsfd specializes to Linux.
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.
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.
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
27 #include <sys/types.h>
35 #include <sys/select.h>
38 #include <linux/sched.h>
39 #include <sys/syscall.h>
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
)
46 return syscall(SYS_kcmp
, pid1
, pid2
, type
, idx1
, idx2
);
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__
)))
64 /* lsfd uses kcmp only for optimization. If the platform doesn't provide
65 * kcmp, just returning an error is acceptable. */
72 * Defined in linux/include/linux/sched.h private header file. */
73 #define PF_KTHREAD 0x00200000 /* I am a kernel thread */
77 #include "closestream.h"
78 #include "column-list-table.h"
81 #include "fileutils.h"
83 #include "pathnames.h"
87 UL_DEBUG_DEFINE_MASK(lsfd
);
88 UL_DEBUG_DEFINE_MASKNAMES(lsfd
) = UL_DEBUG_EMPTY_MASKNAMES
;
90 static void lsfd_init_debug(void)
92 __UL_INIT_DEBUG_FROM_ENV(lsfd
, LSFD_DEBUG_
, 0, LSFD_DEBUG
);
96 * /proc/$pid/mountinfo entries
99 struct list_head nodevs
;
105 #define NODEV_TABLE_SIZE 97
106 struct list_head tables
[NODEV_TABLE_SIZE
];
108 static struct nodev_table nodev_table
;
110 struct mnt_namespace
{
113 struct list_head cooked_bdevs
;
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 */
121 struct list_head cooked_bdevs
;
127 static ino_t self_mntns_id
;
128 static int self_mntns_fd
= -1;
130 struct name_manager
{
131 struct idcache
*cache
;
132 unsigned long next_id
;
136 * /proc/devices entries
139 struct list_head devdrvs
;
144 static struct list_head chrdrvs
;
145 static struct list_head blkdrvs
;
151 #define IPC_TABLE_SIZE 997
153 struct list_head tables
[IPC_TABLE_SIZE
];
156 static struct ipc_table ipc_table
;
159 * Column related stuffs
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") },
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
,
224 [COL_EVENTPOLL_TFDS
] = {"EVENTPOLL.TFDS",
225 0, SCOLS_FL_WRAP
, SCOLS_JSON_ARRAY_NUMBER
,
226 N_("file descriptors targeted by the eventpoll file") },
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
,
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
,
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") },
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") },
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
,
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") },
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
,
386 [COL_TIMERFD_INTERVAL
] = { "TIMERFD.INTERVAL",
387 0, SCOLS_FL_RIGHT
, SCOLS_JSON_FLOAT
,
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") },
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)") },
436 static const int default_columns
[] = {
449 static const int default_threads_columns
[] = {
463 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
464 static size_t ncolumns
;
466 struct counter_spec
{
467 struct list_head specs
;
472 static const struct counter_spec default_counter_specs
[] = {
474 .name
= N_("processes"),
475 .expr
= "ASSOC == 'cwd'",
478 .name
= N_("root owned processes"),
479 .expr
= "(ASSOC == 'cwd') && (UID == 0)",
482 .name
= N_("kernel threads"),
483 .expr
= "(ASSOC == 'cwd') && KTHREAD",
486 .name
= N_("open files"),
490 .name
= N_("RO open files"),
491 .expr
= "(FD >= 0) and (MODE == 'r--')",
494 .name
= N_("WO open files"),
495 .expr
= "(FD >= 0) and (MODE == '-w-')",
498 .name
= N_("shared mappings"),
499 .expr
= "ASSOC == 'shm'",
502 .name
= N_("RO shared mappings"),
503 .expr
= "(ASSOC == 'shm') and (MODE == 'r--')",
506 .name
= N_("WO shared mappings"),
507 .expr
= "(ASSOC == 'shm') and (MODE == '-w-')",
510 .name
= N_("regular files"),
511 .expr
= "(FD >= 0) && (STTYPE == 'REG')",
514 .name
= N_("directories"),
515 .expr
= "(FD >= 0) && (STTYPE == 'DIR')",
518 .name
= N_("sockets"),
519 .expr
= "(FD >= 0) && (STTYPE == 'SOCK')",
522 .name
= N_("fifos/pipes"),
523 .expr
= "(FD >= 0) && (STTYPE == 'FIFO')",
526 .name
= N_("character devices"),
527 .expr
= "(FD >= 0) && (STTYPE == 'CHR')",
530 .name
= N_("block devices"),
531 .expr
= "(FD >= 0) && (STTYPE == 'BLK')",
534 .name
= N_("unknown types"),
535 .expr
= "(FD >= 0) && (STTYPE == 'UNKN')",
539 /* "userdata" used by callback for libsmartcols filter */
545 struct lsfd_control
{
546 struct libscols_table
*tb
; /* output */
547 struct list_head procs
; /* list of all processes */
549 unsigned int noheadings
: 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. */
559 struct libscols_filter
*filter
; /* filter */
560 struct libscols_filter
**ct_filters
; /* counters (NULL terminated array) */
563 static void *proc_tree
; /* for tsearch/tfind */
565 static int proc_tree_compare(const void *a
, const void *b
)
567 return ((struct proc
*)a
)->pid
- ((struct proc
*)b
)->pid
;
570 struct proc
*get_proc(pid_t pid
)
572 struct proc key
= { .pid
= pid
};
573 struct proc
**node
= tfind(&key
, &proc_tree
, proc_tree_compare
);
579 static int column_name_to_id(const char *name
, size_t namesz
)
583 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
584 const char *cn
= infos
[i
].name
;
586 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
589 warnx(_("unknown column: %s"), name
);
593 static int get_column_id(int num
)
596 assert((size_t) num
< ncolumns
);
597 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
602 static const struct colinfo
*get_column_info(int num
)
604 return &infos
[ get_column_id(num
) ];
607 static struct libscols_column
*add_column(struct libscols_table
*tb
,
608 const struct colinfo
*col
, int extra
)
610 struct libscols_column
*cl
;
611 int flags
= col
->flags
;
613 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
, flags
| extra
);
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
,
621 scols_column_set_safechars(cl
, "\n");
628 static struct libscols_column
*add_column_by_id(struct lsfd_control
*ctl
,
629 int colid
, int extra
)
631 struct libscols_column
*cl
;
633 if (ncolumns
>= ARRAY_SIZE(columns
))
634 errx(EXIT_FAILURE
, _("too many columns are added via filter expression"));
636 assert(colid
< LSFD_N_COLS
);
638 cl
= add_column(ctl
->tb
, infos
+ colid
, extra
);
640 err(EXIT_FAILURE
, _("failed to allocate output column"));
641 columns
[ncolumns
++] = colid
;
643 if (colid
== COL_TID
)
649 static const struct file_class
*stat2class(struct stat
*sb
)
655 switch (sb
->st_mode
& S_IFMT
) {
672 if (is_nsfs_dev(dev
))
673 return &nsfs_file_class
;
675 if (is_mqueue_dev(dev
))
676 return &mqueue_file_class
;
678 if (is_pidfs_dev(dev
))
679 return &pidfs_file_class
;
689 static struct file
*new_file(struct proc
*proc
, const struct file_class
*class,
690 struct stat
*sb
, const char *name
, int association
)
695 file
= xcalloc(1, class->size
);
700 INIT_LIST_HEAD(&file
->files
);
701 list_add_tail(&file
->files
, &proc
->files
);
703 file
->association
= association
;
704 file
->name
= xstrdup(name
);
710 static struct file
*new_readlink_error_file(struct proc
*proc
, int error_no
, int association
)
714 file
= xcalloc(1, readlink_error_class
.size
);
715 file
->class = &readlink_error_class
;
719 INIT_LIST_HEAD(&file
->files
);
720 list_add_tail(&file
->files
, &proc
->files
);
722 file
->error
.syscall
= "readlink";
723 file
->error
.number
= error_no
;
724 file
->association
= association
;
730 static struct file
*new_stat_error_file(struct proc
*proc
, const char *name
, int error_no
, int association
)
734 file
= xcalloc(1, stat_error_class
.size
);
735 file
->class = &stat_error_class
;
739 INIT_LIST_HEAD(&file
->files
);
740 list_add_tail(&file
->files
, &proc
->files
);
742 file
->error
.syscall
= "stat";
743 file
->error
.number
= error_no
;
744 file
->association
= association
;
745 file
->name
= xstrdup(name
);
750 static struct file
*copy_file(struct file
*old
, int new_association
)
752 struct file
*file
= xcalloc(1, old
->class->size
);
754 INIT_LIST_HEAD(&file
->files
);
755 file
->proc
= old
->proc
;
756 list_add_tail(&file
->files
, &old
->proc
->files
);
758 file
->class = old
->class;
759 file
->association
= new_association
;
760 file
->name
= xstrdup(old
->name
);
761 file
->stat
= old
->stat
;
766 static void file_init_content(struct file
*file
)
768 if (file
->class && file
->class->initialize_content
)
769 file
->class->initialize_content(file
);
772 static void free_file(struct file
*file
)
774 const struct file_class
*class = file
->class;
777 if (class->free_content
)
778 class->free_content(file
);
779 class = class->super
;
785 static struct proc
*new_proc(pid_t pid
, struct proc
*leader
)
787 struct proc
*proc
= xcalloc(1, sizeof(*proc
));
790 proc
->leader
= leader
? leader
: proc
;
791 proc
->command
= NULL
;
793 INIT_LIST_HEAD(&proc
->files
);
794 INIT_LIST_HEAD(&proc
->procs
);
795 INIT_LIST_HEAD(&proc
->eventpolls
);
801 static void free_proc(struct proc
*proc
)
803 list_free(&proc
->files
, struct file
, files
, free_file
);
809 static void read_fdinfo(struct file
*file
, FILE *fdinfo
)
813 while (fgets(buf
, sizeof(buf
), fdinfo
)) {
814 const struct file_class
*class;
815 char *val
= strchr(buf
, ':');
819 *val
++ = '\0'; /* terminate key */
821 val
= (char *) skip_space(val
);
822 rtrim_whitespace((unsigned char *) val
);
826 if (class->handle_fdinfo
827 && class->handle_fdinfo(file
, buf
, val
))
829 class = class->super
;
834 static struct file
*collect_file_symlink(struct path_cxt
*pc
,
840 char sym
[PATH_MAX
] = { '\0' };
842 struct file
*f
, *prev
;
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.
850 else if ((prev
= list_last_entry(&proc
->files
, struct file
, files
))
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
);
857 const struct file_class
*class = stat2class(&sb
);
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.
865 && (class != &sock_class
) && (class != &nsfs_file_class
))
867 f
= new_file(proc
, class, &sb
, sym
, assoc
);
870 file_init_content(f
);
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
);
882 else if (assoc
>= 0) {
883 /* file-descriptor based association */
886 if (ul_path_stat(pc
, &sb
, AT_SYMLINK_NOFOLLOW
, name
) == 0)
887 f
->mode
= sb
.st_mode
;
889 if (is_nsfs_dev(f
->stat
.st_dev
))
890 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
892 fdinfo
= ul_path_fopenf(pc
, "r", "fdinfo/%d", assoc
);
894 read_fdinfo(f
, fdinfo
);
902 /* read symlinks from /proc/#/fd
904 static void collect_fd_files(struct path_cxt
*pc
, struct proc
*proc
,
908 struct dirent
*d
= NULL
;
909 char path
[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX
))];
911 while (ul_path_next_dirent(pc
, &sub
, "fd", &d
) == 0) {
914 if (ul_strtou64(d
->d_name
, &num
, 10) != 0) /* only numbers */
917 snprintf(path
, sizeof(path
), "fd/%ju", (uintmax_t) num
);
918 collect_file_symlink(pc
, proc
, path
, num
, sockets_only
);
922 static void parse_maps_line(struct path_cxt
*pc
, char *buf
, struct proc
*proc
)
924 uint64_t start
, end
, offset
, ino
;
925 unsigned long major
, minor
;
926 enum association assoc
= ASSOC_MEM
;
928 struct file
*f
, *prev
;
929 char *path
, modestr
[5];
932 /* read rest of the map */
933 if (sscanf(buf
, "%"SCNx64
/* start */
936 " %"SCNx64
/* offset */
937 " %lx:%lx" /* maj:min */
938 " %"SCNu64
, /* inode */
940 &start
, &end
, modestr
, &offset
,
941 &major
, &minor
, &ino
) != 7)
944 /* Skip private anonymous mappings. */
945 if (major
== 0 && minor
== 0 && ino
== 0)
948 devno
= makedev(major
, minor
);
950 if (modestr
[3] == 's')
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.
956 prev
= list_last_entry(&proc
->files
, struct file
, files
);
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,
968 f
= new_file(proc
, stat2class(&sb
), &sb
, path
, -assoc
);
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' };
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
);
981 f
= new_file(proc
, stat2class(&sb
), &sb
, sym
, -assoc
);
984 if (modestr
[0] == 'r')
986 if (modestr
[1] == 'w')
988 if (modestr
[2] == 'x')
991 f
->map_start
= start
;
995 file_init_content(f
);
998 static void collect_mem_files(struct path_cxt
*pc
, struct proc
*proc
)
1003 fp
= ul_path_fopen(pc
, "r", "maps");
1007 while (fgets(buf
, sizeof(buf
), fp
))
1008 parse_maps_line(pc
, buf
, proc
);
1013 static void collect_outofbox_files(struct path_cxt
*pc
,
1015 enum association assocs
[],
1016 const char *names
[],
1022 for (i
= 0; i
< count
; i
++)
1023 collect_file_symlink(pc
, proc
, names
[assocs
[i
]], assocs
[i
] * -1,
1027 static void collect_execve_file(struct path_cxt
*pc
, struct proc
*proc
,
1030 enum association assocs
[] = { ASSOC_EXE
};
1031 const char *names
[] = {
1032 [ASSOC_EXE
] = "exe",
1034 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
1038 static void collect_fs_files(struct path_cxt
*pc
, struct proc
*proc
,
1041 enum association assocs
[] = { ASSOC_CWD
, ASSOC_ROOT
};
1042 const char *names
[] = {
1043 [ASSOC_CWD
] = "cwd",
1044 [ASSOC_ROOT
] = "root",
1046 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
1050 static void collect_namespace_files_tophalf(struct path_cxt
*pc
, struct proc
*proc
)
1052 enum association assocs
[] = {
1057 const char *names
[] = {
1058 [ASSOC_NS_CGROUP
] = "ns/cgroup",
1059 [ASSOC_NS_IPC
] = "ns/ipc",
1060 [ASSOC_NS_MNT
] = "ns/mnt",
1062 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
1063 /* Namespace information is alwasys needed. */
1067 static void collect_namespace_files_bottomhalf(struct path_cxt
*pc
, struct proc
*proc
)
1069 enum association assocs
[] = {
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",
1087 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
1088 /* Namespace information is alwasys needed. */
1092 static void reset_cooked_bdev(struct cooked_bdev
*bdev
, dev_t raw
, const char *filesystem
)
1095 free(bdev
->filesystem
);
1096 bdev
->filesystem
= xstrdup(filesystem
);
1099 static struct cooked_bdev
*new_cooked_bdev(dev_t cooked
, dev_t raw
, const char *filesystem
)
1101 struct cooked_bdev
*bdev
= xmalloc(sizeof(*bdev
));
1103 INIT_LIST_HEAD(&bdev
->cooked_bdevs
);
1104 bdev
->cooked
= cooked
;
1106 if (major(cooked
) == 0) {
1107 bdev
->filesystem
= NULL
;
1108 xasprintf(&bdev
->filesystem
, "%s:%lu",
1109 filesystem
, (unsigned long)minor(cooked
));
1111 bdev
->filesystem
= xstrdup(filesystem
);
1116 static void free_cooked_bdev(struct cooked_bdev
* bdev
)
1118 if (bdev
->filesystem
)
1119 free(bdev
->filesystem
);
1123 static void add_cooked_bdev(struct mnt_namespace
*mnt_ns
, dev_t cooked
, dev_t raw
, const char *filesystem
)
1125 struct cooked_bdev
*bdev
;
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
);
1136 bdev
= new_cooked_bdev(cooked
, raw
, filesystem
);
1137 list_add_tail(&bdev
->cooked_bdevs
, &mnt_ns
->cooked_bdevs
);
1140 static void dedup_cooked_bdevs(struct mnt_namespace
*mnt_ns
)
1142 struct list_head
*n
, *nnext
;
1144 list_for_each_safe(n
, nnext
, &mnt_ns
->cooked_bdevs
) {
1145 struct cooked_bdev
*bdev
= list_entry(n
, struct cooked_bdev
,
1147 if (bdev
->cooked
== bdev
->raw
) {
1149 free_cooked_bdev(bdev
);
1154 list_for_each(n
, &mnt_ns
->cooked_bdevs
) {
1155 struct cooked_bdev
*bdev
= list_entry(n
, struct cooked_bdev
,
1157 fprintf(stderr
, "mntns: %lu (major: %u, minor: %u) => (major: %u, minor: %u)\n",
1159 major(bdev
->cooked
), minor(bdev
->cooked
),
1160 major(bdev
->raw
), minor(bdev
->raw
));
1165 static struct mnt_namespace
*new_mnt_ns(ino_t id
)
1167 struct mnt_namespace
*mnt_ns
= xmalloc(sizeof(*mnt_ns
));
1170 mnt_ns
->read_mountinfo
= false;
1171 INIT_LIST_HEAD(&mnt_ns
->cooked_bdevs
);
1176 static void free_mnt_ns(void *mnt_ns
)
1178 list_free(&((struct mnt_namespace
*)mnt_ns
)->cooked_bdevs
,
1179 struct cooked_bdev
, cooked_bdevs
, free_cooked_bdev
);
1184 static int compare_mnt_ns(const void *a
, const void *b
)
1186 ino_t A
= (((struct mnt_namespace
*)a
)->id
);
1187 ino_t B
= (((struct mnt_namespace
*)b
)->id
);
1197 static struct mnt_namespace
*find_mnt_ns(ino_t id
)
1199 struct mnt_namespace key
= { .id
= id
};
1201 struct mnt_namespace
**mnt_ns
= tfind(&key
, &mnt_namespaces
, compare_mnt_ns
);
1207 static struct mnt_namespace
*add_mnt_ns(ino_t id
)
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"));
1217 static struct nodev
*new_nodev(unsigned long minor
, const char *filesystem
)
1219 struct nodev
*nodev
= xcalloc(1, sizeof(*nodev
));
1221 INIT_LIST_HEAD(&nodev
->nodevs
);
1222 nodev
->minor
= minor
;
1223 nodev
->filesystem
= xstrdup(filesystem
);
1228 static void free_nodev(struct nodev
*nodev
)
1230 free(nodev
->filesystem
);
1234 void add_nodev(unsigned long minor
, const char *filesystem
)
1236 struct nodev
*nodev
= new_nodev(minor
, filesystem
);
1237 unsigned long slot
= nodev
->minor
% NODEV_TABLE_SIZE
;
1239 list_add_tail(&nodev
->nodevs
, &nodev_table
.tables
[slot
]);
1242 static void initialize_nodevs(void)
1247 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1248 INIT_LIST_HEAD(&nodev_table
.tables
[i
]);
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
);
1256 static void finalize_nodevs(void)
1260 if (self_mntns_fd
>= 0)
1261 close(self_mntns_fd
);
1263 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1264 list_free(&nodev_table
.tables
[i
], struct nodev
, nodevs
, free_nodev
);
1266 tdestroy(mnt_namespaces
, free_mnt_ns
);
1269 const char *get_nodev_filesystem(unsigned long minor
)
1271 struct list_head
*n
;
1272 int slot
= minor
% NODEV_TABLE_SIZE
;
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
;
1282 static void add_nodevs_from_cooked_bdevs(struct mnt_namespace
*mnt_ns
)
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
,
1288 if (major(bdev
->cooked
) == 0
1289 && get_nodev_filesystem(minor(bdev
->cooked
)) == NULL
)
1290 add_nodev(minor(bdev
->cooked
), bdev
->filesystem
);
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
)
1299 if (mnt_ns
!= NULL
) {
1301 if (stat(mntpoint_filename
, &sb
) == 0)
1302 add_cooked_bdev(mnt_ns
, sb
.st_dev
, makedev(major
, minor
), filesystem
);
1307 if (get_nodev_filesystem(minor
))
1310 add_nodev(minor
, filesystem
);
1313 static void read_mountinfo(FILE *mountinfo
, struct mnt_namespace
*mnt_ns
)
1315 /* This can be very long. A line in mountinfo can have more than 3
1317 char line
[PATH_MAX
* 3 + 256];
1319 while (fgets(line
, sizeof(line
), mountinfo
)) {
1320 unsigned long major
, minor
;
1321 char filesystem
[256];
1322 int mntpoint_offset
, mntpoint_end_offset
;
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)
1329 /* 23 61 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel
1330 * --------------------------------------------------^
1332 if(sscanf(line
+ scan_offset
, "%*[^-] - %255s %*[^\n]",
1334 /* 1600 1458 0:55 / / rw,nodev,relatime - overlay overlay rw,context="s...
1335 * -------------------------------------^
1337 if (sscanf(line
+ scan_offset
, "- %255s %*[^\n]",
1341 line
[mntpoint_end_offset
] = '\0';
1342 process_mountinfo_entry(major
, minor
, filesystem
,
1343 line
+ mntpoint_offset
, mnt_ns
);
1347 dedup_cooked_bdevs(mnt_ns
);
1348 add_nodevs_from_cooked_bdevs(mnt_ns
);
1352 static void read_mountinfo_in_mntns(FILE *mountinfo
, struct mnt_namespace
*mnt_ns
,
1355 if (mntns_fd
>= 0 && setns(mntns_fd
, CLONE_NEWNS
) < 0) {
1360 read_mountinfo(mountinfo
, mnt_ns
);
1363 setns(self_mntns_fd
, CLONE_NEWNS
);
1366 static void initialize_ipc_table(void)
1368 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1369 INIT_LIST_HEAD(ipc_table
.tables
+ i
);
1372 static void free_ipc(struct ipc
*ipc
)
1374 if (ipc
->class->free
)
1375 ipc
->class->free(ipc
);
1379 static void finalize_ipc_table(void)
1381 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1382 list_free(&ipc_table
.tables
[i
], struct ipc
, ipcs
, free_ipc
);
1385 struct ipc
*new_ipc(const struct ipc_class
*class)
1387 struct ipc
*ipc
= xcalloc(1, class->size
);
1389 INIT_LIST_HEAD(&ipc
->endpoints
);
1390 INIT_LIST_HEAD(&ipc
->ipcs
);
1394 struct ipc
*get_ipc(struct file
*file
)
1397 struct list_head
*e
;
1398 const struct ipc_class
*ipc_class
;
1400 if (!file
->class->get_ipc_class
)
1403 ipc_class
= file
->class->get_ipc_class(file
);
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
)
1412 if (ipc_class
->is_suitable_ipc(ipc
, file
))
1418 void add_ipc(struct ipc
*ipc
, unsigned int hash
)
1420 int slot
= hash
% IPC_TABLE_SIZE
;
1421 list_add(&ipc
->ipcs
, &ipc_table
.tables
[slot
]);
1424 void init_endpoint(struct ipc_endpoint
*endpoint
)
1426 INIT_LIST_HEAD(&endpoint
->endpoints
);
1429 void add_endpoint(struct ipc_endpoint
*endpoint
, struct ipc
*ipc
)
1431 endpoint
->ipc
= ipc
;
1432 list_add(&endpoint
->endpoints
, &ipc
->endpoints
);
1436 static void fill_column(struct proc
*proc
,
1438 struct libscols_line
*ln
,
1440 size_t column_index
)
1442 const struct file_class
*class = file
->class;
1445 if (class->fill_column
1446 && class->fill_column(proc
, file
, ln
,
1447 column_id
, column_index
))
1449 class = class->super
;
1453 static int filter_filler_cb(
1454 struct libscols_filter
*fltr
__attribute__((__unused__
)),
1455 struct libscols_line
*ln
,
1459 struct filler_data
*fid
= (struct filler_data
*) userdata
;
1461 fill_column(fid
->proc
, fid
->file
, ln
, get_column_id(colnum
), colnum
);
1465 static void convert_file(struct proc
*proc
,
1467 struct libscols_line
*ln
)
1472 for (i
= 0; i
< ncolumns
; i
++) {
1473 if (scols_line_is_filled(ln
, i
))
1475 fill_column(proc
, file
, ln
, get_column_id(i
), i
);
1479 static void convert(struct list_head
*procs
, struct lsfd_control
*ctl
)
1481 struct list_head
*p
;
1483 list_for_each (p
, procs
) {
1484 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1485 struct list_head
*f
;
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
;
1493 err(EXIT_FAILURE
, _("failed to allocate output line"));
1496 struct filler_data fid
= {
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"));
1506 scols_table_remove_line(ctl
->tb
, ln
);
1511 convert_file(proc
, file
, ln
);
1513 if (!ctl
->ct_filters
)
1516 for (ct_fltr
= ctl
->ct_filters
; *ct_fltr
; ct_fltr
++)
1517 scols_line_apply_filter(ln
, *ct_fltr
, NULL
);
1522 static void delete(struct list_head
*procs
, struct lsfd_control
*ctl
)
1524 struct list_head
*p
;
1526 list_for_each (p
, procs
) {
1527 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1528 tdelete(proc
, &proc_tree
, proc_tree_compare
);
1530 list_free(procs
, struct proc
, procs
, free_proc
);
1532 scols_unref_table(ctl
->tb
);
1533 scols_unref_filter(ctl
->filter
);
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
);
1543 static void emit(struct lsfd_control
*ctl
)
1545 scols_print_table(ctl
->tb
);
1549 static void initialize_class(const struct file_class
*class)
1551 if (class->initialize_class
)
1552 class->initialize_class();
1555 static void initialize_classes(void)
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
);
1565 static void finalize_class(const struct file_class
*class)
1567 if (class->finalize_class
)
1568 class->finalize_class();
1571 static void finalize_classes(void)
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
);
1580 static struct devdrv
*new_devdrv(unsigned long major
, const char *name
)
1582 struct devdrv
*devdrv
= xcalloc(1, sizeof(*devdrv
));
1584 INIT_LIST_HEAD(&devdrv
->devdrvs
);
1586 devdrv
->major
= major
;
1587 devdrv
->name
= xstrdup(name
);
1592 static void free_devdrv(struct devdrv
*devdrv
)
1598 #define READ_DEVICES_LINE_LEN 256
1599 static struct devdrv
*read_devdrv(const char *line
)
1601 unsigned long major
;
1602 char name
[READ_DEVICES_LINE_LEN
];
1604 if (sscanf(line
, "%lu %s", &major
, name
) != 2)
1607 return new_devdrv(major
, name
);
1610 static void read_devices(struct list_head
*chrdrvs_list
,
1611 struct list_head
*blkdrvs_list
, FILE *devices_fp
)
1613 char line
[READ_DEVICES_LINE_LEN
];
1615 /* Skip to the line "Character devices:". */
1616 while (fgets(line
, sizeof(line
), devices_fp
)) {
1622 while (fgets(line
, sizeof(line
), devices_fp
)) {
1623 /* Find the blank line before "Block devices:" line. */
1624 if (line
[0] == '\n')
1627 /* Read the character device drivers */
1628 struct devdrv
*devdrv
= read_devdrv(line
);
1630 list_add_tail(&devdrv
->devdrvs
, chrdrvs_list
);
1633 /* Skip to the line "Block devices:". */
1634 while (fgets(line
, sizeof(line
), devices_fp
)) {
1640 /* Read the block device drivers */
1641 while (fgets(line
, sizeof(line
), devices_fp
)) {
1642 struct devdrv
*devdrv
= read_devdrv(line
);
1644 list_add_tail(&devdrv
->devdrvs
, blkdrvs_list
);
1648 static void initialize_devdrvs(void)
1652 INIT_LIST_HEAD(&chrdrvs
);
1653 INIT_LIST_HEAD(&blkdrvs
);
1655 devices_fp
= fopen("/proc/devices", "r");
1657 read_devices(&chrdrvs
, &blkdrvs
, devices_fp
);
1662 static void finalize_devdrvs(void)
1664 list_free(&blkdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1665 list_free(&chrdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1668 static const char *get_devdrv(struct list_head
*devdrvs_list
, unsigned long major
)
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
;
1679 const char *get_chrdrv(unsigned long major
)
1681 return get_devdrv(&chrdrvs
, major
);
1684 const char *get_blkdrv(unsigned long major
)
1686 return get_devdrv(&blkdrvs
, major
);
1689 struct name_manager
*new_name_manager(void)
1691 struct name_manager
*nm
= xcalloc(1, sizeof(struct name_manager
));
1693 nm
->cache
= new_idcache();
1695 err(EXIT_FAILURE
, _("failed to allocate an idcache"));
1697 nm
->next_id
= 1; /* 0 is never issued as id. */
1701 void free_name_manager(struct name_manager
*nm
)
1703 free_idcache(nm
->cache
);
1707 const char *get_name(struct name_manager
*nm
, unsigned long id
)
1711 e
= get_id(nm
->cache
, id
);
1713 return e
? e
->name
: NULL
;
1716 unsigned long add_name(struct name_manager
*nm
, const char *name
)
1718 struct identry
*e
= NULL
, *tmp
;
1720 for (tmp
= nm
->cache
->ent
; tmp
; tmp
= tmp
->next
) {
1721 if (strcmp(tmp
->name
, name
) == 0) {
1730 e
= xmalloc(sizeof(struct identry
));
1731 e
->name
= xstrdup(name
);
1732 e
->id
= nm
->next_id
++;
1733 e
->next
= nm
->cache
->ent
;
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
*))
1747 while (procfs_process_next_tid(pc
, &sub
, &tid
) == 0) {
1750 (*cb
)(ctl
, pc
, tid
, proc
);
1754 static int pollfdcmp(const void *a
, const void *b
)
1756 const struct pollfd
*apfd
= a
, *bpfd
= b
;
1758 return apfd
->fd
- bpfd
->fd
;
1761 static void mark_poll_fds_as_multiplexed(char *buf
,
1762 pid_t pid
, struct proc
*proc
)
1768 struct iovec remote
;
1771 struct list_head
*f
;
1773 if (sscanf(buf
, "%lx %lx", &fds
, &nfds
) != 2)
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
;
1784 n
= process_vm_readv(pid
, &local
, 1, &remote
, 1, 0);
1785 if (n
< 0 || ((size_t)n
) != local
.iov_len
)
1788 qsort(local
.iov_base
, nfds
, sizeof(struct pollfd
), pollfdcmp
);
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;
1801 free(local
.iov_base
);
1804 static void mark_select_fds_as_multiplexed(char *buf
,
1805 pid_t pid
, struct proc
*proc
)
1810 struct iovec local
[3];
1811 fd_set local_set
[3];
1812 struct iovec remote
[3];
1814 ssize_t expected_n
= 0;
1816 struct list_head
*f
;
1818 if (sscanf(buf
, "%lx %lx %lx %lx", &nfds
, fds
+ 0, fds
+ 1, fds
+ 2) != 4)
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
]);
1832 n
= process_vm_readv(pid
, local
, 3, remote
, 3, 0);
1833 if (n
< 0 || n
!= expected_n
)
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
;
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;
1850 static void parse_proc_syscall(struct lsfd_control
*ctl
__attribute__((__unused__
)),
1851 struct path_cxt
*pc
, pid_t pid
, struct proc
*proc
)
1857 if (procfs_process_get_syscall(pc
, buf
, sizeof(buf
)) <= 0)
1861 scn
= strtol(buf
, &ptr
, 10);
1870 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1875 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1878 #ifdef SYS_ppoll_time64
1879 case SYS_ppoll_time64
:
1880 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1886 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1891 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1894 #ifdef SYS_pselect6_time64
1895 case SYS_pselect6_time64
:
1896 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1902 static void read_process(struct lsfd_control
*ctl
, struct path_cxt
*pc
,
1903 pid_t pid
, struct proc
*leader
)
1908 if (procfs_process_init_path(pc
, pid
) != 0)
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
);
1916 if (procfs_process_get_stat(pc
, buf
, sizeof(buf
)) > 0) {
1921 /* See proc(5) about the column in the line. */
1922 xstrappend(&pat
, "%*d (");
1923 for (p
= proc
->command
; *p
!= '\0'; p
++) {
1925 xstrappend(&pat
, "%%");
1929 xstrappend(&pat
, ") %*c %*d %*d %*d %*d %*d %u %*[^\n]");
1930 if (sscanf(buf
, pat
, &flags
) == 1)
1931 proc
->kthread
= !!(flags
& PF_KTHREAD
);
1934 if (proc
->kthread
&& !ctl
->threads
) {
1939 collect_execve_file(pc
, proc
, ctl
->sockets_only
);
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
);
1945 /* Reading /proc/$pid/mountinfo is expensive.
1946 * mnt_namespaces is a table for avoiding reading mountinfo files
1947 * for an identical mnt namespace.
1949 * After reading a mountinfo file for a mnt namespace, we store $mnt_id
1950 * identifying the mnt namespace to mnt_namespaces.
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.
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.
1962 /* 1/3. Read /proc/$pid/ns/mnt */
1963 if (proc
->mnt_ns
== NULL
)
1964 collect_namespace_files_tophalf(pc
, proc
);
1966 /* 2/3. read /proc/$pid/mountinfo unless we have read it already.
1967 * The backing device for "nsfs" is solved here.
1969 if (proc
->mnt_ns
== NULL
|| !proc
->mnt_ns
->read_mountinfo
) {
1970 FILE *mountinfo
= ul_path_fopen(pc
, "r", "mountinfo");
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
);
1979 proc
->mnt_ns
->read_mountinfo
= true;
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.
1988 collect_namespace_files_bottomhalf(pc
, proc
);
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().
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
);
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
);
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"));
2008 if (ctl
->show_xmode
)
2009 parse_proc_syscall(ctl
, pc
, pid
, proc
);
2011 /* The tasks collecting overwrites @pc by /proc/<task-pid>/. Keep it as
2012 * the last path based operation in read_process()
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
);
2020 /* Let's be careful with number of open files */
2021 ul_path_close_dirfd(pc
);
2024 static void parse_pids(const char *str
, pid_t
**pids
, int *count
)
2033 v
= strtol(str
, &next
, 10);
2035 err(EXIT_FAILURE
, _("unexpected value for pid specification: %s"), str
);
2037 errx(EXIT_FAILURE
, _("garbage at the end of pid specification: %s"), str
);
2039 errx(EXIT_FAILURE
, _("out of range value for pid specification: %ld"), v
);
2042 *pids
= xreallocarray(*pids
, *count
, sizeof(**pids
));
2043 (*pids
)[*count
- 1] = (pid_t
)v
;
2045 while (next
&& *next
!= '\0'
2046 && (isspace((unsigned char)*next
) || *next
== ','))
2049 parse_pids(next
, pids
, count
);
2052 static int pidcmp(const void *a
, const void *b
)
2054 pid_t pa
= *(pid_t
*)a
;
2055 pid_t pb
= *(pid_t
*)b
;
2065 static void sort_pids(pid_t pids
[], const int count
)
2067 qsort(pids
, count
, sizeof(pid_t
), pidcmp
);
2070 static bool member_pids(const pid_t pid
, const pid_t pids
[], const int count
)
2072 return bsearch(&pid
, pids
, count
, sizeof(pid_t
), pidcmp
)? true: false;
2075 static void collect_processes(struct lsfd_control
*ctl
, const pid_t pids
[], int n_pids
)
2079 struct path_cxt
*pc
= NULL
;
2081 pc
= ul_new_path(NULL
);
2083 err(EXIT_FAILURE
, _("failed to alloc procfs handler"));
2085 dir
= opendir(_PATH_PROC
);
2087 err(EXIT_FAILURE
, _("failed to open /proc"));
2089 while ((d
= readdir(dir
))) {
2092 if (procfs_dirent_get_pid(d
, &pid
) != 0)
2094 if (n_pids
== 0 || member_pids(pid
, pids
, n_pids
))
2095 read_process(ctl
, pc
, pid
, 0);
2102 static void __attribute__((__noreturn__
)) list_colunms(const char *table_name
,
2107 struct libscols_table
*col_tb
= xcolumn_list_table_new(table_name
, out
, raw
, json
);
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>",
2114 scols_print_table(col_tb
);
2115 scols_unref_table(col_tb
);
2120 static void print_columns(FILE *out
, const char *prefix
, const int cols
[], size_t n_cols
)
2122 fprintf(out
, "%15s: ", prefix
);
2123 for (size_t i
= 0; i
< n_cols
; i
++) {
2126 fputs(infos
[cols
[i
]].name
, out
);
2131 static void __attribute__((__noreturn__
)) usage(void)
2135 fputs(USAGE_HEADER
, out
);
2136 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
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
);
2154 fputs(USAGE_SEPARATOR
, out
);
2155 fputs(_(" -H, --list-columns list the available columns\n"), out
);
2156 fprintf(out
, USAGE_HELP_OPTIONS(30));
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
));
2162 fprintf(out
, USAGE_MAN_TAIL("lsfd(1)"));
2167 static void append_filter_expr(char **a
, const char *b
, bool and)
2181 xstrappend(a
, "and(");
2183 xstrappend(a
, "or(");
2190 static struct libscols_filter
*new_filter(const char *expr
, bool debug
, struct lsfd_control
*ctl
)
2192 struct libscols_filter
*f
;
2193 struct libscols_iter
*itr
;
2195 const char *name
= NULL
;
2197 f
= scols_new_filter(NULL
);
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
));
2204 itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
2206 err(EXIT_FAILURE
, _("failed to allocate iterator"));
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
);
2212 int id
= column_name_to_id(name
, strlen(name
));
2214 col
= add_column_by_id(ctl
, id
, SCOLS_FL_HIDDEN
);
2216 nerrs
++; /* report all unknown columns */
2220 scols_filter_assign_column(f
, itr
, name
, col
);
2223 scols_free_iter(itr
);
2226 scols_dump_filter(f
, stdout
);
2235 static struct counter_spec
*new_counter_spec(const char *spec_str
)
2238 struct counter_spec
*spec
;
2240 if (spec_str
[0] == '\0')
2242 _("too short counter specification: -C/--counter %s"),
2244 if (spec_str
[0] == ':')
2246 _("no name for counter: -C/--counter %s"),
2249 sep
= strchr(spec_str
, ':');
2252 _("no name for counter: -C/--counter %s"),
2256 _("empty counter expression given: -C/--counter %s"),
2259 /* Split the spec_str in to name and expr. */
2262 if (strchr(spec_str
, '{'))
2264 _("don't use `{' in the name of a counter: %s"),
2267 spec
= xmalloc(sizeof(struct counter_spec
));
2268 INIT_LIST_HEAD(&spec
->specs
);
2269 spec
->name
= spec_str
;
2270 spec
->expr
= sep
+ 1;
2275 static void free_counter_spec(struct counter_spec
*counter_spec
)
2280 static struct libscols_filter
*new_counter(const struct counter_spec
*spec
, struct lsfd_control
*ctl
)
2282 struct libscols_filter
*f
;
2283 struct libscols_counter
*ct
;
2285 f
= new_filter(spec
->expr
, false, ctl
);
2287 ct
= scols_filter_new_counter(f
);
2289 err(EXIT_FAILURE
, _("failed to allocate counter"));
2291 scols_counter_set_name(ct
, spec
->name
);
2292 scols_counter_set_func(ct
, SCOLS_COUNTER_COUNT
);
2297 static struct libscols_filter
**new_counters(struct list_head
*specs
, struct lsfd_control
*ctl
)
2299 struct libscols_filter
**ct_filters
;
2300 size_t len
= list_count_entries(specs
);
2302 struct list_head
*s
;
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
);
2309 assert(ct_filters
[len
] == NULL
);
2314 static struct libscols_filter
**new_default_counters(struct lsfd_control
*ctl
)
2316 struct libscols_filter
**ct_filters
;
2317 size_t len
= ARRAY_SIZE(default_counter_specs
);
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
);
2325 assert(ct_filters
[len
] == NULL
);
2330 static void dump_default_counter_specs(void)
2332 size_t len
= ARRAY_SIZE(default_counter_specs
);
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
);
2342 static void dump_counter_specs(struct list_head
*specs
)
2344 struct list_head
*s
;
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
);
2353 static struct libscols_table
*new_summary_table(struct lsfd_control
*ctl
)
2355 struct libscols_table
*tb
= scols_new_table();
2357 struct libscols_column
*name_cl
, *value_cl
;
2360 err(EXIT_FAILURE
, _("failed to allocate summary table"));
2362 scols_table_enable_noheadings(tb
, ctl
->noheadings
);
2363 scols_table_enable_raw(tb
, ctl
->raw
);
2364 scols_table_enable_json(tb
, ctl
->json
);
2367 scols_table_set_name(tb
, "lsfd-summary");
2370 value_cl
= scols_table_new_column(tb
, _("VALUE"), 0, SCOLS_FL_RIGHT
);
2372 err(EXIT_FAILURE
, _("failed to allocate summary column"));
2374 scols_column_set_json_type(value_cl
, SCOLS_JSON_NUMBER
);
2376 name_cl
= scols_table_new_column(tb
, _("COUNTER"), 0, 0);
2378 err(EXIT_FAILURE
, _("failed to allocate summary column"));
2380 scols_column_set_json_type(name_cl
, SCOLS_JSON_STRING
);
2385 static void emit_summary(struct lsfd_control
*ctl
)
2387 struct libscols_iter
*itr
;
2388 struct libscols_filter
**ct_fltr
;
2389 struct libscols_table
*tb
= new_summary_table(ctl
);
2391 itr
= scols_new_iter(SCOLS_ITER_FORWARD
);
2393 for (ct_fltr
= ctl
->ct_filters
; *ct_fltr
; ct_fltr
++) {
2394 struct libscols_counter
*ct
= NULL
;
2396 scols_reset_iter(itr
, SCOLS_ITER_FORWARD
);
2397 while (scols_filter_next_counter(*ct_fltr
, itr
, &ct
) == 0) {
2399 struct libscols_line
*ln
;
2401 ln
= scols_table_new_line(tb
, NULL
);
2403 err(EXIT_FAILURE
, _("failed to allocate summary line"));
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"));
2413 scols_free_iter(itr
);
2414 scols_print_table(tb
);
2416 scols_unref_table(tb
);
2419 static void attach_xinfos(struct list_head
*procs
)
2421 struct list_head
*p
;
2423 list_for_each (p
, procs
) {
2424 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
2425 struct list_head
*f
;
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
);
2435 static void set_multiplexed_flags(struct list_head
*procs
)
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;
2452 /* Filter expressions for implementing -i option.
2454 * To list up the protocol names, use the following command line
2457 * find . -type f -exec grep -A 1 --color=auto -nH --null -e 'struct proto .*{' \{\} +
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 ")$\")"
2465 static const char *inet4_subexpr
= INET_SUBEXP_BEGIN
2468 static const char *inet6_subexpr
= INET_SUBEXP_BEGIN
2471 static const char *inet46_subexpr
= INET_SUBEXP_BEGIN
2472 INET4_REG
"|" INET6_REG
2475 int main(int argc
, char *argv
[])
2479 char *outarg
= NULL
;
2480 char *filter_expr
= NULL
;
2481 bool debug_filter
= false;
2482 bool dump_counters
= false;
2485 struct list_head counter_specs
;
2487 struct lsfd_control ctl
= {
2491 INIT_LIST_HEAD(&counter_specs
);
2494 OPT_DEBUG_FILTER
= CHAR_MAX
+ 1,
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 },
2522 setlocale(LC_ALL
, "");
2523 bindtextdomain(PACKAGE
, LOCALEDIR
);
2524 textdomain(PACKAGE
);
2525 close_stdout_atexit();
2527 while ((c
= getopt_long(argc
, argv
, "no:JrVhluQ:p:i::C:sH", longopts
, NULL
)) != -1) {
2548 parse_pids(optarg
, &pids
, &n_pids
);
2551 const char *subexpr
= NULL
;
2553 ctl
.sockets_only
= 1;
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
;
2562 _("unknown -i/--inet argument: %s"),
2565 append_filter_expr(&filter_expr
, subexpr
, true);
2569 append_filter_expr(&filter_expr
, optarg
, true);
2572 struct counter_spec
*c
= new_counter_spec(optarg
);
2573 list_add_tail(&c
->specs
, &counter_specs
);
2576 case OPT_DEBUG_FILTER
:
2577 debug_filter
= true;
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;
2588 errx(EXIT_FAILURE
, _("unsupported --summary argument"));
2590 ctl
.show_summary
= 1, ctl
.show_main
= 0;
2592 case OPT_DUMP_COUNTERS
:
2593 dump_counters
= true;
2595 case OPT_DROP_PRIVILEGE
:
2596 if (setuid(1) == -1)
2597 err(EXIT_FAILURE
, _("failed to drop privilege"));
2600 print_version(EXIT_SUCCESS
);
2607 errtryhelp(EXIT_FAILURE
);
2612 list_colunms("lsfd-columns", stdout
, ctl
.raw
, ctl
.json
); /* print and exit */
2615 errtryhelp(EXIT_FAILURE
);
2617 #define INITIALIZE_COLUMNS(COLUMN_SPEC) \
2618 for (i = 0; i < ARRAY_SIZE(COLUMN_SPEC); i++) \
2619 columns[ncolumns++] = COLUMN_SPEC[i]
2622 INITIALIZE_COLUMNS(default_threads_columns
);
2624 INITIALIZE_COLUMNS(default_columns
);
2627 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
2628 &ncolumns
, column_name_to_id
) < 0)
2629 return EXIT_FAILURE
;
2631 scols_init_debug(0);
2633 INIT_LIST_HEAD(&ctl
.procs
);
2635 /* inilialize scols table */
2636 ctl
.tb
= scols_new_table();
2638 err(EXIT_FAILURE
, _("failed to allocate output table"));
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
);
2644 scols_table_set_name(ctl
.tb
, "lsfd");
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);
2652 err(EXIT_FAILURE
, _("failed to allocate output column"));
2655 int flags
= scols_column_get_flags(cl
);
2656 flags
&= ~SCOLS_FL_TRUNC
;
2657 scols_column_set_flags(cl
, flags
);
2663 ctl
.filter
= new_filter(filter_expr
, debug_filter
, &ctl
);
2667 if (dump_counters
) {
2668 if (list_empty(&counter_specs
))
2669 dump_default_counter_specs();
2671 dump_counter_specs(&counter_specs
);
2676 if (ctl
.show_summary
) {
2677 if (list_empty(&counter_specs
))
2678 ctl
.ct_filters
= new_default_counters(&ctl
);
2680 ctl
.ct_filters
= new_counters(&counter_specs
, &ctl
);
2681 list_free(&counter_specs
, struct counter_spec
, specs
,
2687 sort_pids(pids
, n_pids
);
2689 if (scols_table_get_column_by_name(ctl
.tb
, "XMODE"))
2694 * The call initialize_ipc_table() must come before
2695 * initialize_classes.
2697 initialize_nodevs();
2698 initialize_ipc_table();
2699 initialize_classes();
2700 initialize_devdrvs();
2702 collect_processes(&ctl
, pids
, n_pids
);
2705 attach_xinfos(&ctl
.procs
);
2707 set_multiplexed_flags(&ctl
.procs
);
2710 convert(&ctl
.procs
, &ctl
);
2716 if (ctl
.show_summary
&& ctl
.ct_filters
)
2720 delete(&ctl
.procs
, &ctl
);
2724 finalize_ipc_table();