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_PTMX_TTY_INDEX
] = { "PTMX.TTY-INDEX",
279 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
280 N_("tty index of the counterpart") },
281 [COL_RAW_PROTOCOL
] = { "RAW.PROTOCOL",
282 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
283 N_("protocol number of the raw socket") },
284 [COL_RDEV
] = { "RDEV",
285 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
286 N_("device ID (if special file)") },
287 [COL_SIGNALFD_MASK
] = { "SIGNALFD.MASK",
288 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
289 N_("masked signals") },
290 [COL_SIZE
] = { "SIZE",
291 4, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
293 [COL_SOCK_LISTENING
] = { "SOCK.LISTENING",
294 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
295 N_("listening socket") },
296 [COL_SOCK_NETNS
] = { "SOCK.NETNS",
297 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
298 N_("inode identifying network namespace where the socket belongs to") },
299 [COL_SOCK_PROTONAME
] = { "SOCK.PROTONAME",
300 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
301 N_("protocol name") },
302 [COL_SOCK_SHUTDOWN
] = { "SOCK.SHUTDOWN",
303 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
304 N_("shutdown state of socket ([-r?][-w?])") },
305 [COL_SOCK_STATE
] = { "SOCK.STATE",
306 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
307 N_("state of socket") },
308 [COL_SOCK_TYPE
] = { "SOCK.TYPE",
309 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
310 N_("type of socket") },
311 [COL_SOURCE
] = { "SOURCE",
312 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
313 N_("file system, partition, or device containing file") },
314 [COL_STTYPE
] = { "STTYPE",
315 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
316 N_("file type (raw)") },
317 [COL_TCP_LADDR
] = { "TCP.LADDR",
318 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
319 N_("local TCP address (INET address:TCP port)") },
320 [COL_TCP_RADDR
] = { "TCP.RADDR",
321 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
322 N_("remote TCP address (INET address:TCP port)") },
323 [COL_TCP_LPORT
] = { "TCP.LPORT",
324 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
325 N_("local TCP port") },
326 [COL_TCP_RPORT
] = { "TCP.RPORT",
327 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
328 N_("remote TCP port") },
330 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
331 N_("thread ID of the process opening the file") },
332 [COL_TIMERFD_CLOCKID
] = { "TIMERFD.CLOCKID",
333 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
335 [COL_TIMERFD_INTERVAL
] = { "TIMERFD.INTERVAL",
336 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
338 [COL_TIMERFD_REMAINING
]= { "TIMERFD.REMAINING",
339 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
340 N_("remaining time") },
341 [COL_TUN_IFACE
] = { "TUN.IFACE",
342 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
343 N_("network interface behind the tun device") },
344 [COL_TYPE
] = { "TYPE",
345 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
346 N_("file type (cooked)") },
347 [COL_UDP_LADDR
] = { "UDP.LADDR",
348 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
349 N_("local UDP address (INET address:UDP port)") },
350 [COL_UDP_RADDR
] = { "UDP.RADDR",
351 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
352 N_("remote UDP address (INET address:UDP port)") },
353 [COL_UDP_LPORT
] = { "UDP.LPORT",
354 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
355 N_("local UDP port") },
356 [COL_UDP_RPORT
] = { "UDP.RPORT",
357 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
358 N_("remote UDP port") },
359 [COL_UDPLITE_LADDR
] = { "UDPLITE.LADDR",
360 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
361 N_("local UDPLite address (INET address:UDPLite port)") },
362 [COL_UDPLITE_RADDR
] = { "UDPLITE.RADDR",
363 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
364 N_("remote UDPLite address (INET address:UDPLite port)") },
365 [COL_UDPLITE_LPORT
] = { "UDPLITE.LPORT",
366 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
367 N_("local UDPLite port") },
368 [COL_UDPLITE_RPORT
] = { "UDPLITE.RPORT",
369 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
370 N_("remote UDPLite port") },
372 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
373 N_("user ID number of the process") },
374 [COL_UNIX_PATH
] = { "UNIX.PATH",
375 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
376 N_("filesystem pathname for UNIX domain socket") },
377 [COL_USER
] = { "USER",
378 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
379 N_("user of the process") },
380 [COL_XMODE
] = { "XMODE",
381 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
382 N_("extended version of MDOE (rwxD[Ll]m)") },
385 static const int default_columns
[] = {
398 static const int default_threads_columns
[] = {
412 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
413 static size_t ncolumns
;
415 static ino_t
*mnt_namespaces
;
416 static size_t nspaces
;
418 struct counter_spec
{
419 struct list_head specs
;
424 static const struct counter_spec default_counter_specs
[] = {
426 .name
= N_("processes"),
427 .expr
= "ASSOC == 'cwd'",
430 .name
= N_("root owned processes"),
431 .expr
= "(ASSOC == 'cwd') && (UID == 0)",
434 .name
= N_("kernel threads"),
435 .expr
= "(ASSOC == 'cwd') && KTHREAD",
438 .name
= N_("open files"),
442 .name
= N_("RO open files"),
443 .expr
= "(FD >= 0) and (MODE == 'r--')",
446 .name
= N_("WO open files"),
447 .expr
= "(FD >= 0) and (MODE == '-w-')",
450 .name
= N_("shared mappings"),
451 .expr
= "ASSOC == 'shm'",
454 .name
= N_("RO shared mappings"),
455 .expr
= "(ASSOC == 'shm') and (MODE == 'r--')",
458 .name
= N_("WO shared mappings"),
459 .expr
= "(ASSOC == 'shm') and (MODE == '-w-')",
462 .name
= N_("regular files"),
463 .expr
= "(FD >= 0) && (STTYPE == 'REG')",
466 .name
= N_("directories"),
467 .expr
= "(FD >= 0) && (STTYPE == 'DIR')",
470 .name
= N_("sockets"),
471 .expr
= "(FD >= 0) && (STTYPE == 'SOCK')",
474 .name
= N_("fifos/pipes"),
475 .expr
= "(FD >= 0) && (STTYPE == 'FIFO')",
478 .name
= N_("character devices"),
479 .expr
= "(FD >= 0) && (STTYPE == 'CHR')",
482 .name
= N_("block devices"),
483 .expr
= "(FD >= 0) && (STTYPE == 'BLK')",
486 .name
= N_("unknown types"),
487 .expr
= "(FD >= 0) && (STTYPE == 'UNKN')",
491 struct lsfd_control
{
492 struct libscols_table
*tb
; /* output */
493 struct list_head procs
; /* list of all processes */
495 unsigned int noheadings
: 1,
500 show_main
: 1, /* print main table */
501 show_summary
: 1, /* print summary/counters */
502 sockets_only
: 1, /* display only SOCKETS */
503 show_xmode
: 1; /* XMODE column is enabled. */
505 struct lsfd_filter
*filter
;
506 struct lsfd_counter
**counters
; /* NULL terminated array. */
509 static void *proc_tree
; /* for tsearch/tfind */
511 static int proc_tree_compare(const void *a
, const void *b
)
513 return ((struct proc
*)a
)->pid
- ((struct proc
*)b
)->pid
;
516 struct proc
*get_proc(pid_t pid
)
518 struct proc key
= { .pid
= pid
};
519 struct proc
**node
= tfind(&key
, &proc_tree
, proc_tree_compare
);
525 static int column_name_to_id(const char *name
, size_t namesz
)
529 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
530 const char *cn
= infos
[i
].name
;
532 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
535 warnx(_("unknown column: %s"), name
);
537 return LSFD_FILTER_UNKNOWN_COL_ID
;
540 static int column_name_to_id_cb(const char *name
, void *data
__attribute__((__unused__
)))
542 return column_name_to_id(name
, strlen(name
));
545 static int get_column_id(int num
)
548 assert((size_t) num
< ncolumns
);
549 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
554 static const struct colinfo
*get_column_info(int num
)
556 return &infos
[ get_column_id(num
) ];
559 static struct libscols_column
*add_column(struct libscols_table
*tb
, const struct colinfo
*col
)
561 struct libscols_column
*cl
;
562 int flags
= col
->flags
;
564 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
, flags
);
566 scols_column_set_json_type(cl
, col
->json_type
);
567 if (col
->flags
& SCOLS_FL_WRAP
) {
568 scols_column_set_wrapfunc(cl
,
569 scols_wrapnl_chunksize
,
570 scols_wrapnl_nextchunk
,
572 scols_column_set_safechars(cl
, "\n");
579 static struct libscols_column
*add_column_by_id_cb(struct libscols_table
*tb
, int colid
, void *data
)
581 struct libscols_column
*cl
;
583 if (ncolumns
>= ARRAY_SIZE(columns
))
584 errx(EXIT_FAILURE
, _("too many columns are added via filter expression"));
586 assert(colid
< LSFD_N_COLS
);
588 cl
= add_column(tb
, infos
+ colid
);
590 err(EXIT_FAILURE
, _("failed to allocate output column"));
591 columns
[ncolumns
++] = colid
;
593 if (colid
== COL_TID
) {
594 struct lsfd_control
*ctl
= data
;
601 static int has_mnt_ns(ino_t id
)
605 for (i
= 0; i
< nspaces
; i
++) {
606 if (mnt_namespaces
[i
] == id
)
612 static void add_mnt_ns(ino_t id
)
617 nmax
= (nspaces
+ 16) / 16 * 16;
618 if (nmax
<= nspaces
+ 1) {
620 mnt_namespaces
= xreallocarray(mnt_namespaces
,
621 nmax
, sizeof(ino_t
));
623 mnt_namespaces
[nspaces
++] = id
;
626 static const struct file_class
*stat2class(struct stat
*sb
)
632 switch (sb
->st_mode
& S_IFMT
) {
649 if (is_nsfs_dev(dev
))
650 return &nsfs_file_class
;
652 if (is_mqueue_dev(dev
))
653 return &mqueue_file_class
;
663 static struct file
*new_file(struct proc
*proc
, const struct file_class
*class)
668 file
= xcalloc(1, class->size
);
673 INIT_LIST_HEAD(&file
->files
);
674 list_add_tail(&file
->files
, &proc
->files
);
679 static struct file
*copy_file(struct file
*old
)
681 struct file
*file
= xcalloc(1, old
->class->size
);
683 INIT_LIST_HEAD(&file
->files
);
684 file
->proc
= old
->proc
;
685 list_add_tail(&file
->files
, &old
->proc
->files
);
687 file
->class = old
->class;
688 file
->association
= old
->association
;
689 file
->name
= xstrdup(old
->name
);
690 file
->stat
= old
->stat
;
695 static void file_set_path(struct file
*file
, struct stat
*sb
, const char *name
, int association
)
697 file
->association
= association
;
698 file
->name
= xstrdup(name
);
702 static void file_init_content(struct file
*file
)
704 if (file
->class && file
->class->initialize_content
)
705 file
->class->initialize_content(file
);
708 static void free_file(struct file
*file
)
710 const struct file_class
*class = file
->class;
713 if (class->free_content
)
714 class->free_content(file
);
715 class = class->super
;
721 static struct proc
*new_process(pid_t pid
, struct proc
*leader
)
723 struct proc
*proc
= xcalloc(1, sizeof(*proc
));
726 proc
->leader
= leader
? leader
: proc
;
727 proc
->command
= NULL
;
729 INIT_LIST_HEAD(&proc
->files
);
730 INIT_LIST_HEAD(&proc
->procs
);
731 INIT_LIST_HEAD(&proc
->eventpolls
);
737 static void free_proc(struct proc
*proc
)
739 list_free(&proc
->files
, struct file
, files
, free_file
);
745 static void read_fdinfo(struct file
*file
, FILE *fdinfo
)
749 while (fgets(buf
, sizeof(buf
), fdinfo
)) {
750 const struct file_class
*class;
751 char *val
= strchr(buf
, ':');
755 *val
++ = '\0'; /* terminate key */
757 val
= (char *) skip_space(val
);
758 rtrim_whitespace((unsigned char *) val
);
762 if (class->handle_fdinfo
763 && class->handle_fdinfo(file
, buf
, val
))
765 class = class->super
;
770 static struct file
*collect_file_symlink(struct path_cxt
*pc
,
776 char sym
[PATH_MAX
] = { '\0' };
778 struct file
*f
, *prev
;
780 if (ul_path_readlink(pc
, sym
, sizeof(sym
), name
) < 0)
783 /* The /proc/#/{fd,ns} often contains the same file (e.g. /dev/tty)
784 * more than once. Let's try to reuse the previous file if the real
785 * path is the same to save stat() call.
787 prev
= list_last_entry(&proc
->files
, struct file
, files
);
788 if (prev
&& prev
->name
&& strcmp(prev
->name
, sym
) == 0) {
790 f
->association
= assoc
;
792 const struct file_class
*class;
794 if (ul_path_stat(pc
, &sb
, 0, name
) < 0)
797 class = stat2class(&sb
);
799 /* A nsfs file is not a socket but the nsfs file can
800 * be used as a entry point to collect information from
801 * other network namespaces. Besed on the information,
802 * various columns of sockets can be filled.
804 && (class != &sock_class
) && (class != &nsfs_file_class
))
806 f
= new_file(proc
, class);
807 file_set_path(f
, &sb
, sym
, assoc
);
810 file_init_content(f
);
812 if (is_association(f
, NS_MNT
))
813 proc
->ns_mnt
= f
->stat
.st_ino
;
814 else if (is_association(f
, NS_NET
))
815 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
817 else if (assoc
>= 0) {
818 /* file-descriptor based association */
821 if (ul_path_stat(pc
, &sb
, AT_SYMLINK_NOFOLLOW
, name
) == 0)
822 f
->mode
= sb
.st_mode
;
824 if (is_nsfs_dev(f
->stat
.st_dev
))
825 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
827 fdinfo
= ul_path_fopenf(pc
, "r", "fdinfo/%d", assoc
);
829 read_fdinfo(f
, fdinfo
);
837 /* read symlinks from /proc/#/fd
839 static void collect_fd_files(struct path_cxt
*pc
, struct proc
*proc
,
843 struct dirent
*d
= NULL
;
844 char path
[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX
))];
846 while (ul_path_next_dirent(pc
, &sub
, "fd", &d
) == 0) {
849 if (ul_strtou64(d
->d_name
, &num
, 10) != 0) /* only numbers */
852 snprintf(path
, sizeof(path
), "fd/%ju", (uintmax_t) num
);
853 collect_file_symlink(pc
, proc
, path
, num
, sockets_only
);
857 static void parse_maps_line(struct path_cxt
*pc
, char *buf
, struct proc
*proc
)
859 uint64_t start
, end
, offset
, ino
;
860 unsigned long major
, minor
;
861 enum association assoc
= ASSOC_MEM
;
863 struct file
*f
, *prev
;
864 char *path
, modestr
[5];
867 /* read rest of the map */
868 if (sscanf(buf
, "%"SCNx64
/* start */
871 " %"SCNx64
/* offset */
872 " %lx:%lx" /* maj:min */
873 " %"SCNu64
, /* inode */
875 &start
, &end
, modestr
, &offset
,
876 &major
, &minor
, &ino
) != 7)
879 /* Skip private anonymous mappings. */
880 if (major
== 0 && minor
== 0 && ino
== 0)
883 devno
= makedev(major
, minor
);
885 if (modestr
[3] == 's')
888 /* The map usually contains the same file more than once, try to reuse
889 * the previous file (if devno and ino are the same) to save stat() call.
891 prev
= list_last_entry(&proc
->files
, struct file
, files
);
893 if (prev
&& prev
->stat
.st_dev
== devno
&& prev
->stat
.st_ino
== ino
) {
895 f
->association
= -assoc
;
896 } else if ((path
= strchr(buf
, '/'))) {
897 rtrim_whitespace((unsigned char *) path
);
898 if (stat(path
, &sb
) < 0)
899 /* If a file is mapped but deleted from the file system,
900 * "stat by the file name" may not work. In that case,
903 f
= new_file(proc
, stat2class(&sb
));
904 file_set_path(f
, &sb
, path
, -assoc
);
906 /* As used in tcpdump, AF_PACKET socket can be mmap'ed. */
907 char map_file
[sizeof("map_files/0000000000000000-ffffffffffffffff")];
908 char sym
[PATH_MAX
] = { '\0' };
911 snprintf(map_file
, sizeof(map_file
), "map_files/%"PRIx64
"-%"PRIx64
, start
, end
);
912 if (ul_path_stat(pc
, &sb
, 0, map_file
) < 0)
914 if (ul_path_readlink(pc
, sym
, sizeof(sym
), map_file
) < 0)
916 f
= new_file(proc
, stat2class(&sb
));
917 file_set_path(f
, &sb
, sym
, -assoc
);
920 if (modestr
[0] == 'r')
922 if (modestr
[1] == 'w')
924 if (modestr
[2] == 'x')
927 f
->map_start
= start
;
931 file_init_content(f
);
934 static void collect_mem_files(struct path_cxt
*pc
, struct proc
*proc
)
939 fp
= ul_path_fopen(pc
, "r", "maps");
943 while (fgets(buf
, sizeof(buf
), fp
))
944 parse_maps_line(pc
, buf
, proc
);
949 static void collect_outofbox_files(struct path_cxt
*pc
,
951 enum association assocs
[],
958 for (i
= 0; i
< count
; i
++)
959 collect_file_symlink(pc
, proc
, names
[assocs
[i
]], assocs
[i
] * -1,
963 static void collect_execve_file(struct path_cxt
*pc
, struct proc
*proc
,
966 enum association assocs
[] = { ASSOC_EXE
};
967 const char *names
[] = {
970 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
974 static void collect_fs_files(struct path_cxt
*pc
, struct proc
*proc
,
977 enum association assocs
[] = { ASSOC_EXE
, ASSOC_CWD
, ASSOC_ROOT
};
978 const char *names
[] = {
980 [ASSOC_ROOT
] = "root",
982 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
986 static void collect_namespace_files(struct path_cxt
*pc
, struct proc
*proc
)
988 enum association assocs
[] = {
1000 const char *names
[] = {
1001 [ASSOC_NS_CGROUP
] = "ns/cgroup",
1002 [ASSOC_NS_IPC
] = "ns/ipc",
1003 [ASSOC_NS_MNT
] = "ns/mnt",
1004 [ASSOC_NS_NET
] = "ns/net",
1005 [ASSOC_NS_PID
] = "ns/pid",
1006 [ASSOC_NS_PID4C
] = "ns/pid_for_children",
1007 [ASSOC_NS_TIME
] = "ns/time",
1008 [ASSOC_NS_TIME4C
] = "ns/time_for_children",
1009 [ASSOC_NS_USER
] = "ns/user",
1010 [ASSOC_NS_UTS
] = "ns/uts",
1012 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
1013 /* Namespace information is alwasys needed. */
1017 static struct nodev
*new_nodev(unsigned long minor
, const char *filesystem
)
1019 struct nodev
*nodev
= xcalloc(1, sizeof(*nodev
));
1021 INIT_LIST_HEAD(&nodev
->nodevs
);
1022 nodev
->minor
= minor
;
1023 nodev
->filesystem
= xstrdup(filesystem
);
1028 static void free_nodev(struct nodev
*nodev
)
1030 free(nodev
->filesystem
);
1034 void add_nodev(unsigned long minor
, const char *filesystem
)
1036 struct nodev
*nodev
= new_nodev(minor
, filesystem
);
1037 unsigned long slot
= nodev
->minor
% NODEV_TABLE_SIZE
;
1039 list_add_tail(&nodev
->nodevs
, &nodev_table
.tables
[slot
]);
1042 static void initialize_nodevs(void)
1046 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1047 INIT_LIST_HEAD(&nodev_table
.tables
[i
]);
1050 static void finalize_nodevs(void)
1054 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1055 list_free(&nodev_table
.tables
[i
], struct nodev
, nodevs
, free_nodev
);
1057 free(mnt_namespaces
);
1060 const char *get_nodev_filesystem(unsigned long minor
)
1062 struct list_head
*n
;
1063 int slot
= minor
% NODEV_TABLE_SIZE
;
1065 list_for_each (n
, &nodev_table
.tables
[slot
]) {
1066 struct nodev
*nodev
= list_entry(n
, struct nodev
, nodevs
);
1067 if (nodev
->minor
== minor
)
1068 return nodev
->filesystem
;
1073 static void add_nodevs(FILE *mnt
)
1075 /* This can be very long. A line in mountinfo can have more than 3
1077 char line
[PATH_MAX
* 3 + 256];
1079 while (fgets(line
, sizeof(line
), mnt
)) {
1080 unsigned long major
, minor
;
1081 char filesystem
[256];
1083 /* 23 61 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel */
1084 if(sscanf(line
, "%*d %*d %lu:%lu %*s %*s %*s %*[^-] - %s %*[^\n]",
1085 &major
, &minor
, filesystem
) != 3)
1086 /* 1600 1458 0:55 / / rw,nodev,relatime - overlay overlay rw,context="s... */
1087 if (sscanf(line
, "%*d %*d %lu:%lu %*s %*s %*s - %s %*[^\n]",
1088 &major
, &minor
, filesystem
) != 3)
1093 if (get_nodev_filesystem(minor
))
1096 add_nodev(minor
, filesystem
);
1100 static void initialize_ipc_table(void)
1102 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1103 INIT_LIST_HEAD(ipc_table
.tables
+ i
);
1106 static void free_ipc(struct ipc
*ipc
)
1108 if (ipc
->class->free
)
1109 ipc
->class->free(ipc
);
1113 static void finalize_ipc_table(void)
1115 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1116 list_free(&ipc_table
.tables
[i
], struct ipc
, ipcs
, free_ipc
);
1119 struct ipc
*new_ipc(const struct ipc_class
*class)
1121 struct ipc
*ipc
= xcalloc(1, class->size
);
1123 INIT_LIST_HEAD(&ipc
->endpoints
);
1124 INIT_LIST_HEAD(&ipc
->ipcs
);
1128 struct ipc
*get_ipc(struct file
*file
)
1131 struct list_head
*e
;
1132 const struct ipc_class
*ipc_class
;
1134 if (!file
->class->get_ipc_class
)
1137 ipc_class
= file
->class->get_ipc_class(file
);
1141 slot
= ipc_class
->get_hash(file
) % IPC_TABLE_SIZE
;
1142 list_for_each (e
, &ipc_table
.tables
[slot
]) {
1143 struct ipc
*ipc
= list_entry(e
, struct ipc
, ipcs
);
1144 if (ipc
->class != ipc_class
)
1146 if (ipc_class
->is_suitable_ipc(ipc
, file
))
1152 void add_ipc(struct ipc
*ipc
, unsigned int hash
)
1154 int slot
= hash
% IPC_TABLE_SIZE
;
1155 list_add(&ipc
->ipcs
, &ipc_table
.tables
[slot
]);
1158 void init_endpoint(struct ipc_endpoint
*endpoint
)
1160 INIT_LIST_HEAD(&endpoint
->endpoints
);
1163 void add_endpoint(struct ipc_endpoint
*endpoint
, struct ipc
*ipc
)
1165 endpoint
->ipc
= ipc
;
1166 list_add(&endpoint
->endpoints
, &ipc
->endpoints
);
1169 static void fill_column(struct proc
*proc
,
1171 struct libscols_line
*ln
,
1173 size_t column_index
)
1175 const struct file_class
*class = file
->class;
1178 if (class->fill_column
1179 && class->fill_column(proc
, file
, ln
,
1180 column_id
, column_index
))
1182 class = class->super
;
1186 static void convert_file(struct proc
*proc
,
1188 struct libscols_line
*ln
)
1193 for (i
= 0; i
< ncolumns
; i
++)
1194 fill_column(proc
, file
, ln
, get_column_id(i
), i
);
1197 static void convert(struct list_head
*procs
, struct lsfd_control
*ctl
)
1199 struct list_head
*p
;
1201 list_for_each (p
, procs
) {
1202 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1203 struct list_head
*f
;
1205 list_for_each (f
, &proc
->files
) {
1206 struct file
*file
= list_entry(f
, struct file
, files
);
1207 struct libscols_line
*ln
= scols_table_new_line(ctl
->tb
, NULL
);
1208 struct lsfd_counter
**counter
= NULL
;
1211 err(EXIT_FAILURE
, _("failed to allocate output line"));
1213 convert_file(proc
, file
, ln
);
1215 if (!lsfd_filter_apply(ctl
->filter
, ln
)) {
1216 scols_table_remove_line(ctl
->tb
, ln
);
1223 for (counter
= ctl
->counters
; *counter
; counter
++)
1224 lsfd_counter_accumulate(*counter
, ln
);
1229 static void delete(struct list_head
*procs
, struct lsfd_control
*ctl
)
1231 struct list_head
*p
;
1233 list_for_each (p
, procs
) {
1234 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1235 tdelete(proc
, &proc_tree
, proc_tree_compare
);
1237 list_free(procs
, struct proc
, procs
, free_proc
);
1239 scols_unref_table(ctl
->tb
);
1240 lsfd_filter_free(ctl
->filter
);
1241 if (ctl
->counters
) {
1242 struct lsfd_counter
**counter
;
1243 for (counter
= ctl
->counters
; *counter
; counter
++)
1244 lsfd_counter_free(*counter
);
1245 free(ctl
->counters
);
1249 static void emit(struct lsfd_control
*ctl
)
1251 scols_print_table(ctl
->tb
);
1255 static void initialize_class(const struct file_class
*class)
1257 if (class->initialize_class
)
1258 class->initialize_class();
1261 static void initialize_classes(void)
1263 initialize_class(&file_class
);
1264 initialize_class(&cdev_class
);
1265 initialize_class(&bdev_class
);
1266 initialize_class(&sock_class
);
1267 initialize_class(&unkn_class
);
1270 static void finalize_class(const struct file_class
*class)
1272 if (class->finalize_class
)
1273 class->finalize_class();
1276 static void finalize_classes(void)
1278 finalize_class(&file_class
);
1279 finalize_class(&cdev_class
);
1280 finalize_class(&bdev_class
);
1281 finalize_class(&sock_class
);
1282 finalize_class(&unkn_class
);
1285 static struct devdrv
*new_devdrv(unsigned long major
, const char *name
)
1287 struct devdrv
*devdrv
= xcalloc(1, sizeof(*devdrv
));
1289 INIT_LIST_HEAD(&devdrv
->devdrvs
);
1291 devdrv
->major
= major
;
1292 devdrv
->name
= xstrdup(name
);
1297 static void free_devdrv(struct devdrv
*devdrv
)
1303 #define READ_DEVICES_LINE_LEN 256
1304 static struct devdrv
*read_devdrv(const char *line
)
1306 unsigned long major
;
1307 char name
[READ_DEVICES_LINE_LEN
];
1309 if (sscanf(line
, "%lu %s", &major
, name
) != 2)
1312 return new_devdrv(major
, name
);
1315 static void read_devices(struct list_head
*chrdrvs_list
,
1316 struct list_head
*blkdrvs_list
, FILE *devices_fp
)
1318 char line
[READ_DEVICES_LINE_LEN
];
1320 /* Skip to the line "Character devices:". */
1321 while (fgets(line
, sizeof(line
), devices_fp
)) {
1327 while (fgets(line
, sizeof(line
), devices_fp
)) {
1328 /* Find the blank line before "Block devices:" line. */
1329 if (line
[0] == '\n')
1332 /* Read the character device drivers */
1333 struct devdrv
*devdrv
= read_devdrv(line
);
1335 list_add_tail(&devdrv
->devdrvs
, chrdrvs_list
);
1338 /* Skip to the line "Block devices:". */
1339 while (fgets(line
, sizeof(line
), devices_fp
)) {
1345 /* Read the block device drivers */
1346 while (fgets(line
, sizeof(line
), devices_fp
)) {
1347 struct devdrv
*devdrv
= read_devdrv(line
);
1349 list_add_tail(&devdrv
->devdrvs
, blkdrvs_list
);
1353 static void initialize_devdrvs(void)
1357 INIT_LIST_HEAD(&chrdrvs
);
1358 INIT_LIST_HEAD(&blkdrvs
);
1360 devices_fp
= fopen("/proc/devices", "r");
1362 read_devices(&chrdrvs
, &blkdrvs
, devices_fp
);
1367 static void finalize_devdrvs(void)
1369 list_free(&blkdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1370 list_free(&chrdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1373 static const char *get_devdrv(struct list_head
*devdrvs_list
, unsigned long major
)
1375 struct list_head
*c
;
1376 list_for_each(c
, devdrvs_list
) {
1377 struct devdrv
*devdrv
= list_entry(c
, struct devdrv
, devdrvs
);
1378 if (devdrv
->major
== major
)
1379 return devdrv
->name
;
1384 const char *get_chrdrv(unsigned long major
)
1386 return get_devdrv(&chrdrvs
, major
);
1389 const char *get_blkdrv(unsigned long major
)
1391 return get_devdrv(&blkdrvs
, major
);
1394 struct name_manager
*new_name_manager(void)
1396 struct name_manager
*nm
= xcalloc(1, sizeof(struct name_manager
));
1398 nm
->cache
= new_idcache();
1400 err(EXIT_FAILURE
, _("failed to allocate an idcache"));
1402 nm
->next_id
= 1; /* 0 is never issued as id. */
1406 void free_name_manager(struct name_manager
*nm
)
1408 free_idcache(nm
->cache
);
1412 const char *get_name(struct name_manager
*nm
, unsigned long id
)
1416 e
= get_id(nm
->cache
, id
);
1418 return e
? e
->name
: NULL
;
1421 unsigned long add_name(struct name_manager
*nm
, const char *name
)
1423 struct identry
*e
= NULL
, *tmp
;
1425 for (tmp
= nm
->cache
->ent
; tmp
; tmp
= tmp
->next
) {
1426 if (strcmp(tmp
->name
, name
) == 0) {
1435 e
= xmalloc(sizeof(struct identry
));
1436 e
->name
= xstrdup(name
);
1437 e
->id
= nm
->next_id
++;
1438 e
->next
= nm
->cache
->ent
;
1444 static void walk_threads(struct lsfd_control
*ctl
, struct path_cxt
*pc
,
1445 pid_t pid
, struct proc
*proc
,
1446 void (*cb
)(struct lsfd_control
*, struct path_cxt
*,
1447 pid_t
, struct proc
*))
1452 while (procfs_process_next_tid(pc
, &sub
, &tid
) == 0) {
1455 (*cb
)(ctl
, pc
, tid
, proc
);
1459 static int pollfdcmp(const void *a
, const void *b
)
1461 const struct pollfd
*apfd
= a
, *bpfd
= b
;
1463 return apfd
->fd
- bpfd
->fd
;
1466 static void mark_poll_fds_as_multiplexed(char *buf
,
1467 pid_t pid
, struct proc
*proc
)
1473 struct iovec remote
;
1476 struct list_head
*f
;
1478 if (sscanf(buf
, "%lx %lx", &fds
, &nfds
) != 2)
1484 local
.iov_len
= sizeof(struct pollfd
) * nfds
;
1485 local
.iov_base
= xmalloc(local
.iov_len
);
1486 remote
.iov_len
= local
.iov_len
;
1487 remote
.iov_base
= (void *)fds
;
1489 n
= process_vm_readv(pid
, &local
, 1, &remote
, 1, 0);
1490 if (n
< 0 || ((size_t)n
) != local
.iov_len
)
1493 qsort(local
.iov_base
, nfds
, sizeof(struct pollfd
), pollfdcmp
);
1495 list_for_each (f
, &proc
->files
) {
1496 struct file
*file
= list_entry(f
, struct file
, files
);
1497 if (is_opened_file(file
) && !file
->multiplexed
) {
1498 int fd
= file
->association
;
1499 if (bsearch(&(struct pollfd
){.fd
= fd
,}, local
.iov_base
,
1500 nfds
, sizeof(struct pollfd
), pollfdcmp
))
1501 file
->multiplexed
= 1;
1506 free (local
.iov_base
);
1509 static void mark_select_fds_as_multiplexed(char *buf
,
1510 pid_t pid
, struct proc
*proc
)
1515 struct iovec local
[3];
1516 fd_set local_set
[3];
1517 struct iovec remote
[3];
1519 ssize_t expected_n
= 0;
1521 struct list_head
*f
;
1523 if (sscanf(buf
, "%lx %lx %lx %lx", &nfds
, fds
+ 0, fds
+ 1, fds
+ 2) != 4)
1529 for (int i
= 0; i
< 3; i
++) {
1530 /* If the remote address for the fd_set is 0x0, no set is tehre. */
1531 remote
[i
].iov_len
= local
[i
].iov_len
= fds
[i
]? sizeof(local_set
[i
]): 0;
1532 expected_n
+= (ssize_t
)local
[i
].iov_len
;
1533 local
[i
].iov_base
= local_set
+ i
;
1534 remote
[i
].iov_base
= (void *)(fds
[i
]);
1537 n
= process_vm_readv(pid
, local
, 3, remote
, 3, 0);
1538 if (n
< 0 || n
!= expected_n
)
1541 list_for_each (f
, &proc
->files
) {
1542 struct file
*file
= list_entry(f
, struct file
, files
);
1543 if (is_opened_file(file
) && !file
->multiplexed
) {
1544 int fd
= file
->association
;
1547 if ((fds
[0] && FD_ISSET(fd
, (fd_set
*)local
[0].iov_base
))
1548 || (fds
[1] && FD_ISSET(fd
, (fd_set
*)local
[1].iov_base
))
1549 || (fds
[2] && FD_ISSET(fd
, (fd_set
*)local
[2].iov_base
)))
1550 file
->multiplexed
= 1;
1555 static void parse_proc_syscall(struct lsfd_control
*ctl
__attribute__((__unused__
)),
1556 struct path_cxt
*pc
, pid_t pid
, struct proc
*proc
)
1562 if (procfs_process_get_syscall(pc
, buf
, sizeof(buf
)) <= 0)
1566 scn
= strtol(buf
, &ptr
, 10);
1575 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1580 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1583 #ifdef SYS_ppoll_time64
1584 case SYS_ppoll_time64
:
1585 mark_poll_fds_as_multiplexed(ptr
, pid
, proc
);
1591 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1596 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1599 #ifdef SYS_pselect6_time64
1600 case SYS_pselect6_time64
:
1601 mark_select_fds_as_multiplexed(ptr
, pid
, proc
);
1607 static void read_process(struct lsfd_control
*ctl
, struct path_cxt
*pc
,
1608 pid_t pid
, struct proc
*leader
)
1613 if (procfs_process_init_path(pc
, pid
) != 0)
1616 proc
= new_process(pid
, leader
);
1617 proc
->command
= procfs_process_get_cmdname(pc
, buf
, sizeof(buf
)) > 0 ?
1618 xstrdup(buf
) : xstrdup(_("(unknown)"));
1619 procfs_process_get_uid(pc
, &proc
->uid
);
1621 if (procfs_process_get_stat(pc
, buf
, sizeof(buf
)) > 0) {
1626 /* See proc(5) about the column in the line. */
1627 xstrappend(&pat
, "%*d (");
1628 for (p
= proc
->command
; *p
!= '\0'; p
++) {
1630 xstrappend(&pat
, "%%");
1634 xstrappend(&pat
, ") %*c %*d %*d %*d %*d %*d %u %*[^\n]");
1635 if (sscanf(buf
, pat
, &flags
) == 1)
1636 proc
->kthread
= !!(flags
& PF_KTHREAD
);
1640 collect_execve_file(pc
, proc
, ctl
->sockets_only
);
1642 if (proc
->pid
== proc
->leader
->pid
1643 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_FS
, 0, 0) != 0)
1644 collect_fs_files(pc
, proc
, ctl
->sockets_only
);
1646 if (proc
->ns_mnt
== 0 || !has_mnt_ns(proc
->ns_mnt
)) {
1647 FILE *mnt
= ul_path_fopen(pc
, "r", "mountinfo");
1651 add_mnt_ns(proc
->ns_mnt
);
1656 collect_namespace_files(pc
, proc
);
1658 /* If kcmp is not available,
1659 * there is no way to know whether threads share resources.
1660 * In such cases, we must pay the costs: call collect_mem_files()
1661 * and collect_fd_files().
1663 if ((!ctl
->sockets_only
)
1664 && (proc
->pid
== proc
->leader
->pid
1665 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_VM
, 0, 0) != 0))
1666 collect_mem_files(pc
, proc
);
1668 if (proc
->pid
== proc
->leader
->pid
1669 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_FILES
, 0, 0) != 0)
1670 collect_fd_files(pc
, proc
, ctl
->sockets_only
);
1672 list_add_tail(&proc
->procs
, &ctl
->procs
);
1673 if (tsearch(proc
, &proc_tree
, proc_tree_compare
) == NULL
)
1674 errx(EXIT_FAILURE
, _("failed to allocate memory"));
1676 if (ctl
->show_xmode
)
1677 parse_proc_syscall(ctl
, pc
, pid
, proc
);
1679 /* The tasks collecting overwrites @pc by /proc/<task-pid>/. Keep it as
1680 * the last path based operation in read_process()
1682 if (ctl
->threads
&& leader
== NULL
)
1683 walk_threads(ctl
, pc
, pid
, proc
, read_process
);
1684 else if (ctl
->show_xmode
)
1685 walk_threads(ctl
, pc
, pid
, proc
, parse_proc_syscall
);
1687 /* Let's be careful with number of open files */
1688 ul_path_close_dirfd(pc
);
1691 static void parse_pids(const char *str
, pid_t
**pids
, int *count
)
1700 v
= strtol(str
, &next
, 10);
1702 err(EXIT_FAILURE
, _("unexpected value for pid specification: %s"), str
);
1704 errx(EXIT_FAILURE
, _("garbage at the end of pid specification: %s"), str
);
1706 errx(EXIT_FAILURE
, _("out of range value for pid specification: %ld"), v
);
1709 *pids
= xreallocarray(*pids
, *count
, sizeof(**pids
));
1710 (*pids
)[*count
- 1] = (pid_t
)v
;
1712 while (next
&& *next
!= '\0'
1713 && (isspace((unsigned char)*next
) || *next
== ','))
1716 parse_pids(next
, pids
, count
);
1719 static int pidcmp(const void *a
, const void *b
)
1721 pid_t pa
= *(pid_t
*)a
;
1722 pid_t pb
= *(pid_t
*)b
;
1732 static void sort_pids(pid_t pids
[], const int count
)
1734 qsort(pids
, count
, sizeof(pid_t
), pidcmp
);
1737 static bool member_pids(const pid_t pid
, const pid_t pids
[], const int count
)
1739 return bsearch(&pid
, pids
, count
, sizeof(pid_t
), pidcmp
)? true: false;
1742 static void collect_processes(struct lsfd_control
*ctl
, const pid_t pids
[], int n_pids
)
1746 struct path_cxt
*pc
= NULL
;
1748 pc
= ul_new_path(NULL
);
1750 err(EXIT_FAILURE
, _("failed to alloc procfs handler"));
1752 dir
= opendir(_PATH_PROC
);
1754 err(EXIT_FAILURE
, _("failed to open /proc"));
1756 while ((d
= readdir(dir
))) {
1759 if (procfs_dirent_get_pid(d
, &pid
) != 0)
1761 if (n_pids
== 0 || member_pids(pid
, pids
, n_pids
))
1762 read_process(ctl
, pc
, pid
, 0);
1769 static void __attribute__((__noreturn__
)) list_colunms(FILE *out
)
1771 fputs(USAGE_COLUMNS
, out
);
1772 for (size_t i
= 0; i
< ARRAY_SIZE(infos
); i
++)
1773 fprintf(out
, " %20s %-10s%s\n", infos
[i
].name
,
1774 infos
[i
].json_type
== SCOLS_JSON_STRING
? "<string>":
1775 infos
[i
].json_type
== SCOLS_JSON_ARRAY_STRING
? "<string>":
1776 infos
[i
].json_type
== SCOLS_JSON_ARRAY_NUMBER
? "<string>":
1777 infos
[i
].json_type
== SCOLS_JSON_NUMBER
? "<number>":
1783 static void print_columns(FILE *out
, const char *prefix
, const int cols
[], size_t n_cols
)
1785 fprintf(out
, "%15s: ", prefix
);
1786 for (size_t i
= 0; i
< n_cols
; i
++) {
1789 fputs(infos
[cols
[i
]].name
, out
);
1794 static void __attribute__((__noreturn__
)) usage(void)
1798 fputs(USAGE_HEADER
, out
);
1799 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
1801 fputs(USAGE_OPTIONS
, out
);
1802 fputs(_(" -l, --threads list in threads level\n"), out
);
1803 fputs(_(" -J, --json use JSON output format\n"), out
);
1804 fputs(_(" -n, --noheadings don't print headings\n"), out
);
1805 fputs(_(" -o, --output <list> output columns\n"), out
);
1806 fputs(_(" -r, --raw use raw output format\n"), out
);
1807 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out
);
1808 fputs(_(" -p, --pid <pid(s)> collect information only specified processes\n"), out
);
1809 fputs(_(" -i[4|6], --inet[=4|=6] list only IPv4 and/or IPv6 sockets\n"), out
);
1810 fputs(_(" -Q, --filter <expr> apply display filter\n"), out
);
1811 fputs(_(" --debug-filter dump the internal data structure of filter and exit\n"), out
);
1812 fputs(_(" -C, --counter <name>:<expr> define custom counter for --summary output\n"), out
);
1813 fputs(_(" --dump-counters dump counter definitions\n"), out
);
1814 fputs(_(" --summary[=<when>] print summary information (only, append, or never)\n"), out
);
1816 fputs(USAGE_SEPARATOR
, out
);
1817 fputs(_(" -H, --list-columns list the available columns\n"), out
);
1818 fprintf(out
, USAGE_HELP_OPTIONS(30));
1820 fputs(USAGE_DEFAULT_COLUMNS
, out
);
1821 print_columns(out
, _("Default"), default_columns
, ARRAY_SIZE(default_columns
));
1822 print_columns(out
, _("With --threads"), default_threads_columns
, ARRAY_SIZE(default_threads_columns
));
1824 fprintf(out
, USAGE_MAN_TAIL("lsfd(1)"));
1829 static void append_filter_expr(char **a
, const char *b
, bool and)
1843 xstrappend(a
, "and(");
1845 xstrappend(a
, "or(");
1850 static struct lsfd_filter
*new_filter(const char *expr
, bool debug
, const char *err_prefix
, struct lsfd_control
*ctl
)
1852 struct lsfd_filter
*filter
;
1855 filter
= lsfd_filter_new(expr
, ctl
->tb
,
1857 column_name_to_id_cb
,
1858 add_column_by_id_cb
, ctl
);
1859 errmsg
= lsfd_filter_get_errmsg(filter
);
1861 errx(EXIT_FAILURE
, "%s%s", err_prefix
, errmsg
);
1863 lsfd_filter_dump(filter
, stdout
);
1870 static struct counter_spec
*new_counter_spec(const char *spec_str
)
1873 struct counter_spec
*spec
;
1875 if (spec_str
[0] == '\0')
1877 _("too short counter specification: -C/--counter %s"),
1879 if (spec_str
[0] == ':')
1881 _("no name for counter: -C/--counter %s"),
1884 sep
= strchr(spec_str
, ':');
1887 _("no name for counter: -C/--counter %s"),
1891 _("empty counter expression given: -C/--counter %s"),
1894 /* Split the spec_str in to name and expr. */
1897 if (strchr(spec_str
, '{'))
1899 _("don't use `{' in the name of a counter: %s"),
1902 spec
= xmalloc(sizeof(struct counter_spec
));
1903 INIT_LIST_HEAD(&spec
->specs
);
1904 spec
->name
= spec_str
;
1905 spec
->expr
= sep
+ 1;
1910 static void free_counter_spec(struct counter_spec
*counter_spec
)
1915 static struct lsfd_counter
*new_counter(const struct counter_spec
*spec
, struct lsfd_control
*ctl
)
1917 struct lsfd_filter
*filter
;
1919 filter
= new_filter(spec
->expr
, false,
1920 _("failed in making filter for a counter: "),
1922 return lsfd_counter_new(spec
->name
, filter
);
1925 static struct lsfd_counter
**new_counters(struct list_head
*specs
, struct lsfd_control
*ctl
)
1927 struct lsfd_counter
**counters
;
1928 size_t len
= list_count_entries(specs
);
1930 struct list_head
*s
;
1932 counters
= xcalloc(len
+ 1, sizeof(struct lsfd_counter
*));
1933 list_for_each(s
, specs
) {
1934 struct counter_spec
*spec
= list_entry(s
, struct counter_spec
, specs
);
1935 counters
[i
++] = new_counter(spec
, ctl
);
1937 assert(counters
[len
] == NULL
);
1942 static struct lsfd_counter
**new_default_counters(struct lsfd_control
*ctl
)
1944 struct lsfd_counter
**counters
;
1945 size_t len
= ARRAY_SIZE(default_counter_specs
);
1948 counters
= xcalloc(len
+ 1, sizeof(struct lsfd_counter
*));
1949 for (i
= 0; i
< len
; i
++) {
1950 const struct counter_spec
*spec
= default_counter_specs
+ i
;
1951 counters
[i
] = new_counter(spec
, ctl
);
1953 assert(counters
[len
] == NULL
);
1958 static void dump_default_counter_specs(void)
1960 size_t len
= ARRAY_SIZE(default_counter_specs
);
1963 puts("default counter specs:");
1964 for (i
= 0; i
< len
; i
++) {
1965 const struct counter_spec
*spec
= default_counter_specs
+ i
;
1966 printf("\t%s:%s\n", spec
->name
, spec
->expr
);
1970 static void dump_counter_specs(struct list_head
*specs
)
1972 struct list_head
*s
;
1974 puts("custom counter specs:");
1975 list_for_each(s
, specs
) {
1976 struct counter_spec
*spec
= list_entry(s
, struct counter_spec
, specs
);
1977 printf("\t%s:%s\n", spec
->name
, spec
->expr
);
1981 static struct libscols_table
*new_summary_table(struct lsfd_control
*ctl
)
1983 struct libscols_table
*tb
= scols_new_table();
1985 struct libscols_column
*name_cl
, *value_cl
;
1988 err(EXIT_FAILURE
, _("failed to allocate summary table"));
1990 scols_table_enable_noheadings(tb
, ctl
->noheadings
);
1991 scols_table_enable_raw(tb
, ctl
->raw
);
1992 scols_table_enable_json(tb
, ctl
->json
);
1995 scols_table_set_name(tb
, "lsfd-summary");
1998 value_cl
= scols_table_new_column(tb
, _("VALUE"), 0, SCOLS_FL_RIGHT
);
2000 err(EXIT_FAILURE
, _("failed to allocate summary column"));
2002 scols_column_set_json_type(value_cl
, SCOLS_JSON_NUMBER
);
2004 name_cl
= scols_table_new_column(tb
, _("COUNTER"), 0, 0);
2006 err(EXIT_FAILURE
, _("failed to allocate summary column"));
2008 scols_column_set_json_type(name_cl
, SCOLS_JSON_STRING
);
2013 static void fill_summary_line(struct libscols_line
*ln
, struct lsfd_counter
*counter
)
2017 xasprintf(&str
, "%llu", (unsigned long long)lsfd_counter_value(counter
));
2019 err(EXIT_FAILURE
, _("failed to add summary data"));
2020 if (scols_line_refer_data(ln
, 0, str
))
2021 err(EXIT_FAILURE
, _("failed to add summary data"));
2023 if (scols_line_set_data(ln
, 1, lsfd_counter_name(counter
)))
2024 err(EXIT_FAILURE
, _("failed to add summary data"));
2027 static void emit_summary(struct lsfd_control
*ctl
, struct lsfd_counter
**counter
)
2029 struct libscols_table
*tb
= new_summary_table(ctl
);
2031 for (; *counter
; counter
++) {
2032 struct libscols_line
*ln
= scols_table_new_line(tb
, NULL
);
2033 fill_summary_line(ln
, *counter
);
2035 scols_print_table(tb
);
2037 scols_unref_table(tb
);
2040 static void attach_xinfos(struct list_head
*procs
)
2042 struct list_head
*p
;
2044 list_for_each (p
, procs
) {
2045 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
2046 struct list_head
*f
;
2048 list_for_each (f
, &proc
->files
) {
2049 struct file
*file
= list_entry(f
, struct file
, files
);
2050 if (file
->class->attach_xinfo
)
2051 file
->class->attach_xinfo(file
);
2056 static void set_multiplexed_flags(struct list_head
*procs
)
2058 struct list_head
*p
;
2059 list_for_each (p
, procs
) {
2060 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
2061 struct list_head
*f
;
2062 list_for_each (f
, &proc
->files
) {
2063 struct file
*file
= list_entry(f
, struct file
, files
);
2064 if (is_opened_file(file
) && !file
->multiplexed
) {
2065 int fd
= file
->association
;
2066 if (is_multiplexed_by_eventpoll(fd
, &proc
->eventpolls
))
2067 file
->multiplexed
= 1;
2073 /* Filter expressions for implementing -i option.
2075 * To list up the protocol names, use the following command line
2078 * find . -type f -exec grep -A 1 --color=auto -nH --null -e 'struct proto .*{' \{\} +
2081 #define INET_SUBEXP_BEGIN "(SOCK.PROTONAME =~ \"^("
2082 #define INET4_REG "TCP|UDP|RAW|PING|UDP-Lite|SCTP|DCCP|L2TP/IP|SMC"
2083 #define INET6_REG "TCPv6|UDPv6|RAWv6|PINGv6|UDPLITEv6|SCTPv6|DCCPv6|L2TP/IPv6|SMC6"
2084 #define INET_SUBEXP_END ")$\")"
2086 static const char *inet4_subexpr
= INET_SUBEXP_BEGIN
2089 static const char *inet6_subexpr
= INET_SUBEXP_BEGIN
2092 static const char *inet46_subexpr
= INET_SUBEXP_BEGIN
2093 INET4_REG
"|" INET6_REG
2096 int main(int argc
, char *argv
[])
2100 char *outarg
= NULL
;
2101 char *filter_expr
= NULL
;
2102 bool debug_filter
= false;
2103 bool dump_counters
= false;
2106 struct list_head counter_specs
;
2108 struct lsfd_control ctl
= {
2112 INIT_LIST_HEAD(&counter_specs
);
2115 OPT_DEBUG_FILTER
= CHAR_MAX
+ 1,
2119 static const struct option longopts
[] = {
2120 { "noheadings", no_argument
, NULL
, 'n' },
2121 { "output", required_argument
, NULL
, 'o' },
2122 { "version", no_argument
, NULL
, 'V' },
2123 { "help", no_argument
, NULL
, 'h' },
2124 { "json", no_argument
, NULL
, 'J' },
2125 { "raw", no_argument
, NULL
, 'r' },
2126 { "threads", no_argument
, NULL
, 'l' },
2127 { "notruncate", no_argument
, NULL
, 'u' },
2128 { "pid", required_argument
, NULL
, 'p' },
2129 { "inet", optional_argument
, NULL
, 'i' },
2130 { "filter", required_argument
, NULL
, 'Q' },
2131 { "debug-filter",no_argument
, NULL
, OPT_DEBUG_FILTER
},
2132 { "summary", optional_argument
, NULL
, OPT_SUMMARY
},
2133 { "counter", required_argument
, NULL
, 'C' },
2134 { "dump-counters",no_argument
, NULL
, OPT_DUMP_COUNTERS
},
2135 { "list-columns",no_argument
, NULL
, 'H' },
2136 { NULL
, 0, NULL
, 0 },
2139 setlocale(LC_ALL
, "");
2140 bindtextdomain(PACKAGE
, LOCALEDIR
);
2141 textdomain(PACKAGE
);
2142 close_stdout_atexit();
2144 while ((c
= getopt_long(argc
, argv
, "no:JrVhluQ:p:i::C:sH", longopts
, NULL
)) != -1) {
2165 parse_pids(optarg
, &pids
, &n_pids
);
2168 const char *subexpr
= NULL
;
2170 ctl
.sockets_only
= 1;
2172 subexpr
= inet46_subexpr
;
2173 else if (strcmp(optarg
, "4") == 0)
2174 subexpr
= inet4_subexpr
;
2175 else if (strcmp(optarg
, "6") == 0)
2176 subexpr
= inet6_subexpr
;
2179 _("unknown -i/--inet argument: %s"),
2182 append_filter_expr(&filter_expr
, subexpr
, true);
2186 append_filter_expr(&filter_expr
, optarg
, true);
2189 struct counter_spec
*c
= new_counter_spec(optarg
);
2190 list_add_tail(&c
->specs
, &counter_specs
);
2193 case OPT_DEBUG_FILTER
:
2194 debug_filter
= true;
2198 if (strcmp(optarg
, "never") == 0)
2199 ctl
.show_summary
= 0, ctl
.show_main
= 1;
2200 else if (strcmp(optarg
, "only") == 0)
2201 ctl
.show_summary
= 1, ctl
.show_main
= 0;
2202 else if (strcmp(optarg
, "append") == 0)
2203 ctl
.show_summary
= 1, ctl
.show_main
= 1;
2205 errx(EXIT_FAILURE
, _("unsupported --summary argument"));
2207 ctl
.show_summary
= 1, ctl
.show_main
= 0;
2209 case OPT_DUMP_COUNTERS
:
2210 dump_counters
= true;
2213 print_version(EXIT_SUCCESS
);
2217 list_colunms(stdout
);
2219 errtryhelp(EXIT_FAILURE
);
2223 errtryhelp(EXIT_FAILURE
);
2225 #define INITIALIZE_COLUMNS(COLUMN_SPEC) \
2226 for (i = 0; i < ARRAY_SIZE(COLUMN_SPEC); i++) \
2227 columns[ncolumns++] = COLUMN_SPEC[i]
2230 INITIALIZE_COLUMNS(default_threads_columns
);
2232 INITIALIZE_COLUMNS(default_columns
);
2235 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
2236 &ncolumns
, column_name_to_id
) < 0)
2237 return EXIT_FAILURE
;
2239 scols_init_debug(0);
2241 INIT_LIST_HEAD(&ctl
.procs
);
2243 /* inilialize scols table */
2244 ctl
.tb
= scols_new_table();
2246 err(EXIT_FAILURE
, _("failed to allocate output table"));
2248 scols_table_enable_noheadings(ctl
.tb
, ctl
.noheadings
);
2249 scols_table_enable_raw(ctl
.tb
, ctl
.raw
);
2250 scols_table_enable_json(ctl
.tb
, ctl
.json
);
2252 scols_table_set_name(ctl
.tb
, "lsfd");
2254 /* create output columns */
2255 for (i
= 0; i
< ncolumns
; i
++) {
2256 const struct colinfo
*col
= get_column_info(i
);
2257 struct libscols_column
*cl
= add_column(ctl
.tb
, col
);
2260 err(EXIT_FAILURE
, _("failed to allocate output column"));
2263 int flags
= scols_column_get_flags(cl
);
2264 flags
&= ~SCOLS_FL_TRUNC
;
2265 scols_column_set_flags(cl
, flags
);
2271 ctl
.filter
= new_filter(filter_expr
, debug_filter
, "", &ctl
);
2275 if (dump_counters
) {
2276 if (list_empty(&counter_specs
))
2277 dump_default_counter_specs();
2279 dump_counter_specs(&counter_specs
);
2284 if (ctl
.show_summary
) {
2285 if (list_empty(&counter_specs
))
2286 ctl
.counters
= new_default_counters(&ctl
);
2288 ctl
.counters
= new_counters(&counter_specs
, &ctl
);
2289 list_free(&counter_specs
, struct counter_spec
, specs
,
2295 sort_pids(pids
, n_pids
);
2297 if (scols_table_get_column_by_name(ctl
.tb
, "XMODE"))
2302 * The call initialize_ipc_table() must come before
2303 * initialize_classes.
2305 initialize_nodevs();
2306 initialize_ipc_table();
2307 initialize_classes();
2308 initialize_devdrvs();
2310 collect_processes(&ctl
, pids
, n_pids
);
2313 attach_xinfos(&ctl
.procs
);
2315 set_multiplexed_flags(&ctl
.procs
);
2318 convert(&ctl
.procs
, &ctl
);
2324 if (ctl
.show_summary
&& ctl
.counters
)
2325 emit_summary(&ctl
, ctl
.counters
);
2328 delete(&ctl
.procs
, &ctl
);
2332 finalize_ipc_table();