1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2013 Simon Peeters
13 #include "alloc-util.h"
14 #include "analyze-verify.h"
15 #include "bus-error.h"
16 #include "bus-unit-util.h"
18 #include "calendarspec.h"
20 #include "conf-files.h"
23 #include "glob-util.h"
25 #include "locale-util.h"
28 #include "parse-util.h"
29 #include "path-util.h"
31 #include "seccomp-util.h"
36 #include "terminal-util.h"
37 #include "unit-name.h"
41 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
42 #define SCALE_Y (20.0)
44 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
46 #define svg(...) printf(__VA_ARGS__)
48 #define svg_bar(class, x1, x2, y) \
49 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
51 SCALE_X * (x1), SCALE_Y * (y), \
52 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
54 #define svg_text(b, x, y, format, ...) \
56 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
57 svg(format, ## __VA_ARGS__); \
66 static char** arg_dot_from_patterns
= NULL
;
67 static char** arg_dot_to_patterns
= NULL
;
68 static usec_t arg_fuzz
= 0;
69 static bool arg_no_pager
= false;
70 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
71 static const char *arg_host
= NULL
;
72 static UnitFileScope arg_scope
= UNIT_FILE_SYSTEM
;
73 static bool arg_man
= true;
74 static bool arg_generators
= false;
75 static const char *arg_root
= NULL
;
81 usec_t kernel_done_time
;
83 usec_t userspace_time
;
85 usec_t security_start_time
;
86 usec_t security_finish_time
;
87 usec_t generators_start_time
;
88 usec_t generators_finish_time
;
89 usec_t unitsload_start_time
;
90 usec_t unitsload_finish_time
;
91 usec_t initrd_security_start_time
;
92 usec_t initrd_security_finish_time
;
93 usec_t initrd_generators_start_time
;
94 usec_t initrd_generators_finish_time
;
95 usec_t initrd_unitsload_start_time
;
96 usec_t initrd_unitsload_finish_time
;
99 * If we're analyzing the user instance, all timestamps will be offset
100 * by its own start-up timestamp, which may be arbitrarily big.
101 * With "plot", this causes arbitrarily wide output SVG files which almost
102 * completely consist of empty space. Thus we cancel out this offset.
104 * This offset is subtracted from times above by acquire_boot_times(),
105 * but it still needs to be subtracted from unit-specific timestamps
106 * (so it is stored here for reference).
108 usec_t reverse_offset
;
124 char *kernel_release
;
125 char *kernel_version
;
126 char *os_pretty_name
;
127 char *virtualization
;
131 static int acquire_bus(sd_bus
**bus
, bool *use_full_bus
) {
132 bool user
= arg_scope
!= UNIT_FILE_SYSTEM
;
135 if (use_full_bus
&& *use_full_bus
) {
136 r
= bus_connect_transport(arg_transport
, arg_host
, user
, bus
);
137 if (IN_SET(r
, 0, -EHOSTDOWN
))
140 *use_full_bus
= false;
143 return bus_connect_transport_systemd(arg_transport
, arg_host
, user
, bus
);
146 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
147 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
156 r
= sd_bus_get_property_trivial(
158 "org.freedesktop.systemd1",
166 log_error("Failed to parse reply: %s", bus_error_message(&error
, -r
));
173 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
174 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
182 r
= sd_bus_get_property_strv(
184 "org.freedesktop.systemd1",
186 "org.freedesktop.systemd1.Unit",
191 log_error("Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
198 static int compare_unit_time(const void *a
, const void *b
) {
199 return compare(((struct unit_times
*)b
)->time
,
200 ((struct unit_times
*)a
)->time
);
203 static int compare_unit_start(const void *a
, const void *b
) {
204 return compare(((struct unit_times
*)a
)->activating
,
205 ((struct unit_times
*)b
)->activating
);
208 static void unit_times_free(struct unit_times
*t
) {
209 struct unit_times
*p
;
211 for (p
= t
; p
->has_data
; p
++)
216 DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times
*, unit_times_free
);
218 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
227 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
228 static struct boot_times times
;
229 static bool cached
= false;
234 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
236 if (bus_get_uint64_property(bus
,
237 "/org/freedesktop/systemd1",
238 "org.freedesktop.systemd1.Manager",
239 "FirmwareTimestampMonotonic",
240 ×
.firmware_time
) < 0 ||
241 bus_get_uint64_property(bus
,
242 "/org/freedesktop/systemd1",
243 "org.freedesktop.systemd1.Manager",
244 "LoaderTimestampMonotonic",
245 ×
.loader_time
) < 0 ||
246 bus_get_uint64_property(bus
,
247 "/org/freedesktop/systemd1",
248 "org.freedesktop.systemd1.Manager",
250 ×
.kernel_time
) < 0 ||
251 bus_get_uint64_property(bus
,
252 "/org/freedesktop/systemd1",
253 "org.freedesktop.systemd1.Manager",
254 "InitRDTimestampMonotonic",
255 ×
.initrd_time
) < 0 ||
256 bus_get_uint64_property(bus
,
257 "/org/freedesktop/systemd1",
258 "org.freedesktop.systemd1.Manager",
259 "UserspaceTimestampMonotonic",
260 ×
.userspace_time
) < 0 ||
261 bus_get_uint64_property(bus
,
262 "/org/freedesktop/systemd1",
263 "org.freedesktop.systemd1.Manager",
264 "FinishTimestampMonotonic",
265 ×
.finish_time
) < 0 ||
266 bus_get_uint64_property(bus
,
267 "/org/freedesktop/systemd1",
268 "org.freedesktop.systemd1.Manager",
269 "SecurityStartTimestampMonotonic",
270 ×
.security_start_time
) < 0 ||
271 bus_get_uint64_property(bus
,
272 "/org/freedesktop/systemd1",
273 "org.freedesktop.systemd1.Manager",
274 "SecurityFinishTimestampMonotonic",
275 ×
.security_finish_time
) < 0 ||
276 bus_get_uint64_property(bus
,
277 "/org/freedesktop/systemd1",
278 "org.freedesktop.systemd1.Manager",
279 "GeneratorsStartTimestampMonotonic",
280 ×
.generators_start_time
) < 0 ||
281 bus_get_uint64_property(bus
,
282 "/org/freedesktop/systemd1",
283 "org.freedesktop.systemd1.Manager",
284 "GeneratorsFinishTimestampMonotonic",
285 ×
.generators_finish_time
) < 0 ||
286 bus_get_uint64_property(bus
,
287 "/org/freedesktop/systemd1",
288 "org.freedesktop.systemd1.Manager",
289 "UnitsLoadStartTimestampMonotonic",
290 ×
.unitsload_start_time
) < 0 ||
291 bus_get_uint64_property(bus
,
292 "/org/freedesktop/systemd1",
293 "org.freedesktop.systemd1.Manager",
294 "UnitsLoadFinishTimestampMonotonic",
295 ×
.unitsload_finish_time
) < 0)
298 (void) bus_get_uint64_property(bus
,
299 "/org/freedesktop/systemd1",
300 "org.freedesktop.systemd1.Manager",
301 "InitRDSecurityStartTimestampMonotonic",
302 ×
.initrd_security_start_time
);
303 (void) bus_get_uint64_property(bus
,
304 "/org/freedesktop/systemd1",
305 "org.freedesktop.systemd1.Manager",
306 "InitRDSecurityFinishTimestampMonotonic",
307 ×
.initrd_security_finish_time
);
308 (void) bus_get_uint64_property(bus
,
309 "/org/freedesktop/systemd1",
310 "org.freedesktop.systemd1.Manager",
311 "InitRDGeneratorsStartTimestampMonotonic",
312 ×
.initrd_generators_start_time
);
313 (void) bus_get_uint64_property(bus
,
314 "/org/freedesktop/systemd1",
315 "org.freedesktop.systemd1.Manager",
316 "InitRDGeneratorsFinishTimestampMonotonic",
317 ×
.initrd_generators_finish_time
);
318 (void) bus_get_uint64_property(bus
,
319 "/org/freedesktop/systemd1",
320 "org.freedesktop.systemd1.Manager",
321 "InitRDUnitsLoadStartTimestampMonotonic",
322 ×
.initrd_unitsload_start_time
);
323 (void) bus_get_uint64_property(bus
,
324 "/org/freedesktop/systemd1",
325 "org.freedesktop.systemd1.Manager",
326 "InitRDUnitsLoadFinishTimestampMonotonic",
327 ×
.initrd_unitsload_finish_time
);
329 if (times
.finish_time
<= 0) {
330 log_error("Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64
").\n"
331 "Please try again later.\n"
332 "Hint: Use 'systemctl%s list-jobs' to see active jobs",
334 arg_scope
== UNIT_FILE_SYSTEM
? "" : " --user");
338 if (arg_scope
== UNIT_FILE_SYSTEM
) {
339 if (times
.initrd_time
> 0)
340 times
.kernel_done_time
= times
.initrd_time
;
342 times
.kernel_done_time
= times
.userspace_time
;
345 * User-instance-specific timestamps processing
346 * (see comment to reverse_offset in struct boot_times).
348 times
.reverse_offset
= times
.userspace_time
;
350 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
= times
.userspace_time
= 0;
351 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
353 subtract_timestamp(×
.security_start_time
, times
.reverse_offset
);
354 subtract_timestamp(×
.security_finish_time
, times
.reverse_offset
);
356 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
357 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
359 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
360 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
370 static void free_host_info(struct host_info
*hi
) {
376 free(hi
->kernel_name
);
377 free(hi
->kernel_release
);
378 free(hi
->kernel_version
);
379 free(hi
->os_pretty_name
);
380 free(hi
->virtualization
);
381 free(hi
->architecture
);
385 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
387 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
388 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
389 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
391 struct boot_times
*boot_times
= NULL
;
392 _cleanup_(unit_times_freep
) struct unit_times
*unit_times
= NULL
;
396 r
= acquire_boot_times(bus
, &boot_times
);
400 r
= sd_bus_call_method(
402 "org.freedesktop.systemd1",
403 "/org/freedesktop/systemd1",
404 "org.freedesktop.systemd1.Manager",
409 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
413 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
415 return bus_log_parse_error(r
);
417 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
418 struct unit_times
*t
;
420 if (!GREEDY_REALLOC(unit_times
, size
, c
+2))
423 unit_times
[c
+1].has_data
= false;
427 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
429 if (bus_get_uint64_property(bus
, u
.unit_path
,
430 "org.freedesktop.systemd1.Unit",
431 "InactiveExitTimestampMonotonic",
432 &t
->activating
) < 0 ||
433 bus_get_uint64_property(bus
, u
.unit_path
,
434 "org.freedesktop.systemd1.Unit",
435 "ActiveEnterTimestampMonotonic",
436 &t
->activated
) < 0 ||
437 bus_get_uint64_property(bus
, u
.unit_path
,
438 "org.freedesktop.systemd1.Unit",
439 "ActiveExitTimestampMonotonic",
440 &t
->deactivating
) < 0 ||
441 bus_get_uint64_property(bus
, u
.unit_path
,
442 "org.freedesktop.systemd1.Unit",
443 "InactiveEnterTimestampMonotonic",
444 &t
->deactivated
) < 0)
447 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
448 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
449 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
450 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
452 if (t
->activated
>= t
->activating
)
453 t
->time
= t
->activated
- t
->activating
;
454 else if (t
->deactivated
>= t
->activating
)
455 t
->time
= t
->deactivated
- t
->activating
;
459 if (t
->activating
== 0)
462 t
->name
= strdup(u
.id
);
470 return bus_log_parse_error(r
);
472 *out
= TAKE_PTR(unit_times
);
476 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
477 static const struct bus_properties_map hostname_map
[] = {
478 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
479 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
480 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
481 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
482 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
486 static const struct bus_properties_map manager_map
[] = {
487 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
488 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
492 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
493 _cleanup_(free_host_infop
) struct host_info
*host
;
496 host
= new0(struct host_info
, 1);
500 r
= bus_map_all_properties(bus
,
501 "org.freedesktop.hostname1",
502 "/org/freedesktop/hostname1",
509 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error
, r
));
511 r
= bus_map_all_properties(bus
,
512 "org.freedesktop.systemd1",
513 "/org/freedesktop/systemd1",
520 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
522 *hi
= TAKE_PTR(host
);
527 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
528 char ts
[FORMAT_TIMESPAN_MAX
];
529 struct boot_times
*t
;
530 static char buf
[4096];
534 usec_t activated_time
= USEC_INFINITY
;
535 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
536 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
538 r
= acquire_boot_times(bus
, &t
);
542 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
546 r
= sd_bus_get_property_string(
548 "org.freedesktop.systemd1",
550 "org.freedesktop.systemd1.Unit",
555 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
559 r
= bus_get_uint64_property(bus
, path
,
560 "org.freedesktop.systemd1.Unit",
561 "ActiveEnterTimestampMonotonic",
564 log_info_errno(r
, "Could not get time to reach default.target. Continuing...");
565 activated_time
= USEC_INFINITY
;
571 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
572 if (t
->firmware_time
> 0)
573 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
574 if (t
->loader_time
> 0)
575 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
576 if (t
->kernel_time
> 0)
577 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
578 if (t
->initrd_time
> 0)
579 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
581 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
582 if (t
->kernel_time
> 0)
583 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
585 if (unit_id
&& activated_time
> 0 && activated_time
!= USEC_INFINITY
)
586 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
));
587 else if (unit_id
&& activated_time
== 0)
588 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
589 else if (unit_id
&& activated_time
== USEC_INFINITY
)
590 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.",unit_id
);
592 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
602 static void svg_graph_box(double height
, double begin
, double end
) {
605 /* outside box, fill */
606 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
607 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
609 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
610 /* lines for each second */
611 if (i
% 5000000 == 0)
612 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
613 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
614 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
615 else if (i
% 1000000 == 0)
616 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
617 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
618 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
620 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
621 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
625 static int plot_unit_times(struct unit_times
*u
, double width
, int y
) {
626 char ts
[FORMAT_TIMESPAN_MAX
];
632 svg_bar("activating", u
->activating
, u
->activated
, y
);
633 svg_bar("active", u
->activated
, u
->deactivating
, y
);
634 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
636 /* place the text on the left if we have passed the half of the svg width */
637 b
= u
->activating
* SCALE_X
< width
/ 2;
639 svg_text(b
, u
->activating
, y
, "%s (%s)",
640 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
642 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
647 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
648 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
649 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
650 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
651 struct boot_times
*boot
;
652 int n
, m
= 1, y
= 0, r
;
653 bool use_full_bus
= true;
655 _cleanup_free_
char *pretty_times
= NULL
;
656 struct unit_times
*u
;
658 r
= acquire_bus(&bus
, &use_full_bus
);
660 return log_error_errno(r
, "Failed to create bus connection: %m");
662 n
= acquire_boot_times(bus
, &boot
);
666 n
= pretty_boot_time(bus
, &pretty_times
);
671 n
= acquire_host_info(bus
, &host
);
676 n
= acquire_time_data(bus
, ×
);
680 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
682 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
686 if (boot
->firmware_time
> boot
->loader_time
)
688 if (boot
->loader_time
> 0) {
693 if (boot
->initrd_time
> 0)
695 if (boot
->kernel_time
> 0)
698 for (u
= times
; u
->has_data
; u
++) {
699 double text_start
, text_width
;
701 if (u
->activating
> boot
->finish_time
) {
702 u
->name
= mfree(u
->name
);
706 /* If the text cannot fit on the left side then
707 * increase the svg width so it fits on the right.
708 * TODO: calculate the text width more accurately */
709 text_width
= 8.0 * strlen(u
->name
);
710 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
711 if (text_width
> text_start
&& text_width
+ text_start
> width
)
712 width
= text_width
+ text_start
;
714 if (u
->deactivated
> u
->activating
&&
715 u
->deactivated
<= boot
->finish_time
&&
716 u
->activated
== 0 && u
->deactivating
== 0)
717 u
->activated
= u
->deactivating
= u
->deactivated
;
718 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
719 u
->activated
= boot
->finish_time
;
720 if (u
->deactivating
< u
->activated
|| u
->deactivating
> boot
->finish_time
)
721 u
->deactivating
= boot
->finish_time
;
722 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
723 u
->deactivated
= boot
->finish_time
;
727 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
728 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
729 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
731 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
732 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
733 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
734 5 * SCALE_Y
/* legend */);
736 /* write some basic info as a comment, including some help */
737 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
738 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
739 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
740 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
741 "<!-- point your browser to this file. -->\n\n"
742 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
745 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
746 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
747 " rect.background { fill: rgb(255,255,255); }\n"
748 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
749 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
750 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
751 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
752 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
753 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
754 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
755 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
756 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
757 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
758 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
759 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
760 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
762 " line.sec5 { stroke-width: 2; }\n"
763 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
764 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
765 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
766 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
767 " text.sec { font-size: 10px; }\n"
768 " ]]>\n </style>\n</defs>\n\n");
770 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
771 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
773 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
774 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
775 strempty(host
->hostname
),
776 strempty(host
->kernel_name
),
777 strempty(host
->kernel_release
),
778 strempty(host
->kernel_version
),
779 strempty(host
->architecture
),
780 strempty(host
->virtualization
));
782 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
783 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
785 if (boot
->firmware_time
> 0) {
786 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
787 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
790 if (boot
->loader_time
> 0) {
791 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
792 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
795 if (boot
->kernel_time
> 0) {
796 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
797 svg_text(true, 0, y
, "kernel");
800 if (boot
->initrd_time
> 0) {
801 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
802 if (boot
->initrd_security_start_time
< boot
->initrd_security_finish_time
)
803 svg_bar("security", boot
->initrd_security_start_time
, boot
->initrd_security_finish_time
, y
);
804 if (boot
->initrd_generators_start_time
< boot
->initrd_generators_finish_time
)
805 svg_bar("generators", boot
->initrd_generators_start_time
, boot
->initrd_generators_finish_time
, y
);
806 if (boot
->initrd_unitsload_start_time
< boot
->initrd_unitsload_finish_time
)
807 svg_bar("unitsload", boot
->initrd_unitsload_start_time
, boot
->initrd_unitsload_finish_time
, y
);
808 svg_text(true, boot
->initrd_time
, y
, "initrd");
812 for (u
= times
; u
->has_data
; u
++) {
813 if (u
->activating
>= boot
->userspace_time
)
816 y
+= plot_unit_times(u
, width
, y
);
819 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
820 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
821 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
822 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
823 svg_text(true, boot
->userspace_time
, y
, "systemd");
826 for (; u
->has_data
; u
++)
827 y
+= plot_unit_times(u
, width
, y
);
832 svg("<g transform=\"translate(20,100)\">\n");
834 svg_bar("activating", 0, 300000, y
);
835 svg_text(true, 400000, y
, "Activating");
837 svg_bar("active", 0, 300000, y
);
838 svg_text(true, 400000, y
, "Active");
840 svg_bar("deactivating", 0, 300000, y
);
841 svg_text(true, 400000, y
, "Deactivating");
843 svg_bar("security", 0, 300000, y
);
844 svg_text(true, 400000, y
, "Setting up security module");
846 svg_bar("generators", 0, 300000, y
);
847 svg_text(true, 400000, y
, "Generators");
849 svg_bar("unitsload", 0, 300000, y
);
850 svg_text(true, 400000, y
, "Loading unit files");
860 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
861 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
863 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
865 for (i
= level
; i
!= 0; i
--)
866 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
868 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
872 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
873 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
874 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
875 else if (times
->activated
> boot
->userspace_time
)
876 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
886 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
887 _cleanup_free_
char *path
= NULL
;
893 path
= unit_dbus_path_from_name(name
);
897 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
900 static Hashmap
*unit_times_hashmap
;
902 static int list_dependencies_compare(const void *_a
, const void *_b
) {
903 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
904 usec_t usa
= 0, usb
= 0;
905 struct unit_times
*times
;
907 times
= hashmap_get(unit_times_hashmap
, *a
);
909 usa
= times
->activated
;
910 times
= hashmap_get(unit_times_hashmap
, *b
);
912 usb
= times
->activated
;
917 static bool times_in_range(const struct unit_times
*times
, const struct boot_times
*boot
) {
919 times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
922 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
923 unsigned int branches
) {
924 _cleanup_strv_free_
char **deps
= NULL
;
927 usec_t service_longest
= 0;
929 struct unit_times
*times
;
930 struct boot_times
*boot
;
932 if (strv_extend(units
, name
))
935 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
939 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
941 r
= acquire_boot_times(bus
, &boot
);
945 STRV_FOREACH(c
, deps
) {
946 times
= hashmap_get(unit_times_hashmap
, *c
);
947 if (times_in_range(times
, boot
) &&
948 times
->activated
>= service_longest
)
949 service_longest
= times
->activated
;
952 if (service_longest
== 0)
955 STRV_FOREACH(c
, deps
) {
956 times
= hashmap_get(unit_times_hashmap
, *c
);
957 if (times_in_range(times
, boot
) &&
958 service_longest
- times
->activated
<= arg_fuzz
)
965 STRV_FOREACH(c
, deps
) {
966 times
= hashmap_get(unit_times_hashmap
, *c
);
967 if (!times_in_range(times
, boot
) ||
968 service_longest
- times
->activated
> arg_fuzz
)
973 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
977 if (strv_contains(*units
, *c
)) {
978 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
985 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
986 (branches
<< 1) | (to_print
? 1 : 0));
996 static int list_dependencies(sd_bus
*bus
, const char *name
) {
997 _cleanup_strv_free_
char **units
= NULL
;
998 char ts
[FORMAT_TIMESPAN_MAX
];
999 struct unit_times
*times
;
1002 _cleanup_free_
char *path
= NULL
;
1003 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1004 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1005 struct boot_times
*boot
;
1009 path
= unit_dbus_path_from_name(name
);
1013 r
= sd_bus_get_property(
1015 "org.freedesktop.systemd1",
1017 "org.freedesktop.systemd1.Unit",
1023 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
1027 r
= sd_bus_message_read(reply
, "s", &id
);
1029 return bus_log_parse_error(r
);
1031 times
= hashmap_get(unit_times_hashmap
, id
);
1033 r
= acquire_boot_times(bus
, &boot
);
1039 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
1040 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
1041 else if (times
->activated
> boot
->userspace_time
)
1042 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
1047 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1050 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1051 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1052 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
1053 struct unit_times
*u
;
1057 r
= acquire_bus(&bus
, NULL
);
1059 return log_error_errno(r
, "Failed to create bus connection: %m");
1061 n
= acquire_time_data(bus
, ×
);
1065 h
= hashmap_new(&string_hash_ops
);
1069 for (u
= times
; u
->has_data
; u
++) {
1070 r
= hashmap_put(h
, u
->name
, u
);
1072 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1074 unit_times_hashmap
= h
;
1076 (void) pager_open(arg_no_pager
, false);
1078 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1079 "The time the unit takes to start is printed after the \"+\" character.\n");
1083 STRV_FOREACH(name
, strv_skip(argv
, 1))
1084 list_dependencies(bus
, *name
);
1086 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1088 h
= hashmap_free(h
);
1092 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1093 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1094 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
1095 struct unit_times
*u
;
1098 r
= acquire_bus(&bus
, NULL
);
1100 return log_error_errno(r
, "Failed to create bus connection: %m");
1102 n
= acquire_time_data(bus
, ×
);
1106 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
1108 (void) pager_open(arg_no_pager
, false);
1110 for (u
= times
; u
->has_data
; u
++) {
1111 char ts
[FORMAT_TIMESPAN_MAX
];
1114 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
), u
->name
);
1120 static int analyze_time(int argc
, char *argv
[], void *userdata
) {
1121 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1122 _cleanup_free_
char *buf
= NULL
;
1125 r
= acquire_bus(&bus
, NULL
);
1127 return log_error_errno(r
, "Failed to create bus connection: %m");
1129 r
= pretty_boot_time(bus
, &buf
);
1137 static int graph_one_property(sd_bus
*bus
, const UnitInfo
*u
, const char* prop
, const char *color
, char* patterns
[], char* from_patterns
[], char* to_patterns
[]) {
1138 _cleanup_strv_free_
char **units
= NULL
;
1141 bool match_patterns
;
1147 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1149 if (!strv_isempty(from_patterns
) &&
1151 !strv_fnmatch(from_patterns
, u
->id
, 0))
1154 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1158 STRV_FOREACH(unit
, units
) {
1159 bool match_patterns2
;
1161 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1163 if (!strv_isempty(to_patterns
) &&
1165 !strv_fnmatch(to_patterns
, *unit
, 0))
1168 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1171 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1177 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1183 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1184 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1189 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1190 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1193 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1196 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1199 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1207 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1208 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1212 STRV_FOREACH(pattern
, patterns
) {
1213 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1214 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1216 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1219 if (string_is_glob(*pattern
))
1222 unit
= unit_dbus_path_from_name(*pattern
);
1226 r
= sd_bus_get_property_string(
1228 "org.freedesktop.systemd1",
1230 "org.freedesktop.systemd1.Unit",
1235 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1237 if (!streq(*pattern
, unit_id
)) {
1238 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1243 *ret
= expanded_patterns
;
1244 expanded_patterns
= NULL
; /* do not free */
1249 static int dot(int argc
, char *argv
[], void *userdata
) {
1250 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1251 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1252 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1253 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1254 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1255 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1259 r
= acquire_bus(&bus
, NULL
);
1261 return log_error_errno(r
, "Failed to create bus connection: %m");
1263 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
1267 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1271 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1275 r
= sd_bus_call_method(
1277 "org.freedesktop.systemd1",
1278 "/org/freedesktop/systemd1",
1279 "org.freedesktop.systemd1.Manager",
1285 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1289 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1291 return bus_log_parse_error(r
);
1293 printf("digraph systemd {\n");
1295 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1297 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1302 return bus_log_parse_error(r
);
1306 log_info(" Color legend: black = Requires\n"
1307 " dark blue = Requisite\n"
1308 " dark grey = Wants\n"
1309 " red = Conflicts\n"
1310 " green = After\n");
1313 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1314 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1319 static int dump_fallback(sd_bus
*bus
) {
1320 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1321 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1322 const char *text
= NULL
;
1327 r
= sd_bus_call_method(
1329 "org.freedesktop.systemd1",
1330 "/org/freedesktop/systemd1",
1331 "org.freedesktop.systemd1.Manager",
1337 return log_error_errno(r
, "Failed to issue method call Dump: %s", bus_error_message(&error
, r
));
1339 r
= sd_bus_message_read(reply
, "s", &text
);
1341 return bus_log_parse_error(r
);
1343 fputs(text
, stdout
);
1347 static int dump(int argc
, char *argv
[], void *userdata
) {
1348 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1349 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1350 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1354 r
= acquire_bus(&bus
, NULL
);
1356 return log_error_errno(r
, "Failed to create bus connection: %m");
1358 (void) pager_open(arg_no_pager
, false);
1360 if (!sd_bus_can_send(bus
, SD_BUS_TYPE_UNIX_FD
))
1361 return dump_fallback(bus
);
1363 r
= sd_bus_call_method(
1365 "org.freedesktop.systemd1",
1366 "/org/freedesktop/systemd1",
1367 "org.freedesktop.systemd1.Manager",
1368 "DumpByFileDescriptor",
1373 /* fall back to Dump if DumpByFileDescriptor is not supported */
1374 if (!IN_SET(r
, -EACCES
, -EBADR
))
1375 return log_error_errno(r
, "Failed to issue method call DumpByFileDescriptor: %s", bus_error_message(&error
, r
));
1377 return dump_fallback(bus
);
1380 r
= sd_bus_message_read(reply
, "h", &fd
);
1382 return bus_log_parse_error(r
);
1385 return copy_bytes(fd
, STDOUT_FILENO
, (uint64_t) -1, 0);
1388 static int cat_config(int argc
, char *argv
[], void *userdata
) {
1392 (void) pager_open(arg_no_pager
, false);
1394 STRV_FOREACH(arg
, argv
+ 1) {
1395 const char *t
= NULL
;
1397 if (arg
!= argv
+ 1)
1400 if (path_is_absolute(*arg
)) {
1403 NULSTR_FOREACH(dir
, CONF_PATHS_NULSTR("")) {
1404 t
= path_startswith(*arg
, dir
);
1410 log_error("Path %s does not start with any known prefix.", *arg
);
1416 r
= conf_files_cat(arg_root
, t
);
1424 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1425 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1426 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1432 r
= acquire_bus(&bus
, NULL
);
1434 return log_error_errno(r
, "Failed to create bus connection: %m");
1436 r
= sd_bus_set_property(
1438 "org.freedesktop.systemd1",
1439 "/org/freedesktop/systemd1",
1440 "org.freedesktop.systemd1.Manager",
1446 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1451 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1452 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1453 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1454 _cleanup_free_
char *level
= NULL
;
1457 r
= acquire_bus(&bus
, NULL
);
1459 return log_error_errno(r
, "Failed to create bus connection: %m");
1461 r
= sd_bus_get_property_string(
1463 "org.freedesktop.systemd1",
1464 "/org/freedesktop/systemd1",
1465 "org.freedesktop.systemd1.Manager",
1470 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1476 static int get_or_set_log_level(int argc
, char *argv
[], void *userdata
) {
1477 return (argc
== 1) ? get_log_level(argc
, argv
, userdata
) : set_log_level(argc
, argv
, userdata
);
1480 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1481 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1482 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1488 r
= acquire_bus(&bus
, NULL
);
1490 return log_error_errno(r
, "Failed to create bus connection: %m");
1492 r
= sd_bus_set_property(
1494 "org.freedesktop.systemd1",
1495 "/org/freedesktop/systemd1",
1496 "org.freedesktop.systemd1.Manager",
1502 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1507 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1508 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1509 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1510 _cleanup_free_
char *target
= NULL
;
1513 r
= acquire_bus(&bus
, NULL
);
1515 return log_error_errno(r
, "Failed to create bus connection: %m");
1517 r
= sd_bus_get_property_string(
1519 "org.freedesktop.systemd1",
1520 "/org/freedesktop/systemd1",
1521 "org.freedesktop.systemd1.Manager",
1526 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1532 static int get_or_set_log_target(int argc
, char *argv
[], void *userdata
) {
1533 return (argc
== 1) ? get_log_target(argc
, argv
, userdata
) : set_log_target(argc
, argv
, userdata
);
1536 static int dump_unit_paths(int argc
, char *argv
[], void *userdata
) {
1537 _cleanup_(lookup_paths_free
) LookupPaths paths
= {};
1541 r
= lookup_paths_init(&paths
, arg_scope
, 0, NULL
);
1543 return log_error_errno(r
, "lookup_paths_init() failed: %m");
1545 STRV_FOREACH(p
, paths
.search_path
)
1552 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1553 const char *syscall
;
1555 printf("%s\n", set
->name
);
1556 printf(" # %s\n", set
->help
);
1557 NULSTR_FOREACH(syscall
, set
->value
)
1558 printf(" %s\n", syscall
);
1561 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1564 (void) pager_open(arg_no_pager
, false);
1566 if (strv_isempty(strv_skip(argv
, 1))) {
1569 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1572 dump_syscall_filter(syscall_filter_sets
+ i
);
1578 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1579 const SyscallFilterSet
*set
;
1584 set
= syscall_filter_set_find(*name
);
1586 /* make sure the error appears below normal output */
1589 log_error("Filter set \"%s\" not found.", *name
);
1593 dump_syscall_filter(set
);
1602 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1603 log_error("Not compiled with syscall filters, sorry.");
1608 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
1613 n
= now(CLOCK_REALTIME
);
1615 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1616 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1617 _cleanup_free_
char *t
= NULL
;
1620 r
= calendar_spec_from_string(*p
, &spec
);
1622 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1626 r
= calendar_spec_normalize(spec
);
1628 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1632 r
= calendar_spec_to_string(spec
, &t
);
1634 ret
= log_error_errno(r
, "Failed to format calendar specification '%s': %m", *p
);
1639 printf(" Original form: %s\n", *p
);
1641 printf("Normalized form: %s\n", t
);
1643 r
= calendar_spec_next_usec(spec
, n
, &next
);
1645 printf(" Next elapse: never\n");
1647 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1650 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1652 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1654 if (!in_utc_timezone())
1655 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1657 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1667 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
1668 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1669 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1672 assert(IN_SET(argc
, 1, 2));
1675 r
= acquire_bus(&bus
, NULL
);
1677 return log_error_errno(r
, "Failed to create bus connection: %m");
1679 /* get ServiceWatchdogs */
1681 r
= sd_bus_get_property_trivial(
1683 "org.freedesktop.systemd1",
1684 "/org/freedesktop/systemd1",
1685 "org.freedesktop.systemd1.Manager",
1691 return log_error_errno(r
, "Failed to get service-watchdog state: %s", bus_error_message(&error
, r
));
1693 printf("%s\n", yes_no(!!b
));
1698 /* set ServiceWatchdogs */
1699 b
= parse_boolean(argv
[1]);
1701 log_error("Failed to parse service-watchdogs argument.");
1705 r
= sd_bus_set_property(
1707 "org.freedesktop.systemd1",
1708 "/org/freedesktop/systemd1",
1709 "org.freedesktop.systemd1.Manager",
1715 return log_error_errno(r
, "Failed to set service-watchdog state: %s", bus_error_message(&error
, r
));
1720 static int do_verify(int argc
, char *argv
[], void *userdata
) {
1721 return verify_units(strv_skip(argv
, 1), arg_scope
, arg_man
, arg_generators
);
1724 static int help(int argc
, char *argv
[], void *userdata
) {
1726 (void) pager_open(arg_no_pager
, false);
1728 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1729 "Profile systemd, show unit dependencies, check unit files.\n\n"
1730 " -h --help Show this help\n"
1731 " --version Show package version\n"
1732 " --no-pager Do not pipe output into a pager\n"
1733 " --system Operate on system systemd instance\n"
1734 " --user Operate on user systemd instance\n"
1735 " --global Operate on global user configuration\n"
1736 " -H --host=[USER@]HOST Operate on remote host\n"
1737 " -M --machine=CONTAINER Operate on local container\n"
1738 " --order Show only order in the graph\n"
1739 " --require Show only requirement in the graph\n"
1740 " --from-pattern=GLOB Show only origins in the graph\n"
1741 " --to-pattern=GLOB Show only destinations in the graph\n"
1742 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1743 " earlier than the latest in the branch\n"
1744 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1745 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1747 " time Print time spent in the kernel\n"
1748 " blame Print list of running units ordered by time to init\n"
1749 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1750 " plot Output SVG graphic showing service initialization\n"
1751 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1752 " log-level [LEVEL] Get/set logging threshold for manager\n"
1753 " log-target [TARGET] Get/set logging target for manager\n"
1754 " dump Output state serialization of service manager\n"
1755 " cat-config Show configuration file and drop-ins\n"
1756 " unit-paths List load directories for units\n"
1757 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1758 " verify FILE... Check unit files for correctness\n"
1759 " calendar SPEC... Validate repetitive calendar time events\n"
1760 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1761 , program_invocation_short_name
);
1763 /* When updating this list, including descriptions, apply
1764 * changes to shell-completion/bash/systemd-analyze and
1765 * shell-completion/zsh/_systemd-analyze too. */
1770 static int parse_argv(int argc
, char *argv
[]) {
1772 ARG_VERSION
= 0x100,
1779 ARG_DOT_FROM_PATTERN
,
1787 static const struct option options
[] = {
1788 { "help", no_argument
, NULL
, 'h' },
1789 { "version", no_argument
, NULL
, ARG_VERSION
},
1790 { "order", no_argument
, NULL
, ARG_ORDER
},
1791 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1792 { "root", required_argument
, NULL
, ARG_ROOT
},
1793 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1794 { "user", no_argument
, NULL
, ARG_USER
},
1795 { "global", no_argument
, NULL
, ARG_GLOBAL
},
1796 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1797 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1798 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1799 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1800 { "man", optional_argument
, NULL
, ARG_MAN
},
1801 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1802 { "host", required_argument
, NULL
, 'H' },
1803 { "machine", required_argument
, NULL
, 'M' },
1812 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1816 return help(0, NULL
, NULL
);
1826 arg_scope
= UNIT_FILE_SYSTEM
;
1830 arg_scope
= UNIT_FILE_USER
;
1834 arg_scope
= UNIT_FILE_GLOBAL
;
1838 arg_dot
= DEP_ORDER
;
1842 arg_dot
= DEP_REQUIRE
;
1845 case ARG_DOT_FROM_PATTERN
:
1846 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1851 case ARG_DOT_TO_PATTERN
:
1852 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1858 r
= parse_sec(optarg
, &arg_fuzz
);
1864 arg_no_pager
= true;
1868 arg_transport
= BUS_TRANSPORT_REMOTE
;
1873 arg_transport
= BUS_TRANSPORT_MACHINE
;
1879 r
= parse_boolean(optarg
);
1881 log_error("Failed to parse --man= argument.");
1891 case ARG_GENERATORS
:
1893 r
= parse_boolean(optarg
);
1895 log_error("Failed to parse --generators= argument.");
1901 arg_generators
= true;
1909 assert_not_reached("Unhandled option code.");
1912 if (arg_scope
== UNIT_FILE_GLOBAL
&&
1913 !STR_IN_SET(argv
[optind
] ?: "time", "dot", "unit-paths", "verify")) {
1914 log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
1918 if (arg_root
&& !streq_ptr(argv
[optind
], "cat-config")) {
1919 log_error("Option --root is only supported for cat-config right now.");
1923 return 1; /* work to do */
1926 int main(int argc
, char *argv
[]) {
1928 static const Verb verbs
[] = {
1929 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1930 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
1931 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
1932 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
1933 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
1934 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
1935 { "log-level", VERB_ANY
, 2, 0, get_or_set_log_level
},
1936 { "log-target", VERB_ANY
, 2, 0, get_or_set_log_target
},
1937 /* The following four verbs are deprecated aliases */
1938 { "set-log-level", 2, 2, 0, set_log_level
},
1939 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
1940 { "set-log-target", 2, 2, 0, set_log_target
},
1941 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
1942 { "dump", VERB_ANY
, 1, 0, dump
},
1943 { "cat-config", 2, VERB_ANY
, 0, cat_config
},
1944 { "unit-paths", 1, 1, 0, dump_unit_paths
},
1945 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
1946 { "verify", 2, VERB_ANY
, 0, do_verify
},
1947 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
1948 { "service-watchdogs", VERB_ANY
, 2, 0, service_watchdogs
},
1954 setlocale(LC_ALL
, "");
1955 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1957 log_parse_environment();
1960 r
= parse_argv(argc
, argv
);
1964 r
= dispatch_verb(argc
, argv
, verbs
, NULL
);
1969 strv_free(arg_dot_from_patterns
);
1970 strv_free(arg_dot_to_patterns
);
1972 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;