]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze.c
tree-wide: drop redundant _cleanup_ macros (#8810)
[thirdparty/systemd.git] / src / analyze / analyze.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2265fbf7
SP
2/***
3 This file is part of systemd.
4
ffaa0e25
LP
5 Copyright 2010-2013 Lennart Poettering
6 Copyright 2013 Simon Peeters
2265fbf7
SP
7***/
8
2265fbf7
SP
9#include <getopt.h>
10#include <locale.h>
3f6fd1ba
LP
11#include <stdio.h>
12#include <stdlib.h>
2265fbf7 13
048ecf5b 14#include "sd-bus.h"
3f6fd1ba 15
b5efdb8a 16#include "alloc-util.h"
3f6fd1ba 17#include "analyze-verify.h"
048ecf5b 18#include "bus-error.h"
20b16441 19#include "bus-unit-util.h"
3f6fd1ba 20#include "bus-util.h"
6d86f4bd 21#include "calendarspec.h"
7d50b32a 22#include "glob-util.h"
bb150966 23#include "hashmap.h"
8752c575 24#include "locale-util.h"
3f6fd1ba 25#include "log.h"
9ea9d4cf 26#include "pager.h"
6bedfcbb 27#include "parse-util.h"
349cc4a5 28#if HAVE_SECCOMP
869feb33 29#include "seccomp-util.h"
0f734bdc 30#endif
3f6fd1ba
LP
31#include "special.h"
32#include "strv.h"
33#include "strxcpyx.h"
288a74cc 34#include "terminal-util.h"
3f6fd1ba
LP
35#include "unit-name.h"
36#include "util.h"
a6bcef29 37#include "verbs.h"
2265fbf7 38
2f6eb835 39#define SCALE_X (0.1 / 1000.0) /* pixels per us */
a213b7e9 40#define SCALE_Y (20.0)
2f6eb835 41
2265fbf7 42#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
c170f3a4 43
2265fbf7 44#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
45
46#define svg_bar(class, x1, x2, y) \
2265fbf7 47 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 48 (class), \
2f6eb835
LP
49 SCALE_X * (x1), SCALE_Y * (y), \
50 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
51
52#define svg_text(b, x, y, format, ...) \
53 do { \
2f6eb835 54 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
55 svg(format, ## __VA_ARGS__); \
56 svg("</text>\n"); \
9ed794a3 57 } while (false)
2265fbf7 58
1700761b
SP
59static enum dot {
60 DEP_ALL,
61 DEP_ORDER,
62 DEP_REQUIRE
63} arg_dot = DEP_ALL;
e55933db
ŁS
64static char** arg_dot_from_patterns = NULL;
65static char** arg_dot_to_patterns = NULL;
9ea9d4cf
LP
66static usec_t arg_fuzz = 0;
67static bool arg_no_pager = false;
3cd26e7c 68static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
f72b7018 69static const char *arg_host = NULL;
28b35ef2 70static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
1d3bc017 71static bool arg_man = true;
641c0fd1 72static bool arg_generators = false;
bb150966 73
2265fbf7 74struct boot_times {
c170f3a4
LP
75 usec_t firmware_time;
76 usec_t loader_time;
77 usec_t kernel_time;
78 usec_t kernel_done_time;
79 usec_t initrd_time;
80 usec_t userspace_time;
81 usec_t finish_time;
c2e0d600
TA
82 usec_t security_start_time;
83 usec_t security_finish_time;
518d10e9
UTL
84 usec_t generators_start_time;
85 usec_t generators_finish_time;
d9acfb71
TA
86 usec_t unitsload_start_time;
87 usec_t unitsload_finish_time;
06bef033
IS
88
89 /*
90 * If we're analyzing the user instance, all timestamps will be offset
91 * by its own start-up timestamp, which may be arbitrarily big.
92 * With "plot", this causes arbitrarily wide output SVG files which almost
93 * completely consist of empty space. Thus we cancel out this offset.
94 *
95 * This offset is subtracted from times above by acquire_boot_times(),
96 * but it still needs to be subtracted from unit-specific timestamps
97 * (so it is stored here for reference).
98 */
99 usec_t reverse_offset;
2265fbf7 100};
9ea9d4cf 101
2265fbf7
SP
102struct unit_times {
103 char *name;
cc27380c
TA
104 usec_t activating;
105 usec_t activated;
106 usec_t deactivated;
107 usec_t deactivating;
c170f3a4 108 usec_t time;
2265fbf7
SP
109};
110
7e690cef
DH
111struct host_info {
112 char *hostname;
113 char *kernel_name;
114 char *kernel_release;
115 char *kernel_version;
116 char *os_pretty_name;
117 char *virtualization;
118 char *architecture;
119};
120
5c69b31c
GJ
121static int acquire_systemd_bus(sd_bus **bus) {
122 bool user = arg_scope != UNIT_FILE_SYSTEM;
123
124 return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
125}
126
127static int acquire_full_bus(bool *use_full_bus, sd_bus **bus) {
28b35ef2
ZJS
128 bool user = arg_scope != UNIT_FILE_SYSTEM;
129
5c69b31c
GJ
130 if (*use_full_bus) {
131 if (bus_connect_transport(arg_transport, arg_host, user, bus) == 0)
132 return 0;
133
134 *use_full_bus = false;
135 }
136
137 return acquire_systemd_bus(bus);
bf0e0a4d
ZJS
138}
139
048ecf5b 140static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
4afd3348 141 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4 142 int r;
2265fbf7 143
988b9df2
LP
144 assert(bus);
145 assert(path);
146 assert(interface);
147 assert(property);
148 assert(val);
149
a936124f
TA
150 r = sd_bus_get_property_trivial(
151 bus,
152 "org.freedesktop.systemd1",
153 path,
154 interface,
155 property,
156 &error,
157 't', val);
048ecf5b
TA
158
159 if (r < 0) {
160 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
2265fbf7 161 return r;
2265fbf7
SP
162 }
163
2265fbf7
SP
164 return 0;
165}
166
988b9df2 167static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
4afd3348 168 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
988b9df2
LP
169 int r;
170
171 assert(bus);
172 assert(path);
173 assert(property);
174 assert(strv);
175
176 r = sd_bus_get_property_strv(
177 bus,
178 "org.freedesktop.systemd1",
179 path,
180 "org.freedesktop.systemd1.Unit",
181 property,
182 &error,
183 strv);
184 if (r < 0) {
185 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
186 return r;
187 }
188
189 return 0;
190}
191
c170f3a4 192static int compare_unit_time(const void *a, const void *b) {
2265fbf7
SP
193 return compare(((struct unit_times *)b)->time,
194 ((struct unit_times *)a)->time);
195}
196
c170f3a4 197static int compare_unit_start(const void *a, const void *b) {
cc27380c
TA
198 return compare(((struct unit_times *)a)->activating,
199 ((struct unit_times *)b)->activating);
2265fbf7
SP
200}
201
c170f3a4
LP
202static void free_unit_times(struct unit_times *t, unsigned n) {
203 struct unit_times *p;
204
205 for (p = t; p < t + n; p++)
206 free(p->name);
207
208 free(t);
209}
210
06bef033
IS
211static void subtract_timestamp(usec_t *a, usec_t b) {
212 assert(a);
213
214 if (*a > 0) {
215 assert(*a >= b);
216 *a -= b;
217 }
218}
219
048ecf5b 220static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
2265fbf7
SP
221 static struct boot_times times;
222 static bool cached = false;
c170f3a4 223
2265fbf7 224 if (cached)
c170f3a4
LP
225 goto finish;
226
227 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7
SP
228
229 if (bus_get_uint64_property(bus,
230 "/org/freedesktop/systemd1",
231 "org.freedesktop.systemd1.Manager",
232 "FirmwareTimestampMonotonic",
233 &times.firmware_time) < 0 ||
234 bus_get_uint64_property(bus,
235 "/org/freedesktop/systemd1",
236 "org.freedesktop.systemd1.Manager",
237 "LoaderTimestampMonotonic",
238 &times.loader_time) < 0 ||
239 bus_get_uint64_property(bus,
240 "/org/freedesktop/systemd1",
241 "org.freedesktop.systemd1.Manager",
242 "KernelTimestamp",
243 &times.kernel_time) < 0 ||
244 bus_get_uint64_property(bus,
245 "/org/freedesktop/systemd1",
246 "org.freedesktop.systemd1.Manager",
247 "InitRDTimestampMonotonic",
248 &times.initrd_time) < 0 ||
249 bus_get_uint64_property(bus,
250 "/org/freedesktop/systemd1",
251 "org.freedesktop.systemd1.Manager",
252 "UserspaceTimestampMonotonic",
253 &times.userspace_time) < 0 ||
254 bus_get_uint64_property(bus,
255 "/org/freedesktop/systemd1",
256 "org.freedesktop.systemd1.Manager",
257 "FinishTimestampMonotonic",
518d10e9 258 &times.finish_time) < 0 ||
c2e0d600
TA
259 bus_get_uint64_property(bus,
260 "/org/freedesktop/systemd1",
261 "org.freedesktop.systemd1.Manager",
262 "SecurityStartTimestampMonotonic",
263 &times.security_start_time) < 0 ||
264 bus_get_uint64_property(bus,
265 "/org/freedesktop/systemd1",
266 "org.freedesktop.systemd1.Manager",
267 "SecurityFinishTimestampMonotonic",
268 &times.security_finish_time) < 0 ||
518d10e9
UTL
269 bus_get_uint64_property(bus,
270 "/org/freedesktop/systemd1",
271 "org.freedesktop.systemd1.Manager",
272 "GeneratorsStartTimestampMonotonic",
273 &times.generators_start_time) < 0 ||
274 bus_get_uint64_property(bus,
275 "/org/freedesktop/systemd1",
276 "org.freedesktop.systemd1.Manager",
277 "GeneratorsFinishTimestampMonotonic",
d9acfb71
TA
278 &times.generators_finish_time) < 0 ||
279 bus_get_uint64_property(bus,
280 "/org/freedesktop/systemd1",
281 "org.freedesktop.systemd1.Manager",
282 "UnitsLoadStartTimestampMonotonic",
283 &times.unitsload_start_time) < 0 ||
284 bus_get_uint64_property(bus,
285 "/org/freedesktop/systemd1",
286 "org.freedesktop.systemd1.Manager",
287 "UnitsLoadFinishTimestampMonotonic",
288 &times.unitsload_finish_time) < 0)
c170f3a4 289 return -EIO;
2265fbf7 290
c170f3a4 291 if (times.finish_time <= 0) {
1e9f0cca
ZJS
292 log_error("Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
293 "Please try again later.\n"
294 "Hint: Use 'systemctl%s list-jobs' to see active jobs",
295 times.finish_time,
296 arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
988b9df2 297 return -EINPROGRESS;
2265fbf7
SP
298 }
299
28b35ef2 300 if (arg_scope == UNIT_FILE_SYSTEM) {
baa4880b 301 if (times.initrd_time > 0)
28b35ef2
ZJS
302 times.kernel_done_time = times.initrd_time;
303 else
304 times.kernel_done_time = times.userspace_time;
305 } else {
06bef033
IS
306 /*
307 * User-instance-specific timestamps processing
308 * (see comment to reverse_offset in struct boot_times).
309 */
310 times.reverse_offset = times.userspace_time;
311
312 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
313 subtract_timestamp(&times.finish_time, times.reverse_offset);
314
315 subtract_timestamp(&times.security_start_time, times.reverse_offset);
316 subtract_timestamp(&times.security_finish_time, times.reverse_offset);
317
318 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
319 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
320
321 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
322 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
06bef033 323 }
2265fbf7
SP
324
325 cached = true;
c170f3a4
LP
326
327finish:
328 *bt = &times;
329 return 0;
2265fbf7
SP
330}
331
7e690cef 332static void free_host_info(struct host_info *hi) {
b1b533a0
LP
333
334 if (!hi)
19f462f2 335 return;
b1b533a0 336
7e690cef
DH
337 free(hi->hostname);
338 free(hi->kernel_name);
339 free(hi->kernel_release);
340 free(hi->kernel_version);
341 free(hi->os_pretty_name);
342 free(hi->virtualization);
343 free(hi->architecture);
344 free(hi);
345}
b1b533a0 346
19f462f2 347DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
7e690cef 348
29b8b5ce 349static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
4afd3348
LP
350 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
351 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
29b8b5ce 352 int r, c = 0;
06bef033 353 struct boot_times *boot_times = NULL;
29b8b5ce
IS
354 struct unit_times *unit_times = NULL;
355 size_t size = 0;
356 UnitInfo u;
357
06bef033
IS
358 r = acquire_boot_times(bus, &boot_times);
359 if (r < 0)
360 goto fail;
361
29b8b5ce
IS
362 r = sd_bus_call_method(
363 bus,
364 "org.freedesktop.systemd1",
365 "/org/freedesktop/systemd1",
366 "org.freedesktop.systemd1.Manager",
367 "ListUnits",
368 &error, &reply,
369 NULL);
370 if (r < 0) {
371 log_error("Failed to list units: %s", bus_error_message(&error, -r));
372 goto fail;
373 }
374
375 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
376 if (r < 0) {
377 bus_log_parse_error(r);
378 goto fail;
379 }
380
381 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
382 struct unit_times *t;
383
384 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
385 r = log_oom();
386 goto fail;
387 }
388
389 t = unit_times+c;
390 t->name = NULL;
391
392 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
393
394 if (bus_get_uint64_property(bus, u.unit_path,
395 "org.freedesktop.systemd1.Unit",
396 "InactiveExitTimestampMonotonic",
397 &t->activating) < 0 ||
398 bus_get_uint64_property(bus, u.unit_path,
399 "org.freedesktop.systemd1.Unit",
400 "ActiveEnterTimestampMonotonic",
401 &t->activated) < 0 ||
402 bus_get_uint64_property(bus, u.unit_path,
403 "org.freedesktop.systemd1.Unit",
404 "ActiveExitTimestampMonotonic",
405 &t->deactivating) < 0 ||
406 bus_get_uint64_property(bus, u.unit_path,
407 "org.freedesktop.systemd1.Unit",
408 "InactiveEnterTimestampMonotonic",
409 &t->deactivated) < 0) {
410 r = -EIO;
411 goto fail;
412 }
413
06bef033
IS
414 subtract_timestamp(&t->activating, boot_times->reverse_offset);
415 subtract_timestamp(&t->activated, boot_times->reverse_offset);
416 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
417 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
418
29b8b5ce
IS
419 if (t->activated >= t->activating)
420 t->time = t->activated - t->activating;
421 else if (t->deactivated >= t->activating)
422 t->time = t->deactivated - t->activating;
423 else
424 t->time = 0;
425
426 if (t->activating == 0)
427 continue;
428
429 t->name = strdup(u.id);
234519ae 430 if (!t->name) {
29b8b5ce
IS
431 r = log_oom();
432 goto fail;
433 }
434 c++;
435 }
436 if (r < 0) {
437 bus_log_parse_error(r);
438 goto fail;
439 }
440
441 *out = unit_times;
442 return c;
443
444fail:
230cc99a 445 free_unit_times(unit_times, (unsigned) c);
29b8b5ce
IS
446 return r;
447}
448
7e690cef 449static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
7e690cef 450 static const struct bus_properties_map hostname_map[] = {
b1b533a0
LP
451 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
452 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
453 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
454 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
7e690cef
DH
455 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
456 {}
457 };
458
459 static const struct bus_properties_map manager_map[] = {
b1b533a0
LP
460 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
461 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
7e690cef
DH
462 {}
463 };
464
4afd3348 465 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b1b533a0
LP
466 _cleanup_(free_host_infop) struct host_info *host;
467 int r;
468
7e690cef
DH
469 host = new0(struct host_info, 1);
470 if (!host)
471 return log_oom();
472
473 r = bus_map_all_properties(bus,
474 "org.freedesktop.hostname1",
475 "/org/freedesktop/hostname1",
476 hostname_map,
a7e4861c 477 BUS_MAP_STRDUP,
f9e0eefc 478 &error,
f37f8a61 479 NULL,
7e690cef
DH
480 host);
481 if (r < 0)
b1b533a0 482 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
7e690cef
DH
483
484 r = bus_map_all_properties(bus,
485 "org.freedesktop.systemd1",
486 "/org/freedesktop/systemd1",
487 manager_map,
a7e4861c 488 BUS_MAP_STRDUP,
f9e0eefc 489 &error,
f37f8a61 490 NULL,
7e690cef 491 host);
febda62a 492 if (r < 0)
b1b533a0 493 return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
7e690cef 494
1cc6c93a 495 *hi = TAKE_PTR(host);
b1b533a0 496
7e690cef 497 return 0;
7e690cef
DH
498}
499
048ecf5b 500static int pretty_boot_time(sd_bus *bus, char **_buf) {
c170f3a4 501 char ts[FORMAT_TIMESPAN_MAX];
2265fbf7 502 struct boot_times *t;
2265fbf7 503 static char buf[4096];
c170f3a4
LP
504 size_t size;
505 char *ptr;
506 int r;
bd07d3d0
JR
507 usec_t activated_time = USEC_INFINITY;
508 _cleanup_free_ char* path = NULL, *unit_id = NULL;
509 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4
LP
510
511 r = acquire_boot_times(bus, &t);
512 if (r < 0)
513 return r;
2265fbf7 514
bd07d3d0
JR
515 path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
516 if (!path)
517 return log_oom();
518
519 r = sd_bus_get_property_string(
520 bus,
521 "org.freedesktop.systemd1",
522 path,
523 "org.freedesktop.systemd1.Unit",
524 "Id",
525 &error,
526 &unit_id);
527 if (r < 0) {
528 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
529 unit_id = NULL;
530 }
531
532 r = bus_get_uint64_property(bus, path,
533 "org.freedesktop.systemd1.Unit",
534 "ActiveEnterTimestampMonotonic",
535 &activated_time);
536 if (r < 0) {
da933f7d 537 log_info_errno(r, "Could not get time to reach default.target. Continuing...");
bd07d3d0
JR
538 activated_time = USEC_INFINITY;
539 }
540
c170f3a4
LP
541 ptr = buf;
542 size = sizeof(buf);
2265fbf7
SP
543
544 size = strpcpyf(&ptr, size, "Startup finished in ");
baa4880b 545 if (t->firmware_time > 0)
2fa4092c 546 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
baa4880b 547 if (t->loader_time > 0)
2fa4092c 548 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
baa4880b 549 if (t->kernel_time > 0)
2fa4092c 550 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
2265fbf7 551 if (t->initrd_time > 0)
2fa4092c 552 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
2265fbf7 553
2fa4092c 554 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
d21b0c82
JR
555 if (t->kernel_time > 0)
556 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
2265fbf7 557
baa4880b 558 if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY)
bd07d3d0 559 size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
da933f7d
JR
560 else if (unit_id && activated_time == 0)
561 size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
562 else if (unit_id && activated_time == USEC_INFINITY)
563 size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id);
564 else if (!unit_id)
565 size = strpcpyf(&ptr, size, "\ncould not find default.target");
566
c170f3a4
LP
567 ptr = strdup(buf);
568 if (!ptr)
569 return log_oom();
570
571 *_buf = ptr;
572 return 0;
2265fbf7
SP
573}
574
c170f3a4
LP
575static void svg_graph_box(double height, double begin, double end) {
576 long long i;
577
2265fbf7
SP
578 /* outside box, fill */
579 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
2f6eb835 580 SCALE_X * (end - begin), SCALE_Y * height);
2265fbf7 581
c170f3a4 582 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
2265fbf7 583 /* lines for each second */
c170f3a4 584 if (i % 5000000 == 0)
2265fbf7
SP
585 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
586 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 587 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
c170f3a4 588 else if (i % 1000000 == 0)
2265fbf7
SP
589 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
590 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 591 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
2265fbf7
SP
592 else
593 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
2f6eb835 594 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
2265fbf7
SP
595 }
596}
597
a6bcef29 598static int analyze_plot(int argc, char *argv[], void *userdata) {
b1b533a0 599 _cleanup_(free_host_infop) struct host_info *host = NULL;
a6bcef29 600 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2265fbf7
SP
601 struct unit_times *times;
602 struct boot_times *boot;
a6bcef29 603 int n, m = 1, y = 0, r;
5c69b31c 604 bool use_full_bus = true;
2265fbf7 605 double width;
7e690cef 606 _cleanup_free_ char *pretty_times = NULL;
c170f3a4 607 struct unit_times *u;
2265fbf7 608
5c69b31c 609 r = acquire_full_bus(&use_full_bus, &bus);
a6bcef29
LP
610 if (r < 0)
611 return log_error_errno(r, "Failed to create bus connection: %m");
612
c170f3a4
LP
613 n = acquire_boot_times(bus, &boot);
614 if (n < 0)
615 return n;
2265fbf7 616
c170f3a4
LP
617 n = pretty_boot_time(bus, &pretty_times);
618 if (n < 0)
619 return n;
2265fbf7 620
5c69b31c
GJ
621 if (use_full_bus) {
622 n = acquire_host_info(bus, &host);
623 if (n < 0)
624 return n;
625 }
2265fbf7
SP
626
627 n = acquire_time_data(bus, &times);
c170f3a4 628 if (n <= 0)
19f462f2 629 return n;
2265fbf7
SP
630
631 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
632
2f6eb835 633 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
634 if (width < 800.0)
635 width = 800.0;
636
637 if (boot->firmware_time > boot->loader_time)
638 m++;
baa4880b 639 if (boot->loader_time > 0) {
2265fbf7
SP
640 m++;
641 if (width < 1000.0)
642 width = 1000.0;
643 }
baa4880b 644 if (boot->initrd_time > 0)
2265fbf7 645 m++;
baa4880b 646 if (boot->kernel_time > 0)
2265fbf7
SP
647 m++;
648
c170f3a4 649 for (u = times; u < times + n; u++) {
95168f7d 650 double text_start, text_width;
c170f3a4 651
cc27380c
TA
652 if (u->activating < boot->userspace_time ||
653 u->activating > boot->finish_time) {
a1e58e8e 654 u->name = mfree(u->name);
2265fbf7
SP
655 continue;
656 }
95168f7d
TA
657
658 /* If the text cannot fit on the left side then
659 * increase the svg width so it fits on the right.
660 * TODO: calculate the text width more accurately */
661 text_width = 8.0 * strlen(u->name);
cc27380c 662 text_start = (boot->firmware_time + u->activating) * SCALE_X;
95168f7d
TA
663 if (text_width > text_start && text_width + text_start > width)
664 width = text_width + text_start;
2265fbf7 665
cc27380c
TA
666 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
667 && u->activated == 0 && u->deactivating == 0)
668 u->activated = u->deactivating = u->deactivated;
669 if (u->activated < u->activating || u->activated > boot->finish_time)
670 u->activated = boot->finish_time;
671 if (u->deactivating < u->activated || u->activated > boot->finish_time)
672 u->deactivating = boot->finish_time;
673 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
674 u->deactivated = boot->finish_time;
2265fbf7
SP
675 m++;
676 }
677
678 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
679 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
680 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
681
682 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
683 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
518d10e9 684 80.0 + width, 150.0 + (m * SCALE_Y) +
d9acfb71 685 5 * SCALE_Y /* legend */);
2265fbf7
SP
686
687 /* write some basic info as a comment, including some help */
688 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
689 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
690 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
691 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
692 "<!-- point your browser to this file. -->\n\n"
948aaa7c 693 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION);
2265fbf7
SP
694
695 /* style sheet */
696 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
697 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
418e3750 698 " rect.background { fill: rgb(255,255,255); }\n"
2265fbf7
SP
699 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
700 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
701 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
702 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
703 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
704 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
705 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
706 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
c2e0d600 707 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
518d10e9 708 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
d9acfb71 709 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
2265fbf7
SP
710 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
711 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
712 "// line.sec1 { }\n"
713 " line.sec5 { stroke-width: 2; }\n"
714 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
2b7d6965
TA
715 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
716 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
717 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
718 " text.sec { font-size: 10px; }\n"
2265fbf7
SP
719 " ]]>\n </style>\n</defs>\n\n");
720
418e3750 721 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
2265fbf7 722 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
5c69b31c
GJ
723 if (use_full_bus)
724 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
725 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
726 strempty(host->hostname),
727 strempty(host->kernel_name),
728 strempty(host->kernel_release),
729 strempty(host->kernel_version),
730 strempty(host->architecture),
731 strempty(host->virtualization));
2265fbf7 732
2f6eb835 733 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
b5cfa740 734 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
2265fbf7 735
baa4880b 736 if (boot->firmware_time > 0) {
c170f3a4
LP
737 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
738 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
739 y++;
740 }
baa4880b 741 if (boot->loader_time > 0) {
c170f3a4
LP
742 svg_bar("loader", -(double) boot->loader_time, 0, y);
743 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
744 y++;
745 }
baa4880b 746 if (boot->kernel_time > 0) {
2265fbf7 747 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 748 svg_text(true, 0, y, "kernel");
2265fbf7
SP
749 y++;
750 }
baa4880b 751 if (boot->initrd_time > 0) {
2265fbf7 752 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 753 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
754 y++;
755 }
518d10e9 756 svg_bar("active", boot->userspace_time, boot->finish_time, y);
c2e0d600 757 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
518d10e9 758 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
d9acfb71 759 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
95168f7d 760 svg_text(true, boot->userspace_time, y, "systemd");
2265fbf7
SP
761 y++;
762
c170f3a4
LP
763 for (u = times; u < times + n; u++) {
764 char ts[FORMAT_TIMESPAN_MAX];
fd4a5ec6 765 bool b;
c170f3a4 766
2265fbf7
SP
767 if (!u->name)
768 continue;
c170f3a4 769
cc27380c
TA
770 svg_bar("activating", u->activating, u->activated, y);
771 svg_bar("active", u->activated, u->deactivating, y);
772 svg_bar("deactivating", u->deactivating, u->deactivated, y);
c170f3a4 773
95168f7d 774 /* place the text on the left if we have passed the half of the svg width */
cc27380c 775 b = u->activating * SCALE_X < width / 2;
fd4a5ec6 776 if (u->time)
cc27380c 777 svg_text(b, u->activating, y, "%s (%s)",
2fa4092c 778 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
c170f3a4 779 else
cc27380c 780 svg_text(b, u->activating, y, "%s", u->name);
2265fbf7
SP
781 y++;
782 }
518d10e9 783
b5cfa740
TA
784 svg("</g>\n");
785
518d10e9 786 /* Legend */
b5cfa740 787 svg("<g transform=\"translate(20,100)\">\n");
518d10e9
UTL
788 y++;
789 svg_bar("activating", 0, 300000, y);
95168f7d 790 svg_text(true, 400000, y, "Activating");
518d10e9
UTL
791 y++;
792 svg_bar("active", 0, 300000, y);
95168f7d 793 svg_text(true, 400000, y, "Active");
518d10e9
UTL
794 y++;
795 svg_bar("deactivating", 0, 300000, y);
95168f7d 796 svg_text(true, 400000, y, "Deactivating");
518d10e9 797 y++;
c2e0d600
TA
798 svg_bar("security", 0, 300000, y);
799 svg_text(true, 400000, y, "Setting up security module");
800 y++;
518d10e9 801 svg_bar("generators", 0, 300000, y);
95168f7d 802 svg_text(true, 400000, y, "Generators");
518d10e9 803 y++;
d9acfb71 804 svg_bar("unitsload", 0, 300000, y);
95168f7d 805 svg_text(true, 400000, y, "Loading unit files");
d9acfb71 806 y++;
518d10e9 807
2265fbf7
SP
808 svg("</g>\n\n");
809
988b9df2 810 svg("</svg>\n");
c170f3a4
LP
811
812 free_unit_times(times, (unsigned) n);
813
7e690cef 814 n = 0;
7e690cef 815 return n;
2265fbf7
SP
816}
817
bb150966
HH
818static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
819 bool last, struct unit_times *times, struct boot_times *boot) {
820 unsigned int i;
821 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
822
823 for (i = level; i != 0; i--)
323b7dc9 824 printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
bb150966 825
323b7dc9 826 printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
bb150966
HH
827
828 if (times) {
baa4880b 829 if (times->time > 0)
54f8c958 830 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
cc27380c 831 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
54f8c958 832 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
cc27380c
TA
833 else if (times->activated > boot->userspace_time)
834 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
835 else
836 printf("%s", name);
988b9df2
LP
837 } else
838 printf("%s", name);
bb150966
HH
839 printf("\n");
840
841 return 0;
842}
843
048ecf5b 844static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
988b9df2 845 _cleanup_free_ char *path = NULL;
bb150966
HH
846
847 assert(bus);
848 assert(name);
849 assert(deps);
850
851 path = unit_dbus_path_from_name(name);
234519ae 852 if (!path)
988b9df2 853 return -ENOMEM;
bb150966 854
988b9df2 855 return bus_get_unit_property_strv(bus, path, "After", deps);
bb150966
HH
856}
857
858static Hashmap *unit_times_hashmap;
859
860static int list_dependencies_compare(const void *_a, const void *_b) {
861 const char **a = (const char**) _a, **b = (const char**) _b;
862 usec_t usa = 0, usb = 0;
863 struct unit_times *times;
864
865 times = hashmap_get(unit_times_hashmap, *a);
866 if (times)
cc27380c 867 usa = times->activated;
bb150966
HH
868 times = hashmap_get(unit_times_hashmap, *b);
869 if (times)
cc27380c 870 usb = times->activated;
bb150966
HH
871
872 return usb - usa;
873}
874
230cc99a
ZJS
875static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
876 return times &&
877 times->activated > 0 && times->activated <= boot->finish_time;
878}
879
048ecf5b 880static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
bb150966
HH
881 unsigned int branches) {
882 _cleanup_strv_free_ char **deps = NULL;
883 char **c;
884 int r = 0;
885 usec_t service_longest = 0;
886 int to_print = 0;
887 struct unit_times *times;
888 struct boot_times *boot;
889
988b9df2 890 if (strv_extend(units, name))
bb150966
HH
891 return log_oom();
892
893 r = list_dependencies_get_dependencies(bus, name, &deps);
894 if (r < 0)
895 return r;
896
7ff7394d 897 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
bb150966
HH
898
899 r = acquire_boot_times(bus, &boot);
900 if (r < 0)
901 return r;
902
903 STRV_FOREACH(c, deps) {
904 times = hashmap_get(unit_times_hashmap, *c);
230cc99a
ZJS
905 if (times_in_range(times, boot) &&
906 (times->activated >= service_longest
907 || service_longest == 0)) {
cc27380c 908 service_longest = times->activated;
bb150966
HH
909 break;
910 }
911 }
912
234519ae 913 if (service_longest == 0)
bb150966
HH
914 return r;
915
916 STRV_FOREACH(c, deps) {
917 times = hashmap_get(unit_times_hashmap, *c);
230cc99a
ZJS
918 if (times_in_range(times, boot) &&
919 service_longest - times->activated <= arg_fuzz)
bb150966 920 to_print++;
bb150966
HH
921 }
922
f168c273 923 if (!to_print)
bb150966
HH
924 return r;
925
926 STRV_FOREACH(c, deps) {
927 times = hashmap_get(unit_times_hashmap, *c);
230cc99a
ZJS
928 if (!times_in_range(times, boot) ||
929 service_longest - times->activated > arg_fuzz)
bb150966
HH
930 continue;
931
932 to_print--;
933
934 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
935 if (r < 0)
936 return r;
937
938 if (strv_contains(*units, *c)) {
939 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
940 true, NULL, boot);
872c8faa
ZJS
941 if (r < 0)
942 return r;
bb150966
HH
943 continue;
944 }
945
946 r = list_dependencies_one(bus, *c, level + 1, units,
947 (branches << 1) | (to_print ? 1 : 0));
872c8faa 948 if (r < 0)
bb150966
HH
949 return r;
950
baa4880b 951 if (to_print == 0)
bb150966 952 break;
bb150966
HH
953 }
954 return 0;
955}
956
048ecf5b 957static int list_dependencies(sd_bus *bus, const char *name) {
bb150966
HH
958 _cleanup_strv_free_ char **units = NULL;
959 char ts[FORMAT_TIMESPAN_MAX];
960 struct unit_times *times;
961 int r;
0ee9613d
TA
962 const char *id;
963 _cleanup_free_ char *path = NULL;
4afd3348
LP
964 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
965 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bb150966
HH
966 struct boot_times *boot;
967
968 assert(bus);
969
805bf39c 970 path = unit_dbus_path_from_name(name);
234519ae 971 if (!path)
988b9df2 972 return -ENOMEM;
bb150966 973
a936124f
TA
974 r = sd_bus_get_property(
975 bus,
976 "org.freedesktop.systemd1",
977 path,
978 "org.freedesktop.systemd1.Unit",
979 "Id",
980 &error,
981 &reply,
982 "s");
048ecf5b 983 if (r < 0) {
5b30bef8 984 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
bb150966 985 return r;
bb150966
HH
986 }
987
048ecf5b 988 r = sd_bus_message_read(reply, "s", &id);
5b30bef8
LP
989 if (r < 0)
990 return bus_log_parse_error(r);
bb150966 991
bb150966
HH
992 times = hashmap_get(unit_times_hashmap, id);
993
994 r = acquire_boot_times(bus, &boot);
995 if (r < 0)
996 return r;
997
998 if (times) {
999 if (times->time)
54f8c958
LP
1000 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
1001 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
cc27380c
TA
1002 else if (times->activated > boot->userspace_time)
1003 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
1004 else
1005 printf("%s\n", id);
1006 }
1007
805bf39c 1008 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
1009}
1010
a6bcef29
LP
1011static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
1012 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
bb150966 1013 struct unit_times *times;
bb150966
HH
1014 unsigned int i;
1015 Hashmap *h;
988b9df2 1016 int n, r;
bb150966 1017
5c69b31c 1018 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1019 if (r < 0)
1020 return log_error_errno(r, "Failed to create bus connection: %m");
1021
bb150966
HH
1022 n = acquire_time_data(bus, &times);
1023 if (n <= 0)
1024 return n;
1025
d5099efc 1026 h = hashmap_new(&string_hash_ops);
bb150966 1027 if (!h)
8efbce13 1028 return log_oom();
bb150966 1029
8efbce13 1030 for (i = 0; i < (unsigned) n; i++) {
bb150966
HH
1031 r = hashmap_put(h, times[i].name, &times[i]);
1032 if (r < 0)
8efbce13 1033 return log_error_errno(r, "Failed to add entry to hashmap: %m");
bb150966
HH
1034 }
1035 unit_times_hashmap = h;
1036
ee5324aa 1037 (void) pager_open(arg_no_pager, false);
9ea9d4cf 1038
bb150966
HH
1039 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1040 "The time the unit takes to start is printed after the \"+\" character.\n");
1041
a6bcef29 1042 if (argc > 1) {
805bf39c 1043 char **name;
a6bcef29 1044 STRV_FOREACH(name, strv_skip(argv, 1))
805bf39c 1045 list_dependencies(bus, *name);
9ea9d4cf 1046 } else
805bf39c 1047 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966 1048
a6bcef29 1049 h = hashmap_free(h);
bb150966
HH
1050 free_unit_times(times, (unsigned) n);
1051 return 0;
1052}
1053
a6bcef29
LP
1054static int analyze_blame(int argc, char *argv[], void *userdata) {
1055 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2265fbf7 1056 struct unit_times *times;
c170f3a4 1057 unsigned i;
a6bcef29
LP
1058 int n, r;
1059
5c69b31c 1060 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1061 if (r < 0)
1062 return log_error_errno(r, "Failed to create bus connection: %m");
c170f3a4
LP
1063
1064 n = acquire_time_data(bus, &times);
1065 if (n <= 0)
2265fbf7
SP
1066 return n;
1067
1068 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
1069
ee5324aa 1070 (void) pager_open(arg_no_pager, false);
9ea9d4cf 1071
c170f3a4
LP
1072 for (i = 0; i < (unsigned) n; i++) {
1073 char ts[FORMAT_TIMESPAN_MAX];
1074
1075 if (times[i].time > 0)
2fa4092c 1076 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
2265fbf7 1077 }
c170f3a4
LP
1078
1079 free_unit_times(times, (unsigned) n);
2265fbf7
SP
1080 return 0;
1081}
1082
a6bcef29
LP
1083static int analyze_time(int argc, char *argv[], void *userdata) {
1084 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
c170f3a4
LP
1085 _cleanup_free_ char *buf = NULL;
1086 int r;
1087
5c69b31c 1088 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1089 if (r < 0)
1090 return log_error_errno(r, "Failed to create bus connection: %m");
1091
c170f3a4
LP
1092 r = pretty_boot_time(bus, &buf);
1093 if (r < 0)
1094 return r;
1095
1096 puts(buf);
2265fbf7
SP
1097 return 0;
1098}
1099
4387795e 1100static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) {
048ecf5b 1101 _cleanup_strv_free_ char **units = NULL;
048ecf5b
TA
1102 char **unit;
1103 int r;
6ecb6cec 1104 bool match_patterns;
1700761b 1105
048ecf5b 1106 assert(u);
1700761b 1107 assert(prop);
048ecf5b
TA
1108 assert(color);
1109
2404701e 1110 match_patterns = strv_fnmatch(patterns, u->id, 0);
6ecb6cec 1111
4387795e 1112 if (!strv_isempty(from_patterns) &&
6ecb6cec 1113 !match_patterns &&
4387795e 1114 !strv_fnmatch(from_patterns, u->id, 0))
6ecb6cec
ZJS
1115 return 0;
1116
6e6ca4a5 1117 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
07d0eaa0 1118 if (r < 0)
988b9df2 1119 return r;
1700761b 1120
048ecf5b 1121 STRV_FOREACH(unit, units) {
6ecb6cec
ZJS
1122 bool match_patterns2;
1123
2404701e 1124 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
816f25e8 1125
4387795e 1126 if (!strv_isempty(to_patterns) &&
6ecb6cec 1127 !match_patterns2 &&
4387795e 1128 !strv_fnmatch(to_patterns, *unit, 0))
bceccd5e 1129 continue;
e55933db 1130
6ecb6cec 1131 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
bceccd5e 1132 continue;
048ecf5b
TA
1133
1134 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1700761b
SP
1135 }
1136
1137 return 0;
1138}
1139
4387795e 1140static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1700761b 1141 int r;
1700761b
SP
1142
1143 assert(bus);
1144 assert(u);
1145
8901b405 1146 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
4387795e 1147 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
048ecf5b
TA
1148 if (r < 0)
1149 return r;
1700761b
SP
1150 }
1151
8901b405 1152 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
4387795e 1153 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
8901b405
MS
1154 if (r < 0)
1155 return r;
1156 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
048ecf5b
TA
1157 if (r < 0)
1158 return r;
4387795e 1159 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
048ecf5b
TA
1160 if (r < 0)
1161 return r;
4387795e 1162 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
048ecf5b
TA
1163 if (r < 0)
1164 return r;
1700761b
SP
1165 }
1166
1167 return 0;
1168}
1169
83efb7c2
EV
1170static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1171 _cleanup_strv_free_ char **expanded_patterns = NULL;
1172 char **pattern;
1173 int r;
1174
1175 STRV_FOREACH(pattern, patterns) {
4afd3348 1176 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
83efb7c2
EV
1177 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1178
1179 if (strv_extend(&expanded_patterns, *pattern) < 0)
1180 return log_oom();
1181
1182 if (string_is_glob(*pattern))
1183 continue;
1184
1185 unit = unit_dbus_path_from_name(*pattern);
1186 if (!unit)
1187 return log_oom();
1188
1189 r = sd_bus_get_property_string(
1190 bus,
1191 "org.freedesktop.systemd1",
1192 unit,
1193 "org.freedesktop.systemd1.Unit",
1194 "Id",
1195 &error,
1196 &unit_id);
1197 if (r < 0)
1198 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1199
1200 if (!streq(*pattern, unit_id)) {
1201 if (strv_extend(&expanded_patterns, unit_id) < 0)
1202 return log_oom();
1203 }
1204 }
1205
1206 *ret = expanded_patterns;
1207 expanded_patterns = NULL; /* do not free */
1208
1209 return 0;
1210}
1211
a6bcef29 1212static int dot(int argc, char *argv[], void *userdata) {
4afd3348
LP
1213 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1214 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29 1215 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
83efb7c2 1216 _cleanup_strv_free_ char **expanded_patterns = NULL;
4387795e
EV
1217 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1218 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1700761b 1219 int r;
f459b602 1220 UnitInfo u;
048ecf5b 1221
5c69b31c 1222 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1223 if (r < 0)
1224 return log_error_errno(r, "Failed to create bus connection: %m");
1225
1226 r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
83efb7c2
EV
1227 if (r < 0)
1228 return r;
1229
4387795e
EV
1230 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1231 if (r < 0)
1232 return r;
1233
1234 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1235 if (r < 0)
1236 return r;
1237
a936124f
TA
1238 r = sd_bus_call_method(
1239 bus,
1240 "org.freedesktop.systemd1",
1241 "/org/freedesktop/systemd1",
1242 "org.freedesktop.systemd1.Manager",
1243 "ListUnits",
1244 &error,
1245 &reply,
1246 "");
048ecf5b 1247 if (r < 0) {
988b9df2
LP
1248 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1249 return r;
048ecf5b 1250 }
1700761b 1251
048ecf5b 1252 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1700761b 1253 if (r < 0)
988b9df2 1254 return bus_log_parse_error(r);
1700761b
SP
1255
1256 printf("digraph systemd {\n");
1257
048ecf5b 1258 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
f459b602 1259
4387795e 1260 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1700761b
SP
1261 if (r < 0)
1262 return r;
1263 }
f459b602
MAP
1264 if (r < 0)
1265 return bus_log_parse_error(r);
1700761b
SP
1266
1267 printf("}\n");
1268
1269 log_info(" Color legend: black = Requires\n"
1270 " dark blue = Requisite\n"
1271 " dark grey = Wants\n"
1272 " red = Conflicts\n"
1273 " green = After\n");
1274
1275 if (on_tty())
1276 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1277 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1278
1279 return 0;
1280}
c8a8806e 1281
a6bcef29 1282static int dump(int argc, char *argv[], void *userdata) {
4afd3348 1283 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29
LP
1284 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1285 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
048ecf5b 1286 const char *text = NULL;
9ea9d4cf 1287 int r;
9ea9d4cf 1288
5c69b31c 1289 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1290 if (r < 0)
1291 return log_error_errno(r, "Failed to create bus connection: %m");
a65615ca 1292
ee5324aa 1293 (void) pager_open(arg_no_pager, false);
9ea9d4cf 1294
a936124f
TA
1295 r = sd_bus_call_method(
1296 bus,
8486c923
LP
1297 "org.freedesktop.systemd1",
1298 "/org/freedesktop/systemd1",
1299 "org.freedesktop.systemd1.Manager",
1300 "Dump",
1301 &error,
1302 &reply,
1303 NULL);
2ca2a91c 1304 if (r < 0)
94e91c83 1305 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
9ea9d4cf 1306
048ecf5b 1307 r = sd_bus_message_read(reply, "s", &text);
5b30bef8
LP
1308 if (r < 0)
1309 return bus_log_parse_error(r);
9ea9d4cf
LP
1310
1311 fputs(text, stdout);
1312 return 0;
1313}
1314
a6bcef29 1315static int set_log_level(int argc, char *argv[], void *userdata) {
4afd3348 1316 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29 1317 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
048ecf5b 1318 int r;
a65615ca 1319
a6bcef29
LP
1320 assert(argc == 2);
1321 assert(argv);
a65615ca 1322
5c69b31c 1323 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1324 if (r < 0)
1325 return log_error_errno(r, "Failed to create bus connection: %m");
a65615ca 1326
a936124f
TA
1327 r = sd_bus_set_property(
1328 bus,
1329 "org.freedesktop.systemd1",
1330 "/org/freedesktop/systemd1",
1331 "org.freedesktop.systemd1.Manager",
1332 "LogLevel",
1333 &error,
1334 "s",
a6bcef29 1335 argv[1]);
2ca2a91c
LP
1336 if (r < 0)
1337 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1338
1339 return 0;
1340}
1341
a6bcef29 1342static int get_log_level(int argc, char *argv[], void *userdata) {
ef5a8cb1 1343 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29 1344 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
ef5a8cb1 1345 _cleanup_free_ char *level = NULL;
a6bcef29 1346 int r;
ef5a8cb1 1347
5c69b31c 1348 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1349 if (r < 0)
1350 return log_error_errno(r, "Failed to create bus connection: %m");
ef5a8cb1
LW
1351
1352 r = sd_bus_get_property_string(
1353 bus,
1354 "org.freedesktop.systemd1",
1355 "/org/freedesktop/systemd1",
1356 "org.freedesktop.systemd1.Manager",
1357 "LogLevel",
1358 &error,
1359 &level);
1360 if (r < 0)
1361 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1362
1363 puts(level);
1364 return 0;
1365}
1366
90657286
YW
1367static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
1368 return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
1369}
1370
a6bcef29 1371static int set_log_target(int argc, char *argv[], void *userdata) {
4afd3348 1372 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29 1373 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
2ca2a91c
LP
1374 int r;
1375
a6bcef29
LP
1376 assert(argc == 2);
1377 assert(argv);
2ca2a91c 1378
5c69b31c 1379 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1380 if (r < 0)
1381 return log_error_errno(r, "Failed to create bus connection: %m");
a65615ca 1382
2ca2a91c
LP
1383 r = sd_bus_set_property(
1384 bus,
1385 "org.freedesktop.systemd1",
1386 "/org/freedesktop/systemd1",
1387 "org.freedesktop.systemd1.Manager",
1388 "LogTarget",
1389 &error,
1390 "s",
a6bcef29 1391 argv[1]);
2ca2a91c
LP
1392 if (r < 0)
1393 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1394
a65615ca
LP
1395 return 0;
1396}
1397
a6bcef29 1398static int get_log_target(int argc, char *argv[], void *userdata) {
ef5a8cb1 1399 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
a6bcef29 1400 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
ef5a8cb1 1401 _cleanup_free_ char *target = NULL;
a6bcef29 1402 int r;
ef5a8cb1 1403
5c69b31c 1404 r = acquire_systemd_bus(&bus);
a6bcef29
LP
1405 if (r < 0)
1406 return log_error_errno(r, "Failed to create bus connection: %m");
ef5a8cb1
LW
1407
1408 r = sd_bus_get_property_string(
1409 bus,
1410 "org.freedesktop.systemd1",
1411 "/org/freedesktop/systemd1",
1412 "org.freedesktop.systemd1.Manager",
1413 "LogTarget",
1414 &error,
1415 &target);
1416 if (r < 0)
1417 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1418
1419 puts(target);
1420 return 0;
1421}
1422
90657286
YW
1423static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
1424 return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
1425}
1426
31a5924e 1427static int dump_unit_paths(int argc, char *argv[], void *userdata) {
8e766630 1428 _cleanup_(lookup_paths_free) LookupPaths paths = {};
31a5924e
ZJS
1429 int r;
1430 char **p;
1431
1432 r = lookup_paths_init(&paths, arg_scope, 0, NULL);
1433 if (r < 0)
1434 return log_error_errno(r, "lookup_paths_init() failed: %m");
1435
1436 STRV_FOREACH(p, paths.search_path)
1437 puts(*p);
1438
1439 return 0;
1440}
1441
349cc4a5 1442#if HAVE_SECCOMP
869feb33
ZJS
1443static void dump_syscall_filter(const SyscallFilterSet *set) {
1444 const char *syscall;
1445
1446 printf("%s\n", set->name);
d5efc18b 1447 printf(" # %s\n", set->help);
869feb33
ZJS
1448 NULSTR_FOREACH(syscall, set->value)
1449 printf(" %s\n", syscall);
1450}
1451
a6bcef29 1452static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
869feb33
ZJS
1453 bool first = true;
1454
ee5324aa 1455 (void) pager_open(arg_no_pager, false);
869feb33 1456
a6bcef29 1457 if (strv_isempty(strv_skip(argv, 1))) {
869feb33
ZJS
1458 int i;
1459
1460 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1461 if (!first)
1462 puts("");
1463 dump_syscall_filter(syscall_filter_sets + i);
1464 first = false;
1465 }
1466 } else {
1467 char **name;
1468
a6bcef29 1469 STRV_FOREACH(name, strv_skip(argv, 1)) {
869feb33
ZJS
1470 const SyscallFilterSet *set;
1471
1472 if (!first)
1473 puts("");
1474
1475 set = syscall_filter_set_find(*name);
1476 if (!set) {
1477 /* make sure the error appears below normal output */
1478 fflush(stdout);
1479
1480 log_error("Filter set \"%s\" not found.", *name);
1481 return -ENOENT;
1482 }
1483
1484 dump_syscall_filter(set);
1485 first = false;
1486 }
1487 }
1488
1489 return 0;
1490}
1491
1720590b 1492#else
ad552e58 1493static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
1720590b
ZJS
1494 log_error("Not compiled with syscall filters, sorry.");
1495 return -EOPNOTSUPP;
1496}
1497#endif
1498
a6bcef29 1499static int test_calendar(int argc, char *argv[], void *userdata) {
6d86f4bd
LP
1500 int ret = 0, r;
1501 char **p;
1502 usec_t n;
1503
6d86f4bd
LP
1504 n = now(CLOCK_REALTIME);
1505
a6bcef29 1506 STRV_FOREACH(p, strv_skip(argv, 1)) {
6d86f4bd
LP
1507 _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1508 _cleanup_free_ char *t = NULL;
1509 usec_t next;
1510
1511 r = calendar_spec_from_string(*p, &spec);
1512 if (r < 0) {
1513 ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p);
1514 continue;
1515 }
1516
1517 r = calendar_spec_normalize(spec);
1518 if (r < 0) {
1519 ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p);
1520 continue;
1521 }
1522
1523 r = calendar_spec_to_string(spec, &t);
1524 if (r < 0) {
3a6a6889 1525 ret = log_error_errno(r, "Failed to format calendar specification '%s': %m", *p);
6d86f4bd
LP
1526 continue;
1527 }
1528
1529 if (!streq(t, *p))
1530 printf(" Original form: %s\n", *p);
1531
1532 printf("Normalized form: %s\n", t);
1533
1534 r = calendar_spec_next_usec(spec, n, &next);
1535 if (r == -ENOENT)
1536 printf(" Next elapse: never\n");
1537 else if (r < 0) {
1538 ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p);
1539 continue;
1540 } else {
1541 char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
1542
1543 printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next));
1544
1545 if (!in_utc_timezone())
1546 printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
1547
1548 printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
1549 }
1550
1551 if (*(p+1))
1552 putchar('\n');
1553 }
1554
1555 return ret;
1556}
1557
889d695d
JK
1558static int service_watchdogs(int argc, char *argv[], void *userdata) {
1559 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1560 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1561 int b, r;
1562
90657286 1563 assert(IN_SET(argc, 1, 2));
889d695d
JK
1564 assert(argv);
1565
5c69b31c 1566 r = acquire_systemd_bus(&bus);
90657286
YW
1567 if (r < 0)
1568 return log_error_errno(r, "Failed to create bus connection: %m");
1569
1570 /* get ServiceWatchdogs */
1571 if (argc == 1) {
1572 r = sd_bus_get_property_trivial(
1573 bus,
1574 "org.freedesktop.systemd1",
1575 "/org/freedesktop/systemd1",
1576 "org.freedesktop.systemd1.Manager",
1577 "ServiceWatchdogs",
1578 &error,
1579 'b',
1580 &b);
1581 if (r < 0)
1582 return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
1583
1584 printf("%s\n", yes_no(!!b));
1585
1586 return 0;
1587 }
1588
1589 /* set ServiceWatchdogs */
889d695d
JK
1590 b = parse_boolean(argv[1]);
1591 if (b < 0) {
1592 log_error("Failed to parse service-watchdogs argument.");
1593 return -EINVAL;
1594 }
1595
889d695d
JK
1596 r = sd_bus_set_property(
1597 bus,
1598 "org.freedesktop.systemd1",
1599 "/org/freedesktop/systemd1",
1600 "org.freedesktop.systemd1.Manager",
1601 "ServiceWatchdogs",
1602 &error,
1603 "b",
1604 b);
1605 if (r < 0)
90657286 1606 return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
889d695d
JK
1607
1608 return 0;
1609}
1610
a6bcef29 1611static int do_verify(int argc, char *argv[], void *userdata) {
28b35ef2 1612 return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
a6bcef29
LP
1613}
1614
1615static int help(int argc, char *argv[], void *userdata) {
9ea9d4cf 1616
ee5324aa 1617 (void) pager_open(arg_no_pager, false);
9ea9d4cf 1618
2265fbf7 1619 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1d3bc017 1620 "Profile systemd, show unit dependencies, check unit files.\n\n"
869feb33
ZJS
1621 " -h --help Show this help\n"
1622 " --version Show package version\n"
1623 " --no-pager Do not pipe output into a pager\n"
1624 " --system Operate on system systemd instance\n"
1625 " --user Operate on user systemd instance\n"
28b35ef2 1626 " --global Operate on global user configuration\n"
869feb33
ZJS
1627 " -H --host=[USER@]HOST Operate on remote host\n"
1628 " -M --machine=CONTAINER Operate on local container\n"
1629 " --order Show only order in the graph\n"
1630 " --require Show only requirement in the graph\n"
1631 " --from-pattern=GLOB Show only origins in the graph\n"
1632 " --to-pattern=GLOB Show only destinations in the graph\n"
1633 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1634 " earlier than the latest in the branch\n"
1635 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
641c0fd1 1636 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
2265fbf7 1637 "Commands:\n"
869feb33
ZJS
1638 " time Print time spent in the kernel\n"
1639 " blame Print list of running units ordered by time to init\n"
bc6695ec 1640 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
869feb33 1641 " plot Output SVG graphic showing service initialization\n"
bc6695ec 1642 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
90657286
YW
1643 " log-level [LEVEL] Get/set logging threshold for manager\n"
1644 " log-target [TARGET] Get/set logging target for manager\n"
869feb33 1645 " dump Output state serialization of service manager\n"
31a5924e 1646 " unit-paths List load directories for units\n"
869feb33
ZJS
1647 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1648 " verify FILE... Check unit files for correctness\n"
6d86f4bd 1649 " calendar SPEC... Validate repetitive calendar time events\n"
90657286 1650 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1d3bc017 1651 , program_invocation_short_name);
96de7c04
ZJS
1652
1653 /* When updating this list, including descriptions, apply
1d3bc017
ZJS
1654 * changes to shell-completion/bash/systemd-analyze and
1655 * shell-completion/zsh/_systemd-analyze too. */
a6bcef29
LP
1656
1657 return 0;
2265fbf7
SP
1658}
1659
9ea9d4cf 1660static int parse_argv(int argc, char *argv[]) {
2265fbf7
SP
1661 enum {
1662 ARG_VERSION = 0x100,
1700761b
SP
1663 ARG_ORDER,
1664 ARG_REQUIRE,
e55933db 1665 ARG_SYSTEM,
28b35ef2
ZJS
1666 ARG_USER,
1667 ARG_GLOBAL,
e55933db 1668 ARG_DOT_FROM_PATTERN,
bb150966 1669 ARG_DOT_TO_PATTERN,
9ea9d4cf 1670 ARG_FUZZ,
1d3bc017 1671 ARG_NO_PAGER,
dad29dff 1672 ARG_MAN,
641c0fd1 1673 ARG_GENERATORS,
2265fbf7
SP
1674 };
1675
1676 static const struct option options[] = {
9ea9d4cf
LP
1677 { "help", no_argument, NULL, 'h' },
1678 { "version", no_argument, NULL, ARG_VERSION },
1679 { "order", no_argument, NULL, ARG_ORDER },
1680 { "require", no_argument, NULL, ARG_REQUIRE },
9ea9d4cf 1681 { "system", no_argument, NULL, ARG_SYSTEM },
28b35ef2
ZJS
1682 { "user", no_argument, NULL, ARG_USER },
1683 { "global", no_argument, NULL, ARG_GLOBAL },
9ea9d4cf
LP
1684 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1685 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1686 { "fuzz", required_argument, NULL, ARG_FUZZ },
1687 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
dad29dff 1688 { "man", optional_argument, NULL, ARG_MAN },
641c0fd1 1689 { "generators", optional_argument, NULL, ARG_GENERATORS },
8fe12d88
LP
1690 { "host", required_argument, NULL, 'H' },
1691 { "machine", required_argument, NULL, 'M' },
eb9da376 1692 {}
2265fbf7
SP
1693 };
1694
eb9da376
LP
1695 int r, c;
1696
2265fbf7
SP
1697 assert(argc >= 0);
1698 assert(argv);
1699
601185b4 1700 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
eb9da376 1701 switch (c) {
c170f3a4
LP
1702
1703 case 'h':
a6bcef29 1704 return help(0, NULL, NULL);
c170f3a4
LP
1705
1706 case ARG_VERSION:
3f6fd1ba 1707 return version();
c170f3a4 1708
28b35ef2
ZJS
1709 case ARG_SYSTEM:
1710 arg_scope = UNIT_FILE_SYSTEM;
1711 break;
1712
c170f3a4 1713 case ARG_USER:
28b35ef2 1714 arg_scope = UNIT_FILE_USER;
c170f3a4
LP
1715 break;
1716
28b35ef2
ZJS
1717 case ARG_GLOBAL:
1718 arg_scope = UNIT_FILE_GLOBAL;
c170f3a4
LP
1719 break;
1720
1721 case ARG_ORDER:
1722 arg_dot = DEP_ORDER;
1723 break;
1724
1725 case ARG_REQUIRE:
1726 arg_dot = DEP_REQUIRE;
1727 break;
1728
e55933db 1729 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
1730 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1731 return log_oom();
1732
e55933db
ŁS
1733 break;
1734
1735 case ARG_DOT_TO_PATTERN:
903a0b07
LP
1736 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1737 return log_oom();
1738
e55933db
ŁS
1739 break;
1740
bb150966
HH
1741 case ARG_FUZZ:
1742 r = parse_sec(optarg, &arg_fuzz);
1743 if (r < 0)
1744 return r;
1745 break;
1746
9ea9d4cf
LP
1747 case ARG_NO_PAGER:
1748 arg_no_pager = true;
1749 break;
1750
3cd26e7c
LP
1751 case 'H':
1752 arg_transport = BUS_TRANSPORT_REMOTE;
1753 arg_host = optarg;
1754 break;
1755
1756 case 'M':
de33fc62 1757 arg_transport = BUS_TRANSPORT_MACHINE;
3cd26e7c
LP
1758 arg_host = optarg;
1759 break;
1760
dad29dff
LP
1761 case ARG_MAN:
1762 if (optarg) {
1763 r = parse_boolean(optarg);
1764 if (r < 0) {
1765 log_error("Failed to parse --man= argument.");
1766 return -EINVAL;
1767 }
1768
1769 arg_man = !!r;
1770 } else
1771 arg_man = true;
1772
1d3bc017
ZJS
1773 break;
1774
641c0fd1
ZJS
1775 case ARG_GENERATORS:
1776 if (optarg) {
1777 r = parse_boolean(optarg);
1778 if (r < 0) {
1779 log_error("Failed to parse --generators= argument.");
1780 return -EINVAL;
1781 }
1782
1783 arg_generators = !!r;
1784 } else
1785 arg_generators = true;
1786
1787 break;
1788
c170f3a4
LP
1789 case '?':
1790 return -EINVAL;
1791
1792 default:
1d3bc017 1793 assert_not_reached("Unhandled option code.");
2265fbf7 1794 }
eb9da376 1795
31a5924e
ZJS
1796 if (arg_scope == UNIT_FILE_GLOBAL &&
1797 !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) {
1798 log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
1799 return -EINVAL;
1800 }
1801
1d3bc017 1802 return 1; /* work to do */
2265fbf7
SP
1803}
1804
1805int main(int argc, char *argv[]) {
a6bcef29
LP
1806
1807 static const Verb verbs[] = {
889d695d
JK
1808 { "help", VERB_ANY, VERB_ANY, 0, help },
1809 { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
1810 { "blame", VERB_ANY, 1, 0, analyze_blame },
1811 { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
1812 { "plot", VERB_ANY, 1, 0, analyze_plot },
1813 { "dot", VERB_ANY, VERB_ANY, 0, dot },
90657286
YW
1814 { "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
1815 { "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
1816 /* The following four verbs are deprecated aliases */
889d695d
JK
1817 { "set-log-level", 2, 2, 0, set_log_level },
1818 { "get-log-level", VERB_ANY, 1, 0, get_log_level },
1819 { "set-log-target", 2, 2, 0, set_log_target },
1820 { "get-log-target", VERB_ANY, 1, 0, get_log_target },
1821 { "dump", VERB_ANY, 1, 0, dump },
31a5924e 1822 { "unit-paths", 1, 1, 0, dump_unit_paths },
889d695d
JK
1823 { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
1824 { "verify", 2, VERB_ANY, 0, do_verify },
1825 { "calendar", 2, VERB_ANY, 0, test_calendar },
90657286 1826 { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
a6bcef29
LP
1827 {}
1828 };
1829
5220a6f3 1830 int r;
2265fbf7
SP
1831
1832 setlocale(LC_ALL, "");
c170f3a4 1833 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
a6bcef29 1834
2265fbf7
SP
1835 log_parse_environment();
1836 log_open();
1837
1838 r = parse_argv(argc, argv);
9ea9d4cf
LP
1839 if (r <= 0)
1840 goto finish;
2265fbf7 1841
a6bcef29 1842 r = dispatch_verb(argc, argv, verbs, NULL);
2265fbf7 1843
9ea9d4cf
LP
1844finish:
1845 pager_close();
1846
e55933db
ŁS
1847 strv_free(arg_dot_from_patterns);
1848 strv_free(arg_dot_to_patterns);
c170f3a4
LP
1849
1850 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 1851}