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