1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-19 22:50:17 +03:00

Merge pull request #18338 from poettering/print-table-with-bells-and-whistles

json/table printing unification work
This commit is contained in:
Luca Boccassi 2021-01-21 22:41:18 +00:00 committed by GitHub
commit 87970ee5bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 108 additions and 67 deletions

View File

@ -52,4 +52,13 @@
Before each file, the filename is printed as a comment.</para>
</listitem>
</varlistentry>
<varlistentry id='json'>
<term><option>--json=</option><replaceable>MODE</replaceable></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
off JSON output, the default).</para></listitem>
</varlistentry>
</variablelist>

View File

@ -162,15 +162,6 @@
operation begins.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--json=</option><replaceable>MODE</replaceable></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
off json output).</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
@ -237,6 +228,9 @@
url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions Specification</ulink>.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View File

@ -278,15 +278,6 @@
and graphic illustrating the changes applied.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--json=</option><replaceable>MODE</replaceable></term>
<listitem><para>Shows output formatted as JSON. Expects one of <literal>short</literal> (for the
shortest possible output without any redundant whitespace or line breaks), <literal>pretty</literal>
(for a pretty version of the same, with indentation and line breaks) or <literal>off</literal> (to turn
off json output).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--definitions=</option></term>
@ -321,6 +312,9 @@
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View File

@ -211,14 +211,6 @@
<filename>/opt/</filename> hierarchies, but below some specified root directory.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--json=</option></term>
<listitem><para>Generate JSON output, instead of human readable tabular output. Takes one of
<literal>short</literal>, <literal>pretty</literal> or <literal>off</literal> in order to control the
output style, or explicitly disabling JSON output.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--force</option></term>
@ -229,6 +221,8 @@
</varlistentry>
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="no-legend" />
<xi:include href="standard-options.xml" xpointer="json" />
</variablelist>
</refsect1>

View File

@ -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) {
@ -2255,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"

View File

@ -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 {

View File

@ -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)) {

View File

@ -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)

View File

@ -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;
}

View File

@ -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")

View File

@ -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)