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 <linux/sched.h>
36 #include <sys/syscall.h>
37 #include <linux/kcmp.h>
38 static int kcmp(pid_t pid1
, pid_t pid2
, int type
,
39 unsigned long idx1
, unsigned long idx2
)
41 return syscall(SYS_kcmp
, pid1
, pid2
, type
, idx1
, idx2
);
45 * Defined in linux/include/linux/sched.h private header file. */
46 #define PF_KTHREAD 0x00200000 /* I am a kernel thread */
52 #include "closestream.h"
55 #include "fileutils.h"
57 #include "pathnames.h"
59 #include "libsmartcols.h"
62 #include "lsfd-filter.h"
63 #include "lsfd-counter.h"
66 * /proc/$pid/mountinfo entries
69 struct list_head nodevs
;
75 #define NODEV_TABLE_SIZE 97
76 struct list_head tables
[NODEV_TABLE_SIZE
];
78 static struct nodev_table nodev_table
;
81 struct idcache
*cache
;
82 unsigned long next_id
;
86 * /proc/devices entries
89 struct list_head devdrvs
;
94 static struct list_head chrdrvs
;
95 static struct list_head blkdrvs
;
101 #define IPC_TABLE_SIZE 997
103 struct list_head tables
[IPC_TABLE_SIZE
];
106 static struct ipc_table ipc_table
;
109 * Column related stuffs
121 /* columns descriptions */
122 static const struct colinfo infos
[] = {
123 [COL_AINODECLASS
] = { "AINODECLASS",
124 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
125 N_("class of anonymous inode") },
126 [COL_ASSOC
] = { "ASSOC",
127 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
128 N_("association between file and process") },
129 [COL_BLKDRV
] = { "BLKDRV",
130 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
131 N_("block device driver name resolved by /proc/devices") },
132 [COL_CHRDRV
] = { "CHRDRV",
133 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
134 N_("character device driver name resolved by /proc/devices") },
135 [COL_COMMAND
] = { "COMMAND",
136 0.3, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
137 N_("command of the process opening the file") },
138 [COL_DELETED
] = { "DELETED",
139 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
140 N_("reachability from the file system") },
142 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
143 N_("ID of device containing file") },
144 [COL_DEVTYPE
] = { "DEVTYPE",
145 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
146 N_("device type (blk, char, or nodev)") },
147 [COL_ENDPOINTS
] = { "ENDPOINTS",
148 0, SCOLS_FL_WRAP
, SCOLS_JSON_STRING
,
149 N_("IPC endpoints information communicated with the fd") },
150 [COL_EVENTFD_ID
] = {"EVENTFD.ID",
151 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
153 [COL_EVENTPOLL_TFDS
] = {"EVENTPOLL.TFDS",
154 0, SCOLS_FL_WRAP
, SCOLS_JSON_STRING
,
155 N_("file descriptors targeted by the eventpoll file") },
157 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
158 N_("file descriptor for the file") },
159 [COL_FLAGS
] = { "FLAGS",
160 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
161 N_("flags specified when opening the file") },
162 [COL_FUID
] = { "FUID",
163 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
164 N_("user ID number of the file's owner") },
165 [COL_INET_LADDR
] = { "INET.LADDR",
166 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
167 N_("local IP address") },
168 [COL_INET_RADDR
] = { "INET.RADDR",
169 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
170 N_("remote IP address") },
171 [COL_INET6_LADDR
] = { "INET6.LADDR",
172 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
173 N_("local IPv6 address") },
174 [COL_INET6_RADDR
] = { "INET6.RADDR",
175 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
176 N_("remote IPv6 address") },
177 [COL_INODE
] = { "INODE",
178 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
179 N_("inode number") },
180 [COL_KNAME
] = { "KNAME",
181 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
182 N_("name of the file (raw)") },
183 [COL_KTHREAD
] = { "KTHREAD",
184 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
185 N_("opened by a kernel thread") },
186 [COL_MAJMIN
] = { "MAJ:MIN",
187 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
188 N_("device ID for special, or ID of device containing file") },
189 [COL_MAPLEN
] = { "MAPLEN",
190 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
191 N_("length of file mapping (in page)") },
192 [COL_MISCDEV
] = { "MISCDEV",
193 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
194 N_("misc character device name resolved by /proc/misc") },
195 [COL_MNT_ID
] = { "MNTID",
196 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
198 [COL_MODE
] = { "MODE",
199 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
200 N_("access mode (rwx)") },
201 [COL_NAME
] = { "NAME",
202 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
203 N_("name of the file (cooked)") },
204 [COL_NETLINK_GROUPS
] = { "NETLINK.GROUPS",
205 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
206 N_("netlink multicast groups") },
207 [COL_NETLINK_LPORT
] = { "NETLINK.LPORT",
208 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
209 N_("netlink local port id") },
210 [COL_NETLINK_PROTOCOL
] = { "NETLINK.PROTOCOL",
211 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
212 N_("netlink protocol") },
213 [COL_NLINK
] = { "NLINK",
214 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
216 [COL_NS_NAME
] = { "NS.NAME",
217 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
218 N_("name of the namespace (NS.TYPE:[INODE])") },
219 [COL_NS_TYPE
] = { "NS.TYPE",
220 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
221 N_("type of the namespace") },
222 [COL_OWNER
] = { "OWNER",
223 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
224 N_("owner of the file") },
225 [COL_PACKET_IFACE
] = { "PACKET.IFACE",
226 0, SCOLS_FL_RIGHT
,SCOLS_JSON_STRING
,
227 N_("net interface associated with the packet socket") },
228 [COL_PACKET_PROTOCOL
] = { "PACKET.PROTOCOL",
229 0, SCOLS_FL_RIGHT
,SCOLS_JSON_STRING
,
230 N_("L3 protocol associated with the packet socket") },
231 [COL_PARTITION
] = { "PARTITION",
232 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
233 N_("block device name resolved by /proc/partition") },
235 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
236 N_("PID of the process opening the file") },
237 [COL_PIDFD_COMM
] = { "PIDFD.COMM",
238 0.2, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
239 N_("command of the process targeted by the pidfd") },
240 [COL_PIDFD_NSPID
] = { "PIDFD.NSPID",
241 0.2, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
242 N_("NSpid field in fdinfo of the pidfd") },
243 [COL_PIDFD_PID
] = { "PIDFD.PID",
244 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
245 N_("PID of the process targeted by the pidfd") },
246 [COL_PING_ID
] = { "PING.ID",
247 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
248 N_("ICMP echo request ID") },
250 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
251 N_("file position") },
252 [COL_RAW_PROTOCOL
] = { "RAW.PROTOCOL",
253 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
254 N_("protocol number of the raw socket") },
255 [COL_RDEV
] = { "RDEV",
256 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
257 N_("device ID (if special file)") },
258 [COL_SIZE
] = { "SIZE",
259 4, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
261 [COL_SOCK_LISTENING
] = { "SOCK.LISTENING",
262 0, SCOLS_FL_RIGHT
, SCOLS_JSON_BOOLEAN
,
263 N_("listening socket") },
264 [COL_SOCK_NETNS
] = { "SOCK.NETNS",
265 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
266 N_("inode identifying network namespace where the socket belongs to") },
267 [COL_SOCK_PROTONAME
] = { "SOCK.PROTONAME",
268 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
269 N_("protocol name") },
270 [COL_SOCK_STATE
] = { "SOCK.STATE",
271 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
272 N_("State of socket") },
273 [COL_SOCK_TYPE
] = { "SOCK.TYPE",
274 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
275 N_("Type of socket") },
276 [COL_SOURCE
] = { "SOURCE",
277 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
278 N_("file system, partition, or device containing file") },
279 [COL_STTYPE
] = { "STTYPE",
280 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
281 N_("file type (raw)") },
282 [COL_TCP_LADDR
] = { "TCP.LADDR",
283 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
284 N_("local TCP address (INET address:TCP port)") },
285 [COL_TCP_RADDR
] = { "TCP.RADDR",
286 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
287 N_("remote TCP address (INET address:TCP port)") },
288 [COL_TCP_LPORT
] = { "TCP.LPORT",
289 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
290 N_("local TCP port") },
291 [COL_TCP_RPORT
] = { "TCP.RPORT",
292 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
293 N_("remote TCP port") },
295 5, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
296 N_("thread ID of the process opening the file") },
297 [COL_TIMERFD_CLOCKID
] = { "TIMERFD.CLOCKID",
298 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
300 [COL_TIMERFD_INTERVAL
] = { "TIMERFD.INTERVAL",
301 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
303 [COL_TIMERFD_REMAINING
]= { "TIMERFD.REMAINING",
304 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
305 N_("remaining time") },
306 [COL_TYPE
] = { "TYPE",
307 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
308 N_("file type (cooked)") },
309 [COL_UDP_LADDR
] = { "UDP.LADDR",
310 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
311 N_("local UDP address (INET address:UDP port)") },
312 [COL_UDP_RADDR
] = { "UDP.RADDR",
313 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
314 N_("remote UDP address (INET address:UDP port)") },
315 [COL_UDP_LPORT
] = { "UDP.LPORT",
316 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
317 N_("local UDP port") },
318 [COL_UDP_RPORT
] = { "UDP.RPORT",
319 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
320 N_("remote UDP port") },
321 [COL_UDPLITE_LADDR
] = { "UDPLITE.LADDR",
322 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
323 N_("local UDPLite address (INET address:UDPLite port)") },
324 [COL_UDPLITE_RADDR
] = { "UDPLITE.RADDR",
325 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
326 N_("remote UDPLite address (INET address:UDPLite port)") },
327 [COL_UDPLITE_LPORT
] = { "UDPLITE.LPORT",
328 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
329 N_("local UDPLite port") },
330 [COL_UDPLITE_RPORT
] = { "UDPLITE.RPORT",
331 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
332 N_("remote UDPLite port") },
334 0, SCOLS_FL_RIGHT
, SCOLS_JSON_NUMBER
,
335 N_("user ID number of the process") },
336 [COL_UNIX_PATH
] = { "UNIX.PATH",
337 0.4, SCOLS_FL_TRUNC
, SCOLS_JSON_STRING
,
338 N_("filesystem pathname for UNIX domain socket") },
339 [COL_USER
] = { "USER",
340 0, SCOLS_FL_RIGHT
, SCOLS_JSON_STRING
,
341 N_("user of the process") },
344 static const int default_columns
[] = {
357 static const int default_threads_columns
[] = {
371 static int columns
[ARRAY_SIZE(infos
) * 2] = {-1};
372 static size_t ncolumns
;
374 static ino_t
*mnt_namespaces
;
375 static size_t nspaces
;
377 struct counter_spec
{
378 struct list_head specs
;
383 static const struct counter_spec default_counter_specs
[] = {
385 .name
= N_("processes"),
386 .expr
= "ASSOC == 'cwd'",
389 .name
= N_("root owned processes"),
390 .expr
= "(ASSOC == 'cwd') && (UID == 0)",
393 .name
= N_("kernel threads"),
394 .expr
= "(ASSOC == 'cwd') && KTHREAD",
397 .name
= N_("open files"),
401 .name
= N_("RO open files"),
402 .expr
= "(FD >= 0) and (MODE == 'r--')",
405 .name
= N_("WO open files"),
406 .expr
= "(FD >= 0) and (MODE == '-w-')",
409 .name
= N_("shared mappings"),
410 .expr
= "ASSOC == 'shm'",
413 .name
= N_("RO shared mappings"),
414 .expr
= "(ASSOC == 'shm') and (MODE == 'r--')",
417 .name
= N_("WO shared mappings"),
418 .expr
= "(ASSOC == 'shm') and (MODE == '-w-')",
421 .name
= N_("regular files"),
422 .expr
= "(FD >= 0) && (STTYPE == 'REG')",
425 .name
= N_("directories"),
426 .expr
= "(FD >= 0) && (STTYPE == 'DIR')",
429 .name
= N_("sockets"),
430 .expr
= "(FD >= 0) && (STTYPE == 'SOCK')",
433 .name
= N_("fifos/pipes"),
434 .expr
= "(FD >= 0) && (STTYPE == 'FIFO')",
437 .name
= N_("character devices"),
438 .expr
= "(FD >= 0) && (STTYPE == 'CHR')",
441 .name
= N_("block devices"),
442 .expr
= "(FD >= 0) && (STTYPE == 'BLK')",
445 .name
= N_("unknown types"),
446 .expr
= "(FD >= 0) && (STTYPE == 'UNKN')",
450 struct lsfd_control
{
451 struct libscols_table
*tb
; /* output */
452 struct list_head procs
; /* list of all processes */
454 unsigned int noheadings
: 1,
459 show_main
: 1, /* print main table */
460 show_summary
: 1, /* print summary/counters */
461 sockets_only
: 1; /* display only SOCKETS */
463 struct lsfd_filter
*filter
;
464 struct lsfd_counter
**counters
; /* NULL terminated array. */
467 static void *proc_tree
; /* for tsearch/tfind */
469 static int proc_tree_compare(const void *a
, const void *b
)
471 return ((struct proc
*)a
)->pid
- ((struct proc
*)b
)->pid
;
474 struct proc
*get_proc(pid_t pid
)
476 struct proc
**node
= tfind(&pid
, &proc_tree
, proc_tree_compare
);
482 static int column_name_to_id(const char *name
, size_t namesz
)
486 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++) {
487 const char *cn
= infos
[i
].name
;
489 if (!strncasecmp(name
, cn
, namesz
) && !*(cn
+ namesz
))
492 warnx(_("unknown column: %s"), name
);
494 return LSFD_FILTER_UNKNOWN_COL_ID
;
497 static int column_name_to_id_cb(const char *name
, void *data
__attribute__((__unused__
)))
499 return column_name_to_id(name
, strlen(name
));
502 static int get_column_id(int num
)
505 assert((size_t) num
< ncolumns
);
506 assert(columns
[num
] < (int) ARRAY_SIZE(infos
));
511 static const struct colinfo
*get_column_info(int num
)
513 return &infos
[ get_column_id(num
) ];
516 static struct libscols_column
*add_column(struct libscols_table
*tb
, const struct colinfo
*col
)
518 struct libscols_column
*cl
;
519 int flags
= col
->flags
;
521 cl
= scols_table_new_column(tb
, col
->name
, col
->whint
, flags
);
523 scols_column_set_json_type(cl
, col
->json_type
);
524 if (col
->flags
& SCOLS_FL_WRAP
) {
525 scols_column_set_wrapfunc(cl
,
526 scols_wrapnl_chunksize
,
527 scols_wrapnl_nextchunk
,
529 scols_column_set_safechars(cl
, "\n");
536 static struct libscols_column
*add_column_by_id_cb(struct libscols_table
*tb
, int colid
, void *data
)
538 struct libscols_column
*cl
;
540 if (ncolumns
>= ARRAY_SIZE(columns
))
541 errx(EXIT_FAILURE
, _("too many columns are added via filter expression"));
543 assert(colid
< LSFD_N_COLS
);
545 cl
= add_column(tb
, infos
+ colid
);
547 err(EXIT_FAILURE
, _("failed to allocate output column"));
548 columns
[ncolumns
++] = colid
;
550 if (colid
== COL_TID
) {
551 struct lsfd_control
*ctl
= data
;
558 static int has_mnt_ns(ino_t id
)
562 for (i
= 0; i
< nspaces
; i
++) {
563 if (mnt_namespaces
[i
] == id
)
569 static void add_mnt_ns(ino_t id
)
574 nmax
= (nspaces
+ 16) / 16 * 16;
575 if (nmax
<= nspaces
+ 1) {
577 mnt_namespaces
= xrealloc(mnt_namespaces
,
578 sizeof(ino_t
) * nmax
);
580 mnt_namespaces
[nspaces
++] = id
;
583 static const struct file_class
*stat2class(struct stat
*sb
)
589 switch (sb
->st_mode
& S_IFMT
) {
606 if (is_nsfs_dev(dev
))
607 return &nsfs_file_class
;
609 if (is_mqueue_dev(dev
))
610 return &mqueue_file_class
;
620 static struct file
*new_file(struct proc
*proc
, const struct file_class
*class)
625 file
= xcalloc(1, class->size
);
630 INIT_LIST_HEAD(&file
->files
);
631 list_add_tail(&file
->files
, &proc
->files
);
636 static struct file
*copy_file(struct file
*old
)
638 struct file
*file
= xcalloc(1, old
->class->size
);
640 INIT_LIST_HEAD(&file
->files
);
641 file
->proc
= old
->proc
;
642 list_add_tail(&file
->files
, &old
->proc
->files
);
644 file
->class = old
->class;
645 file
->association
= old
->association
;
646 file
->name
= xstrdup(old
->name
);
647 file
->stat
= old
->stat
;
652 static void file_set_path(struct file
*file
, struct stat
*sb
, const char *name
, int association
)
654 file
->association
= association
;
655 file
->name
= xstrdup(name
);
659 static void file_init_content(struct file
*file
)
661 if (file
->class && file
->class->initialize_content
)
662 file
->class->initialize_content(file
);
665 static void free_file(struct file
*file
)
667 const struct file_class
*class = file
->class;
670 if (class->free_content
)
671 class->free_content(file
);
672 class = class->super
;
678 static struct proc
*new_process(pid_t pid
, struct proc
*leader
)
680 struct proc
*proc
= xcalloc(1, sizeof(*proc
));
683 proc
->leader
= leader
? leader
: proc
;
684 proc
->command
= NULL
;
686 INIT_LIST_HEAD(&proc
->files
);
687 INIT_LIST_HEAD(&proc
->procs
);
693 static void free_proc(struct proc
*proc
)
695 list_free(&proc
->files
, struct file
, files
, free_file
);
701 static void read_fdinfo(struct file
*file
, FILE *fdinfo
)
705 while (fgets(buf
, sizeof(buf
), fdinfo
)) {
706 const struct file_class
*class;
707 char *val
= strchr(buf
, ':');
711 *val
++ = '\0'; /* terminate key */
713 val
= (char *) skip_space(val
);
714 rtrim_whitespace((unsigned char *) val
);
718 if (class->handle_fdinfo
719 && class->handle_fdinfo(file
, buf
, val
))
721 class = class->super
;
726 static struct file
*collect_file_symlink(struct path_cxt
*pc
,
732 char sym
[PATH_MAX
] = { '\0' };
734 struct file
*f
, *prev
;
736 if (ul_path_readlink(pc
, sym
, sizeof(sym
), name
) < 0)
739 /* The /proc/#/{fd,ns} often contains the same file (e.g. /dev/tty)
740 * more than once. Let's try to reuse the previous file if the real
741 * path is the same to save stat() call.
743 prev
= list_last_entry(&proc
->files
, struct file
, files
);
744 if (prev
&& prev
->name
&& strcmp(prev
->name
, sym
) == 0) {
746 f
->association
= assoc
;
748 const struct file_class
*class;
750 if (ul_path_stat(pc
, &sb
, 0, name
) < 0)
753 class = stat2class(&sb
);
755 /* A nsfs file is not a socket but the nsfs file can
756 * be used as a entry point to collect information from
757 * other network namespaces. Besed on the information,
758 * various columns of sockets can be filled.
760 && (class != &sock_class
) && (class != &nsfs_file_class
))
762 f
= new_file(proc
, class);
763 file_set_path(f
, &sb
, sym
, assoc
);
766 file_init_content(f
);
768 if (is_association(f
, NS_MNT
))
769 proc
->ns_mnt
= f
->stat
.st_ino
;
770 else if (is_association(f
, NS_NET
))
771 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
773 else if (assoc
>= 0) {
774 /* file-descriptor based association */
777 if (ul_path_stat(pc
, &sb
, AT_SYMLINK_NOFOLLOW
, name
) == 0)
778 f
->mode
= sb
.st_mode
;
780 if (is_nsfs_dev(f
->stat
.st_dev
))
781 load_sock_xinfo(pc
, name
, f
->stat
.st_ino
);
783 fdinfo
= ul_path_fopenf(pc
, "r", "fdinfo/%d", assoc
);
785 read_fdinfo(f
, fdinfo
);
793 /* read symlinks from /proc/#/fd
795 static void collect_fd_files(struct path_cxt
*pc
, struct proc
*proc
,
799 struct dirent
*d
= NULL
;
800 char path
[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX
))];
802 while (ul_path_next_dirent(pc
, &sub
, "fd", &d
) == 0) {
805 if (ul_strtou64(d
->d_name
, &num
, 10) != 0) /* only numbers */
808 snprintf(path
, sizeof(path
), "fd/%ju", (uintmax_t) num
);
809 collect_file_symlink(pc
, proc
, path
, num
, sockets_only
);
813 static void parse_maps_line(struct path_cxt
*pc
, char *buf
, struct proc
*proc
)
815 uint64_t start
, end
, offset
, ino
;
816 unsigned long major
, minor
;
817 enum association assoc
= ASSOC_MEM
;
819 struct file
*f
, *prev
;
820 char *path
, modestr
[5];
823 /* read rest of the map */
824 if (sscanf(buf
, "%"SCNx64
/* start */
827 " %"SCNx64
/* offset */
828 " %lx:%lx" /* maj:min */
829 " %"SCNu64
, /* inode */
831 &start
, &end
, modestr
, &offset
,
832 &major
, &minor
, &ino
) != 7)
835 /* Skip private anonymous mappings. */
836 if (major
== 0 && minor
== 0 && ino
== 0)
839 devno
= makedev(major
, minor
);
841 if (modestr
[3] == 's')
844 /* The map usually contains the same file more than once, try to reuse
845 * the previous file (if devno and ino are the same) to save stat() call.
847 prev
= list_last_entry(&proc
->files
, struct file
, files
);
849 if (prev
&& prev
->stat
.st_dev
== devno
&& prev
->stat
.st_ino
== ino
) {
851 f
->association
= -assoc
;
852 } else if ((path
= strchr(buf
, '/'))) {
853 rtrim_whitespace((unsigned char *) path
);
854 if (stat(path
, &sb
) < 0)
855 /* If a file is mapped but deleted from the file system,
856 * "stat by the file name" may not work. In that case,
859 f
= new_file(proc
, stat2class(&sb
));
860 file_set_path(f
, &sb
, path
, -assoc
);
862 /* As used in tcpdump, AF_PACKET socket can be mmap'ed. */
863 char map_file
[sizeof("map_files/0000000000000000-ffffffffffffffff")];
864 char sym
[PATH_MAX
] = { '\0' };
867 snprintf(map_file
, sizeof(map_file
), "map_files/%"PRIx64
"-%"PRIx64
, start
, end
);
868 if (ul_path_stat(pc
, &sb
, 0, map_file
) < 0)
870 if (ul_path_readlink(pc
, sym
, sizeof(sym
), map_file
) < 0)
872 f
= new_file(proc
, stat2class(&sb
));
873 file_set_path(f
, &sb
, sym
, -assoc
);
876 if (modestr
[0] == 'r')
878 if (modestr
[1] == 'w')
880 if (modestr
[2] == 'x')
883 f
->map_start
= start
;
887 file_init_content(f
);
890 static void collect_mem_files(struct path_cxt
*pc
, struct proc
*proc
)
895 fp
= ul_path_fopen(pc
, "r", "maps");
899 while (fgets(buf
, sizeof(buf
), fp
))
900 parse_maps_line(pc
, buf
, proc
);
905 static void collect_outofbox_files(struct path_cxt
*pc
,
907 enum association assocs
[],
914 for (i
= 0; i
< count
; i
++)
915 collect_file_symlink(pc
, proc
, names
[assocs
[i
]], assocs
[i
] * -1,
919 static void collect_execve_file(struct path_cxt
*pc
, struct proc
*proc
,
922 enum association assocs
[] = { ASSOC_EXE
};
923 const char *names
[] = {
926 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
930 static void collect_fs_files(struct path_cxt
*pc
, struct proc
*proc
,
933 enum association assocs
[] = { ASSOC_EXE
, ASSOC_CWD
, ASSOC_ROOT
};
934 const char *names
[] = {
936 [ASSOC_ROOT
] = "root",
938 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
942 static void collect_namespace_files(struct path_cxt
*pc
, struct proc
*proc
)
944 enum association assocs
[] = {
956 const char *names
[] = {
957 [ASSOC_NS_CGROUP
] = "ns/cgroup",
958 [ASSOC_NS_IPC
] = "ns/ipc",
959 [ASSOC_NS_MNT
] = "ns/mnt",
960 [ASSOC_NS_NET
] = "ns/net",
961 [ASSOC_NS_PID
] = "ns/pid",
962 [ASSOC_NS_PID4C
] = "ns/pid_for_children",
963 [ASSOC_NS_TIME
] = "ns/time",
964 [ASSOC_NS_TIME4C
] = "ns/time_for_children",
965 [ASSOC_NS_USER
] = "ns/user",
966 [ASSOC_NS_UTS
] = "ns/uts",
968 collect_outofbox_files(pc
, proc
, assocs
, names
, ARRAY_SIZE(assocs
),
969 /* Namespace information is alwasys needed. */
973 static struct nodev
*new_nodev(unsigned long minor
, const char *filesystem
)
975 struct nodev
*nodev
= xcalloc(1, sizeof(*nodev
));
977 INIT_LIST_HEAD(&nodev
->nodevs
);
978 nodev
->minor
= minor
;
979 nodev
->filesystem
= xstrdup(filesystem
);
984 static void free_nodev(struct nodev
*nodev
)
986 free(nodev
->filesystem
);
990 void add_nodev(unsigned long minor
, const char *filesystem
)
992 struct nodev
*nodev
= new_nodev(minor
, filesystem
);
993 unsigned long slot
= nodev
->minor
% NODEV_TABLE_SIZE
;
995 list_add_tail(&nodev
->nodevs
, &nodev_table
.tables
[slot
]);
998 static void initialize_nodevs(void)
1002 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1003 INIT_LIST_HEAD(&nodev_table
.tables
[i
]);
1006 static void finalize_nodevs(void)
1010 for (i
= 0; i
< NODEV_TABLE_SIZE
; i
++)
1011 list_free(&nodev_table
.tables
[i
], struct nodev
, nodevs
, free_nodev
);
1013 free(mnt_namespaces
);
1016 const char *get_nodev_filesystem(unsigned long minor
)
1018 struct list_head
*n
;
1019 int slot
= minor
% NODEV_TABLE_SIZE
;
1021 list_for_each (n
, &nodev_table
.tables
[slot
]) {
1022 struct nodev
*nodev
= list_entry(n
, struct nodev
, nodevs
);
1023 if (nodev
->minor
== minor
)
1024 return nodev
->filesystem
;
1029 static void add_nodevs(FILE *mnt
)
1031 /* This can be very long. A line in mountinfo can have more than 3
1033 char line
[PATH_MAX
* 3 + 256];
1035 while (fgets(line
, sizeof(line
), mnt
)) {
1036 unsigned long major
, minor
;
1037 char filesystem
[256];
1039 /* 23 61 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel */
1040 if(sscanf(line
, "%*d %*d %lu:%lu %*s %*s %*s %*[^-] - %s %*[^\n]",
1041 &major
, &minor
, filesystem
) != 3)
1042 /* 1600 1458 0:55 / / rw,nodev,relatime - overlay overlay rw,context="s... */
1043 if (sscanf(line
, "%*d %*d %lu:%lu %*s %*s %*s - %s %*[^\n]",
1044 &major
, &minor
, filesystem
) != 3)
1049 if (get_nodev_filesystem(minor
))
1052 add_nodev(minor
, filesystem
);
1056 static void initialize_ipc_table(void)
1058 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1059 INIT_LIST_HEAD(ipc_table
.tables
+ i
);
1062 static void free_ipc(struct ipc
*ipc
)
1064 if (ipc
->class->free
)
1065 ipc
->class->free(ipc
);
1069 static void finalize_ipc_table(void)
1071 for (int i
= 0; i
< IPC_TABLE_SIZE
; i
++)
1072 list_free(&ipc_table
.tables
[i
], struct ipc
, ipcs
, free_ipc
);
1075 struct ipc
*new_ipc(const struct ipc_class
*class)
1077 struct ipc
*ipc
= xcalloc(1, class->size
);
1079 INIT_LIST_HEAD(&ipc
->endpoints
);
1080 INIT_LIST_HEAD(&ipc
->ipcs
);
1084 struct ipc
*get_ipc(struct file
*file
)
1087 struct list_head
*e
;
1088 const struct ipc_class
*ipc_class
;
1090 if (!file
->class->get_ipc_class
)
1093 ipc_class
= file
->class->get_ipc_class(file
);
1097 slot
= ipc_class
->get_hash(file
) % IPC_TABLE_SIZE
;
1098 list_for_each (e
, &ipc_table
.tables
[slot
]) {
1099 struct ipc
*ipc
= list_entry(e
, struct ipc
, ipcs
);
1100 if (ipc
->class != ipc_class
)
1102 if (ipc_class
->is_suitable_ipc(ipc
, file
))
1108 void add_ipc(struct ipc
*ipc
, unsigned int hash
)
1110 int slot
= hash
% IPC_TABLE_SIZE
;
1111 list_add(&ipc
->ipcs
, &ipc_table
.tables
[slot
]);
1114 void init_endpoint(struct ipc_endpoint
*endpoint
)
1116 INIT_LIST_HEAD(&endpoint
->endpoints
);
1119 void add_endpoint(struct ipc_endpoint
*endpoint
, struct ipc
*ipc
)
1121 endpoint
->ipc
= ipc
;
1122 list_add(&endpoint
->endpoints
, &ipc
->endpoints
);
1125 static void fill_column(struct proc
*proc
,
1127 struct libscols_line
*ln
,
1129 size_t column_index
)
1131 const struct file_class
*class = file
->class;
1134 if (class->fill_column
1135 && class->fill_column(proc
, file
, ln
,
1136 column_id
, column_index
))
1138 class = class->super
;
1142 static void convert_file(struct proc
*proc
,
1144 struct libscols_line
*ln
)
1149 for (i
= 0; i
< ncolumns
; i
++)
1150 fill_column(proc
, file
, ln
, get_column_id(i
), i
);
1153 static void convert(struct list_head
*procs
, struct lsfd_control
*ctl
)
1155 struct list_head
*p
;
1157 list_for_each (p
, procs
) {
1158 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1159 struct list_head
*f
;
1161 list_for_each (f
, &proc
->files
) {
1162 struct file
*file
= list_entry(f
, struct file
, files
);
1163 struct libscols_line
*ln
= scols_table_new_line(ctl
->tb
, NULL
);
1164 struct lsfd_counter
**counter
= NULL
;
1167 err(EXIT_FAILURE
, _("failed to allocate output line"));
1169 convert_file(proc
, file
, ln
);
1171 if (!lsfd_filter_apply(ctl
->filter
, ln
)) {
1172 scols_table_remove_line(ctl
->tb
, ln
);
1179 for (counter
= ctl
->counters
; *counter
; counter
++)
1180 lsfd_counter_accumulate(*counter
, ln
);
1185 static void delete(struct list_head
*procs
, struct lsfd_control
*ctl
)
1187 struct list_head
*p
;
1189 list_for_each (p
, procs
) {
1190 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1191 tdelete(proc
, &proc_tree
, proc_tree_compare
);
1193 list_free(procs
, struct proc
, procs
, free_proc
);
1195 scols_unref_table(ctl
->tb
);
1196 lsfd_filter_free(ctl
->filter
);
1197 if (ctl
->counters
) {
1198 struct lsfd_counter
**counter
;
1199 for (counter
= ctl
->counters
; *counter
; counter
++)
1200 lsfd_counter_free(*counter
);
1201 free(ctl
->counters
);
1205 static void emit(struct lsfd_control
*ctl
)
1207 scols_print_table(ctl
->tb
);
1211 static void initialize_class(const struct file_class
*class)
1213 if (class->initialize_class
)
1214 class->initialize_class();
1217 static void initialize_classes(void)
1219 initialize_class(&file_class
);
1220 initialize_class(&cdev_class
);
1221 initialize_class(&bdev_class
);
1222 initialize_class(&sock_class
);
1223 initialize_class(&unkn_class
);
1226 static void finalize_class(const struct file_class
*class)
1228 if (class->finalize_class
)
1229 class->finalize_class();
1232 static void finalize_classes(void)
1234 finalize_class(&file_class
);
1235 finalize_class(&cdev_class
);
1236 finalize_class(&bdev_class
);
1237 finalize_class(&sock_class
);
1238 finalize_class(&unkn_class
);
1241 static struct devdrv
*new_devdrv(unsigned long major
, const char *name
)
1243 struct devdrv
*devdrv
= xcalloc(1, sizeof(*devdrv
));
1245 INIT_LIST_HEAD(&devdrv
->devdrvs
);
1247 devdrv
->major
= major
;
1248 devdrv
->name
= xstrdup(name
);
1253 static void free_devdrv(struct devdrv
*devdrv
)
1259 #define READ_DEVICES_LINE_LEN 256
1260 static struct devdrv
*read_devdrv(const char *line
)
1262 unsigned long major
;
1263 char name
[READ_DEVICES_LINE_LEN
];
1265 if (sscanf(line
, "%lu %s", &major
, name
) != 2)
1268 return new_devdrv(major
, name
);
1271 static void read_devices(struct list_head
*chrdrvs_list
,
1272 struct list_head
*blkdrvs_list
, FILE *devices_fp
)
1274 char line
[READ_DEVICES_LINE_LEN
];
1276 /* Skip to the line "Character devices:". */
1277 while (fgets(line
, sizeof(line
), devices_fp
)) {
1283 while (fgets(line
, sizeof(line
), devices_fp
)) {
1284 /* Find the blank line before "Block devices:" line. */
1285 if (line
[0] == '\n')
1288 /* Read the character device drivers */
1289 struct devdrv
*devdrv
= read_devdrv(line
);
1291 list_add_tail(&devdrv
->devdrvs
, chrdrvs_list
);
1294 /* Skip to the line "Block devices:". */
1295 while (fgets(line
, sizeof(line
), devices_fp
)) {
1301 /* Read the block device drivers */
1302 while (fgets(line
, sizeof(line
), devices_fp
)) {
1303 struct devdrv
*devdrv
= read_devdrv(line
);
1305 list_add_tail(&devdrv
->devdrvs
, blkdrvs_list
);
1309 static void initialize_devdrvs(void)
1313 INIT_LIST_HEAD(&chrdrvs
);
1314 INIT_LIST_HEAD(&blkdrvs
);
1316 devices_fp
= fopen("/proc/devices", "r");
1318 read_devices(&chrdrvs
, &blkdrvs
, devices_fp
);
1323 static void finalize_devdrvs(void)
1325 list_free(&blkdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1326 list_free(&chrdrvs
, struct devdrv
, devdrvs
, free_devdrv
);
1329 static const char *get_devdrv(struct list_head
*devdrvs_list
, unsigned long major
)
1331 struct list_head
*c
;
1332 list_for_each(c
, devdrvs_list
) {
1333 struct devdrv
*devdrv
= list_entry(c
, struct devdrv
, devdrvs
);
1334 if (devdrv
->major
== major
)
1335 return devdrv
->name
;
1340 const char *get_chrdrv(unsigned long major
)
1342 return get_devdrv(&chrdrvs
, major
);
1345 const char *get_blkdrv(unsigned long major
)
1347 return get_devdrv(&blkdrvs
, major
);
1350 struct name_manager
*new_name_manager(void)
1352 struct name_manager
*nm
= xcalloc(1, sizeof(struct name_manager
));
1354 nm
->cache
= new_idcache();
1356 err(EXIT_FAILURE
, _("failed to allocate an idcache"));
1358 nm
->next_id
= 1; /* 0 is never issued as id. */
1362 void free_name_manager(struct name_manager
*nm
)
1364 free_idcache(nm
->cache
);
1368 const char *get_name(struct name_manager
*nm
, unsigned long id
)
1372 e
= get_id(nm
->cache
, id
);
1374 return e
? e
->name
: NULL
;
1377 unsigned long add_name(struct name_manager
*nm
, const char *name
)
1379 struct identry
*e
= NULL
, *tmp
;
1381 for (tmp
= nm
->cache
->ent
; tmp
; tmp
= tmp
->next
) {
1382 if (strcmp(tmp
->name
, name
) == 0) {
1391 e
= xmalloc(sizeof(struct identry
));
1392 e
->name
= xstrdup(name
);
1393 e
->id
= nm
->next_id
++;
1394 e
->next
= nm
->cache
->ent
;
1400 static void read_process(struct lsfd_control
*ctl
, struct path_cxt
*pc
,
1401 pid_t pid
, struct proc
*leader
)
1406 if (procfs_process_init_path(pc
, pid
) != 0)
1409 proc
= new_process(pid
, leader
);
1410 proc
->command
= procfs_process_get_cmdname(pc
, buf
, sizeof(buf
)) > 0 ?
1411 xstrdup(buf
) : xstrdup(_("(unknown)"));
1412 procfs_process_get_uid(pc
, &proc
->uid
);
1414 if (procfs_process_get_stat(pc
, buf
, sizeof(buf
)) > 0) {
1419 /* See proc(5) about the column in the line. */
1420 xstrappend(&pat
, "%*d (");
1421 for (p
= proc
->command
; *p
!= '\0'; p
++) {
1423 xstrappend(&pat
, "%%");
1427 xstrappend(&pat
, ") %*c %*d %*d %*d %*d %*d %u %*[^\n]");
1428 if (sscanf(buf
, pat
, &flags
) == 1)
1429 proc
->kthread
= !!(flags
& PF_KTHREAD
);
1433 collect_execve_file(pc
, proc
, ctl
->sockets_only
);
1435 if (proc
->pid
== proc
->leader
->pid
1436 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_FS
, 0, 0) != 0)
1437 collect_fs_files(pc
, proc
, ctl
->sockets_only
);
1439 if (proc
->ns_mnt
== 0 || !has_mnt_ns(proc
->ns_mnt
)) {
1440 FILE *mnt
= ul_path_fopen(pc
, "r", "mountinfo");
1444 add_mnt_ns(proc
->ns_mnt
);
1449 collect_namespace_files(pc
, proc
);
1451 /* If kcmp is not available,
1452 * there is no way to no whether threads share resources.
1453 * In such cases, we must pay the costs: call collect_mem_files()
1454 * and collect_fd_files().
1456 if ((!ctl
->sockets_only
)
1457 && (proc
->pid
== proc
->leader
->pid
1458 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_VM
, 0, 0) != 0))
1459 collect_mem_files(pc
, proc
);
1461 if (proc
->pid
== proc
->leader
->pid
1462 || kcmp(proc
->leader
->pid
, proc
->pid
, KCMP_FILES
, 0, 0) != 0)
1463 collect_fd_files(pc
, proc
, ctl
->sockets_only
);
1465 list_add_tail(&proc
->procs
, &ctl
->procs
);
1466 if (tsearch(proc
, &proc_tree
, proc_tree_compare
) == NULL
)
1467 errx(EXIT_FAILURE
, _("failed to allocate memory"));
1469 /* The tasks collecting overwrites @pc by /proc/<task-pid>/. Keep it as
1470 * the last path based operation in read_process()
1472 if (ctl
->threads
&& leader
== NULL
) {
1476 while (procfs_process_next_tid(pc
, &sub
, &tid
) == 0) {
1479 read_process(ctl
, pc
, tid
, proc
);
1483 /* Let's be careful with number of open files */
1484 ul_path_close_dirfd(pc
);
1487 static void parse_pids(const char *str
, pid_t
**pids
, int *count
)
1496 v
= strtol(str
, &next
, 10);
1498 err(EXIT_FAILURE
, _("unexpected value for pid specification: %s"), str
);
1500 errx(EXIT_FAILURE
, _("garbage at the end of pid specification: %s"), str
);
1502 errx(EXIT_FAILURE
, _("out of range value for pid specification: %ld"), v
);
1505 *pids
= xrealloc(*pids
, (*count
) * sizeof(**pids
));
1506 (*pids
)[*count
- 1] = (pid_t
)v
;
1508 while (next
&& *next
!= '\0'
1509 && (isspace((unsigned char)*next
) || *next
== ','))
1512 parse_pids(next
, pids
, count
);
1515 static int pidcmp(const void *a
, const void *b
)
1517 pid_t pa
= *(pid_t
*)a
;
1518 pid_t pb
= *(pid_t
*)b
;
1528 static void sort_pids(pid_t pids
[], const int count
)
1530 qsort(pids
, count
, sizeof(pid_t
), pidcmp
);
1533 static bool member_pids(const pid_t pid
, const pid_t pids
[], const int count
)
1535 return bsearch(&pid
, pids
, count
, sizeof(pid_t
), pidcmp
)? true: false;
1538 static void collect_processes(struct lsfd_control
*ctl
, const pid_t pids
[], int n_pids
)
1542 struct path_cxt
*pc
= NULL
;
1544 pc
= ul_new_path(NULL
);
1546 err(EXIT_FAILURE
, _("failed to alloc procfs handler"));
1548 dir
= opendir(_PATH_PROC
);
1550 err(EXIT_FAILURE
, _("failed to open /proc"));
1552 while ((d
= readdir(dir
))) {
1555 if (procfs_dirent_get_pid(d
, &pid
) != 0)
1557 if (n_pids
== 0 || member_pids(pid
, pids
, n_pids
))
1558 read_process(ctl
, pc
, pid
, 0);
1565 static void __attribute__((__noreturn__
)) usage(void)
1570 fputs(USAGE_HEADER
, out
);
1571 fprintf(out
, _(" %s [options]\n"), program_invocation_short_name
);
1573 fputs(USAGE_OPTIONS
, out
);
1574 fputs(_(" -l, --threads list in threads level\n"), out
);
1575 fputs(_(" -J, --json use JSON output format\n"), out
);
1576 fputs(_(" -n, --noheadings don't print headings\n"), out
);
1577 fputs(_(" -o, --output <list> output columns\n"), out
);
1578 fputs(_(" -r, --raw use raw output format\n"), out
);
1579 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out
);
1580 fputs(_(" -p, --pid <pid(s)> collect information only specified processes\n"), out
);
1581 fputs(_(" -i[4|6], --inet[=4|6] list only IPv4 and/or IPv6 sockets\n"), out
);
1582 fputs(_(" -Q, --filter <expr> apply display filter\n"), out
);
1583 fputs(_(" --debug-filter dump the internal data structure of filter and exit\n"), out
);
1584 fputs(_(" -C, --counter <name>:<expr>\n"
1585 " define custom counter for --summary output\n"), out
);
1586 fputs(_(" --dump-counters dump counter definitions\n"), out
);
1587 fputs(_(" --summary[=<when>] print summary information (only, append, or never)\n"), out
);
1589 fputs(USAGE_SEPARATOR
, out
);
1590 printf(USAGE_HELP_OPTIONS(30));
1592 fprintf(out
, USAGE_COLUMNS
);
1594 for (i
= 0; i
< ARRAY_SIZE(infos
); i
++)
1595 fprintf(out
, " %16s %-10s%s\n", infos
[i
].name
,
1596 infos
[i
].json_type
== SCOLS_JSON_STRING
? "<string>":
1597 infos
[i
].json_type
== SCOLS_JSON_NUMBER
? "<number>":
1601 printf(USAGE_MAN_TAIL("lsfd(1)"));
1606 static void append_filter_expr(char **a
, const char *b
, bool and)
1620 xstrappend(a
, "and(");
1622 xstrappend(a
, "or(");
1627 static struct lsfd_filter
*new_filter(const char *expr
, bool debug
, const char *err_prefix
, struct lsfd_control
*ctl
)
1629 struct lsfd_filter
*filter
;
1632 filter
= lsfd_filter_new(expr
, ctl
->tb
,
1634 column_name_to_id_cb
,
1635 add_column_by_id_cb
, ctl
);
1636 errmsg
= lsfd_filter_get_errmsg(filter
);
1638 errx(EXIT_FAILURE
, "%s%s", err_prefix
, errmsg
);
1640 lsfd_filter_dump(filter
, stdout
);
1647 static struct counter_spec
*new_counter_spec(const char *spec_str
)
1650 struct counter_spec
*spec
;
1652 if (spec_str
[0] == '\0')
1654 _("too short counter specification: -C/--counter %s"),
1656 if (spec_str
[0] == ':')
1658 _("no name for counter: -C/--counter %s"),
1661 sep
= strchr(spec_str
, ':');
1664 _("no name for counter: -C/--counter %s"),
1668 _("empty counter expression given: -C/--counter %s"),
1671 /* Split the spec_str in to name and expr. */
1674 if (strchr(spec_str
, '{'))
1676 _("don't use `{' in the name of a counter: %s"),
1679 spec
= xmalloc(sizeof(struct counter_spec
));
1680 INIT_LIST_HEAD(&spec
->specs
);
1681 spec
->name
= spec_str
;
1682 spec
->expr
= sep
+ 1;
1687 static void free_counter_spec(struct counter_spec
*counter_spec
)
1692 static struct lsfd_counter
*new_counter(const struct counter_spec
*spec
, struct lsfd_control
*ctl
)
1694 struct lsfd_filter
*filter
;
1696 filter
= new_filter(spec
->expr
, false,
1697 _("failed in making filter for a counter: "),
1699 return lsfd_counter_new(spec
->name
, filter
);
1702 static struct lsfd_counter
**new_counters(struct list_head
*specs
, struct lsfd_control
*ctl
)
1704 struct lsfd_counter
**counters
;
1705 size_t len
= list_count_entries(specs
);
1707 struct list_head
*s
;
1709 counters
= xcalloc(len
+ 1, sizeof(struct lsfd_counter
*));
1710 list_for_each(s
, specs
) {
1711 struct counter_spec
*spec
= list_entry(s
, struct counter_spec
, specs
);
1712 counters
[i
++] = new_counter(spec
, ctl
);
1714 assert(counters
[len
] == NULL
);
1719 static struct lsfd_counter
**new_default_counters(struct lsfd_control
*ctl
)
1721 struct lsfd_counter
**counters
;
1722 size_t len
= ARRAY_SIZE(default_counter_specs
);
1725 counters
= xcalloc(len
+ 1, sizeof(struct lsfd_counter
*));
1726 for (i
= 0; i
< len
; i
++) {
1727 const struct counter_spec
*spec
= default_counter_specs
+ i
;
1728 counters
[i
] = new_counter(spec
, ctl
);
1730 assert(counters
[len
] == NULL
);
1735 static void dump_default_counter_specs(void)
1737 size_t len
= ARRAY_SIZE(default_counter_specs
);
1740 puts("default counter specs:");
1741 for (i
= 0; i
< len
; i
++) {
1742 const struct counter_spec
*spec
= default_counter_specs
+ i
;
1743 printf("\t%s:%s\n", spec
->name
, spec
->expr
);
1747 static void dump_counter_specs(struct list_head
*specs
)
1749 struct list_head
*s
;
1751 puts("custom counter specs:");
1752 list_for_each(s
, specs
) {
1753 struct counter_spec
*spec
= list_entry(s
, struct counter_spec
, specs
);
1754 printf("\t%s:%s\n", spec
->name
, spec
->expr
);
1758 static struct libscols_table
*new_summary_table(struct lsfd_control
*ctl
)
1760 struct libscols_table
*tb
= scols_new_table();
1762 struct libscols_column
*name_cl
, *value_cl
;
1765 err(EXIT_FAILURE
, _("failed to allocate summary table"));
1767 scols_table_enable_noheadings(tb
, ctl
->noheadings
);
1768 scols_table_enable_raw(tb
, ctl
->raw
);
1769 scols_table_enable_json(tb
, ctl
->json
);
1772 scols_table_set_name(tb
, "lsfd-summary");
1775 value_cl
= scols_table_new_column(tb
, _("VALUE"), 0, SCOLS_FL_RIGHT
);
1777 err(EXIT_FAILURE
, _("failed to allocate summary column"));
1779 scols_column_set_json_type(value_cl
, SCOLS_JSON_NUMBER
);
1781 name_cl
= scols_table_new_column(tb
, _("COUNTER"), 0, 0);
1783 err(EXIT_FAILURE
, _("failed to allocate summary column"));
1785 scols_column_set_json_type(name_cl
, SCOLS_JSON_STRING
);
1790 static void fill_summary_line(struct libscols_line
*ln
, struct lsfd_counter
*counter
)
1794 xasprintf(&str
, "%llu", (unsigned long long)lsfd_counter_value(counter
));
1796 err(EXIT_FAILURE
, _("failed to add summary data"));
1797 if (scols_line_refer_data(ln
, 0, str
))
1798 err(EXIT_FAILURE
, _("failed to add summary data"));
1800 if (scols_line_set_data(ln
, 1, lsfd_counter_name(counter
)))
1801 err(EXIT_FAILURE
, _("failed to add summary data"));
1804 static void emit_summary(struct lsfd_control
*ctl
, struct lsfd_counter
**counter
)
1806 struct libscols_table
*tb
= new_summary_table(ctl
);
1808 for (; *counter
; counter
++) {
1809 struct libscols_line
*ln
= scols_table_new_line(tb
, NULL
);
1810 fill_summary_line(ln
, *counter
);
1812 scols_print_table(tb
);
1814 scols_unref_table(tb
);
1817 static void attach_xinfos(struct list_head
*procs
)
1819 struct list_head
*p
;
1821 list_for_each (p
, procs
) {
1822 struct proc
*proc
= list_entry(p
, struct proc
, procs
);
1823 struct list_head
*f
;
1825 list_for_each (f
, &proc
->files
) {
1826 struct file
*file
= list_entry(f
, struct file
, files
);
1827 if (file
->class->attach_xinfo
)
1828 file
->class->attach_xinfo(file
);
1833 /* Filter expressions for implementing -i option.
1835 * To list up the protocol names, use the following command line
1838 * find . -type f -exec grep -A 1 --color=auto -nH --null -e 'struct proto .*{' \{\} +
1841 #define INET_SUBEXP_BEGIN "(SOCK.PROTONAME =~ \"^("
1842 #define INET4_REG "TCP|UDP|RAW|PING|UDP-Lite|SCTP|DCCP|L2TP/IP|SMC"
1843 #define INET6_REG "TCPv6|UDPv6|RAWv6|PINGv6|UDPLITEv6|SCTPv6|DCCPv6|L2TP/IPv6|SMC6"
1844 #define INET_SUBEXP_END ")$\")"
1846 static const char *inet4_subexpr
= INET_SUBEXP_BEGIN
1849 static const char *inet6_subexpr
= INET_SUBEXP_BEGIN
1852 static const char *inet46_subexpr
= INET_SUBEXP_BEGIN
1853 INET4_REG
"|" INET6_REG
1856 int main(int argc
, char *argv
[])
1860 char *outarg
= NULL
;
1861 char *filter_expr
= NULL
;
1862 bool debug_filter
= false;
1863 bool dump_counters
= false;
1866 struct list_head counter_specs
;
1868 struct lsfd_control ctl
= {
1872 INIT_LIST_HEAD(&counter_specs
);
1875 OPT_DEBUG_FILTER
= CHAR_MAX
+ 1,
1879 static const struct option longopts
[] = {
1880 { "noheadings", no_argument
, NULL
, 'n' },
1881 { "output", required_argument
, NULL
, 'o' },
1882 { "version", no_argument
, NULL
, 'V' },
1883 { "help", no_argument
, NULL
, 'h' },
1884 { "json", no_argument
, NULL
, 'J' },
1885 { "raw", no_argument
, NULL
, 'r' },
1886 { "threads", no_argument
, NULL
, 'l' },
1887 { "notruncate", no_argument
, NULL
, 'u' },
1888 { "pid", required_argument
, NULL
, 'p' },
1889 { "inet", optional_argument
, NULL
, 'i' },
1890 { "filter", required_argument
, NULL
, 'Q' },
1891 { "debug-filter",no_argument
, NULL
, OPT_DEBUG_FILTER
},
1892 { "summary", optional_argument
, NULL
, OPT_SUMMARY
},
1893 { "counter", required_argument
, NULL
, 'C' },
1894 { "dump-counters",no_argument
, NULL
, OPT_DUMP_COUNTERS
},
1895 { NULL
, 0, NULL
, 0 },
1898 setlocale(LC_ALL
, "");
1899 bindtextdomain(PACKAGE
, LOCALEDIR
);
1900 textdomain(PACKAGE
);
1901 close_stdout_atexit();
1903 while ((c
= getopt_long(argc
, argv
, "no:JrVhluQ:p:i::C:s", longopts
, NULL
)) != -1) {
1924 parse_pids(optarg
, &pids
, &n_pids
);
1927 const char *subexpr
= NULL
;
1929 ctl
.sockets_only
= 1;
1931 subexpr
= inet46_subexpr
;
1932 else if (strcmp(optarg
, "4") == 0)
1933 subexpr
= inet4_subexpr
;
1934 else if (strcmp(optarg
, "6") == 0)
1935 subexpr
= inet6_subexpr
;
1938 _("unknown -i/--inet argument: %s"),
1941 append_filter_expr(&filter_expr
, subexpr
, true);
1945 append_filter_expr(&filter_expr
, optarg
, true);
1948 struct counter_spec
*c
= new_counter_spec(optarg
);
1949 list_add_tail(&c
->specs
, &counter_specs
);
1952 case OPT_DEBUG_FILTER
:
1953 debug_filter
= true;
1957 if (strcmp(optarg
, "never") == 0)
1958 ctl
.show_summary
= 0, ctl
.show_main
= 1;
1959 else if (strcmp(optarg
, "only") == 0)
1960 ctl
.show_summary
= 1, ctl
.show_main
= 0;
1961 else if (strcmp(optarg
, "append") == 0)
1962 ctl
.show_summary
= 1, ctl
.show_main
= 1;
1964 errx(EXIT_FAILURE
, _("unsupported --summary argument"));
1966 ctl
.show_summary
= 1, ctl
.show_main
= 0;
1968 case OPT_DUMP_COUNTERS
:
1969 dump_counters
= true;
1972 print_version(EXIT_SUCCESS
);
1976 errtryhelp(EXIT_FAILURE
);
1980 errtryhelp(EXIT_FAILURE
);
1982 #define INITIALIZE_COLUMNS(COLUMN_SPEC) \
1983 for (i = 0; i < ARRAY_SIZE(COLUMN_SPEC); i++) \
1984 columns[ncolumns++] = COLUMN_SPEC[i]
1987 INITIALIZE_COLUMNS(default_threads_columns
);
1989 INITIALIZE_COLUMNS(default_columns
);
1992 if (outarg
&& string_add_to_idarray(outarg
, columns
, ARRAY_SIZE(columns
),
1993 &ncolumns
, column_name_to_id
) < 0)
1994 return EXIT_FAILURE
;
1996 scols_init_debug(0);
1998 INIT_LIST_HEAD(&ctl
.procs
);
2000 /* inilialize scols table */
2001 ctl
.tb
= scols_new_table();
2003 err(EXIT_FAILURE
, _("failed to allocate output table"));
2005 scols_table_enable_noheadings(ctl
.tb
, ctl
.noheadings
);
2006 scols_table_enable_raw(ctl
.tb
, ctl
.raw
);
2007 scols_table_enable_json(ctl
.tb
, ctl
.json
);
2009 scols_table_set_name(ctl
.tb
, "lsfd");
2011 /* create output columns */
2012 for (i
= 0; i
< ncolumns
; i
++) {
2013 const struct colinfo
*col
= get_column_info(i
);
2014 struct libscols_column
*cl
= add_column(ctl
.tb
, col
);
2017 err(EXIT_FAILURE
, _("failed to allocate output column"));
2020 int flags
= scols_column_get_flags(cl
);
2021 flags
&= ~SCOLS_FL_TRUNC
;
2022 scols_column_set_flags(cl
, flags
);
2028 ctl
.filter
= new_filter(filter_expr
, debug_filter
, "", &ctl
);
2032 if (dump_counters
) {
2033 if (list_empty(&counter_specs
))
2034 dump_default_counter_specs();
2036 dump_counter_specs(&counter_specs
);
2041 if (ctl
.show_summary
) {
2042 if (list_empty(&counter_specs
))
2043 ctl
.counters
= new_default_counters(&ctl
);
2045 ctl
.counters
= new_counters(&counter_specs
, &ctl
);
2046 list_free(&counter_specs
, struct counter_spec
, specs
,
2052 sort_pids(pids
, n_pids
);
2055 initialize_nodevs();
2056 initialize_classes();
2057 initialize_devdrvs();
2058 initialize_ipc_table();
2060 collect_processes(&ctl
, pids
, n_pids
);
2063 attach_xinfos(&ctl
.procs
);
2065 convert(&ctl
.procs
, &ctl
);
2071 if (ctl
.show_summary
&& ctl
.counters
)
2072 emit_summary(&ctl
, ctl
.counters
);
2075 delete(&ctl
.procs
, &ctl
);
2077 finalize_ipc_table();