]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: introduce -Q option for generic filtering
authorMasatake YAMATO <yamato@redhat.com>
Mon, 20 Sep 2021 17:58:48 +0000 (02:58 +0900)
committerKarel Zak <kzak@redhat.com>
Wed, 6 Oct 2021 09:01:54 +0000 (11:01 +0200)
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 <yamato@redhat.com>
misc-utils/lsfd.c
misc-utils/lsfd.h

index 6d8bae4ccf5b82b0a893b60851b69c669a89b87a..1662cf9698d67764d26fcec395ca2ab13524aae5 100644 (file)
@@ -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 <dir>   use specified directory as system root\n"), out);
        fputs(_(" -u, --notruncate      don't truncate text in columns\n"), out);
+       fputs(_(" -Q, --filter <expr>   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?  "<string>":
+                       infos[i].json_type == SCOLS_JSON_NUMBER?  "<number>":
+                       "<boolean>",
+                       _(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 */
index 80727d0aee2acca655b02b073dd4a1ccb1b6227f..f9f8bb9097ed6647c9d5aa46ffe195c39bd83f50 100644 (file)
@@ -68,6 +68,7 @@ enum {
        COL_TYPE,
        COL_UID,
        COL_USER,
+       LSFD_N_COLS             /* This must be at last. */
 };
 
 /*