From 52ab46daa2243276f12a55ad744680f8d2434863 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 21 Sep 2021 02:58:48 +0900 Subject: [PATCH] lsfd: introduce -Q option for generic filtering Examples: # ./lsfd -Q "(FD <= 9) and (TYPE == 'SOCK')" -Q "COMMAND == 'ssh'" | head COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME ssh 153907 root 4 rw- SOCK sockfs 9 2450021 TCP:[2450021] ssh 1657696 root 4 rw- SOCK sockfs 9 64357645 TCP:[64357645] ssh 1657696 root 5 rw- SOCK sockfs 9 64363596 UNIX:[64363596] ssh 2345473 root 4 rw- SOCK sockfs 9 81978848 TCP:[81978848] ssh 2345473 root 5 rw- SOCK sockfs 9 81980736 UNIX:[81980736] ssh 2345473 root 9 rw- SOCK sockfs 9 81980737 UNIX:[81980737] ssh 2345475 root 4 rw- SOCK sockfs 9 81975091 UNIX:[81975091] ssh 3958804 root 0 rw- SOCK sockfs 9 87840900 UNIX:[87840900] ssh 3958804 root 4 rw- SOCK sockfs 9 87836642 TCP:[87836642] # ./lsfd -Q "((ASSOC == 'exe') || (ASSOC == 'pid')) and (USER == 'yamato')" COMMAND PID USER ASSOC MODE TYPE SOURCE MNTID INODE NAME lsfd 690292 yamato exe --- REG dm-2 0 34474245 /home/yamato/var/util-linux/util-linux/.libs/lsfd lsfd 690292 yamato pid --- REG nsfs 0 4026531836 pid:[4026531836] pidns 1458677 yamato exe --- REG dm-2 0 34212178 /home/yamato/var/kernel-essential-training/container/pidns pidns 1458677 yamato pid --- REG nsfs 0 4026533715 pid:[4026533715] fd-catalog 1677961 yamato exe --- REG dm-2 0 128715536 /home/yamato/var/fd-catalog/fd-catalog (deleted) fd-catalog 1677961 yamato pid --- REG nsfs 0 4026531836 pid:[4026531836] Signed-off-by: Masatake YAMATO --- misc-utils/lsfd.c | 104 ++++++++++++++++++++++++++++++++++++++++++---- misc-utils/lsfd.h | 1 + 2 files changed, 96 insertions(+), 9 deletions(-) diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 6d8bae4ccf..1662cf9698 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -51,6 +51,7 @@ static int kcmp(pid_t pid1, pid_t pid2, int type, #include "libsmartcols.h" #include "lsfd.h" +#include "lsfd-filter.h" /* * /proc/$pid/mountinfo entries @@ -182,6 +183,8 @@ struct lsfd_control { json : 1, notrunc : 1, threads : 1; + + struct lsfd_filter *filter; }; static int column_name_to_id(const char *name, size_t namesz) @@ -199,6 +202,11 @@ static int column_name_to_id(const char *name, size_t namesz) return -1; } +static int column_name_to_id_cb(const char *name, void *data __attribute__((__unused__))) +{ + return column_name_to_id(name, strlen(name)); +} + static int get_column_id(int num) { assert(num >= 0); @@ -213,6 +221,32 @@ static const struct colinfo *get_column_info(int num) return &infos[ get_column_id(num) ]; } +static struct libscols_column *add_column(struct libscols_table *tb, const struct colinfo *col) +{ + struct libscols_column *cl; + int flags = col->flags; + + cl = scols_table_new_column(tb, col->name, col->whint, flags); + if (cl) + scols_column_set_json_type(cl, col->json_type); + + return cl; +} + +static struct libscols_column *add_column_by_id_cb(struct libscols_table *tb, int colid, void *data __attribute__((__unused__))) +{ + if (ncolumns >= ARRAY_SIZE(columns)) + errx(EXIT_FAILURE, _("too many columns are added via filter expression")); + + assert(colid < LSFD_N_COLS); + + struct libscols_column *cl = add_column(tb, infos + colid); + if (!cl) + err(EXIT_FAILURE, _("failed to allocate output column")); + columns[ncolumns++] = colid; + return cl; +} + static const struct file_class *stat2class(struct stat *sb) { assert(sb); @@ -668,6 +702,9 @@ static void convert(struct list_head *procs, struct lsfd_control *ctl) if (!ln) err(EXIT_FAILURE, _("failed to allocate output line")); convert1(proc, file, ln); + + if (!lsfd_filter_apply(ctl->filter, ln)) + scols_table_remove_line(ctl->tb, ln); } } } @@ -677,6 +714,7 @@ static void delete(struct list_head *procs, struct lsfd_control *ctl) list_free(procs, struct proc, procs, free_proc); scols_unref_table(ctl->tb); + lsfd_filter_free(ctl->filter); } static void emit(struct lsfd_control *ctl) @@ -902,6 +940,7 @@ static void __attribute__((__noreturn__)) usage(void) fputs(_(" -r, --raw use raw output format\n"), out); fputs(_(" --sysroot use specified directory as system root\n"), out); fputs(_(" -u, --notruncate don't truncate text in columns\n"), out); + fputs(_(" -Q, --filter apply display filter\n"), out); fputs(USAGE_SEPARATOR, out); printf(USAGE_HELP_OPTIONS(23)); @@ -909,19 +948,51 @@ static void __attribute__((__noreturn__)) usage(void) fprintf(out, USAGE_COLUMNS); for (i = 0; i < ARRAY_SIZE(infos); i++) - fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); + fprintf(out, " %11s %-10s%s\n", infos[i].name, + infos[i].json_type == SCOLS_JSON_STRING? "": + infos[i].json_type == SCOLS_JSON_NUMBER? "": + "", + _(infos[i].help)); printf(USAGE_MAN_TAIL("lsfd(1)")); exit(EXIT_SUCCESS); } +static void xstrappend(char **a, const char *b) +{ + if (strappend(a, b) < 0) + err(EXIT_FAILURE, _("failed to allocate memory for string")); +} + +static void append_filter_expr(char **a, const char *b, bool and) +{ + if (*a == NULL) { + *a = xstrdup(b); + return; + } + + char *tmp = *a; + *a = NULL; + + xstrappend(a, "("); + xstrappend(a, tmp); + xstrappend(a, ")"); + if (and) + xstrappend(a, "and("); + else + xstrappend(a, "or("); + xstrappend(a, b); + xstrappend(a, ")"); +} + int main(int argc, char *argv[]) { int c; size_t i; char *outarg = NULL; struct lsfd_control ctl = {}; + char *filter_expr = NULL; enum { OPT_SYSROOT = CHAR_MAX + 1 @@ -936,6 +1007,7 @@ int main(int argc, char *argv[]) { "threads", no_argument, NULL, 'l' }, { "notruncate", no_argument, NULL, 'u' }, { "sysroot", required_argument, NULL, OPT_SYSROOT }, + { "filter", required_argument, NULL, 'Q' }, { NULL, 0, NULL, 0 }, }; @@ -944,7 +1016,7 @@ int main(int argc, char *argv[]) textdomain(PACKAGE); close_stdout_atexit(); - while ((c = getopt_long(argc, argv, "no:JrVhlu", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "no:JrVhluQ:", longopts, NULL)) != -1) { switch (c) { case 'n': ctl.noheadings = 1; @@ -967,6 +1039,9 @@ int main(int argc, char *argv[]) case OPT_SYSROOT: ctl.sysroot = optarg; break; + case 'Q': + append_filter_expr(&filter_expr, optarg, true); + break; case 'V': print_version(EXIT_SUCCESS); @@ -1009,17 +1084,28 @@ int main(int argc, char *argv[]) /* create output columns */ for (i = 0; i < ncolumns; i++) { const struct colinfo *col = get_column_info(i); - struct libscols_column *cl; - int flags = col->flags; + struct libscols_column *cl = add_column(ctl.tb, col); - if (ctl.notrunc) - flags &= ~SCOLS_FL_TRUNC; - cl = scols_table_new_column(ctl.tb, col->name, col->whint, flags); if (!cl) err(EXIT_FAILURE, _("failed to allocate output column")); - if (ctl.json) - scols_column_set_json_type(cl, col->json_type); + if (ctl.notrunc) { + int flags = scols_column_get_flags(cl); + flags &= ~SCOLS_FL_TRUNC; + scols_column_set_flags(cl, flags); + } + } + + /* make fitler */ + if (filter_expr) { + ctl.filter = lsfd_filter_new(filter_expr, ctl.tb, + LSFD_N_COLS, + column_name_to_id_cb, + add_column_by_id_cb, NULL); + const char *errmsg = lsfd_filter_get_errmsg(ctl.filter); + if (errmsg) + errx(EXIT_FAILURE, "%s", errmsg); + free(filter_expr); } /* collect data */ diff --git a/misc-utils/lsfd.h b/misc-utils/lsfd.h index 80727d0aee..f9f8bb9097 100644 --- a/misc-utils/lsfd.h +++ b/misc-utils/lsfd.h @@ -68,6 +68,7 @@ enum { COL_TYPE, COL_UID, COL_USER, + LSFD_N_COLS /* This must be at last. */ }; /* -- 2.47.3