#include "conf-files.h"
#include "copy.h"
#include "def.h"
+#include "exit-status.h"
#include "fd-util.h"
#include "fileio.h"
+#include "format-table.h"
#include "glob-util.h"
#include "hashmap.h"
#include "locale-util.h"
return 0;
}
-static int compare_unit_time(const struct unit_times *a, const struct unit_times *b) {
- return CMP(b->time, a->time);
-}
-
static int compare_unit_start(const struct unit_times *a, const struct unit_times *b) {
return CMP(a->activating, b->activating);
}
static int analyze_blame(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(unit_times_freep) struct unit_times *times = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
struct unit_times *u;
+ TableCell *cell;
int n, r;
r = acquire_bus(&bus, NULL);
if (n <= 0)
return n;
- typesafe_qsort(times, n, compare_unit_time);
+ table = table_new("time", "unit");
+ if (!table)
+ return log_oom();
+
+ table_set_header(table, false);
- (void) pager_open(arg_pager_flags);
+ assert_se(cell = table_get_cell(table, 0, 0));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ r = table_set_align_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ assert_se(cell = table_get_cell(table, 0, 1));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ r = table_set_sort(table, 0, SIZE_MAX);
+ if (r < 0)
+ return r;
+
+ r = table_set_reverse(table, 0, true);
+ if (r < 0)
+ return r;
for (u = times; u->has_data; u++) {
- char ts[FORMAT_TIMESPAN_MAX];
+ if (u->time <= 0)
+ continue;
+
+ r = table_add_cell(table, NULL, TABLE_TIMESPAN_MSEC, &u->time);
+ if (r < 0)
+ return r;
- if (u->time > 0)
- printf("%16s %s\n", format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC), u->name);
+ r = table_add_cell(table, NULL, TABLE_STRING, u->name);
+ if (r < 0)
+ return r;
}
- return 0;
+ (void) pager_open(arg_pager_flags);
+
+ return table_print(table, NULL);
}
static int analyze_time(int argc, char *argv[], void *userdata) {
printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
}
+static int dump_exit_codes(int argc, char *argv[], void *userdata) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ int r;
+
+ table = table_new("name", "code", "class");
+ if (!table)
+ return log_oom();
+
+ if (strv_isempty(strv_skip(argv, 1)))
+ for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) {
+ if (!exit_status_mappings[i].name)
+ continue;
+
+ r = table_add_many(table,
+ TABLE_STRING, exit_status_mappings[i].name,
+ TABLE_UINT, i,
+ TABLE_STRING, exit_status_class(i));
+ if (r < 0)
+ return r;
+ }
+ else
+ for (int i = 1; i < argc; i++) {
+ int code;
+
+ code = exit_status_from_string(argv[i]);
+ if (code < 0)
+ return log_error_errno(r, "Invalid exit code \"%s\": %m", argv[i]);
+
+ assert(code >= 0 && (size_t) code < ELEMENTSOF(exit_status_mappings));
+ r = table_add_many(table,
+ TABLE_STRING, exit_status_mappings[code].name ?: "-",
+ TABLE_UINT, code,
+ TABLE_STRING, exit_status_class(code) ?: "-");
+ if (r < 0)
+ return r;
+ }
+
+ (void) pager_open(arg_pager_flags);
+
+ return table_print(table, NULL);
+}
+
static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
bool first = true;
char **input_timespan;
STRV_FOREACH(input_timespan, strv_skip(argv, 1)) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ usec_t output_usecs;
+ TableCell *cell;
int r;
- usec_t usec_magnitude = 1, output_usecs;
- char ft_buf[FORMAT_TIMESPAN_MAX];
r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC);
if (r < 0) {
return r;
}
- printf("Original: %s\n", *input_timespan);
- printf(" %ss: %" PRIu64 "\n", special_glyph(SPECIAL_GLYPH_MU), output_usecs);
- printf(" Human: %s%s%s\n",
- ansi_highlight(),
- format_timespan(ft_buf, sizeof(ft_buf), output_usecs, usec_magnitude),
- ansi_normal());
+ table = table_new("name", "value");
+ if (!table)
+ return log_oom();
+
+ table_set_header(table, false);
+
+ assert_se(cell = table_get_cell(table, 0, 0));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ r = table_set_align_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ assert_se(cell = table_get_cell(table, 0, 1));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "Original:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, *input_timespan);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU));
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_UINT64, &output_usecs);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "Human:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_TIMESPAN, &output_usecs);
+ if (r < 0)
+ return r;
+
+ r = table_set_color(table, cell, ansi_highlight());
+ if (r < 0)
+ return r;
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return r;
if (input_timespan[1])
putchar('\n');
}
static int test_timestamp_one(const char *p) {
+ _cleanup_(table_unrefp) Table *table = NULL;
+ TableCell *cell;
usec_t usec;
- char buf[FORMAT_TIMESTAMP_MAX];
int r;
r = parse_timestamp(p, &usec);
return r;
}
- printf(" Original form: %s\n", p);
- printf("Normalized form: %s%s%s\n",
- ansi_highlight_blue(),
- format_timestamp(buf, sizeof buf, usec),
- ansi_normal());
+ table = table_new("name", "value");
+ if (!table)
+ return log_oom();
- if (!in_utc_timezone())
- printf(" (in UTC): %s\n", format_timestamp_utc(buf, sizeof buf, usec));
+ table_set_header(table, false);
- printf(" UNIX seconds: @%"PRI_USEC"%s%0*"PRI_USEC"\n",
- usec / USEC_PER_SEC,
- usec % USEC_PER_SEC ? "." : "",
- usec % USEC_PER_SEC ? 6 : 0,
- usec % USEC_PER_SEC);
+ assert_se(cell = table_get_cell(table, 0, 0));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
- printf(" From now: %s\n", format_timestamp_relative(buf, sizeof buf, usec));
+ r = table_set_align_percent(table, cell, 100);
+ if (r < 0)
+ return r;
- return 0;
+ assert_se(cell = table_get_cell(table, 0, 1));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, p);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &usec);
+ if (r < 0)
+ return r;
+
+ r = table_set_color(table, cell, ansi_highlight_blue());
+ if (r < 0)
+ return r;
+
+ if (!in_utc_timezone()) {
+ r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_TIMESTAMP_UTC, &usec);
+ if (r < 0)
+ return r;
+ }
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:");
+ if (r < 0)
+ return r;
+
+ if (usec % USEC_PER_SEC == 0)
+ r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC,
+ usec / USEC_PER_SEC);
+ else
+ r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC".%06"PRI_USEC"",
+ usec / USEC_PER_SEC,
+ usec % USEC_PER_SEC);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_TIMESTAMP_RELATIVE, &usec);
+ if (r < 0)
+ return r;
+
+ return table_print(table, NULL);
}
static int test_timestamp(int argc, char *argv[], void *userdata) {
static int test_calendar_one(usec_t n, const char *p) {
_cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
_cleanup_free_ char *t = NULL;
+ TableCell *cell;
int r;
r = calendar_spec_from_string(p, &spec);
if (r < 0)
return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
- if (!streq(t, p))
- printf(" Original form: %s\n", p);
+ table = table_new("name", "value");
+ if (!table)
+ return log_oom();
+
+ table_set_header(table, false);
+
+ assert_se(cell = table_get_cell(table, 0, 0));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ r = table_set_align_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ assert_se(cell = table_get_cell(table, 0, 1));
+ r = table_set_ellipsize_percent(table, cell, 100);
+ if (r < 0)
+ return r;
+
+ if (!streq(t, p)) {
+ r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_STRING, p);
+ if (r < 0)
+ return r;
+ }
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
+ if (r < 0)
+ return r;
- printf("Normalized form: %s\n", t);
+ r = table_add_cell(table, NULL, TABLE_STRING, t);
+ if (r < 0)
+ return r;
for (unsigned i = 0; i < arg_iterations; i++) {
- char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
usec_t next;
r = calendar_spec_next_usec(spec, n, &next);
if (r == -ENOENT) {
- if (i == 0)
- printf(" Next elapse: %snever%s\n", ansi_highlight_yellow(), ansi_normal());
- return 0;
+ if (i == 0) {
+ r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_STRING, "never");
+ if (r < 0)
+ return r;
+
+ r = table_set_color(table, cell, ansi_highlight_yellow());
+ if (r < 0)
+ return r;
+ }
+ break;
}
if (r < 0)
return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p);
- if (i == 0)
- printf(" Next elapse: %s%s%s\n",
- ansi_highlight_blue(),
- format_timestamp(buffer, sizeof(buffer), next),
- ansi_normal());
- else {
+ if (i == 0) {
+ r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
+ if (r < 0)
+ return r;
+
+ r = table_set_color(table, cell, ansi_highlight_blue());
+ if (r < 0)
+ return r;
+ } else {
int k = DECIMAL_STR_WIDTH(i + 1);
if (k < 8)
else
k = 0;
- printf("%*sIter. #%u: %s%s%s\n",
- k, "", i+1,
- ansi_highlight_blue(), format_timestamp(buffer, sizeof(buffer), next), ansi_normal());
+ r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
+ if (r < 0)
+ return r;
+
+ r = table_set_color(table, cell, ansi_highlight_blue());
+ if (r < 0)
+ return r;
}
- if (!in_utc_timezone())
- printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
+ if (!in_utc_timezone()) {
+ r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
+ if (r < 0)
+ return r;
- printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
+ r = table_add_cell(table, NULL, TABLE_TIMESTAMP_UTC, &next);
+ if (r < 0)
+ return r;
+ }
+
+ r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
+ if (r < 0)
+ return r;
+
+ r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &next);
+ if (r < 0)
+ return r;
n = next;
}
- return 0;
+ return table_print(table, NULL);
}
static int test_calendar(int argc, char *argv[], void *userdata) {
}
static int help(int argc, char *argv[], void *userdata) {
- _cleanup_free_ char *link = NULL;
+ _cleanup_free_ char *link = NULL, *dot_link = NULL;
int r;
(void) pager_open(arg_pager_flags);
if (r < 0)
return log_oom();
+ /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
+ r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
+ if (r < 0)
+ return log_oom();
+
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Profile systemd, show unit dependencies, check unit files.\n\n"
" -h --help Show this help\n"
" blame Print list of running units ordered by time to init\n"
" critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
- " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
+ " dot [UNIT...] Output dependency graph in %s format\n"
" log-level [LEVEL] Get/set logging threshold for manager\n"
" log-target [TARGET] Get/set logging target for manager\n"
" dump Output state serialization of service manager\n"
" cat-config Show configuration file and drop-ins\n"
" unit-paths List load directories for units\n"
+ " exit-codes List exit code definitions\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
" condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\n"
" security [UNIT...] Analyze security of unit\n"
"\nSee the %s for details.\n"
, program_invocation_short_name
+ , dot_link
, link
);
{ "dump", VERB_ANY, 1, 0, dump },
{ "cat-config", 2, VERB_ANY, 0, cat_config },
{ "unit-paths", 1, 1, 0, dump_unit_paths },
+ { "exit-codes", VERB_ANY, VERB_ANY, 0, dump_exit_codes },
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
{ "condition", 2, VERB_ANY, 0, do_condition },
{ "verify", 2, VERB_ANY, 0, do_verify },