LCOV - code coverage report
Current view: top level - analyze - analyze.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 30 1229 2.4 %
Date: 2019-08-22 15:41:25 Functions: 6 62 9.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : /***
       3             :   Copyright © 2013 Simon Peeters
       4             : ***/
       5             : 
       6             : #include <getopt.h>
       7             : #include <inttypes.h>
       8             : #include <locale.h>
       9             : #include <stdio.h>
      10             : #include <stdlib.h>
      11             : #include <unistd.h>
      12             : 
      13             : #include "sd-bus.h"
      14             : 
      15             : #include "alloc-util.h"
      16             : #include "analyze-condition.h"
      17             : #include "analyze-security.h"
      18             : #include "analyze-verify.h"
      19             : #include "build.h"
      20             : #include "bus-error.h"
      21             : #include "bus-unit-util.h"
      22             : #include "bus-util.h"
      23             : #include "calendarspec.h"
      24             : #include "conf-files.h"
      25             : #include "copy.h"
      26             : #include "def.h"
      27             : #include "exit-status.h"
      28             : #include "fd-util.h"
      29             : #include "fileio.h"
      30             : #include "format-table.h"
      31             : #include "glob-util.h"
      32             : #include "hashmap.h"
      33             : #include "locale-util.h"
      34             : #include "log.h"
      35             : #include "main-func.h"
      36             : #include "nulstr-util.h"
      37             : #include "pager.h"
      38             : #include "parse-util.h"
      39             : #include "path-util.h"
      40             : #include "pretty-print.h"
      41             : #if HAVE_SECCOMP
      42             : #  include "seccomp-util.h"
      43             : #endif
      44             : #include "sort-util.h"
      45             : #include "special.h"
      46             : #include "strv.h"
      47             : #include "strxcpyx.h"
      48             : #include "terminal-util.h"
      49             : #include "time-util.h"
      50             : #include "unit-name.h"
      51             : #include "util.h"
      52             : #include "verbs.h"
      53             : 
      54             : #define SCALE_X (0.1 / 1000.0) /* pixels per us */
      55             : #define SCALE_Y (20.0)
      56             : 
      57             : #define svg(...) printf(__VA_ARGS__)
      58             : 
      59             : #define svg_bar(class, x1, x2, y)                                       \
      60             :         svg("  <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
      61             :             (class),                                                    \
      62             :             SCALE_X * (x1), SCALE_Y * (y),                              \
      63             :             SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
      64             : 
      65             : #define svg_text(b, x, y, format, ...)                                  \
      66             :         do {                                                            \
      67             :                 svg("  <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
      68             :                 svg(format, ## __VA_ARGS__);                            \
      69             :                 svg("</text>\n");                                       \
      70             :         } while (false)
      71             : 
      72             : static enum dot {
      73             :         DEP_ALL,
      74             :         DEP_ORDER,
      75             :         DEP_REQUIRE
      76             : } arg_dot = DEP_ALL;
      77             : static char **arg_dot_from_patterns = NULL;
      78             : static char **arg_dot_to_patterns = NULL;
      79             : static usec_t arg_fuzz = 0;
      80             : static PagerFlags arg_pager_flags = 0;
      81             : static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
      82             : static const char *arg_host = NULL;
      83             : static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
      84             : static bool arg_man = true;
      85             : static bool arg_generators = false;
      86             : static const char *arg_root = NULL;
      87             : static unsigned arg_iterations = 1;
      88             : 
      89           4 : STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
      90           4 : STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
      91             : 
      92             : struct boot_times {
      93             :         usec_t firmware_time;
      94             :         usec_t loader_time;
      95             :         usec_t kernel_time;
      96             :         usec_t kernel_done_time;
      97             :         usec_t initrd_time;
      98             :         usec_t userspace_time;
      99             :         usec_t finish_time;
     100             :         usec_t security_start_time;
     101             :         usec_t security_finish_time;
     102             :         usec_t generators_start_time;
     103             :         usec_t generators_finish_time;
     104             :         usec_t unitsload_start_time;
     105             :         usec_t unitsload_finish_time;
     106             :         usec_t initrd_security_start_time;
     107             :         usec_t initrd_security_finish_time;
     108             :         usec_t initrd_generators_start_time;
     109             :         usec_t initrd_generators_finish_time;
     110             :         usec_t initrd_unitsload_start_time;
     111             :         usec_t initrd_unitsload_finish_time;
     112             : 
     113             :         /*
     114             :          * If we're analyzing the user instance, all timestamps will be offset
     115             :          * by its own start-up timestamp, which may be arbitrarily big.
     116             :          * With "plot", this causes arbitrarily wide output SVG files which almost
     117             :          * completely consist of empty space. Thus we cancel out this offset.
     118             :          *
     119             :          * This offset is subtracted from times above by acquire_boot_times(),
     120             :          * but it still needs to be subtracted from unit-specific timestamps
     121             :          * (so it is stored here for reference).
     122             :          */
     123             :         usec_t reverse_offset;
     124             : };
     125             : 
     126             : struct unit_times {
     127             :         bool has_data;
     128             :         char *name;
     129             :         usec_t activating;
     130             :         usec_t activated;
     131             :         usec_t deactivated;
     132             :         usec_t deactivating;
     133             :         usec_t time;
     134             : };
     135             : 
     136             : struct host_info {
     137             :         char *hostname;
     138             :         char *kernel_name;
     139             :         char *kernel_release;
     140             :         char *kernel_version;
     141             :         char *os_pretty_name;
     142             :         char *virtualization;
     143             :         char *architecture;
     144             : };
     145             : 
     146           0 : static int acquire_bus(sd_bus **bus, bool *use_full_bus) {
     147           0 :         bool user = arg_scope != UNIT_FILE_SYSTEM;
     148             :         int r;
     149             : 
     150           0 :         if (use_full_bus && *use_full_bus) {
     151           0 :                 r = bus_connect_transport(arg_transport, arg_host, user, bus);
     152           0 :                 if (IN_SET(r, 0, -EHOSTDOWN))
     153           0 :                         return r;
     154             : 
     155           0 :                 *use_full_bus = false;
     156             :         }
     157             : 
     158           0 :         return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
     159             : }
     160             : 
     161           0 : static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
     162           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     163             :         int r;
     164             : 
     165           0 :         assert(bus);
     166           0 :         assert(path);
     167           0 :         assert(interface);
     168           0 :         assert(property);
     169           0 :         assert(val);
     170             : 
     171           0 :         r = sd_bus_get_property_trivial(
     172             :                         bus,
     173             :                         "org.freedesktop.systemd1",
     174             :                         path,
     175             :                         interface,
     176             :                         property,
     177             :                         &error,
     178             :                         't', val);
     179             : 
     180           0 :         if (r < 0)
     181           0 :                 return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r));
     182             : 
     183           0 :         return 0;
     184             : }
     185             : 
     186           0 : static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
     187           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     188             :         int r;
     189             : 
     190           0 :         assert(bus);
     191           0 :         assert(path);
     192           0 :         assert(property);
     193           0 :         assert(strv);
     194             : 
     195           0 :         r = sd_bus_get_property_strv(
     196             :                         bus,
     197             :                         "org.freedesktop.systemd1",
     198             :                         path,
     199             :                         "org.freedesktop.systemd1.Unit",
     200             :                         property,
     201             :                         &error,
     202             :                         strv);
     203           0 :         if (r < 0)
     204           0 :                 return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
     205             : 
     206           0 :         return 0;
     207             : }
     208             : 
     209           0 : static int compare_unit_start(const struct unit_times *a, const struct unit_times *b) {
     210           0 :         return CMP(a->activating, b->activating);
     211             : }
     212             : 
     213           0 : static void unit_times_free(struct unit_times *t) {
     214             :         struct unit_times *p;
     215             : 
     216           0 :         for (p = t; p->has_data; p++)
     217           0 :                 free(p->name);
     218           0 :         free(t);
     219           0 : }
     220             : 
     221           0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times *, unit_times_free);
     222             : 
     223           0 : static void subtract_timestamp(usec_t *a, usec_t b) {
     224           0 :         assert(a);
     225             : 
     226           0 :         if (*a > 0) {
     227           0 :                 assert(*a >= b);
     228           0 :                 *a -= b;
     229             :         }
     230           0 : }
     231             : 
     232           0 : static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
     233             :         static const struct bus_properties_map property_map[] = {
     234             :                 { "FirmwareTimestampMonotonic",               "t", NULL, offsetof(struct boot_times, firmware_time)                 },
     235             :                 { "LoaderTimestampMonotonic",                 "t", NULL, offsetof(struct boot_times, loader_time)                   },
     236             :                 { "KernelTimestamp",                          "t", NULL, offsetof(struct boot_times, kernel_time)                   },
     237             :                 { "InitRDTimestampMonotonic",                 "t", NULL, offsetof(struct boot_times, initrd_time)                   },
     238             :                 { "UserspaceTimestampMonotonic",              "t", NULL, offsetof(struct boot_times, userspace_time)                },
     239             :                 { "FinishTimestampMonotonic",                 "t", NULL, offsetof(struct boot_times, finish_time)                   },
     240             :                 { "SecurityStartTimestampMonotonic",          "t", NULL, offsetof(struct boot_times, security_start_time)           },
     241             :                 { "SecurityFinishTimestampMonotonic",         "t", NULL, offsetof(struct boot_times, security_finish_time)          },
     242             :                 { "GeneratorsStartTimestampMonotonic",        "t", NULL, offsetof(struct boot_times, generators_start_time)         },
     243             :                 { "GeneratorsFinishTimestampMonotonic",       "t", NULL, offsetof(struct boot_times, generators_finish_time)        },
     244             :                 { "UnitsLoadStartTimestampMonotonic",         "t", NULL, offsetof(struct boot_times, unitsload_start_time)          },
     245             :                 { "UnitsLoadFinishTimestampMonotonic",        "t", NULL, offsetof(struct boot_times, unitsload_finish_time)         },
     246             :                 { "InitRDSecurityStartTimestampMonotonic",    "t", NULL, offsetof(struct boot_times, initrd_security_start_time)    },
     247             :                 { "InitRDSecurityFinishTimestampMonotonic",   "t", NULL, offsetof(struct boot_times, initrd_security_finish_time)   },
     248             :                 { "InitRDGeneratorsStartTimestampMonotonic",  "t", NULL, offsetof(struct boot_times, initrd_generators_start_time)  },
     249             :                 { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(struct boot_times, initrd_generators_finish_time) },
     250             :                 { "InitRDUnitsLoadStartTimestampMonotonic",   "t", NULL, offsetof(struct boot_times, initrd_unitsload_start_time)   },
     251             :                 { "InitRDUnitsLoadFinishTimestampMonotonic",  "t", NULL, offsetof(struct boot_times, initrd_unitsload_finish_time)  },
     252             :                 {},
     253             :         };
     254           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     255             :         static struct boot_times times;
     256             :         static bool cached = false;
     257             :         int r;
     258             : 
     259           0 :         if (cached)
     260           0 :                 goto finish;
     261             : 
     262             :         assert_cc(sizeof(usec_t) == sizeof(uint64_t));
     263             : 
     264           0 :         r = bus_map_all_properties(
     265             :                         bus,
     266             :                         "org.freedesktop.systemd1",
     267             :                         "/org/freedesktop/systemd1",
     268             :                         property_map,
     269             :                         BUS_MAP_STRDUP,
     270             :                         &error,
     271             :                         NULL,
     272             :                         &times);
     273           0 :         if (r < 0)
     274           0 :                 return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r));
     275             : 
     276           0 :         if (times.finish_time <= 0)
     277           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS),
     278             :                                        "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
     279             :                                        "Please try again later.\n"
     280             :                                        "Hint: Use 'systemctl%s list-jobs' to see active jobs",
     281             :                                        times.finish_time,
     282             :                                        arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
     283             : 
     284           0 :         if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) {
     285             :                 /* security_start_time is set when systemd is not running under container environment. */
     286           0 :                 if (times.initrd_time > 0)
     287           0 :                         times.kernel_done_time = times.initrd_time;
     288             :                 else
     289           0 :                         times.kernel_done_time = times.userspace_time;
     290             :         } else {
     291             :                 /*
     292             :                  * User-instance-specific or container-system-specific timestamps processing
     293             :                  * (see comment to reverse_offset in struct boot_times).
     294             :                  */
     295           0 :                 times.reverse_offset = times.userspace_time;
     296             : 
     297           0 :                 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
     298           0 :                         times.userspace_time = times.security_start_time = times.security_finish_time = 0;
     299             : 
     300           0 :                 subtract_timestamp(&times.finish_time, times.reverse_offset);
     301             : 
     302           0 :                 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
     303           0 :                 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
     304             : 
     305           0 :                 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
     306           0 :                 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
     307             :         }
     308             : 
     309           0 :         cached = true;
     310             : 
     311           0 : finish:
     312           0 :         *bt = &times;
     313           0 :         return 0;
     314             : }
     315             : 
     316           0 : static void free_host_info(struct host_info *hi) {
     317           0 :         if (!hi)
     318           0 :                 return;
     319             : 
     320           0 :         free(hi->hostname);
     321           0 :         free(hi->kernel_name);
     322           0 :         free(hi->kernel_release);
     323           0 :         free(hi->kernel_version);
     324           0 :         free(hi->os_pretty_name);
     325           0 :         free(hi->virtualization);
     326           0 :         free(hi->architecture);
     327           0 :         free(hi);
     328             : }
     329             : 
     330           0 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info *, free_host_info);
     331             : 
     332           0 : static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
     333             :         static const struct bus_properties_map property_map[] = {
     334             :                 { "InactiveExitTimestampMonotonic",  "t", NULL, offsetof(struct unit_times, activating)   },
     335             :                 { "ActiveEnterTimestampMonotonic",   "t", NULL, offsetof(struct unit_times, activated)    },
     336             :                 { "ActiveExitTimestampMonotonic",    "t", NULL, offsetof(struct unit_times, deactivating) },
     337             :                 { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(struct unit_times, deactivated)  },
     338             :                 {},
     339             :         };
     340           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     341           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     342           0 :         _cleanup_(unit_times_freep) struct unit_times *unit_times = NULL;
     343           0 :         struct boot_times *boot_times = NULL;
     344           0 :         size_t allocated = 0, c = 0;
     345             :         UnitInfo u;
     346             :         int r;
     347             : 
     348           0 :         r = acquire_boot_times(bus, &boot_times);
     349           0 :         if (r < 0)
     350           0 :                 return r;
     351             : 
     352           0 :         r = sd_bus_call_method(
     353             :                         bus,
     354             :                         "org.freedesktop.systemd1",
     355             :                         "/org/freedesktop/systemd1",
     356             :                         "org.freedesktop.systemd1.Manager",
     357             :                         "ListUnits",
     358             :                         &error, &reply,
     359             :                         NULL);
     360           0 :         if (r < 0)
     361           0 :                 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
     362             : 
     363           0 :         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
     364           0 :         if (r < 0)
     365           0 :                 return bus_log_parse_error(r);
     366             : 
     367           0 :         while ((r = bus_parse_unit_info(reply, &u)) > 0) {
     368             :                 struct unit_times *t;
     369             : 
     370           0 :                 if (!GREEDY_REALLOC(unit_times, allocated, c + 2))
     371           0 :                         return log_oom();
     372             : 
     373           0 :                 unit_times[c + 1].has_data = false;
     374           0 :                 t = &unit_times[c];
     375           0 :                 t->name = NULL;
     376             : 
     377             :                 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
     378             : 
     379           0 :                 r = bus_map_all_properties(
     380             :                                 bus,
     381             :                                 "org.freedesktop.systemd1",
     382             :                                 u.unit_path,
     383             :                                 property_map,
     384             :                                 BUS_MAP_STRDUP,
     385             :                                 &error,
     386             :                                 NULL,
     387             :                                 t);
     388           0 :                 if (r < 0)
     389           0 :                         return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
     390             :                                                u.id, bus_error_message(&error, r));
     391             : 
     392           0 :                 subtract_timestamp(&t->activating, boot_times->reverse_offset);
     393           0 :                 subtract_timestamp(&t->activated, boot_times->reverse_offset);
     394           0 :                 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
     395           0 :                 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
     396             : 
     397           0 :                 if (t->activated >= t->activating)
     398           0 :                         t->time = t->activated - t->activating;
     399           0 :                 else if (t->deactivated >= t->activating)
     400           0 :                         t->time = t->deactivated - t->activating;
     401             :                 else
     402           0 :                         t->time = 0;
     403             : 
     404           0 :                 if (t->activating == 0)
     405           0 :                         continue;
     406             : 
     407           0 :                 t->name = strdup(u.id);
     408           0 :                 if (!t->name)
     409           0 :                         return log_oom();
     410             : 
     411           0 :                 t->has_data = true;
     412           0 :                 c++;
     413             :         }
     414           0 :         if (r < 0)
     415           0 :                 return bus_log_parse_error(r);
     416             : 
     417           0 :         *out = TAKE_PTR(unit_times);
     418           0 :         return c;
     419             : }
     420             : 
     421           0 : static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
     422             :         static const struct bus_properties_map hostname_map[] = {
     423             :                 { "Hostname",                  "s", NULL, offsetof(struct host_info, hostname)       },
     424             :                 { "KernelName",                "s", NULL, offsetof(struct host_info, kernel_name)    },
     425             :                 { "KernelRelease",             "s", NULL, offsetof(struct host_info, kernel_release) },
     426             :                 { "KernelVersion",             "s", NULL, offsetof(struct host_info, kernel_version) },
     427             :                 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
     428             :                 {}
     429             :         };
     430             : 
     431             :         static const struct bus_properties_map manager_map[] = {
     432             :                 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
     433             :                 { "Architecture",   "s", NULL, offsetof(struct host_info, architecture)   },
     434             :                 {}
     435             :         };
     436             : 
     437           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     438           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
     439           0 :         _cleanup_(free_host_infop) struct host_info *host;
     440             :         int r;
     441             : 
     442           0 :         host = new0(struct host_info, 1);
     443           0 :         if (!host)
     444           0 :                 return log_oom();
     445             : 
     446           0 :         if (arg_scope != UNIT_FILE_SYSTEM) {
     447           0 :                 r = bus_connect_transport(arg_transport, arg_host, false, &system_bus);
     448           0 :                 if (r < 0) {
     449           0 :                         log_debug_errno(r, "Failed to connect to system bus, ignoring: %m");
     450           0 :                         goto manager;
     451             :                 }
     452             :         }
     453             : 
     454           0 :         r = bus_map_all_properties(
     455           0 :                         system_bus ?: bus,
     456             :                         "org.freedesktop.hostname1",
     457             :                         "/org/freedesktop/hostname1",
     458             :                         hostname_map,
     459             :                         BUS_MAP_STRDUP,
     460             :                         &error,
     461             :                         NULL,
     462             :                         host);
     463           0 :         if (r < 0) {
     464           0 :                 log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s",
     465             :                                 bus_error_message(&error, r));
     466           0 :                 sd_bus_error_free(&error);
     467             :         }
     468             : 
     469           0 : manager:
     470           0 :         r = bus_map_all_properties(
     471             :                         bus,
     472             :                         "org.freedesktop.systemd1",
     473             :                         "/org/freedesktop/systemd1",
     474             :                         manager_map,
     475             :                         BUS_MAP_STRDUP,
     476             :                         &error,
     477             :                         NULL,
     478             :                         host);
     479           0 :         if (r < 0)
     480           0 :                 return log_error_errno(r, "Failed to get host information from systemd: %s",
     481             :                                        bus_error_message(&error, r));
     482             : 
     483           0 :         *hi = TAKE_PTR(host);
     484           0 :         return 0;
     485             : }
     486             : 
     487           0 : static int pretty_boot_time(sd_bus *bus, char **_buf) {
     488             :         char ts[FORMAT_TIMESPAN_MAX];
     489             :         struct boot_times *t;
     490             :         static char buf[4096];
     491             :         size_t size;
     492             :         char *ptr;
     493             :         int r;
     494           0 :         usec_t activated_time = USEC_INFINITY;
     495           0 :         _cleanup_free_ char *path = NULL, *unit_id = NULL;
     496           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     497             : 
     498           0 :         r = acquire_boot_times(bus, &t);
     499           0 :         if (r < 0)
     500           0 :                 return r;
     501             : 
     502           0 :         path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
     503           0 :         if (!path)
     504           0 :                 return log_oom();
     505             : 
     506           0 :         r = sd_bus_get_property_string(
     507             :                         bus,
     508             :                         "org.freedesktop.systemd1",
     509             :                         path,
     510             :                         "org.freedesktop.systemd1.Unit",
     511             :                         "Id",
     512             :                         &error,
     513             :                         &unit_id);
     514           0 :         if (r < 0) {
     515           0 :                 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
     516           0 :                 unit_id = NULL;
     517             :         }
     518             : 
     519           0 :         r = bus_get_uint64_property(bus, path,
     520             :                         "org.freedesktop.systemd1.Unit",
     521             :                         "ActiveEnterTimestampMonotonic",
     522             :                         &activated_time);
     523           0 :         if (r < 0) {
     524           0 :                 log_info_errno(r, "Could not get time to reach default.target, ignoring: %m");
     525           0 :                 activated_time = USEC_INFINITY;
     526             :         }
     527             : 
     528           0 :         ptr = buf;
     529           0 :         size = sizeof(buf);
     530             : 
     531           0 :         size = strpcpyf(&ptr, size, "Startup finished in ");
     532           0 :         if (t->firmware_time > 0)
     533           0 :                 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
     534           0 :         if (t->loader_time > 0)
     535           0 :                 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
     536           0 :         if (t->kernel_done_time > 0)
     537           0 :                 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
     538           0 :         if (t->initrd_time > 0)
     539           0 :                 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
     540             : 
     541           0 :         size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
     542           0 :         if (t->kernel_done_time > 0)
     543           0 :                 strpcpyf(&ptr, size, "= %s ", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
     544             : 
     545           0 :         if (unit_id && timestamp_is_set(activated_time)) {
     546           0 :                 usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
     547             : 
     548           0 :                 size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id,
     549             :                                 format_timespan(ts, sizeof(ts), activated_time - base, USEC_PER_MSEC));
     550           0 :         } else if (unit_id && activated_time == 0)
     551           0 :                 size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
     552           0 :         else if (unit_id && activated_time == USEC_INFINITY)
     553           0 :                 size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id);
     554           0 :         else if (!unit_id)
     555           0 :                 size = strpcpyf(&ptr, size, "\ncould not find default.target");
     556             : 
     557           0 :         ptr = strdup(buf);
     558           0 :         if (!ptr)
     559           0 :                 return log_oom();
     560             : 
     561           0 :         *_buf = ptr;
     562           0 :         return 0;
     563             : }
     564             : 
     565           0 : static void svg_graph_box(double height, double begin, double end) {
     566             :         long long i;
     567             : 
     568             :         /* outside box, fill */
     569           0 :         svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
     570             :             SCALE_X * (end - begin),
     571             :             SCALE_Y * height);
     572             : 
     573           0 :         for (i = ((long long) (begin / 100000)) * 100000; i <= end; i += 100000) {
     574             :                 /* lines for each second */
     575           0 :                 if (i % 5000000 == 0)
     576           0 :                         svg("  <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
     577             :                             "  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
     578             :                             SCALE_X * i,
     579             :                             SCALE_X * i,
     580             :                             SCALE_Y * height,
     581             :                             SCALE_X * i,
     582             :                             -5.0,
     583             :                             0.000001 * i);
     584           0 :                 else if (i % 1000000 == 0)
     585           0 :                         svg("  <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
     586             :                             "  <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
     587             :                             SCALE_X * i,
     588             :                             SCALE_X * i,
     589             :                             SCALE_Y * height,
     590             :                             SCALE_X * i,
     591             :                             -5.0,
     592             :                             0.000001 * i);
     593             :                 else
     594           0 :                         svg("  <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
     595             :                             SCALE_X * i,
     596             :                             SCALE_X * i,
     597             :                             SCALE_Y * height);
     598             :         }
     599           0 : }
     600             : 
     601           0 : static int plot_unit_times(struct unit_times *u, double width, int y) {
     602             :         char ts[FORMAT_TIMESPAN_MAX];
     603             :         bool b;
     604             : 
     605           0 :         if (!u->name)
     606           0 :                 return 0;
     607             : 
     608           0 :         svg_bar("activating",   u->activating, u->activated, y);
     609           0 :         svg_bar("active",       u->activated, u->deactivating, y);
     610           0 :         svg_bar("deactivating", u->deactivating, u->deactivated, y);
     611             : 
     612             :         /* place the text on the left if we have passed the half of the svg width */
     613           0 :         b = u->activating * SCALE_X < width / 2;
     614           0 :         if (u->time)
     615           0 :                 svg_text(b, u->activating, y, "%s (%s)",
     616             :                          u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
     617             :         else
     618           0 :                 svg_text(b, u->activating, y, "%s", u->name);
     619             : 
     620           0 :         return 1;
     621             : }
     622             : 
     623           0 : static int analyze_plot(int argc, char *argv[], void *userdata) {
     624           0 :         _cleanup_(free_host_infop) struct host_info *host = NULL;
     625           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
     626           0 :         _cleanup_(unit_times_freep) struct unit_times *times = NULL;
     627           0 :         _cleanup_free_ char *pretty_times = NULL;
     628           0 :         bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM;
     629             :         struct boot_times *boot;
     630             :         struct unit_times *u;
     631           0 :         int n, m = 1, y = 0, r;
     632             :         double width;
     633             : 
     634           0 :         r = acquire_bus(&bus, &use_full_bus);
     635           0 :         if (r < 0)
     636           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
     637             : 
     638           0 :         n = acquire_boot_times(bus, &boot);
     639           0 :         if (n < 0)
     640           0 :                 return n;
     641             : 
     642           0 :         n = pretty_boot_time(bus, &pretty_times);
     643           0 :         if (n < 0)
     644           0 :                 return n;
     645             : 
     646           0 :         if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) {
     647           0 :                 n = acquire_host_info(bus, &host);
     648           0 :                 if (n < 0)
     649           0 :                         return n;
     650             :         }
     651             : 
     652           0 :         n = acquire_time_data(bus, &times);
     653           0 :         if (n <= 0)
     654           0 :                 return n;
     655             : 
     656           0 :         typesafe_qsort(times, n, compare_unit_start);
     657             : 
     658           0 :         width = SCALE_X * (boot->firmware_time + boot->finish_time);
     659           0 :         if (width < 800.0)
     660           0 :                 width = 800.0;
     661             : 
     662           0 :         if (boot->firmware_time > boot->loader_time)
     663           0 :                 m++;
     664           0 :         if (boot->loader_time > 0) {
     665           0 :                 m++;
     666           0 :                 if (width < 1000.0)
     667           0 :                         width = 1000.0;
     668             :         }
     669           0 :         if (boot->initrd_time > 0)
     670           0 :                 m++;
     671           0 :         if (boot->kernel_done_time > 0)
     672           0 :                 m++;
     673             : 
     674           0 :         for (u = times; u->has_data; u++) {
     675             :                 double text_start, text_width;
     676             : 
     677           0 :                 if (u->activating > boot->finish_time) {
     678           0 :                         u->name = mfree(u->name);
     679           0 :                         continue;
     680             :                 }
     681             : 
     682             :                 /* If the text cannot fit on the left side then
     683             :                  * increase the svg width so it fits on the right.
     684             :                  * TODO: calculate the text width more accurately */
     685           0 :                 text_width = 8.0 * strlen(u->name);
     686           0 :                 text_start = (boot->firmware_time + u->activating) * SCALE_X;
     687           0 :                 if (text_width > text_start && text_width + text_start > width)
     688           0 :                         width = text_width + text_start;
     689             : 
     690           0 :                 if (u->deactivated > u->activating &&
     691           0 :                     u->deactivated <= boot->finish_time &&
     692           0 :                     u->activated == 0 && u->deactivating == 0)
     693           0 :                         u->activated = u->deactivating = u->deactivated;
     694           0 :                 if (u->activated < u->activating || u->activated > boot->finish_time)
     695           0 :                         u->activated = boot->finish_time;
     696           0 :                 if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
     697           0 :                         u->deactivating = boot->finish_time;
     698           0 :                 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
     699           0 :                         u->deactivated = boot->finish_time;
     700           0 :                 m++;
     701             :         }
     702             : 
     703           0 :         svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
     704             :             "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
     705             :             "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
     706             : 
     707           0 :         svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
     708             :             "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
     709             :                         80.0 + width, 150.0 + (m * SCALE_Y) +
     710             :                         5 * SCALE_Y /* legend */);
     711             : 
     712             :         /* write some basic info as a comment, including some help */
     713           0 :         svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a   -->\n"
     714             :             "<!-- browser such as Chrome, Chromium or Firefox. Other applications     -->\n"
     715             :             "<!-- that render these files properly but much slower are ImageMagick,   -->\n"
     716             :             "<!-- gimp, inkscape, etc. To display the files on your system, just      -->\n"
     717             :             "<!-- point your browser to this file.                                    -->\n\n"
     718             :             "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", GIT_VERSION);
     719             : 
     720             :         /* style sheet */
     721           0 :         svg("<defs>\n  <style type=\"text/css\">\n    <![CDATA[\n"
     722             :             "      rect       { stroke-width: 1; stroke-opacity: 0; }\n"
     723             :             "      rect.background   { fill: rgb(255,255,255); }\n"
     724             :             "      rect.activating   { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
     725             :             "      rect.active       { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
     726             :             "      rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
     727             :             "      rect.kernel       { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
     728             :             "      rect.initrd       { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
     729             :             "      rect.firmware     { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
     730             :             "      rect.loader       { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
     731             :             "      rect.userspace    { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
     732             :             "      rect.security     { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
     733             :             "      rect.generators   { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
     734             :             "      rect.unitsload    { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
     735             :             "      rect.box   { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
     736             :             "      line       { stroke: rgb(64,64,64); stroke-width: 1; }\n"
     737             :             "//    line.sec1  { }\n"
     738             :             "      line.sec5  { stroke-width: 2; }\n"
     739             :             "      line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
     740             :             "      text       { font-family: Verdana, Helvetica; font-size: 14px; }\n"
     741             :             "      text.left  { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
     742             :             "      text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
     743             :             "      text.sec   { font-size: 10px; }\n"
     744             :             "    ]]>\n   </style>\n</defs>\n\n");
     745             : 
     746           0 :         svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
     747           0 :         svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
     748           0 :         if (host)
     749           0 :                 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
     750             :                     isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
     751             :                     strempty(host->hostname),
     752             :                     strempty(host->kernel_name),
     753             :                     strempty(host->kernel_release),
     754             :                     strempty(host->kernel_version),
     755             :                     strempty(host->architecture),
     756             :                     strempty(host->virtualization));
     757             : 
     758           0 :         svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
     759           0 :         svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
     760             : 
     761           0 :         if (boot->firmware_time > 0) {
     762           0 :                 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
     763           0 :                 svg_text(true, -(double) boot->firmware_time, y, "firmware");
     764           0 :                 y++;
     765             :         }
     766           0 :         if (boot->loader_time > 0) {
     767           0 :                 svg_bar("loader", -(double) boot->loader_time, 0, y);
     768           0 :                 svg_text(true, -(double) boot->loader_time, y, "loader");
     769           0 :                 y++;
     770             :         }
     771           0 :         if (boot->kernel_done_time > 0) {
     772           0 :                 svg_bar("kernel", 0, boot->kernel_done_time, y);
     773           0 :                 svg_text(true, 0, y, "kernel");
     774           0 :                 y++;
     775             :         }
     776           0 :         if (boot->initrd_time > 0) {
     777           0 :                 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
     778           0 :                 if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
     779           0 :                         svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
     780           0 :                 if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time)
     781           0 :                         svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y);
     782           0 :                 if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time)
     783           0 :                         svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y);
     784           0 :                 svg_text(true, boot->initrd_time, y, "initrd");
     785           0 :                 y++;
     786             :         }
     787             : 
     788           0 :         for (u = times; u->has_data; u++) {
     789           0 :                 if (u->activating >= boot->userspace_time)
     790           0 :                         break;
     791             : 
     792           0 :                 y += plot_unit_times(u, width, y);
     793             :         }
     794             : 
     795           0 :         svg_bar("active", boot->userspace_time, boot->finish_time, y);
     796           0 :         if (boot->security_start_time > 0)
     797           0 :                 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
     798           0 :         svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
     799           0 :         svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
     800           0 :         svg_text(true, boot->userspace_time, y, "systemd");
     801           0 :         y++;
     802             : 
     803           0 :         for (; u->has_data; u++)
     804           0 :                 y += plot_unit_times(u, width, y);
     805             : 
     806           0 :         svg("</g>\n");
     807             : 
     808             :         /* Legend */
     809           0 :         svg("<g transform=\"translate(20,100)\">\n");
     810           0 :         y++;
     811           0 :         svg_bar("activating", 0, 300000, y);
     812           0 :         svg_text(true, 400000, y, "Activating");
     813           0 :         y++;
     814           0 :         svg_bar("active", 0, 300000, y);
     815           0 :         svg_text(true, 400000, y, "Active");
     816           0 :         y++;
     817           0 :         svg_bar("deactivating", 0, 300000, y);
     818           0 :         svg_text(true, 400000, y, "Deactivating");
     819           0 :         y++;
     820           0 :         if (boot->security_start_time > 0) {
     821           0 :                 svg_bar("security", 0, 300000, y);
     822           0 :                 svg_text(true, 400000, y, "Setting up security module");
     823           0 :                 y++;
     824             :         }
     825           0 :         svg_bar("generators", 0, 300000, y);
     826           0 :         svg_text(true, 400000, y, "Generators");
     827           0 :         y++;
     828           0 :         svg_bar("unitsload", 0, 300000, y);
     829           0 :         svg_text(true, 400000, y, "Loading unit files");
     830           0 :         y++;
     831             : 
     832           0 :         svg("</g>\n\n");
     833             : 
     834           0 :         svg("</svg>\n");
     835             : 
     836           0 :         return 0;
     837             : }
     838             : 
     839           0 : static int list_dependencies_print(
     840             :                 const char *name,
     841             :                 unsigned level,
     842             :                 unsigned branches,
     843             :                 bool last,
     844             :                 struct unit_times *times,
     845             :                 struct boot_times *boot) {
     846             : 
     847             :         unsigned i;
     848             :         char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
     849             : 
     850           0 :         for (i = level; i != 0; i--)
     851           0 :                 printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
     852             : 
     853           0 :         printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
     854             : 
     855           0 :         if (times) {
     856           0 :                 if (times->time > 0)
     857           0 :                         printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
     858           0 :                                format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
     859             :                                format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
     860           0 :                 else if (times->activated > boot->userspace_time)
     861           0 :                         printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
     862             :                 else
     863           0 :                         printf("%s", name);
     864             :         } else
     865           0 :                 printf("%s", name);
     866           0 :         printf("\n");
     867             : 
     868           0 :         return 0;
     869             : }
     870             : 
     871           0 : static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
     872           0 :         _cleanup_free_ char *path = NULL;
     873             : 
     874           0 :         assert(bus);
     875           0 :         assert(name);
     876           0 :         assert(deps);
     877             : 
     878           0 :         path = unit_dbus_path_from_name(name);
     879           0 :         if (!path)
     880           0 :                 return -ENOMEM;
     881             : 
     882           0 :         return bus_get_unit_property_strv(bus, path, "After", deps);
     883             : }
     884             : 
     885             : static Hashmap *unit_times_hashmap;
     886             : 
     887           0 : static int list_dependencies_compare(char *const *a, char *const *b) {
     888           0 :         usec_t usa = 0, usb = 0;
     889             :         struct unit_times *times;
     890             : 
     891           0 :         times = hashmap_get(unit_times_hashmap, *a);
     892           0 :         if (times)
     893           0 :                 usa = times->activated;
     894           0 :         times = hashmap_get(unit_times_hashmap, *b);
     895           0 :         if (times)
     896           0 :                 usb = times->activated;
     897             : 
     898           0 :         return CMP(usb, usa);
     899             : }
     900             : 
     901           0 : static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
     902           0 :         return times && times->activated > 0 && times->activated <= boot->finish_time;
     903             : }
     904             : 
     905           0 : static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) {
     906           0 :         _cleanup_strv_free_ char **deps = NULL;
     907             :         char **c;
     908           0 :         int r = 0;
     909           0 :         usec_t service_longest = 0;
     910           0 :         int to_print = 0;
     911             :         struct unit_times *times;
     912             :         struct boot_times *boot;
     913             : 
     914           0 :         if (strv_extend(units, name))
     915           0 :                 return log_oom();
     916             : 
     917           0 :         r = list_dependencies_get_dependencies(bus, name, &deps);
     918           0 :         if (r < 0)
     919           0 :                 return r;
     920             : 
     921           0 :         typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
     922             : 
     923           0 :         r = acquire_boot_times(bus, &boot);
     924           0 :         if (r < 0)
     925           0 :                 return r;
     926             : 
     927           0 :         STRV_FOREACH(c, deps) {
     928           0 :                 times = hashmap_get(unit_times_hashmap, *c);
     929           0 :                 if (times_in_range(times, boot) && times->activated >= service_longest)
     930           0 :                         service_longest = times->activated;
     931             :         }
     932             : 
     933           0 :         if (service_longest == 0)
     934           0 :                 return r;
     935             : 
     936           0 :         STRV_FOREACH(c, deps) {
     937           0 :                 times = hashmap_get(unit_times_hashmap, *c);
     938           0 :                 if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz)
     939           0 :                         to_print++;
     940             :         }
     941             : 
     942           0 :         if (!to_print)
     943           0 :                 return r;
     944             : 
     945           0 :         STRV_FOREACH(c, deps) {
     946           0 :                 times = hashmap_get(unit_times_hashmap, *c);
     947           0 :                 if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz)
     948           0 :                         continue;
     949             : 
     950           0 :                 to_print--;
     951             : 
     952           0 :                 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
     953           0 :                 if (r < 0)
     954           0 :                         return r;
     955             : 
     956           0 :                 if (strv_contains(*units, *c)) {
     957           0 :                         r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
     958             :                                                     true, NULL, boot);
     959           0 :                         if (r < 0)
     960           0 :                                 return r;
     961           0 :                         continue;
     962             :                 }
     963             : 
     964           0 :                 r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0));
     965           0 :                 if (r < 0)
     966           0 :                         return r;
     967             : 
     968           0 :                 if (to_print == 0)
     969           0 :                         break;
     970             :         }
     971           0 :         return 0;
     972             : }
     973             : 
     974           0 : static int list_dependencies(sd_bus *bus, const char *name) {
     975           0 :         _cleanup_strv_free_ char **units = NULL;
     976             :         char ts[FORMAT_TIMESPAN_MAX];
     977             :         struct unit_times *times;
     978             :         int r;
     979             :         const char *id;
     980           0 :         _cleanup_free_ char *path = NULL;
     981           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
     982           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     983             :         struct boot_times *boot;
     984             : 
     985           0 :         assert(bus);
     986             : 
     987           0 :         path = unit_dbus_path_from_name(name);
     988           0 :         if (!path)
     989           0 :                 return -ENOMEM;
     990             : 
     991           0 :         r = sd_bus_get_property(
     992             :                         bus,
     993             :                         "org.freedesktop.systemd1",
     994             :                         path,
     995             :                         "org.freedesktop.systemd1.Unit",
     996             :                         "Id",
     997             :                         &error,
     998             :                         &reply,
     999             :                         "s");
    1000           0 :         if (r < 0)
    1001           0 :                 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
    1002             : 
    1003           0 :         r = sd_bus_message_read(reply, "s", &id);
    1004           0 :         if (r < 0)
    1005           0 :                 return bus_log_parse_error(r);
    1006             : 
    1007           0 :         times = hashmap_get(unit_times_hashmap, id);
    1008             : 
    1009           0 :         r = acquire_boot_times(bus, &boot);
    1010           0 :         if (r < 0)
    1011           0 :                 return r;
    1012             : 
    1013           0 :         if (times) {
    1014           0 :                 if (times->time)
    1015           0 :                         printf("%s%s +%s%s\n", ansi_highlight_red(), id,
    1016             :                                format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
    1017           0 :                 else if (times->activated > boot->userspace_time)
    1018           0 :                         printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
    1019             :                 else
    1020           0 :                         printf("%s\n", id);
    1021             :         }
    1022             : 
    1023           0 :         return list_dependencies_one(bus, name, 0, &units, 0);
    1024             : }
    1025             : 
    1026           0 : static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
    1027           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1028           0 :         _cleanup_(unit_times_freep) struct unit_times *times = NULL;
    1029             :         struct unit_times *u;
    1030             :         Hashmap *h;
    1031             :         int n, r;
    1032             : 
    1033           0 :         r = acquire_bus(&bus, NULL);
    1034           0 :         if (r < 0)
    1035           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1036             : 
    1037           0 :         n = acquire_time_data(bus, &times);
    1038           0 :         if (n <= 0)
    1039           0 :                 return n;
    1040             : 
    1041           0 :         h = hashmap_new(&string_hash_ops);
    1042           0 :         if (!h)
    1043           0 :                 return log_oom();
    1044             : 
    1045           0 :         for (u = times; u->has_data; u++) {
    1046           0 :                 r = hashmap_put(h, u->name, u);
    1047           0 :                 if (r < 0)
    1048           0 :                         return log_error_errno(r, "Failed to add entry to hashmap: %m");
    1049             :         }
    1050           0 :         unit_times_hashmap = h;
    1051             : 
    1052           0 :         (void) pager_open(arg_pager_flags);
    1053             : 
    1054           0 :         puts("The time when unit became active or started is printed after the \"@\" character.\n"
    1055             :              "The time the unit took to start is printed after the \"+\" character.\n");
    1056             : 
    1057           0 :         if (argc > 1) {
    1058             :                 char **name;
    1059           0 :                 STRV_FOREACH(name, strv_skip(argv, 1))
    1060           0 :                         list_dependencies(bus, *name);
    1061             :         } else
    1062           0 :                 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
    1063             : 
    1064           0 :         h = hashmap_free(h);
    1065           0 :         return 0;
    1066             : }
    1067             : 
    1068           0 : static int analyze_blame(int argc, char *argv[], void *userdata) {
    1069           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1070           0 :         _cleanup_(unit_times_freep) struct unit_times *times = NULL;
    1071           0 :         _cleanup_(table_unrefp) Table *table = NULL;
    1072             :         struct unit_times *u;
    1073             :         TableCell *cell;
    1074             :         int n, r;
    1075             : 
    1076           0 :         r = acquire_bus(&bus, NULL);
    1077           0 :         if (r < 0)
    1078           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1079             : 
    1080           0 :         n = acquire_time_data(bus, &times);
    1081           0 :         if (n <= 0)
    1082           0 :                 return n;
    1083             : 
    1084           0 :         table = table_new("time", "unit");
    1085           0 :         if (!table)
    1086           0 :                 return log_oom();
    1087             : 
    1088           0 :         table_set_header(table, false);
    1089             : 
    1090           0 :         assert_se(cell = table_get_cell(table, 0, 0));
    1091           0 :         r = table_set_ellipsize_percent(table, cell, 100);
    1092           0 :         if (r < 0)
    1093           0 :                 return r;
    1094             : 
    1095           0 :         r = table_set_align_percent(table, cell, 100);
    1096           0 :         if (r < 0)
    1097           0 :                 return r;
    1098             : 
    1099           0 :         assert_se(cell = table_get_cell(table, 0, 1));
    1100           0 :         r = table_set_ellipsize_percent(table, cell, 100);
    1101           0 :         if (r < 0)
    1102           0 :                 return r;
    1103             : 
    1104           0 :         r = table_set_sort(table, 0, SIZE_MAX);
    1105           0 :         if (r < 0)
    1106           0 :                 return r;
    1107             : 
    1108           0 :         r = table_set_reverse(table, 0, true);
    1109           0 :         if (r < 0)
    1110           0 :                 return r;
    1111             : 
    1112           0 :         for (u = times; u->has_data; u++) {
    1113           0 :                 if (u->time <= 0)
    1114           0 :                         continue;
    1115             : 
    1116           0 :                 r = table_add_cell(table, NULL, TABLE_TIMESPAN_MSEC, &u->time);
    1117           0 :                 if (r < 0)
    1118           0 :                         return r;
    1119             : 
    1120           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, u->name);
    1121           0 :                 if (r < 0)
    1122           0 :                         return r;
    1123             :         }
    1124             : 
    1125           0 :         (void) pager_open(arg_pager_flags);
    1126             : 
    1127           0 :         return table_print(table, NULL);
    1128             : }
    1129             : 
    1130           0 : static int analyze_time(int argc, char *argv[], void *userdata) {
    1131           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1132           0 :         _cleanup_free_ char *buf = NULL;
    1133             :         int r;
    1134             : 
    1135           0 :         r = acquire_bus(&bus, NULL);
    1136           0 :         if (r < 0)
    1137           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1138             : 
    1139           0 :         r = pretty_boot_time(bus, &buf);
    1140           0 :         if (r < 0)
    1141           0 :                 return r;
    1142             : 
    1143           0 :         puts(buf);
    1144           0 :         return 0;
    1145             : }
    1146             : 
    1147           0 : static int graph_one_property(
    1148             :                 sd_bus *bus,
    1149             :                 const UnitInfo *u,
    1150             :                 const char *prop,
    1151             :                 const char *color,
    1152             :                 char *patterns[],
    1153             :                 char *from_patterns[],
    1154             :                 char *to_patterns[]) {
    1155             : 
    1156           0 :         _cleanup_strv_free_ char **units = NULL;
    1157             :         char **unit;
    1158             :         int r;
    1159             :         bool match_patterns;
    1160             : 
    1161           0 :         assert(u);
    1162           0 :         assert(prop);
    1163           0 :         assert(color);
    1164             : 
    1165           0 :         match_patterns = strv_fnmatch(patterns, u->id, 0);
    1166             : 
    1167           0 :         if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id, 0))
    1168           0 :                 return 0;
    1169             : 
    1170           0 :         r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
    1171           0 :         if (r < 0)
    1172           0 :                 return r;
    1173             : 
    1174           0 :         STRV_FOREACH(unit, units) {
    1175             :                 bool match_patterns2;
    1176             : 
    1177           0 :                 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
    1178             : 
    1179           0 :                 if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit, 0))
    1180           0 :                         continue;
    1181             : 
    1182           0 :                 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
    1183           0 :                         continue;
    1184             : 
    1185           0 :                 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
    1186             :         }
    1187             : 
    1188           0 :         return 0;
    1189             : }
    1190             : 
    1191           0 : static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
    1192             :         int r;
    1193             : 
    1194           0 :         assert(bus);
    1195           0 :         assert(u);
    1196             : 
    1197           0 :         if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
    1198           0 :                 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
    1199           0 :                 if (r < 0)
    1200           0 :                         return r;
    1201             :         }
    1202             : 
    1203           0 :         if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
    1204           0 :                 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
    1205           0 :                 if (r < 0)
    1206           0 :                         return r;
    1207           0 :                 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
    1208           0 :                 if (r < 0)
    1209           0 :                         return r;
    1210           0 :                 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
    1211           0 :                 if (r < 0)
    1212           0 :                         return r;
    1213           0 :                 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
    1214           0 :                 if (r < 0)
    1215           0 :                         return r;
    1216             :         }
    1217             : 
    1218           0 :         return 0;
    1219             : }
    1220             : 
    1221           0 : static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
    1222           0 :         _cleanup_strv_free_ char **expanded_patterns = NULL;
    1223             :         char **pattern;
    1224             :         int r;
    1225             : 
    1226           0 :         STRV_FOREACH(pattern, patterns) {
    1227           0 :                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1228           0 :                 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
    1229             : 
    1230           0 :                 if (strv_extend(&expanded_patterns, *pattern) < 0)
    1231           0 :                         return log_oom();
    1232             : 
    1233           0 :                 if (string_is_glob(*pattern))
    1234           0 :                         continue;
    1235             : 
    1236           0 :                 unit = unit_dbus_path_from_name(*pattern);
    1237           0 :                 if (!unit)
    1238           0 :                         return log_oom();
    1239             : 
    1240           0 :                 r = sd_bus_get_property_string(
    1241             :                                 bus,
    1242             :                                 "org.freedesktop.systemd1",
    1243             :                                 unit,
    1244             :                                 "org.freedesktop.systemd1.Unit",
    1245             :                                 "Id",
    1246             :                                 &error,
    1247             :                                 &unit_id);
    1248           0 :                 if (r < 0)
    1249           0 :                         return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
    1250             : 
    1251           0 :                 if (!streq(*pattern, unit_id)) {
    1252           0 :                         if (strv_extend(&expanded_patterns, unit_id) < 0)
    1253           0 :                                 return log_oom();
    1254             :                 }
    1255             :         }
    1256             : 
    1257           0 :         *ret = expanded_patterns;
    1258           0 :         expanded_patterns = NULL; /* do not free */
    1259             : 
    1260           0 :         return 0;
    1261             : }
    1262             : 
    1263           0 : static int dot(int argc, char *argv[], void *userdata) {
    1264           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1265           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1266           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1267           0 :         _cleanup_strv_free_ char **expanded_patterns = NULL;
    1268           0 :         _cleanup_strv_free_ char **expanded_from_patterns = NULL;
    1269           0 :         _cleanup_strv_free_ char **expanded_to_patterns = NULL;
    1270             :         int r;
    1271             :         UnitInfo u;
    1272             : 
    1273           0 :         r = acquire_bus(&bus, NULL);
    1274           0 :         if (r < 0)
    1275           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1276             : 
    1277           0 :         r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
    1278           0 :         if (r < 0)
    1279           0 :                 return r;
    1280             : 
    1281           0 :         r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
    1282           0 :         if (r < 0)
    1283           0 :                 return r;
    1284             : 
    1285           0 :         r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
    1286           0 :         if (r < 0)
    1287           0 :                 return r;
    1288             : 
    1289           0 :         r = sd_bus_call_method(
    1290             :                         bus,
    1291             :                        "org.freedesktop.systemd1",
    1292             :                        "/org/freedesktop/systemd1",
    1293             :                        "org.freedesktop.systemd1.Manager",
    1294             :                        "ListUnits",
    1295             :                        &error,
    1296             :                        &reply,
    1297             :                        "");
    1298           0 :         if (r < 0)
    1299           0 :                 log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
    1300             : 
    1301           0 :         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
    1302           0 :         if (r < 0)
    1303           0 :                 return bus_log_parse_error(r);
    1304             : 
    1305           0 :         printf("digraph systemd {\n");
    1306             : 
    1307           0 :         while ((r = bus_parse_unit_info(reply, &u)) > 0) {
    1308             : 
    1309           0 :                 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
    1310           0 :                 if (r < 0)
    1311           0 :                         return r;
    1312             :         }
    1313           0 :         if (r < 0)
    1314           0 :                 return bus_log_parse_error(r);
    1315             : 
    1316           0 :         printf("}\n");
    1317             : 
    1318           0 :         log_info("   Color legend: black     = Requires\n"
    1319             :                  "                 dark blue = Requisite\n"
    1320             :                  "                 dark grey = Wants\n"
    1321             :                  "                 red       = Conflicts\n"
    1322             :                  "                 green     = After\n");
    1323             : 
    1324           0 :         if (on_tty())
    1325           0 :                 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
    1326             :                            "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
    1327             : 
    1328           0 :         return 0;
    1329             : }
    1330             : 
    1331           0 : static int dump_fallback(sd_bus *bus) {
    1332           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1333           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1334           0 :         const char *text = NULL;
    1335             :         int r;
    1336             : 
    1337           0 :         assert(bus);
    1338             : 
    1339           0 :         r = sd_bus_call_method(
    1340             :                         bus,
    1341             :                         "org.freedesktop.systemd1",
    1342             :                         "/org/freedesktop/systemd1",
    1343             :                         "org.freedesktop.systemd1.Manager",
    1344             :                         "Dump",
    1345             :                         &error,
    1346             :                         &reply,
    1347             :                         NULL);
    1348           0 :         if (r < 0)
    1349           0 :                 return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r));
    1350             : 
    1351           0 :         r = sd_bus_message_read(reply, "s", &text);
    1352           0 :         if (r < 0)
    1353           0 :                 return bus_log_parse_error(r);
    1354             : 
    1355           0 :         fputs(text, stdout);
    1356           0 :         return 0;
    1357             : }
    1358             : 
    1359           0 : static int dump(int argc, char *argv[], void *userdata) {
    1360           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1361           0 :         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
    1362           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1363           0 :         int fd = -1;
    1364             :         int r;
    1365             : 
    1366           0 :         r = acquire_bus(&bus, NULL);
    1367           0 :         if (r < 0)
    1368           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1369             : 
    1370           0 :         (void) pager_open(arg_pager_flags);
    1371             : 
    1372           0 :         if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
    1373           0 :                 return dump_fallback(bus);
    1374             : 
    1375           0 :         r = sd_bus_call_method(
    1376             :                         bus,
    1377             :                         "org.freedesktop.systemd1",
    1378             :                         "/org/freedesktop/systemd1",
    1379             :                         "org.freedesktop.systemd1.Manager",
    1380             :                         "DumpByFileDescriptor",
    1381             :                         &error,
    1382             :                         &reply,
    1383             :                         NULL);
    1384           0 :         if (r < 0) {
    1385             :                 /* fall back to Dump if DumpByFileDescriptor is not supported */
    1386           0 :                 if (!IN_SET(r, -EACCES, -EBADR))
    1387           0 :                         return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s",
    1388             :                                                bus_error_message(&error, r));
    1389             : 
    1390           0 :                 return dump_fallback(bus);
    1391             :         }
    1392             : 
    1393           0 :         r = sd_bus_message_read(reply, "h", &fd);
    1394           0 :         if (r < 0)
    1395           0 :                 return bus_log_parse_error(r);
    1396             : 
    1397           0 :         fflush(stdout);
    1398           0 :         return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
    1399             : }
    1400             : 
    1401           0 : static int cat_config(int argc, char *argv[], void *userdata) {
    1402             :         char **arg, **list;
    1403             :         int r;
    1404             : 
    1405           0 :         (void) pager_open(arg_pager_flags);
    1406             : 
    1407           0 :         list = strv_skip(argv, 1);
    1408           0 :         STRV_FOREACH(arg, list) {
    1409           0 :                 const char *t = NULL;
    1410             : 
    1411           0 :                 if (arg != list)
    1412           0 :                         print_separator();
    1413             : 
    1414           0 :                 if (path_is_absolute(*arg)) {
    1415             :                         const char *dir;
    1416             : 
    1417           0 :                         NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
    1418           0 :                                 t = path_startswith(*arg, dir);
    1419           0 :                                 if (t)
    1420           0 :                                         break;
    1421             :                         }
    1422             : 
    1423           0 :                         if (!t)
    1424           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1425             :                                                        "Path %s does not start with any known prefix.", *arg);
    1426             :                 } else
    1427           0 :                         t = *arg;
    1428             : 
    1429           0 :                 r = conf_files_cat(arg_root, t);
    1430           0 :                 if (r < 0)
    1431           0 :                         return r;
    1432             :         }
    1433             : 
    1434           0 :         return 0;
    1435             : }
    1436             : 
    1437           0 : static int set_log_level(int argc, char *argv[], void *userdata) {
    1438           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1439           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1440             :         int r;
    1441             : 
    1442           0 :         assert(argc == 2);
    1443           0 :         assert(argv);
    1444             : 
    1445           0 :         r = acquire_bus(&bus, NULL);
    1446           0 :         if (r < 0)
    1447           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1448             : 
    1449           0 :         r = sd_bus_set_property(
    1450             :                         bus,
    1451             :                         "org.freedesktop.systemd1",
    1452             :                         "/org/freedesktop/systemd1",
    1453             :                         "org.freedesktop.systemd1.Manager",
    1454             :                         "LogLevel",
    1455             :                         &error,
    1456             :                         "s",
    1457           0 :                         argv[1]);
    1458           0 :         if (r < 0)
    1459           0 :                 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
    1460             : 
    1461           0 :         return 0;
    1462             : }
    1463             : 
    1464           0 : static int get_log_level(int argc, char *argv[], void *userdata) {
    1465           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1466           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1467           0 :         _cleanup_free_ char *level = NULL;
    1468             :         int r;
    1469             : 
    1470           0 :         r = acquire_bus(&bus, NULL);
    1471           0 :         if (r < 0)
    1472           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1473             : 
    1474           0 :         r = sd_bus_get_property_string(
    1475             :                         bus,
    1476             :                         "org.freedesktop.systemd1",
    1477             :                         "/org/freedesktop/systemd1",
    1478             :                         "org.freedesktop.systemd1.Manager",
    1479             :                         "LogLevel",
    1480             :                         &error,
    1481             :                         &level);
    1482           0 :         if (r < 0)
    1483           0 :                 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
    1484             : 
    1485           0 :         puts(level);
    1486           0 :         return 0;
    1487             : }
    1488             : 
    1489           0 : static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
    1490           0 :         return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
    1491             : }
    1492             : 
    1493           0 : static int set_log_target(int argc, char *argv[], void *userdata) {
    1494           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1495           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1496             :         int r;
    1497             : 
    1498           0 :         assert(argc == 2);
    1499           0 :         assert(argv);
    1500             : 
    1501           0 :         r = acquire_bus(&bus, NULL);
    1502           0 :         if (r < 0)
    1503           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1504             : 
    1505           0 :         r = sd_bus_set_property(
    1506             :                         bus,
    1507             :                         "org.freedesktop.systemd1",
    1508             :                         "/org/freedesktop/systemd1",
    1509             :                         "org.freedesktop.systemd1.Manager",
    1510             :                         "LogTarget",
    1511             :                         &error,
    1512             :                         "s",
    1513           0 :                         argv[1]);
    1514           0 :         if (r < 0)
    1515           0 :                 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
    1516             : 
    1517           0 :         return 0;
    1518             : }
    1519             : 
    1520           0 : static int get_log_target(int argc, char *argv[], void *userdata) {
    1521           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    1522           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    1523           0 :         _cleanup_free_ char *target = NULL;
    1524             :         int r;
    1525             : 
    1526           0 :         r = acquire_bus(&bus, NULL);
    1527           0 :         if (r < 0)
    1528           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    1529             : 
    1530           0 :         r = sd_bus_get_property_string(
    1531             :                         bus,
    1532             :                         "org.freedesktop.systemd1",
    1533             :                         "/org/freedesktop/systemd1",
    1534             :                         "org.freedesktop.systemd1.Manager",
    1535             :                         "LogTarget",
    1536             :                         &error,
    1537             :                         &target);
    1538           0 :         if (r < 0)
    1539           0 :                 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
    1540             : 
    1541           0 :         puts(target);
    1542           0 :         return 0;
    1543             : }
    1544             : 
    1545           0 : static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
    1546           0 :         return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
    1547             : }
    1548             : 
    1549           0 : static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
    1550             :         char **s;
    1551           0 :         STRV_FOREACH(s, strv)
    1552           0 :                 if (strv_fnmatch_or_empty(patterns, *s, flags))
    1553           0 :                         return true;
    1554             : 
    1555           0 :         return false;
    1556             : }
    1557             : 
    1558           0 : static int do_unit_files(int argc, char *argv[], void *userdata) {
    1559           0 :         _cleanup_(lookup_paths_free) LookupPaths lp = {};
    1560           0 :         _cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
    1561           0 :         _cleanup_hashmap_free_ Hashmap *unit_names = NULL;
    1562           0 :         char **patterns = strv_skip(argv, 1);
    1563             :         Iterator i;
    1564             :         const char *k, *dst;
    1565             :         char **v;
    1566             :         int r;
    1567             : 
    1568           0 :         r = lookup_paths_init(&lp, arg_scope, 0, NULL);
    1569           0 :         if (r < 0)
    1570           0 :                 return log_error_errno(r, "lookup_paths_init() failed: %m");
    1571             : 
    1572           0 :         r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL);
    1573           0 :         if (r < 0)
    1574           0 :                 return log_error_errno(r, "unit_file_build_name_map() failed: %m");
    1575             : 
    1576           0 :         HASHMAP_FOREACH_KEY(dst, k, unit_ids, i) {
    1577           0 :                 if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
    1578           0 :                     !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE))
    1579           0 :                         continue;
    1580             : 
    1581           0 :                 printf("ids: %s → %s\n", k, dst);
    1582             :         }
    1583             : 
    1584           0 :         HASHMAP_FOREACH_KEY(v, k, unit_names, i) {
    1585           0 :                 if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
    1586           0 :                     !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE))
    1587           0 :                         continue;
    1588             : 
    1589           0 :                 _cleanup_free_ char *j = strv_join(v, ", ");
    1590           0 :                 printf("aliases: %s ← %s\n", k, j);
    1591             :         }
    1592             : 
    1593           0 :         return 0;
    1594             : }
    1595             : 
    1596           0 : static int dump_unit_paths(int argc, char *argv[], void *userdata) {
    1597           0 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    1598             :         int r;
    1599             :         char **p;
    1600             : 
    1601           0 :         r = lookup_paths_init(&paths, arg_scope, 0, NULL);
    1602           0 :         if (r < 0)
    1603           0 :                 return log_error_errno(r, "lookup_paths_init() failed: %m");
    1604             : 
    1605           0 :         STRV_FOREACH(p, paths.search_path)
    1606           0 :                 puts(*p);
    1607             : 
    1608           0 :         return 0;
    1609             : }
    1610             : 
    1611           0 : static int dump_exit_status(int argc, char *argv[], void *userdata) {
    1612           0 :         _cleanup_(table_unrefp) Table *table = NULL;
    1613             :         int r;
    1614             : 
    1615           0 :         table = table_new("name", "status", "class");
    1616           0 :         if (!table)
    1617           0 :                 return log_oom();
    1618             : 
    1619           0 :         r = table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
    1620           0 :         if (r < 0)
    1621           0 :                 return log_error_errno(r, "Failed to right-align status: %m");
    1622             : 
    1623           0 :         if (strv_isempty(strv_skip(argv, 1)))
    1624           0 :                 for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) {
    1625           0 :                         if (!exit_status_mappings[i].name)
    1626           0 :                                 continue;
    1627             : 
    1628           0 :                         r = table_add_many(table,
    1629             :                                            TABLE_STRING, exit_status_mappings[i].name,
    1630             :                                            TABLE_INT, (int) i,
    1631             :                                            TABLE_STRING, exit_status_class(i));
    1632           0 :                         if (r < 0)
    1633           0 :                                 return r;
    1634             :                 }
    1635             :         else
    1636           0 :                 for (int i = 1; i < argc; i++) {
    1637             :                         int status;
    1638             : 
    1639           0 :                         status = exit_status_from_string(argv[i]);
    1640           0 :                         if (status < 0)
    1641           0 :                                 return log_error_errno(r, "Invalid exit status \"%s\": %m", argv[i]);
    1642             : 
    1643           0 :                         assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings));
    1644           0 :                         r = table_add_many(table,
    1645             :                                            TABLE_STRING, exit_status_mappings[status].name ?: "-",
    1646             :                                            TABLE_INT, status,
    1647             :                                            TABLE_STRING, exit_status_class(status) ?: "-");
    1648           0 :                         if (r < 0)
    1649           0 :                                 return r;
    1650             :                 }
    1651             : 
    1652           0 :         (void) pager_open(arg_pager_flags);
    1653             : 
    1654           0 :         return table_print(table, NULL);
    1655             : }
    1656             : 
    1657             : #if HAVE_SECCOMP
    1658             : 
    1659           0 : static int load_kernel_syscalls(Set **ret) {
    1660           0 :         _cleanup_(set_free_freep) Set *syscalls = NULL;
    1661           0 :         _cleanup_fclose_ FILE *f = NULL;
    1662             :         int r;
    1663             : 
    1664             :         /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
    1665             :          * but good enough for analysis purposes. */
    1666             : 
    1667           0 :         f = fopen("/sys/kernel/tracing/available_events", "re");
    1668           0 :         if (!f) {
    1669             :                 /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
    1670             :                  * old debugfs mount point works? */
    1671           0 :                 f = fopen("/sys/kernel/debug/tracing/available_events", "re");
    1672           0 :                 if (!f)
    1673           0 :                         return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
    1674             :                                               "Can't read open tracefs' available_events file: %m");
    1675             :         }
    1676             : 
    1677           0 :         for (;;) {
    1678           0 :                 _cleanup_free_ char *line = NULL;
    1679             :                 const char *e;
    1680             : 
    1681           0 :                 r = read_line(f, LONG_LINE_MAX, &line);
    1682           0 :                 if (r < 0)
    1683           0 :                         return log_error_errno(r, "Failed to read system call list: %m");
    1684           0 :                 if (r == 0)
    1685           0 :                         break;
    1686             : 
    1687           0 :                 e = startswith(line, "syscalls:sys_enter_");
    1688           0 :                 if (!e)
    1689           0 :                         continue;
    1690             : 
    1691             :                 /* These are named differently inside the kernel than their external name for historical
    1692             :                  * reasons. Let's hide them here. */
    1693           0 :                 if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
    1694           0 :                         continue;
    1695             : 
    1696           0 :                 r = set_ensure_allocated(&syscalls, &string_hash_ops);
    1697           0 :                 if (r < 0)
    1698           0 :                         return log_oom();
    1699             : 
    1700           0 :                 r = set_put_strdup(syscalls, e);
    1701           0 :                 if (r < 0)
    1702           0 :                         return log_error_errno(r, "Failed to add system call to list: %m");
    1703             :         }
    1704             : 
    1705           0 :         *ret = TAKE_PTR(syscalls);
    1706           0 :         return 0;
    1707             : }
    1708             : 
    1709           0 : static void kernel_syscalls_remove(Set *s, const SyscallFilterSet *set) {
    1710             :         const char *syscall;
    1711             : 
    1712           0 :         NULSTR_FOREACH(syscall, set->value) {
    1713           0 :                 if (syscall[0] == '@')
    1714           0 :                         continue;
    1715             : 
    1716           0 :                 (void) set_remove(s, syscall);
    1717             :         }
    1718           0 : }
    1719             : 
    1720           0 : static void dump_syscall_filter(const SyscallFilterSet *set) {
    1721             :         const char *syscall;
    1722             : 
    1723           0 :         printf("%s%s%s\n"
    1724             :                "    # %s\n",
    1725             :                ansi_highlight(),
    1726             :                set->name,
    1727             :                ansi_normal(),
    1728             :                set->help);
    1729             : 
    1730           0 :         NULSTR_FOREACH(syscall, set->value)
    1731           0 :                 printf("    %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
    1732           0 : }
    1733             : 
    1734           0 : static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
    1735           0 :         bool first = true;
    1736             : 
    1737           0 :         (void) pager_open(arg_pager_flags);
    1738             : 
    1739           0 :         if (strv_isempty(strv_skip(argv, 1))) {
    1740           0 :                 _cleanup_(set_free_freep) Set *kernel = NULL;
    1741             :                 int i, k;
    1742             : 
    1743           0 :                 k = load_kernel_syscalls(&kernel);
    1744             : 
    1745           0 :                 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
    1746           0 :                         const SyscallFilterSet *set = syscall_filter_sets + i;
    1747           0 :                         if (!first)
    1748           0 :                                 puts("");
    1749             : 
    1750           0 :                         dump_syscall_filter(set);
    1751           0 :                         kernel_syscalls_remove(kernel, set);
    1752           0 :                         first = false;
    1753             :                 }
    1754             : 
    1755           0 :                 if (k < 0) {
    1756           0 :                         fputc('\n', stdout);
    1757           0 :                         fflush(stdout);
    1758           0 :                         log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
    1759           0 :                 } else if (!set_isempty(kernel)) {
    1760             :                         const char *syscall;
    1761             :                         Iterator j;
    1762             : 
    1763           0 :                         printf("\n"
    1764             :                                "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
    1765             :                                ansi_highlight(), ansi_normal());
    1766             : 
    1767           0 :                         SET_FOREACH(syscall, kernel, j)
    1768           0 :                                 printf("#   %s\n", syscall);
    1769             :                 }
    1770             :         } else {
    1771             :                 char **name;
    1772             : 
    1773           0 :                 STRV_FOREACH(name, strv_skip(argv, 1)) {
    1774             :                         const SyscallFilterSet *set;
    1775             : 
    1776           0 :                         if (!first)
    1777           0 :                                 puts("");
    1778             : 
    1779           0 :                         set = syscall_filter_set_find(*name);
    1780           0 :                         if (!set) {
    1781             :                                 /* make sure the error appears below normal output */
    1782           0 :                                 fflush(stdout);
    1783             : 
    1784           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
    1785             :                                                        "Filter set \"%s\" not found.", *name);
    1786             :                         }
    1787             : 
    1788           0 :                         dump_syscall_filter(set);
    1789           0 :                         first = false;
    1790             :                 }
    1791             :         }
    1792             : 
    1793           0 :         return 0;
    1794             : }
    1795             : 
    1796             : #else
    1797             : static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
    1798             :         return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry.");
    1799             : }
    1800             : #endif
    1801             : 
    1802           0 : static void parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
    1803           0 :         if (calendar && calendar_spec_from_string(p, NULL) >= 0)
    1804           0 :                 log_notice("Hint: this expression is a valid calendar specification. "
    1805             :                            "Use 'systemd-analyze calendar \"%s\"' instead?", p);
    1806           0 :         if (timestamp && parse_timestamp(p, NULL) >= 0)
    1807           0 :                 log_notice("Hint: this expression is a valid timestamp. "
    1808             :                            "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
    1809           0 :         if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
    1810           0 :                 log_notice("Hint: this expression is a valid timespan. "
    1811             :                            "Use 'systemd-analyze timespan \"%s\"' instead?", p);
    1812           0 : }
    1813             : 
    1814           0 : static int dump_timespan(int argc, char *argv[], void *userdata) {
    1815             :         char **input_timespan;
    1816             : 
    1817           0 :         STRV_FOREACH(input_timespan, strv_skip(argv, 1)) {
    1818           0 :                 _cleanup_(table_unrefp) Table *table = NULL;
    1819             :                 usec_t output_usecs;
    1820             :                 TableCell *cell;
    1821             :                 int r;
    1822             : 
    1823           0 :                 r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC);
    1824           0 :                 if (r < 0) {
    1825           0 :                         log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan);
    1826           0 :                         parsing_hint(*input_timespan, true, true, false);
    1827           0 :                         return r;
    1828             :                 }
    1829             : 
    1830           0 :                 table = table_new("name", "value");
    1831           0 :                 if (!table)
    1832           0 :                         return log_oom();
    1833             : 
    1834           0 :                 table_set_header(table, false);
    1835             : 
    1836           0 :                 assert_se(cell = table_get_cell(table, 0, 0));
    1837           0 :                 r = table_set_ellipsize_percent(table, cell, 100);
    1838           0 :                 if (r < 0)
    1839           0 :                         return r;
    1840             : 
    1841           0 :                 r = table_set_align_percent(table, cell, 100);
    1842           0 :                 if (r < 0)
    1843           0 :                         return r;
    1844             : 
    1845           0 :                 assert_se(cell = table_get_cell(table, 0, 1));
    1846           0 :                 r = table_set_ellipsize_percent(table, cell, 100);
    1847           0 :                 if (r < 0)
    1848           0 :                         return r;
    1849             : 
    1850           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, "Original:");
    1851           0 :                 if (r < 0)
    1852           0 :                         return r;
    1853             : 
    1854           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, *input_timespan);
    1855           0 :                 if (r < 0)
    1856           0 :                         return r;
    1857             : 
    1858           0 :                 r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU));
    1859           0 :                 if (r < 0)
    1860           0 :                         return r;
    1861             : 
    1862           0 :                 r = table_add_cell(table, NULL, TABLE_UINT64, &output_usecs);
    1863           0 :                 if (r < 0)
    1864           0 :                         return r;
    1865             : 
    1866           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, "Human:");
    1867           0 :                 if (r < 0)
    1868           0 :                         return r;
    1869             : 
    1870           0 :                 r = table_add_cell(table, &cell, TABLE_TIMESPAN, &output_usecs);
    1871           0 :                 if (r < 0)
    1872           0 :                         return r;
    1873             : 
    1874           0 :                 r = table_set_color(table, cell, ansi_highlight());
    1875           0 :                 if (r < 0)
    1876           0 :                         return r;
    1877             : 
    1878           0 :                 r = table_print(table, NULL);
    1879           0 :                 if (r < 0)
    1880           0 :                         return r;
    1881             : 
    1882           0 :                 if (input_timespan[1])
    1883           0 :                         putchar('\n');
    1884             :         }
    1885             : 
    1886           0 :         return EXIT_SUCCESS;
    1887             : }
    1888             : 
    1889           0 : static int test_timestamp_one(const char *p) {
    1890           0 :         _cleanup_(table_unrefp) Table *table = NULL;
    1891             :         TableCell *cell;
    1892             :         usec_t usec;
    1893             :         int r;
    1894             : 
    1895           0 :         r = parse_timestamp(p, &usec);
    1896           0 :         if (r < 0) {
    1897           0 :                 log_error_errno(r, "Failed to parse \"%s\": %m", p);
    1898           0 :                 parsing_hint(p, true, false, true);
    1899           0 :                 return r;
    1900             :         }
    1901             : 
    1902           0 :         table = table_new("name", "value");
    1903           0 :         if (!table)
    1904           0 :                 return log_oom();
    1905             : 
    1906           0 :         table_set_header(table, false);
    1907             : 
    1908           0 :         assert_se(cell = table_get_cell(table, 0, 0));
    1909           0 :         r = table_set_ellipsize_percent(table, cell, 100);
    1910           0 :         if (r < 0)
    1911           0 :                 return r;
    1912             : 
    1913           0 :         r = table_set_align_percent(table, cell, 100);
    1914           0 :         if (r < 0)
    1915           0 :                 return r;
    1916             : 
    1917           0 :         assert_se(cell = table_get_cell(table, 0, 1));
    1918           0 :         r = table_set_ellipsize_percent(table, cell, 100);
    1919           0 :         if (r < 0)
    1920           0 :                 return r;
    1921             : 
    1922           0 :         r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
    1923           0 :         if (r < 0)
    1924           0 :                 return r;
    1925             : 
    1926           0 :         r = table_add_cell(table, NULL, TABLE_STRING, p);
    1927           0 :         if (r < 0)
    1928           0 :                 return r;
    1929             : 
    1930           0 :         r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
    1931           0 :         if (r < 0)
    1932           0 :                 return r;
    1933             : 
    1934           0 :         r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &usec);
    1935           0 :         if (r < 0)
    1936           0 :                 return r;
    1937             : 
    1938           0 :         r = table_set_color(table, cell, ansi_highlight_blue());
    1939           0 :         if (r < 0)
    1940           0 :                 return r;
    1941             : 
    1942           0 :         if (!in_utc_timezone()) {
    1943           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
    1944           0 :                 if (r < 0)
    1945           0 :                         return r;
    1946             : 
    1947           0 :                 r = table_add_cell(table, &cell, TABLE_TIMESTAMP_UTC, &usec);
    1948           0 :                 if (r < 0)
    1949           0 :                         return r;
    1950             :         }
    1951             : 
    1952           0 :         r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:");
    1953           0 :         if (r < 0)
    1954           0 :                 return r;
    1955             : 
    1956           0 :         if (usec % USEC_PER_SEC == 0)
    1957           0 :                 r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC,
    1958             :                                            usec / USEC_PER_SEC);
    1959             :         else
    1960           0 :                 r = table_add_cell_stringf(table, &cell, "@%"PRI_USEC".%06"PRI_USEC"",
    1961             :                                            usec / USEC_PER_SEC,
    1962             :                                            usec % USEC_PER_SEC);
    1963           0 :         if (r < 0)
    1964           0 :                 return r;
    1965             : 
    1966           0 :         r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
    1967           0 :         if (r < 0)
    1968           0 :                 return r;
    1969             : 
    1970           0 :         r = table_add_cell(table, &cell, TABLE_TIMESTAMP_RELATIVE, &usec);
    1971           0 :         if (r < 0)
    1972           0 :                 return r;
    1973             : 
    1974           0 :         return table_print(table, NULL);
    1975             : }
    1976             : 
    1977           0 : static int test_timestamp(int argc, char *argv[], void *userdata) {
    1978           0 :         int ret = 0, r;
    1979             :         char **p;
    1980             : 
    1981           0 :         STRV_FOREACH(p, strv_skip(argv, 1)) {
    1982           0 :                 r = test_timestamp_one(*p);
    1983           0 :                 if (ret == 0 && r < 0)
    1984           0 :                         ret = r;
    1985             : 
    1986           0 :                 if (*(p + 1))
    1987           0 :                         putchar('\n');
    1988             :         }
    1989             : 
    1990           0 :         return ret;
    1991             : }
    1992             : 
    1993           0 : static int test_calendar_one(usec_t n, const char *p) {
    1994           0 :         _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
    1995           0 :         _cleanup_(table_unrefp) Table *table = NULL;
    1996           0 :         _cleanup_free_ char *t = NULL;
    1997             :         TableCell *cell;
    1998             :         int r;
    1999             : 
    2000           0 :         r = calendar_spec_from_string(p, &spec);
    2001           0 :         if (r < 0) {
    2002           0 :                 log_error_errno(r, "Failed to parse calendar specification '%s': %m", p);
    2003           0 :                 parsing_hint(p, false, true, true);
    2004           0 :                 return r;
    2005             :         }
    2006             : 
    2007           0 :         r = calendar_spec_to_string(spec, &t);
    2008           0 :         if (r < 0)
    2009           0 :                 return log_error_errno(r, "Failed to format calendar specification '%s': %m", p);
    2010             : 
    2011           0 :         table = table_new("name", "value");
    2012           0 :         if (!table)
    2013           0 :                 return log_oom();
    2014             : 
    2015           0 :         table_set_header(table, false);
    2016             : 
    2017           0 :         assert_se(cell = table_get_cell(table, 0, 0));
    2018           0 :         r = table_set_ellipsize_percent(table, cell, 100);
    2019           0 :         if (r < 0)
    2020           0 :                 return r;
    2021             : 
    2022           0 :         r = table_set_align_percent(table, cell, 100);
    2023           0 :         if (r < 0)
    2024           0 :                 return r;
    2025             : 
    2026           0 :         assert_se(cell = table_get_cell(table, 0, 1));
    2027           0 :         r = table_set_ellipsize_percent(table, cell, 100);
    2028           0 :         if (r < 0)
    2029           0 :                 return r;
    2030             : 
    2031           0 :         if (!streq(t, p)) {
    2032           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, "Original form:");
    2033           0 :                 if (r < 0)
    2034           0 :                         return r;
    2035             : 
    2036           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, p);
    2037           0 :                 if (r < 0)
    2038           0 :                         return r;
    2039             :         }
    2040             : 
    2041           0 :         r = table_add_cell(table, NULL, TABLE_STRING, "Normalized form:");
    2042           0 :         if (r < 0)
    2043           0 :                 return r;
    2044             : 
    2045           0 :         r = table_add_cell(table, NULL, TABLE_STRING, t);
    2046           0 :         if (r < 0)
    2047           0 :                 return r;
    2048             : 
    2049           0 :         for (unsigned i = 0; i < arg_iterations; i++) {
    2050             :                 usec_t next;
    2051             : 
    2052           0 :                 r = calendar_spec_next_usec(spec, n, &next);
    2053           0 :                 if (r == -ENOENT) {
    2054           0 :                         if (i == 0) {
    2055           0 :                                 r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
    2056           0 :                                 if (r < 0)
    2057           0 :                                         return r;
    2058             : 
    2059           0 :                                 r = table_add_cell(table, &cell, TABLE_STRING, "never");
    2060           0 :                                 if (r < 0)
    2061           0 :                                         return r;
    2062             : 
    2063           0 :                                 r = table_set_color(table, cell, ansi_highlight_yellow());
    2064           0 :                                 if (r < 0)
    2065           0 :                                         return r;
    2066             :                         }
    2067           0 :                         break;
    2068             :                 }
    2069           0 :                 if (r < 0)
    2070           0 :                         return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p);
    2071             : 
    2072           0 :                 if (i == 0) {
    2073           0 :                         r = table_add_cell(table, NULL, TABLE_STRING, "Next elapse:");
    2074           0 :                         if (r < 0)
    2075           0 :                                 return r;
    2076             : 
    2077           0 :                         r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
    2078           0 :                         if (r < 0)
    2079           0 :                                 return r;
    2080             : 
    2081           0 :                         r = table_set_color(table, cell, ansi_highlight_blue());
    2082           0 :                         if (r < 0)
    2083           0 :                                 return r;
    2084             :                 } else {
    2085           0 :                         int k = DECIMAL_STR_WIDTH(i + 1);
    2086             : 
    2087           0 :                         if (k < 8)
    2088           0 :                                 k = 8 - k;
    2089             :                         else
    2090           0 :                                 k = 0;
    2091             : 
    2092           0 :                         r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1);
    2093           0 :                         if (r < 0)
    2094           0 :                                 return r;
    2095             : 
    2096           0 :                         r = table_add_cell(table, &cell, TABLE_TIMESTAMP, &next);
    2097           0 :                         if (r < 0)
    2098           0 :                                 return r;
    2099             : 
    2100           0 :                         r = table_set_color(table, cell, ansi_highlight_blue());
    2101           0 :                         if (r < 0)
    2102           0 :                                 return r;
    2103             :                 }
    2104             : 
    2105           0 :                 if (!in_utc_timezone()) {
    2106           0 :                         r = table_add_cell(table, NULL, TABLE_STRING, "(in UTC):");
    2107           0 :                         if (r < 0)
    2108           0 :                                 return r;
    2109             : 
    2110           0 :                         r = table_add_cell(table, NULL, TABLE_TIMESTAMP_UTC, &next);
    2111           0 :                         if (r < 0)
    2112           0 :                                 return r;
    2113             :                 }
    2114             : 
    2115           0 :                 r = table_add_cell(table, NULL, TABLE_STRING, "From now:");
    2116           0 :                 if (r < 0)
    2117           0 :                         return r;
    2118             : 
    2119           0 :                 r = table_add_cell(table, NULL, TABLE_TIMESTAMP_RELATIVE, &next);
    2120           0 :                 if (r < 0)
    2121           0 :                         return r;
    2122             : 
    2123           0 :                 n = next;
    2124             :         }
    2125             : 
    2126           0 :         return table_print(table, NULL);
    2127             : }
    2128             : 
    2129           0 : static int test_calendar(int argc, char *argv[], void *userdata) {
    2130           0 :         int ret = 0, r;
    2131             :         char **p;
    2132             :         usec_t n;
    2133             : 
    2134           0 :         n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */
    2135             : 
    2136           0 :         STRV_FOREACH(p, strv_skip(argv, 1)) {
    2137           0 :                 r = test_calendar_one(n, *p);
    2138           0 :                 if (ret == 0 && r < 0)
    2139           0 :                         ret = r;
    2140             : 
    2141           0 :                 if (*(p + 1))
    2142           0 :                         putchar('\n');
    2143             :         }
    2144             : 
    2145           0 :         return ret;
    2146             : }
    2147             : 
    2148           0 : static int service_watchdogs(int argc, char *argv[], void *userdata) {
    2149           0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
    2150           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    2151             :         int b, r;
    2152             : 
    2153           0 :         assert(IN_SET(argc, 1, 2));
    2154           0 :         assert(argv);
    2155             : 
    2156           0 :         r = acquire_bus(&bus, NULL);
    2157           0 :         if (r < 0)
    2158           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    2159             : 
    2160             :         /* get ServiceWatchdogs */
    2161           0 :         if (argc == 1) {
    2162           0 :                 r = sd_bus_get_property_trivial(
    2163             :                                 bus,
    2164             :                                 "org.freedesktop.systemd1",
    2165             :                                 "/org/freedesktop/systemd1",
    2166             :                                 "org.freedesktop.systemd1.Manager",
    2167             :                                 "ServiceWatchdogs",
    2168             :                                 &error,
    2169             :                                 'b',
    2170             :                                 &b);
    2171           0 :                 if (r < 0)
    2172           0 :                         return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
    2173             : 
    2174           0 :                 printf("%s\n", yes_no(!!b));
    2175             : 
    2176           0 :                 return 0;
    2177             :         }
    2178             : 
    2179             :         /* set ServiceWatchdogs */
    2180           0 :         b = parse_boolean(argv[1]);
    2181           0 :         if (b < 0) {
    2182           0 :                 log_error("Failed to parse service-watchdogs argument.");
    2183           0 :                 return -EINVAL;
    2184             :         }
    2185             : 
    2186           0 :         r = sd_bus_set_property(
    2187             :                         bus,
    2188             :                         "org.freedesktop.systemd1",
    2189             :                         "/org/freedesktop/systemd1",
    2190             :                         "org.freedesktop.systemd1.Manager",
    2191             :                         "ServiceWatchdogs",
    2192             :                         &error,
    2193             :                         "b",
    2194             :                         b);
    2195           0 :         if (r < 0)
    2196           0 :                 return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
    2197             : 
    2198           0 :         return 0;
    2199             : }
    2200             : 
    2201           0 : static int do_condition(int argc, char *argv[], void *userdata) {
    2202           0 :         return verify_conditions(strv_skip(argv, 1), arg_scope);
    2203             : }
    2204             : 
    2205           0 : static int do_verify(int argc, char *argv[], void *userdata) {
    2206           0 :         return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
    2207             : }
    2208             : 
    2209           0 : static int do_security(int argc, char *argv[], void *userdata) {
    2210           0 :         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
    2211             :         int r;
    2212             : 
    2213           0 :         r = acquire_bus(&bus, NULL);
    2214           0 :         if (r < 0)
    2215           0 :                 return log_error_errno(r, "Failed to create bus connection: %m");
    2216             : 
    2217           0 :         (void) pager_open(arg_pager_flags);
    2218             : 
    2219           0 :         return analyze_security(bus, strv_skip(argv, 1), 0);
    2220             : }
    2221             : 
    2222           3 : static int help(int argc, char *argv[], void *userdata) {
    2223           3 :         _cleanup_free_ char *link = NULL, *dot_link = NULL;
    2224             :         int r;
    2225             : 
    2226           3 :         (void) pager_open(arg_pager_flags);
    2227             : 
    2228           3 :         r = terminal_urlify_man("systemd-analyze", "1", &link);
    2229           3 :         if (r < 0)
    2230           0 :                 return log_oom();
    2231             : 
    2232             :         /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
    2233           3 :         r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
    2234           3 :         if (r < 0)
    2235           0 :                 return log_oom();
    2236             : 
    2237           3 :         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
    2238             :                "Profile systemd, show unit dependencies, check unit files.\n\n"
    2239             :                "  -h --help                Show this help\n"
    2240             :                "     --version             Show package version\n"
    2241             :                "     --no-pager            Do not pipe output into a pager\n"
    2242             :                "     --system              Operate on system systemd instance\n"
    2243             :                "     --user                Operate on user systemd instance\n"
    2244             :                "     --global              Operate on global user configuration\n"
    2245             :                "  -H --host=[USER@]HOST    Operate on remote host\n"
    2246             :                "  -M --machine=CONTAINER   Operate on local container\n"
    2247             :                "     --order               Show only order in the graph\n"
    2248             :                "     --require             Show only requirement in the graph\n"
    2249             :                "     --from-pattern=GLOB   Show only origins in the graph\n"
    2250             :                "     --to-pattern=GLOB     Show only destinations in the graph\n"
    2251             :                "     --fuzz=SECONDS        Also print services which finished SECONDS earlier\n"
    2252             :                "                           than the latest in the branch\n"
    2253             :                "     --man[=BOOL]          Do [not] check for existence of man pages\n"
    2254             :                "     --generators[=BOOL]   Do [not] run unit generators (requires privileges)\n"
    2255             :                "     --iterations=N        Show the specified number of iterations\n"
    2256             :                "\nCommands:\n"
    2257             :                "  time                     Print time spent in the kernel\n"
    2258             :                "  blame                    Print list of running units ordered by time to init\n"
    2259             :                "  critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
    2260             :                "  plot                     Output SVG graphic showing service initialization\n"
    2261             :                "  dot [UNIT...]            Output dependency graph in %s format\n"
    2262             :                "  log-level [LEVEL]        Get/set logging threshold for manager\n"
    2263             :                "  log-target [TARGET]      Get/set logging target for manager\n"
    2264             :                "  dump                     Output state serialization of service manager\n"
    2265             :                "  cat-config               Show configuration file and drop-ins\n"
    2266             :                "  unit-files               List files and symlinks for units\n"
    2267             :                "  unit-paths               List load directories for units\n"
    2268             :                "  exit-status [STATUS...]  List exit status definitions\n"
    2269             :                "  syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
    2270             :                "  condition CONDITION...   Evaluate conditions and asserts\n"
    2271             :                "  verify FILE...           Check unit files for correctness\n"
    2272             :                "  service-watchdogs [BOOL] Get/set service watchdog state\n"
    2273             :                "  calendar SPEC...         Validate repetitive calendar time events\n"
    2274             :                "  timestamp TIMESTAMP...   Validate a timestamp\n"
    2275             :                "  timespan SPAN...         Validate a time span\n"
    2276             :                "  security [UNIT...]       Analyze security of unit\n"
    2277             :                "\nSee the %s for details.\n"
    2278             :                , program_invocation_short_name
    2279             :                , dot_link
    2280             :                , link
    2281             :         );
    2282             : 
    2283             :         /* When updating this list, including descriptions, apply changes to
    2284             :          * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
    2285             : 
    2286           3 :         return 0;
    2287             : }
    2288             : 
    2289           4 : static int parse_argv(int argc, char *argv[]) {
    2290             :         enum {
    2291             :                 ARG_VERSION = 0x100,
    2292             :                 ARG_ORDER,
    2293             :                 ARG_REQUIRE,
    2294             :                 ARG_ROOT,
    2295             :                 ARG_SYSTEM,
    2296             :                 ARG_USER,
    2297             :                 ARG_GLOBAL,
    2298             :                 ARG_DOT_FROM_PATTERN,
    2299             :                 ARG_DOT_TO_PATTERN,
    2300             :                 ARG_FUZZ,
    2301             :                 ARG_NO_PAGER,
    2302             :                 ARG_MAN,
    2303             :                 ARG_GENERATORS,
    2304             :                 ARG_ITERATIONS,
    2305             :         };
    2306             : 
    2307             :         static const struct option options[] = {
    2308             :                 { "help",         no_argument,       NULL, 'h'                  },
    2309             :                 { "version",      no_argument,       NULL, ARG_VERSION          },
    2310             :                 { "order",        no_argument,       NULL, ARG_ORDER            },
    2311             :                 { "require",      no_argument,       NULL, ARG_REQUIRE          },
    2312             :                 { "root",         required_argument, NULL, ARG_ROOT             },
    2313             :                 { "system",       no_argument,       NULL, ARG_SYSTEM           },
    2314             :                 { "user",         no_argument,       NULL, ARG_USER             },
    2315             :                 { "global",       no_argument,       NULL, ARG_GLOBAL           },
    2316             :                 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
    2317             :                 { "to-pattern",   required_argument, NULL, ARG_DOT_TO_PATTERN   },
    2318             :                 { "fuzz",         required_argument, NULL, ARG_FUZZ             },
    2319             :                 { "no-pager",     no_argument,       NULL, ARG_NO_PAGER         },
    2320             :                 { "man",          optional_argument, NULL, ARG_MAN              },
    2321             :                 { "generators",   optional_argument, NULL, ARG_GENERATORS       },
    2322             :                 { "host",         required_argument, NULL, 'H'                  },
    2323             :                 { "machine",      required_argument, NULL, 'M'                  },
    2324             :                 { "iterations",   required_argument, NULL, ARG_ITERATIONS       },
    2325             :                 {}
    2326             :         };
    2327             : 
    2328             :         int r, c;
    2329             : 
    2330           4 :         assert(argc >= 0);
    2331           4 :         assert(argv);
    2332             : 
    2333           4 :         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
    2334           4 :                 switch (c) {
    2335             : 
    2336           3 :                 case 'h':
    2337           3 :                         return help(0, NULL, NULL);
    2338             : 
    2339           0 :                 case ARG_VERSION:
    2340           0 :                         return version();
    2341             : 
    2342           0 :                 case ARG_ROOT:
    2343           0 :                         arg_root = optarg;
    2344           0 :                         break;
    2345             : 
    2346           0 :                 case ARG_SYSTEM:
    2347           0 :                         arg_scope = UNIT_FILE_SYSTEM;
    2348           0 :                         break;
    2349             : 
    2350           0 :                 case ARG_USER:
    2351           0 :                         arg_scope = UNIT_FILE_USER;
    2352           0 :                         break;
    2353             : 
    2354           0 :                 case ARG_GLOBAL:
    2355           0 :                         arg_scope = UNIT_FILE_GLOBAL;
    2356           0 :                         break;
    2357             : 
    2358           0 :                 case ARG_ORDER:
    2359           0 :                         arg_dot = DEP_ORDER;
    2360           0 :                         break;
    2361             : 
    2362           0 :                 case ARG_REQUIRE:
    2363           0 :                         arg_dot = DEP_REQUIRE;
    2364           0 :                         break;
    2365             : 
    2366           0 :                 case ARG_DOT_FROM_PATTERN:
    2367           0 :                         if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
    2368           0 :                                 return log_oom();
    2369             : 
    2370           0 :                         break;
    2371             : 
    2372           0 :                 case ARG_DOT_TO_PATTERN:
    2373           0 :                         if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
    2374           0 :                                 return log_oom();
    2375             : 
    2376           0 :                         break;
    2377             : 
    2378           0 :                 case ARG_FUZZ:
    2379           0 :                         r = parse_sec(optarg, &arg_fuzz);
    2380           0 :                         if (r < 0)
    2381           0 :                                 return r;
    2382           0 :                         break;
    2383             : 
    2384           0 :                 case ARG_NO_PAGER:
    2385           0 :                         arg_pager_flags |= PAGER_DISABLE;
    2386           0 :                         break;
    2387             : 
    2388           0 :                 case 'H':
    2389           0 :                         arg_transport = BUS_TRANSPORT_REMOTE;
    2390           0 :                         arg_host = optarg;
    2391           0 :                         break;
    2392             : 
    2393           0 :                 case 'M':
    2394           0 :                         arg_transport = BUS_TRANSPORT_MACHINE;
    2395           0 :                         arg_host = optarg;
    2396           0 :                         break;
    2397             : 
    2398           0 :                 case ARG_MAN:
    2399           0 :                         if (optarg) {
    2400           0 :                                 r = parse_boolean(optarg);
    2401           0 :                                 if (r < 0)
    2402           0 :                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2403             :                                                                "Failed to parse --man= argument.");
    2404             : 
    2405           0 :                                 arg_man = r;
    2406             :                         } else
    2407           0 :                                 arg_man = true;
    2408             : 
    2409           0 :                         break;
    2410             : 
    2411           0 :                 case ARG_GENERATORS:
    2412           0 :                         if (optarg) {
    2413           0 :                                 r = parse_boolean(optarg);
    2414           0 :                                 if (r < 0)
    2415           0 :                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2416             :                                                                "Failed to parse --generators= argument.");
    2417             : 
    2418           0 :                                 arg_generators = r;
    2419             :                         } else
    2420           0 :                                 arg_generators = true;
    2421             : 
    2422           0 :                         break;
    2423             : 
    2424           0 :                 case ARG_ITERATIONS:
    2425           0 :                         r = safe_atou(optarg, &arg_iterations);
    2426           0 :                         if (r < 0)
    2427           0 :                                 return log_error_errno(r, "Failed to parse iterations: %s", optarg);
    2428             : 
    2429           0 :                         break;
    2430             : 
    2431           1 :                 case '?':
    2432           1 :                         return -EINVAL;
    2433             : 
    2434           0 :                 default:
    2435           0 :                         assert_not_reached("Unhandled option code.");
    2436             :                 }
    2437             : 
    2438           0 :         if (arg_scope == UNIT_FILE_GLOBAL &&
    2439           0 :             !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
    2440           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2441             :                                        "Option --global only makes sense with verbs dot, unit-paths, verify.");
    2442             : 
    2443           0 :         if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER)
    2444           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2445             :                                        "Option --user is not supported for cat-config right now.");
    2446             : 
    2447           0 :         if (arg_root && !streq_ptr(argv[optind], "cat-config"))
    2448           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    2449             :                                        "Option --root is only supported for cat-config right now.");
    2450             : 
    2451           0 :         return 1; /* work to do */
    2452             : }
    2453             : 
    2454           4 : static int run(int argc, char *argv[]) {
    2455             : 
    2456             :         static const Verb verbs[] = {
    2457             :                 { "help",              VERB_ANY, VERB_ANY, 0,            help                   },
    2458             :                 { "time",              VERB_ANY, 1,        VERB_DEFAULT, analyze_time           },
    2459             :                 { "blame",             VERB_ANY, 1,        0,            analyze_blame          },
    2460             :                 { "critical-chain",    VERB_ANY, VERB_ANY, 0,            analyze_critical_chain },
    2461             :                 { "plot",              VERB_ANY, 1,        0,            analyze_plot           },
    2462             :                 { "dot",               VERB_ANY, VERB_ANY, 0,            dot                    },
    2463             :                 { "log-level",         VERB_ANY, 2,        0,            get_or_set_log_level   },
    2464             :                 { "log-target",        VERB_ANY, 2,        0,            get_or_set_log_target  },
    2465             :                 /* The following four verbs are deprecated aliases */
    2466             :                 { "set-log-level",     2,        2,        0,            set_log_level          },
    2467             :                 { "get-log-level",     VERB_ANY, 1,        0,            get_log_level          },
    2468             :                 { "set-log-target",    2,        2,        0,            set_log_target         },
    2469             :                 { "get-log-target",    VERB_ANY, 1,        0,            get_log_target         },
    2470             : 
    2471             :                 { "dump",              VERB_ANY, 1,        0,            dump                   },
    2472             :                 { "cat-config",        2,        VERB_ANY, 0,            cat_config             },
    2473             :                 { "unit-files",        VERB_ANY, VERB_ANY, 0,            do_unit_files          },
    2474             :                 { "unit-paths",        1,        1,        0,            dump_unit_paths        },
    2475             :                 { "exit-status",       VERB_ANY, VERB_ANY, 0,            dump_exit_status       },
    2476             :                 { "syscall-filter",    VERB_ANY, VERB_ANY, 0,            dump_syscall_filters   },
    2477             :                 { "condition",         2,        VERB_ANY, 0,            do_condition           },
    2478             :                 { "verify",            2,        VERB_ANY, 0,            do_verify              },
    2479             :                 { "calendar",          2,        VERB_ANY, 0,            test_calendar          },
    2480             :                 { "timestamp",         2,        VERB_ANY, 0,            test_timestamp         },
    2481             :                 { "timespan",          2,        VERB_ANY, 0,            dump_timespan          },
    2482             :                 { "service-watchdogs", VERB_ANY, 2,        0,            service_watchdogs      },
    2483             :                 { "security",          VERB_ANY, VERB_ANY, 0,            do_security            },
    2484             :                 {}
    2485             :         };
    2486             : 
    2487             :         int r;
    2488             : 
    2489           4 :         setlocale(LC_ALL, "");
    2490           4 :         setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
    2491             : 
    2492           4 :         log_show_color(true);
    2493           4 :         log_parse_environment();
    2494           4 :         log_open();
    2495             : 
    2496           4 :         r = parse_argv(argc, argv);
    2497           4 :         if (r <= 0)
    2498           4 :                 return r;
    2499             : 
    2500           0 :         return dispatch_verb(argc, argv, verbs, NULL);
    2501             : }
    2502             : 
    2503           4 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14