mirror of
https://github.com/systemd/systemd
synced 2024-10-15 04:24:19 +00:00
Merge pull request #13216 from poettering/busctl-format-table
port "busctl list" to format-table.h
This commit is contained in:
commit
cd2d52d34f
|
@ -15,6 +15,7 @@
|
|||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "json.h"
|
||||
#include "locale-util.h"
|
||||
#include "log.h"
|
||||
|
@ -141,17 +142,27 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
|
|||
}
|
||||
|
||||
static int list_bus_names(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_strv_free_ char **acquired = NULL, **activatable = NULL;
|
||||
_cleanup_free_ char **merged = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_hashmap_free_ Hashmap *names = NULL;
|
||||
char **i;
|
||||
int r;
|
||||
size_t max_i = 0;
|
||||
unsigned n = 0;
|
||||
void *v;
|
||||
char *k;
|
||||
_cleanup_(table_unrefp) Table *table = NULL;
|
||||
Iterator iterator;
|
||||
char **i, *k;
|
||||
void *v;
|
||||
int r;
|
||||
|
||||
enum {
|
||||
COLUMN_ACTIVATABLE,
|
||||
COLUMN_NAME,
|
||||
COLUMN_PID,
|
||||
COLUMN_PROCESS,
|
||||
COLUMN_USER,
|
||||
COLUMN_CONNECTION,
|
||||
COLUMN_UNIT,
|
||||
COLUMN_SESSION,
|
||||
COLUMN_DESCRIPTION,
|
||||
COLUMN_MACHINE,
|
||||
};
|
||||
|
||||
if (!arg_unique && !arg_acquired && !arg_activatable)
|
||||
arg_unique = arg_acquired = arg_activatable = true;
|
||||
|
@ -164,81 +175,95 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
|
|||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to list names: %m");
|
||||
|
||||
(void) pager_open(arg_pager_flags);
|
||||
|
||||
names = hashmap_new(&string_hash_ops);
|
||||
if (!names)
|
||||
return log_oom();
|
||||
|
||||
STRV_FOREACH(i, acquired) {
|
||||
max_i = MAX(max_i, strlen(*i));
|
||||
|
||||
r = hashmap_put(names, *i, NAME_IS_ACQUIRED);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add to hashmap: %m");
|
||||
}
|
||||
|
||||
STRV_FOREACH(i, activatable) {
|
||||
max_i = MAX(max_i, strlen(*i));
|
||||
|
||||
r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE);
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return log_error_errno(r, "Failed to add to hashmap: %m");
|
||||
}
|
||||
|
||||
merged = new(char*, hashmap_size(names) + 1);
|
||||
if (!merged)
|
||||
table = table_new("activatable", "name", "pid", "process", "user", "connection", "unit", "session", "description", "machine");
|
||||
if (!table)
|
||||
return log_oom();
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, k, names, iterator)
|
||||
merged[n++] = k;
|
||||
r = table_set_align_percent(table, table_get_cell(table, 0, COLUMN_PID), 100);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set alignment: %m");
|
||||
|
||||
merged[n] = NULL;
|
||||
strv_sort(merged);
|
||||
r = table_set_empty_string(table, "-");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set empty string: %m");
|
||||
|
||||
if (arg_legend) {
|
||||
printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s",
|
||||
(int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION");
|
||||
r = table_set_sort(table, COLUMN_NAME, (size_t) -1);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set sort column: %m");
|
||||
|
||||
if (arg_show_machine)
|
||||
puts(" MACHINE");
|
||||
else
|
||||
putchar('\n');
|
||||
}
|
||||
if (arg_show_machine)
|
||||
r = table_set_display(table, COLUMN_NAME, COLUMN_PID, COLUMN_PROCESS, COLUMN_USER, COLUMN_CONNECTION, COLUMN_UNIT, COLUMN_SESSION, COLUMN_DESCRIPTION, COLUMN_MACHINE, (size_t) -1);
|
||||
else
|
||||
r = table_set_display(table, COLUMN_NAME, COLUMN_PID, COLUMN_PROCESS, COLUMN_USER, COLUMN_CONNECTION, COLUMN_UNIT, COLUMN_SESSION, COLUMN_DESCRIPTION, (size_t) -1);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set columns to display: %m");
|
||||
|
||||
STRV_FOREACH(i, merged) {
|
||||
table_set_header(table, arg_legend);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, k, names, iterator) {
|
||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||
sd_id128_t mid;
|
||||
|
||||
if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) {
|
||||
/* Activatable */
|
||||
if (v == NAME_IS_ACTIVATABLE) {
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_INT, PTR_TO_INT(v),
|
||||
TABLE_STRING, k,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_STRING, "(activatable)", TABLE_SET_COLOR, ansi_grey(),
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY,
|
||||
TABLE_EMPTY);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fill line: %m");
|
||||
|
||||
printf("%-*s", (int) max_i, *i);
|
||||
printf(" - - - (activatable) - - ");
|
||||
if (arg_show_machine)
|
||||
puts(" -");
|
||||
else
|
||||
putchar('\n');
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (!arg_unique && (*i)[0] == ':')
|
||||
assert(v == NAME_IS_ACQUIRED);
|
||||
|
||||
if (!arg_unique && k[0] == ':')
|
||||
continue;
|
||||
|
||||
if (!arg_acquired && (*i)[0] != ':')
|
||||
if (!arg_acquired && k[0] != ':')
|
||||
continue;
|
||||
|
||||
printf("%-*s", (int) max_i, *i);
|
||||
r = table_add_many(table,
|
||||
TABLE_INT, PTR_TO_INT(v),
|
||||
TABLE_STRING, k);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add name %s to table: %m", k);
|
||||
|
||||
r = sd_bus_get_name_creds(
|
||||
bus, *i,
|
||||
bus, k,
|
||||
(arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) |
|
||||
SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|
|
||||
SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION|
|
||||
SD_BUS_CREDS_DESCRIPTION, &creds);
|
||||
if (r >= 0) {
|
||||
const char *unique, *session, *unit, *cn;
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to acquire credentials of service %s, ignoring: %m", k);
|
||||
|
||||
r = table_fill_empty(table, COLUMN_MACHINE);
|
||||
} else {
|
||||
const char *unique = NULL, *session = NULL, *unit = NULL, *cn = NULL;
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
|
||||
|
@ -246,11 +271,15 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
|
|||
if (r >= 0) {
|
||||
const char *comm = NULL;
|
||||
|
||||
sd_bus_creds_get_comm(creds, &comm);
|
||||
(void) sd_bus_creds_get_comm(creds, &comm);
|
||||
|
||||
printf(" %10lu %-15s", (unsigned long) pid, strna(comm));
|
||||
r = table_add_many(table,
|
||||
TABLE_PID, pid,
|
||||
TABLE_STRING, strna(comm));
|
||||
} else
|
||||
fputs(" - - ", stdout);
|
||||
r = table_add_many(table, TABLE_EMPTY, TABLE_EMPTY);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add fields to table: %m");
|
||||
|
||||
r = sd_bus_creds_get_euid(creds, &uid);
|
||||
if (r >= 0) {
|
||||
|
@ -260,57 +289,61 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
|
|||
if (!u)
|
||||
return log_oom();
|
||||
|
||||
if (strlen(u) > 16)
|
||||
u[16] = 0;
|
||||
|
||||
printf(" %-16s", u);
|
||||
r = table_add_cell(table, NULL, TABLE_STRING, u);
|
||||
} else
|
||||
fputs(" - ", stdout);
|
||||
r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add field to table: %m");
|
||||
|
||||
r = sd_bus_creds_get_unique_name(creds, &unique);
|
||||
if (r >= 0)
|
||||
printf(" %-13s", unique);
|
||||
else
|
||||
fputs(" - ", stdout);
|
||||
(void) sd_bus_creds_get_unique_name(creds, &unique);
|
||||
(void) sd_bus_creds_get_unit(creds, &unit);
|
||||
(void) sd_bus_creds_get_session(creds, &session);
|
||||
(void) sd_bus_creds_get_description(creds, &cn);
|
||||
|
||||
r = sd_bus_creds_get_unit(creds, &unit);
|
||||
if (r >= 0) {
|
||||
_cleanup_free_ char *e;
|
||||
|
||||
e = ellipsize(unit, 25, 100);
|
||||
if (!e)
|
||||
return log_oom();
|
||||
|
||||
printf(" %-25s", e);
|
||||
} else
|
||||
fputs(" - ", stdout);
|
||||
|
||||
r = sd_bus_creds_get_session(creds, &session);
|
||||
if (r >= 0)
|
||||
printf(" %-10s", session);
|
||||
else
|
||||
fputs(" - ", stdout);
|
||||
|
||||
r = sd_bus_creds_get_description(creds, &cn);
|
||||
if (r >= 0)
|
||||
printf(" %-19s", cn);
|
||||
else
|
||||
fputs(" - ", stdout);
|
||||
|
||||
} else
|
||||
printf(" - - - - - - - ");
|
||||
r = table_add_many(
|
||||
table,
|
||||
TABLE_STRING, unique,
|
||||
TABLE_STRING, unit,
|
||||
TABLE_STRING, session,
|
||||
TABLE_STRING, cn);
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add fields to table: %m");
|
||||
|
||||
if (arg_show_machine) {
|
||||
r = sd_bus_get_name_machine_id(bus, *i, &mid);
|
||||
if (r >= 0) {
|
||||
sd_id128_t mid;
|
||||
|
||||
r = sd_bus_get_name_machine_id(bus, k, &mid);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to acquire credentials of service %s, ignoring: %m", k);
|
||||
else {
|
||||
char m[SD_ID128_STRING_MAX];
|
||||
printf(" %s\n", sd_id128_to_string(mid, m));
|
||||
} else
|
||||
puts(" -");
|
||||
} else
|
||||
putchar('\n');
|
||||
|
||||
r = table_add_cell(table, NULL, TABLE_STRING, sd_id128_to_string(mid, m));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add field to table: %m");
|
||||
|
||||
continue; /* line fully filled, no need to fill the remainder below */
|
||||
}
|
||||
}
|
||||
|
||||
r = table_fill_empty(table, 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fill line: %m");
|
||||
}
|
||||
|
||||
if (IN_SET(arg_json, JSON_OFF, JSON_PRETTY))
|
||||
(void) pager_open(arg_pager_flags);
|
||||
|
||||
if (arg_json)
|
||||
r = table_print_json(table, stdout, (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) | JSON_FORMAT_COLOR_AUTO);
|
||||
else
|
||||
r = table_print(table, stdout);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to show table: %m");
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,6 +127,8 @@ struct Table {
|
|||
size_t n_sort_map;
|
||||
|
||||
bool *reverse_map;
|
||||
|
||||
char *empty_string;
|
||||
};
|
||||
|
||||
Table *table_new_raw(size_t n_columns) {
|
||||
|
@ -218,6 +220,7 @@ Table *table_unref(Table *t) {
|
|||
free(t->display_map);
|
||||
free(t->sort_map);
|
||||
free(t->reverse_map);
|
||||
free(t->empty_string);
|
||||
|
||||
return mfree(t);
|
||||
}
|
||||
|
@ -373,6 +376,10 @@ int table_add_cell_full(
|
|||
assert(type >= 0);
|
||||
assert(type < _TABLE_DATA_TYPE_MAX);
|
||||
|
||||
/* Special rule: patch NULL data fields to the empty field */
|
||||
if (!data)
|
||||
type = TABLE_EMPTY;
|
||||
|
||||
/* Determine the cell adjacent to the current one, but one row up */
|
||||
if (t->n_cells >= t->n_columns)
|
||||
assert_se(p = t->data[t->n_cells - t->n_columns]);
|
||||
|
@ -431,6 +438,27 @@ int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, .
|
|||
return table_add_cell(t, ret_cell, TABLE_STRING, buffer);
|
||||
}
|
||||
|
||||
int table_fill_empty(Table *t, size_t until_column) {
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
|
||||
/* Fill the rest of the current line with empty cells until we reach the specified column. Will add
|
||||
* at least one cell. Pass 0 in order to fill a line to the end or insert an empty line. */
|
||||
|
||||
if (until_column >= t->n_columns)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
r = table_add_cell(t, NULL, TABLE_EMPTY, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
} while ((t->n_cells % t->n_columns) != until_column);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int table_dup_cell(Table *t, TableCell *cell) {
|
||||
size_t i;
|
||||
|
||||
|
@ -930,6 +958,12 @@ void table_set_width(Table *t, size_t width) {
|
|||
t->width = width;
|
||||
}
|
||||
|
||||
int table_set_empty_string(Table *t, const char *empty) {
|
||||
assert(t);
|
||||
|
||||
return free_and_strdup(&t->empty_string, empty);
|
||||
}
|
||||
|
||||
int table_set_display(Table *t, size_t first_column, ...) {
|
||||
size_t allocated, column;
|
||||
va_list ap;
|
||||
|
@ -1105,7 +1139,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(TableData *d) {
|
||||
static const char *table_data_format(Table *t, TableData *d) {
|
||||
assert(d);
|
||||
|
||||
if (d->formatted)
|
||||
|
@ -1113,7 +1147,7 @@ static const char *table_data_format(TableData *d) {
|
|||
|
||||
switch (d->type) {
|
||||
case TABLE_EMPTY:
|
||||
return "";
|
||||
return strempty(t->empty_string);
|
||||
|
||||
case TABLE_STRING:
|
||||
if (d->uppercase) {
|
||||
|
@ -1374,11 +1408,11 @@ static const char *table_data_format(TableData *d) {
|
|||
return d->formatted;
|
||||
}
|
||||
|
||||
static int table_data_requested_width(TableData *d, size_t *ret) {
|
||||
static int table_data_requested_width(Table *table, TableData *d, size_t *ret) {
|
||||
const char *t;
|
||||
size_t l;
|
||||
|
||||
t = table_data_format(d);
|
||||
t = table_data_format(table, d);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1456,6 +1490,19 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const char* table_data_color(TableData *d) {
|
||||
assert(d);
|
||||
|
||||
if (d->color)
|
||||
return d->color;
|
||||
|
||||
/* Let's implicitly color all "empty" cells in grey, in case an "empty_string" is set that is not empty */
|
||||
if (d->type == TABLE_EMPTY)
|
||||
return ansi_grey();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int table_print(Table *t, FILE *f) {
|
||||
size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
|
||||
i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
|
||||
|
@ -1521,7 +1568,7 @@ int table_print(Table *t, FILE *f) {
|
|||
|
||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||
|
||||
r = table_data_requested_width(d, &req);
|
||||
r = table_data_requested_width(t, d, &req);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1688,7 +1735,7 @@ int table_print(Table *t, FILE *f) {
|
|||
|
||||
assert_se(d = row[t->display_map ? t->display_map[j] : j]);
|
||||
|
||||
field = table_data_format(d);
|
||||
field = table_data_format(t, d);
|
||||
if (!field)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1729,16 +1776,16 @@ int table_print(Table *t, FILE *f) {
|
|||
if (j > 0)
|
||||
fputc(' ', f); /* column separator */
|
||||
|
||||
if (d->color && colors_enabled()) {
|
||||
if (table_data_color(d) && colors_enabled()) {
|
||||
if (row == t->data) /* first undo header underliner */
|
||||
fputs(ANSI_NORMAL, f);
|
||||
|
||||
fputs(d->color, f);
|
||||
fputs(table_data_color(d), f);
|
||||
}
|
||||
|
||||
fputs(field, f);
|
||||
|
||||
if (colors_enabled() && (d->color || row == t->data))
|
||||
if (colors_enabled() && (table_data_color(d) || row == t->data))
|
||||
fputs(ANSI_NORMAL, f);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,10 @@ typedef enum TableDataType {
|
|||
_TABLE_DATA_TYPE_INVALID = -1,
|
||||
} TableDataType;
|
||||
|
||||
/* PIDs are just 32bit signed integers on Linux */
|
||||
#define TABLE_PID TABLE_INT32
|
||||
assert_cc(sizeof(pid_t) == sizeof(int32_t));
|
||||
|
||||
typedef struct Table Table;
|
||||
typedef struct TableCell TableCell;
|
||||
|
||||
|
@ -65,6 +69,8 @@ static inline int table_add_cell(Table *t, TableCell **ret_cell, TableDataType t
|
|||
}
|
||||
int table_add_cell_stringf(Table *t, TableCell **ret_cell, const char *format, ...) _printf_(3, 4);
|
||||
|
||||
int table_fill_empty(Table *t, size_t until_column);
|
||||
|
||||
int table_dup_cell(Table *t, TableCell *cell);
|
||||
|
||||
int table_set_minimum_width(Table *t, TableCell *cell, size_t minimum_width);
|
||||
|
@ -83,6 +89,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...);
|
|||
|
||||
void table_set_header(Table *table, bool b);
|
||||
void table_set_width(Table *t, size_t width);
|
||||
int table_set_empty_string(Table *t, const char *empty);
|
||||
int table_set_display(Table *t, size_t first_column, ...);
|
||||
int table_set_sort(Table *t, size_t first_column, ...);
|
||||
int table_set_reverse(Table *t, size_t column, bool b);
|
||||
|
|
Loading…
Reference in a new issue