1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010-2013 Lennart Poettering
6 Copyright 2013 Simon Peeters
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "alloc-util.h"
30 #include "analyze-verify.h"
31 #include "bus-error.h"
32 #include "bus-unit-util.h"
34 #include "calendarspec.h"
35 #include "glob-util.h"
37 #include "locale-util.h"
40 #include "parse-util.h"
42 #include "seccomp-util.h"
47 #include "terminal-util.h"
48 #include "unit-name.h"
52 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
53 #define SCALE_Y (20.0)
55 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
57 #define svg(...) printf(__VA_ARGS__)
59 #define svg_bar(class, x1, x2, y) \
60 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
62 SCALE_X * (x1), SCALE_Y * (y), \
63 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
65 #define svg_text(b, x, y, format, ...) \
67 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
68 svg(format, ## __VA_ARGS__); \
77 static char** arg_dot_from_patterns
= NULL
;
78 static char** arg_dot_to_patterns
= NULL
;
79 static usec_t arg_fuzz
= 0;
80 static bool arg_no_pager
= false;
81 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
82 static const char *arg_host
= NULL
;
83 static bool arg_user
= false;
84 static bool arg_man
= true;
85 static bool arg_generators
= false;
91 usec_t kernel_done_time
;
93 usec_t userspace_time
;
95 usec_t security_start_time
;
96 usec_t security_finish_time
;
97 usec_t generators_start_time
;
98 usec_t generators_finish_time
;
99 usec_t unitsload_start_time
;
100 usec_t unitsload_finish_time
;
103 * If we're analyzing the user instance, all timestamps will be offset
104 * by its own start-up timestamp, which may be arbitrarily big.
105 * With "plot", this causes arbitrarily wide output SVG files which almost
106 * completely consist of empty space. Thus we cancel out this offset.
108 * This offset is subtracted from times above by acquire_boot_times(),
109 * but it still needs to be subtracted from unit-specific timestamps
110 * (so it is stored here for reference).
112 usec_t reverse_offset
;
127 char *kernel_release
;
128 char *kernel_version
;
129 char *os_pretty_name
;
130 char *virtualization
;
134 static int acquire_bus(bool need_full_bus
, sd_bus
**bus
) {
136 return bus_connect_transport(arg_transport
, arg_host
, arg_user
, bus
);
138 return bus_connect_transport_systemd(arg_transport
, arg_host
, arg_user
, bus
);
141 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
142 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
151 r
= sd_bus_get_property_trivial(
153 "org.freedesktop.systemd1",
161 log_error("Failed to parse reply: %s", bus_error_message(&error
, -r
));
168 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
169 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
177 r
= sd_bus_get_property_strv(
179 "org.freedesktop.systemd1",
181 "org.freedesktop.systemd1.Unit",
186 log_error("Failed to get unit property %s: %s", property
, bus_error_message(&error
, -r
));
193 static int compare_unit_time(const void *a
, const void *b
) {
194 return compare(((struct unit_times
*)b
)->time
,
195 ((struct unit_times
*)a
)->time
);
198 static int compare_unit_start(const void *a
, const void *b
) {
199 return compare(((struct unit_times
*)a
)->activating
,
200 ((struct unit_times
*)b
)->activating
);
203 static void free_unit_times(struct unit_times
*t
, unsigned n
) {
204 struct unit_times
*p
;
206 for (p
= t
; p
< t
+ n
; p
++)
212 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
221 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
222 static struct boot_times times
;
223 static bool cached
= false;
228 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
230 if (bus_get_uint64_property(bus
,
231 "/org/freedesktop/systemd1",
232 "org.freedesktop.systemd1.Manager",
233 "FirmwareTimestampMonotonic",
234 ×
.firmware_time
) < 0 ||
235 bus_get_uint64_property(bus
,
236 "/org/freedesktop/systemd1",
237 "org.freedesktop.systemd1.Manager",
238 "LoaderTimestampMonotonic",
239 ×
.loader_time
) < 0 ||
240 bus_get_uint64_property(bus
,
241 "/org/freedesktop/systemd1",
242 "org.freedesktop.systemd1.Manager",
244 ×
.kernel_time
) < 0 ||
245 bus_get_uint64_property(bus
,
246 "/org/freedesktop/systemd1",
247 "org.freedesktop.systemd1.Manager",
248 "InitRDTimestampMonotonic",
249 ×
.initrd_time
) < 0 ||
250 bus_get_uint64_property(bus
,
251 "/org/freedesktop/systemd1",
252 "org.freedesktop.systemd1.Manager",
253 "UserspaceTimestampMonotonic",
254 ×
.userspace_time
) < 0 ||
255 bus_get_uint64_property(bus
,
256 "/org/freedesktop/systemd1",
257 "org.freedesktop.systemd1.Manager",
258 "FinishTimestampMonotonic",
259 ×
.finish_time
) < 0 ||
260 bus_get_uint64_property(bus
,
261 "/org/freedesktop/systemd1",
262 "org.freedesktop.systemd1.Manager",
263 "SecurityStartTimestampMonotonic",
264 ×
.security_start_time
) < 0 ||
265 bus_get_uint64_property(bus
,
266 "/org/freedesktop/systemd1",
267 "org.freedesktop.systemd1.Manager",
268 "SecurityFinishTimestampMonotonic",
269 ×
.security_finish_time
) < 0 ||
270 bus_get_uint64_property(bus
,
271 "/org/freedesktop/systemd1",
272 "org.freedesktop.systemd1.Manager",
273 "GeneratorsStartTimestampMonotonic",
274 ×
.generators_start_time
) < 0 ||
275 bus_get_uint64_property(bus
,
276 "/org/freedesktop/systemd1",
277 "org.freedesktop.systemd1.Manager",
278 "GeneratorsFinishTimestampMonotonic",
279 ×
.generators_finish_time
) < 0 ||
280 bus_get_uint64_property(bus
,
281 "/org/freedesktop/systemd1",
282 "org.freedesktop.systemd1.Manager",
283 "UnitsLoadStartTimestampMonotonic",
284 ×
.unitsload_start_time
) < 0 ||
285 bus_get_uint64_property(bus
,
286 "/org/freedesktop/systemd1",
287 "org.freedesktop.systemd1.Manager",
288 "UnitsLoadFinishTimestampMonotonic",
289 ×
.unitsload_finish_time
) < 0)
292 if (times
.finish_time
<= 0) {
293 log_error("Bootup is not yet finished. Please try again later.");
299 * User-instance-specific timestamps processing
300 * (see comment to reverse_offset in struct boot_times).
302 times
.reverse_offset
= times
.userspace_time
;
304 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
= times
.userspace_time
= 0;
305 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
307 subtract_timestamp(×
.security_start_time
, times
.reverse_offset
);
308 subtract_timestamp(×
.security_finish_time
, times
.reverse_offset
);
310 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
311 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
313 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
314 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
316 if (times
.initrd_time
)
317 times
.kernel_done_time
= times
.initrd_time
;
319 times
.kernel_done_time
= times
.userspace_time
;
329 static void free_host_info(struct host_info
*hi
) {
335 free(hi
->kernel_name
);
336 free(hi
->kernel_release
);
337 free(hi
->kernel_version
);
338 free(hi
->os_pretty_name
);
339 free(hi
->virtualization
);
340 free(hi
->architecture
);
344 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
346 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
347 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
348 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
350 struct boot_times
*boot_times
= NULL
;
351 struct unit_times
*unit_times
= NULL
;
355 r
= acquire_boot_times(bus
, &boot_times
);
359 r
= sd_bus_call_method(
361 "org.freedesktop.systemd1",
362 "/org/freedesktop/systemd1",
363 "org.freedesktop.systemd1.Manager",
368 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
372 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
374 bus_log_parse_error(r
);
378 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
379 struct unit_times
*t
;
381 if (!GREEDY_REALLOC(unit_times
, size
, c
+1)) {
389 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
391 if (bus_get_uint64_property(bus
, u
.unit_path
,
392 "org.freedesktop.systemd1.Unit",
393 "InactiveExitTimestampMonotonic",
394 &t
->activating
) < 0 ||
395 bus_get_uint64_property(bus
, u
.unit_path
,
396 "org.freedesktop.systemd1.Unit",
397 "ActiveEnterTimestampMonotonic",
398 &t
->activated
) < 0 ||
399 bus_get_uint64_property(bus
, u
.unit_path
,
400 "org.freedesktop.systemd1.Unit",
401 "ActiveExitTimestampMonotonic",
402 &t
->deactivating
) < 0 ||
403 bus_get_uint64_property(bus
, u
.unit_path
,
404 "org.freedesktop.systemd1.Unit",
405 "InactiveEnterTimestampMonotonic",
406 &t
->deactivated
) < 0) {
411 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
412 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
413 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
414 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
416 if (t
->activated
>= t
->activating
)
417 t
->time
= t
->activated
- t
->activating
;
418 else if (t
->deactivated
>= t
->activating
)
419 t
->time
= t
->deactivated
- t
->activating
;
423 if (t
->activating
== 0)
426 t
->name
= strdup(u
.id
);
434 bus_log_parse_error(r
);
443 free_unit_times(unit_times
, (unsigned) c
);
447 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
448 static const struct bus_properties_map hostname_map
[] = {
449 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
450 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
451 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
452 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
453 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
457 static const struct bus_properties_map manager_map
[] = {
458 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
459 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
463 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
464 _cleanup_(free_host_infop
) struct host_info
*host
;
467 host
= new0(struct host_info
, 1);
471 r
= bus_map_all_properties(bus
,
472 "org.freedesktop.hostname1",
473 "/org/freedesktop/hostname1",
478 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error
, r
));
480 r
= bus_map_all_properties(bus
,
481 "org.freedesktop.systemd1",
482 "/org/freedesktop/systemd1",
487 return log_error_errno(r
, "Failed to get host information from systemd: %s", bus_error_message(&error
, r
));
495 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
496 char ts
[FORMAT_TIMESPAN_MAX
];
497 struct boot_times
*t
;
498 static char buf
[4096];
502 usec_t activated_time
= USEC_INFINITY
;
503 _cleanup_free_
char* path
= NULL
, *unit_id
= NULL
;
504 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
506 r
= acquire_boot_times(bus
, &t
);
510 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
514 r
= sd_bus_get_property_string(
516 "org.freedesktop.systemd1",
518 "org.freedesktop.systemd1.Unit",
523 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
527 r
= bus_get_uint64_property(bus
, path
,
528 "org.freedesktop.systemd1.Unit",
529 "ActiveEnterTimestampMonotonic",
532 log_info_errno(r
, "Could not get time to reach default.target. Continuing...");
533 activated_time
= USEC_INFINITY
;
539 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
540 if (t
->firmware_time
)
541 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
543 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
545 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
546 if (t
->initrd_time
> 0)
547 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
549 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
550 if (t
->kernel_time
> 0)
551 strpcpyf(&ptr
, size
, "= %s", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
553 if (unit_id
&& (activated_time
> 0 && activated_time
!= USEC_INFINITY
))
554 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
));
555 else if (unit_id
&& activated_time
== 0)
556 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
557 else if (unit_id
&& activated_time
== USEC_INFINITY
)
558 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.",unit_id
);
560 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
571 static void svg_graph_box(double height
, double begin
, double end
) {
574 /* outside box, fill */
575 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
576 SCALE_X
* (end
- begin
), SCALE_Y
* height
);
578 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+=100000) {
579 /* lines for each second */
580 if (i
% 5000000 == 0)
581 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
582 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
583 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
584 else if (i
% 1000000 == 0)
585 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
586 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
587 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
, SCALE_X
* i
, -5.0, 0.000001 * i
);
589 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
590 SCALE_X
* i
, SCALE_X
* i
, SCALE_Y
* height
);
594 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
595 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
596 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
597 struct unit_times
*times
;
598 struct boot_times
*boot
;
599 int n
, m
= 1, y
= 0, r
;
601 _cleanup_free_
char *pretty_times
= NULL
;
602 struct unit_times
*u
;
604 r
= acquire_bus(true, &bus
);
606 return log_error_errno(r
, "Failed to create bus connection: %m");
608 n
= acquire_boot_times(bus
, &boot
);
612 n
= pretty_boot_time(bus
, &pretty_times
);
616 n
= acquire_host_info(bus
, &host
);
620 n
= acquire_time_data(bus
, ×
);
624 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_start
);
626 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
630 if (boot
->firmware_time
> boot
->loader_time
)
632 if (boot
->loader_time
) {
637 if (boot
->initrd_time
)
639 if (boot
->kernel_time
)
642 for (u
= times
; u
< times
+ n
; u
++) {
643 double text_start
, text_width
;
645 if (u
->activating
< boot
->userspace_time
||
646 u
->activating
> boot
->finish_time
) {
647 u
->name
= mfree(u
->name
);
651 /* If the text cannot fit on the left side then
652 * increase the svg width so it fits on the right.
653 * TODO: calculate the text width more accurately */
654 text_width
= 8.0 * strlen(u
->name
);
655 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
656 if (text_width
> text_start
&& text_width
+ text_start
> width
)
657 width
= text_width
+ text_start
;
659 if (u
->deactivated
> u
->activating
&& u
->deactivated
<= boot
->finish_time
660 && u
->activated
== 0 && u
->deactivating
== 0)
661 u
->activated
= u
->deactivating
= u
->deactivated
;
662 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
663 u
->activated
= boot
->finish_time
;
664 if (u
->deactivating
< u
->activated
|| u
->activated
> boot
->finish_time
)
665 u
->deactivating
= boot
->finish_time
;
666 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
667 u
->deactivated
= boot
->finish_time
;
671 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
672 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
673 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
675 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
676 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
677 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
678 5 * SCALE_Y
/* legend */);
680 /* write some basic info as a comment, including some help */
681 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
682 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
683 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
684 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
685 "<!-- point your browser to this file. -->\n\n"
686 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION
);
689 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
690 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
691 " rect.background { fill: rgb(255,255,255); }\n"
692 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
693 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
694 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
695 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
696 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
697 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
698 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
699 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
700 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
701 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
702 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
703 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
704 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
706 " line.sec5 { stroke-width: 2; }\n"
707 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
708 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
709 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
710 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
711 " text.sec { font-size: 10px; }\n"
712 " ]]>\n </style>\n</defs>\n\n");
714 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
715 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
716 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
717 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
718 strempty(host
->hostname
),
719 strempty(host
->kernel_name
),
720 strempty(host
->kernel_release
),
721 strempty(host
->kernel_version
),
722 strempty(host
->architecture
),
723 strempty(host
->virtualization
));
725 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
726 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
728 if (boot
->firmware_time
) {
729 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
730 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
733 if (boot
->loader_time
) {
734 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
735 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
738 if (boot
->kernel_time
) {
739 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
740 svg_text(true, 0, y
, "kernel");
743 if (boot
->initrd_time
) {
744 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
745 svg_text(true, boot
->initrd_time
, y
, "initrd");
748 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
749 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
750 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
751 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
752 svg_text(true, boot
->userspace_time
, y
, "systemd");
755 for (u
= times
; u
< times
+ n
; u
++) {
756 char ts
[FORMAT_TIMESPAN_MAX
];
762 svg_bar("activating", u
->activating
, u
->activated
, y
);
763 svg_bar("active", u
->activated
, u
->deactivating
, y
);
764 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
766 /* place the text on the left if we have passed the half of the svg width */
767 b
= u
->activating
* SCALE_X
< width
/ 2;
769 svg_text(b
, u
->activating
, y
, "%s (%s)",
770 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
772 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
779 svg("<g transform=\"translate(20,100)\">\n");
781 svg_bar("activating", 0, 300000, y
);
782 svg_text(true, 400000, y
, "Activating");
784 svg_bar("active", 0, 300000, y
);
785 svg_text(true, 400000, y
, "Active");
787 svg_bar("deactivating", 0, 300000, y
);
788 svg_text(true, 400000, y
, "Deactivating");
790 svg_bar("security", 0, 300000, y
);
791 svg_text(true, 400000, y
, "Setting up security module");
793 svg_bar("generators", 0, 300000, y
);
794 svg_text(true, 400000, y
, "Generators");
796 svg_bar("unitsload", 0, 300000, y
);
797 svg_text(true, 400000, y
, "Loading unit files");
804 free_unit_times(times
, (unsigned) n
);
810 static int list_dependencies_print(const char *name
, unsigned int level
, unsigned int branches
,
811 bool last
, struct unit_times
*times
, struct boot_times
*boot
) {
813 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
815 for (i
= level
; i
!= 0; i
--)
816 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? TREE_VERTICAL
: TREE_SPACE
));
818 printf("%s", special_glyph(last
? TREE_RIGHT
: TREE_BRANCH
));
822 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
823 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
824 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
825 else if (times
->activated
> boot
->userspace_time
)
826 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
836 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
837 _cleanup_free_
char *path
= NULL
;
843 path
= unit_dbus_path_from_name(name
);
847 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
850 static Hashmap
*unit_times_hashmap
;
852 static int list_dependencies_compare(const void *_a
, const void *_b
) {
853 const char **a
= (const char**) _a
, **b
= (const char**) _b
;
854 usec_t usa
= 0, usb
= 0;
855 struct unit_times
*times
;
857 times
= hashmap_get(unit_times_hashmap
, *a
);
859 usa
= times
->activated
;
860 times
= hashmap_get(unit_times_hashmap
, *b
);
862 usb
= times
->activated
;
867 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned int level
, char ***units
,
868 unsigned int branches
) {
869 _cleanup_strv_free_
char **deps
= NULL
;
872 usec_t service_longest
= 0;
874 struct unit_times
*times
;
875 struct boot_times
*boot
;
877 if (strv_extend(units
, name
))
880 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
884 qsort_safe(deps
, strv_length(deps
), sizeof (char*), list_dependencies_compare
);
886 r
= acquire_boot_times(bus
, &boot
);
890 STRV_FOREACH(c
, deps
) {
891 times
= hashmap_get(unit_times_hashmap
, *c
);
894 && times
->activated
<= boot
->finish_time
895 && (times
->activated
>= service_longest
896 || service_longest
== 0)) {
897 service_longest
= times
->activated
;
902 if (service_longest
== 0)
905 STRV_FOREACH(c
, deps
) {
906 times
= hashmap_get(unit_times_hashmap
, *c
);
907 if (times
&& times
->activated
&& times
->activated
<= boot
->finish_time
&& (service_longest
- times
->activated
) <= arg_fuzz
)
914 STRV_FOREACH(c
, deps
) {
915 times
= hashmap_get(unit_times_hashmap
, *c
);
918 || times
->activated
> boot
->finish_time
919 || service_longest
- times
->activated
> arg_fuzz
)
924 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
928 if (strv_contains(*units
, *c
)) {
929 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
936 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
,
937 (branches
<< 1) | (to_print
? 1 : 0));
947 static int list_dependencies(sd_bus
*bus
, const char *name
) {
948 _cleanup_strv_free_
char **units
= NULL
;
949 char ts
[FORMAT_TIMESPAN_MAX
];
950 struct unit_times
*times
;
953 _cleanup_free_
char *path
= NULL
;
954 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
955 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
956 struct boot_times
*boot
;
960 path
= unit_dbus_path_from_name(name
);
964 r
= sd_bus_get_property(
966 "org.freedesktop.systemd1",
968 "org.freedesktop.systemd1.Unit",
974 log_error("Failed to get ID: %s", bus_error_message(&error
, -r
));
978 r
= sd_bus_message_read(reply
, "s", &id
);
980 return bus_log_parse_error(r
);
982 times
= hashmap_get(unit_times_hashmap
, id
);
984 r
= acquire_boot_times(bus
, &boot
);
990 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
991 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
992 else if (times
->activated
> boot
->userspace_time
)
993 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
998 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1001 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1002 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1003 struct unit_times
*times
;
1008 r
= acquire_bus(false, &bus
);
1010 return log_error_errno(r
, "Failed to create bus connection: %m");
1012 n
= acquire_time_data(bus
, ×
);
1016 h
= hashmap_new(&string_hash_ops
);
1020 for (i
= 0; i
< (unsigned) n
; i
++) {
1021 r
= hashmap_put(h
, times
[i
].name
, ×
[i
]);
1023 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1025 unit_times_hashmap
= h
;
1027 pager_open(arg_no_pager
, false);
1029 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1030 "The time the unit takes to start is printed after the \"+\" character.\n");
1034 STRV_FOREACH(name
, strv_skip(argv
, 1))
1035 list_dependencies(bus
, *name
);
1037 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1039 h
= hashmap_free(h
);
1040 free_unit_times(times
, (unsigned) n
);
1044 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1045 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1046 struct unit_times
*times
;
1050 r
= acquire_bus(false, &bus
);
1052 return log_error_errno(r
, "Failed to create bus connection: %m");
1054 n
= acquire_time_data(bus
, ×
);
1058 qsort(times
, n
, sizeof(struct unit_times
), compare_unit_time
);
1060 pager_open(arg_no_pager
, false);
1062 for (i
= 0; i
< (unsigned) n
; i
++) {
1063 char ts
[FORMAT_TIMESPAN_MAX
];
1065 if (times
[i
].time
> 0)
1066 printf("%16s %s\n", format_timespan(ts
, sizeof(ts
), times
[i
].time
, USEC_PER_MSEC
), times
[i
].name
);
1069 free_unit_times(times
, (unsigned) n
);
1073 static int analyze_time(int argc
, char *argv
[], void *userdata
) {
1074 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1075 _cleanup_free_
char *buf
= NULL
;
1078 r
= acquire_bus(false, &bus
);
1080 return log_error_errno(r
, "Failed to create bus connection: %m");
1082 r
= pretty_boot_time(bus
, &buf
);
1090 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
[]) {
1091 _cleanup_strv_free_
char **units
= NULL
;
1094 bool match_patterns
;
1100 match_patterns
= strv_fnmatch(patterns
, u
->id
, 0);
1102 if (!strv_isempty(from_patterns
) &&
1104 !strv_fnmatch(from_patterns
, u
->id
, 0))
1107 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1111 STRV_FOREACH(unit
, units
) {
1112 bool match_patterns2
;
1114 match_patterns2
= strv_fnmatch(patterns
, *unit
, 0);
1116 if (!strv_isempty(to_patterns
) &&
1118 !strv_fnmatch(to_patterns
, *unit
, 0))
1121 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1124 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1130 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1136 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1137 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1142 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1143 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1146 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1149 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1152 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1160 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1161 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1165 STRV_FOREACH(pattern
, patterns
) {
1166 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1167 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1169 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1172 if (string_is_glob(*pattern
))
1175 unit
= unit_dbus_path_from_name(*pattern
);
1179 r
= sd_bus_get_property_string(
1181 "org.freedesktop.systemd1",
1183 "org.freedesktop.systemd1.Unit",
1188 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1190 if (!streq(*pattern
, unit_id
)) {
1191 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1196 *ret
= expanded_patterns
;
1197 expanded_patterns
= NULL
; /* do not free */
1202 static int dot(int argc
, char *argv
[], void *userdata
) {
1203 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1204 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1205 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1206 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1207 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1208 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1212 r
= acquire_bus(false, &bus
);
1214 return log_error_errno(r
, "Failed to create bus connection: %m");
1216 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
1220 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1224 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1228 r
= sd_bus_call_method(
1230 "org.freedesktop.systemd1",
1231 "/org/freedesktop/systemd1",
1232 "org.freedesktop.systemd1.Manager",
1238 log_error("Failed to list units: %s", bus_error_message(&error
, -r
));
1242 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1244 return bus_log_parse_error(r
);
1246 printf("digraph systemd {\n");
1248 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1250 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1255 return bus_log_parse_error(r
);
1259 log_info(" Color legend: black = Requires\n"
1260 " dark blue = Requisite\n"
1261 " dark grey = Wants\n"
1262 " red = Conflicts\n"
1263 " green = After\n");
1266 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1267 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1272 static int dump(int argc
, char *argv
[], void *userdata
) {
1273 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1274 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1275 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1276 const char *text
= NULL
;
1279 r
= acquire_bus(false, &bus
);
1281 return log_error_errno(r
, "Failed to create bus connection: %m");
1283 pager_open(arg_no_pager
, false);
1285 r
= sd_bus_call_method(
1287 "org.freedesktop.systemd1",
1288 "/org/freedesktop/systemd1",
1289 "org.freedesktop.systemd1.Manager",
1295 return log_error_errno(r
, "Failed issue method call: %s", bus_error_message(&error
, r
));
1297 r
= sd_bus_message_read(reply
, "s", &text
);
1299 return bus_log_parse_error(r
);
1301 fputs(text
, stdout
);
1305 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1306 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1307 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1313 r
= acquire_bus(false, &bus
);
1315 return log_error_errno(r
, "Failed to create bus connection: %m");
1317 r
= sd_bus_set_property(
1319 "org.freedesktop.systemd1",
1320 "/org/freedesktop/systemd1",
1321 "org.freedesktop.systemd1.Manager",
1327 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1332 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1333 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1334 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1335 _cleanup_free_
char *level
= NULL
;
1338 r
= acquire_bus(false, &bus
);
1340 return log_error_errno(r
, "Failed to create bus connection: %m");
1342 r
= sd_bus_get_property_string(
1344 "org.freedesktop.systemd1",
1345 "/org/freedesktop/systemd1",
1346 "org.freedesktop.systemd1.Manager",
1351 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1357 static int get_or_set_log_level(int argc
, char *argv
[], void *userdata
) {
1358 return (argc
== 1) ? get_log_level(argc
, argv
, userdata
) : set_log_level(argc
, argv
, userdata
);
1361 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1362 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1363 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1369 r
= acquire_bus(false, &bus
);
1371 return log_error_errno(r
, "Failed to create bus connection: %m");
1373 r
= sd_bus_set_property(
1375 "org.freedesktop.systemd1",
1376 "/org/freedesktop/systemd1",
1377 "org.freedesktop.systemd1.Manager",
1383 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1388 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1389 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1390 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1391 _cleanup_free_
char *target
= NULL
;
1394 r
= acquire_bus(false, &bus
);
1396 return log_error_errno(r
, "Failed to create bus connection: %m");
1398 r
= sd_bus_get_property_string(
1400 "org.freedesktop.systemd1",
1401 "/org/freedesktop/systemd1",
1402 "org.freedesktop.systemd1.Manager",
1407 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1413 static int get_or_set_log_target(int argc
, char *argv
[], void *userdata
) {
1414 return (argc
== 1) ? get_log_target(argc
, argv
, userdata
) : set_log_target(argc
, argv
, userdata
);
1418 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1419 const char *syscall
;
1421 printf("%s\n", set
->name
);
1422 printf(" # %s\n", set
->help
);
1423 NULSTR_FOREACH(syscall
, set
->value
)
1424 printf(" %s\n", syscall
);
1427 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1430 pager_open(arg_no_pager
, false);
1432 if (strv_isempty(strv_skip(argv
, 1))) {
1435 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1438 dump_syscall_filter(syscall_filter_sets
+ i
);
1444 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1445 const SyscallFilterSet
*set
;
1450 set
= syscall_filter_set_find(*name
);
1452 /* make sure the error appears below normal output */
1455 log_error("Filter set \"%s\" not found.", *name
);
1459 dump_syscall_filter(set
);
1468 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1469 log_error("Not compiled with syscall filters, sorry.");
1474 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
1479 n
= now(CLOCK_REALTIME
);
1481 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1482 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1483 _cleanup_free_
char *t
= NULL
;
1486 r
= calendar_spec_from_string(*p
, &spec
);
1488 ret
= log_error_errno(r
, "Failed to parse calendar specification '%s': %m", *p
);
1492 r
= calendar_spec_normalize(spec
);
1494 ret
= log_error_errno(r
, "Failed to normalize calendar specification '%s': %m", *p
);
1498 r
= calendar_spec_to_string(spec
, &t
);
1500 ret
= log_error_errno(r
, "Failed to fomat calendar specification '%s': %m", *p
);
1505 printf(" Original form: %s\n", *p
);
1507 printf("Normalized form: %s\n", t
);
1509 r
= calendar_spec_next_usec(spec
, n
, &next
);
1511 printf(" Next elapse: never\n");
1513 ret
= log_error_errno(r
, "Failed to determine next elapse for '%s': %m", *p
);
1516 char buffer
[CONST_MAX(FORMAT_TIMESTAMP_MAX
, FORMAT_TIMESTAMP_RELATIVE_MAX
)];
1518 printf(" Next elapse: %s\n", format_timestamp(buffer
, sizeof(buffer
), next
));
1520 if (!in_utc_timezone())
1521 printf(" (in UTC): %s\n", format_timestamp_utc(buffer
, sizeof(buffer
), next
));
1523 printf(" From now: %s\n", format_timestamp_relative(buffer
, sizeof(buffer
), next
));
1533 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
1534 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1535 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1538 assert(IN_SET(argc
, 1, 2));
1541 r
= acquire_bus(false, &bus
);
1543 return log_error_errno(r
, "Failed to create bus connection: %m");
1545 /* get ServiceWatchdogs */
1547 r
= sd_bus_get_property_trivial(
1549 "org.freedesktop.systemd1",
1550 "/org/freedesktop/systemd1",
1551 "org.freedesktop.systemd1.Manager",
1557 return log_error_errno(r
, "Failed to get service-watchdog state: %s", bus_error_message(&error
, r
));
1559 printf("%s\n", yes_no(!!b
));
1564 /* set ServiceWatchdogs */
1565 b
= parse_boolean(argv
[1]);
1567 log_error("Failed to parse service-watchdogs argument.");
1571 r
= sd_bus_set_property(
1573 "org.freedesktop.systemd1",
1574 "/org/freedesktop/systemd1",
1575 "org.freedesktop.systemd1.Manager",
1581 return log_error_errno(r
, "Failed to set service-watchdog state: %s", bus_error_message(&error
, r
));
1586 static int do_verify(int argc
, char *argv
[], void *userdata
) {
1587 return verify_units(strv_skip(argv
, 1),
1588 arg_user
? UNIT_FILE_USER
: UNIT_FILE_SYSTEM
,
1593 static int help(int argc
, char *argv
[], void *userdata
) {
1595 pager_open(arg_no_pager
, false);
1597 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1598 "Profile systemd, show unit dependencies, check unit files.\n\n"
1599 " -h --help Show this help\n"
1600 " --version Show package version\n"
1601 " --no-pager Do not pipe output into a pager\n"
1602 " --system Operate on system systemd instance\n"
1603 " --user Operate on user systemd instance\n"
1604 " -H --host=[USER@]HOST Operate on remote host\n"
1605 " -M --machine=CONTAINER Operate on local container\n"
1606 " --order Show only order in the graph\n"
1607 " --require Show only requirement in the graph\n"
1608 " --from-pattern=GLOB Show only origins in the graph\n"
1609 " --to-pattern=GLOB Show only destinations in the graph\n"
1610 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1611 " earlier than the latest in the branch\n"
1612 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1613 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
1615 " time Print time spent in the kernel\n"
1616 " blame Print list of running units ordered by time to init\n"
1617 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
1618 " plot Output SVG graphic showing service initialization\n"
1619 " dot [UNIT...] Output dependency graph in man:dot(1) format\n"
1620 " log-level [LEVEL] Get/set logging threshold for manager\n"
1621 " log-target [TARGET] Get/set logging target for manager\n"
1622 " dump Output state serialization of service manager\n"
1623 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1624 " verify FILE... Check unit files for correctness\n"
1625 " calendar SPEC... Validate repetitive calendar time events\n"
1626 " service-watchdogs [BOOL] Get/set service watchdog state\n"
1627 , program_invocation_short_name
);
1629 /* When updating this list, including descriptions, apply
1630 * changes to shell-completion/bash/systemd-analyze and
1631 * shell-completion/zsh/_systemd-analyze too. */
1636 static int parse_argv(int argc
, char *argv
[]) {
1638 ARG_VERSION
= 0x100,
1643 ARG_DOT_FROM_PATTERN
,
1651 static const struct option options
[] = {
1652 { "help", no_argument
, NULL
, 'h' },
1653 { "version", no_argument
, NULL
, ARG_VERSION
},
1654 { "order", no_argument
, NULL
, ARG_ORDER
},
1655 { "require", no_argument
, NULL
, ARG_REQUIRE
},
1656 { "user", no_argument
, NULL
, ARG_USER
},
1657 { "system", no_argument
, NULL
, ARG_SYSTEM
},
1658 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
1659 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
1660 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
1661 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1662 { "man", optional_argument
, NULL
, ARG_MAN
},
1663 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
1664 { "host", required_argument
, NULL
, 'H' },
1665 { "machine", required_argument
, NULL
, 'M' },
1674 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
1678 return help(0, NULL
, NULL
);
1692 arg_dot
= DEP_ORDER
;
1696 arg_dot
= DEP_REQUIRE
;
1699 case ARG_DOT_FROM_PATTERN
:
1700 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
1705 case ARG_DOT_TO_PATTERN
:
1706 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
1712 r
= parse_sec(optarg
, &arg_fuzz
);
1718 arg_no_pager
= true;
1722 arg_transport
= BUS_TRANSPORT_REMOTE
;
1727 arg_transport
= BUS_TRANSPORT_MACHINE
;
1733 r
= parse_boolean(optarg
);
1735 log_error("Failed to parse --man= argument.");
1745 case ARG_GENERATORS
:
1747 r
= parse_boolean(optarg
);
1749 log_error("Failed to parse --generators= argument.");
1753 arg_generators
= !!r
;
1755 arg_generators
= true;
1763 assert_not_reached("Unhandled option code.");
1766 return 1; /* work to do */
1769 int main(int argc
, char *argv
[]) {
1771 static const Verb verbs
[] = {
1772 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
1773 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
1774 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
1775 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
1776 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
1777 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
1778 { "log-level", VERB_ANY
, 2, 0, get_or_set_log_level
},
1779 { "log-target", VERB_ANY
, 2, 0, get_or_set_log_target
},
1780 /* The following four verbs are deprecated aliases */
1781 { "set-log-level", 2, 2, 0, set_log_level
},
1782 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
1783 { "set-log-target", 2, 2, 0, set_log_target
},
1784 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
1785 { "dump", VERB_ANY
, 1, 0, dump
},
1786 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
1787 { "verify", 2, VERB_ANY
, 0, do_verify
},
1788 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
1789 { "service-watchdogs", VERB_ANY
, 2, 0, service_watchdogs
},
1795 setlocale(LC_ALL
, "");
1796 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
1798 log_parse_environment();
1801 r
= parse_argv(argc
, argv
);
1805 r
= dispatch_verb(argc
, argv
, verbs
, NULL
);
1810 strv_free(arg_dot_from_patterns
);
1811 strv_free(arg_dot_to_patterns
);
1813 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;