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>
40 #include <linux/kcmp.h>
41 static int kcmp(pid_t pid1
, pid_t pid2
, int type
,
42 unsigned long idx1
, unsigned long idx2
)
44 return syscall(SYS_kcmp
, pid1
, pid2
, type
, idx1
, idx2
);
48 * Defined in linux/include/linux/sched.h private header file. */
49 #define PF_KTHREAD 0x00200000 /* I am a kernel thread */
53 #include "closestream.h"
56 #include "fileutils.h"
58 #include "pathnames.h"
61 #include "lsfd-filter.h"
62 #include "lsfd-counter.h"
65 * /proc/$pid/mountinfo entries
68 struct list_head nodevs
;
74 #define NODEV_TABLE_SIZE 97
75 struct list_head tables
[NODEV_TABLE_SIZE
];
77 static struct nodev_table nodev_table
;
80 struct idcache
*cache
;
81 unsigned long next_id
;
85 * /proc/devices entries
88 struct list_head devdrvs
;
93 static struct list_head chrdrvs
;
94 static struct list_head blkdrvs
;
100 #define IPC_TABLE_SIZE 997
102 struct list_head tables
[IPC_TABLE_SIZE
];
105 static struct ipc_table ipc_table
;
108 * Column related stuffs
120 /* columns descriptions */
121 static const struct colinfo infos
[] = {
122 [COL_AINODECLASS
] = { "AINODECLASS",
123 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
124 N_("class of anonymous inode") },
125 [COL_ASSOC
] = { "ASSOC",
126 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
127 N_("association between file and process") },
128 [COL_BLKDRV
] = { "BLKDRV",
129 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
130 N_("block device driver name resolved by /proc/devices") },
131 [COL_BPF_MAP_ID
] = { "BPF-MAP.ID",
132 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
133 N_("bpf map id associated with the fd") },
134 [COL_BPF_MAP_TYPE
] = { "BPF-MAP.TYPE",
135 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
136 N_("bpf map type (decoded)") },
137 [COL_BPF_MAP_TYPE_RAW
]= { "BPF-MAP.TYPE.RAW",
138 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
139 N_("bpf map type (raw)") },
140 [COL_BPF_NAME
] = { "BPF.NAME",
141 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
142 N_("bpf object name") },
143 [COL_BPF_PROG_ID
] = { "BPF-PROG.ID",
144 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
145 N_("bpf program id associated with the fd") },
146 [COL_BPF_PROG_TYPE
] = { "BPF-PROG.TYPE",
147 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
148 N_("bpf program type (decoded)") },
149 [COL_BPF_PROG_TYPE_RAW
]= { "BPF-PROG.TYPE.RAW",
150 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
151 N_("bpf program type (raw)") },
152 [COL_CHRDRV
] = { "CHRDRV",
153 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
154 N_("character device driver name resolved by /proc/devices") },
155 [COL_COMMAND
] = { "COMMAND",
156 0.3, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
157 N_("command of the process opening the file") },
158 [COL_DELETED
] = { "DELETED",
159 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
160 N_("reachability from the file system") },
162 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
163 N_("ID of device containing file") },
164 [COL_DEVTYPE
] = { "DEVTYPE",
165 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
166 N_("device type (blk, char, or nodev)") },
167 [COL_ENDPOINTS
] = { "ENDPOINTS",
168 0, SCOLS_FL_WRAP
, SCOLS_JSON_ARRAY_STRING
,
169 N_("IPC endpoints information communicated with the fd") },
170 [COL_EVENTFD_ID
] = {"EVENTFD.ID",
171 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
173 [COL_EVENTPOLL_TFDS
] = {"EVENTPOLL.TFDS",
174 0, SCOLS_FL_WRAP
, SCOLS_JSON_ARRAY_NUMBER
,
175 N_("file descriptors targeted by the eventpoll file") },
177 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
178 N_("file descriptor for the file") },
179 [COL_FLAGS
] = { "FLAGS",
180 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
181 N_("flags specified when opening the file") },
182 [COL_FUID
] = { "FUID",
183 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
184 N_("user ID number of the file's owner") },
185 [COL_INET_LADDR
] = { "INET.LADDR",
186 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
187 N_("local IP address") },
188 [COL_INET_RADDR
] = { "INET.RADDR",
189 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
190 N_("remote IP address") },
191 [COL_INET6_LADDR
] = { "INET6.LADDR",
192 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
193 N_("local IPv6 address") },
194 [COL_INET6_RADDR
] = { "INET6.RADDR",
195 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
196 N_("remote IPv6 address") },
197 [COL_INODE
] = { "INODE",
198 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
199 N_("inode number") },
200 [COL_INOTIFY_INODES
] = { "INOTIFY.INODES",
201 0, SCOLS_FL_WRAP
, SCOLS_JSON_ARRAY_STRING
,
202 N_("list of monitoring inodes (cooked)") },
203 [COL_INOTIFY_INODES_RAW
]={ "INOTIFY.INODES.RAW",
204 0, SCOLS_FL_WRAP
, SCOLS_JSON_ARRAY_STRING
,
205 N_("list of monitoring inodes (raw, don't decode devices)") },
206 [COL_KNAME
] = { "KNAME",
207 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
208 N_("name of the file (raw)") },
209 [COL_KTHREAD
] = { "KTHREAD",
210 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
211 N_("opened by a kernel thread") },
212 [COL_MAJMIN
] = { "MAJ:MIN",
213 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
214 N_("device ID for special, or ID of device containing file") },
215 [COL_MAPLEN
] = { "MAPLEN",
216 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
217 N_("length of file mapping (in page)") },
218 [COL_MISCDEV
] = { "MISCDEV",
219 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
220 N_("misc character device name resolved by /proc/misc") },
221 [COL_MNT_ID
] = { "MNTID",
222 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
224 [COL_MODE
] = { "MODE",
225 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
226 N_("access mode (rwx)") },
227 [COL_NAME
] = { "NAME",
228 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
229 N_("name of the file (cooked)") },
230 [COL_NETLINK_GROUPS
] = { "NETLINK.GROUPS",
231 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
232 N_("netlink multicast groups") },
233 [COL_NETLINK_LPORT
] = { "NETLINK.LPORT",
234 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
235 N_("netlink local port id") },
236 [COL_NETLINK_PROTOCOL
] = { "NETLINK.PROTOCOL",
237 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
238 N_("netlink protocol") },
239 [COL_NLINK
] = { "NLINK",
240 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
242 [COL_NS_NAME
] = { "NS.NAME",
243 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
244 N_("name of the namespace (NS.TYPE:[INODE])") },
245 [COL_NS_TYPE
] = { "NS.TYPE",
246 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
247 N_("type of the namespace") },
248 [COL_OWNER
] = { "OWNER",
249 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
250 N_("owner of the file") },
251 [COL_PACKET_IFACE
] = { "PACKET.IFACE",
252 0, SCOLS_FL_RIGHT
,SCOLS_JSON_STRING
,
253 N_("net interface associated with the packet socket") },
254 [COL_PACKET_PROTOCOL
] = { "PACKET.PROTOCOL",
255 0, SCOLS_FL_RIGHT
,SCOLS_JSON_STRING
,
256 N_("L3 protocol associated with the packet socket") },
257 [COL_PARTITION
] = { "PARTITION",
258 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
259 N_("block device name resolved by /proc/partition") },
261 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
262 N_("PID of the process opening the file") },
263 [COL_PIDFD_COMM
] = { "PIDFD.COMM",
264 0.2, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
265 N_("command of the process targeted by the pidfd") },
266 [COL_PIDFD_NSPID
] = { "PIDFD.NSPID",
267 0.2, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
268 N_("NSpid field in fdinfo of the pidfd") },
269 [COL_PIDFD_PID
] = { "PIDFD.PID",
270 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
271 N_("PID of the process targeted by the pidfd") },
272 [COL_PING_ID
] = { "PING.ID",
273 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
274 N_("ICMP echo request ID") },
276 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
277 N_("file position") },
278 [COL_RAW_PROTOCOL
] = { "RAW.PROTOCOL",
279 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
280 N_("protocol number of the raw socket") },
281 [COL_RDEV
] = { "RDEV",
282 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
283 N_("device ID (if special file)") },
284 [COL_SIGNALFD_MASK
] = { "SIGNALFD.MASK",
285 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
286 N_("masked signals") },
287 [COL_SIZE
] = { "SIZE",
288 4, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
290 [COL_SOCK_LISTENING
] = { "SOCK.LISTENING",
291 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
292 N_("listening socket") },
293 [COL_SOCK_NETNS
] = { "SOCK.NETNS",
294 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
295 N_("inode identifying network namespace where the socket belongs to") },
296 [COL_SOCK_PROTONAME
] = { "SOCK.PROTONAME",
297 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
298 N_("protocol name") },
299 [COL_SOCK_SHUTDOWN
] = { "SOCK.SHUTDOWN",
300 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
301 N_("shutdown state of socket ([-r?][-w?])") },
302 [COL_SOCK_STATE
] = { "SOCK.STATE",
303 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
304 N_("state of socket") },
305 [COL_SOCK_TYPE
] = { "SOCK.TYPE",
306 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
307 N_("type of socket") },
308 [COL_SOURCE
] = { "SOURCE",
309 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
310 N_("file system, partition, or device containing file") },
311 [COL_STTYPE
] = { "STTYPE",
312 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
313 N_("file type (raw)") },
314 [COL_TCP_LADDR
] = { "TCP.LADDR",
315 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
316 N_("local TCP address (INET address:TCP port)") },
317 [COL_TCP_RADDR
] = { "TCP.RADDR",
318 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
319 N_("remote TCP address (INET address:TCP port)") },
320 [COL_TCP_LPORT
] = { "TCP.LPORT",
321 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
322 N_("local TCP port") },
323 [COL_TCP_RPORT
] = { "TCP.RPORT",
324 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
325 N_("remote TCP port") },
327 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
328 N_("thread ID of the process opening the file") },
329 [COL_TIMERFD_CLOCKID
] = { "TIMERFD.CLOCKID",
330 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
332 [COL_TIMERFD_INTERVAL
] = { "TIMERFD.INTERVAL",
333 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
335 [COL_TIMERFD_REMAINING
]= { "TIMERFD.REMAINING",
336 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
337 N_("remaining time") },
338 [COL_TUN_IFACE
] = { "TUN.IFACE",
339 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
340 N_("network interface behind the tun device") },
341 [COL_TYPE
] = { "TYPE",
342 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
343 N_("file type (cooked)") },
344 [COL_UDP_LADDR
] = { "UDP.LADDR",
345 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
346 N_("local UDP address (INET address:UDP port)") },
347 [COL_UDP_RADDR
] = { "UDP.RADDR",
348 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
349 N_("remote UDP address (INET address:UDP port)") },
350 [COL_UDP_LPORT
] = { "UDP.LPORT",
351 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
352 N_("local UDP port") },
353 [COL_UDP_RPORT
] = { "UDP.RPORT",
354 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
355 N_("remote UDP port") },
356 [COL_UDPLITE_LADDR
] = { "UDPLITE.LADDR",
357 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
358 N_("local UDPLite address (INET address:UDPLite port)") },
359 [COL_UDPLITE_RADDR
] = { "UDPLITE.RADDR",
360 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
361 N_("remote UDPLite address (INET address:UDPLite port)") },
362 [COL_UDPLITE_LPORT
] = { "UDPLITE.LPORT",
363 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
364 N_("local UDPLite port") },
365 [COL_UDPLITE_RPORT
] = { "UDPLITE.RPORT",
366 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
367 N_("remote UDPLite port") },
369 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
370 N_("user ID number of the process") },
371 [COL_UNIX_PATH
] = { "UNIX.PATH",
372 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
373 N_("filesystem pathname for UNIX domain socket") },
374 [COL_USER
] = { "USER",
375 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
376 N_("user of the process") },
377 [COL_XMODE
] = { "XMODE",
378 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
379 N_("extended version of MDOE (rwxD[Ll]m)") },
382 static const int default_columns
[] = {
395 static const int default_threads_columns
[] = {
409 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
410 static size_t ncolumns
;
412 static ino_t
*mnt_namespaces
;
413 static size_t nspaces
;
415 struct counter_spec
{
416 struct list_head specs
;
421 static const struct counter_spec default_counter_specs
[] = {
423 .name
= N_("processes"),
424 .expr
= "ASSOC == 'cwd'",
427 .name
= N_("root owned processes"),
428 .expr
= "(ASSOC == 'cwd') && (UID == 0)",
431 .name
= N_("kernel threads"),
432 .expr
= "(ASSOC == 'cwd') && KTHREAD",
435 .name
= N_("open files"),
439 .name
= N_("RO open files"),
440 .expr
= "(FD >= 0) and (MODE == 'r--')",
443 .name
= N_("WO open files"),
444 .expr
= "(FD >= 0) and (MODE == '-w-')",
447 .name
= N_("shared mappings"),
448 .expr
= "ASSOC == 'shm'",
451 .name
= N_("RO shared mappings"),
452 .expr
= "(ASSOC == 'shm') and (MODE == 'r--')",
455 .name
= N_("WO shared mappings"),
456 .expr
= "(ASSOC == 'shm') and (MODE == '-w-')",
459 .name
= N_("regular files"),
460 .expr
= "(FD >= 0) && (STTYPE == 'REG')",
463 .name
= N_("directories"),
464 .expr
= "(FD >= 0) && (STTYPE == 'DIR')",
467 .name
= N_("sockets"),
468 .expr
= "(FD >= 0) && (STTYPE == 'SOCK')",
471 .name
= N_("fifos/pipes"),
472 .expr
= "(FD >= 0) && (STTYPE == 'FIFO')",
475 .name
= N_("character devices"),
476 .expr
= "(FD >= 0) && (STTYPE == 'CHR')",
479 .name
= N_("block devices"),
480 .expr
= "(FD >= 0) && (STTYPE == 'BLK')",
483 .name
= N_("unknown types"),
484 .expr
= "(FD >= 0) && (STTYPE == 'UNKN')",
488 struct lsfd_control
{
489 struct libscols_table
*tb
; /* output */
490 struct list_head procs
; /* list of all processes */
492 unsigned int noheadings
: 1,
497 show_main
: 1, /* print main table */
498 show_summary
: 1, /* print summary/counters */
499 sockets_only
: 1, /* display only SOCKETS */
500 show_xmode
: 1; /* XMODE column is enabled. */
502 struct lsfd_filter
*filter
;
503 struct lsfd_counter
**counters
; /* NULL terminated array. */
506 static void *proc_tree
; /* for tsearch/tfind */
508 static int proc_tree_compare(const void *a
, const void *b
)
510 return ((struct proc
*)a
)->pid
- ((struct proc
*)b
)->pid
;
513 struct proc
*get_proc(pid_t pid
)
515 struct proc
**node
= tfind(&pid
, &proc_tree
, proc_tree_compare
);
521 static int column_name_to_id(const char *name
, size_t namesz
)
525 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
526 const char *cn
= infos
[i
].name
;
528 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
531 warnx(_("unknown column: %s"), name
);
533 return LSFD_FILTER_UNKNOWN_COL_ID
;
536 static int column_name_to_id_cb(const char *name
, void *data
__attribute__((__unused__
)))
538 return column_name_to_id(name
, strlen(name
));
541 static int get_column_id(int num
)
544 assert((size_t) num
< ncolumns
);
545 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
550 static const struct colinfo
*get_column_info(int num
)
552 return &infos
[ get_column_id(num
) ];
555 static struct libscols_column
*add_column(struct libscols_table
*tb
, const struct colinfo
*col
)
557 struct libscols_column
*cl
;
558 int flags
= col
->flags
;
560 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
, flags
);
562 scols_column_set_json_type(cl
, col
->json_type
);
563 if (col
->flags
& SCOLS_FL_WRAP
) {
564 scols_column_set_wrapfunc(cl
,
565 scols_wrapnl_chunksize
,
566 scols_wrapnl_nextchunk
,
568 scols_column_set_safechars(cl
, "\n");
575 static struct libscols_column
*add_column_by_id_cb(struct libscols_table
*tb
, int colid
, void *data
)
577 struct libscols_column
*cl
;
579 if (ncolumns
>= ARRAY_SIZE(columns
))
580 errx(EXIT_FAILURE
, _("too many columns are added via filter expression"));
582 assert(colid
< LSFD_N_COLS
);
584 cl
= add_column(tb
, infos
+ colid
);
586 err(EXIT_FAILURE
, _("failed to allocate output column"));
587 columns
[ncolumns
++] = colid
;
589 if (colid
== COL_TID
) {
590 struct lsfd_control
*ctl
= data
;
597 static int has_mnt_ns(ino_t id
)
601 for (i
= 0; i
< nspaces
; i
++) {
602 if (mnt_namespaces
[i
] == id
)
608 static void add_mnt_ns(ino_t id
)
613 nmax
= (nspaces
+ 16) / 16 * 16;
614 if (nmax
<= nspaces
+ 1) {
616 mnt_namespaces
= xreallocarray(mnt_namespaces
,
617 nmax
, sizeof(ino_t
));
619 mnt_namespaces
[nspaces
++] = id
;
622 static const struct file_class
*stat2class(struct stat
*sb
)
628 switch (sb
->st_mode
& S_IFMT
) {
645 if (is_nsfs_dev(dev
))
646 return &nsfs_file_class
;
648 if (is_mqueue_dev(dev
))
649 return &mqueue_file_class
;
659 static struct file
*new_file(struct proc
*proc
, const struct file_class
*class)
664 file
= xcalloc(1, class->size
);
669 INIT_LIST_HEAD(&file
->files
);
670 list_add_tail(&file
->files
, &proc
->files
);
675 static struct file
*copy_file(struct file
*old
)
677 struct file
*file
= xcalloc(1, old
->class->size
);
679 INIT_LIST_HEAD(&file
->files
);
680 file
->proc
= old
->proc
;
681 list_add_tail(&file
->files
, &old
->proc
->files
);
683 file
->class = old
->class;
684 file
->association
= old
->association
;
685 file
->name
= xstrdup(old
->name
);
686 file
->stat
= old
->stat
;
691 static void file_set_path(struct file
*file
, struct stat
*sb
, const char *name
, int association
)
693 file
->association
= association
;
694 file
->name
= xstrdup(name
);
698 static void file_init_content(struct file
*file
)
700 if (file
->class && file
->class->initialize_content
)
701 file
->class->initialize_content(file
);
704 static void free_file(struct file
*file
)
706 const struct file_class
*class = file
->class;
709 if (class->free_content
)
710 class->free_content(file
);
711 class = class->super
;
717 static struct proc
*new_process(pid_t pid
, struct proc
*leader
)
719 struct proc
*proc
= xcalloc(1, sizeof(*proc
));
722 proc
->leader
= leader
? leader
: proc
;
723 proc
->command
= NULL
;
725 INIT_LIST_HEAD(&proc
->files
);
726 INIT_LIST_HEAD(&proc
->procs
);
727 INIT_LIST_HEAD(&proc
->eventpolls
);
733 static void free_proc(struct proc
*proc
)
735 list_free(&proc
->files
, struct file
, files
, free_file
);
741 static void read_fdinfo(struct file
*file
, FILE *fdinfo
)
745 while (fgets(buf
, sizeof(buf
), fdinfo
)) {
746 const struct file_class
*class;
747 char *val
= strchr(buf
, ':');
751 *val
++ = '\0'; /* terminate key */
753 val
= (char *) skip_space(val
);
754 rtrim_whitespace((unsigned char *) val
);
758 if (class->handle_fdinfo
759 && class->handle_fdinfo(file
, buf
, val
))
761 class = class->super
;
766 static struct file
*collect_file_symlink(struct path_cxt
*pc
,
772 char sym
[PATH_MAX
] = { '\0' };
774 struct file
*f
, *prev
;
776 if (ul_path_readlink(pc
, sym
, sizeof(sym
), name
) < 0)
779 /* The /proc/#/{fd,ns} often contains the same file (e.g. /dev/tty)
780 * more than once. Let's try to reuse the previous file if the real
781 * path is the same to save stat() call.
783 prev
= list_last_entry(&proc
->files
, struct file
, files
);
784 if (prev
&& prev
->name
&& strcmp(prev
->name
, sym
) == 0) {
786 f
->association
= assoc
;
788 const struct file_class
*class;
790 if (ul_path_stat(pc
, &sb
, 0, name
) < 0)
793 class = stat2class(&sb
);
795 /* A nsfs file is not a socket but the nsfs file can
796 * be used as a entry point to collect information from
797 * other network namespaces. Besed on the information,
798 * various columns of sockets can be filled.
800 && (class != &sock_class
) && (class != &nsfs_file_class
))
802 f
= new_file(proc
, class);
803 file_set_path(f
, &sb
, sym
, assoc
);
806 file_init_content(f
);
808 if (is_association(f
, NS_MNT
))
809 proc
->ns_mnt
= f
->stat
.st_ino
;
810 else if (is_association(f
, NS_NET
))
811 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
813 else if (assoc
>= 0) {
814 /* file-descriptor based association */
817 if (ul_path_stat(pc
, &sb
, AT_SYMLINK_NOFOLLOW
, name
) == 0)
818 f
->mode
= sb
.st_mode
;
820 if (is_nsfs_dev(f
->stat
.st_dev
))
821 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
823 fdinfo
= ul_path_fopenf(pc
, "r", "fdinfo/%d", assoc
);
825 read_fdinfo(f
, fdinfo
);
833 /* read symlinks from /proc/#/fd
835 static void collect_fd_files(struct path_cxt
*pc
, struct proc
*proc
,
839 struct dirent
*d
= NULL
;
840 char path
[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX
))];
842 while (ul_path_next_dirent(pc
, &sub
, "fd", &d
) == 0) {
845 if (ul_strtou64(d
->d_name
, &num
, 10) != 0) /* only numbers */
848 snprintf(path
, sizeof(path
), "fd/%ju", (uintmax_t) num
);
849 collect_file_symlink(pc
, proc
, path
, num
, sockets_only
);
853 static void parse_maps_line(struct path_cxt
*pc
, char *buf
, struct proc
*proc
)
855 uint64_t start
, end
, offset
, ino
;
856 unsigned long major
, minor
;
857 enum association assoc
= ASSOC_MEM
;
859 struct file
*f
, *prev
;
860 char *path
, modestr
[5];
863 /* read rest of the map */
864 if (sscanf(buf
, "%"SCNx64
/* start */
867 " %"SCNx64
/* offset */
868 " %lx:%lx" /* maj:min */
869 " %"SCNu64
, /* inode */
871 &start
, &end
, modestr
, &offset
,
872 &major
, &minor
, &ino
) != 7)
875 /* Skip private anonymous mappings. */
876 if (major
== 0 && minor
== 0 && ino
== 0)
879 devno
= makedev(major
, minor
);
881 if (modestr
[3] == 's')
884 /* The map usually contains the same file more than once, try to reuse
885 * the previous file (if devno and ino are the same) to save stat() call.
887 prev
= list_last_entry(&proc
->files
, struct file
, files
);
889 if (prev
&& prev
->stat
.st_dev
== devno
&& prev
->stat
.st_ino
== ino
) {
891 f
->association
= -assoc
;
892 } else if ((path
= strchr(buf
, '/'))) {
893 rtrim_whitespace((unsigned char *) path
);
894 if (stat(path
, &sb
) < 0)
895 /* If a file is mapped but deleted from the file system,
896 * "stat by the file name" may not work. In that case,
899 f
= new_file(proc
, stat2class(&sb
));
900 file_set_path(f
, &sb
, path
, -assoc
);
902 /* As used in tcpdump, AF_PACKET socket can be mmap'ed. */
903 char map_file
[sizeof("map_files/0000000000000000-ffffffffffffffff")];
904 char sym
[PATH_MAX
] = { '\0' };
907 snprintf(map_file
, sizeof(map_file
), "map_files/%"PRIx64
"-%"PRIx64
, start
, end
);
908 if (ul_path_stat(pc
, &sb
, 0, map_file
) < 0)
910 if (ul_path_readlink(pc
, sym
, sizeof(sym
), map_file
) < 0)
912 f
= new_file(proc
, stat2class(&sb
));
913 file_set_path(f
, &sb
, sym
, -assoc
);
916 if (modestr
[0] == 'r')
918 if (modestr
[1] == 'w')
920 if (modestr
[2] == 'x')
923 f
->map_start
= start
;
927 file_init_content(f
);
930 static void collect_mem_files(struct path_cxt
*pc
, struct proc
*proc
)
935 fp
= ul_path_fopen(pc
, "r", "maps");
939 while (fgets(buf
, sizeof(buf
), fp
))
940 parse_maps_line(pc
, buf
, proc
);
945 static void collect_outofbox_files(struct path_cxt
*pc
,
947 enum association assocs
[],
954 for (i
= 0; i
< count
; i
++)
955 collect_file_symlink(pc
, proc
, names
[assocs
[i
]], assocs
[i
] * -1,
959 static void collect_execve_file(struct path_cxt
*pc
, struct proc
*proc
,
962 enum association assocs
[] = { ASSOC_EXE
};
963 const char *names
[] = {
966 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
970 static void collect_fs_files(struct path_cxt
*pc
, struct proc
*proc
,
973 enum association assocs
[] = { ASSOC_EXE
, ASSOC_CWD
, ASSOC_ROOT
};
974 const char *names
[] = {
976 [ASSOC_ROOT
] = "root",
978 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
982 static void collect_namespace_files(struct path_cxt
*pc
, struct proc
*proc
)
984 enum association assocs
[] = {
996 const char *names
[] = {
997 [ASSOC_NS_CGROUP
] = "ns/cgroup",
998 [ASSOC_NS_IPC
] = "ns/ipc",
999 [ASSOC_NS_MNT
] = "ns/mnt",
1000 [ASSOC_NS_NET
] = "ns/net",
1001 [ASSOC_NS_PID
] = "ns/pid",
1002 [ASSOC_NS_PID4C
] = "ns/pid_for_children",
1003 [ASSOC_NS_TIME
] = "ns/time",
1004 [ASSOC_NS_TIME4C
] = "ns/time_for_children",
1005 [ASSOC_NS_USER
] = "ns/user",
1006 [ASSOC_NS_UTS
] = "ns/uts",
1008 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
1009 /* Namespace information is alwasys needed. */
1013 static struct nodev
*new_nodev(unsigned long minor
, const char *filesystem
)
1015 struct nodev
*nodev
= xcalloc(1, sizeof(*nodev
));
1017 INIT_LIST_HEAD(&nodev
->nodevs
);
1018 nodev
->minor
= minor
;
1019 nodev
->filesystem
= xstrdup(filesystem
);
1024 static void free_nodev(struct nodev
*nodev
)
1026 free(nodev
->filesystem
);
1030 void add_nodev(unsigned long minor
, const char *filesystem
)
1032 struct nodev
*nodev
= new_nodev(minor
, filesystem
);
1033 unsigned long slot
= nodev
->minor
% NODEV_TABLE_SIZE
;
1035 list_add_tail(&nodev
->nodevs
, &nodev_table
.tables
[slot
]);
1038 static void initialize_nodevs(void)
1042 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1043 INIT_LIST_HEAD(&nodev_table
.tables
[i
]);
1046 static void finalize_nodevs(void)
1050 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1051 list_free(&nodev_table
.tables
[i
], struct nodev
, nodevs
, free_nodev
);
1053 free(mnt_namespaces
);
1056 const char *get_nodev_filesystem(unsigned long minor
)
1058 struct list_head
*n
;
1059 int slot
= minor
% NODEV_TABLE_SIZE
;
1061 list_for_each (n
, &nodev_table
.tables
[slot
]) {
1062 struct nodev
*nodev
= list_entry(n
, struct nodev
, nodevs
);
1063 if (nodev
->minor
== minor
)
1064 return nodev
->filesystem
;
1069 static void add_nodevs(FILE *mnt
)
1071 /* This can be very long. A line in mountinfo can have more than 3
1073 char line
[PATH_MAX
* 3 + 256];
1075 while (fgets(line
, sizeof(line
), mnt
)) {
1076 unsigned long major
, minor
;
1077 char filesystem
[256];
1079 /* 23 61 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel */
1080 if(sscanf(line
, "%*d %*d %lu:%lu %*s %*s %*s %*[^-] - %s %*[^\n]",
1081 &major
, &minor
, filesystem
) != 3)
1082 /* 1600 1458 0:55 / / rw,nodev,relatime - overlay overlay rw,context="s... */
1083 if (sscanf(line
, "%*d %*d %lu:%lu %*s %*s %*s - %s %*[^\n]",
1084 &major
, &minor
, filesystem
) != 3)
1089 if (get_nodev_filesystem(minor
))
1092 add_nodev(minor
, filesystem
);
1096 static void initialize_ipc_table(void)
1098 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1099 INIT_LIST_HEAD(ipc_table
.tables
+ i
);
1102 static void free_ipc(struct ipc
*ipc
)
1104 if (ipc
->class->free
)
1105 ipc
->class->free(ipc
);
1109 static void finalize_ipc_table(void)
1111 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1112 list_free(&ipc_table
.tables
[i
], struct ipc
, ipcs
, free_ipc
);
1115 struct ipc
*new_ipc(const struct ipc_class
*class)
1117 struct ipc
*ipc
= xcalloc(1, class->size
);
1119 INIT_LIST_HEAD(&ipc
->endpoints
);
1120 INIT_LIST_HEAD(&ipc
->ipcs
);
1124 struct ipc
*get_ipc(struct file
*file
)
1127 struct list_head
*e
;
1128 const struct ipc_class
*ipc_class
;
1130 if (!file
->class->get_ipc_class
)
1133 ipc_class
= file
->class->get_ipc_class(file
);
1137 slot
= ipc_class
->get_hash(file
) % IPC_TABLE_SIZE
;
1138 list_for_each (e
, &ipc_table
.tables
[slot
]) {
1139 struct ipc
*ipc
= list_entry(e
, struct ipc
, ipcs
);
1140 if (ipc
->class != ipc_class
)
1142 if (ipc_class
->is_suitable_ipc(ipc
, file
))
1148 void add_ipc(struct ipc
*ipc
, unsigned int hash
)
1150 int slot
= hash
% IPC_TABLE_SIZE
;
1151 list_add(&ipc
->ipcs
, &ipc_table
.tables
[slot
]);
1154 void init_endpoint(struct ipc_endpoint
*endpoint
)
1156 INIT_LIST_HEAD(&endpoint
->endpoints
);
1159 void add_endpoint(struct ipc_endpoint
*endpoint
, struct ipc
*ipc
)
1161 endpoint
->ipc
= ipc
;
1162 list_add(&endpoint
->endpoints
, &ipc
->endpoints
);
1165 static void fill_column(struct proc
*proc
,
1167 struct libscols_line
*ln
,
1169 size_t column_index
)
1171 const struct file_class
*class = file
->class;
1174 if (class->fill_column
1175 && class->fill_column(proc
, file
, ln
,
1176 column_id
, column_index
))
1178 class = class->super
;
1182 static void convert_file(struct proc
*proc
,
1184 struct libscols_line
*ln
)
1189 for (i
= 0; i
< ncolumns
; i
++)
1190 fill_column(proc
, file
, ln
, get_column_id(i
), i
);
1193 static void convert(struct list_head
*procs
, struct lsfd_control
*ctl
)
1195 struct list_head
*p
;
1197 list_for_each (p
, procs
) {
1198 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1199 struct list_head
*f
;
1201 list_for_each (f
, &proc
->files
) {
1202 struct file
*file
= list_entry(f
, struct file
, files
);
1203 struct libscols_line
*ln
= scols_table_new_line(ctl
->tb
, NULL
);
1204 struct lsfd_counter
**counter
= NULL
;
1207 err(EXIT_FAILURE
, _("failed to allocate output line"));
1209 convert_file(proc
, file
, ln
);
1211 if (!lsfd_filter_apply(ctl
->filter
, ln
)) {
1212 scols_table_remove_line(ctl
->tb
, ln
);
1219 for (counter
= ctl
->counters
; *counter
; counter
++)
1220 lsfd_counter_accumulate(*counter
, ln
);
1225 static void delete(struct list_head
*procs
, struct lsfd_control
*ctl
)
1227 struct list_head
*p
;
1229 list_for_each (p
, procs
) {
1230 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1231 tdelete(proc
, &proc_tree
, proc_tree_compare
);
1233 list_free(procs
, struct proc
, procs
, free_proc
);
1235 scols_unref_table(ctl
->tb
);
1236 lsfd_filter_free(ctl
->filter
);
1237 if (ctl
->counters
) {
1238 struct lsfd_counter
**counter
;
1239 for (counter
= ctl
->counters
; *counter
; counter
++)
1240 lsfd_counter_free(*counter
);
1241 free(ctl
->counters
);
1245 static void emit(struct lsfd_control
*ctl
)
1247 scols_print_table(ctl
->tb
);
1251 static void initialize_class(const struct file_class
*class)
1253 if (class->initialize_class
)
1254 class->initialize_class();
1257 static void initialize_classes(void)
1259 initialize_class(&file_class
);
1260 initialize_class(&cdev_class
);
1261 initialize_class(&bdev_class
);
1262 initialize_class(&sock_class
);
1263 initialize_class(&unkn_class
);
1266 static void finalize_class(const struct file_class
*class)
1268 if (class->finalize_class
)
1269 class->finalize_class();
1272 static void finalize_classes(void)
1274 finalize_class(&file_class
);
1275 finalize_class(&cdev_class
);
1276 finalize_class(&bdev_class
);
1277 finalize_class(&sock_class
);
1278 finalize_class(&unkn_class
);
1281 static struct devdrv
*new_devdrv(unsigned long major
, const char *name
)
1283 struct devdrv
*devdrv
= xcalloc(1, sizeof(*devdrv
));
1285 INIT_LIST_HEAD(&devdrv
->devdrvs
);
1287 devdrv
->major
= major
;
1288 devdrv
->name
= xstrdup(name
);
1293 static void free_devdrv(struct devdrv
*devdrv
)
1299 #define READ_DEVICES_LINE_LEN 256
1300 static struct devdrv
*read_devdrv(const char *line
)
1302 unsigned long major
;
1303 char name
[READ_DEVICES_LINE_LEN
];
1305 if (sscanf(line
, "%lu %s", &major
, name
) != 2)
1308 return new_devdrv(major
, name
);
1311 static void read_devices(struct list_head
*chrdrvs_list
,
1312 struct list_head
*blkdrvs_list
, FILE *devices_fp
)
1314 char line
[READ_DEVICES_LINE_LEN
];
1316 /* Skip to the line "Character devices:". */
1317 while (fgets(line
, sizeof(line
), devices_fp
)) {
1323 while (fgets(line
, sizeof(line
), devices_fp
)) {
1324 /* Find the blank line before "Block devices:" line. */
1325 if (line
[0] == '\n')
1328 /* Read the character device drivers */
1329 struct devdrv
*devdrv
= read_devdrv(line
);
1331 list_add_tail(&devdrv
->devdrvs
, chrdrvs_list
);
1334 /* Skip to the line "Block devices:". */
1335 while (fgets(line
, sizeof(line
), devices_fp
)) {
1341 /* Read the block device drivers */
1342 while (fgets(line
, sizeof(line
), devices_fp
)) {
1343 struct devdrv
*devdrv
= read_devdrv(line
);
1345 list_add_tail(&devdrv
->devdrvs
, blkdrvs_list
);
1349 static void initialize_devdrvs(void)
1353 INIT_LIST_HEAD(&chrdrvs
);
1354 INIT_LIST_HEAD(&blkdrvs
);
1356 devices_fp
= fopen("/proc/devices", "r");
1358 read_devices(&chrdrvs
, &blkdrvs
, devices_fp
);
1363 static void finalize_devdrvs(void)
1365 list_free(&blkdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1366 list_free(&chrdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1369 static const char *get_devdrv(struct list_head
*devdrvs_list
, unsigned long major
)
1371 struct list_head
*c
;
1372 list_for_each(c
, devdrvs_list
) {
1373 struct devdrv
*devdrv
= list_entry(c
, struct devdrv
, devdrvs
);
1374 if (devdrv
->major
== major
)
1375 return devdrv
->name
;
1380 const char *get_chrdrv(unsigned long major
)
1382 return get_devdrv(&chrdrvs
, major
);
1385 const char *get_blkdrv(unsigned long major
)
1387 return get_devdrv(&blkdrvs
, major
);
1390 struct name_manager
*new_name_manager(void)
1392 struct name_manager
*nm
= xcalloc(1, sizeof(struct name_manager
));
1394 nm
->cache
= new_idcache();
1396 err(EXIT_FAILURE
, _("failed to allocate an idcache"));
1398 nm
->next_id
= 1; /* 0 is never issued as id. */
1402 void free_name_manager(struct name_manager
*nm
)
1404 free_idcache(nm
->cache
);
1408 const char *get_name(struct name_manager
*nm
, unsigned long id
)
1412 e
= get_id(nm
->cache
, id
);
1414 return e
? e
->name
: NULL
;
1417 unsigned long add_name(struct name_manager
*nm
, const char *name
)
1419 struct identry
*e
= NULL
, *tmp
;
1421 for (tmp
= nm
->cache
->ent
; tmp
; tmp
= tmp
->next
) {
1422 if (strcmp(tmp
->name
, name
) == 0) {
1431 e
= xmalloc(sizeof(struct identry
));
1432 e
->name
= xstrdup(name
);
1433 e
->id
= nm
->next_id
++;
1434 e
->next
= nm
->cache
->ent
;
1440 static void walk_threads(struct lsfd_control
*ctl
, struct path_cxt
*pc
,
1441 pid_t pid
, struct proc
*proc
,
1442 void (*cb
)(struct lsfd_control
*, struct path_cxt
*,
1443 pid_t
, struct proc
*))
1448 while (procfs_process_next_tid(pc
, &sub
, &tid
) == 0) {
1451 (*cb
)(ctl
, pc
, tid
, proc
);
1455 static int pollfdcmp(const void *a
, const void *b
)
1457 const struct pollfd
*apfd
= a
, *bpfd
= b
;
1459 return apfd
->fd
- bpfd
->fd
;
1462 static void mark_poll_fds_as_multiplexed(char *buf
,
1463 pid_t pid
, struct proc
*proc
)
1469 struct iovec remote
;
1472 struct list_head
*f
;
1474 if (sscanf(buf
, "%lx %lx", &fds
, &nfds
) != 2)
1480 local
.iov_len
= sizeof(struct pollfd
) * nfds
;
1481 local
.iov_base
= xmalloc(local
.iov_len
);
1482 remote
.iov_len
= local
.iov_len
;
1483 remote
.iov_base
= (void *)fds
;
1485 n
= process_vm_readv(pid
, &local
, 1, &remote
, 1, 0);
1486 if (n
< 0 || ((size_t)n
) != local
.iov_len
)
1489 qsort(local
.iov_base
, nfds
, sizeof(struct pollfd
), pollfdcmp
);
1491 list_for_each (f
, &proc
->files
) {
1492 struct file
*file
= list_entry(f
, struct file
, files
);
1493 if (is_opened_file(file
) && !file
->multiplexed
) {
1494 int fd
= file
->association
;
1495 if (bsearch(&(struct pollfd
){.fd
= fd
,}, local
.iov_base
,
1496 nfds
, sizeof(struct pollfd
), pollfdcmp
))
1497 file
->multiplexed
= 1;
1502 free (local
.iov_base
);
1505 static void mark_select_fds_as_multiplexed(char *buf
,
1506 pid_t pid
, struct proc
*proc
)
1511 struct iovec local
[3];
1512 fd_set local_set
[3];
1513 struct iovec remote
[3];
1515 ssize_t expected_n
= 0;
1517 struct list_head
*f
;
1519 if (sscanf(buf
, "%lx %lx %lx %lx", &nfds
, fds
+ 0, fds
+ 1, fds
+ 2) != 4)
1525 for (int i
= 0; i
< 3; i
++) {
1526 /* If the remote address for the fd_set is 0x0, no set is tehre. */
1527 remote
[i
].iov_len
= local
[i
].iov_len
= fds
[i
]? sizeof(local_set
[i
]): 0;
1528 expected_n
+= (ssize_t
)local
[i
].iov_len
;
1529 local
[i
].iov_base
= local_set
+ i
;
1530 remote
[i
].iov_base
= (void *)(fds
[i
]);
1533 n
= process_vm_readv(pid
, local
, 3, remote
, 3, 0);
1534 if (n
< 0 || n
!= expected_n
)
1537 list_for_each (f
, &proc
->files
) {
1538 struct file
*file
= list_entry(f
, struct file
, files
);
1539 if (is_opened_file(file
) && !file
->multiplexed
) {
1540 int fd
= file
->association
;
1543 if ((fds
[0] && FD_ISSET(fd
, (fd_set
*)local
[0].iov_base
))
1544 || (fds
[1] && FD_ISSET(fd
, (fd_set
*)local
[1].iov_base
))
1545 || (fds
[2] && FD_ISSET(fd
, (fd_set
*)local
[2].iov_base
)))
1546 file
->multiplexed
= 1;
1551 static void parse_proc_syscall(struct lsfd_control
*ctl
__attribute__((__unused__
)),
1552 struct path_cxt
*pc
, pid_t pid
, struct proc
*proc
)
1558 if (procfs_process_get_syscall(pc
, buf
, sizeof(buf
)) <= 0)
1562 scn
= strtol(buf
, &ptr
, 10);
1571 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1576 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1579 #ifdef SYS_ppoll_time64
1580 case SYS_ppoll_time64
:
1581 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1587 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1592 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1595 #ifdef SYS_pselect6_time64
1596 case SYS_pselect6_time64
:
1597 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1603 static void read_process(struct lsfd_control
*ctl
, struct path_cxt
*pc
,
1604 pid_t pid
, struct proc
*leader
)
1609 if (procfs_process_init_path(pc
, pid
) != 0)
1612 proc
= new_process(pid
, leader
);
1613 proc
->command
= procfs_process_get_cmdname(pc
, buf
, sizeof(buf
)) > 0 ?
1614 xstrdup(buf
) : xstrdup(_("(unknown)"));
1615 procfs_process_get_uid(pc
, &proc
->uid
);
1617 if (procfs_process_get_stat(pc
, buf
, sizeof(buf
)) > 0) {
1622 /* See proc(5) about the column in the line. */
1623 xstrappend(&pat
, "%*d (");
1624 for (p
= proc
->command
; *p
!= '\0'; p
++) {
1626 xstrappend(&pat
, "%%");
1630 xstrappend(&pat
, ") %*c %*d %*d %*d %*d %*d %u %*[^\n]");
1631 if (sscanf(buf
, pat
, &flags
) == 1)
1632 proc
->kthread
= !!(flags
& PF_KTHREAD
);
1636 collect_execve_file(pc
, proc
, ctl
->sockets_only
);
1638 if (proc
->pid
== proc
->leader
->pid
1639 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_FS
, 0, 0) != 0)
1640 collect_fs_files(pc
, proc
, ctl
->sockets_only
);
1642 if (proc
->ns_mnt
== 0 || !has_mnt_ns(proc
->ns_mnt
)) {
1643 FILE *mnt
= ul_path_fopen(pc
, "r", "mountinfo");
1647 add_mnt_ns(proc
->ns_mnt
);
1652 collect_namespace_files(pc
, proc
);
1654 /* If kcmp is not available,
1655 * there is no way to know whether threads share resources.
1656 * In such cases, we must pay the costs: call collect_mem_files()
1657 * and collect_fd_files().
1659 if ((!ctl
->sockets_only
)
1660 && (proc
->pid
== proc
->leader
->pid
1661 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_VM
, 0, 0) != 0))
1662 collect_mem_files(pc
, proc
);
1664 if (proc
->pid
== proc
->leader
->pid
1665 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_FILES
, 0, 0) != 0)
1666 collect_fd_files(pc
, proc
, ctl
->sockets_only
);
1668 list_add_tail(&proc
->procs
, &ctl
->procs
);
1669 if (tsearch(proc
, &proc_tree
, proc_tree_compare
) == NULL
)
1670 errx(EXIT_FAILURE
, _("failed to allocate memory"));
1672 if (ctl
->show_xmode
)
1673 parse_proc_syscall(ctl
, pc
, pid
, proc
);
1675 /* The tasks collecting overwrites @pc by /proc/<task-pid>/. Keep it as
1676 * the last path based operation in read_process()
1678 if (ctl
->threads
&& leader
== NULL
)
1679 walk_threads(ctl
, pc
, pid
, proc
, read_process
);
1680 else if (ctl
->show_xmode
)
1681 walk_threads(ctl
, pc
, pid
, proc
, parse_proc_syscall
);
1683 /* Let's be careful with number of open files */
1684 ul_path_close_dirfd(pc
);
1687 static void parse_pids(const char *str
, pid_t
**pids
, int *count
)
1696 v
= strtol(str
, &next
, 10);
1698 err(EXIT_FAILURE
, _("unexpected value for pid specification: %s"), str
);
1700 errx(EXIT_FAILURE
, _("garbage at the end of pid specification: %s"), str
);
1702 errx(EXIT_FAILURE
, _("out of range value for pid specification: %ld"), v
);
1705 *pids
= xreallocarray(*pids
, *count
, sizeof(**pids
));
1706 (*pids
)[*count
- 1] = (pid_t
)v
;
1708 while (next
&& *next
!= '\0'
1709 && (isspace((unsigned char)*next
) || *next
== ','))
1712 parse_pids(next
, pids
, count
);
1715 static int pidcmp(const void *a
, const void *b
)
1717 pid_t pa
= *(pid_t
*)a
;
1718 pid_t pb
= *(pid_t
*)b
;
1728 static void sort_pids(pid_t pids
[], const int count
)
1730 qsort(pids
, count
, sizeof(pid_t
), pidcmp
);
1733 static bool member_pids(const pid_t pid
, const pid_t pids
[], const int count
)
1735 return bsearch(&pid
, pids
, count
, sizeof(pid_t
), pidcmp
)? true: false;
1738 static void collect_processes(struct lsfd_control
*ctl
, const pid_t pids
[], int n_pids
)
1742 struct path_cxt
*pc
= NULL
;
1744 pc
= ul_new_path(NULL
);
1746 err(EXIT_FAILURE
, _("failed to alloc procfs handler"));
1748 dir
= opendir(_PATH_PROC
);
1750 err(EXIT_FAILURE
, _("failed to open /proc"));
1752 while ((d
= readdir(dir
))) {
1755 if (procfs_dirent_get_pid(d
, &pid
) != 0)
1757 if (n_pids
== 0 || member_pids(pid
, pids
, n_pids
))
1758 read_process(ctl
, pc
, pid
, 0);
1765 static void __attribute__((__noreturn__
)) list_colunms(FILE *out
)
1767 fprintf(out
, USAGE_COLUMNS
);
1768 for (size_t i
= 0; i
< ARRAY_SIZE(infos
); i
++)
1769 fprintf(out
, " %20s %-10s%s\n", infos
[i
].name
,
1770 infos
[i
].json_type
== SCOLS_JSON_STRING
? "<string>":
1771 infos
[i
].json_type
== SCOLS_JSON_ARRAY_STRING
? "<string>":
1772 infos
[i
].json_type
== SCOLS_JSON_ARRAY_NUMBER
? "<string>":
1773 infos
[i
].json_type
== SCOLS_JSON_NUMBER
? "<number>":
1779 static void print_columns(FILE *out
, const char *prefix
, const int cols
[], size_t n_cols
)
1781 fprintf(out
, "%15s: ", prefix
);
1782 for (size_t i
= 0; i
< n_cols
; i
++) {
1785 fputs(infos
[cols
[i
]].name
, out
);
1790 static void __attribute__((__noreturn__
)) usage(void)
1794 fputs(USAGE_HEADER
, out
);
1795 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
1797 fputs(USAGE_OPTIONS
, out
);
1798 fputs(_(" -l, --threads list in threads level\n"), out
);
1799 fputs(_(" -J, --json use JSON output format\n"), out
);
1800 fputs(_(" -n, --noheadings don't print headings\n"), out
);
1801 fputs(_(" -o, --output <list> output columns\n"), out
);
1802 fputs(_(" -r, --raw use raw output format\n"), out
);
1803 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out
);
1804 fputs(_(" -p, --pid <pid(s)> collect information only specified processes\n"), out
);
1805 fputs(_(" -i[4|6], --inet[=4|=6] list only IPv4 and/or IPv6 sockets\n"), out
);
1806 fputs(_(" -Q, --filter <expr> apply display filter\n"), out
);
1807 fputs(_(" --debug-filter dump the internal data structure of filter and exit\n"), out
);
1808 fputs(_(" -C, --counter <name>:<expr> define custom counter for --summary output\n"), out
);
1809 fputs(_(" --dump-counters dump counter definitions\n"), out
);
1810 fputs(_(" --summary[=<when>] print summary information (only, append, or never)\n"), out
);
1812 fputs(USAGE_SEPARATOR
, out
);
1813 fputs(_(" -H, --list-columns list the available columns\n"), out
);
1814 fprintf(out
, USAGE_HELP_OPTIONS(30));
1816 fputs(USAGE_DEFAULT_COLUMNS
, out
);
1817 print_columns(out
, _("Default"), default_columns
, ARRAY_SIZE(default_columns
));
1818 print_columns(out
, _("With --threads"), default_threads_columns
, ARRAY_SIZE(default_threads_columns
));
1820 fprintf(out
, USAGE_MAN_TAIL("lsfd(1)"));
1825 static void append_filter_expr(char **a
, const char *b
, bool and)
1839 xstrappend(a
, "and(");
1841 xstrappend(a
, "or(");
1846 static struct lsfd_filter
*new_filter(const char *expr
, bool debug
, const char *err_prefix
, struct lsfd_control
*ctl
)
1848 struct lsfd_filter
*filter
;
1851 filter
= lsfd_filter_new(expr
, ctl
->tb
,
1853 column_name_to_id_cb
,
1854 add_column_by_id_cb
, ctl
);
1855 errmsg
= lsfd_filter_get_errmsg(filter
);
1857 errx(EXIT_FAILURE
, "%s%s", err_prefix
, errmsg
);
1859 lsfd_filter_dump(filter
, stdout
);
1866 static struct counter_spec
*new_counter_spec(const char *spec_str
)
1869 struct counter_spec
*spec
;
1871 if (spec_str
[0] == '\0')
1873 _("too short counter specification: -C/--counter %s"),
1875 if (spec_str
[0] == ':')
1877 _("no name for counter: -C/--counter %s"),
1880 sep
= strchr(spec_str
, ':');
1883 _("no name for counter: -C/--counter %s"),
1887 _("empty counter expression given: -C/--counter %s"),
1890 /* Split the spec_str in to name and expr. */
1893 if (strchr(spec_str
, '{'))
1895 _("don't use `{' in the name of a counter: %s"),
1898 spec
= xmalloc(sizeof(struct counter_spec
));
1899 INIT_LIST_HEAD(&spec
->specs
);
1900 spec
->name
= spec_str
;
1901 spec
->expr
= sep
+ 1;
1906 static void free_counter_spec(struct counter_spec
*counter_spec
)
1911 static struct lsfd_counter
*new_counter(const struct counter_spec
*spec
, struct lsfd_control
*ctl
)
1913 struct lsfd_filter
*filter
;
1915 filter
= new_filter(spec
->expr
, false,
1916 _("failed in making filter for a counter: "),
1918 return lsfd_counter_new(spec
->name
, filter
);
1921 static struct lsfd_counter
**new_counters(struct list_head
*specs
, struct lsfd_control
*ctl
)
1923 struct lsfd_counter
**counters
;
1924 size_t len
= list_count_entries(specs
);
1926 struct list_head
*s
;
1928 counters
= xcalloc(len
+ 1, sizeof(struct lsfd_counter
*));
1929 list_for_each(s
, specs
) {
1930 struct counter_spec
*spec
= list_entry(s
, struct counter_spec
, specs
);
1931 counters
[i
++] = new_counter(spec
, ctl
);
1933 assert(counters
[len
] == NULL
);
1938 static struct lsfd_counter
**new_default_counters(struct lsfd_control
*ctl
)
1940 struct lsfd_counter
**counters
;
1941 size_t len
= ARRAY_SIZE(default_counter_specs
);
1944 counters
= xcalloc(len
+ 1, sizeof(struct lsfd_counter
*));
1945 for (i
= 0; i
< len
; i
++) {
1946 const struct counter_spec
*spec
= default_counter_specs
+ i
;
1947 counters
[i
] = new_counter(spec
, ctl
);
1949 assert(counters
[len
] == NULL
);
1954 static void dump_default_counter_specs(void)
1956 size_t len
= ARRAY_SIZE(default_counter_specs
);
1959 puts("default counter specs:");
1960 for (i
= 0; i
< len
; i
++) {
1961 const struct counter_spec
*spec
= default_counter_specs
+ i
;
1962 printf("\t%s:%s\n", spec
->name
, spec
->expr
);
1966 static void dump_counter_specs(struct list_head
*specs
)
1968 struct list_head
*s
;
1970 puts("custom counter specs:");
1971 list_for_each(s
, specs
) {
1972 struct counter_spec
*spec
= list_entry(s
, struct counter_spec
, specs
);
1973 printf("\t%s:%s\n", spec
->name
, spec
->expr
);
1977 static struct libscols_table
*new_summary_table(struct lsfd_control
*ctl
)
1979 struct libscols_table
*tb
= scols_new_table();
1981 struct libscols_column
*name_cl
, *value_cl
;
1984 err(EXIT_FAILURE
, _("failed to allocate summary table"));
1986 scols_table_enable_noheadings(tb
, ctl
->noheadings
);
1987 scols_table_enable_raw(tb
, ctl
->raw
);
1988 scols_table_enable_json(tb
, ctl
->json
);
1991 scols_table_set_name(tb
, "lsfd-summary");
1994 value_cl
= scols_table_new_column(tb
, _("VALUE"), 0, SCOLS_FL_RIGHT
);
1996 err(EXIT_FAILURE
, _("failed to allocate summary column"));
1998 scols_column_set_json_type(value_cl
, SCOLS_JSON_NUMBER
);
2000 name_cl
= scols_table_new_column(tb
, _("COUNTER"), 0, 0);
2002 err(EXIT_FAILURE
, _("failed to allocate summary column"));
2004 scols_column_set_json_type(name_cl
, SCOLS_JSON_STRING
);
2009 static void fill_summary_line(struct libscols_line
*ln
, struct lsfd_counter
*counter
)
2013 xasprintf(&str
, "%llu", (unsigned long long)lsfd_counter_value(counter
));
2015 err(EXIT_FAILURE
, _("failed to add summary data"));
2016 if (scols_line_refer_data(ln
, 0, str
))
2017 err(EXIT_FAILURE
, _("failed to add summary data"));
2019 if (scols_line_set_data(ln
, 1, lsfd_counter_name(counter
)))
2020 err(EXIT_FAILURE
, _("failed to add summary data"));
2023 static void emit_summary(struct lsfd_control
*ctl
, struct lsfd_counter
**counter
)
2025 struct libscols_table
*tb
= new_summary_table(ctl
);
2027 for (; *counter
; counter
++) {
2028 struct libscols_line
*ln
= scols_table_new_line(tb
, NULL
);
2029 fill_summary_line(ln
, *counter
);
2031 scols_print_table(tb
);
2033 scols_unref_table(tb
);
2036 static void attach_xinfos(struct list_head
*procs
)
2038 struct list_head
*p
;
2040 list_for_each (p
, procs
) {
2041 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
2042 struct list_head
*f
;
2044 list_for_each (f
, &proc
->files
) {
2045 struct file
*file
= list_entry(f
, struct file
, files
);
2046 if (file
->class->attach_xinfo
)
2047 file
->class->attach_xinfo(file
);
2052 static void set_multiplexed_flags(struct list_head
*procs
)
2054 struct list_head
*p
;
2055 list_for_each (p
, procs
) {
2056 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
2057 struct list_head
*f
;
2058 list_for_each (f
, &proc
->files
) {
2059 struct file
*file
= list_entry(f
, struct file
, files
);
2060 if (is_opened_file(file
) && !file
->multiplexed
) {
2061 int fd
= file
->association
;
2062 if (is_multiplexed_by_eventpoll(fd
, &proc
->eventpolls
))
2063 file
->multiplexed
= 1;
2069 /* Filter expressions for implementing -i option.
2071 * To list up the protocol names, use the following command line
2074 * find . -type f -exec grep -A 1 --color=auto -nH --null -e 'struct proto .*{' \{\} +
2077 #define INET_SUBEXP_BEGIN "(SOCK.PROTONAME =~ \"^("
2078 #define INET4_REG "TCP|UDP|RAW|PING|UDP-Lite|SCTP|DCCP|L2TP/IP|SMC"
2079 #define INET6_REG "TCPv6|UDPv6|RAWv6|PINGv6|UDPLITEv6|SCTPv6|DCCPv6|L2TP/IPv6|SMC6"
2080 #define INET_SUBEXP_END ")$\")"
2082 static const char *inet4_subexpr
= INET_SUBEXP_BEGIN
2085 static const char *inet6_subexpr
= INET_SUBEXP_BEGIN
2088 static const char *inet46_subexpr
= INET_SUBEXP_BEGIN
2089 INET4_REG
"|" INET6_REG
2092 int main(int argc
, char *argv
[])
2096 char *outarg
= NULL
;
2097 char *filter_expr
= NULL
;
2098 bool debug_filter
= false;
2099 bool dump_counters
= false;
2102 struct list_head counter_specs
;
2104 struct lsfd_control ctl
= {
2108 INIT_LIST_HEAD(&counter_specs
);
2111 OPT_DEBUG_FILTER
= CHAR_MAX
+ 1,
2115 static const struct option longopts
[] = {
2116 { "noheadings", no_argument
, NULL
, 'n' },
2117 { "output", required_argument
, NULL
, 'o' },
2118 { "version", no_argument
, NULL
, 'V' },
2119 { "help", no_argument
, NULL
, 'h' },
2120 { "json", no_argument
, NULL
, 'J' },
2121 { "raw", no_argument
, NULL
, 'r' },
2122 { "threads", no_argument
, NULL
, 'l' },
2123 { "notruncate", no_argument
, NULL
, 'u' },
2124 { "pid", required_argument
, NULL
, 'p' },
2125 { "inet", optional_argument
, NULL
, 'i' },
2126 { "filter", required_argument
, NULL
, 'Q' },
2127 { "debug-filter",no_argument
, NULL
, OPT_DEBUG_FILTER
},
2128 { "summary", optional_argument
, NULL
, OPT_SUMMARY
},
2129 { "counter", required_argument
, NULL
, 'C' },
2130 { "dump-counters",no_argument
, NULL
, OPT_DUMP_COUNTERS
},
2131 { "list-columns",no_argument
, NULL
, 'H' },
2132 { NULL
, 0, NULL
, 0 },
2135 setlocale(LC_ALL
, "");
2136 bindtextdomain(PACKAGE
, LOCALEDIR
);
2137 textdomain(PACKAGE
);
2138 close_stdout_atexit();
2140 while ((c
= getopt_long(argc
, argv
, "no:JrVhluQ:p:i::C:sH", longopts
, NULL
)) != -1) {
2161 parse_pids(optarg
, &pids
, &n_pids
);
2164 const char *subexpr
= NULL
;
2166 ctl
.sockets_only
= 1;
2168 subexpr
= inet46_subexpr
;
2169 else if (strcmp(optarg
, "4") == 0)
2170 subexpr
= inet4_subexpr
;
2171 else if (strcmp(optarg
, "6") == 0)
2172 subexpr
= inet6_subexpr
;
2175 _("unknown -i/--inet argument: %s"),
2178 append_filter_expr(&filter_expr
, subexpr
, true);
2182 append_filter_expr(&filter_expr
, optarg
, true);
2185 struct counter_spec
*c
= new_counter_spec(optarg
);
2186 list_add_tail(&c
->specs
, &counter_specs
);
2189 case OPT_DEBUG_FILTER
:
2190 debug_filter
= true;
2194 if (strcmp(optarg
, "never") == 0)
2195 ctl
.show_summary
= 0, ctl
.show_main
= 1;
2196 else if (strcmp(optarg
, "only") == 0)
2197 ctl
.show_summary
= 1, ctl
.show_main
= 0;
2198 else if (strcmp(optarg
, "append") == 0)
2199 ctl
.show_summary
= 1, ctl
.show_main
= 1;
2201 errx(EXIT_FAILURE
, _("unsupported --summary argument"));
2203 ctl
.show_summary
= 1, ctl
.show_main
= 0;
2205 case OPT_DUMP_COUNTERS
:
2206 dump_counters
= true;
2209 print_version(EXIT_SUCCESS
);
2213 list_colunms(stdout
);
2215 errtryhelp(EXIT_FAILURE
);
2219 errtryhelp(EXIT_FAILURE
);
2221 #define INITIALIZE_COLUMNS(COLUMN_SPEC) \
2222 for (i = 0; i < ARRAY_SIZE(COLUMN_SPEC); i++) \
2223 columns[ncolumns++] = COLUMN_SPEC[i]
2226 INITIALIZE_COLUMNS(default_threads_columns
);
2228 INITIALIZE_COLUMNS(default_columns
);
2231 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
2232 &ncolumns
, column_name_to_id
) < 0)
2233 return EXIT_FAILURE
;
2235 scols_init_debug(0);
2237 INIT_LIST_HEAD(&ctl
.procs
);
2239 /* inilialize scols table */
2240 ctl
.tb
= scols_new_table();
2242 err(EXIT_FAILURE
, _("failed to allocate output table"));
2244 scols_table_enable_noheadings(ctl
.tb
, ctl
.noheadings
);
2245 scols_table_enable_raw(ctl
.tb
, ctl
.raw
);
2246 scols_table_enable_json(ctl
.tb
, ctl
.json
);
2248 scols_table_set_name(ctl
.tb
, "lsfd");
2250 /* create output columns */
2251 for (i
= 0; i
< ncolumns
; i
++) {
2252 const struct colinfo
*col
= get_column_info(i
);
2253 struct libscols_column
*cl
= add_column(ctl
.tb
, col
);
2256 err(EXIT_FAILURE
, _("failed to allocate output column"));
2259 int flags
= scols_column_get_flags(cl
);
2260 flags
&= ~SCOLS_FL_TRUNC
;
2261 scols_column_set_flags(cl
, flags
);
2267 ctl
.filter
= new_filter(filter_expr
, debug_filter
, "", &ctl
);
2271 if (dump_counters
) {
2272 if (list_empty(&counter_specs
))
2273 dump_default_counter_specs();
2275 dump_counter_specs(&counter_specs
);
2280 if (ctl
.show_summary
) {
2281 if (list_empty(&counter_specs
))
2282 ctl
.counters
= new_default_counters(&ctl
);
2284 ctl
.counters
= new_counters(&counter_specs
, &ctl
);
2285 list_free(&counter_specs
, struct counter_spec
, specs
,
2291 sort_pids(pids
, n_pids
);
2293 if (scols_table_get_column_by_name(ctl
.tb
, "XMODE"))
2298 * The call initialize_ipc_table() must come before
2299 * initialize_classes.
2301 initialize_nodevs();
2302 initialize_ipc_table();
2303 initialize_classes();
2304 initialize_devdrvs();
2306 collect_processes(&ctl
, pids
, n_pids
);
2309 attach_xinfos(&ctl
.procs
);
2311 set_multiplexed_flags(&ctl
.procs
);
2314 convert(&ctl
.procs
, &ctl
);
2320 if (ctl
.show_summary
&& ctl
.counters
)
2321 emit_summary(&ctl
, ctl
.counters
);
2324 delete(&ctl
.procs
, &ctl
);
2326 finalize_ipc_table();