Merge pull request #18521 from poettering/coredumpctl-table
coredumpctl improvements (mostly about table formatting)
This commit is contained in:
+11
-9
@@ -154,20 +154,22 @@
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--no-legend</option></term>
|
||||
|
||||
<listitem><para>Do not print column headers.</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" />
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-1</option></term>
|
||||
|
||||
<listitem><para>Show information of a single core dump only, instead of listing
|
||||
all known core dumps.</para></listitem>
|
||||
<listitem><para>Show information of the most recent core dump only, instead of listing all known core
|
||||
dumps. (Equivalent to <option>--reverse -n 1</option></para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-n</option> <replaceable>INT</replaceable></term>
|
||||
|
||||
<listitem><para>Show at most the specified number of entries. The specified parameter must be an
|
||||
integer greater or equal to 1.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
||||
+189
-83
@@ -16,6 +16,7 @@
|
||||
#include "compress.h"
|
||||
#include "def.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-table.h"
|
||||
#include "fs-util.h"
|
||||
#include "glob-util.h"
|
||||
#include "journal-internal.h"
|
||||
@@ -47,9 +48,10 @@ static const char *arg_debugger = NULL;
|
||||
static char **arg_debugger_args = NULL;
|
||||
static const char *arg_directory = NULL;
|
||||
static char **arg_file = NULL;
|
||||
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
static int arg_no_legend = false;
|
||||
static int arg_one = false;
|
||||
static int arg_legend = true;
|
||||
static size_t arg_rows_max = SIZE_MAX;
|
||||
static const char* arg_output = NULL;
|
||||
static bool arg_reverse = false;
|
||||
static bool arg_quiet = false;
|
||||
@@ -155,20 +157,23 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%sList or retrieve coredumps from the journal.%s\n"
|
||||
"\nCommands:\n"
|
||||
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
|
||||
"%5$sList or retrieve coredumps from the journal.%6$s\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" list [MATCHES...] List available coredumps (default)\n"
|
||||
" info [MATCHES...] Show detailed information about one or more coredumps\n"
|
||||
" dump [MATCHES...] Print first matching coredump to stdout\n"
|
||||
" debug [MATCHES...] Start a debugger for the first matching coredump\n"
|
||||
"\nOptions:\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version string\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --no-legend Do not print the column headers\n"
|
||||
" --json=pretty|short|off\n"
|
||||
" Generate JSON output\n"
|
||||
" --debugger=DEBUGGER Use the given debugger\n"
|
||||
" -A --debugger-arguments=ARGS Pass the given arguments to the debugger\n"
|
||||
" -n INT Show maximum number of rows\n"
|
||||
" -1 Show information about most recent entry only\n"
|
||||
" -S --since=DATE Only print coredumps since the date\n"
|
||||
" -U --until=DATE Only print coredumps until the date\n"
|
||||
@@ -178,11 +183,13 @@ static int help(void) {
|
||||
" --file=PATH Use journal file\n"
|
||||
" -D --directory=DIR Use journal files from directory\n\n"
|
||||
" -q --quiet Do not show info messages and privilege warning\n"
|
||||
"\nSee the %s for details.\n",
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
link,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -192,6 +199,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_NO_PAGER,
|
||||
ARG_NO_LEGEND,
|
||||
ARG_JSON,
|
||||
ARG_DEBUGGER,
|
||||
ARG_FILE,
|
||||
};
|
||||
@@ -213,13 +221,14 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "since", required_argument, NULL, 'S' },
|
||||
{ "until", required_argument, NULL, 'U' },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{}
|
||||
};
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "hA:o:F:1D:rS:U:q", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "hA:o:F:1D:rS:U:qn:", options, NULL)) >= 0)
|
||||
switch(c) {
|
||||
case 'h':
|
||||
return help();
|
||||
@@ -232,7 +241,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case ARG_NO_LEGEND:
|
||||
arg_no_legend = true;
|
||||
arg_legend = false;
|
||||
break;
|
||||
|
||||
case ARG_DEBUGGER:
|
||||
@@ -282,9 +291,22 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
|
||||
case '1':
|
||||
arg_one = true;
|
||||
arg_rows_max = 1;
|
||||
arg_reverse = true;
|
||||
break;
|
||||
|
||||
case 'n': {
|
||||
unsigned n;
|
||||
|
||||
r = safe_atou(optarg, &n);
|
||||
if (r < 0 || n < 1)
|
||||
return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid numeric parameter to -n: %s", optarg);
|
||||
|
||||
arg_rows_max = n;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'D':
|
||||
arg_directory = optarg;
|
||||
break;
|
||||
@@ -297,6 +319,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case ARG_JSON:
|
||||
r = json_parse_cmdline_parameter_and_warn(optarg, &arg_json_format_flags);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -376,21 +405,78 @@ static int print_field(FILE* file, sd_journal *j) {
|
||||
continue; \
|
||||
}
|
||||
|
||||
static int print_list(FILE* file, sd_journal *j, int had_legend) {
|
||||
static void analyze_coredump_file(
|
||||
const char *path,
|
||||
const char **ret_state,
|
||||
const char **ret_color,
|
||||
uint64_t *ret_size) {
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret_state);
|
||||
assert(ret_color);
|
||||
assert(ret_size);
|
||||
|
||||
fd = open(path, O_PATH|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT) {
|
||||
*ret_state = "missing";
|
||||
*ret_color = ansi_grey();
|
||||
*ret_size = UINT64_MAX;
|
||||
return;
|
||||
}
|
||||
|
||||
r = -errno;
|
||||
} else
|
||||
r = access_fd(fd, R_OK);
|
||||
if (ERRNO_IS_PRIVILEGE(r)) {
|
||||
*ret_state = "inaccessible";
|
||||
*ret_color = ansi_highlight_yellow();
|
||||
*ret_size = UINT64_MAX;
|
||||
return;
|
||||
}
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
goto error;
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
goto error;
|
||||
|
||||
*ret_state = "present";
|
||||
*ret_color = NULL;
|
||||
*ret_size = st.st_size;
|
||||
return;
|
||||
|
||||
error:
|
||||
*ret_state = "error";
|
||||
*ret_color = ansi_highlight_red();
|
||||
*ret_size = UINT64_MAX;
|
||||
}
|
||||
|
||||
static int print_list(FILE* file, sd_journal *j, Table *t) {
|
||||
_cleanup_free_ char
|
||||
*mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL,
|
||||
*sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL,
|
||||
*filename = NULL, *truncated = NULL, *coredump = NULL;
|
||||
const void *d;
|
||||
size_t l;
|
||||
usec_t t;
|
||||
char buf[FORMAT_TIMESTAMP_MAX];
|
||||
int r;
|
||||
const char *present;
|
||||
usec_t ts;
|
||||
int r, signal_as_int = 0;
|
||||
const char *present = NULL, *color = NULL;
|
||||
uint64_t size = UINT64_MAX;
|
||||
bool normal_coredump;
|
||||
uid_t uid_as_int = UID_INVALID;
|
||||
gid_t gid_as_int = GID_INVALID;
|
||||
pid_t pid_as_int = 0;
|
||||
|
||||
assert(file);
|
||||
assert(j);
|
||||
assert(t);
|
||||
|
||||
SD_JOURNAL_FOREACH_DATA(j, d, l) {
|
||||
RETRIEVE(d, l, "MESSAGE_ID", mid);
|
||||
@@ -406,54 +492,46 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) {
|
||||
RETRIEVE(d, l, "COREDUMP", coredump);
|
||||
}
|
||||
|
||||
if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename) {
|
||||
log_warning("Empty coredump log entry");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!pid && !uid && !gid && !sgnl && !exe && !comm && !cmdline && !filename)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Empty coredump log entry");
|
||||
|
||||
r = sd_journal_get_realtime_usec(j, &t);
|
||||
(void) parse_uid(uid, &uid_as_int);
|
||||
(void) parse_gid(gid, &gid_as_int);
|
||||
(void) parse_pid(pid, &pid_as_int);
|
||||
signal_as_int = signal_from_string(sgnl);
|
||||
|
||||
r = sd_journal_get_realtime_usec(j, &ts);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
||||
|
||||
format_timestamp(buf, sizeof(buf), t);
|
||||
|
||||
if (!had_legend && !arg_no_legend)
|
||||
fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
|
||||
FORMAT_TIMESTAMP_WIDTH, "TIME",
|
||||
6, "PID",
|
||||
5, "UID",
|
||||
5, "GID",
|
||||
3, "SIG",
|
||||
9, "COREFILE",
|
||||
"EXE");
|
||||
|
||||
normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR);
|
||||
|
||||
if (filename)
|
||||
if (access(filename, R_OK) == 0)
|
||||
present = "present";
|
||||
else if (errno == ENOENT)
|
||||
present = "missing";
|
||||
else
|
||||
present = "error";
|
||||
analyze_coredump_file(filename, &present, &color, &size);
|
||||
else if (coredump)
|
||||
present = "journal";
|
||||
else if (normal_coredump)
|
||||
else if (normal_coredump) {
|
||||
present = "none";
|
||||
else
|
||||
present = "-";
|
||||
color = ansi_grey();
|
||||
} else
|
||||
present = NULL;
|
||||
|
||||
if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
|
||||
if (STRPTR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0)
|
||||
present = "truncated";
|
||||
|
||||
fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n",
|
||||
FORMAT_TIMESTAMP_WIDTH, buf,
|
||||
6, strna(pid),
|
||||
5, strna(uid),
|
||||
5, strna(gid),
|
||||
3, normal_coredump ? strna(sgnl) : "-",
|
||||
9, present,
|
||||
strna(exe ?: (comm ?: cmdline)));
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_TIMESTAMP, ts,
|
||||
TABLE_PID, pid_as_int,
|
||||
TABLE_UID, uid_as_int,
|
||||
TABLE_GID, gid_as_int,
|
||||
TABLE_SIGNAL, normal_coredump ? signal_as_int : 0,
|
||||
TABLE_STRING, present,
|
||||
TABLE_SET_COLOR, color,
|
||||
TABLE_STRING, exe ?: comm ?: cmdline,
|
||||
TABLE_SIZE, size);
|
||||
if (r < 0)
|
||||
return table_log_add_error(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -612,24 +690,27 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
|
||||
fprintf(file, " Hostname: %s\n", hostname);
|
||||
|
||||
if (filename) {
|
||||
bool inacc, trunc;
|
||||
const char *state = NULL, *color = NULL;
|
||||
uint64_t size = UINT64_MAX;
|
||||
char buf[FORMAT_BYTES_MAX];
|
||||
|
||||
inacc = access(filename, R_OK) < 0;
|
||||
trunc = truncated && parse_boolean(truncated) > 0;
|
||||
analyze_coredump_file(filename, &state, &color, &size);
|
||||
|
||||
if (inacc || trunc)
|
||||
fprintf(file, " Storage: %s%s (%s%s%s)%s\n",
|
||||
ansi_highlight_red(),
|
||||
filename,
|
||||
inacc ? "inaccessible" : "",
|
||||
inacc && trunc ? ", " : "",
|
||||
trunc ? "truncated" : "",
|
||||
ansi_normal());
|
||||
else
|
||||
fprintf(file, " Storage: %s\n", filename);
|
||||
}
|
||||
if (STRPTR_IN_SET(state, "present", "journal") && truncated && parse_boolean(truncated) > 0)
|
||||
state = "truncated";
|
||||
|
||||
else if (coredump)
|
||||
fprintf(file,
|
||||
" Storage: %s%s (%s)%s\n",
|
||||
strempty(color),
|
||||
filename,
|
||||
state,
|
||||
ansi_normal());
|
||||
|
||||
if (size != UINT64_MAX)
|
||||
fprintf(file,
|
||||
" Disk Size: %s\n",
|
||||
format_bytes(buf, sizeof(buf), size));
|
||||
} else if (coredump)
|
||||
fprintf(file, " Storage: journal\n");
|
||||
else
|
||||
fprintf(file, " Storage: none\n");
|
||||
@@ -659,43 +740,61 @@ static int focus(sd_journal *j) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int print_entry(sd_journal *j, unsigned n_found, bool verb_is_info) {
|
||||
static int print_entry(
|
||||
sd_journal *j,
|
||||
size_t n_found,
|
||||
Table *t) {
|
||||
|
||||
assert(j);
|
||||
|
||||
if (verb_is_info)
|
||||
return print_info(stdout, j, n_found);
|
||||
if (t)
|
||||
return print_list(stdout, j, t);
|
||||
else if (arg_field)
|
||||
return print_field(stdout, j);
|
||||
else
|
||||
return print_list(stdout, j, n_found);
|
||||
return print_info(stdout, j, n_found > 0);
|
||||
}
|
||||
|
||||
static int dump_list(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
||||
unsigned n_found = 0;
|
||||
_cleanup_(table_unrefp) Table *t = NULL;
|
||||
size_t n_found = 0;
|
||||
bool verb_is_info;
|
||||
int r;
|
||||
|
||||
verb_is_info = (argc >= 1 && streq(argv[0], "info"));
|
||||
verb_is_info = argc >= 1 && streq(argv[0], "info");
|
||||
|
||||
r = acquire_journal(&j, argv + 1);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) pager_open(arg_pager_flags);
|
||||
/* The coredumps are likely compressed, and for just listing them we don't need to decompress them,
|
||||
* so let's pick a fairly low data threshold here */
|
||||
(void) sd_journal_set_data_threshold(j, 4096);
|
||||
|
||||
/* The coredumps are likely to compressed, and for just
|
||||
* listing them we don't need to decompress them, so let's
|
||||
* pick a fairly low data threshold here */
|
||||
sd_journal_set_data_threshold(j, 4096);
|
||||
if (!verb_is_info && !arg_field) {
|
||||
t = table_new("time", "pid", "uid", "gid", "sig", "corefile", "exe", "size");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
(void) table_set_align_percent(t, TABLE_HEADER_CELL(1), 100);
|
||||
(void) table_set_align_percent(t, TABLE_HEADER_CELL(2), 100);
|
||||
(void) table_set_align_percent(t, TABLE_HEADER_CELL(3), 100);
|
||||
(void) table_set_align_percent(t, TABLE_HEADER_CELL(7), 100);
|
||||
|
||||
(void) table_set_empty_string(t, "-");
|
||||
} else
|
||||
(void) pager_open(arg_pager_flags);
|
||||
|
||||
/* "info" without pattern implies "-1" */
|
||||
if (arg_one || (verb_is_info && argc == 1)) {
|
||||
if ((arg_rows_max == 1 && arg_reverse) || (verb_is_info && argc == 1)) {
|
||||
r = focus(j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return print_entry(j, 0, verb_is_info);
|
||||
r = print_entry(j, 0, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
if (arg_since != USEC_INFINITY && !arg_reverse)
|
||||
r = sd_journal_seek_realtime_usec(j, arg_since);
|
||||
@@ -713,10 +812,8 @@ static int dump_list(int argc, char **argv, void *userdata) {
|
||||
r = sd_journal_next(j);
|
||||
else
|
||||
r = sd_journal_previous(j);
|
||||
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to iterate through journal: %m");
|
||||
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
@@ -740,9 +837,12 @@ static int dump_list(int argc, char **argv, void *userdata) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = print_entry(j, n_found++, verb_is_info);
|
||||
r = print_entry(j, n_found++, t);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_rows_max != SIZE_MAX && n_found >= arg_rows_max)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!arg_field && n_found <= 0) {
|
||||
@@ -752,6 +852,12 @@ static int dump_list(int argc, char **argv, void *userdata) {
|
||||
}
|
||||
}
|
||||
|
||||
if (t) {
|
||||
r = table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "process-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "sort-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strxcpyx.h"
|
||||
#include "terminal-util.h"
|
||||
#include "time-util.h"
|
||||
#include "user-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -100,6 +103,9 @@ typedef struct TableData {
|
||||
int ifindex;
|
||||
union in_addr_union address;
|
||||
sd_id128_t id128;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
/* … add more here as we start supporting more cell data types … */
|
||||
};
|
||||
} TableData;
|
||||
@@ -284,6 +290,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
|
||||
case TABLE_UINT:
|
||||
case TABLE_PERCENT:
|
||||
case TABLE_IFINDEX:
|
||||
case TABLE_SIGNAL:
|
||||
return sizeof(int);
|
||||
|
||||
case TABLE_IN_ADDR:
|
||||
@@ -296,6 +303,13 @@ static size_t table_data_size(TableDataType type, const void *data) {
|
||||
case TABLE_ID128:
|
||||
return sizeof(sd_id128_t);
|
||||
|
||||
case TABLE_UID:
|
||||
return sizeof(uid_t);
|
||||
case TABLE_GID:
|
||||
return sizeof(gid_t);
|
||||
case TABLE_PID:
|
||||
return sizeof(pid_t);
|
||||
|
||||
default:
|
||||
assert_not_reached("Uh? Unexpected cell type");
|
||||
}
|
||||
@@ -801,6 +815,9 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
|
||||
bool b;
|
||||
union in_addr_union address;
|
||||
sd_id128_t id128;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t pid;
|
||||
} buffer;
|
||||
|
||||
switch (type) {
|
||||
@@ -840,6 +857,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
|
||||
break;
|
||||
|
||||
case TABLE_INT:
|
||||
case TABLE_SIGNAL:
|
||||
buffer.int_val = va_arg(ap, int);
|
||||
data = &buffer.int_val;
|
||||
break;
|
||||
@@ -931,6 +949,21 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
|
||||
data = &buffer.id128;
|
||||
break;
|
||||
|
||||
case TABLE_UID:
|
||||
buffer.uid = va_arg(ap, uid_t);
|
||||
data = &buffer.uid;
|
||||
break;
|
||||
|
||||
case TABLE_GID:
|
||||
buffer.gid = va_arg(ap, gid_t);
|
||||
data = &buffer.gid;
|
||||
break;
|
||||
|
||||
case TABLE_PID:
|
||||
buffer.pid = va_arg(ap, pid_t);
|
||||
data = &buffer.pid;
|
||||
break;
|
||||
|
||||
case TABLE_SET_MINIMUM_WIDTH: {
|
||||
size_t w = va_arg(ap, size_t);
|
||||
|
||||
@@ -1189,6 +1222,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
|
||||
return CMP(a->size, b->size);
|
||||
|
||||
case TABLE_INT:
|
||||
case TABLE_SIGNAL:
|
||||
return CMP(a->int_val, b->int_val);
|
||||
|
||||
case TABLE_INT8:
|
||||
@@ -1234,6 +1268,15 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
|
||||
case TABLE_ID128:
|
||||
return memcmp(&a->id128, &b->id128, sizeof(sd_id128_t));
|
||||
|
||||
case TABLE_UID:
|
||||
return CMP(a->uid, b->uid);
|
||||
|
||||
case TABLE_GID:
|
||||
return CMP(a->gid, b->gid);
|
||||
|
||||
case TABLE_PID:
|
||||
return CMP(a->pid, b->pid);
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
@@ -1618,6 +1661,67 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
|
||||
break;
|
||||
}
|
||||
|
||||
case TABLE_UID: {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
if (!uid_is_valid(d->uid))
|
||||
return "n/a";
|
||||
|
||||
p = new(char, DECIMAL_STR_WIDTH(d->uid) + 1);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
sprintf(p, UID_FMT, d->uid);
|
||||
d->formatted = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case TABLE_GID: {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
if (!gid_is_valid(d->gid))
|
||||
return "n/a";
|
||||
|
||||
p = new(char, DECIMAL_STR_WIDTH(d->gid) + 1);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
sprintf(p, GID_FMT, d->gid);
|
||||
d->formatted = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case TABLE_PID: {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
if (!pid_is_valid(d->pid))
|
||||
return "n/a";
|
||||
|
||||
p = new(char, DECIMAL_STR_WIDTH(d->pid) + 1);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
sprintf(p, PID_FMT, d->pid);
|
||||
d->formatted = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case TABLE_SIGNAL: {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
const char *suffix;
|
||||
|
||||
suffix = signal_to_string(d->int_val);
|
||||
if (!suffix)
|
||||
return "n/a";
|
||||
|
||||
p = strjoin("SIG", suffix);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
d->formatted = TAKE_PTR(p);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert_not_reached("Unexpected type?");
|
||||
}
|
||||
@@ -2374,6 +2478,9 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
|
||||
return json_variant_new_integer(ret, d->percent);
|
||||
|
||||
case TABLE_IFINDEX:
|
||||
if (d->ifindex <= 0)
|
||||
return json_variant_new_null(ret);
|
||||
|
||||
return json_variant_new_integer(ret, d->ifindex);
|
||||
|
||||
case TABLE_IN_ADDR:
|
||||
@@ -2392,6 +2499,30 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
|
||||
return json_variant_new_string(ret, id128_to_uuid_string(d->id128, buf));
|
||||
}
|
||||
|
||||
case TABLE_UID:
|
||||
if (!uid_is_valid(d->uid))
|
||||
return json_variant_new_null(ret);
|
||||
|
||||
return json_variant_new_integer(ret, d->uid);
|
||||
|
||||
case TABLE_GID:
|
||||
if (!gid_is_valid(d->gid))
|
||||
return json_variant_new_null(ret);
|
||||
|
||||
return json_variant_new_integer(ret, d->gid);
|
||||
|
||||
case TABLE_PID:
|
||||
if (!pid_is_valid(d->pid))
|
||||
return json_variant_new_null(ret);
|
||||
|
||||
return json_variant_new_integer(ret, d->pid);
|
||||
|
||||
case TABLE_SIGNAL:
|
||||
if (!SIGNAL_VALID(d->int_val))
|
||||
return json_variant_new_null(ret);
|
||||
|
||||
return json_variant_new_integer(ret, d->int_val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ typedef enum TableDataType {
|
||||
TABLE_IN6_ADDR, /* Takes a union in_addr_union (or a struct in6_addr) */
|
||||
TABLE_ID128,
|
||||
TABLE_UUID,
|
||||
TABLE_UID,
|
||||
TABLE_GID,
|
||||
TABLE_PID,
|
||||
TABLE_SIGNAL,
|
||||
_TABLE_DATA_TYPE_MAX,
|
||||
|
||||
/* The following are not really data types, but commands for table_add_cell_many() to make changes to
|
||||
@@ -57,16 +61,6 @@ 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));
|
||||
|
||||
/* UIDs/GIDs are just 32bit unsigned integers on Linux */
|
||||
#define TABLE_UID TABLE_UINT32
|
||||
#define TABLE_GID TABLE_UINT32
|
||||
assert_cc(sizeof(uid_t) == sizeof(uint32_t));
|
||||
assert_cc(sizeof(gid_t) == sizeof(uint32_t));
|
||||
|
||||
typedef struct Table Table;
|
||||
typedef struct TableCell TableCell;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user