]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lslocks: add --pager and --nopager options
authoryuriyryabikov <22548029+kurok@users.noreply.github.com>
Wed, 22 Apr 2026 11:46:13 +0000 (12:46 +0100)
committeryuriyryabikov <22548029+kurok@users.noreply.github.com>
Wed, 22 Apr 2026 11:46:13 +0000 (12:46 +0100)
Signed-off-by: yuriyryabikov <22548029+kurok@users.noreply.github.com>
bash-completion/lslocks
include/pager.h
lib/pager.c
misc-utils/Makemodule.am
misc-utils/lslocks.8.adoc
misc-utils/lslocks.c
misc-utils/meson.build

index b2a617e71d7a5650322d71bf9f7c3e3d4aa64572..cd6d95cb3dc917b159f88d56e2a71480c0ed5d40 100644 (file)
@@ -42,6 +42,8 @@ _lslocks_module()
                --notruncate
                --list-columns
                --filter
+               --pager
+               --nopager
                --help
                --version
        "
index 8a2edb3ae994cd2389f4351389f65e878fedfcae..50887993495192fb3eafe6c5727dde3245db0d1f 100644 (file)
@@ -7,8 +7,18 @@
 #ifndef UTIL_LINUX_PAGER
 #define UTIL_LINUX_PAGER
 
+#include <stdbool.h>
+
+/* --pager / --nopager */
+enum ul_pagermode {
+       UL_PAGER_AUTO = 0,      /* unspecified by user; obey PAGER_ENABLE */
+       UL_PAGER_NEVER,         /* --nopager (or --json/--raw) */
+       UL_PAGER_ALWAYS         /* --pager */
+};
+
 void pager_open(void);
 void pager_open_header(int header_lines, int first_col_width);
 void pager_close(void);
+bool pager_is_enabled(enum ul_pagermode mode);
 
 #endif
index 64a0a3a6b52e4c1be444aa1e1ed592b96f0dbef1..180447c1b0c3f3fde96c824530d3d3b1743d0a59 100644 (file)
@@ -26,6 +26,7 @@
 #include "strutils.h"
 #include "ttyutils.h"
 #include "pager.h"
+#include "env.h"
 
 static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
 
@@ -356,6 +357,40 @@ void pager_close(void)
        pager_caught_sigpipe = 0;
 }
 
+/* Decide whether the pager should run for the given @mode.
+ *
+ * UL_PAGER_ALWAYS and UL_PAGER_NEVER reflect an explicit user choice
+ * (--pager / --nopager) and are honored unconditionally.
+ *
+ * UL_PAGER_AUTO consults the PAGER_ENABLE environment variable (accepted
+ * values parsed by ul_strtobool()). PAGER_ENABLE is ignored when stdout
+ * is not a terminal, so pipelines like `lslocks | grep foo` do not spawn
+ * a pager. The variable is also suppressed in privileged (SUID/SGID)
+ * contexts via safe_getenv().
+ */
+bool pager_is_enabled(enum ul_pagermode mode)
+{
+       switch (mode) {
+       case UL_PAGER_ALWAYS:
+               return true;
+       case UL_PAGER_NEVER:
+               return false;
+       case UL_PAGER_AUTO: {
+               const char *s;
+               bool val;
+
+               if (!isatty(STDOUT_FILENO))
+                       return false;
+               s = safe_getenv("PAGER_ENABLE");
+               if (!s || ul_strtobool(s, &val) != 0)
+                       return false;
+               return val;
+       }
+       default:
+               return false;
+       }
+}
+
 #ifdef TEST_PROGRAM_PAGER
 
 #define MAX 255
index 27abeb3cad521ba5bae9a23630bf32f139d8c6b0..11b2f82eedb9eaac9e4a9e48d56ea55d3d4a1a2f 100644 (file)
@@ -85,7 +85,7 @@ usrbin_exec_PROGRAMS += lslocks
 MANPAGES += misc-utils/lslocks.8
 dist_noinst_DATA += misc-utils/lslocks.8.adoc
 lslocks_LDADD = $(LDADD) libcommon.la libmount.la libsmartcols.la
-lslocks_SOURCES = misc-utils/lslocks.c
+lslocks_SOURCES = misc-utils/lslocks.c include/pager.h lib/pager.c
 lslocks_CFLAGS = $(AM_CFLAGS) -I$(ul_libmount_incdir) -I$(ul_libsmartcols_incdir)
 endif
 
index 964ec08a81be0e8f8db57b4b12a9700e97d94102..69894c728ce1fb2c59ff077d7e0310b5be05eef4 100644 (file)
@@ -72,6 +72,16 @@ ____
  lslocks --filter 'TYPE != "POSIX" and MODE == "READ" and PATH =~ ".*\.qcow2"'
 ____
 
+*--pager*::
+Pipe output into a pager (by default *less*(1)). The pager freezes the
+header line so column names stay visible while scrolling.
+The *PAGER* environment variable overrides the default pager program.
+Mutually exclusive with *--json* and *--raw*.
+
+*--nopager*::
+Do not pipe output into a pager. Overrides the *PAGER_ENABLE* environment
+variable.
+
 *-r*, *--raw*::
 Use the raw output format.
 
