diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2020-05-05 11:05:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-05 11:05:00 +0200 |
commit | 90f1f8188b842c0d21cc3e24253ff900cac1f953 (patch) | |
tree | f364b4f1e01ab8e740dad943b0f6f803e06d4619 | |
parent | cae97f3b60da6bcdd9cc01efe9e6efb7a019ebd6 (diff) | |
parent | 49cd06fa2d41e196d000a509d71b4356461cdb01 (diff) | |
download | systemd-90f1f8188b842c0d21cc3e24253ff900cac1f953.tar.gz |
Merge pull request #15701 from poettering/systemctl-json-table
systemctl: optionally output tables as json
-rw-r--r-- | src/shared/format-table.c | 46 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 40 |
2 files changed, 70 insertions, 16 deletions
diff --git a/src/shared/format-table.c b/src/shared/format-table.c index fd51abf05d..aac8ac47fa 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -1241,7 +1241,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) { return CMP(*a, *b); } -static const char *table_data_format(Table *t, TableData *d) { +static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) { assert(d); if (d->formatted) @@ -1253,7 +1253,7 @@ static const char *table_data_format(Table *t, TableData *d) { case TABLE_STRING: case TABLE_PATH: - if (d->uppercase) { + if (d->uppercase && !avoid_uppercasing) { char *p, *q; d->formatted = new(char, strlen(d->string) + 1); @@ -1602,7 +1602,7 @@ static int table_data_requested_width_height( const char *t; int r; - t = table_data_format(table, d); + t = table_data_format(table, d, false); if (!t) return -ENOMEM; @@ -1784,7 +1784,7 @@ int table_print(Table *t, FILE *f) { * ellipsis. Hence, let's figure out the last line, and account for its * length plus ellipsis. */ - field = table_data_format(t, d); + field = table_data_format(t, d, false); if (!field) return -ENOMEM; @@ -1969,7 +1969,7 @@ int table_print(Table *t, FILE *f) { assert_se(d = row[t->display_map ? t->display_map[j] : j]); - field = table_data_format(t, d); + field = table_data_format(t, d, false); if (!field) return -ENOMEM; @@ -2256,6 +2256,24 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { } } +static char* string_to_json_field_name(const char *f) { + char *c, *x; + + /* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a + * field name can be hence this is a bit vague and black magic. Right now we only convert spaces to + * underscores and leave everything as is. */ + + c = strdup(f); + if (!c) + return NULL; + + for (x = c; *x; x++) + if (isspace(*x)) + *x = '_'; + + return c; +} + int table_to_json(Table *t, JsonVariant **ret) { JsonVariant **rows = NULL, **elements = NULL; _cleanup_free_ size_t *sorted = NULL; @@ -2298,11 +2316,27 @@ int table_to_json(Table *t, JsonVariant **ret) { } for (j = 0; j < display_columns; j++) { + _cleanup_free_ char *mangled = NULL; + const char *formatted; TableData *d; assert_se(d = t->data[t->display_map ? t->display_map[j] : j]); - r = table_data_to_json(d, elements + j*2); + /* Field names must be strings, hence format whatever we got here as a string first */ + formatted = table_data_format(t, d, true); + if (!formatted) { + r = -ENOMEM; + goto finish; + } + + /* Arbitrary strings suck as field names, try to mangle them into something more suitable hence */ + mangled = string_to_json_field_name(formatted); + if (!mangled) { + r = -ENOMEM; + goto finish; + } + + r = json_variant_new_string(elements + j*2, mangled); if (r < 0) goto finish; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 33f960bd93..a658c65a08 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -390,6 +390,21 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) { return true; } +static int output_table(Table *table) { + int r; + + assert(table); + + if (OUTPUT_MODE_IS_JSON(arg_output)) + r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO); + else + r = table_print(table, NULL); + if (r < 0) + return log_error_errno(r, "Failed to show table: %m"); + + return 0; +} + static int output_units_list(const UnitInfo *unit_infos, unsigned c) { _cleanup_(table_unrefp) Table *table = NULL; const UnitInfo *u; @@ -473,9 +488,9 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { return log_error_errno(r, "Failed to hide column: %m"); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { const char *on, *off; @@ -1039,9 +1054,9 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { off = ansi_normal(); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { printf("\n%s%u sockets listed.%s\n", on, cs, off); @@ -1285,9 +1300,9 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) { off = ansi_normal(); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { printf("\n%s%u timers listed.%s\n", on, n, off); @@ -1501,9 +1516,9 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { return table_log_add_error(r); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) printf("\n%u unit files listed.\n", c); @@ -2033,9 +2048,9 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n) return table_log_add_error(r); } - r = table_print(table, NULL); + r = output_table(table); if (r < 0) - return log_error_errno(r, "Failed to print the table: %m"); + return r; if (!arg_no_legend) { printf("\n"); @@ -8601,6 +8616,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown output '%s'.", optarg); + + if (OUTPUT_MODE_IS_JSON(arg_output)) { + arg_no_legend = true; + arg_plain = true; + } break; case 'i': |