summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2020-05-05 11:05:00 +0200
committerGitHub <noreply@github.com>2020-05-05 11:05:00 +0200
commit90f1f8188b842c0d21cc3e24253ff900cac1f953 (patch)
treef364b4f1e01ab8e740dad943b0f6f803e06d4619
parentcae97f3b60da6bcdd9cc01efe9e6efb7a019ebd6 (diff)
parent49cd06fa2d41e196d000a509d71b4356461cdb01 (diff)
downloadsystemd-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.c46
-rw-r--r--src/systemctl/systemctl.c40
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':