@@ -87,6 +97,12 @@ include::man-common/env-smartcols.adoc[]
 *LSLOCKS_COLUMNS*=::
 Specify a comma-separated list of output columns to print. All columns listed by *--list-columns* can be used.
 
+*PAGER_ENABLE*=::
+If set to a recognized boolean value (e.g. "yes", "true", "on", "1"), automatically pipe output through a pager.
+The *--pager* and *--nopager* command-line options override this variable.
+Ignored when standard output is not a terminal, and in privileged (setuid/setgid) contexts.
+This is a project-independent variable; other tools may support it as well.
+
 == OUTPUT
 
 COMMAND::
index 8d79af4d794e822940ac4386ca4b4d124762f81e..fbaf7a2f80ef1449b515d26b95b7aa0b51d8df02 100644 (file)
@@ -50,6 +50,7 @@
 #include "procfs.h"
 #include "column-list-table.h"
 #include "fileutils.h"
+#include "pager.h"
 
 /* column IDs */
 enum {
@@ -138,6 +139,7 @@ struct lslocks {
        int raw;
        int json;
        int bytes;
+       enum ul_pagermode pager_mode;
 
        pid_t target_pid;
 
@@ -947,6 +949,8 @@ static void __attribute__((__noreturn__)) usage(void)
        fputs(_(" -Q, --filter <expr>    apply display filter\n"), out);
        fputs(_(" -r, --raw              use the raw output format\n"), out);
        fputs(_(" -u, --notruncate       don't truncate text in columns\n"), out);
+       fputs(_("     --pager            pipe output into a pager\n"), out);
+       fputs(_("     --nopager          disable pager output\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fprintf(out, USAGE_LIST_COLUMNS_OPTION(24));
@@ -1081,7 +1085,9 @@ int main(int argc, char *argv[])
        struct libscols_filter *filter = NULL;
        char *outarg = NULL;
        enum {
-               OPT_OUTPUT_ALL = CHAR_MAX + 1
+               OPT_OUTPUT_ALL = CHAR_MAX + 1,
+               OPT_PAGER,
+               OPT_NO_PAGER,
        };
        static const struct option long_opts[] = {
                { "bytes",      no_argument,       NULL, 'b' },
@@ -1097,11 +1103,16 @@ int main(int argc, char *argv[])
                { "noinaccessible", no_argument, NULL, 'i' },
                { "filter",     required_argument, NULL, 'Q' },
                { "list-columns", no_argument,     NULL, 'H' },
+               { "pager",      no_argument,       NULL, OPT_PAGER },
+               { "nopager",    no_argument,       NULL, OPT_NO_PAGER },
                { NULL, 0, NULL, 0 }
        };
 
        static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
                { 'J','r' },
+               { 'J', OPT_PAGER },
+               { 'r', OPT_PAGER },
+               { OPT_PAGER, OPT_NO_PAGER },
                { 0 }
        };
        int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
@@ -1127,6 +1138,7 @@ int main(int argc, char *argv[])
                        break;
                case 'J':
                        lslocks.json = 1;
+                       lslocks.pager_mode = UL_PAGER_NEVER;
                        break;
                case 'p':
                        lslocks.target_pid = strtopid_or_err(optarg, _("invalid PID argument"));
@@ -1143,6 +1155,7 @@ int main(int argc, char *argv[])
                        break;
                case 'r':
                        lslocks.raw = 1;
+                       lslocks.pager_mode = UL_PAGER_NEVER;
                        break;
                case 'u':
                        disable_columns_truncate();
@@ -1151,6 +1164,12 @@ int main(int argc, char *argv[])
                case 'H':
                        collist = 1;
                        break;
+               case OPT_PAGER:
+                       lslocks.pager_mode = UL_PAGER_ALWAYS;
+                       break;
+               case OPT_NO_PAGER:
+                       lslocks.pager_mode = UL_PAGER_NEVER;
+                       break;
                case 'Q':
                        filter = new_filter(optarg);
                        break;
@@ -1191,6 +1210,9 @@ int main(int argc, char *argv[])
        if (filter)
                init_scols_filter(table, filter, lslocks.bytes);
 
+       if (pager_is_enabled(lslocks.pager_mode))
+               pager_open_header(lslocks.no_headings ? 0 : 1, 0);
+
        /* get_pids_locks() get locks related information from "lock:" fields
         * of /proc/$pid/fdinfo/$fd as fallback information.
         * get_proc_locks() used the fallback information if /proc/locks
@@ -1208,5 +1230,7 @@ int main(int argc, char *argv[])
        scols_unref_table(table);
 
        lslocks_free(&lslocks);
+
+       pager_close();
        return rc;
 }
index 4d95beadce96c7ff8d4108b9437beccf590cc814..8bb99822e866c451b9d2508579c8decdf202fb32 100644 (file)
@@ -45,7 +45,8 @@ whereis_manadocs = files('whereis.1.adoc')
 
 lslocks_sources = files(
   'lslocks.c',
-)
+) + \
+  pager_c
 lslocks_manadocs = files('lslocks.8.adoc')
 
 lsblk_sources = files(