From e676b4fc8bbf1e7e602d839721b092fd58aacc05 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 17:36:53 +0100 Subject: [PATCH 1/7] format-table: add new helper table_print_with_pager() This adds a new function table_print_with_pager() which is a wrapper around table_print_json()/table_print() but spawns a pager first, if that's enabled, and optionally turns off the header line of the table. This addresses the fact that many of our tools actually keep doing very this very similar stuff, over and over again. Let's unify this in one place. --- src/shared/format-table.c | 27 +++++++++++++++++++++++++++ src/shared/format-table.h | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 645e5b9afa..8b9c81664d 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -2550,3 +2550,30 @@ int table_print_json(Table *t, FILE *f, JsonFormatFlags flags) { return fflush_and_check(f); } + +int table_print_with_pager( + Table *t, + JsonFormatFlags json_format_flags, + PagerFlags pager_flags, + bool show_header) { + + bool saved_header; + int r; + + assert(t); + + /* A all-in-one solution for showing tables, and turning on a pager first. Also optionally suppresses + * the table header and logs about any error. */ + + if (json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) + (void) pager_open(pager_flags); + + saved_header = t->header; + t->header = show_header; + r = table_print_json(t, stdout, json_format_flags); + t->header = saved_header; + if (r < 0) + return table_log_print_error(r); + + return 0; +} diff --git a/src/shared/format-table.h b/src/shared/format-table.h index 965549b60a..6627a291f3 100644 --- a/src/shared/format-table.h +++ b/src/shared/format-table.h @@ -7,6 +7,7 @@ #include "json.h" #include "macro.h" +#include "pager.h" typedef enum TableDataType { TABLE_EMPTY, @@ -129,6 +130,8 @@ const void *table_get_at(Table *t, size_t row, size_t column); int table_to_json(Table *t, JsonVariant **ret); int table_print_json(Table *t, FILE *f, JsonFormatFlags json_flags); +int table_print_with_pager(Table *t, JsonFormatFlags json_format_flags, PagerFlags pager_flags, bool show_header); + #define table_log_add_error(r) \ log_error_errno(r, "Failed to add cell(s) to table: %m") From 665ffc7fba3222271ccce2b7e010663a3bfe4533 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 17:39:01 +0100 Subject: [PATCH 2/7] busctl/homectl: port the obvious cases to table_print_with_pager() --- src/busctl/busctl.c | 11 +---------- src/home/homectl.c | 8 ++------ 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 276cae1b8b..15e855062f 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -239,8 +239,6 @@ static int list_bus_names(int argc, char **argv, void *userdata) { if (r < 0) return log_error_errno(r, "Failed to set columns to display: %m"); - table_set_header(table, arg_legend); - HASHMAP_FOREACH_KEY(v, k, names) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; @@ -357,14 +355,7 @@ static int list_bus_names(int argc, char **argv, void *userdata) { return log_error_errno(r, "Failed to fill line: %m"); } - if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) - (void) pager_open(arg_pager_flags); - - r = table_print_json(table, NULL, arg_json_format_flags); - if (r < 0) - return table_log_print_error(r); - - return 0; + return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); } static void print_subtree(const char *prefix, const char *path, char **l) { diff --git a/src/home/homectl.c b/src/home/homectl.c index f9c07dfec7..93d322110b 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -113,8 +113,6 @@ static int list_homes(int argc, char *argv[], void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; int r; - (void) pager_open(arg_pager_flags); - r = acquire_bus(&bus); if (r < 0) return r; @@ -175,11 +173,9 @@ static int list_homes(int argc, char *argv[], void *userdata) { if (r < 0) return table_log_sort_error(r); - table_set_header(table, arg_legend); - - r = table_print_json(table, stdout, arg_json_format_flags); + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); if (r < 0) - return table_log_print_error(r); + return r; } if (arg_legend && (arg_json_format_flags & JSON_FORMAT_OFF)) { From 896e678bea45e6aec9566dc378e29d150ce57820 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 17:40:02 +0100 Subject: [PATCH 3/7] repart: port to table_print_with_pager() This adds support for showing a pager, turning of the legend, like in the other tools. --- man/systemd-repart.xml | 2 ++ src/partition/repart.c | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index 858b5be66d..ee48dd9c96 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -321,6 +321,8 @@ + + diff --git a/src/partition/repart.c b/src/partition/repart.c index d98051d134..3de7a052df 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -107,6 +107,8 @@ static int arg_pretty = -1; static uint64_t arg_size = UINT64_MAX; static bool arg_size_auto = false; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; +static PagerFlags arg_pager_flags = 0; +static bool arg_legend = true; static void *arg_key = NULL; static size_t arg_key_size = 0; static char *arg_tpm2_device = NULL; @@ -1918,11 +1920,7 @@ static int context_dump_partitions(Context *context, const char *node) { return table_log_add_error(r); } - r = table_print_json(t, stdout, arg_json_format_flags); - if (r < 0) - return table_log_print_error(r); - - return 0; + return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); } static void context_bar_char_process_partition( @@ -3479,6 +3477,8 @@ static int help(void) { "\n%sGrow and add partitions to partition table.%s\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" " --dry-run=BOOL Whether to run dry-run operation\n" " --empty=MODE One of refuse, allow, require, force, create; controls\n" " how to handle empty disks lacking partition tables\n" @@ -3510,6 +3510,8 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, ARG_DRY_RUN, ARG_EMPTY, ARG_DISCARD, @@ -3529,6 +3531,8 @@ static int parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "dry-run", required_argument, NULL, ARG_DRY_RUN }, { "empty", required_argument, NULL, ARG_EMPTY }, { "discard", required_argument, NULL, ARG_DISCARD }, @@ -3561,6 +3565,14 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + case ARG_DRY_RUN: r = parse_boolean(optarg); if (r < 0) From 16a36b56163d1e3ab3354bd47e505b484b5a5f55 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 17:57:13 +0100 Subject: [PATCH 4/7] sysext: port to table_print_with_pager() This adds --no-legend as side-effect. --- man/systemd-sysext.xml | 1 + src/sysext/sysext.c | 36 +++++++++++++++++------------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml index e5f2e36899..8539983bcb 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -229,6 +229,7 @@ + diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 590dfc9c20..81b3b29c08 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -35,6 +35,7 @@ static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default */ static char *arg_root = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; +static bool arg_legend = true; static bool arg_force = false; STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep); @@ -226,12 +227,9 @@ static int verb_status(int argc, char **argv, void *userdata) { (void) table_set_sort(t, (size_t) 0, (size_t) -1); - if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) - (void) pager_open(arg_pager_flags); - - r = table_print_json(t, stdout, arg_json_format_flags); + r = table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); if (r < 0) - return table_log_add_error(r); + return r; return ret; } @@ -860,14 +858,7 @@ static int verb_list(int argc, char **argv, void *userdata) { (void) table_set_sort(t, (size_t) 0, (size_t) -1); - if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) - (void) pager_open(arg_pager_flags); - - r = table_print_json(t, stdout, arg_json_format_flags); - if (r < 0) - return table_log_print_error(r); - - return 0; + return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); } static int verb_help(int argc, char **argv, void *userdata) { @@ -890,6 +881,7 @@ static int verb_help(int argc, char **argv, void *userdata) { " --version Show package version\n" "\n%3$sOptions:%4$s\n" " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" " --root=PATH Operate relative to root path\n" " --json=pretty|short|off\n" " Generate JSON output\n" @@ -909,18 +901,20 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_NO_PAGER, + ARG_NO_LEGEND, ARG_ROOT, ARG_JSON, ARG_FORCE, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "root", required_argument, NULL, ARG_ROOT }, - { "json", required_argument, NULL, ARG_JSON }, - { "force", no_argument, NULL, ARG_FORCE }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "root", required_argument, NULL, ARG_ROOT }, + { "json", required_argument, NULL, ARG_JSON }, + { "force", no_argument, NULL, ARG_FORCE }, {} }; @@ -943,6 +937,10 @@ static int parse_argv(int argc, char *argv[]) { arg_pager_flags |= PAGER_DISABLE; break; + case ARG_NO_LEGEND: + arg_legend = false; + break; + case ARG_ROOT: r = parse_path_argument_and_warn(optarg, false, &arg_root); if (r < 0) From 17547fb5cb2edd20a4c7a96c9d7971b52002ced2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 18:12:23 +0100 Subject: [PATCH 5/7] dissect: almost port systemd-dissect to table_print_with_pager() This doesn't actually port systemd-dissect to table_print_with_pager() but at least rearranges things so that similar behaviour is exposed. The reason it's not ported over 1:1 is that systemd-dissect actually adjusts the JSON output of the table with additional info, and doesn't print the table 1:1 as JSON. --- man/systemd-dissect.xml | 2 ++ src/dissect/dissect.c | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index ed2153f765..71615b10a6 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -237,6 +237,8 @@ url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification. + + diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index c5d161b2b5..9b127d456a 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -46,6 +46,8 @@ static const char *arg_target = NULL; static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK; static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; +static PagerFlags arg_pager_flags = 0; +static bool arg_legend = true; STATIC_DESTRUCTOR_REGISTER(arg_verity_settings, verity_settings_done); @@ -63,6 +65,8 @@ static int help(void) { "%1$s [OPTIONS...] --copy-to IMAGE [SOURCE] PATH\n\n" "%5$sDissect a file system OS image.%6$s\n\n" "%3$sOptions:%4$s\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" " -r --read-only Mount read-only\n" " --fsck=BOOL Run fsck before mounting\n" " --mkdir Make mount directory before mounting, if missing\n" @@ -96,6 +100,8 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, ARG_DISCARD, ARG_FSCK, ARG_ROOT_HASH, @@ -108,6 +114,8 @@ static int parse_argv(int argc, char *argv[]) { static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "mount", no_argument, NULL, 'm' }, { "read-only", no_argument, NULL, 'r' }, { "discard", required_argument, NULL, ARG_DISCARD }, @@ -137,6 +145,14 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); + case ARG_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + case 'm': arg_action = ACTION_MOUNT; break; @@ -339,6 +355,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { assert(m); assert(d); + if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) + (void) pager_open(arg_pager_flags); + if (arg_json_format_flags & JSON_FORMAT_OFF) printf(" Name: %s\n", basename(arg_image)); @@ -482,7 +501,9 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) { } if (arg_json_format_flags & JSON_FORMAT_OFF) { - r = table_print(t, stdout); + (void) table_set_header(t, arg_legend); + + r = table_print(t, NULL); if (r < 0) return table_log_print_error(r); } else { From bb1a5700185cf0500e096a7b3371d2dd4439fa5b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 17:59:01 +0100 Subject: [PATCH 6/7] busctl: show --help in pager We do this for other larger --help texts, do so here too. --- src/busctl/busctl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 15e855062f..8a31916ad7 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -2246,6 +2246,8 @@ static int help(void) { if (r < 0) return log_oom(); + (void) pager_open(arg_pager_flags); + printf("%s [OPTIONS...] COMMAND ...\n\n" "%sIntrospect the D-Bus IPC bus.%s\n" "\nCommands:\n" From 8d0d1a309eb25c2e154105822a2ff56c012e794a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Jan 2021 18:21:41 +0100 Subject: [PATCH 7/7] man: share a common explanation for --json= --- man/standard-options.xml | 9 +++++++++ man/systemd-dissect.xml | 10 +--------- man/systemd-repart.xml | 10 +--------- man/systemd-sysext.xml | 9 +-------- 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/man/standard-options.xml b/man/standard-options.xml index 64274ce8f3..8ba5b09df8 100644 --- a/man/standard-options.xml +++ b/man/standard-options.xml @@ -52,4 +52,13 @@ Before each file, the filename is printed as a comment. + + + MODE + + Shows output formatted as JSON. Expects one of short (for the + shortest possible output without any redundant whitespace or line breaks), pretty + (for a pretty version of the same, with indentation and line breaks) or off (to turn + off JSON output, the default). + diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml index 71615b10a6..fd55fd0bfb 100644 --- a/man/systemd-dissect.xml +++ b/man/systemd-dissect.xml @@ -162,15 +162,6 @@ operation begins. - - MODE - - Shows output formatted as JSON. Expects one of short (for the - shortest possible output without any redundant whitespace or line breaks), pretty - (for a pretty version of the same, with indentation and line breaks) or off (to turn - off json output). - - @@ -239,6 +230,7 @@ + diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index ee48dd9c96..a5a0890c52 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -278,15 +278,6 @@ and graphic illustrating the changes applied. - - MODE - - Shows output formatted as JSON. Expects one of short (for the - shortest possible output without any redundant whitespace or line breaks), pretty - (for a pretty version of the same, with indentation and line breaks) or off (to turn - off json output). - - @@ -323,6 +314,7 @@ + diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml index 8539983bcb..ad6a401c7a 100644 --- a/man/systemd-sysext.xml +++ b/man/systemd-sysext.xml @@ -211,14 +211,6 @@ /opt/ hierarchies, but below some specified root directory. - - - - Generate JSON output, instead of human readable tabular output. Takes one of - short, pretty or off in order to control the - output style, or explicitly disabling JSON output. - - @@ -230,6 +222,7 @@ +