]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze.c
analyze: split out "dump" verb
[thirdparty/systemd.git] / src / analyze / analyze.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2265fbf7 2/***
96b2fb93 3 Copyright © 2013 Simon Peeters
2265fbf7
SP
4***/
5
2265fbf7 6#include <getopt.h>
3f1c1287 7#include <inttypes.h>
3f6fd1ba
LP
8#include <stdio.h>
9#include <stdlib.h>
ca78ad1d 10#include <unistd.h>
2265fbf7 11
048ecf5b 12#include "sd-bus.h"
3f6fd1ba 13
b5efdb8a 14#include "alloc-util.h"
30bddc06 15#include "analyze.h"
5229b03c 16#include "analyze-calendar.h"
edfea9fe 17#include "analyze-condition.h"
25eb70af 18#include "analyze-dump.h"
917e6554 19#include "analyze-elf.h"
08e36480 20#include "analyze-filesystems.h"
ec16f3b6 21#include "analyze-security.h"
389638d3 22#include "analyze-syscall-filter.h"
30bddc06 23#include "analyze-timespan.h"
503ccaaa 24#include "analyze-timestamp.h"
3f6fd1ba 25#include "analyze-verify.h"
048ecf5b 26#include "bus-error.h"
9b71e4ab 27#include "bus-locator.h"
807542be 28#include "bus-map-properties.h"
20b16441 29#include "bus-unit-util.h"
6d86f4bd 30#include "calendarspec.h"
b2af819b
LP
31#include "cap-list.h"
32#include "capability-util.h"
854a42fb 33#include "conf-files.h"
c0a1bfac 34#include "copy.h"
90bea744 35#include "def.h"
76ed04d9 36#include "exit-status.h"
da845dab 37#include "extract-word.h"
c0a1bfac 38#include "fd-util.h"
cdf6258c 39#include "fileio.h"
b41711cd 40#include "filesystems.h"
d8bfdbe1 41#include "format-table.h"
7d50b32a 42#include "glob-util.h"
bb150966 43#include "hashmap.h"
8752c575 44#include "locale-util.h"
3f6fd1ba 45#include "log.h"
d665c7b2 46#include "main-func.h"
e5ea5c3a 47#include "mount-util.h"
d8b4d14d 48#include "nulstr-util.h"
9ea9d4cf 49#include "pager.h"
599c7c54 50#include "parse-argument.h"
6bedfcbb 51#include "parse-util.h"
854a42fb 52#include "path-util.h"
294bf0c3 53#include "pretty-print.h"
da845dab 54#include "rm-rf.h"
349cc4a5 55#if HAVE_SECCOMP
294bf0c3 56# include "seccomp-util.h"
0f734bdc 57#endif
760877e9 58#include "sort-util.h"
3f6fd1ba 59#include "special.h"
b41711cd 60#include "stat-util.h"
3cc3dc77 61#include "string-table.h"
3f6fd1ba
LP
62#include "strv.h"
63#include "strxcpyx.h"
288a74cc 64#include "terminal-util.h"
760877e9 65#include "time-util.h"
da845dab 66#include "tmpfile-util.h"
3f6fd1ba
LP
67#include "unit-name.h"
68#include "util.h"
a87b151a 69#include "verb-log-control.h"
a6bcef29 70#include "verbs.h"
47350c5f 71#include "version.h"
2265fbf7 72
1ace223c 73#define SCALE_X (0.1 / 1000.0) /* pixels per us */
a213b7e9 74#define SCALE_Y (20.0)
2f6eb835 75
2265fbf7 76#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
77
78#define svg_bar(class, x1, x2, y) \
2265fbf7 79 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 80 (class), \
2f6eb835
LP
81 SCALE_X * (x1), SCALE_Y * (y), \
82 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
83
84#define svg_text(b, x, y, format, ...) \
85 do { \
2f6eb835 86 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
c170f3a4
LP
87 svg(format, ## __VA_ARGS__); \
88 svg("</text>\n"); \
9ed794a3 89 } while (false)
2265fbf7 90
1700761b
SP
91static enum dot {
92 DEP_ALL,
93 DEP_ORDER,
94 DEP_REQUIRE
95} arg_dot = DEP_ALL;
1ace223c
SJ
96static char **arg_dot_from_patterns = NULL;
97static char **arg_dot_to_patterns = NULL;
9ea9d4cf 98static usec_t arg_fuzz = 0;
08e36480 99PagerFlags arg_pager_flags = 0;
25eb70af 100BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
f72b7018 101static const char *arg_host = NULL;
28b35ef2 102static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
3cc3dc77 103static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES;
1d3bc017 104static bool arg_man = true;
641c0fd1 105static bool arg_generators = false;
782671bc 106static char *arg_root = NULL;
e5ea5c3a 107static char *arg_image = NULL;
ecfd082b 108static char *arg_security_policy = NULL;
bb43d853 109static bool arg_offline = false;
dfbda879 110static unsigned arg_threshold = 100;
5229b03c
LP
111unsigned arg_iterations = 1;
112usec_t arg_base_time = USEC_INFINITY;
8de7929d 113static char *arg_unit = NULL;
4b4a8ef7 114static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
08e36480 115bool arg_quiet = false;
04469211 116static char *arg_profile = NULL;
bb150966 117
d665c7b2
YW
118STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
119STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
782671bc 120STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
e5ea5c3a 121STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
ecfd082b 122STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
8de7929d 123STATIC_DESTRUCTOR_REGISTER(arg_unit, freep);
04469211 124STATIC_DESTRUCTOR_REGISTER(arg_profile, freep);
d665c7b2 125
6aa601c5 126typedef struct BootTimes {
c170f3a4
LP
127 usec_t firmware_time;
128 usec_t loader_time;
129 usec_t kernel_time;
130 usec_t kernel_done_time;
131 usec_t initrd_time;
132 usec_t userspace_time;
133 usec_t finish_time;
c2e0d600
TA
134 usec_t security_start_time;
135 usec_t security_finish_time;
518d10e9
UTL
136 usec_t generators_start_time;
137 usec_t generators_finish_time;
d9acfb71
TA
138 usec_t unitsload_start_time;
139 usec_t unitsload_finish_time;
8c006565
YW
140 usec_t initrd_security_start_time;
141 usec_t initrd_security_finish_time;
142 usec_t initrd_generators_start_time;
143 usec_t initrd_generators_finish_time;
144 usec_t initrd_unitsload_start_time;
145 usec_t initrd_unitsload_finish_time;
06bef033
IS
146
147 /*
148 * If we're analyzing the user instance, all timestamps will be offset
149 * by its own start-up timestamp, which may be arbitrarily big.
150 * With "plot", this causes arbitrarily wide output SVG files which almost
151 * completely consist of empty space. Thus we cancel out this offset.
152 *
153 * This offset is subtracted from times above by acquire_boot_times(),
154 * but it still needs to be subtracted from unit-specific timestamps
155 * (so it is stored here for reference).
156 */
157 usec_t reverse_offset;
6aa601c5 158} BootTimes;
9ea9d4cf 159
6aa601c5 160typedef struct UnitTimes {
df560cf6 161 bool has_data;
2265fbf7 162 char *name;
cc27380c
TA
163 usec_t activating;
164 usec_t activated;
165 usec_t deactivated;
166 usec_t deactivating;
c170f3a4 167 usec_t time;
6aa601c5 168} UnitTimes;
2265fbf7 169
6aa601c5 170typedef struct HostInfo {
7e690cef
DH
171 char *hostname;
172 char *kernel_name;
173 char *kernel_release;
174 char *kernel_version;
175 char *os_pretty_name;
176 char *virtualization;
177 char *architecture;
6aa601c5 178} HostInfo;
7e690cef 179
25eb70af 180int acquire_bus(sd_bus **bus, bool *use_full_bus) {
5c69b31c 181 bool user = arg_scope != UNIT_FILE_SYSTEM;
fb507898 182 int r;
5c69b31c 183
f7e29336 184 if (use_full_bus && *use_full_bus) {
fb507898
YW
185 r = bus_connect_transport(arg_transport, arg_host, user, bus);
186 if (IN_SET(r, 0, -EHOSTDOWN))
187 return r;
5c69b31c
GJ
188
189 *use_full_bus = false;
190 }
191
f7e29336 192 return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
bf0e0a4d
ZJS
193}
194
048ecf5b 195static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
4afd3348 196 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4 197 int r;
2265fbf7 198
988b9df2
LP
199 assert(bus);
200 assert(path);
201 assert(interface);
202 assert(property);
203 assert(val);
204
a936124f
TA
205 r = sd_bus_get_property_trivial(
206 bus,
207 "org.freedesktop.systemd1",
208 path,
209 interface,
210 property,
211 &error,
212 't', val);
048ecf5b 213
4ae25393 214 if (r < 0)
0ed3da7c 215 return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r));
2265fbf7 216
2265fbf7
SP
217 return 0;
218}
219
988b9df2 220static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
4afd3348 221 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
988b9df2
LP
222 int r;
223
224 assert(bus);
225 assert(path);
226 assert(property);
227 assert(strv);
228
229 r = sd_bus_get_property_strv(
230 bus,
231 "org.freedesktop.systemd1",
232 path,
233 "org.freedesktop.systemd1.Unit",
234 property,
235 &error,
236 strv);
4ae25393 237 if (r < 0)
0ed3da7c 238 return log_error_errno(r, "Failed to get unit property %s: %s", property, bus_error_message(&error, r));
988b9df2
LP
239
240 return 0;
241}
242
6aa601c5 243static int compare_unit_start(const UnitTimes *a, const UnitTimes *b) {
93bab288 244 return CMP(a->activating, b->activating);
2265fbf7
SP
245}
246
da845dab
AB
247static int process_aliases(char *argv[], char *tempdir, char ***ret) {
248 _cleanup_strv_free_ char **filenames = NULL;
249 char **filename;
250 int r;
251
252 assert(argv);
253 assert(tempdir);
254 assert(ret);
255
256 STRV_FOREACH(filename, strv_skip(argv, 1)) {
437346c9
AB
257 _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL;
258 const char *parse_arg;
da845dab 259
437346c9
AB
260 parse_arg = *filename;
261 r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE);
da845dab
AB
262 if (r < 0)
263 return r;
264
265 if (!parse_arg) {
437346c9 266 r = strv_consume(&filenames, TAKE_PTR(src));
da845dab 267 if (r < 0)
437346c9 268 return r;
da845dab
AB
269
270 continue;
271 }
272
437346c9
AB
273 r = path_extract_filename(parse_arg, &base);
274 if (r < 0)
275 return r;
276
277 dst = path_join(tempdir, base);
da845dab
AB
278 if (!dst)
279 return -ENOMEM;
280
281 r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK);
282 if (r < 0)
283 return r;
284
285 r = strv_consume(&filenames, TAKE_PTR(dst));
286 if (r < 0)
437346c9 287 return r;
da845dab
AB
288 }
289
290 *ret = TAKE_PTR(filenames);
291 return 0;
292}
293
15567b3a 294static UnitTimes* unit_times_free_array(UnitTimes *t) {
75db809a 295 for (UnitTimes *p = t; p && p->has_data; p++)
c170f3a4 296 free(p->name);
75db809a 297 return mfree(t);
c170f3a4 298}
15567b3a 299DEFINE_TRIVIAL_CLEANUP_FUNC(UnitTimes*, unit_times_free_array);
df560cf6 300
06bef033
IS
301static void subtract_timestamp(usec_t *a, usec_t b) {
302 assert(a);
303
304 if (*a > 0) {
305 assert(*a >= b);
306 *a -= b;
307 }
308}
309
6aa601c5 310static int acquire_boot_times(sd_bus *bus, BootTimes **bt) {
cc0eb780 311 static const struct bus_properties_map property_map[] = {
6aa601c5
ZJS
312 { "FirmwareTimestampMonotonic", "t", NULL, offsetof(BootTimes, firmware_time) },
313 { "LoaderTimestampMonotonic", "t", NULL, offsetof(BootTimes, loader_time) },
314 { "KernelTimestamp", "t", NULL, offsetof(BootTimes, kernel_time) },
315 { "InitRDTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_time) },
316 { "UserspaceTimestampMonotonic", "t", NULL, offsetof(BootTimes, userspace_time) },
317 { "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) },
318 { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) },
319 { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) },
320 { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_start_time) },
321 { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_finish_time) },
322 { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_start_time) },
323 { "UnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_finish_time) },
324 { "InitRDSecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_security_start_time) },
325 { "InitRDSecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_security_finish_time) },
326 { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_start_time) },
327 { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) },
328 { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time) },
329 { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time) },
cc0eb780
YW
330 {},
331 };
332 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
6aa601c5 333 static BootTimes times;
2265fbf7 334 static bool cached = false;
cc0eb780 335 int r;
c170f3a4 336
2265fbf7 337 if (cached)
c170f3a4
LP
338 goto finish;
339
340 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7 341
cc0eb780
YW
342 r = bus_map_all_properties(
343 bus,
344 "org.freedesktop.systemd1",
345 "/org/freedesktop/systemd1",
346 property_map,
347 BUS_MAP_STRDUP,
348 &error,
349 NULL,
350 &times);
351 if (r < 0)
352 return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r));
8c006565 353
c2953e08
ZJS
354 if (times.finish_time <= 0)
355 return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS),
356 "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
357 "Please try again later.\n"
358 "Hint: Use 'systemctl%s list-jobs' to see active jobs",
359 times.finish_time,
360 arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
2265fbf7 361
eddb5037
YW
362 if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) {
363 /* security_start_time is set when systemd is not running under container environment. */
baa4880b 364 if (times.initrd_time > 0)
28b35ef2
ZJS
365 times.kernel_done_time = times.initrd_time;
366 else
367 times.kernel_done_time = times.userspace_time;
368 } else {
06bef033 369 /*
eddb5037 370 * User-instance-specific or container-system-specific timestamps processing
6aa601c5 371 * (see comment to reverse_offset in BootTimes).
06bef033
IS
372 */
373 times.reverse_offset = times.userspace_time;
374
1ace223c
SJ
375 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time =
376 times.userspace_time = times.security_start_time = times.security_finish_time = 0;
06bef033 377
79ecaae4 378 subtract_timestamp(&times.finish_time, times.reverse_offset);
06bef033
IS
379
380 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
381 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
382
383 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
384 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
06bef033 385 }
2265fbf7
SP
386
387 cached = true;
c170f3a4
LP
388
389finish:
390 *bt = &times;
391 return 0;
2265fbf7
SP
392}
393
75db809a 394static HostInfo* free_host_info(HostInfo *hi) {
b1b533a0 395 if (!hi)
75db809a 396 return NULL;
b1b533a0 397
7e690cef
DH
398 free(hi->hostname);
399 free(hi->kernel_name);
400 free(hi->kernel_release);
401 free(hi->kernel_version);
402 free(hi->os_pretty_name);
403 free(hi->virtualization);
404 free(hi->architecture);
75db809a 405 return mfree(hi);
7e690cef 406}
b1b533a0 407
6aa601c5 408DEFINE_TRIVIAL_CLEANUP_FUNC(HostInfo *, free_host_info);
7e690cef 409
6aa601c5 410static int acquire_time_data(sd_bus *bus, UnitTimes **out) {
cc0eb780 411 static const struct bus_properties_map property_map[] = {
6aa601c5
ZJS
412 { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activating) },
413 { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activated) },
414 { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivating) },
415 { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivated) },
cc0eb780
YW
416 {},
417 };
4afd3348
LP
418 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
419 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
15567b3a 420 _cleanup_(unit_times_free_arrayp) UnitTimes *unit_times = NULL;
6aa601c5 421 BootTimes *boot_times = NULL;
319a4f4b 422 size_t c = 0;
29b8b5ce 423 UnitInfo u;
97cec9ba 424 int r;
29b8b5ce 425
06bef033
IS
426 r = acquire_boot_times(bus, &boot_times);
427 if (r < 0)
df560cf6 428 return r;
06bef033 429
de770b60 430 r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL);
4ae25393 431 if (r < 0)
0ed3da7c 432 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
29b8b5ce
IS
433
434 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
df560cf6
FB
435 if (r < 0)
436 return bus_log_parse_error(r);
29b8b5ce
IS
437
438 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
6aa601c5 439 UnitTimes *t;
29b8b5ce 440
319a4f4b 441 if (!GREEDY_REALLOC(unit_times, c + 2))
df560cf6 442 return log_oom();
29b8b5ce 443
1ace223c 444 unit_times[c + 1].has_data = false;
df560cf6 445 t = &unit_times[c];
29b8b5ce
IS
446 t->name = NULL;
447
448 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
449
cc0eb780
YW
450 r = bus_map_all_properties(
451 bus,
452 "org.freedesktop.systemd1",
453 u.unit_path,
454 property_map,
455 BUS_MAP_STRDUP,
456 &error,
457 NULL,
458 t);
459 if (r < 0)
c2953e08
ZJS
460 return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s",
461 u.id, bus_error_message(&error, r));
29b8b5ce 462
06bef033
IS
463 subtract_timestamp(&t->activating, boot_times->reverse_offset);
464 subtract_timestamp(&t->activated, boot_times->reverse_offset);
465 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
466 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
467
29b8b5ce
IS
468 if (t->activated >= t->activating)
469 t->time = t->activated - t->activating;
470 else if (t->deactivated >= t->activating)
471 t->time = t->deactivated - t->activating;
472 else
473 t->time = 0;
474
475 if (t->activating == 0)
476 continue;
477
478 t->name = strdup(u.id);
df560cf6
FB
479 if (!t->name)
480 return log_oom();
481
482 t->has_data = true;
29b8b5ce
IS
483 c++;
484 }
df560cf6
FB
485 if (r < 0)
486 return bus_log_parse_error(r);
29b8b5ce 487
df560cf6 488 *out = TAKE_PTR(unit_times);
29b8b5ce 489 return c;
29b8b5ce
IS
490}
491
6aa601c5 492static int acquire_host_info(sd_bus *bus, HostInfo **hi) {
7e690cef 493 static const struct bus_properties_map hostname_map[] = {
6aa601c5
ZJS
494 { "Hostname", "s", NULL, offsetof(HostInfo, hostname) },
495 { "KernelName", "s", NULL, offsetof(HostInfo, kernel_name) },
496 { "KernelRelease", "s", NULL, offsetof(HostInfo, kernel_release) },
497 { "KernelVersion", "s", NULL, offsetof(HostInfo, kernel_version) },
498 { "OperatingSystemPrettyName", "s", NULL, offsetof(HostInfo, os_pretty_name) },
7e690cef
DH
499 {}
500 };
501
502 static const struct bus_properties_map manager_map[] = {
6aa601c5
ZJS
503 { "Virtualization", "s", NULL, offsetof(HostInfo, virtualization) },
504 { "Architecture", "s", NULL, offsetof(HostInfo, architecture) },
7e690cef
DH
505 {}
506 };
507
4afd3348 508 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
4f481d76 509 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL;
75db809a 510 _cleanup_(free_host_infop) HostInfo *host = NULL;
b1b533a0
LP
511 int r;
512
6aa601c5 513 host = new0(HostInfo, 1);
7e690cef
DH
514 if (!host)
515 return log_oom();
516
4f481d76
YW
517 if (arg_scope != UNIT_FILE_SYSTEM) {
518 r = bus_connect_transport(arg_transport, arg_host, false, &system_bus);
519 if (r < 0) {
520 log_debug_errno(r, "Failed to connect to system bus, ignoring: %m");
521 goto manager;
522 }
523 }
524
1ace223c
SJ
525 r = bus_map_all_properties(
526 system_bus ?: bus,
527 "org.freedesktop.hostname1",
528 "/org/freedesktop/hostname1",
529 hostname_map,
530 BUS_MAP_STRDUP,
531 &error,
532 NULL,
533 host);
4f481d76 534 if (r < 0) {
c2953e08
ZJS
535 log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s",
536 bus_error_message(&error, r));
4f481d76
YW
537 sd_bus_error_free(&error);
538 }
7e690cef 539
4f481d76 540manager:
1ace223c
SJ
541 r = bus_map_all_properties(
542 bus,
543 "org.freedesktop.systemd1",
544 "/org/freedesktop/systemd1",
545 manager_map,
546 BUS_MAP_STRDUP,
547 &error,
548 NULL,
549 host);
febda62a 550 if (r < 0)
c2953e08
ZJS
551 return log_error_errno(r, "Failed to get host information from systemd: %s",
552 bus_error_message(&error, r));
7e690cef 553
1cc6c93a 554 *hi = TAKE_PTR(host);
7e690cef 555 return 0;
7e690cef
DH
556}
557
048ecf5b 558static int pretty_boot_time(sd_bus *bus, char **_buf) {
6aa601c5 559 BootTimes *t;
2265fbf7 560 static char buf[4096];
c170f3a4
LP
561 size_t size;
562 char *ptr;
563 int r;
bd07d3d0 564 usec_t activated_time = USEC_INFINITY;
1ace223c 565 _cleanup_free_ char *path = NULL, *unit_id = NULL;
bd07d3d0 566 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4
LP
567
568 r = acquire_boot_times(bus, &t);
569 if (r < 0)
570 return r;
2265fbf7 571
bd07d3d0
JR
572 path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
573 if (!path)
574 return log_oom();
575
576 r = sd_bus_get_property_string(
577 bus,
578 "org.freedesktop.systemd1",
579 path,
580 "org.freedesktop.systemd1.Unit",
581 "Id",
582 &error,
583 &unit_id);
584 if (r < 0) {
585 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
586 unit_id = NULL;
587 }
588
589 r = bus_get_uint64_property(bus, path,
590 "org.freedesktop.systemd1.Unit",
591 "ActiveEnterTimestampMonotonic",
592 &activated_time);
593 if (r < 0) {
0c753963 594 log_info_errno(r, "Could not get time to reach default.target, ignoring: %m");
bd07d3d0
JR
595 activated_time = USEC_INFINITY;
596 }
597
c170f3a4
LP
598 ptr = buf;
599 size = sizeof(buf);
2265fbf7
SP
600
601 size = strpcpyf(&ptr, size, "Startup finished in ");
baa4880b 602 if (t->firmware_time > 0)
5291f26d 603 size = strpcpyf(&ptr, size, "%s (firmware) + ", FORMAT_TIMESPAN(t->firmware_time - t->loader_time, USEC_PER_MSEC));
baa4880b 604 if (t->loader_time > 0)
5291f26d 605 size = strpcpyf(&ptr, size, "%s (loader) + ", FORMAT_TIMESPAN(t->loader_time, USEC_PER_MSEC));
02be0cca 606 if (t->kernel_done_time > 0)
5291f26d 607 size = strpcpyf(&ptr, size, "%s (kernel) + ", FORMAT_TIMESPAN(t->kernel_done_time, USEC_PER_MSEC));
2265fbf7 608 if (t->initrd_time > 0)
5291f26d 609 size = strpcpyf(&ptr, size, "%s (initrd) + ", FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC));
2265fbf7 610
5291f26d 611 size = strpcpyf(&ptr, size, "%s (userspace) ", FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC));
02be0cca 612 if (t->kernel_done_time > 0)
5291f26d 613 strpcpyf(&ptr, size, "= %s ", FORMAT_TIMESPAN(t->firmware_time + t->finish_time, USEC_PER_MSEC));
2265fbf7 614
1f65fd49 615 if (unit_id && timestamp_is_set(activated_time)) {
b5d6f7ea
ZJS
616 usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
617
618 size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id,
5291f26d 619 FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC));
eddb5037 620 } else if (unit_id && activated_time == 0)
da933f7d
JR
621 size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
622 else if (unit_id && activated_time == USEC_INFINITY)
feb92776 623 size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id);
da933f7d
JR
624 else if (!unit_id)
625 size = strpcpyf(&ptr, size, "\ncould not find default.target");
626
c170f3a4
LP
627 ptr = strdup(buf);
628 if (!ptr)
629 return log_oom();
630
631 *_buf = ptr;
632 return 0;
2265fbf7
SP
633}
634
c170f3a4 635static void svg_graph_box(double height, double begin, double end) {
2265fbf7
SP
636 /* outside box, fill */
637 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
1ace223c
SJ
638 SCALE_X * (end - begin),
639 SCALE_Y * height);
2265fbf7 640
6aa601c5 641 for (long long i = ((long long) (begin / 100000)) * 100000; i <= end; i += 100000) {
2265fbf7 642 /* lines for each second */
c170f3a4 643 if (i % 5000000 == 0)
2265fbf7
SP
644 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
645 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
1ace223c
SJ
646 SCALE_X * i,
647 SCALE_X * i,
648 SCALE_Y * height,
649 SCALE_X * i,
650 -5.0,
651 0.000001 * i);
c170f3a4 652 else if (i % 1000000 == 0)
2265fbf7
SP
653 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
654 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
1ace223c
SJ
655 SCALE_X * i,
656 SCALE_X * i,
657 SCALE_Y * height,
658 SCALE_X * i,
659 -5.0,
660 0.000001 * i);
2265fbf7
SP
661 else
662 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
1ace223c
SJ
663 SCALE_X * i,
664 SCALE_X * i,
665 SCALE_Y * height);
2265fbf7
SP
666 }
667}
668
6aa601c5 669static int plot_unit_times(UnitTimes *u, double width, int y) {
7725fc11
YW
670 bool b;
671
672 if (!u->name)
673 return 0;
674
675 svg_bar("activating", u->activating, u->activated, y);
676 svg_bar("active", u->activated, u->deactivating, y);
677 svg_bar("deactivating", u->deactivating, u->deactivated, y);
678
679 /* place the text on the left if we have passed the half of the svg width */
680 b = u->activating * SCALE_X < width / 2;
681 if (u->time)
682 svg_text(b, u->activating, y, "%s (%s)",
5291f26d 683 u->name, FORMAT_TIMESPAN(u->time, USEC_PER_MSEC));
7725fc11
YW
684 else
685 svg_text(b, u->activating, y, "%s", u->name);
686
687 return 1;
688}
689
a6bcef29 690static int analyze_plot(int argc, char *argv[], void *userdata) {
6aa601c5 691 _cleanup_(free_host_infop) HostInfo *host = NULL;
a6bcef29 692 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
15567b3a 693 _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL;
4f481d76
YW
694 _cleanup_free_ char *pretty_times = NULL;
695 bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM;
6aa601c5
ZJS
696 BootTimes *boot;
697 UnitTimes *u;
a6bcef29 698 int n, m = 1, y = 0, r;
2265fbf7 699 double width;
2265fbf7 700
f7e29336 701 r = acquire_bus(&bus, &use_full_bus);
a6bcef29 702 if (r < 0)
10a7340a 703 return bus_log_connect_error(r, arg_transport);
a6bcef29 704
c170f3a4
LP
705 n = acquire_boot_times(bus, &boot);
706 if (n < 0)
707 return n;
2265fbf7 708
c170f3a4
LP
709 n = pretty_boot_time(bus, &pretty_times);
710 if (n < 0)
711 return n;
2265fbf7 712
4f481d76 713 if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) {
5c69b31c
GJ
714 n = acquire_host_info(bus, &host);
715 if (n < 0)
716 return n;
717 }
2265fbf7
SP
718
719 n = acquire_time_data(bus, &times);
c170f3a4 720 if (n <= 0)
19f462f2 721 return n;
2265fbf7 722
93bab288 723 typesafe_qsort(times, n, compare_unit_start);
2265fbf7 724
2f6eb835 725 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
726 if (width < 800.0)
727 width = 800.0;
728
729 if (boot->firmware_time > boot->loader_time)
730 m++;
baa4880b 731 if (boot->loader_time > 0) {
2265fbf7
SP
732 m++;
733 if (width < 1000.0)
734 width = 1000.0;
735 }
baa4880b 736 if (boot->initrd_time > 0)
2265fbf7 737 m++;
02be0cca 738 if (boot->kernel_done_time > 0)
2265fbf7
SP
739 m++;
740
df560cf6 741 for (u = times; u->has_data; u++) {
95168f7d 742 double text_start, text_width;
c170f3a4 743
7725fc11 744 if (u->activating > boot->finish_time) {
a1e58e8e 745 u->name = mfree(u->name);
2265fbf7
SP
746 continue;
747 }
95168f7d
TA
748
749 /* If the text cannot fit on the left side then
750 * increase the svg width so it fits on the right.
751 * TODO: calculate the text width more accurately */
752 text_width = 8.0 * strlen(u->name);
cc27380c 753 text_start = (boot->firmware_time + u->activating) * SCALE_X;
95168f7d
TA
754 if (text_width > text_start && text_width + text_start > width)
755 width = text_width + text_start;
2265fbf7 756
efe6112d
YW
757 if (u->deactivated > u->activating &&
758 u->deactivated <= boot->finish_time &&
759 u->activated == 0 && u->deactivating == 0)
cc27380c
TA
760 u->activated = u->deactivating = u->deactivated;
761 if (u->activated < u->activating || u->activated > boot->finish_time)
762 u->activated = boot->finish_time;
efe6112d 763 if (u->deactivating < u->activated || u->deactivating > boot->finish_time)
cc27380c
TA
764 u->deactivating = boot->finish_time;
765 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
766 u->deactivated = boot->finish_time;
2265fbf7
SP
767 m++;
768 }
769
770 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
771 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
772 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
773
774 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
775 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
518d10e9 776 80.0 + width, 150.0 + (m * SCALE_Y) +
d9acfb71 777 5 * SCALE_Y /* legend */);
2265fbf7
SP
778
779 /* write some basic info as a comment, including some help */
780 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
781 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
782 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
783 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
784 "<!-- point your browser to this file. -->\n\n"
681bd2c5 785 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", GIT_VERSION);
2265fbf7
SP
786
787 /* style sheet */
788 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
789 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
418e3750 790 " rect.background { fill: rgb(255,255,255); }\n"
2265fbf7
SP
791 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
792 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
793 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
794 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
795 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
796 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
797 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
798 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
c2e0d600 799 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
518d10e9 800 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
d9acfb71 801 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
2265fbf7
SP
802 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
803 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
804 "// line.sec1 { }\n"
805 " line.sec5 { stroke-width: 2; }\n"
806 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
2b7d6965
TA
807 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
808 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
809 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
810 " text.sec { font-size: 10px; }\n"
2265fbf7
SP
811 " ]]>\n </style>\n</defs>\n\n");
812
418e3750 813 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
2265fbf7 814 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
4f481d76 815 if (host)
5c69b31c
GJ
816 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
817 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
818 strempty(host->hostname),
819 strempty(host->kernel_name),
820 strempty(host->kernel_release),
821 strempty(host->kernel_version),
822 strempty(host->architecture),
823 strempty(host->virtualization));
2265fbf7 824
2f6eb835 825 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
b5cfa740 826 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
2265fbf7 827
baa4880b 828 if (boot->firmware_time > 0) {
c170f3a4
LP
829 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
830 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
831 y++;
832 }
baa4880b 833 if (boot->loader_time > 0) {
c170f3a4
LP
834 svg_bar("loader", -(double) boot->loader_time, 0, y);
835 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
836 y++;
837 }
02be0cca 838 if (boot->kernel_done_time > 0) {
2265fbf7 839 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 840 svg_text(true, 0, y, "kernel");
2265fbf7
SP
841 y++;
842 }
baa4880b 843 if (boot->initrd_time > 0) {
2265fbf7 844 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
8c006565
YW
845 if (boot->initrd_security_start_time < boot->initrd_security_finish_time)
846 svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y);
847 if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time)
848 svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y);
849 if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time)
850 svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y);
c170f3a4 851 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
852 y++;
853 }
7725fc11
YW
854
855 for (u = times; u->has_data; u++) {
856 if (u->activating >= boot->userspace_time)
857 break;
858
859 y += plot_unit_times(u, width, y);
860 }
861
518d10e9 862 svg_bar("active", boot->userspace_time, boot->finish_time, y);
79ecaae4
YW
863 if (boot->security_start_time > 0)
864 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
518d10e9 865 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
d9acfb71 866 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
95168f7d 867 svg_text(true, boot->userspace_time, y, "systemd");
2265fbf7
SP
868 y++;
869
7725fc11
YW
870 for (; u->has_data; u++)
871 y += plot_unit_times(u, width, y);
518d10e9 872
b5cfa740
TA
873 svg("</g>\n");
874
518d10e9 875 /* Legend */
b5cfa740 876 svg("<g transform=\"translate(20,100)\">\n");
518d10e9
UTL
877 y++;
878 svg_bar("activating", 0, 300000, y);
95168f7d 879 svg_text(true, 400000, y, "Activating");
518d10e9
UTL
880 y++;
881 svg_bar("active", 0, 300000, y);
95168f7d 882 svg_text(true, 400000, y, "Active");
518d10e9
UTL
883 y++;
884 svg_bar("deactivating", 0, 300000, y);
95168f7d 885 svg_text(true, 400000, y, "Deactivating");
518d10e9 886 y++;
79ecaae4
YW
887 if (boot->security_start_time > 0) {
888 svg_bar("security", 0, 300000, y);
889 svg_text(true, 400000, y, "Setting up security module");
890 y++;
891 }
518d10e9 892 svg_bar("generators", 0, 300000, y);
95168f7d 893 svg_text(true, 400000, y, "Generators");
518d10e9 894 y++;
d9acfb71 895 svg_bar("unitsload", 0, 300000, y);
95168f7d 896 svg_text(true, 400000, y, "Loading unit files");
d9acfb71 897 y++;
518d10e9 898
2265fbf7
SP
899 svg("</g>\n\n");
900
988b9df2 901 svg("</svg>\n");
c170f3a4 902
df560cf6 903 return 0;
2265fbf7
SP
904}
905
1ace223c
SJ
906static int list_dependencies_print(
907 const char *name,
908 unsigned level,
909 unsigned branches,
910 bool last,
6aa601c5
ZJS
911 UnitTimes *times,
912 BootTimes *boot) {
1ace223c 913
6aa601c5 914 for (unsigned i = level; i != 0; i--)
9a6f746f 915 printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE));
bb150966 916
9a6f746f 917 printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH));
bb150966
HH
918
919 if (times) {
baa4880b 920 if (times->time > 0)
54f8c958 921 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
5291f26d
ZJS
922 FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC),
923 FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal());
cc27380c 924 else if (times->activated > boot->userspace_time)
5291f26d 925 printf("%s @%s", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
926 else
927 printf("%s", name);
988b9df2
LP
928 } else
929 printf("%s", name);
bb150966
HH
930 printf("\n");
931
932 return 0;
933}
934
048ecf5b 935static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
988b9df2 936 _cleanup_free_ char *path = NULL;
bb150966
HH
937
938 assert(bus);
939 assert(name);
940 assert(deps);
941
942 path = unit_dbus_path_from_name(name);
234519ae 943 if (!path)
988b9df2 944 return -ENOMEM;
bb150966 945
988b9df2 946 return bus_get_unit_property_strv(bus, path, "After", deps);
bb150966
HH
947}
948
949static Hashmap *unit_times_hashmap;
950
1ace223c 951static int list_dependencies_compare(char *const *a, char *const *b) {
bb150966 952 usec_t usa = 0, usb = 0;
6aa601c5 953 UnitTimes *times;
bb150966
HH
954
955 times = hashmap_get(unit_times_hashmap, *a);
956 if (times)
cc27380c 957 usa = times->activated;
bb150966
HH
958 times = hashmap_get(unit_times_hashmap, *b);
959 if (times)
cc27380c 960 usb = times->activated;
bb150966 961
93bab288 962 return CMP(usb, usa);
bb150966
HH
963}
964
6aa601c5 965static bool times_in_range(const UnitTimes *times, const BootTimes *boot) {
1ace223c 966 return times && times->activated > 0 && times->activated <= boot->finish_time;
230cc99a
ZJS
967}
968
1ace223c 969static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) {
bb150966
HH
970 _cleanup_strv_free_ char **deps = NULL;
971 char **c;
05f7a068 972 int r;
bb150966
HH
973 usec_t service_longest = 0;
974 int to_print = 0;
6aa601c5
ZJS
975 UnitTimes *times;
976 BootTimes *boot;
bb150966 977
988b9df2 978 if (strv_extend(units, name))
bb150966
HH
979 return log_oom();
980
981 r = list_dependencies_get_dependencies(bus, name, &deps);
982 if (r < 0)
983 return r;
984
93bab288 985 typesafe_qsort(deps, strv_length(deps), list_dependencies_compare);
bb150966
HH
986
987 r = acquire_boot_times(bus, &boot);
988 if (r < 0)
989 return r;
990
991 STRV_FOREACH(c, deps) {
33b7988d 992 times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */
1ace223c 993 if (times_in_range(times, boot) && times->activated >= service_longest)
cc27380c 994 service_longest = times->activated;
bb150966
HH
995 }
996
234519ae 997 if (service_longest == 0)
bb150966
HH
998 return r;
999
1000 STRV_FOREACH(c, deps) {
33b7988d 1001 times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */
1ace223c 1002 if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz)
bb150966 1003 to_print++;
bb150966
HH
1004 }
1005
f168c273 1006 if (!to_print)
bb150966
HH
1007 return r;
1008
1009 STRV_FOREACH(c, deps) {
33b7988d 1010 times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */
1ace223c 1011 if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz)
bb150966
HH
1012 continue;
1013
1014 to_print--;
1015
1016 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
1017 if (r < 0)
1018 return r;
1019
1020 if (strv_contains(*units, *c)) {
1021 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
1022 true, NULL, boot);
872c8faa
ZJS
1023 if (r < 0)
1024 return r;
bb150966
HH
1025 continue;
1026 }
1027
1ace223c 1028 r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0));
872c8faa 1029 if (r < 0)
bb150966
HH
1030 return r;
1031
baa4880b 1032 if (to_print == 0)
bb150966 1033 break;
bb150966
HH
1034 }
1035 return 0;
1036}
1037
048ecf5b 1038static int list_dependencies(sd_bus *bus, const char *name) {
bb150966 1039 _cleanup_strv_free_ char **units = NULL;
6aa601c5 1040 UnitTimes *times;
bb150966 1041 int r;
0ee9613d
TA
1042 const char *id;
1043 _cleanup_free_ char *path = NULL;
4afd3348
LP
1044 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1045 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
6aa601c5 1046 BootTimes *boot;
bb150966
HH
1047
1048 assert(bus);
1049
805bf39c 1050 path = unit_dbus_path_from_name(name);
234519ae 1051 if (!path)
988b9df2 1052 return -ENOMEM;
bb150966 1053
a936124f
TA
1054 r = sd_bus_get_property(
1055 bus,
1056 "org.freedesktop.systemd1",
1057 path,
1058 "org.freedesktop.systemd1.Unit",
1059 "Id",
1060 &error,
1061 &reply,
1062 "s");
4ae25393 1063 if (r < 0)
0ed3da7c 1064 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
bb150966 1065
048ecf5b 1066 r = sd_bus_message_read(reply, "s", &id);
5b30bef8
LP
1067 if (r < 0)
1068 return bus_log_parse_error(r);
bb150966 1069
bb150966
HH
1070 times = hashmap_get(unit_times_hashmap, id);
1071
1072 r = acquire_boot_times(bus, &boot);
1073 if (r < 0)
1074 return r;
1075
1076 if (times) {
1077 if (times->time)
54f8c958 1078 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
5291f26d 1079 FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal());
cc27380c 1080 else if (times->activated > boot->userspace_time)
5291f26d
ZJS
1081 printf("%s @%s\n", id,
1082 FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
1083 else
1084 printf("%s\n", id);
1085 }
1086
805bf39c 1087 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
1088}
1089
a6bcef29
LP
1090static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
1091 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
15567b3a 1092 _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL;
bb150966 1093 Hashmap *h;
988b9df2 1094 int n, r;
bb150966 1095
f7e29336 1096 r = acquire_bus(&bus, NULL);
a6bcef29 1097 if (r < 0)
10a7340a 1098 return bus_log_connect_error(r, arg_transport);
a6bcef29 1099
bb150966
HH
1100 n = acquire_time_data(bus, &times);
1101 if (n <= 0)
1102 return n;
1103
d5099efc 1104 h = hashmap_new(&string_hash_ops);
bb150966 1105 if (!h)
8efbce13 1106 return log_oom();
bb150966 1107
6aa601c5 1108 for (UnitTimes *u = times; u->has_data; u++) {
df560cf6 1109 r = hashmap_put(h, u->name, u);
bb150966 1110 if (r < 0)
8efbce13 1111 return log_error_errno(r, "Failed to add entry to hashmap: %m");
bb150966
HH
1112 }
1113 unit_times_hashmap = h;
1114
384c2c32 1115 pager_open(arg_pager_flags);
9ea9d4cf 1116
2fffb93b
ZJS
1117 puts("The time when unit became active or started is printed after the \"@\" character.\n"
1118 "The time the unit took to start is printed after the \"+\" character.\n");
bb150966 1119
a6bcef29 1120 if (argc > 1) {
805bf39c 1121 char **name;
a6bcef29 1122 STRV_FOREACH(name, strv_skip(argv, 1))
805bf39c 1123 list_dependencies(bus, *name);
9ea9d4cf 1124 } else
805bf39c 1125 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966 1126
a6bcef29 1127 h = hashmap_free(h);
bb150966
HH
1128 return 0;
1129}
1130
a6bcef29
LP
1131static int analyze_blame(int argc, char *argv[], void *userdata) {
1132 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
15567b3a 1133 _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL;
d8bfdbe1 1134 _cleanup_(table_unrefp) Table *table = NULL;
d8bfdbe1 1135 TableCell *cell;
a6bcef29
LP
1136 int n, r;
1137
f7e29336 1138 r = acquire_bus(&bus, NULL);
a6bcef29 1139 if (r < 0)
10a7340a 1140 return bus_log_connect_error(r, arg_transport);
c170f3a4
LP
1141
1142 n = acquire_time_data(bus, &times);
1143 if (n <= 0)
2265fbf7
SP
1144 return n;
1145
4252171a 1146 table = table_new("time", "unit");
d8bfdbe1
YW
1147 if (!table)
1148 return log_oom();
2265fbf7 1149
d8bfdbe1
YW
1150 table_set_header(table, false);
1151
1152 assert_se(cell = table_get_cell(table, 0, 0));
1153 r = table_set_ellipsize_percent(table, cell, 100);
1154 if (r < 0)
1155 return r;
1156
1157 r = table_set_align_percent(table, cell, 100);
1158 if (r < 0)
1159 return r;
1160
1161 assert_se(cell = table_get_cell(table, 0, 1));
1162 r = table_set_ellipsize_percent(table, cell, 100);
1163 if (r < 0)
1164 return r;
1165
ef1e0b9a 1166 r = table_set_sort(table, (size_t) 0);
d8bfdbe1
YW
1167 if (r < 0)
1168 return r;
1169
1170 r = table_set_reverse(table, 0, true);
1171 if (r < 0)
1172 return r;
9ea9d4cf 1173
6aa601c5 1174 for (UnitTimes *u = times; u->has_data; u++) {
d8bfdbe1
YW
1175 if (u->time <= 0)
1176 continue;
1177
9c46b437 1178 r = table_add_many(table,
47cc458e 1179 TABLE_TIMESPAN_MSEC, u->time,
9c46b437 1180 TABLE_STRING, u->name);
d8bfdbe1 1181 if (r < 0)
9c46b437 1182 return table_log_add_error(r);
2265fbf7 1183 }
c170f3a4 1184
384c2c32 1185 pager_open(arg_pager_flags);
d8bfdbe1
YW
1186
1187 return table_print(table, NULL);
2265fbf7
SP
1188}
1189
a6bcef29
LP
1190static int analyze_time(int argc, char *argv[], void *userdata) {
1191 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
c170f3a4
LP
1192 _cleanup_free_ char *buf = NULL;
1193 int r;
1194
f7e29336 1195 r = acquire_bus(&bus, NULL);
a6bcef29 1196 if (r < 0)
10a7340a 1197 return bus_log_connect_error(r, arg_transport);
a6bcef29 1198
c170f3a4
LP
1199 r = pretty_boot_time(bus, &buf);
1200 if (r < 0)
1201 return r;
1202
1203 puts(buf);
2265fbf7
SP
1204 return 0;
1205}
1206
1ace223c
SJ
1207static int graph_one_property(
1208 sd_bus *bus,
1209 const UnitInfo *u,
1210 const char *prop,
1211 const char *color,
1212 char *patterns[],
1213 char *from_patterns[],
1214 char *to_patterns[]) {
1215
048ecf5b 1216 _cleanup_strv_free_ char **units = NULL;
048ecf5b
TA
1217 char **unit;
1218 int r;
6ecb6cec 1219 bool match_patterns;
1700761b 1220
048ecf5b 1221 assert(u);
1700761b 1222 assert(prop);
048ecf5b
TA
1223 assert(color);
1224
191a3f16 1225 match_patterns = strv_fnmatch(patterns, u->id);
6ecb6cec 1226
191a3f16 1227 if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id))
1ace223c 1228 return 0;
6ecb6cec 1229
6e6ca4a5 1230 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
07d0eaa0 1231 if (r < 0)
988b9df2 1232 return r;
1700761b 1233
048ecf5b 1234 STRV_FOREACH(unit, units) {
6ecb6cec
ZJS
1235 bool match_patterns2;
1236
191a3f16 1237 match_patterns2 = strv_fnmatch(patterns, *unit);
816f25e8 1238
191a3f16 1239 if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit))
bceccd5e 1240 continue;
e55933db 1241
6ecb6cec 1242 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
bceccd5e 1243 continue;
048ecf5b
TA
1244
1245 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1700761b
SP
1246 }
1247
1248 return 0;
1249}
1250
4387795e 1251static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1700761b 1252 int r;
1700761b
SP
1253
1254 assert(bus);
1255 assert(u);
1256
8901b405 1257 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
4387795e 1258 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
048ecf5b
TA
1259 if (r < 0)
1260 return r;
1700761b
SP
1261 }
1262
8901b405 1263 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
4387795e 1264 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
8901b405
MS
1265 if (r < 0)
1266 return r;
1267 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
048ecf5b
TA
1268 if (r < 0)
1269 return r;
4387795e 1270 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
048ecf5b
TA
1271 if (r < 0)
1272 return r;
4387795e 1273 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
048ecf5b
TA
1274 if (r < 0)
1275 return r;
1700761b
SP
1276 }
1277
1278 return 0;
1279}
1280
83efb7c2
EV
1281static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1282 _cleanup_strv_free_ char **expanded_patterns = NULL;
1283 char **pattern;
1284 int r;
1285
1286 STRV_FOREACH(pattern, patterns) {
4afd3348 1287 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
83efb7c2
EV
1288 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1289
1290 if (strv_extend(&expanded_patterns, *pattern) < 0)
1291 return log_oom();
1292
1293 if (string_is_glob(*pattern))
1294 continue;
1295
1296 unit = unit_dbus_path_from_name(*pattern);
1297 if (!unit)
1298 return log_oom();
1299
1300 r = sd_bus_get_property_string(
1301 bus,
1302 "org.freedesktop.systemd1",
1303 unit,
1304 "org.freedesktop.systemd1.Unit",
1305 "Id",
1306 &error,
1307 &unit_id);
1308 if (r < 0)
1309 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1310
1311 if (!streq(*pattern, unit_id)) {
1312 if (strv_extend(&expanded_patterns, unit_id) < 0)
1313 return log_oom();
1314 }
1315 }
1316
d7a0f1f4 1317 *ret = TAKE_PTR(expanded_patterns); /* do not free */
83efb7c2
EV
1318
1319 return 0;
1320}
1321
a6bcef29 1322static int dot(int argc, char *argv[], void *userdata) {
4afd3348
LP
1323 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1324 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29 1325 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
83efb7c2 1326 _cleanup_strv_free_ char **expanded_patterns = NULL;
4387795e
EV
1327 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1328 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1700761b 1329 int r;
f459b602 1330 UnitInfo u;
048ecf5b 1331
f7e29336 1332 r = acquire_bus(&bus, NULL);
a6bcef29 1333 if (r < 0)
10a7340a 1334 return bus_log_connect_error(r, arg_transport);
a6bcef29
LP
1335
1336 r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
83efb7c2
EV
1337 if (r < 0)
1338 return r;
1339
4387795e
EV
1340 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1341 if (r < 0)
1342 return r;
1343
1344 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1345 if (r < 0)
1346 return r;
1347
41b88bb8 1348 r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL);
4ae25393 1349 if (r < 0)
0ed3da7c 1350 log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
1700761b 1351
048ecf5b 1352 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1700761b 1353 if (r < 0)
988b9df2 1354 return bus_log_parse_error(r);
1700761b
SP
1355
1356 printf("digraph systemd {\n");
1357
048ecf5b 1358 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
f459b602 1359
4387795e 1360 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1700761b
SP
1361 if (r < 0)
1362 return r;
1363 }
f459b602
MAP
1364 if (r < 0)
1365 return bus_log_parse_error(r);
1700761b
SP
1366
1367 printf("}\n");
1368
1369 log_info(" Color legend: black = Requires\n"
1370 " dark blue = Requisite\n"
1371 " dark grey = Wants\n"
1372 " red = Conflicts\n"
1373 " green = After\n");
1374
52117f5a 1375 if (on_tty() && !arg_quiet)
1700761b
SP
1376 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1377 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1378
1379 return 0;
1380}
c8a8806e 1381
854a42fb 1382static int cat_config(int argc, char *argv[], void *userdata) {
2987225c 1383 char **arg, **list;
854a42fb
ZJS
1384 int r;
1385
384c2c32 1386 pager_open(arg_pager_flags);
854a42fb 1387
2987225c
LP
1388 list = strv_skip(argv, 1);
1389 STRV_FOREACH(arg, list) {
971f6ea5
ZJS
1390 const char *t = NULL;
1391
2987225c 1392 if (arg != list)
cb91deaf 1393 print_separator();
854a42fb
ZJS
1394
1395 if (path_is_absolute(*arg)) {
971f6ea5
ZJS
1396 const char *dir;
1397
1398 NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
1399 t = path_startswith(*arg, dir);
1400 if (t)
1401 break;
1402 }
1403
baaa35ad
ZJS
1404 if (!t)
1405 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
c2953e08 1406 "Path %s does not start with any known prefix.", *arg);
971f6ea5
ZJS
1407 } else
1408 t = *arg;
1409
1410 r = conf_files_cat(arg_root, t);
854a42fb
ZJS
1411 if (r < 0)
1412 return r;
1413 }
1414
1415 return 0;
1416}
1417
a87b151a 1418static int verb_log_control(int argc, char *argv[], void *userdata) {
a6bcef29 1419 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2ca2a91c
LP
1420 int r;
1421
b98416e1 1422 assert(IN_SET(argc, 1, 2));
2ca2a91c 1423
f7e29336 1424 r = acquire_bus(&bus, NULL);
a6bcef29 1425 if (r < 0)
10a7340a 1426 return bus_log_connect_error(r, arg_transport);
a65615ca 1427
a87b151a 1428 return verb_log_control_common(bus, "org.freedesktop.systemd1", argv[0], argc == 2 ? argv[1] : NULL);
90657286
YW
1429}
1430
e67cd21d
ZJS
1431static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) {
1432 char **s;
1433 STRV_FOREACH(s, strv)
1434 if (strv_fnmatch_or_empty(patterns, *s, flags))
1435 return true;
1436
1437 return false;
1438}
1439
1440static int do_unit_files(int argc, char *argv[], void *userdata) {
1441 _cleanup_(lookup_paths_free) LookupPaths lp = {};
1442 _cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
1443 _cleanup_hashmap_free_ Hashmap *unit_names = NULL;
1444 char **patterns = strv_skip(argv, 1);
e67cd21d
ZJS
1445 const char *k, *dst;
1446 char **v;
1447 int r;
1448
1449 r = lookup_paths_init(&lp, arg_scope, 0, NULL);
1450 if (r < 0)
1451 return log_error_errno(r, "lookup_paths_init() failed: %m");
1452
91e0ee5f 1453 r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL);
e67cd21d
ZJS
1454 if (r < 0)
1455 return log_error_errno(r, "unit_file_build_name_map() failed: %m");
1456
90e74a66 1457 HASHMAP_FOREACH_KEY(dst, k, unit_ids) {
e67cd21d
ZJS
1458 if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
1459 !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE))
1460 continue;
1461
1462 printf("ids: %s → %s\n", k, dst);
1463 }
1464
90e74a66 1465 HASHMAP_FOREACH_KEY(v, k, unit_names) {
e67cd21d
ZJS
1466 if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) &&
1467 !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE))
1468 continue;
1469
1470 _cleanup_free_ char *j = strv_join(v, ", ");
1471 printf("aliases: %s ← %s\n", k, j);
1472 }
1473
1474 return 0;
1475}
1476
31a5924e 1477static int dump_unit_paths(int argc, char *argv[], void *userdata) {
8e766630 1478 _cleanup_(lookup_paths_free) LookupPaths paths = {};
31a5924e
ZJS
1479 int r;
1480 char **p;
1481
1482 r = lookup_paths_init(&paths, arg_scope, 0, NULL);
1483 if (r < 0)
1484 return log_error_errno(r, "lookup_paths_init() failed: %m");
1485
1486 STRV_FOREACH(p, paths.search_path)
1487 puts(*p);
1488
1489 return 0;
1490}
1491
417b82e1
MG
1492static int dump_exit_status(int argc, char *argv[], void *userdata) {
1493 _cleanup_(table_unrefp) Table *table = NULL;
1494 int r;
1495
1496 table = table_new("name", "status", "class");
1497 if (!table)
1498 return log_oom();
1499
1500 r = table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
1501 if (r < 0)
1502 return log_error_errno(r, "Failed to right-align status: %m");
1503
1504 if (strv_isempty(strv_skip(argv, 1)))
1505 for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) {
1506 if (!exit_status_mappings[i].name)
1507 continue;
1508
1509 r = table_add_many(table,
1510 TABLE_STRING, exit_status_mappings[i].name,
1511 TABLE_INT, (int) i,
1512 TABLE_STRING, exit_status_class(i));
1513 if (r < 0)
9c46b437 1514 return table_log_add_error(r);
417b82e1
MG
1515 }
1516 else
1517 for (int i = 1; i < argc; i++) {
1518 int status;
1519
1520 status = exit_status_from_string(argv[i]);
1521 if (status < 0)
7211c853 1522 return log_error_errno(status, "Invalid exit status \"%s\".", argv[i]);
417b82e1
MG
1523
1524 assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings));
1525 r = table_add_many(table,
1526 TABLE_STRING, exit_status_mappings[status].name ?: "-",
1527 TABLE_INT, status,
1528 TABLE_STRING, exit_status_class(status) ?: "-");
1529 if (r < 0)
9c46b437 1530 return table_log_add_error(r);
417b82e1
MG
1531 }
1532
384c2c32 1533 pager_open(arg_pager_flags);
417b82e1
MG
1534
1535 return table_print(table, NULL);
1536}
1537
b2af819b
LP
1538static int dump_capabilities(int argc, char *argv[], void *userdata) {
1539 _cleanup_(table_unrefp) Table *table = NULL;
1540 unsigned last_cap;
1541 int r;
1542
1543 table = table_new("name", "number");
1544 if (!table)
1545 return log_oom();
1546
1547 (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 100);
1548
1549 /* Determine the maximum of the last cap known by the kernel and by us */
1550 last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap());
1551
1552 if (strv_isempty(strv_skip(argv, 1)))
1553 for (unsigned c = 0; c <= last_cap; c++) {
1554 r = table_add_many(table,
1555 TABLE_STRING, capability_to_name(c) ?: "cap_???",
1556 TABLE_UINT, c);
1557 if (r < 0)
1558 return table_log_add_error(r);
1559 }
1560 else {
1561 for (int i = 1; i < argc; i++) {
1562 int c;
1563
1564 c = capability_from_name(argv[i]);
1565 if (c < 0 || (unsigned) c > last_cap)
1566 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" not known.", argv[i]);
1567
1568 r = table_add_many(table,
1569 TABLE_STRING, capability_to_name(c) ?: "cap_???",
1570 TABLE_UINT, (unsigned) c);
1571 if (r < 0)
1572 return table_log_add_error(r);
1573 }
1574
ef1e0b9a 1575 (void) table_set_sort(table, (size_t) 1);
b2af819b
LP
1576 }
1577
384c2c32 1578 pager_open(arg_pager_flags);
b2af819b
LP
1579
1580 return table_print(table, NULL);
1581}
1582
30bddc06 1583void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) {
c269607f
ZJS
1584 if (calendar && calendar_spec_from_string(p, NULL) >= 0)
1585 log_notice("Hint: this expression is a valid calendar specification. "
1586 "Use 'systemd-analyze calendar \"%s\"' instead?", p);
1587 if (timestamp && parse_timestamp(p, NULL) >= 0)
1588 log_notice("Hint: this expression is a valid timestamp. "
1589 "Use 'systemd-analyze timestamp \"%s\"' instead?", p);
1590 if (timespan && parse_time(p, NULL, USEC_PER_SEC) >= 0)
1591 log_notice("Hint: this expression is a valid timespan. "
1592 "Use 'systemd-analyze timespan \"%s\"' instead?", p);
6d86f4bd
LP
1593}
1594
889d695d
JK
1595static int service_watchdogs(int argc, char *argv[], void *userdata) {
1596 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1597 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1598 int b, r;
1599
90657286 1600 assert(IN_SET(argc, 1, 2));
889d695d
JK
1601 assert(argv);
1602
f7e29336 1603 r = acquire_bus(&bus, NULL);
90657286 1604 if (r < 0)
10a7340a 1605 return bus_log_connect_error(r, arg_transport);
90657286 1606
90657286 1607 if (argc == 1) {
6ab86319 1608 /* get ServiceWatchdogs */
de770b60 1609 r = bus_get_property_trivial(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, 'b', &b);
90657286
YW
1610 if (r < 0)
1611 return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
1612
1613 printf("%s\n", yes_no(!!b));
1614
6ab86319
ZJS
1615 } else {
1616 /* set ServiceWatchdogs */
1617 b = parse_boolean(argv[1]);
1618 if (b < 0)
1619 return log_error_errno(b, "Failed to parse service-watchdogs argument: %m");
90657286 1620
de770b60 1621 r = bus_set_property(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, "b", b);
6ab86319
ZJS
1622 if (r < 0)
1623 return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
889d695d
JK
1624 }
1625
889d695d
JK
1626 return 0;
1627}
1628
edfea9fe 1629static int do_condition(int argc, char *argv[], void *userdata) {
8de7929d 1630 return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root);
edfea9fe
ZJS
1631}
1632
a6bcef29 1633static int do_verify(int argc, char *argv[], void *userdata) {
da845dab
AB
1634 _cleanup_strv_free_ char **filenames = NULL;
1635 _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL;
1636 int r;
1637
1638 r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir);
1639 if (r < 0)
1640 return log_error_errno(r, "Failed to setup working directory: %m");
1641
1642 r = process_aliases(argv, tempdir, &filenames);
1643 if (r < 0)
1644 return log_error_errno(r, "Couldn't process aliases: %m");
1645
1646 return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root);
a6bcef29
LP
1647}
1648
ec16f3b6
LP
1649static int do_security(int argc, char *argv[], void *userdata) {
1650 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
ecfd082b 1651 _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
ec16f3b6 1652 int r;
ecfd082b 1653 unsigned line, column;
ec16f3b6 1654
741c4c8d
LB
1655 if (!arg_offline) {
1656 r = acquire_bus(&bus, NULL);
1657 if (r < 0)
1658 return bus_log_connect_error(r, arg_transport);
1659 }
ec16f3b6 1660
384c2c32 1661 pager_open(arg_pager_flags);
ec16f3b6 1662
ecfd082b
MG
1663 if (arg_security_policy) {
1664 r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
1665 if (r < 0)
1666 return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
1667 } else {
1668 _cleanup_fclose_ FILE *f = NULL;
1669 _cleanup_free_ char *pp = NULL;
1670
1671 r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
1672 if (r < 0 && r != -ENOENT)
1673 return r;
1674
b98416e1 1675 if (f) {
ecfd082b
MG
1676 r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
1677 if (r < 0)
1678 return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
1679 }
1680 }
1681
1682 return analyze_security(bus,
1683 strv_skip(argv, 1),
1684 policy,
1685 arg_scope,
1686 arg_man,
1687 arg_generators,
1688 arg_offline,
1689 arg_threshold,
1690 arg_root,
04469211 1691 arg_profile,
4b4a8ef7
MG
1692 arg_json_format_flags,
1693 arg_pager_flags,
ecfd082b 1694 /*flags=*/ 0);
ec16f3b6
LP
1695}
1696
917e6554
LB
1697static int do_elf_inspection(int argc, char *argv[], void *userdata) {
1698 pager_open(arg_pager_flags);
1699
1700 return analyze_elf(strv_skip(argv, 1), arg_json_format_flags);
1701}
1702
a6bcef29 1703static int help(int argc, char *argv[], void *userdata) {
49139a5d 1704 _cleanup_free_ char *link = NULL, *dot_link = NULL;
37ec0fdd 1705 int r;
9ea9d4cf 1706
384c2c32 1707 pager_open(arg_pager_flags);
9ea9d4cf 1708
37ec0fdd
LP
1709 r = terminal_urlify_man("systemd-analyze", "1", &link);
1710 if (r < 0)
1711 return log_oom();
1712
49139a5d
LP
1713 /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
1714 r = terminal_urlify("man:dot(1)", "dot(1)", &dot_link);
1715 if (r < 0)
1716 return log_oom();
1717
353b2baa
LP
1718 printf("%s [OPTIONS...] COMMAND ...\n\n"
1719 "%sProfile systemd, show unit dependencies, check unit files.%s\n"
20a51f6a 1720 "\nCommands:\n"
3cc3dc77
MG
1721 " [time] Print time required to boot the machine\n"
1722 " blame Print list of running units ordered by\n"
1723 " time to init\n"
1724 " critical-chain [UNIT...] Print a tree of the time critical chain\n"
1725 " of units\n"
1726 " plot Output SVG graphic showing service\n"
1727 " initialization\n"
1728 " dot [UNIT...] Output dependency graph in %s format\n"
1729 " dump Output state serialization of service\n"
1730 " manager\n"
1731 " cat-config Show configuration file and drop-ins\n"
1732 " unit-files List files and symlinks for units\n"
1733 " unit-paths List load directories for units\n"
1734 " exit-status [STATUS...] List exit status definitions\n"
1735 " capability [CAP...] List capability definitions\n"
52117f5a
ZJS
1736 " syscall-filter [NAME...] List syscalls in seccomp filters\n"
1737 " filesystems [NAME...] List known filesystems\n"
3cc3dc77
MG
1738 " condition CONDITION... Evaluate conditions and asserts\n"
1739 " verify FILE... Check unit files for correctness\n"
1740 " calendar SPEC... Validate repetitive calendar time\n"
1741 " events\n"
1742 " timestamp TIMESTAMP... Validate a timestamp\n"
1743 " timespan SPAN... Validate a time span\n"
1744 " security [UNIT...] Analyze security of unit\n"
917e6554 1745 " inspect-elf FILE... Parse and print ELF package metadata\n"
353b2baa 1746 "\nOptions:\n"
3cc3dc77 1747 " --recursive-errors=MODE Control which units are verified\n"
bb43d853 1748 " --offline=BOOL Perform a security review on unit file(s)\n"
dfbda879
MG
1749 " --threshold=N Exit with a non-zero status when overall\n"
1750 " exposure level is over threshold value\n"
ecfd082b
MG
1751 " --security-policy=PATH Use custom JSON security policy instead\n"
1752 " of built-in one\n"
4b4a8ef7
MG
1753 " --json=pretty|short|off Generate JSON output of the security\n"
1754 " analysis table\n"
3cc3dc77
MG
1755 " --no-pager Do not pipe output into a pager\n"
1756 " --system Operate on system systemd instance\n"
1757 " --user Operate on user systemd instance\n"
1758 " --global Operate on global user configuration\n"
1759 " -H --host=[USER@]HOST Operate on remote host\n"
1760 " -M --machine=CONTAINER Operate on local container\n"
1761 " --order Show only order in the graph\n"
1762 " --require Show only requirement in the graph\n"
1763 " --from-pattern=GLOB Show only origins in the graph\n"
1764 " --to-pattern=GLOB Show only destinations in the graph\n"
1765 " --fuzz=SECONDS Also print services which finished SECONDS\n"
1766 " earlier than the latest in the branch\n"
1767 " --man[=BOOL] Do [not] check for existence of man pages\n"
1768 " --generators[=BOOL] Do [not] run unit generators\n"
1769 " (requires privileges)\n"
1770 " --iterations=N Show the specified number of iterations\n"
1771 " --base-time=TIMESTAMP Calculate calendar times relative to\n"
1772 " specified time\n"
04469211
LB
1773 " --profile=name|PATH Include the specified profile in the\n"
1774 " security review of the unit(s)\n"
52117f5a
ZJS
1775 " -h --help Show this help\n"
1776 " --version Show package version\n"
1777 " -q --quiet Do not emit hints\n"
bc556335
DDM
1778 "\nSee the %s for details.\n",
1779 program_invocation_short_name,
1780 ansi_highlight(),
1781 ansi_normal(),
1782 dot_link,
1783 link);
96de7c04 1784
1ace223c
SJ
1785 /* When updating this list, including descriptions, apply changes to
1786 * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
a6bcef29
LP
1787
1788 return 0;
2265fbf7
SP
1789}
1790
9ea9d4cf 1791static int parse_argv(int argc, char *argv[]) {
2265fbf7
SP
1792 enum {
1793 ARG_VERSION = 0x100,
1700761b
SP
1794 ARG_ORDER,
1795 ARG_REQUIRE,
46d8646a 1796 ARG_ROOT,
e5ea5c3a 1797 ARG_IMAGE,
e55933db 1798 ARG_SYSTEM,
28b35ef2
ZJS
1799 ARG_USER,
1800 ARG_GLOBAL,
e55933db 1801 ARG_DOT_FROM_PATTERN,
bb150966 1802 ARG_DOT_TO_PATTERN,
9ea9d4cf 1803 ARG_FUZZ,
1d3bc017 1804 ARG_NO_PAGER,
dad29dff 1805 ARG_MAN,
641c0fd1 1806 ARG_GENERATORS,
f2ccf832 1807 ARG_ITERATIONS,
985c1880 1808 ARG_BASE_TIME,
3cc3dc77 1809 ARG_RECURSIVE_ERRORS,
bb43d853 1810 ARG_OFFLINE,
dfbda879 1811 ARG_THRESHOLD,
ecfd082b 1812 ARG_SECURITY_POLICY,
4b4a8ef7 1813 ARG_JSON,
04469211 1814 ARG_PROFILE,
2265fbf7
SP
1815 };
1816
1817 static const struct option options[] = {
3cc3dc77
MG
1818 { "help", no_argument, NULL, 'h' },
1819 { "version", no_argument, NULL, ARG_VERSION },
52117f5a 1820 { "quiet", no_argument, NULL, 'q' },
3cc3dc77
MG
1821 { "order", no_argument, NULL, ARG_ORDER },
1822 { "require", no_argument, NULL, ARG_REQUIRE },
1823 { "root", required_argument, NULL, ARG_ROOT },
1824 { "image", required_argument, NULL, ARG_IMAGE },
1825 { "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
bb43d853 1826 { "offline", required_argument, NULL, ARG_OFFLINE },
dfbda879 1827 { "threshold", required_argument, NULL, ARG_THRESHOLD },
ecfd082b 1828 { "security-policy", required_argument, NULL, ARG_SECURITY_POLICY },
3cc3dc77
MG
1829 { "system", no_argument, NULL, ARG_SYSTEM },
1830 { "user", no_argument, NULL, ARG_USER },
1831 { "global", no_argument, NULL, ARG_GLOBAL },
1832 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1833 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1834 { "fuzz", required_argument, NULL, ARG_FUZZ },
1835 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1836 { "man", optional_argument, NULL, ARG_MAN },
1837 { "generators", optional_argument, NULL, ARG_GENERATORS },
1838 { "host", required_argument, NULL, 'H' },
1839 { "machine", required_argument, NULL, 'M' },
1840 { "iterations", required_argument, NULL, ARG_ITERATIONS },
1841 { "base-time", required_argument, NULL, ARG_BASE_TIME },
8de7929d 1842 { "unit", required_argument, NULL, 'U' },
4b4a8ef7 1843 { "json", required_argument, NULL, ARG_JSON },
04469211 1844 { "profile", required_argument, NULL, ARG_PROFILE },
eb9da376 1845 {}
2265fbf7
SP
1846 };
1847
eb9da376
LP
1848 int r, c;
1849
2265fbf7
SP
1850 assert(argc >= 0);
1851 assert(argv);
1852
8de7929d 1853 while ((c = getopt_long(argc, argv, "hH:M:U:", options, NULL)) >= 0)
eb9da376 1854 switch (c) {
c170f3a4
LP
1855
1856 case 'h':
a6bcef29 1857 return help(0, NULL, NULL);
c170f3a4 1858
52117f5a
ZJS
1859 case ARG_VERSION:
1860 return version();
1861
1862 case 'q':
1863 arg_quiet = true;
1864 break;
1865
3cc3dc77
MG
1866 case ARG_RECURSIVE_ERRORS:
1867 if (streq(optarg, "help")) {
1868 DUMP_STRING_TABLE(recursive_errors, RecursiveErrors, _RECURSIVE_ERRORS_MAX);
1869 return 0;
1870 }
1871 r = recursive_errors_from_string(optarg);
1872 if (r < 0)
1873 return log_error_errno(r, "Unknown mode passed to --recursive-errors='%s'.", optarg);
1874
1875 arg_recursive_errors = r;
1876 break;
1877
46d8646a 1878 case ARG_ROOT:
782671bc
MG
1879 r = parse_path_argument(optarg, /* suppress_root= */ true, &arg_root);
1880 if (r < 0)
1881 return r;
46d8646a
ZJS
1882 break;
1883
e5ea5c3a
MG
1884 case ARG_IMAGE:
1885 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
1886 if (r < 0)
1887 return r;
1888 break;
1889
28b35ef2
ZJS
1890 case ARG_SYSTEM:
1891 arg_scope = UNIT_FILE_SYSTEM;
1892 break;
1893
c170f3a4 1894 case ARG_USER:
28b35ef2 1895 arg_scope = UNIT_FILE_USER;
c170f3a4
LP
1896 break;
1897
28b35ef2
ZJS
1898 case ARG_GLOBAL:
1899 arg_scope = UNIT_FILE_GLOBAL;
c170f3a4
LP
1900 break;
1901
1902 case ARG_ORDER:
1903 arg_dot = DEP_ORDER;
1904 break;
1905
1906 case ARG_REQUIRE:
1907 arg_dot = DEP_REQUIRE;
1908 break;
1909
e55933db 1910 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
1911 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1912 return log_oom();
1913
e55933db
ŁS
1914 break;
1915
1916 case ARG_DOT_TO_PATTERN:
903a0b07
LP
1917 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1918 return log_oom();
1919
e55933db
ŁS
1920 break;
1921
bb150966
HH
1922 case ARG_FUZZ:
1923 r = parse_sec(optarg, &arg_fuzz);
1924 if (r < 0)
1925 return r;
1926 break;
1927
9ea9d4cf 1928 case ARG_NO_PAGER:
0221d68a 1929 arg_pager_flags |= PAGER_DISABLE;
9ea9d4cf
LP
1930 break;
1931
3cd26e7c
LP
1932 case 'H':
1933 arg_transport = BUS_TRANSPORT_REMOTE;
1934 arg_host = optarg;
1935 break;
1936
1937 case 'M':
de33fc62 1938 arg_transport = BUS_TRANSPORT_MACHINE;
3cd26e7c
LP
1939 arg_host = optarg;
1940 break;
1941
dad29dff 1942 case ARG_MAN:
599c7c54
ZJS
1943 r = parse_boolean_argument("--man", optarg, &arg_man);
1944 if (r < 0)
1945 return r;
1d3bc017
ZJS
1946 break;
1947
641c0fd1 1948 case ARG_GENERATORS:
599c7c54
ZJS
1949 r = parse_boolean_argument("--generators", optarg, &arg_generators);
1950 if (r < 0)
1951 return r;
641c0fd1
ZJS
1952 break;
1953
bb43d853
MG
1954 case ARG_OFFLINE:
1955 r = parse_boolean_argument("--offline", optarg, &arg_offline);
1956 if (r < 0)
1957 return r;
1958 break;
1959
dfbda879
MG
1960 case ARG_THRESHOLD:
1961 r = safe_atou(optarg, &arg_threshold);
1962 if (r < 0 || arg_threshold > 100)
1963 return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse threshold: %s", optarg);
1964
1965 break;
1966
ecfd082b
MG
1967 case ARG_SECURITY_POLICY:
1968 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_security_policy);
1969 if (r < 0)
1970 return r;
1971 break;
1972
4b4a8ef7
MG
1973 case ARG_JSON:
1974 r = parse_json_argument(optarg, &arg_json_format_flags);
1975 if (r <= 0)
1976 return r;
1977 break;
1978
f2ccf832
LP
1979 case ARG_ITERATIONS:
1980 r = safe_atou(optarg, &arg_iterations);
1981 if (r < 0)
1982 return log_error_errno(r, "Failed to parse iterations: %s", optarg);
1983
1984 break;
1985
985c1880
LP
1986 case ARG_BASE_TIME:
1987 r = parse_timestamp(optarg, &arg_base_time);
1988 if (r < 0)
1989 return log_error_errno(r, "Failed to parse --base-time= parameter: %s", optarg);
1990
1991 break;
1992
04469211
LB
1993 case ARG_PROFILE:
1994 if (isempty(optarg))
1995 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name is empty");
1996
1997 if (is_path(optarg)) {
1998 r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_profile);
1999 if (r < 0)
2000 return r;
2001 if (!endswith(arg_profile, ".conf"))
2002 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Profile file name must end with .conf: %s", arg_profile);
2003 } else {
2004 r = free_and_strdup(&arg_profile, optarg);
2005 if (r < 0)
2006 return log_oom();
2007 }
2008
2009 break;
2010
8de7929d
DDM
2011 case 'U': {
2012 _cleanup_free_ char *mangled = NULL;
2013
2014 r = unit_name_mangle(optarg, UNIT_NAME_MANGLE_WARN, &mangled);
2015 if (r < 0)
2016 return log_error_errno(r, "Failed to mangle unit name %s: %m", optarg);
2017
2018 free_and_replace(arg_unit, mangled);
2019 break;
2020 }
c170f3a4
LP
2021 case '?':
2022 return -EINVAL;
2023
2024 default:
04499a70 2025 assert_not_reached();
2265fbf7 2026 }
eb9da376 2027
bb43d853
MG
2028 if (arg_offline && !streq_ptr(argv[optind], "security"))
2029 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2030 "Option --offline= is only supported for security right now.");
2031
917e6554 2032 if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf"))
4b4a8ef7 2033 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
c0f65909 2034 "Option --json= is only supported for security and inspect-elf right now.");
4b4a8ef7 2035
dfbda879
MG
2036 if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
2037 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2038 "Option --threshold= is only supported for security right now.");
2039
31a5924e 2040 if (arg_scope == UNIT_FILE_GLOBAL &&
baaa35ad
ZJS
2041 !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
2042 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2043 "Option --global only makes sense with verbs dot, unit-paths, verify.");
31a5924e 2044
f1d9d36a
ZJS
2045 if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER)
2046 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2047 "Option --user is not supported for cat-config right now.");
2048
ecfd082b
MG
2049 if (arg_security_policy && !streq_ptr(argv[optind], "security"))
2050 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2051 "Option --security-policy= is only supported for security.");
2052
8de7929d 2053 if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify", "condition")) &&
bb43d853 2054 (!(streq_ptr(argv[optind], "security") && arg_offline)))
baaa35ad 2055 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
8de7929d 2056 "Options --root= and --image= are only supported for cat-config, verify, condition and security when used with --offline= right now.");
e5ea5c3a
MG
2057
2058 /* Having both an image and a root is not supported by the code */
2059 if (arg_root && arg_image)
2060 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
46d8646a 2061
8de7929d
DDM
2062 if (arg_unit && !streq_ptr(argv[optind], "condition"))
2063 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --unit= is only supported for condition");
2064
2065 if (streq_ptr(argv[optind], "condition") && !arg_unit && optind >= argc - 1)
2066 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too few arguments for condition");
2067
2068 if (streq_ptr(argv[optind], "condition") && arg_unit && optind < argc - 1)
2069 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No conditions can be passed if --unit= is used.");
2070
1d3bc017 2071 return 1; /* work to do */
2265fbf7
SP
2072}
2073
d665c7b2 2074static int run(int argc, char *argv[]) {
e5ea5c3a
MG
2075 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2076 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2077 _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
a6bcef29
LP
2078
2079 static const Verb verbs[] = {
889d695d
JK
2080 { "help", VERB_ANY, VERB_ANY, 0, help },
2081 { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
2082 { "blame", VERB_ANY, 1, 0, analyze_blame },
2083 { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
2084 { "plot", VERB_ANY, 1, 0, analyze_plot },
2085 { "dot", VERB_ANY, VERB_ANY, 0, dot },
26e1e973 2086 /* The following seven verbs are deprecated */
a87b151a
DDM
2087 { "log-level", VERB_ANY, 2, 0, verb_log_control },
2088 { "log-target", VERB_ANY, 2, 0, verb_log_control },
2089 { "set-log-level", 2, 2, 0, verb_log_control },
2090 { "get-log-level", VERB_ANY, 1, 0, verb_log_control },
2091 { "set-log-target", 2, 2, 0, verb_log_control },
2092 { "get-log-target", VERB_ANY, 1, 0, verb_log_control },
26e1e973 2093 { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
889d695d 2094 { "dump", VERB_ANY, 1, 0, dump },
854a42fb 2095 { "cat-config", 2, VERB_ANY, 0, cat_config },
e67cd21d 2096 { "unit-files", VERB_ANY, VERB_ANY, 0, do_unit_files },
31a5924e 2097 { "unit-paths", 1, 1, 0, dump_unit_paths },
5238d9a8 2098 { "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status },
889d695d 2099 { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
b2af819b 2100 { "capability", VERB_ANY, VERB_ANY, 0, dump_capabilities },
b41711cd 2101 { "filesystems", VERB_ANY, VERB_ANY, 0, dump_filesystems },
8de7929d 2102 { "condition", VERB_ANY, VERB_ANY, 0, do_condition },
889d695d
JK
2103 { "verify", 2, VERB_ANY, 0, do_verify },
2104 { "calendar", 2, VERB_ANY, 0, test_calendar },
2cae4711 2105 { "timestamp", 2, VERB_ANY, 0, test_timestamp },
3f1c1287 2106 { "timespan", 2, VERB_ANY, 0, dump_timespan },
ec16f3b6 2107 { "security", VERB_ANY, VERB_ANY, 0, do_security },
917e6554 2108 { "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection },
a6bcef29
LP
2109 {}
2110 };
2111
5220a6f3 2112 int r;
2265fbf7
SP
2113
2114 setlocale(LC_ALL, "");
c170f3a4 2115 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
a6bcef29 2116
d2acb93d 2117 log_setup();
2265fbf7
SP
2118
2119 r = parse_argv(argc, argv);
9ea9d4cf 2120 if (r <= 0)
d665c7b2 2121 return r;
c170f3a4 2122
e5ea5c3a
MG
2123 /* Open up and mount the image */
2124 if (arg_image) {
2125 assert(!arg_root);
2126
2127 r = mount_image_privately_interactively(
2128 arg_image,
2129 DISSECT_IMAGE_GENERIC_ROOT |
2130 DISSECT_IMAGE_RELAX_VAR_CHECK |
2131 DISSECT_IMAGE_READ_ONLY,
2132 &unlink_dir,
2133 &loop_device,
2134 &decrypted_image);
2135 if (r < 0)
2136 return r;
2137
2138 arg_root = strdup(unlink_dir);
2139 if (!arg_root)
2140 return log_oom();
2141 }
2142
d665c7b2 2143 return dispatch_verb(argc, argv, verbs, NULL);
2265fbf7 2144}
d665c7b2
YW
2145
2146DEFINE_MAIN_FUNCTION(run);