]> git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/lsfd.c
lsfd: implement --summary and --counter options
[thirdparty/util-linux.git] / misc-utils / lsfd.c
1 /*
2 * lsfd(1) - list file descriptors
3 *
4 * Copyright (C) 2021 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 * Karel Zak <kzak@redhat.com>
7 *
8 * Very generally based on lsof(8) by Victor A. Abell <abe@purdue.edu>
9 * It supports multiple OSes. lsfd specializes to Linux.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it would be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include <stdio.h>
27 #include <sys/types.h>
28 #include <inttypes.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <getopt.h>
32 #include <ctype.h>
33
34 #include <linux/sched.h>
35 #include <sys/syscall.h>
36 #include <linux/kcmp.h>
37 static int kcmp(pid_t pid1, pid_t pid2, int type,
38 unsigned long idx1, unsigned long idx2)
39 {
40 return syscall(SYS_kcmp, pid1, pid2, type, idx1, idx2);
41 }
42
43 /* See proc(5).
44 * Defined in linux/include/linux/sched.h private header file. */
45 #define PF_KTHREAD 0x00200000 /* I am a kernel thread */
46
47 #include "c.h"
48 #include "nls.h"
49 #include "xalloc.h"
50 #include "list.h"
51 #include "closestream.h"
52 #include "strutils.h"
53 #include "procfs.h"
54 #include "fileutils.h"
55 #include "idcache.h"
56 #include "pathnames.h"
57
58 #include "libsmartcols.h"
59
60 #include "lsfd.h"
61 #include "lsfd-filter.h"
62 #include "lsfd-counter.h"
63
64 /*
65 * /proc/$pid/mountinfo entries
66 */
67 struct nodev {
68 struct list_head nodevs;
69 unsigned long minor;
70 char *filesystem;
71 };
72
73 struct nodev_table {
74 #define NODEV_TABLE_SIZE 97
75 struct list_head tables[NODEV_TABLE_SIZE];
76 } nodev_table;
77
78 struct name_manager {
79 struct idcache *cache;
80 unsigned long next_id;
81 };
82
83 /*
84 * Column related stuffs
85 */
86
87 /* column names */
88 struct colinfo {
89 const char *name;
90 double whint;
91 int flags;
92 int json_type;
93 const char *help;
94 };
95
96 /* columns descriptions */
97 static struct colinfo infos[] = {
98 [COL_ASSOC] = { "ASSOC", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
99 N_("association between file and process") },
100 [COL_CHRDRV] = { "CHRDRV", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
101 N_("character device driver name resolved by /proc/devices") },
102 [COL_COMMAND] = { "COMMAND",0.3, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
103 N_("command of the process opening the file") },
104 [COL_DELETED] = { "DELETED", 0, SCOLS_FL_RIGHT, SCOLS_JSON_BOOLEAN,
105 N_("reachability from the file system") },
106 [COL_DEV] = { "DEV", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
107 N_("ID of device containing file") },
108 [COL_DEVTYPE] = { "DEVTYPE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
109 N_("device type (blk, char, or nodev)") },
110 [COL_FLAGS] = { "FLAGS", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
111 N_("flags specified when opening the file") },
112 [COL_FD] = { "FD", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
113 N_("file descriptor for the file") },
114 [COL_FUID] = { "FUID", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
115 N_("user ID number of the file's owner") },
116 [COL_INODE] = { "INODE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
117 N_("inode number") },
118 [COL_KTHREAD] = { "KTHREAD", 0, SCOLS_FL_RIGHT, SCOLS_JSON_BOOLEAN,
119 N_("opened by a kernel thread") },
120 [COL_MAJMIN] = { "MAJ:MIN", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
121 N_("device ID for special, or ID of device containing file") },
122 [COL_MAPLEN] = { "MAPLEN", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
123 N_("length of file mapping (in page)") },
124 [COL_MISCDEV] = { "MISCDEV", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
125 N_("misc character device name resolved by /proc/misc") },
126 [COL_MNT_ID] = { "MNTID", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
127 N_("mount id") },
128 [COL_MODE] = { "MODE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
129 N_("access mode (rwx)") },
130 [COL_NAME] = { "NAME", 0.4, SCOLS_FL_TRUNC, SCOLS_JSON_STRING,
131 N_("name of the file") },
132 [COL_NLINK] = { "NLINK", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
133 N_("link count") },
134 [COL_OWNER] = { "OWNER", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
135 N_("owner of the file") },
136 [COL_PID] = { "PID", 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
137 N_("PID of the process opening the file") },
138 [COL_PARTITION]={ "PARTITION",0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
139 N_("block device name resolved by /proc/partition") },
140 [COL_POS] = { "POS", 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
141 N_("file position") },
142 [COL_PROTONAME]={ "PROTONAME",0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
143 N_("protocol name") },
144 [COL_RDEV] = { "RDEV", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
145 N_("device ID (if special file)") },
146 [COL_SIZE] = { "SIZE", 4, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
147 N_("file size"), },
148 [COL_SOURCE] = { "SOURCE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
149 N_("file system, partition, or device containing file") },
150 [COL_TID] = { "TID", 5, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
151 N_("thread ID of the process opening the file") },
152 [COL_TYPE] = { "TYPE", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
153 N_("file type") },
154 [COL_UID] = { "UID", 0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER,
155 N_("user ID number of the process") },
156 [COL_USER] = { "USER", 0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
157 N_("user of the process") },
158 };
159
160 static const int default_columns[] = {
161 COL_COMMAND,
162 COL_PID,
163 COL_USER,
164 COL_ASSOC,
165 COL_MODE,
166 COL_TYPE,
167 COL_SOURCE,
168 COL_MNT_ID,
169 COL_INODE,
170 COL_NAME,
171 };
172
173 static const int default_threads_columns[] = {
174 COL_COMMAND,
175 COL_PID,
176 COL_TID,
177 COL_USER,
178 COL_ASSOC,
179 COL_MODE,
180 COL_TYPE,
181 COL_SOURCE,
182 COL_MNT_ID,
183 COL_INODE,
184 COL_NAME,
185 };
186
187 static int columns[ARRAY_SIZE(infos) * 2] = {-1};
188 static size_t ncolumns;
189
190 static ino_t *mnt_namespaces;
191 static size_t nspaces;
192
193 struct counter_spec {
194 struct list_head specs;
195 const char *name;
196 const char *expr;
197 };
198
199 static struct counter_spec default_counter_specs[] = {
200 {
201 .name = N_("processes"),
202 .expr = "ASSOC == 'cwd'",
203 },
204 {
205 .name = N_("root owned processes"),
206 .expr = "(ASSOC == 'cwd') && (UID == 0)",
207 },
208 {
209 .name = N_("kernel threads"),
210 .expr = "(ASSOC == 'cwd') && KTHREAD",
211 },
212 {
213 .name = N_("open files"),
214 .expr = "FD >= 0",
215 },
216 {
217 .name = N_("RO open files"),
218 .expr = "(FD >= 0) and (MODE == 'r--')",
219 },
220 {
221 .name = N_("WO open files"),
222 .expr = "(FD >= 0) and (MODE == '-w-')",
223 },
224 {
225 .name = N_("shared mappings"),
226 .expr = "ASSOC == 'shm'",
227 },
228 {
229 .name = N_("RO shared mappings"),
230 .expr = "(ASSOC == 'shm') and (MODE == 'r--')",
231 },
232 {
233 .name = N_("WO shared mappings"),
234 .expr = "(ASSOC == 'shm') and (MODE == '-w-')",
235 },
236 {
237 .name = N_("regular files"),
238 .expr = "(FD >= 0) && (TYPE == 'REG')",
239 },
240 {
241 .name = N_("directories"),
242 .expr = "(FD >= 0) && (TYPE == 'DIR')",
243 },
244 {
245 .name = N_("sockets"),
246 .expr = "(FD >= 0) && (TYPE == 'SOCK')",
247 },
248 {
249 .name = N_("fifos/pipes"),
250 .expr = "(FD >= 0) && (TYPE == 'FIFO')",
251 },
252 {
253 .name = N_("character devices"),
254 .expr = "(FD >= 0) && (TYPE == 'CHR')",
255 },
256 {
257 .name = N_("block devices"),
258 .expr = "(FD >= 0) && (TYPE == 'BLK')",
259 },
260 {
261 .name = N_("unknown types"),
262 .expr = "(FD >= 0) && (TYPE == 'UNKN')",
263 }
264 };
265
266 enum {
267 SUMMARY_EMIT = 1 << 0,
268 SUMMARY_ONLY = 1 << 1,
269 };
270
271 struct lsfd_control {
272 struct libscols_table *tb; /* output */
273 struct list_head procs; /* list of all processes */
274
275 unsigned int noheadings : 1,
276 raw : 1,
277 json : 1,
278 notrunc : 1,
279 threads : 1,
280 summary: 2;
281
282 struct lsfd_filter *filter;
283 struct lsfd_counter **counters; /* NULL terminated array. */
284 };
285
286 static void xstrappend(char **a, const char *b);
287 static void xstrputc(char **a, char c);
288
289 static int column_name_to_id(const char *name, size_t namesz)
290 {
291 size_t i;
292
293 for (i = 0; i < ARRAY_SIZE(infos); i++) {
294 const char *cn = infos[i].name;
295
296 if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
297 return i;
298 }
299 warnx(_("unknown column: %s"), name);
300
301 return LSFD_FILTER_UNKNOWN_COL_ID;
302 }
303
304 static int column_name_to_id_cb(const char *name, void *data __attribute__((__unused__)))
305 {
306 return column_name_to_id(name, strlen(name));
307 }
308
309 static int get_column_id(int num)
310 {
311 assert(num >= 0);
312 assert((size_t) num < ncolumns);
313 assert(columns[num] < (int) ARRAY_SIZE(infos));
314
315 return columns[num];
316 }
317
318 static const struct colinfo *get_column_info(int num)
319 {
320 return &infos[ get_column_id(num) ];
321 }
322
323 static struct libscols_column *add_column(struct libscols_table *tb, const struct colinfo *col)
324 {
325 struct libscols_column *cl;
326 int flags = col->flags;
327
328 cl = scols_table_new_column(tb, col->name, col->whint, flags);
329 if (cl)
330 scols_column_set_json_type(cl, col->json_type);
331
332 return cl;
333 }
334
335 static struct libscols_column *add_column_by_id_cb(struct libscols_table *tb, int colid, void *data)
336 {
337 struct libscols_column *cl;
338
339 if (ncolumns >= ARRAY_SIZE(columns))
340 errx(EXIT_FAILURE, _("too many columns are added via filter expression"));
341
342 assert(colid < LSFD_N_COLS);
343
344 cl = add_column(tb, infos + colid);
345 if (!cl)
346 err(EXIT_FAILURE, _("failed to allocate output column"));
347 columns[ncolumns++] = colid;
348
349 if (colid == COL_TID) {
350 struct lsfd_control *ctl = data;
351 ctl->threads = 1;
352 }
353
354 return cl;
355 }
356
357 static int has_mnt_ns(ino_t id)
358 {
359 size_t i;
360
361 for (i = 0; i < nspaces; i++) {
362 if (mnt_namespaces[i] == id)
363 return 1;
364 }
365 return 0;
366 }
367
368 static void add_mnt_ns(ino_t id)
369 {
370 size_t nmax = 0;
371
372 if (nspaces)
373 nmax = (nspaces + 16) / 16 * 16;
374 if (nmax <= nspaces + 1) {
375 nmax += 16;
376 mnt_namespaces = xrealloc(mnt_namespaces,
377 sizeof(ino_t) * nmax);
378 }
379 mnt_namespaces[nspaces++] = id;
380 }
381
382 static const struct file_class *stat2class(struct stat *sb)
383 {
384 assert(sb);
385
386 switch (sb->st_mode & S_IFMT) {
387 case S_IFCHR:
388 return &cdev_class;
389 case S_IFBLK:
390 return &bdev_class;
391 case S_IFSOCK:
392 return &sock_class;
393 case S_IFIFO:
394 return &fifo_class;
395 case S_IFLNK:
396 case S_IFREG:
397 case S_IFDIR:
398 return &file_class;
399 default:
400 break;
401 }
402
403 return &unkn_class;
404 }
405
406 static struct file *new_file(struct proc *proc, const struct file_class *class)
407 {
408 struct file *file;
409
410 file = xcalloc(1, class->size);
411 file->proc = proc;
412
413 INIT_LIST_HEAD(&file->files);
414 list_add_tail(&file->files, &proc->files);
415
416 return file;
417 }
418
419 static struct file *copy_file(struct file *old)
420 {
421 struct file *file = xcalloc(1, old->class->size);
422
423 INIT_LIST_HEAD(&file->files);
424 file->proc = old->proc;
425 list_add_tail(&file->files, &old->proc->files);
426
427 file->class = old->class;
428 file->association = old->association;
429 file->name = xstrdup(old->name);
430 file->stat = old->stat;
431
432 return file;
433 }
434
435 static void file_set_path(struct file *file, struct stat *sb, const char *name, int association)
436 {
437 const struct file_class *class = stat2class(sb);
438
439 assert(class);
440
441 file->class = class;
442 file->association = association;
443 file->name = xstrdup(name);
444 file->stat = *sb;
445 }
446
447 static void file_init_content(struct file *file)
448 {
449 if (file->class && file->class->initialize_content)
450 file->class->initialize_content(file);
451 }
452
453 static void free_file(struct file *file)
454 {
455 const struct file_class *class = file->class;
456
457 while (class) {
458 if (class->free_content)
459 class->free_content(file);
460 class = class->super;
461 }
462 free(file);
463 }
464
465
466 static struct proc *new_process(pid_t pid, struct proc *leader)
467 {
468 struct proc *proc = xcalloc(1, sizeof(*proc));
469
470 proc->pid = pid;
471 proc->leader = leader? leader: proc;
472 proc->command = NULL;
473
474 INIT_LIST_HEAD(&proc->files);
475 INIT_LIST_HEAD(&proc->procs);
476
477 proc->kthread = 0;
478 return proc;
479 }
480
481 static void free_proc(struct proc *proc)
482 {
483 list_free(&proc->files, struct file, files, free_file);
484
485 free(proc->command);
486 free(proc);
487 }
488
489 static void read_fdinfo(struct file *file, FILE *fdinfo)
490 {
491 char buf[1024];
492
493 while (fgets(buf, sizeof(buf), fdinfo)) {
494 const struct file_class *class;
495 char *val = strchr(buf, ':');
496
497 if (!val)
498 continue;
499 *val++ = '\0'; /* terminate key */
500
501 val = (char *) skip_space(val);
502 rtrim_whitespace((unsigned char *) val);
503
504 class = file->class;
505 while (class) {
506 if (class->handle_fdinfo
507 && class->handle_fdinfo(file, buf, val))
508 break;
509 class = class->super;
510 }
511 }
512 }
513
514 static struct file *collect_file_symlink(struct path_cxt *pc,
515 struct proc *proc,
516 const char *name,
517 int assoc)
518 {
519 char sym[PATH_MAX] = { '\0' };
520 struct stat sb;
521 struct file *f, *prev;
522
523 if (ul_path_readlink(pc, sym, sizeof(sym), name) < 0)
524 return NULL;
525
526 /* The /proc/#/{fd,ns} often contains the same file (e.g. /dev/tty)
527 * more than once. Let's try to reuse the previous file if the real
528 * path is the same to save stat() call.
529 */
530 prev = list_last_entry(&proc->files, struct file, files);
531 if (prev && prev->name && strcmp(prev->name, sym) == 0) {
532 f = copy_file(prev);
533 f->association = assoc;
534 } else {
535 if (ul_path_stat(pc, &sb, 0, name) < 0)
536 return NULL;
537
538 f = new_file(proc, stat2class(&sb));
539 file_set_path(f, &sb, sym, assoc);
540 }
541
542 file_init_content(f);
543
544 if (is_association(f, NS_MNT))
545 proc->ns_mnt = sb.st_ino;
546
547 else if (assoc >= 0) {
548 /* file-descriptor based association */
549 FILE *fdinfo;
550
551 if (ul_path_stat(pc, &sb, AT_SYMLINK_NOFOLLOW, name) == 0)
552 f->mode = sb.st_mode;
553
554 fdinfo = ul_path_fopenf(pc, "r", "fdinfo/%d", assoc);
555 if (fdinfo) {
556 read_fdinfo(f, fdinfo);
557 fclose(fdinfo);
558 }
559 }
560
561 return f;
562 }
563
564 /* read symlinks from /proc/#/fd
565 */
566 static void collect_fd_files(struct path_cxt *pc, struct proc *proc)
567 {
568 DIR *sub = NULL;
569 struct dirent *d = NULL;
570 char path[sizeof("fd/") + sizeof(stringify_value(UINT64_MAX))];
571
572 while (ul_path_next_dirent(pc, &sub, "fd", &d) == 0) {
573 uint64_t num;
574
575 if (ul_strtou64(d->d_name, &num, 10) != 0) /* only numbers */
576 continue;
577
578 snprintf(path, sizeof(path), "fd/%ju", (uintmax_t) num);
579 collect_file_symlink(pc, proc, path, num);
580 }
581 }
582
583 static void parse_maps_line(char *buf, struct proc *proc)
584 {
585 uint64_t start, end, offset, ino;
586 unsigned long major, minor;
587 enum association assoc = ASSOC_MEM;
588 struct stat sb;
589 struct file *f, *prev;
590 char *path, modestr[5];
591 dev_t devno;
592
593 /* ignore non-path entries */
594 path = strchr(buf, '/');
595 if (!path)
596 return;
597 rtrim_whitespace((unsigned char *) path);
598
599 /* read rest of the map */
600 if (sscanf(buf, "%"SCNx64 /* start */
601 "-%"SCNx64 /* end */
602 " %4[^ ]" /* mode */
603 " %"SCNx64 /* offset */
604 " %lx:%lx" /* maj:min */
605 " %"SCNu64, /* inode */
606
607 &start, &end, modestr, &offset,
608 &major, &minor, &ino) != 7)
609 return;
610
611 devno = makedev(major, minor);
612
613 if (modestr[3] == 's')
614 assoc = ASSOC_SHM;
615
616 /* The map usually contains the same file more than once, try to reuse
617 * the previous file (if devno and ino are the same) to save stat() call.
618 */
619 prev = list_last_entry(&proc->files, struct file, files);
620
621 if (prev && prev->stat.st_dev == devno && prev->stat.st_ino == ino) {
622 f = copy_file(prev);
623 f->association = -assoc;
624 } else {
625 if (stat(path, &sb) < 0)
626 return;
627 f = new_file(proc, stat2class(&sb));
628 if (!f)
629 return;
630
631 file_set_path(f, &sb, path, -assoc);
632 }
633
634 if (modestr[0] == 'r')
635 f->mode |= S_IRUSR;
636 if (modestr[1] == 'w')
637 f->mode |= S_IWUSR;
638 if (modestr[2] == 'x')
639 f->mode |= S_IXUSR;
640
641 f->map_start = start;
642 f->map_end = end;
643 f->pos = offset;
644
645 file_init_content(f);
646 }
647
648 static void collect_mem_files(struct path_cxt *pc, struct proc *proc)
649 {
650 FILE *fp;
651 char buf[BUFSIZ];
652
653 fp = ul_path_fopen(pc, "r", "maps");
654 if (!fp)
655 return;
656
657 while (fgets(buf, sizeof(buf), fp))
658 parse_maps_line(buf, proc);
659
660 fclose(fp);
661 }
662
663 static void collect_outofbox_files(struct path_cxt *pc,
664 struct proc *proc,
665 enum association assocs[],
666 const char *names[],
667 size_t count)
668 {
669 size_t i;
670
671 for (i = 0; i < count; i++)
672 collect_file_symlink(pc, proc, names[assocs[i]], assocs[i] * -1);
673 }
674
675 static void collect_execve_file(struct path_cxt *pc, struct proc *proc)
676 {
677 enum association assocs[] = { ASSOC_EXE };
678 const char *names[] = {
679 [ASSOC_EXE] = "exe",
680 };
681 collect_outofbox_files(pc, proc, assocs, names, ARRAY_SIZE(assocs));
682 }
683
684 static void collect_fs_files(struct path_cxt *pc, struct proc *proc)
685 {
686 enum association assocs[] = { ASSOC_EXE, ASSOC_CWD, ASSOC_ROOT };
687 const char *names[] = {
688 [ASSOC_CWD] = "cwd",
689 [ASSOC_ROOT] = "root",
690 };
691 collect_outofbox_files(pc, proc, assocs, names, ARRAY_SIZE(assocs));
692 }
693
694 static void collect_namespace_files(struct path_cxt *pc, struct proc *proc)
695 {
696 enum association assocs[] = {
697 ASSOC_NS_CGROUP,
698 ASSOC_NS_IPC,
699 ASSOC_NS_MNT,
700 ASSOC_NS_NET,
701 ASSOC_NS_PID,
702 ASSOC_NS_PID4C,
703 ASSOC_NS_TIME,
704 ASSOC_NS_TIME4C,
705 ASSOC_NS_USER,
706 ASSOC_NS_UTS,
707 };
708 const char *names[] = {
709 [ASSOC_NS_CGROUP] = "ns/cgroup",
710 [ASSOC_NS_IPC] = "ns/ipc",
711 [ASSOC_NS_MNT] = "ns/mnt",
712 [ASSOC_NS_NET] = "ns/net",
713 [ASSOC_NS_PID] = "ns/pid",
714 [ASSOC_NS_PID4C] = "ns/pid_for_children",
715 [ASSOC_NS_TIME] = "ns/time",
716 [ASSOC_NS_TIME4C] = "ns/time_for_children",
717 [ASSOC_NS_USER] = "ns/user",
718 [ASSOC_NS_UTS] = "ns/uts",
719 };
720 collect_outofbox_files(pc, proc, assocs, names, ARRAY_SIZE(assocs));
721 }
722
723 static struct nodev *new_nodev(unsigned long minor, const char *filesystem)
724 {
725 struct nodev *nodev = xcalloc(1, sizeof(*nodev));
726
727 INIT_LIST_HEAD(&nodev->nodevs);
728 nodev->minor = minor;
729 nodev->filesystem = xstrdup(filesystem);
730
731 return nodev;
732 }
733
734 static void free_nodev(struct nodev *nodev)
735 {
736 free(nodev->filesystem);
737 free(nodev);
738 }
739
740 static void initialize_nodevs(void)
741 {
742 int i;
743
744 for (i = 0; i < NODEV_TABLE_SIZE; i++)
745 INIT_LIST_HEAD(&nodev_table.tables[i]);
746 }
747
748 static void finalize_nodevs(void)
749 {
750 int i;
751
752 for (i = 0; i < NODEV_TABLE_SIZE; i++)
753 list_free(&nodev_table.tables[i], struct nodev, nodevs, free_nodev);
754
755 free(mnt_namespaces);
756 }
757
758 const char *get_nodev_filesystem(unsigned long minor)
759 {
760 struct list_head *n;
761 int slot = minor % NODEV_TABLE_SIZE;
762
763 list_for_each (n, &nodev_table.tables[slot]) {
764 struct nodev *nodev = list_entry(n, struct nodev, nodevs);
765 if (nodev->minor == minor)
766 return nodev->filesystem;
767 }
768 return NULL;
769 }
770
771 static void add_nodevs(FILE *mnt)
772 {
773 /* This can be very long. A line in mountinfo can have more than 3
774 * paths. */
775 char line[PATH_MAX * 3 + 256];
776
777 while (fgets(line, sizeof(line), mnt)) {
778 unsigned long major, minor;
779 char filesystem[256];
780 struct nodev *nodev;
781 int slot;
782
783
784 /* 23 61 0:22 / /sys rw,nosuid,nodev,noexec,relatime shared:2 - sysfs sysfs rw,seclabel */
785 if(sscanf(line, "%*d %*d %lu:%lu %*s %*s %*s %*[^-] - %s %*[^\n]",
786 &major, &minor, filesystem) != 3)
787 /* 1600 1458 0:55 / / rw,nodev,relatime - overlay overlay rw,context="s... */
788 if (sscanf(line, "%*d %*d %lu:%lu %*s %*s %*s - %s %*[^\n]",
789 &major, &minor, filesystem) != 3)
790 continue;
791
792 if (major != 0)
793 continue;
794 if (get_nodev_filesystem(minor))
795 continue;
796
797 nodev = new_nodev(minor, filesystem);
798 slot = minor % NODEV_TABLE_SIZE;
799
800 list_add_tail(&nodev->nodevs, &nodev_table.tables[slot]);
801 }
802 }
803
804 static void fill_column(struct proc *proc,
805 struct file *file,
806 struct libscols_line *ln,
807 int column_id,
808 size_t column_index)
809 {
810 const struct file_class *class = file->class;
811
812 while (class) {
813 if (class->fill_column
814 && class->fill_column(proc, file, ln,
815 column_id, column_index))
816 break;
817 class = class->super;
818 }
819 }
820
821 static void convert_file(struct proc *proc,
822 struct file *file,
823 struct libscols_line *ln)
824
825 {
826 size_t i;
827
828 for (i = 0; i < ncolumns; i++)
829 fill_column(proc, file, ln, get_column_id(i), i);
830 }
831
832 static void convert(struct list_head *procs, struct lsfd_control *ctl)
833 {
834 struct list_head *p;
835
836 list_for_each (p, procs) {
837 struct proc *proc = list_entry(p, struct proc, procs);
838 struct list_head *f;
839
840 list_for_each (f, &proc->files) {
841 struct file *file = list_entry(f, struct file, files);
842 struct libscols_line *ln = scols_table_new_line(ctl->tb, NULL);
843 struct lsfd_counter **counter = NULL;
844
845 if (!ln)
846 err(EXIT_FAILURE, _("failed to allocate output line"));
847
848 convert_file(proc, file, ln);
849
850 if (!lsfd_filter_apply(ctl->filter, ln)) {
851 scols_table_remove_line(ctl->tb, ln);
852 continue;
853 }
854
855 if (!ctl->counters)
856 continue;
857
858 for (counter = ctl->counters; *counter; counter++)
859 lsfd_counter_accumulate(*counter, ln);
860 }
861 }
862 }
863
864 static void delete(struct list_head *procs, struct lsfd_control *ctl)
865 {
866 list_free(procs, struct proc, procs, free_proc);
867
868 scols_unref_table(ctl->tb);
869 lsfd_filter_free(ctl->filter);
870 if (ctl->counters) {
871 struct lsfd_counter **counter;
872 for (counter = ctl->counters; *counter; counter++)
873 lsfd_counter_free(*counter);
874 free(ctl->counters);
875 }
876 }
877
878 static void emit(struct lsfd_control *ctl)
879 {
880 scols_print_table(ctl->tb);
881 }
882
883
884 static void initialize_class(const struct file_class *class)
885 {
886 if (class->initialize_class)
887 class->initialize_class();
888 }
889
890 static void initialize_classes(void)
891 {
892 initialize_class(&file_class);
893 initialize_class(&cdev_class);
894 initialize_class(&bdev_class);
895 initialize_class(&sock_class);
896 initialize_class(&unkn_class);
897 }
898
899 static void finalize_class(const struct file_class *class)
900 {
901 if (class->finalize_class)
902 class->finalize_class();
903 }
904
905 static void finalize_classes(void)
906 {
907 finalize_class(&file_class);
908 finalize_class(&cdev_class);
909 finalize_class(&bdev_class);
910 finalize_class(&sock_class);
911 finalize_class(&unkn_class);
912 }
913
914
915
916 struct name_manager *new_name_manager(void)
917 {
918 struct name_manager *nm = xcalloc(1, sizeof(struct name_manager));
919
920 nm->cache = new_idcache();
921 if (!nm->cache)
922 err(EXIT_FAILURE, _("failed to allocate an idcache"));
923
924 nm->next_id = 1; /* 0 is never issued as id. */
925 return nm;
926 }
927
928 void free_name_manager(struct name_manager *nm)
929 {
930 free_idcache(nm->cache);
931 free(nm);
932 }
933
934 const char *get_name(struct name_manager *nm, unsigned long id)
935 {
936 struct identry *e;
937
938 e = get_id(nm->cache, id);
939
940 return e? e->name: NULL;
941 }
942
943 unsigned long add_name(struct name_manager *nm, const char *name)
944 {
945 struct identry *e = NULL, *tmp;
946
947 for (tmp = nm->cache->ent; tmp; tmp = tmp->next) {
948 if (strcmp(tmp->name, name) == 0) {
949 e = tmp;
950 break;
951 }
952 }
953
954 if (e)
955 return e->id;
956
957 e = xmalloc(sizeof(struct identry));
958 e->name = xstrdup(name);
959 e->id = nm->next_id++;
960 e->next = nm->cache->ent;
961 nm->cache->ent = e;
962
963 return e->id;
964 }
965
966 static void read_process(struct lsfd_control *ctl, struct path_cxt *pc,
967 pid_t pid, struct proc *leader)
968 {
969 char buf[BUFSIZ];
970 struct proc *proc;
971
972 if (procfs_process_init_path(pc, pid) != 0)
973 return;
974
975 proc = new_process(pid, leader);
976 proc->command = procfs_process_get_cmdname(pc, buf, sizeof(buf)) > 0 ?
977 xstrdup(buf) : xstrdup(_("(unknown)"));
978 procfs_process_get_uid(pc, &proc->uid);
979
980 if (procfs_process_get_stat(pc, buf, sizeof(buf)) > 0) {
981 char *p;
982 unsigned int flags;
983 char *pat = NULL;
984
985 /* See proc(5) about the column in the line. */
986 xstrappend(&pat, "%*d (");
987 for (p = proc->command; *p != '\0'; p++) {
988 if (*p == '%')
989 xstrappend(&pat, "%%");
990 else
991 xstrputc(&pat, *p);
992 }
993 xstrappend(&pat, ") %*c %*d %*d %*d %*d %*d %u %*[^\n]");
994 if (sscanf(buf, pat, &flags) == 1)
995 proc->kthread = !!(flags & PF_KTHREAD);
996 free(pat);
997 }
998
999 collect_execve_file(pc, proc);
1000
1001 if (proc->pid == proc->leader->pid
1002 || kcmp(proc->leader->pid, proc->pid, KCMP_FS, 0, 0) != 0)
1003 collect_fs_files(pc, proc);
1004
1005 collect_namespace_files(pc, proc);
1006
1007 if (proc->ns_mnt == 0 || !has_mnt_ns(proc->ns_mnt)) {
1008 FILE *mnt = ul_path_fopen(pc, "r", "mountinfo");
1009 if (mnt) {
1010 add_nodevs(mnt);
1011 if (proc->ns_mnt)
1012 add_mnt_ns(proc->ns_mnt);
1013 fclose(mnt);
1014 }
1015 }
1016
1017 /* If kcmp is not available,
1018 * there is no way to no whether threads share resources.
1019 * In such cases, we must pay the costs: call collect_mem_files()
1020 * and collect_fd_files().
1021 */
1022 if (proc->pid == proc->leader->pid
1023 || kcmp(proc->leader->pid, proc->pid, KCMP_VM, 0, 0) != 0)
1024 collect_mem_files(pc, proc);
1025
1026 if (proc->pid == proc->leader->pid
1027 || kcmp(proc->leader->pid, proc->pid, KCMP_FILES, 0, 0) != 0)
1028 collect_fd_files(pc, proc);
1029
1030 list_add_tail(&proc->procs, &ctl->procs);
1031
1032 /* The tasks collecting overwrites @pc by /proc/<task-pid>/. Keep it as
1033 * the last path based operation in read_process()
1034 */
1035 if (ctl->threads && leader == NULL) {
1036 DIR *sub = NULL;
1037 pid_t tid;
1038
1039 while (procfs_process_next_tid(pc, &sub, &tid) == 0) {
1040 if (tid == pid)
1041 continue;
1042 read_process(ctl, pc, tid, proc);
1043 }
1044 }
1045
1046 /* Let's be careful with number of open files */
1047 ul_path_close_dirfd(pc);
1048 }
1049
1050 static void parse_pids(const char *str, pid_t **pids, int *count)
1051 {
1052 long v;
1053 char *next = NULL;
1054
1055 if (*str == '\0')
1056 return;
1057
1058 errno = 0;
1059 v = strtol(str, &next, 10);
1060 if (errno)
1061 err(EXIT_FAILURE, _("unexpected value for pid specification: %s"), str);
1062 if (next == str)
1063 errx(EXIT_FAILURE, _("garbage at the end of pid specification: %s"), str);
1064 if (v < 0)
1065 errx(EXIT_FAILURE, _("out of range value for pid specification: %ld"), v);
1066
1067 (*count)++;
1068 *pids = xrealloc(*pids, (*count) * sizeof(**pids));
1069 (*pids)[*count - 1] = (pid_t)v;
1070
1071 while (next && *next != '\0'
1072 && (isspace((unsigned char)*next) || *next == ','))
1073 next++;
1074 if (*next != '\0')
1075 parse_pids(next, pids, count);
1076 }
1077
1078 static int pidcmp(const void *a, const void *b)
1079 {
1080 pid_t pa = *(pid_t *)a;
1081 pid_t pb = *(pid_t *)b;
1082
1083 if (pa < pb)
1084 return -1;
1085 else if (pa == pb)
1086 return 0;
1087 else
1088 return 1;
1089 }
1090
1091 static void sort_pids(pid_t pids[], const int count)
1092 {
1093 qsort(pids, count, sizeof(pid_t), pidcmp);
1094 }
1095
1096 static bool member_pids(const pid_t pid, const pid_t pids[], const int count)
1097 {
1098 return bsearch(&pid, pids, count, sizeof(pid_t), pidcmp)? true: false;
1099 }
1100
1101 static void collect_processes(struct lsfd_control *ctl, const pid_t pids[], int n_pids)
1102 {
1103 DIR *dir;
1104 struct dirent *d;
1105 struct path_cxt *pc = NULL;
1106
1107 pc = ul_new_path(NULL);
1108 if (!pc)
1109 err(EXIT_FAILURE, _("failed to alloc procfs handler"));
1110
1111 dir = opendir(_PATH_PROC);
1112 if (!dir)
1113 err(EXIT_FAILURE, _("failed to open /proc"));
1114
1115 while ((d = readdir(dir))) {
1116 pid_t pid;
1117
1118 if (procfs_dirent_get_pid(d, &pid) != 0)
1119 continue;
1120 if (n_pids == 0 || member_pids(pid, pids, n_pids))
1121 read_process(ctl, pc, pid, 0);
1122 }
1123
1124 closedir(dir);
1125 ul_unref_path(pc);
1126 }
1127
1128 static void __attribute__((__noreturn__)) usage(void)
1129 {
1130 FILE *out = stdout;
1131 size_t i;
1132
1133 fputs(USAGE_HEADER, out);
1134 fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
1135
1136 fputs(USAGE_OPTIONS, out);
1137 fputs(_(" -l, --threads list in threads level\n"), out);
1138 fputs(_(" -J, --json use JSON output format\n"), out);
1139 fputs(_(" -n, --noheadings don't print headings\n"), out);
1140 fputs(_(" -o, --output <list> output columns\n"), out);
1141 fputs(_(" -r, --raw use raw output format\n"), out);
1142 fputs(_(" -u, --notruncate don't truncate text in columns\n"), out);
1143 fputs(_(" -p, --pid <pid(s)> collect information only specified processes\n"), out);
1144 fputs(_(" -Q, --filter <expr> apply display filter\n"), out);
1145 fputs(_(" --debug-filter dump the innternal data structure of filter and exit\n"), out);
1146 fputs(_(" -C, --counter <name>:<expr>\n"
1147 " make a counter used in --summary output\n"), out);
1148 fputs(_(" --summary[=when] print summary information (never,always or only)\n"), out);
1149
1150 fputs(USAGE_SEPARATOR, out);
1151 printf(USAGE_HELP_OPTIONS(23));
1152
1153 fprintf(out, USAGE_COLUMNS);
1154
1155 for (i = 0; i < ARRAY_SIZE(infos); i++)
1156 fprintf(out, " %11s %-10s%s\n", infos[i].name,
1157 infos[i].json_type == SCOLS_JSON_STRING? "<string>":
1158 infos[i].json_type == SCOLS_JSON_NUMBER? "<number>":
1159 "<boolean>",
1160 _(infos[i].help));
1161
1162 printf(USAGE_MAN_TAIL("lsfd(1)"));
1163
1164 exit(EXIT_SUCCESS);
1165 }
1166
1167 static void xstrappend(char **a, const char *b)
1168 {
1169 if (strappend(a, b) < 0)
1170 err(EXIT_FAILURE, _("failed to allocate memory for string"));
1171 }
1172
1173 static void xstrputc(char **a, char c)
1174 {
1175 char b[] = {c, '\0'};
1176 xstrappend(a, b);
1177 }
1178
1179 static void append_filter_expr(char **a, const char *b, bool and)
1180 {
1181 if (*a == NULL) {
1182 *a = xstrdup(b);
1183 return;
1184 }
1185
1186 char *tmp = *a;
1187 *a = NULL;
1188
1189 xstrappend(a, "(");
1190 xstrappend(a, tmp);
1191 xstrappend(a, ")");
1192 if (and)
1193 xstrappend(a, "and(");
1194 else
1195 xstrappend(a, "or(");
1196 xstrappend(a, b);
1197 xstrappend(a, ")");
1198 }
1199
1200 static struct lsfd_filter *new_filter(const char *expr, bool debug, const char *err_prefix, struct lsfd_control *ctl)
1201 {
1202 struct lsfd_filter *filter;
1203 const char *errmsg;
1204
1205 filter = lsfd_filter_new(expr, ctl->tb,
1206 LSFD_N_COLS,
1207 column_name_to_id_cb,
1208 add_column_by_id_cb, ctl);
1209 errmsg = lsfd_filter_get_errmsg(filter);
1210 if (errmsg)
1211 errx(EXIT_FAILURE, "%s%s", err_prefix, errmsg);
1212 if (debug) {
1213 lsfd_filter_dump(filter, stdout);
1214 exit(EXIT_SUCCESS);
1215 }
1216
1217 return filter;
1218 }
1219
1220 static struct counter_spec *new_counter_spec(const char *spec_str)
1221 {
1222 char *sep;
1223 struct counter_spec *spec;
1224
1225 if (spec_str[0] == '\0')
1226 errx(EXIT_FAILURE,
1227 _("too short counter specification: -C/--counter %s"),
1228 spec_str);
1229 if (spec_str[0] == ':')
1230 errx(EXIT_FAILURE,
1231 _("no name for counter: -C/--counter %s"),
1232 spec_str);
1233
1234 sep = strchr(spec_str, ':');
1235 if (sep == NULL)
1236 errx(EXIT_FAILURE,
1237 _("no name for counter: -C/--counter %s"),
1238 spec_str);
1239 if (sep[1] == '\0')
1240 errx(EXIT_FAILURE,
1241 _("empty ecounter expression given: -C/--counter %s"),
1242 spec_str);
1243
1244 /* Split the spec_str in to name and expr. */
1245 *sep = '\0';
1246
1247 if (strchr(spec_str, '{'))
1248 errx(EXIT_FAILURE,
1249 _("don't use `{' in the name of a counter: %s"),
1250 spec_str);
1251
1252 spec = xmalloc(sizeof(struct counter_spec));
1253 INIT_LIST_HEAD(&spec->specs);
1254 spec->name = spec_str;
1255 spec->expr = sep + 1;
1256
1257 return spec;
1258 }
1259
1260 static void free_counter_spec(struct counter_spec *counter_spec)
1261 {
1262 free(counter_spec);
1263 }
1264
1265 static struct lsfd_counter *new_counter(struct counter_spec *spec, struct lsfd_control *ctl)
1266 {
1267 struct lsfd_filter *filter;
1268
1269 filter = new_filter(spec->expr, false,
1270 _("failed in making filter for a counter: "),
1271 ctl);
1272 return lsfd_counter_new(spec->name, filter);
1273 }
1274
1275 static struct lsfd_counter **new_counters(struct list_head *specs, struct lsfd_control *ctl)
1276 {
1277 struct lsfd_counter **counters;
1278 size_t len = list_count_entries(specs);
1279 size_t i = 0;
1280 struct list_head *s;
1281
1282 counters = xcalloc(len + 1, sizeof(struct lsfd_counter *));
1283 list_for_each(s, specs) {
1284 struct counter_spec *spec = list_entry(s, struct counter_spec, specs);
1285 counters[i++] = new_counter(spec, ctl);
1286 }
1287 assert(counters[len] == NULL);
1288
1289 return counters;
1290 }
1291
1292 static struct lsfd_counter **new_default_counters(struct lsfd_control *ctl)
1293 {
1294 struct lsfd_counter **counters;
1295 size_t len = ARRAY_SIZE(default_counter_specs);
1296 size_t i;
1297
1298 counters = xcalloc(len + 1, sizeof(struct lsfd_counter *));
1299 for (i = 0; i < len; i++) {
1300 struct counter_spec *spec = default_counter_specs + i;
1301 counters[i] = new_counter(spec, ctl);
1302 }
1303 assert(counters[len] == NULL);
1304
1305 return counters;
1306 }
1307
1308 static struct libscols_table *new_summary_table(struct lsfd_control *ctl)
1309 {
1310 struct libscols_table *tb = scols_new_table();
1311
1312 struct libscols_column *name_cl, *value_cl;
1313
1314 if (!tb)
1315 err(EXIT_FAILURE, _("failed to allocate summary table"));
1316
1317 scols_table_enable_noheadings(tb, ctl->noheadings);
1318 scols_table_enable_raw(tb, ctl->raw);
1319 scols_table_enable_json(tb, ctl->json);
1320
1321 if(ctl->json)
1322 scols_table_set_name(tb, "lsfd-summary");
1323
1324
1325 value_cl = scols_table_new_column(tb, _("VALUE"), 0, SCOLS_FL_RIGHT);
1326 if (!value_cl)
1327 err(EXIT_FAILURE, _("failed to allocate summary column"));
1328 if (ctl->json)
1329 scols_column_set_json_type(value_cl, SCOLS_JSON_NUMBER);
1330
1331 name_cl = scols_table_new_column(tb, _("COUNTER"), 0, 0);
1332 if (!name_cl)
1333 err(EXIT_FAILURE, _("failed to allocate summary column"));
1334 if (ctl->json)
1335 scols_column_set_json_type(name_cl, SCOLS_JSON_STRING);
1336
1337 return tb;
1338 }
1339
1340 static void fill_summary_line(struct libscols_line *ln, struct lsfd_counter *counter)
1341 {
1342 char *str = NULL;
1343
1344 xasprintf(&str, "%llu", (unsigned long long)lsfd_counter_value(counter));
1345 if (!str)
1346 err(EXIT_FAILURE, _("failed to add summary data"));
1347 if (scols_line_refer_data(ln, 0, str))
1348 err(EXIT_FAILURE, _("failed to add summary data"));
1349
1350 if (scols_line_set_data(ln, 1, lsfd_counter_name(counter)))
1351 err(EXIT_FAILURE, _("failed to add summary data"));
1352 }
1353
1354 static void emit_summary(struct lsfd_control *ctl, struct lsfd_counter **counter)
1355 {
1356 struct libscols_table *tb = new_summary_table(ctl);
1357
1358 for (; *counter; counter++) {
1359 struct libscols_line *ln = scols_table_new_line(tb, NULL);
1360 fill_summary_line(ln, *counter);
1361 }
1362 scols_print_table(tb);
1363
1364 scols_unref_table(tb);
1365 }
1366
1367 int main(int argc, char *argv[])
1368 {
1369 int c;
1370 size_t i;
1371 char *outarg = NULL;
1372 struct lsfd_control ctl = {};
1373 char *filter_expr = NULL;
1374 bool debug_filter = false;
1375 pid_t *pids = NULL;
1376 int n_pids = 0;
1377 struct list_head counter_specs;
1378
1379 INIT_LIST_HEAD(&counter_specs);
1380
1381 enum {
1382 OPT_DEBUG_FILTER = CHAR_MAX + 1,
1383 OPT_SUMMARY,
1384 };
1385 static const struct option longopts[] = {
1386 { "noheadings", no_argument, NULL, 'n' },
1387 { "output", required_argument, NULL, 'o' },
1388 { "version", no_argument, NULL, 'V' },
1389 { "help", no_argument, NULL, 'h' },
1390 { "json", no_argument, NULL, 'J' },
1391 { "raw", no_argument, NULL, 'r' },
1392 { "threads", no_argument, NULL, 'l' },
1393 { "notruncate", no_argument, NULL, 'u' },
1394 { "pid", required_argument, NULL, 'p' },
1395 { "filter", required_argument, NULL, 'Q' },
1396 { "debug-filter",no_argument, NULL, OPT_DEBUG_FILTER },
1397 { "summary", optional_argument, NULL, OPT_SUMMARY },
1398 { "counter", required_argument, NULL, 'C' },
1399 { NULL, 0, NULL, 0 },
1400 };
1401
1402 setlocale(LC_ALL, "");
1403 bindtextdomain(PACKAGE, LOCALEDIR);
1404 textdomain(PACKAGE);
1405 close_stdout_atexit();
1406
1407 while ((c = getopt_long(argc, argv, "no:JrVhluQ:p:C:s", longopts, NULL)) != -1) {
1408 switch (c) {
1409 case 'n':
1410 ctl.noheadings = 1;
1411 break;
1412 case 'o':
1413 outarg = optarg;
1414 break;
1415 case 'J':
1416 ctl.json = 1;
1417 break;
1418 case 'r':
1419 ctl.raw = 1;
1420 break;
1421 case 'l':
1422 ctl.threads = 1;
1423 break;
1424 case 'u':
1425 ctl.notrunc = 1;
1426 break;
1427 case 'p':
1428 parse_pids(optarg, &pids, &n_pids);
1429 break;
1430 case 'Q':
1431 append_filter_expr(&filter_expr, optarg, true);
1432 break;
1433 case 'C': {
1434 struct counter_spec *c = new_counter_spec(optarg);
1435 list_add_tail(&c->specs, &counter_specs);
1436 break;
1437 }
1438 case OPT_DEBUG_FILTER:
1439 debug_filter = true;
1440 break;
1441 case OPT_SUMMARY:
1442 if (optarg) {
1443 if (strcmp(optarg, "never") == 0)
1444 ctl.summary = 0;
1445 else if (strcmp(optarg, "only") == 0)
1446 ctl.summary |= (SUMMARY_ONLY|SUMMARY_EMIT);
1447 else if (strcmp(optarg, "always") == 0)
1448 ctl.summary |= SUMMARY_EMIT;
1449 else
1450 errx(EXIT_FAILURE, _("unsupported --summary argument"));
1451 } else
1452 ctl.summary |= SUMMARY_EMIT;
1453 break;
1454 case 'V':
1455 print_version(EXIT_SUCCESS);
1456 case 'h':
1457 usage();
1458 default:
1459 errtryhelp(EXIT_FAILURE);
1460 }
1461 }
1462
1463 #define INITIALIZE_COLUMNS(COLUMN_SPEC) \
1464 for (i = 0; i < ARRAY_SIZE(COLUMN_SPEC); i++) \
1465 columns[ncolumns++] = COLUMN_SPEC[i]
1466 if (!ncolumns) {
1467 if (ctl.threads)
1468 INITIALIZE_COLUMNS(default_threads_columns);
1469 else
1470 INITIALIZE_COLUMNS(default_columns);
1471 }
1472
1473 if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
1474 &ncolumns, column_name_to_id) < 0)
1475 return EXIT_FAILURE;
1476
1477 scols_init_debug(0);
1478
1479 INIT_LIST_HEAD(&ctl.procs);
1480
1481 /* inilialize scols table */
1482 ctl.tb = scols_new_table();
1483 if (!ctl.tb)
1484 err(EXIT_FAILURE, _("failed to allocate output table"));
1485
1486 scols_table_enable_noheadings(ctl.tb, ctl.noheadings);
1487 scols_table_enable_raw(ctl.tb, ctl.raw);
1488 scols_table_enable_json(ctl.tb, ctl.json);
1489 if (ctl.json)
1490 scols_table_set_name(ctl.tb, "lsfd");
1491
1492 /* create output columns */
1493 for (i = 0; i < ncolumns; i++) {
1494 const struct colinfo *col = get_column_info(i);
1495 struct libscols_column *cl = add_column(ctl.tb, col);
1496
1497 if (!cl)
1498 err(EXIT_FAILURE, _("failed to allocate output column"));
1499
1500 if (ctl.notrunc) {
1501 int flags = scols_column_get_flags(cl);
1502 flags &= ~SCOLS_FL_TRUNC;
1503 scols_column_set_flags(cl, flags);
1504 }
1505 }
1506
1507 /* make fitler */
1508 if (filter_expr) {
1509 ctl.filter = new_filter(filter_expr, debug_filter, "", &ctl);
1510 free(filter_expr);
1511 }
1512
1513 /* make counters */
1514 if (ctl.summary & SUMMARY_EMIT) {
1515 if (list_empty(&counter_specs))
1516 ctl.counters = new_default_counters(&ctl);
1517 else {
1518 ctl.counters = new_counters(&counter_specs, &ctl);
1519 list_free(&counter_specs, struct counter_spec, specs,
1520 free_counter_spec);
1521 }
1522 }
1523
1524 if (n_pids > 0)
1525 sort_pids(pids, n_pids);
1526
1527 /* collect data */
1528 initialize_nodevs();
1529 initialize_classes();
1530
1531 collect_processes(&ctl, pids, n_pids);
1532 free(pids);
1533
1534 convert(&ctl.procs, &ctl);
1535 if (!(ctl.summary & SUMMARY_ONLY))
1536 emit(&ctl);
1537 if (ctl.counters && (ctl.summary & SUMMARY_EMIT))
1538 emit_summary(&ctl, ctl.counters);
1539
1540 /* cleanup */
1541 delete(&ctl.procs, &ctl);
1542
1543 finalize_classes();
1544 finalize_nodevs();
1545
1546 return 0;
1547 }