]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: add --hyperlink command line option
authorKarel Zak <kzak@redhat.com>
Mon, 2 Dec 2024 12:22:10 +0000 (13:22 +0100)
committerMasatake YAMATO <yamato@redhat.com>
Fri, 13 Dec 2024 17:54:41 +0000 (02:54 +0900)
Co-Author: Masatake YAMTO <yamato@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
lsfd-cmd/bdev.c
lsfd-cmd/cdev.c
lsfd-cmd/fifo.c
lsfd-cmd/file.c
lsfd-cmd/lsfd.1.adoc
lsfd-cmd/lsfd.c
lsfd-cmd/lsfd.h
lsfd-cmd/sock.c
lsfd-cmd/unkn.c
tests/expected/lsfd/option-hyperlink-regular-file [new file with mode: 0644]
tests/ts/lsfd/option-hyperlink [new file with mode: 0755]

index b61fdbbf4c5882375eae8cd4352342ced97190d3..0da71301acbcd4a2bb99cd3fad135b20ea2876fb 100644 (file)
@@ -33,7 +33,8 @@ static bool bdev_fill_column(struct proc *proc __attribute__((__unused__)),
                             struct file *file __attribute__((__unused__)),
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
        const char *partition, *devdrv;
index 3319479ae7b143cbdf0fe1afcf05a987c40d792f..9b0663e761bae0a76bdc6d68a51e9b13a9ac23ac 100644 (file)
@@ -67,7 +67,8 @@ static bool cdev_fill_column(struct proc *proc __attribute__((__unused__)),
                             struct file *file,
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        struct cdev *cdev = (struct cdev *)file;
        const struct cdev_ops *ops = cdev->cdev_ops;
index b78e6d4875cb85170bebc6df8a4dccb07a568093..45821630c09d717307713ebd2a8cc5a27e5e0937 100644 (file)
@@ -45,7 +45,8 @@ static bool fifo_fill_column(struct proc *proc __attribute__((__unused__)),
                             struct file *file,
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
 
index 91c8d87a46857b95dd07166848a1a80c18600075..7f8ec8780b3759dcc9d5fafcd3371bb72f018747 100644 (file)
@@ -116,7 +116,8 @@ static bool abst_fill_column(struct proc *proc,
                             struct file *file,
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
 
@@ -260,7 +261,8 @@ static bool error_fill_column(struct proc *proc __attribute__((__unused__)),
                              struct file *file __attribute__((__unused__)),
                              struct libscols_line *ln,
                              int column_id,
-                             size_t column_index)
+                             size_t column_index,
+                             const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
        const char *ename;
@@ -301,7 +303,8 @@ static bool readlink_error_fill_column(struct proc *proc __attribute__((__unused
                                       struct file *file __attribute__((__unused__)),
                                       struct libscols_line *ln __attribute__((__unused__)),
                                       int column_id,
-                                      size_t column_index __attribute__((__unused__)))
+                                      size_t column_index __attribute__((__unused__)),
+                                      const char *uri __attribute__((__unused__)))
 {
        switch(column_id) {
        case COL_NAME:
@@ -402,7 +405,8 @@ static bool file_fill_column(struct proc *proc __attribute__((__unused__)),
                             struct file *file,
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
        mode_t ftype;
@@ -428,6 +432,12 @@ static bool file_fill_column(struct proc *proc __attribute__((__unused__)),
                if (file->name
                    && scols_line_set_data(ln, column_index, file->name))
                        err(EXIT_FAILURE, _("failed to add output data"));
+
+               if (uri && (!file->name || *file->name != '/')) {
+                       struct libscols_cell *ce = scols_line_get_cell(ln, column_index);
+                       if (ce)
+                               scols_cell_disable_uri(ce, 1);
+               }
                return true;
        case COL_STTYPE:
        case COL_TYPE:
@@ -797,7 +807,8 @@ static bool nsfs_file_fill_column(struct proc *proc __attribute__((__unused__)),
                                  struct file *file,
                                  struct libscols_line *ln,
                                  int column_id,
-                                 size_t column_index)
+                                 size_t column_index,
+                                 const char *uri __attribute__((__unused__)))
 {
        struct nsfs_file *nsfs_file = (struct nsfs_file *)file;
        char *name = NULL;
@@ -874,7 +885,8 @@ static bool mqueue_file_fill_column(struct proc *proc __attribute__((__unused__)
                                    struct file *file __attribute__((__unused__)),
                                    struct libscols_line *ln,
                                    int column_id,
-                                   size_t column_index)
+                                   size_t column_index,
+                                   const char *uri __attribute__((__unused__)))
 {
        switch (column_id) {
        case COL_TYPE:
@@ -986,7 +998,8 @@ static bool pidfs_file_fill_column(struct proc *proc __attribute__((__unused__))
                                   struct file *file,
                                   struct libscols_line *ln,
                                   int column_id,
-                                  size_t column_index)
+                                  size_t column_index,
+                                  const char *uri __attribute__((__unused__)))
 {
        struct pidfs_file *pidfs_file = (struct pidfs_file *)file;
        char *buf = NULL;
index 71078f493f80c6f764339451b264d944a1155e32..28f6abd5eb863d6a0d7bba4f851fc105be52e707 100644 (file)
@@ -110,6 +110,9 @@ only for *lsfd* developers.
 *--dump-counters*::
 Dump the definition of counters used in *--summary* output.
 
+*--hyperlink*[=_mode_]::
+Print paths as terminal hyperlinks. The _mode_ can be set to "always", "never", or "auto". The optional argument _when_ can be set to "auto", "never", or "always". If the _when_ argument is omitted, it will default to "auto". The "auto" setting means that hyperlinks will only be used if the output is on a terminal.
+
 *-H*, *--list-columns*::
 List available columns that you can specify at *--output* option.
 
index b6486379f94ea773dbaf3b9c4671a3ace39a25a3..242dd4ef72db24876a95eb76c56fecce553b84b7 100644 (file)
@@ -543,6 +543,7 @@ static const struct counter_spec default_counter_specs[] = {
 struct filler_data {
        struct proc *proc;
        struct file *file;
+       const char *uri;
 };
 
 struct lsfd_control {
@@ -559,6 +560,8 @@ struct lsfd_control {
                        sockets_only : 1,       /* display only SOCKETS */
                        show_xmode : 1;         /* XMODE column is enabled. */
 
+       char *uri;
+
        struct libscols_filter *filter;         /* filter */
        struct libscols_filter **ct_filters;    /* counters (NULL terminated array) */
 };
@@ -608,7 +611,7 @@ static const struct colinfo *get_column_info(int id)
 }
 
 static struct libscols_column *add_column(struct libscols_table *tb,
-                                         int id, int extra)
+                                         int id, int extra, char *uri)
 {
        const struct colinfo *col;
        struct libscols_column *cl;
@@ -628,6 +631,9 @@ static struct libscols_column *add_column(struct libscols_table *tb,
                                                  NULL);
                        scols_column_set_safechars(cl, "\n");
                }
+               if (!(extra & SCOLS_FL_HIDDEN) && uri &&
+                   (id == COL_NAME || id == COL_KNAME))
+                       scols_column_set_uri(cl, uri);
        }
 
        return cl;
@@ -641,7 +647,7 @@ static struct libscols_column *add_hidden_column(struct lsfd_control *ctl,
        if (ncolumns >= ARRAY_SIZE(columns))
                errx(EXIT_FAILURE, _("too many columns are added via filter expression"));
 
-       cl = add_column(ctl->tb, colid, SCOLS_FL_HIDDEN);
+       cl = add_column(ctl->tb, colid, SCOLS_FL_HIDDEN, ctl->uri);
        if (!cl)
                err(EXIT_FAILURE, _("failed to allocate output column"));
        columns[ncolumns++] = colid;
@@ -1448,14 +1454,15 @@ static void fill_column(struct proc *proc,
                        struct file *file,
                        struct libscols_line *ln,
                        int column_id,
-                       size_t column_index)
+                       size_t column_index,
+                       const char *uri __attribute__((__unused__)))
 {
        const struct file_class *class = file->class;
 
        while (class) {
                if (class->fill_column
                    && class->fill_column(proc, file, ln,
-                                         column_id, column_index))
+                                         column_id, column_index, uri))
                        break;
                class = class->super;
        }
@@ -1469,13 +1476,15 @@ static int filter_filler_cb(
 {
        struct filler_data *fid = (struct filler_data *) userdata;
 
-       fill_column(fid->proc, fid->file, ln, get_column_id(colnum), colnum);
+       fill_column(fid->proc, fid->file, ln, get_column_id(colnum), colnum,
+                   fid->uri);
        return 0;
 }
 
 static void convert_file(struct proc *proc,
                     struct file *file,
-                    struct libscols_line *ln)
+                    struct libscols_line *ln,
+                    const char *uri __attribute__((__unused__)))
 
 {
        size_t i;
@@ -1483,7 +1492,7 @@ static void convert_file(struct proc *proc,
        for (i = 0; i < ncolumns; i++) {
                if (scols_line_is_filled(ln, i))
                        continue;
-               fill_column(proc, file, ln, get_column_id(i), i);
+               fill_column(proc, file, ln, get_column_id(i), i, uri);
        }
 }
 
@@ -1506,7 +1515,8 @@ static void convert(struct list_head *procs, struct lsfd_control *ctl)
                                int status = 0;
                                struct filler_data fid = {
                                        .proc = proc,
-                                       .file = file
+                                       .file = file,
+                                       .uri = ctl->uri,
                                };
 
                                scols_filter_set_filler_cb(ctl->filter,
@@ -1519,7 +1529,7 @@ static void convert(struct list_head *procs, struct lsfd_control *ctl)
                                }
                        }
 
-                       convert_file(proc, file, ln);
+                       convert_file(proc, file, ln, ctl->uri);
 
                        if (!ctl->ct_filters)
                                continue;
@@ -2160,6 +2170,7 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_("     --debug-filter           dump the internal data structure of filter and exit\n"), out);
        fputs(_(" -C, --counter <name>:<expr>  define custom counter for --summary output\n"), out);
        fputs(_("     --dump-counters          dump counter definitions\n"), out);
+       fputs(_("     --hyperlink[=mode]       print paths as terminal hyperlinks (always, never, or auto)\n"), out);
        fputs(_("     --summary[=<when>]       print summary information (only, append, or never)\n"), out);
        fputs(_("     --_drop-privilege        (testing purpose) do setuid(1) just after starting\n"), out);
 
@@ -2505,6 +2516,7 @@ int main(int argc, char *argv[])
                OPT_SUMMARY,
                OPT_DUMP_COUNTERS,
                OPT_DROP_PRIVILEGE,
+               OPT_HYPERLINK
        };
        static const struct option longopts[] = {
                { "noheadings", no_argument, NULL, 'n' },
@@ -2524,6 +2536,7 @@ int main(int argc, char *argv[])
                { "dump-counters",no_argument, NULL, OPT_DUMP_COUNTERS },
                { "list-columns",no_argument, NULL, 'H' },
                { "_drop-privilege",no_argument,NULL,OPT_DROP_PRIVILEGE },
+               { "hyperlink",  optional_argument, NULL, OPT_HYPERLINK },
                { NULL, 0, NULL, 0 },
        };
 
@@ -2606,6 +2619,11 @@ int main(int argc, char *argv[])
                        if (setuid(1) == -1)
                                err(EXIT_FAILURE, _("failed to drop privilege"));
                        break;
+               case OPT_HYPERLINK:
+                       if (hyperlinkwanted_or_err(optarg,
+                                       _("invalid hyperlink argument")))
+                               ctl.uri = xgethosturi(NULL);
+                       break;
                case 'V':
                        print_version(EXIT_SUCCESS);
                case 'h':
@@ -2655,7 +2673,7 @@ int main(int argc, char *argv[])
 
        /* create output columns */
        for (i = 0; i < ncolumns; i++) {
-               struct libscols_column *cl = add_column(ctl.tb, get_column_id(i), 0);
+               struct libscols_column *cl = add_column(ctl.tb, get_column_id(i), 0, ctl.uri);
 
                if (!cl)
                        err(EXIT_FAILURE, _("failed to allocate output column"));
index e25aec4d6cb2607bf90492ac779b74f876e16b4c..28eb69c1485b3967de91fe5c7a80344be1367786 100644 (file)
@@ -226,7 +226,8 @@ struct file_class {
                            struct file *file,
                            struct libscols_line *ln,
                            int column_id,
-                           size_t column_index);
+                           size_t column_index,
+                           const char *uri);
        int  (*handle_fdinfo)(struct file *file, const char *key, const char* value);
        void (*attach_xinfo)(struct file *file);
        void (*initialize_content)(struct file *file);
@@ -281,6 +282,7 @@ enum decode_source_level {
 
 void decode_source(char *buf, size_t bufsize, unsigned int dev_major, unsigned int dev_minor,
                   enum decode_source_level level);
+
 /*
  * Name managing
  */
index d296ceb4c28d9a7af4cbb673411f9d2427ca2c28..b6eb5fdfeeb105838746e5bb941792f1e3b4421e 100644 (file)
@@ -51,7 +51,8 @@ static bool sock_fill_column(struct proc *proc __attribute__((__unused__)),
                             struct file *file,
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
        struct sock *sock = (struct sock *)file;
index b1883bf34c8c69715cc99db93a720d60862fddf0..f604e6453279ca769b213663fc1822a7720ae979 100644 (file)
@@ -82,7 +82,8 @@ static bool unkn_fill_column(struct proc *proc,
                             struct file *file,
                             struct libscols_line *ln,
                             int column_id,
-                            size_t column_index)
+                            size_t column_index,
+                            const char *uri __attribute__((__unused__)))
 {
        char *str = NULL;
        struct unkn *unkn = (struct unkn *)file;
diff --git a/tests/expected/lsfd/option-hyperlink-regular-file b/tests/expected/lsfd/option-hyperlink-regular-file
new file mode 100644 (file)
index 0000000..bc97081
--- /dev/null
@@ -0,0 +1,10 @@
+# NAME
+00000000  1b 5d 38 3b 3b 66 69 6c  65 3a 2f 2f 2f 65 74 63  |.]8;;file:///etc|
+00000010  2f 70 61 73 73 77 64 1b  5c 2f 65 74 63 2f 70 61  |/passwd.\/etc/pa|
+00000020  73 73 77 64 1b 5d 38 3b  3b 1b 5c 0a              |sswd.]8;;.\.|
+0000002c
+# KNAME
+00000000  1b 5d 38 3b 3b 66 69 6c  65 3a 2f 2f 2f 65 74 63  |.]8;;file:///etc|
+00000010  2f 70 61 73 73 77 64 1b  5c 2f 65 74 63 2f 70 61  |/passwd.\/etc/pa|
+00000020  73 73 77 64 1b 5d 38 3b  3b 1b 5c 0a              |sswd.]8;;.\.|
+0000002c
diff --git a/tests/ts/lsfd/option-hyperlink b/tests/ts/lsfd/option-hyperlink
new file mode 100755 (executable)
index 0000000..a7fe12b
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# Copyright (C) 2024 Masatake YAMATO <yamato@redhat.com>
+#
+# This file is part of util-linux.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This file is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+TS_TOPDIR="${0%/*}/../.."
+TS_DESC="--hyperlink option"
+
+. "$TS_TOPDIR"/functions.sh
+ts_init "$*"
+
+ts_check_test_command "$TS_CMD_LSFD"
+ts_check_test_command "$TS_HELPER_MKFDS"
+ts_check_test_command "$TS_HELPER_SYSINFO"
+ts_check_test_command "$TS_CMD_HEXDUMP"
+
+ts_check_prog "sed"
+
+HOSTNAME=
+if ! HOSTNAME=$("$TS_HELPER_SYSINFO" hostname); then
+    ts_skip "failed in getting hostname"
+fi
+HOSTNAME_PATTERN=${HOSTNAME//./\\.}
+replace_hostname()
+{
+    # Make the output host independent: replacing file://foo.example.com/
+    # with file:///
+    sed -e 's@file://'"${HOSTNAME_PATTERN}"'/@file:///@'
+}
+
+ts_cd "$TS_OUTDIR"
+PID=
+FD=3
+EXPR='(FD == '"$FD"')'
+
+ts_init_subtest "regular-file"
+{
+    coproc MKFDS { "$TS_HELPER_MKFDS" ro-regular-file $FD; }
+    if read -r -u "${MKFDS[0]}" PID; then
+       for o in NAME KNAME; do
+           echo "# $o"
+           "${TS_CMD_LSFD}" -n --hyperlink=always -o "$o" -p "${PID}" -Q "$EXPR" | replace_hostname | "$TS_CMD_HEXDUMP" -C
+       done
+       echo DONE >&"${MKFDS[1]}"
+    fi
+} > "$TS_OUTPUT" 2>&1
+wait "${MKFDS_PID}"
+ts_finalize_subtest
+
+ts_finalize