]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/analyze/analyze.c
Merge pull request #13207 from keszybz/symbolic-exit-code-names
[thirdparty/systemd.git] / src / analyze / analyze.c
index 97642660e49f4a2da6dafbbf7a4eba9ee8115c8c..45e41fedee2b9dc33ee06e90a7f8ee1ea286c863 100644 (file)
 #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"
@@ -204,10 +206,6 @@ static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char
         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);
 }
@@ -1070,7 +1068,9 @@ static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
 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);
@@ -1081,18 +1081,50 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
         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) {
@@ -1606,6 +1638,48 @@ static void dump_syscall_filter(const SyscallFilterSet *set) {
                 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;
 
@@ -1690,9 +1764,10 @@ static int dump_timespan(int argc, char *argv[], void *userdata) {
         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) {
@@ -1701,12 +1776,57 @@ static int dump_timespan(int argc, char *argv[], void *userdata) {
                         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');
@@ -1716,8 +1836,9 @@ static int dump_timespan(int argc, char *argv[], void *userdata) {
 }
 
 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);
@@ -1727,24 +1848,79 @@ static int test_timestamp_one(const char *p) {
                 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) {
@@ -1765,7 +1941,9 @@ 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);
@@ -1779,30 +1957,80 @@ static int test_calendar_one(usec_t n, const char *p) {
         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)
@@ -1810,20 +2038,41 @@ static int test_calendar_one(usec_t n, const char *p) {
                         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) {
@@ -1920,7 +2169,7 @@ static int do_security(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);
@@ -1929,6 +2178,11 @@ static int help(int argc, char *argv[], void *userdata) {
         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"
@@ -1953,12 +2207,13 @@ static int help(int argc, char *argv[], void *userdata) {
                "  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"
@@ -1969,6 +2224,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  security [UNIT...]       Analyze security of unit\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
+               , dot_link
                , link
         );
 
@@ -2162,6 +2418,7 @@ static int run(int argc, char *argv[]) {
                 { "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              },