1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 Copyright © 2013 Simon Peeters
14 #include "alloc-util.h"
15 #include "analyze-condition.h"
16 #include "analyze-security.h"
17 #include "analyze-verify.h"
19 #include "bus-error.h"
20 #include "bus-locator.h"
21 #include "bus-map-properties.h"
22 #include "bus-unit-util.h"
23 #include "calendarspec.h"
25 #include "capability-util.h"
26 #include "conf-files.h"
29 #include "exit-status.h"
32 #include "format-table.h"
33 #include "glob-util.h"
35 #include "locale-util.h"
37 #include "main-func.h"
38 #include "nulstr-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "pretty-print.h"
44 # include "seccomp-util.h"
46 #include "sort-util.h"
50 #include "terminal-util.h"
51 #include "time-util.h"
52 #include "unit-name.h"
56 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
57 #define SCALE_Y (20.0)
59 #define svg(...) printf(__VA_ARGS__)
61 #define svg_bar(class, x1, x2, y) \
62 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
64 SCALE_X * (x1), SCALE_Y * (y), \
65 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
67 #define svg_text(b, x, y, format, ...) \
69 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
70 svg(format, ## __VA_ARGS__); \
79 static char **arg_dot_from_patterns
= NULL
;
80 static char **arg_dot_to_patterns
= NULL
;
81 static usec_t arg_fuzz
= 0;
82 static PagerFlags arg_pager_flags
= 0;
83 static BusTransport arg_transport
= BUS_TRANSPORT_LOCAL
;
84 static const char *arg_host
= NULL
;
85 static UnitFileScope arg_scope
= UNIT_FILE_SYSTEM
;
86 static bool arg_man
= true;
87 static bool arg_generators
= false;
88 static const char *arg_root
= NULL
;
89 static unsigned arg_iterations
= 1;
90 static usec_t arg_base_time
= USEC_INFINITY
;
92 STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns
, strv_freep
);
93 STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns
, strv_freep
);
99 usec_t kernel_done_time
;
101 usec_t userspace_time
;
103 usec_t security_start_time
;
104 usec_t security_finish_time
;
105 usec_t generators_start_time
;
106 usec_t generators_finish_time
;
107 usec_t unitsload_start_time
;
108 usec_t unitsload_finish_time
;
109 usec_t initrd_security_start_time
;
110 usec_t initrd_security_finish_time
;
111 usec_t initrd_generators_start_time
;
112 usec_t initrd_generators_finish_time
;
113 usec_t initrd_unitsload_start_time
;
114 usec_t initrd_unitsload_finish_time
;
117 * If we're analyzing the user instance, all timestamps will be offset
118 * by its own start-up timestamp, which may be arbitrarily big.
119 * With "plot", this causes arbitrarily wide output SVG files which almost
120 * completely consist of empty space. Thus we cancel out this offset.
122 * This offset is subtracted from times above by acquire_boot_times(),
123 * but it still needs to be subtracted from unit-specific timestamps
124 * (so it is stored here for reference).
126 usec_t reverse_offset
;
142 char *kernel_release
;
143 char *kernel_version
;
144 char *os_pretty_name
;
145 char *virtualization
;
149 static int acquire_bus(sd_bus
**bus
, bool *use_full_bus
) {
150 bool user
= arg_scope
!= UNIT_FILE_SYSTEM
;
153 if (use_full_bus
&& *use_full_bus
) {
154 r
= bus_connect_transport(arg_transport
, arg_host
, user
, bus
);
155 if (IN_SET(r
, 0, -EHOSTDOWN
))
158 *use_full_bus
= false;
161 return bus_connect_transport_systemd(arg_transport
, arg_host
, user
, bus
);
164 static int bus_get_uint64_property(sd_bus
*bus
, const char *path
, const char *interface
, const char *property
, uint64_t *val
) {
165 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
174 r
= sd_bus_get_property_trivial(
176 "org.freedesktop.systemd1",
184 return log_error_errno(r
, "Failed to parse reply: %s", bus_error_message(&error
, r
));
189 static int bus_get_unit_property_strv(sd_bus
*bus
, const char *path
, const char *property
, char ***strv
) {
190 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
198 r
= sd_bus_get_property_strv(
200 "org.freedesktop.systemd1",
202 "org.freedesktop.systemd1.Unit",
207 return log_error_errno(r
, "Failed to get unit property %s: %s", property
, bus_error_message(&error
, r
));
212 static int compare_unit_start(const struct unit_times
*a
, const struct unit_times
*b
) {
213 return CMP(a
->activating
, b
->activating
);
216 static void unit_times_free(struct unit_times
*t
) {
217 struct unit_times
*p
;
219 for (p
= t
; p
->has_data
; p
++)
224 DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times
*, unit_times_free
);
226 static void subtract_timestamp(usec_t
*a
, usec_t b
) {
235 static int acquire_boot_times(sd_bus
*bus
, struct boot_times
**bt
) {
236 static const struct bus_properties_map property_map
[] = {
237 { "FirmwareTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, firmware_time
) },
238 { "LoaderTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, loader_time
) },
239 { "KernelTimestamp", "t", NULL
, offsetof(struct boot_times
, kernel_time
) },
240 { "InitRDTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_time
) },
241 { "UserspaceTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, userspace_time
) },
242 { "FinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, finish_time
) },
243 { "SecurityStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, security_start_time
) },
244 { "SecurityFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, security_finish_time
) },
245 { "GeneratorsStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, generators_start_time
) },
246 { "GeneratorsFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, generators_finish_time
) },
247 { "UnitsLoadStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, unitsload_start_time
) },
248 { "UnitsLoadFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, unitsload_finish_time
) },
249 { "InitRDSecurityStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_security_start_time
) },
250 { "InitRDSecurityFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_security_finish_time
) },
251 { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_generators_start_time
) },
252 { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_generators_finish_time
) },
253 { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_unitsload_start_time
) },
254 { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL
, offsetof(struct boot_times
, initrd_unitsload_finish_time
) },
257 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
258 static struct boot_times times
;
259 static bool cached
= false;
265 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
267 r
= bus_map_all_properties(
269 "org.freedesktop.systemd1",
270 "/org/freedesktop/systemd1",
277 return log_error_errno(r
, "Failed to get timestamp properties: %s", bus_error_message(&error
, r
));
279 if (times
.finish_time
<= 0)
280 return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS
),
281 "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64
").\n"
282 "Please try again later.\n"
283 "Hint: Use 'systemctl%s list-jobs' to see active jobs",
285 arg_scope
== UNIT_FILE_SYSTEM
? "" : " --user");
287 if (arg_scope
== UNIT_FILE_SYSTEM
&& times
.security_start_time
> 0) {
288 /* security_start_time is set when systemd is not running under container environment. */
289 if (times
.initrd_time
> 0)
290 times
.kernel_done_time
= times
.initrd_time
;
292 times
.kernel_done_time
= times
.userspace_time
;
295 * User-instance-specific or container-system-specific timestamps processing
296 * (see comment to reverse_offset in struct boot_times).
298 times
.reverse_offset
= times
.userspace_time
;
300 times
.firmware_time
= times
.loader_time
= times
.kernel_time
= times
.initrd_time
=
301 times
.userspace_time
= times
.security_start_time
= times
.security_finish_time
= 0;
303 subtract_timestamp(×
.finish_time
, times
.reverse_offset
);
305 subtract_timestamp(×
.generators_start_time
, times
.reverse_offset
);
306 subtract_timestamp(×
.generators_finish_time
, times
.reverse_offset
);
308 subtract_timestamp(×
.unitsload_start_time
, times
.reverse_offset
);
309 subtract_timestamp(×
.unitsload_finish_time
, times
.reverse_offset
);
319 static void free_host_info(struct host_info
*hi
) {
324 free(hi
->kernel_name
);
325 free(hi
->kernel_release
);
326 free(hi
->kernel_version
);
327 free(hi
->os_pretty_name
);
328 free(hi
->virtualization
);
329 free(hi
->architecture
);
333 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info
*, free_host_info
);
335 static int acquire_time_data(sd_bus
*bus
, struct unit_times
**out
) {
336 static const struct bus_properties_map property_map
[] = {
337 { "InactiveExitTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, activating
) },
338 { "ActiveEnterTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, activated
) },
339 { "ActiveExitTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, deactivating
) },
340 { "InactiveEnterTimestampMonotonic", "t", NULL
, offsetof(struct unit_times
, deactivated
) },
343 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
344 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
345 _cleanup_(unit_times_freep
) struct unit_times
*unit_times
= NULL
;
346 struct boot_times
*boot_times
= NULL
;
347 size_t allocated
= 0, c
= 0;
351 r
= acquire_boot_times(bus
, &boot_times
);
355 r
= bus_call_method(bus
, bus_systemd_mgr
, "ListUnits", &error
, &reply
, NULL
);
357 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
359 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
361 return bus_log_parse_error(r
);
363 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
364 struct unit_times
*t
;
366 if (!GREEDY_REALLOC(unit_times
, allocated
, c
+ 2))
369 unit_times
[c
+ 1].has_data
= false;
373 assert_cc(sizeof(usec_t
) == sizeof(uint64_t));
375 r
= bus_map_all_properties(
377 "org.freedesktop.systemd1",
385 return log_error_errno(r
, "Failed to get timestamp properties of unit %s: %s",
386 u
.id
, bus_error_message(&error
, r
));
388 subtract_timestamp(&t
->activating
, boot_times
->reverse_offset
);
389 subtract_timestamp(&t
->activated
, boot_times
->reverse_offset
);
390 subtract_timestamp(&t
->deactivating
, boot_times
->reverse_offset
);
391 subtract_timestamp(&t
->deactivated
, boot_times
->reverse_offset
);
393 if (t
->activated
>= t
->activating
)
394 t
->time
= t
->activated
- t
->activating
;
395 else if (t
->deactivated
>= t
->activating
)
396 t
->time
= t
->deactivated
- t
->activating
;
400 if (t
->activating
== 0)
403 t
->name
= strdup(u
.id
);
411 return bus_log_parse_error(r
);
413 *out
= TAKE_PTR(unit_times
);
417 static int acquire_host_info(sd_bus
*bus
, struct host_info
**hi
) {
418 static const struct bus_properties_map hostname_map
[] = {
419 { "Hostname", "s", NULL
, offsetof(struct host_info
, hostname
) },
420 { "KernelName", "s", NULL
, offsetof(struct host_info
, kernel_name
) },
421 { "KernelRelease", "s", NULL
, offsetof(struct host_info
, kernel_release
) },
422 { "KernelVersion", "s", NULL
, offsetof(struct host_info
, kernel_version
) },
423 { "OperatingSystemPrettyName", "s", NULL
, offsetof(struct host_info
, os_pretty_name
) },
427 static const struct bus_properties_map manager_map
[] = {
428 { "Virtualization", "s", NULL
, offsetof(struct host_info
, virtualization
) },
429 { "Architecture", "s", NULL
, offsetof(struct host_info
, architecture
) },
433 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
434 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*system_bus
= NULL
;
435 _cleanup_(free_host_infop
) struct host_info
*host
;
438 host
= new0(struct host_info
, 1);
442 if (arg_scope
!= UNIT_FILE_SYSTEM
) {
443 r
= bus_connect_transport(arg_transport
, arg_host
, false, &system_bus
);
445 log_debug_errno(r
, "Failed to connect to system bus, ignoring: %m");
450 r
= bus_map_all_properties(
452 "org.freedesktop.hostname1",
453 "/org/freedesktop/hostname1",
460 log_debug_errno(r
, "Failed to get host information from systemd-hostnamed, ignoring: %s",
461 bus_error_message(&error
, r
));
462 sd_bus_error_free(&error
);
466 r
= bus_map_all_properties(
468 "org.freedesktop.systemd1",
469 "/org/freedesktop/systemd1",
476 return log_error_errno(r
, "Failed to get host information from systemd: %s",
477 bus_error_message(&error
, r
));
479 *hi
= TAKE_PTR(host
);
483 static int pretty_boot_time(sd_bus
*bus
, char **_buf
) {
484 char ts
[FORMAT_TIMESPAN_MAX
];
485 struct boot_times
*t
;
486 static char buf
[4096];
490 usec_t activated_time
= USEC_INFINITY
;
491 _cleanup_free_
char *path
= NULL
, *unit_id
= NULL
;
492 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
494 r
= acquire_boot_times(bus
, &t
);
498 path
= unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET
);
502 r
= sd_bus_get_property_string(
504 "org.freedesktop.systemd1",
506 "org.freedesktop.systemd1.Unit",
511 log_error_errno(r
, "default.target doesn't seem to exist: %s", bus_error_message(&error
, r
));
515 r
= bus_get_uint64_property(bus
, path
,
516 "org.freedesktop.systemd1.Unit",
517 "ActiveEnterTimestampMonotonic",
520 log_info_errno(r
, "Could not get time to reach default.target, ignoring: %m");
521 activated_time
= USEC_INFINITY
;
527 size
= strpcpyf(&ptr
, size
, "Startup finished in ");
528 if (t
->firmware_time
> 0)
529 size
= strpcpyf(&ptr
, size
, "%s (firmware) + ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
- t
->loader_time
, USEC_PER_MSEC
));
530 if (t
->loader_time
> 0)
531 size
= strpcpyf(&ptr
, size
, "%s (loader) + ", format_timespan(ts
, sizeof(ts
), t
->loader_time
, USEC_PER_MSEC
));
532 if (t
->kernel_done_time
> 0)
533 size
= strpcpyf(&ptr
, size
, "%s (kernel) + ", format_timespan(ts
, sizeof(ts
), t
->kernel_done_time
, USEC_PER_MSEC
));
534 if (t
->initrd_time
> 0)
535 size
= strpcpyf(&ptr
, size
, "%s (initrd) + ", format_timespan(ts
, sizeof(ts
), t
->userspace_time
- t
->initrd_time
, USEC_PER_MSEC
));
537 size
= strpcpyf(&ptr
, size
, "%s (userspace) ", format_timespan(ts
, sizeof(ts
), t
->finish_time
- t
->userspace_time
, USEC_PER_MSEC
));
538 if (t
->kernel_done_time
> 0)
539 strpcpyf(&ptr
, size
, "= %s ", format_timespan(ts
, sizeof(ts
), t
->firmware_time
+ t
->finish_time
, USEC_PER_MSEC
));
541 if (unit_id
&& timestamp_is_set(activated_time
)) {
542 usec_t base
= t
->userspace_time
> 0 ? t
->userspace_time
: t
->reverse_offset
;
544 size
= strpcpyf(&ptr
, size
, "\n%s reached after %s in userspace", unit_id
,
545 format_timespan(ts
, sizeof(ts
), activated_time
- base
, USEC_PER_MSEC
));
546 } else if (unit_id
&& activated_time
== 0)
547 size
= strpcpyf(&ptr
, size
, "\n%s was never reached", unit_id
);
548 else if (unit_id
&& activated_time
== USEC_INFINITY
)
549 size
= strpcpyf(&ptr
, size
, "\nCould not get time to reach %s.", unit_id
);
551 size
= strpcpyf(&ptr
, size
, "\ncould not find default.target");
561 static void svg_graph_box(double height
, double begin
, double end
) {
564 /* outside box, fill */
565 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
566 SCALE_X
* (end
- begin
),
569 for (i
= ((long long) (begin
/ 100000)) * 100000; i
<= end
; i
+= 100000) {
570 /* lines for each second */
571 if (i
% 5000000 == 0)
572 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
573 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
580 else if (i
% 1000000 == 0)
581 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
582 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
590 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
597 static int plot_unit_times(struct unit_times
*u
, double width
, int y
) {
598 char ts
[FORMAT_TIMESPAN_MAX
];
604 svg_bar("activating", u
->activating
, u
->activated
, y
);
605 svg_bar("active", u
->activated
, u
->deactivating
, y
);
606 svg_bar("deactivating", u
->deactivating
, u
->deactivated
, y
);
608 /* place the text on the left if we have passed the half of the svg width */
609 b
= u
->activating
* SCALE_X
< width
/ 2;
611 svg_text(b
, u
->activating
, y
, "%s (%s)",
612 u
->name
, format_timespan(ts
, sizeof(ts
), u
->time
, USEC_PER_MSEC
));
614 svg_text(b
, u
->activating
, y
, "%s", u
->name
);
619 static int analyze_plot(int argc
, char *argv
[], void *userdata
) {
620 _cleanup_(free_host_infop
) struct host_info
*host
= NULL
;
621 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
622 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
623 _cleanup_free_
char *pretty_times
= NULL
;
624 bool use_full_bus
= arg_scope
== UNIT_FILE_SYSTEM
;
625 struct boot_times
*boot
;
626 struct unit_times
*u
;
627 int n
, m
= 1, y
= 0, r
;
630 r
= acquire_bus(&bus
, &use_full_bus
);
632 return bus_log_connect_error(r
);
634 n
= acquire_boot_times(bus
, &boot
);
638 n
= pretty_boot_time(bus
, &pretty_times
);
642 if (use_full_bus
|| arg_scope
!= UNIT_FILE_SYSTEM
) {
643 n
= acquire_host_info(bus
, &host
);
648 n
= acquire_time_data(bus
, ×
);
652 typesafe_qsort(times
, n
, compare_unit_start
);
654 width
= SCALE_X
* (boot
->firmware_time
+ boot
->finish_time
);
658 if (boot
->firmware_time
> boot
->loader_time
)
660 if (boot
->loader_time
> 0) {
665 if (boot
->initrd_time
> 0)
667 if (boot
->kernel_done_time
> 0)
670 for (u
= times
; u
->has_data
; u
++) {
671 double text_start
, text_width
;
673 if (u
->activating
> boot
->finish_time
) {
674 u
->name
= mfree(u
->name
);
678 /* If the text cannot fit on the left side then
679 * increase the svg width so it fits on the right.
680 * TODO: calculate the text width more accurately */
681 text_width
= 8.0 * strlen(u
->name
);
682 text_start
= (boot
->firmware_time
+ u
->activating
) * SCALE_X
;
683 if (text_width
> text_start
&& text_width
+ text_start
> width
)
684 width
= text_width
+ text_start
;
686 if (u
->deactivated
> u
->activating
&&
687 u
->deactivated
<= boot
->finish_time
&&
688 u
->activated
== 0 && u
->deactivating
== 0)
689 u
->activated
= u
->deactivating
= u
->deactivated
;
690 if (u
->activated
< u
->activating
|| u
->activated
> boot
->finish_time
)
691 u
->activated
= boot
->finish_time
;
692 if (u
->deactivating
< u
->activated
|| u
->deactivating
> boot
->finish_time
)
693 u
->deactivating
= boot
->finish_time
;
694 if (u
->deactivated
< u
->deactivating
|| u
->deactivated
> boot
->finish_time
)
695 u
->deactivated
= boot
->finish_time
;
699 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
700 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
701 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
703 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
704 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
705 80.0 + width
, 150.0 + (m
* SCALE_Y
) +
706 5 * SCALE_Y
/* legend */);
708 /* write some basic info as a comment, including some help */
709 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
710 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
711 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
712 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
713 "<!-- point your browser to this file. -->\n\n"
714 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", GIT_VERSION
);
717 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
718 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
719 " rect.background { fill: rgb(255,255,255); }\n"
720 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
721 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
722 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
723 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
724 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
725 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
726 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
727 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
728 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
729 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
730 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
731 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
732 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
734 " line.sec5 { stroke-width: 2; }\n"
735 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
736 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
737 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
738 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
739 " text.sec { font-size: 10px; }\n"
740 " ]]>\n </style>\n</defs>\n\n");
742 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
743 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times
);
745 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
746 isempty(host
->os_pretty_name
) ? "Linux" : host
->os_pretty_name
,
747 strempty(host
->hostname
),
748 strempty(host
->kernel_name
),
749 strempty(host
->kernel_release
),
750 strempty(host
->kernel_version
),
751 strempty(host
->architecture
),
752 strempty(host
->virtualization
));
754 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X
* boot
->firmware_time
));
755 svg_graph_box(m
, -(double) boot
->firmware_time
, boot
->finish_time
);
757 if (boot
->firmware_time
> 0) {
758 svg_bar("firmware", -(double) boot
->firmware_time
, -(double) boot
->loader_time
, y
);
759 svg_text(true, -(double) boot
->firmware_time
, y
, "firmware");
762 if (boot
->loader_time
> 0) {
763 svg_bar("loader", -(double) boot
->loader_time
, 0, y
);
764 svg_text(true, -(double) boot
->loader_time
, y
, "loader");
767 if (boot
->kernel_done_time
> 0) {
768 svg_bar("kernel", 0, boot
->kernel_done_time
, y
);
769 svg_text(true, 0, y
, "kernel");
772 if (boot
->initrd_time
> 0) {
773 svg_bar("initrd", boot
->initrd_time
, boot
->userspace_time
, y
);
774 if (boot
->initrd_security_start_time
< boot
->initrd_security_finish_time
)
775 svg_bar("security", boot
->initrd_security_start_time
, boot
->initrd_security_finish_time
, y
);
776 if (boot
->initrd_generators_start_time
< boot
->initrd_generators_finish_time
)
777 svg_bar("generators", boot
->initrd_generators_start_time
, boot
->initrd_generators_finish_time
, y
);
778 if (boot
->initrd_unitsload_start_time
< boot
->initrd_unitsload_finish_time
)
779 svg_bar("unitsload", boot
->initrd_unitsload_start_time
, boot
->initrd_unitsload_finish_time
, y
);
780 svg_text(true, boot
->initrd_time
, y
, "initrd");
784 for (u
= times
; u
->has_data
; u
++) {
785 if (u
->activating
>= boot
->userspace_time
)
788 y
+= plot_unit_times(u
, width
, y
);
791 svg_bar("active", boot
->userspace_time
, boot
->finish_time
, y
);
792 if (boot
->security_start_time
> 0)
793 svg_bar("security", boot
->security_start_time
, boot
->security_finish_time
, y
);
794 svg_bar("generators", boot
->generators_start_time
, boot
->generators_finish_time
, y
);
795 svg_bar("unitsload", boot
->unitsload_start_time
, boot
->unitsload_finish_time
, y
);
796 svg_text(true, boot
->userspace_time
, y
, "systemd");
799 for (; u
->has_data
; u
++)
800 y
+= plot_unit_times(u
, width
, y
);
805 svg("<g transform=\"translate(20,100)\">\n");
807 svg_bar("activating", 0, 300000, y
);
808 svg_text(true, 400000, y
, "Activating");
810 svg_bar("active", 0, 300000, y
);
811 svg_text(true, 400000, y
, "Active");
813 svg_bar("deactivating", 0, 300000, y
);
814 svg_text(true, 400000, y
, "Deactivating");
816 if (boot
->security_start_time
> 0) {
817 svg_bar("security", 0, 300000, y
);
818 svg_text(true, 400000, y
, "Setting up security module");
821 svg_bar("generators", 0, 300000, y
);
822 svg_text(true, 400000, y
, "Generators");
824 svg_bar("unitsload", 0, 300000, y
);
825 svg_text(true, 400000, y
, "Loading unit files");
835 static int list_dependencies_print(
840 struct unit_times
*times
,
841 struct boot_times
*boot
) {
844 char ts
[FORMAT_TIMESPAN_MAX
], ts2
[FORMAT_TIMESPAN_MAX
];
846 for (i
= level
; i
!= 0; i
--)
847 printf("%s", special_glyph(branches
& (1 << (i
-1)) ? SPECIAL_GLYPH_TREE_VERTICAL
: SPECIAL_GLYPH_TREE_SPACE
));
849 printf("%s", special_glyph(last
? SPECIAL_GLYPH_TREE_RIGHT
: SPECIAL_GLYPH_TREE_BRANCH
));
853 printf("%s%s @%s +%s%s", ansi_highlight_red(), name
,
854 format_timespan(ts
, sizeof(ts
), times
->activating
- boot
->userspace_time
, USEC_PER_MSEC
),
855 format_timespan(ts2
, sizeof(ts2
), times
->time
, USEC_PER_MSEC
), ansi_normal());
856 else if (times
->activated
> boot
->userspace_time
)
857 printf("%s @%s", name
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
867 static int list_dependencies_get_dependencies(sd_bus
*bus
, const char *name
, char ***deps
) {
868 _cleanup_free_
char *path
= NULL
;
874 path
= unit_dbus_path_from_name(name
);
878 return bus_get_unit_property_strv(bus
, path
, "After", deps
);
881 static Hashmap
*unit_times_hashmap
;
883 static int list_dependencies_compare(char *const *a
, char *const *b
) {
884 usec_t usa
= 0, usb
= 0;
885 struct unit_times
*times
;
887 times
= hashmap_get(unit_times_hashmap
, *a
);
889 usa
= times
->activated
;
890 times
= hashmap_get(unit_times_hashmap
, *b
);
892 usb
= times
->activated
;
894 return CMP(usb
, usa
);
897 static bool times_in_range(const struct unit_times
*times
, const struct boot_times
*boot
) {
898 return times
&& times
->activated
> 0 && times
->activated
<= boot
->finish_time
;
901 static int list_dependencies_one(sd_bus
*bus
, const char *name
, unsigned level
, char ***units
, unsigned branches
) {
902 _cleanup_strv_free_
char **deps
= NULL
;
905 usec_t service_longest
= 0;
907 struct unit_times
*times
;
908 struct boot_times
*boot
;
910 if (strv_extend(units
, name
))
913 r
= list_dependencies_get_dependencies(bus
, name
, &deps
);
917 typesafe_qsort(deps
, strv_length(deps
), list_dependencies_compare
);
919 r
= acquire_boot_times(bus
, &boot
);
923 STRV_FOREACH(c
, deps
) {
924 times
= hashmap_get(unit_times_hashmap
, *c
);
925 if (times_in_range(times
, boot
) && times
->activated
>= service_longest
)
926 service_longest
= times
->activated
;
929 if (service_longest
== 0)
932 STRV_FOREACH(c
, deps
) {
933 times
= hashmap_get(unit_times_hashmap
, *c
);
934 if (times_in_range(times
, boot
) && service_longest
- times
->activated
<= arg_fuzz
)
941 STRV_FOREACH(c
, deps
) {
942 times
= hashmap_get(unit_times_hashmap
, *c
);
943 if (!times_in_range(times
, boot
) || service_longest
- times
->activated
> arg_fuzz
)
948 r
= list_dependencies_print(*c
, level
, branches
, to_print
== 0, times
, boot
);
952 if (strv_contains(*units
, *c
)) {
953 r
= list_dependencies_print("...", level
+ 1, (branches
<< 1) | (to_print
? 1 : 0),
960 r
= list_dependencies_one(bus
, *c
, level
+ 1, units
, (branches
<< 1) | (to_print
? 1 : 0));
970 static int list_dependencies(sd_bus
*bus
, const char *name
) {
971 _cleanup_strv_free_
char **units
= NULL
;
972 char ts
[FORMAT_TIMESPAN_MAX
];
973 struct unit_times
*times
;
976 _cleanup_free_
char *path
= NULL
;
977 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
978 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
979 struct boot_times
*boot
;
983 path
= unit_dbus_path_from_name(name
);
987 r
= sd_bus_get_property(
989 "org.freedesktop.systemd1",
991 "org.freedesktop.systemd1.Unit",
997 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
999 r
= sd_bus_message_read(reply
, "s", &id
);
1001 return bus_log_parse_error(r
);
1003 times
= hashmap_get(unit_times_hashmap
, id
);
1005 r
= acquire_boot_times(bus
, &boot
);
1011 printf("%s%s +%s%s\n", ansi_highlight_red(), id
,
1012 format_timespan(ts
, sizeof(ts
), times
->time
, USEC_PER_MSEC
), ansi_normal());
1013 else if (times
->activated
> boot
->userspace_time
)
1014 printf("%s @%s\n", id
, format_timespan(ts
, sizeof(ts
), times
->activated
- boot
->userspace_time
, USEC_PER_MSEC
));
1019 return list_dependencies_one(bus
, name
, 0, &units
, 0);
1022 static int analyze_critical_chain(int argc
, char *argv
[], void *userdata
) {
1023 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1024 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
1025 struct unit_times
*u
;
1029 r
= acquire_bus(&bus
, NULL
);
1031 return bus_log_connect_error(r
);
1033 n
= acquire_time_data(bus
, ×
);
1037 h
= hashmap_new(&string_hash_ops
);
1041 for (u
= times
; u
->has_data
; u
++) {
1042 r
= hashmap_put(h
, u
->name
, u
);
1044 return log_error_errno(r
, "Failed to add entry to hashmap: %m");
1046 unit_times_hashmap
= h
;
1048 (void) pager_open(arg_pager_flags
);
1050 puts("The time when unit became active or started is printed after the \"@\" character.\n"
1051 "The time the unit took to start is printed after the \"+\" character.\n");
1055 STRV_FOREACH(name
, strv_skip(argv
, 1))
1056 list_dependencies(bus
, *name
);
1058 list_dependencies(bus
, SPECIAL_DEFAULT_TARGET
);
1060 h
= hashmap_free(h
);
1064 static int analyze_blame(int argc
, char *argv
[], void *userdata
) {
1065 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1066 _cleanup_(unit_times_freep
) struct unit_times
*times
= NULL
;
1067 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1068 struct unit_times
*u
;
1072 r
= acquire_bus(&bus
, NULL
);
1074 return bus_log_connect_error(r
);
1076 n
= acquire_time_data(bus
, ×
);
1080 table
= table_new("time", "unit");
1084 table_set_header(table
, false);
1086 assert_se(cell
= table_get_cell(table
, 0, 0));
1087 r
= table_set_ellipsize_percent(table
, cell
, 100);
1091 r
= table_set_align_percent(table
, cell
, 100);
1095 assert_se(cell
= table_get_cell(table
, 0, 1));
1096 r
= table_set_ellipsize_percent(table
, cell
, 100);
1100 r
= table_set_sort(table
, (size_t) 0, (size_t) SIZE_MAX
);
1104 r
= table_set_reverse(table
, 0, true);
1108 for (u
= times
; u
->has_data
; u
++) {
1112 r
= table_add_many(table
,
1113 TABLE_TIMESPAN_MSEC
, u
->time
,
1114 TABLE_STRING
, u
->name
);
1116 return table_log_add_error(r
);
1119 (void) pager_open(arg_pager_flags
);
1121 return table_print(table
, NULL
);
1124 static int analyze_time(int argc
, char *argv
[], void *userdata
) {
1125 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1126 _cleanup_free_
char *buf
= NULL
;
1129 r
= acquire_bus(&bus
, NULL
);
1131 return bus_log_connect_error(r
);
1133 r
= pretty_boot_time(bus
, &buf
);
1141 static int graph_one_property(
1147 char *from_patterns
[],
1148 char *to_patterns
[]) {
1150 _cleanup_strv_free_
char **units
= NULL
;
1153 bool match_patterns
;
1159 match_patterns
= strv_fnmatch(patterns
, u
->id
);
1161 if (!strv_isempty(from_patterns
) && !match_patterns
&& !strv_fnmatch(from_patterns
, u
->id
))
1164 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
1168 STRV_FOREACH(unit
, units
) {
1169 bool match_patterns2
;
1171 match_patterns2
= strv_fnmatch(patterns
, *unit
);
1173 if (!strv_isempty(to_patterns
) && !match_patterns2
&& !strv_fnmatch(to_patterns
, *unit
))
1176 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
1179 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
1185 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
1191 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
1192 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
1197 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
1198 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
1201 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
1204 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
1207 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
1215 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
1216 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1220 STRV_FOREACH(pattern
, patterns
) {
1221 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1222 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
1224 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
1227 if (string_is_glob(*pattern
))
1230 unit
= unit_dbus_path_from_name(*pattern
);
1234 r
= sd_bus_get_property_string(
1236 "org.freedesktop.systemd1",
1238 "org.freedesktop.systemd1.Unit",
1243 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
1245 if (!streq(*pattern
, unit_id
)) {
1246 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
1251 *ret
= TAKE_PTR(expanded_patterns
); /* do not free */
1256 static int dot(int argc
, char *argv
[], void *userdata
) {
1257 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1258 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1259 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1260 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
1261 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
1262 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
1266 r
= acquire_bus(&bus
, NULL
);
1268 return bus_log_connect_error(r
);
1270 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
1274 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
1278 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
1282 r
= bus_call_method(bus
, bus_systemd_mgr
, "ListUnits", &error
, &reply
, "");
1284 log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
1286 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
1288 return bus_log_parse_error(r
);
1290 printf("digraph systemd {\n");
1292 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
1294 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
1299 return bus_log_parse_error(r
);
1303 log_info(" Color legend: black = Requires\n"
1304 " dark blue = Requisite\n"
1305 " dark grey = Wants\n"
1306 " red = Conflicts\n"
1307 " green = After\n");
1310 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1311 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1316 static int dump_fallback(sd_bus
*bus
) {
1317 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1318 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1319 const char *text
= NULL
;
1324 r
= bus_call_method(bus
, bus_systemd_mgr
, "Dump", &error
, &reply
, NULL
);
1326 return log_error_errno(r
, "Failed to issue method call Dump: %s", bus_error_message(&error
, r
));
1328 r
= sd_bus_message_read(reply
, "s", &text
);
1330 return bus_log_parse_error(r
);
1332 fputs(text
, stdout
);
1336 static int dump(int argc
, char *argv
[], void *userdata
) {
1337 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1338 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1339 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1343 r
= acquire_bus(&bus
, NULL
);
1345 return bus_log_connect_error(r
);
1347 (void) pager_open(arg_pager_flags
);
1349 if (!sd_bus_can_send(bus
, SD_BUS_TYPE_UNIX_FD
))
1350 return dump_fallback(bus
);
1352 r
= bus_call_method(bus
, bus_systemd_mgr
, "DumpByFileDescriptor", &error
, &reply
, NULL
);
1354 /* fall back to Dump if DumpByFileDescriptor is not supported */
1355 if (!IN_SET(r
, -EACCES
, -EBADR
))
1356 return log_error_errno(r
, "Failed to issue method call DumpByFileDescriptor: %s",
1357 bus_error_message(&error
, r
));
1359 return dump_fallback(bus
);
1362 r
= sd_bus_message_read(reply
, "h", &fd
);
1364 return bus_log_parse_error(r
);
1367 return copy_bytes(fd
, STDOUT_FILENO
, (uint64_t) -1, 0);
1370 static int cat_config(int argc
, char *argv
[], void *userdata
) {
1374 (void) pager_open(arg_pager_flags
);
1376 list
= strv_skip(argv
, 1);
1377 STRV_FOREACH(arg
, list
) {
1378 const char *t
= NULL
;
1383 if (path_is_absolute(*arg
)) {
1386 NULSTR_FOREACH(dir
, CONF_PATHS_NULSTR("")) {
1387 t
= path_startswith(*arg
, dir
);
1393 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1394 "Path %s does not start with any known prefix.", *arg
);
1398 r
= conf_files_cat(arg_root
, t
);
1406 static int set_log_level(int argc
, char *argv
[], void *userdata
) {
1407 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1408 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1414 r
= acquire_bus(&bus
, NULL
);
1416 return bus_log_connect_error(r
);
1418 r
= bus_set_property(bus
, bus_systemd_mgr
, "LogLevel", &error
, "s", argv
[1]);
1420 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1425 static int get_log_level(int argc
, char *argv
[], void *userdata
) {
1426 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1427 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1428 _cleanup_free_
char *level
= NULL
;
1431 r
= acquire_bus(&bus
, NULL
);
1433 return bus_log_connect_error(r
);
1435 r
= bus_get_property_string(bus
, bus_systemd_mgr
, "LogLevel", &error
, &level
);
1437 return log_error_errno(r
, "Failed to get log level: %s", bus_error_message(&error
, r
));
1443 static int get_or_set_log_level(int argc
, char *argv
[], void *userdata
) {
1444 return (argc
== 1) ? get_log_level(argc
, argv
, userdata
) : set_log_level(argc
, argv
, userdata
);
1447 static int set_log_target(int argc
, char *argv
[], void *userdata
) {
1448 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1449 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1455 r
= acquire_bus(&bus
, NULL
);
1457 return bus_log_connect_error(r
);
1459 r
= bus_set_property(bus
, bus_systemd_mgr
, "LogTarget", &error
, "s", argv
[1]);
1461 return log_error_errno(r
, "Failed to issue method call: %s", bus_error_message(&error
, r
));
1466 static int get_log_target(int argc
, char *argv
[], void *userdata
) {
1467 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1468 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
1469 _cleanup_free_
char *target
= NULL
;
1472 r
= acquire_bus(&bus
, NULL
);
1474 return bus_log_connect_error(r
);
1476 r
= bus_get_property_string(bus
, bus_systemd_mgr
, "LogTarget", &error
, &target
);
1478 return log_error_errno(r
, "Failed to get log target: %s", bus_error_message(&error
, r
));
1484 static int get_or_set_log_target(int argc
, char *argv
[], void *userdata
) {
1485 return (argc
== 1) ? get_log_target(argc
, argv
, userdata
) : set_log_target(argc
, argv
, userdata
);
1488 static bool strv_fnmatch_strv_or_empty(char* const* patterns
, char **strv
, int flags
) {
1490 STRV_FOREACH(s
, strv
)
1491 if (strv_fnmatch_or_empty(patterns
, *s
, flags
))
1497 static int do_unit_files(int argc
, char *argv
[], void *userdata
) {
1498 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
1499 _cleanup_hashmap_free_ Hashmap
*unit_ids
= NULL
;
1500 _cleanup_hashmap_free_ Hashmap
*unit_names
= NULL
;
1501 char **patterns
= strv_skip(argv
, 1);
1502 const char *k
, *dst
;
1506 r
= lookup_paths_init(&lp
, arg_scope
, 0, NULL
);
1508 return log_error_errno(r
, "lookup_paths_init() failed: %m");
1510 r
= unit_file_build_name_map(&lp
, NULL
, &unit_ids
, &unit_names
, NULL
);
1512 return log_error_errno(r
, "unit_file_build_name_map() failed: %m");
1514 HASHMAP_FOREACH_KEY(dst
, k
, unit_ids
) {
1515 if (!strv_fnmatch_or_empty(patterns
, k
, FNM_NOESCAPE
) &&
1516 !strv_fnmatch_or_empty(patterns
, dst
, FNM_NOESCAPE
))
1519 printf("ids: %s → %s\n", k
, dst
);
1522 HASHMAP_FOREACH_KEY(v
, k
, unit_names
) {
1523 if (!strv_fnmatch_or_empty(patterns
, k
, FNM_NOESCAPE
) &&
1524 !strv_fnmatch_strv_or_empty(patterns
, v
, FNM_NOESCAPE
))
1527 _cleanup_free_
char *j
= strv_join(v
, ", ");
1528 printf("aliases: %s ← %s\n", k
, j
);
1534 static int dump_unit_paths(int argc
, char *argv
[], void *userdata
) {
1535 _cleanup_(lookup_paths_free
) LookupPaths paths
= {};
1539 r
= lookup_paths_init(&paths
, arg_scope
, 0, NULL
);
1541 return log_error_errno(r
, "lookup_paths_init() failed: %m");
1543 STRV_FOREACH(p
, paths
.search_path
)
1549 static int dump_exit_status(int argc
, char *argv
[], void *userdata
) {
1550 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1553 table
= table_new("name", "status", "class");
1557 r
= table_set_align_percent(table
, table_get_cell(table
, 0, 1), 100);
1559 return log_error_errno(r
, "Failed to right-align status: %m");
1561 if (strv_isempty(strv_skip(argv
, 1)))
1562 for (size_t i
= 0; i
< ELEMENTSOF(exit_status_mappings
); i
++) {
1563 if (!exit_status_mappings
[i
].name
)
1566 r
= table_add_many(table
,
1567 TABLE_STRING
, exit_status_mappings
[i
].name
,
1569 TABLE_STRING
, exit_status_class(i
));
1571 return table_log_add_error(r
);
1574 for (int i
= 1; i
< argc
; i
++) {
1577 status
= exit_status_from_string(argv
[i
]);
1579 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Invalid exit status \"%s\".", argv
[i
]);
1581 assert(status
>= 0 && (size_t) status
< ELEMENTSOF(exit_status_mappings
));
1582 r
= table_add_many(table
,
1583 TABLE_STRING
, exit_status_mappings
[status
].name
?: "-",
1585 TABLE_STRING
, exit_status_class(status
) ?: "-");
1587 return table_log_add_error(r
);
1590 (void) pager_open(arg_pager_flags
);
1592 return table_print(table
, NULL
);
1595 static int dump_capabilities(int argc
, char *argv
[], void *userdata
) {
1596 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1600 table
= table_new("name", "number");
1604 (void) table_set_align_percent(table
, table_get_cell(table
, 0, 1), 100);
1606 /* Determine the maximum of the last cap known by the kernel and by us */
1607 last_cap
= MAX((unsigned) CAP_LAST_CAP
, cap_last_cap());
1609 if (strv_isempty(strv_skip(argv
, 1)))
1610 for (unsigned c
= 0; c
<= last_cap
; c
++) {
1611 r
= table_add_many(table
,
1612 TABLE_STRING
, capability_to_name(c
) ?: "cap_???",
1615 return table_log_add_error(r
);
1618 for (int i
= 1; i
< argc
; i
++) {
1621 c
= capability_from_name(argv
[i
]);
1622 if (c
< 0 || (unsigned) c
> last_cap
)
1623 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Capability \"%s\" not known.", argv
[i
]);
1625 r
= table_add_many(table
,
1626 TABLE_STRING
, capability_to_name(c
) ?: "cap_???",
1627 TABLE_UINT
, (unsigned) c
);
1629 return table_log_add_error(r
);
1632 (void) table_set_sort(table
, (size_t) 1, (size_t) -1);
1635 (void) pager_open(arg_pager_flags
);
1637 return table_print(table
, NULL
);
1642 static int load_kernel_syscalls(Set
**ret
) {
1643 _cleanup_set_free_ Set
*syscalls
= NULL
;
1644 _cleanup_fclose_
FILE *f
= NULL
;
1647 /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
1648 * but good enough for analysis purposes. */
1650 f
= fopen("/sys/kernel/tracing/available_events", "re");
1652 /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
1653 * old debugfs mount point works? */
1654 f
= fopen("/sys/kernel/debug/tracing/available_events", "re");
1656 return log_full_errno(IN_SET(errno
, EPERM
, EACCES
, ENOENT
) ? LOG_DEBUG
: LOG_WARNING
, errno
,
1657 "Can't read open tracefs' available_events file: %m");
1661 _cleanup_free_
char *line
= NULL
;
1664 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1666 return log_error_errno(r
, "Failed to read system call list: %m");
1670 e
= startswith(line
, "syscalls:sys_enter_");
1674 /* These are named differently inside the kernel than their external name for historical
1675 * reasons. Let's hide them here. */
1676 if (STR_IN_SET(e
, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
1679 r
= set_put_strdup(&syscalls
, e
);
1681 return log_error_errno(r
, "Failed to add system call to list: %m");
1684 *ret
= TAKE_PTR(syscalls
);
1688 static void syscall_set_remove(Set
*s
, const SyscallFilterSet
*set
) {
1689 const char *syscall
;
1691 NULSTR_FOREACH(syscall
, set
->value
) {
1692 if (syscall
[0] == '@')
1695 free(set_remove(s
, syscall
));
1699 static void dump_syscall_filter(const SyscallFilterSet
*set
) {
1700 const char *syscall
;
1709 NULSTR_FOREACH(syscall
, set
->value
)
1710 printf(" %s%s%s\n", syscall
[0] == '@' ? ansi_underline() : "", syscall
, ansi_normal());
1713 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1716 (void) pager_open(arg_pager_flags
);
1718 if (strv_isempty(strv_skip(argv
, 1))) {
1719 _cleanup_set_free_ Set
*kernel
= NULL
, *known
= NULL
;
1723 NULSTR_FOREACH(sys
, syscall_filter_sets
[SYSCALL_FILTER_SET_KNOWN
].value
)
1724 if (set_put_strdup(&known
, sys
) < 0)
1727 k
= load_kernel_syscalls(&kernel
);
1729 for (i
= 0; i
< _SYSCALL_FILTER_SET_MAX
; i
++) {
1730 const SyscallFilterSet
*set
= syscall_filter_sets
+ i
;
1734 dump_syscall_filter(set
);
1735 syscall_set_remove(kernel
, set
);
1736 if (i
!= SYSCALL_FILTER_SET_KNOWN
)
1737 syscall_set_remove(known
, set
);
1741 if (!set_isempty(known
)) {
1742 _cleanup_free_
char **l
= NULL
;
1746 "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n",
1747 ansi_highlight(), ansi_normal());
1749 l
= set_get_strv(known
);
1755 STRV_FOREACH(syscall
, l
)
1756 printf("# %s\n", *syscall
);
1760 fputc('\n', stdout
);
1762 log_notice_errno(k
, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
1763 } else if (!set_isempty(kernel
)) {
1764 _cleanup_free_
char **l
= NULL
;
1768 "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
1769 ansi_highlight(), ansi_normal());
1771 l
= set_get_strv(kernel
);
1777 STRV_FOREACH(syscall
, l
)
1778 printf("# %s\n", *syscall
);
1783 STRV_FOREACH(name
, strv_skip(argv
, 1)) {
1784 const SyscallFilterSet
*set
;
1789 set
= syscall_filter_set_find(*name
);
1791 /* make sure the error appears below normal output */
1794 return log_error_errno(SYNTHETIC_ERRNO(ENOENT
),
1795 "Filter set \"%s\" not found.", *name
);
1798 dump_syscall_filter(set
);
1807 static int dump_syscall_filters(int argc
, char *argv
[], void *userdata
) {
1808 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
), "Not compiled with syscall filters, sorry.");
1812 static void parsing_hint(const char *p
, bool calendar
, bool timestamp
, bool timespan
) {
1813 if (calendar
&& calendar_spec_from_string(p
, NULL
) >= 0)
1814 log_notice("Hint: this expression is a valid calendar specification. "
1815 "Use 'systemd-analyze calendar \"%s\"' instead?", p
);
1816 if (timestamp
&& parse_timestamp(p
, NULL
) >= 0)
1817 log_notice("Hint: this expression is a valid timestamp. "
1818 "Use 'systemd-analyze timestamp \"%s\"' instead?", p
);
1819 if (timespan
&& parse_time(p
, NULL
, USEC_PER_SEC
) >= 0)
1820 log_notice("Hint: this expression is a valid timespan. "
1821 "Use 'systemd-analyze timespan \"%s\"' instead?", p
);
1824 static int dump_timespan(int argc
, char *argv
[], void *userdata
) {
1825 char **input_timespan
;
1827 STRV_FOREACH(input_timespan
, strv_skip(argv
, 1)) {
1828 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1829 usec_t output_usecs
;
1833 r
= parse_time(*input_timespan
, &output_usecs
, USEC_PER_SEC
);
1835 log_error_errno(r
, "Failed to parse time span '%s': %m", *input_timespan
);
1836 parsing_hint(*input_timespan
, true, true, false);
1840 table
= table_new("name", "value");
1844 table_set_header(table
, false);
1846 assert_se(cell
= table_get_cell(table
, 0, 0));
1847 r
= table_set_ellipsize_percent(table
, cell
, 100);
1851 r
= table_set_align_percent(table
, cell
, 100);
1855 assert_se(cell
= table_get_cell(table
, 0, 1));
1856 r
= table_set_ellipsize_percent(table
, cell
, 100);
1860 r
= table_add_many(table
,
1861 TABLE_STRING
, "Original:",
1862 TABLE_STRING
, *input_timespan
);
1864 return table_log_add_error(r
);
1866 r
= table_add_cell_stringf(table
, NULL
, "%ss:", special_glyph(SPECIAL_GLYPH_MU
));
1868 return table_log_add_error(r
);
1870 r
= table_add_many(table
,
1871 TABLE_UINT64
, output_usecs
,
1872 TABLE_STRING
, "Human:",
1873 TABLE_TIMESPAN
, output_usecs
,
1874 TABLE_SET_COLOR
, ansi_highlight());
1876 return table_log_add_error(r
);
1878 r
= table_print(table
, NULL
);
1882 if (input_timespan
[1])
1886 return EXIT_SUCCESS
;
1889 static int test_timestamp_one(const char *p
) {
1890 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1895 r
= parse_timestamp(p
, &usec
);
1897 log_error_errno(r
, "Failed to parse \"%s\": %m", p
);
1898 parsing_hint(p
, true, false, true);
1902 table
= table_new("name", "value");
1906 table_set_header(table
, false);
1908 assert_se(cell
= table_get_cell(table
, 0, 0));
1909 r
= table_set_ellipsize_percent(table
, cell
, 100);
1913 r
= table_set_align_percent(table
, cell
, 100);
1917 assert_se(cell
= table_get_cell(table
, 0, 1));
1918 r
= table_set_ellipsize_percent(table
, cell
, 100);
1922 r
= table_add_many(table
,
1923 TABLE_STRING
, "Original form:",
1925 TABLE_STRING
, "Normalized form:",
1926 TABLE_TIMESTAMP
, usec
,
1927 TABLE_SET_COLOR
, ansi_highlight_blue());
1929 return table_log_add_error(r
);
1931 if (!in_utc_timezone()) {
1932 r
= table_add_many(table
,
1933 TABLE_STRING
, "(in UTC):",
1934 TABLE_TIMESTAMP_UTC
, usec
);
1936 return table_log_add_error(r
);
1939 r
= table_add_cell(table
, NULL
, TABLE_STRING
, "UNIX seconds:");
1941 return table_log_add_error(r
);
1943 if (usec
% USEC_PER_SEC
== 0)
1944 r
= table_add_cell_stringf(table
, NULL
, "@%"PRI_USEC
,
1945 usec
/ USEC_PER_SEC
);
1947 r
= table_add_cell_stringf(table
, NULL
, "@%"PRI_USEC
".%06"PRI_USEC
"",
1948 usec
/ USEC_PER_SEC
,
1949 usec
% USEC_PER_SEC
);
1953 r
= table_add_many(table
,
1954 TABLE_STRING
, "From now:",
1955 TABLE_TIMESTAMP_RELATIVE
, usec
);
1957 return table_log_add_error(r
);
1959 return table_print(table
, NULL
);
1962 static int test_timestamp(int argc
, char *argv
[], void *userdata
) {
1966 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
1967 r
= test_timestamp_one(*p
);
1968 if (ret
== 0 && r
< 0)
1978 static int test_calendar_one(usec_t n
, const char *p
) {
1979 _cleanup_(calendar_spec_freep
) CalendarSpec
*spec
= NULL
;
1980 _cleanup_(table_unrefp
) Table
*table
= NULL
;
1981 _cleanup_free_
char *t
= NULL
;
1985 r
= calendar_spec_from_string(p
, &spec
);
1987 log_error_errno(r
, "Failed to parse calendar specification '%s': %m", p
);
1988 parsing_hint(p
, false, true, true);
1992 r
= calendar_spec_to_string(spec
, &t
);
1994 return log_error_errno(r
, "Failed to format calendar specification '%s': %m", p
);
1996 table
= table_new("name", "value");
2000 table_set_header(table
, false);
2002 assert_se(cell
= table_get_cell(table
, 0, 0));
2003 r
= table_set_ellipsize_percent(table
, cell
, 100);
2007 r
= table_set_align_percent(table
, cell
, 100);
2011 assert_se(cell
= table_get_cell(table
, 0, 1));
2012 r
= table_set_ellipsize_percent(table
, cell
, 100);
2017 r
= table_add_many(table
,
2018 TABLE_STRING
, "Original form:",
2021 return table_log_add_error(r
);
2024 r
= table_add_many(table
,
2025 TABLE_STRING
, "Normalized form:",
2028 return table_log_add_error(r
);
2030 for (unsigned i
= 0; i
< arg_iterations
; i
++) {
2033 r
= calendar_spec_next_usec(spec
, n
, &next
);
2036 r
= table_add_many(table
,
2037 TABLE_STRING
, "Next elapse:",
2038 TABLE_STRING
, "never",
2039 TABLE_SET_COLOR
, ansi_highlight_yellow());
2041 return table_log_add_error(r
);
2046 return log_error_errno(r
, "Failed to determine next elapse for '%s': %m", p
);
2049 r
= table_add_many(table
,
2050 TABLE_STRING
, "Next elapse:",
2051 TABLE_TIMESTAMP
, next
,
2052 TABLE_SET_COLOR
, ansi_highlight_blue());
2054 return table_log_add_error(r
);
2056 int k
= DECIMAL_STR_WIDTH(i
+ 1);
2063 r
= table_add_cell_stringf(table
, NULL
, "Iter. #%u:", i
+1);
2065 return table_log_add_error(r
);
2067 r
= table_add_many(table
,
2068 TABLE_TIMESTAMP
, next
,
2069 TABLE_SET_COLOR
, ansi_highlight_blue());
2071 return table_log_add_error(r
);
2074 if (!in_utc_timezone()) {
2075 r
= table_add_many(table
,
2076 TABLE_STRING
, "(in UTC):",
2077 TABLE_TIMESTAMP_UTC
, next
);
2079 return table_log_add_error(r
);
2082 r
= table_add_many(table
,
2083 TABLE_STRING
, "From now:",
2084 TABLE_TIMESTAMP_RELATIVE
, next
);
2086 return table_log_add_error(r
);
2091 return table_print(table
, NULL
);
2094 static int test_calendar(int argc
, char *argv
[], void *userdata
) {
2099 if (arg_base_time
!= USEC_INFINITY
)
2102 n
= now(CLOCK_REALTIME
); /* We want to use the same "base" for all expressions */
2104 STRV_FOREACH(p
, strv_skip(argv
, 1)) {
2105 r
= test_calendar_one(n
, *p
);
2106 if (ret
== 0 && r
< 0)
2116 static int service_watchdogs(int argc
, char *argv
[], void *userdata
) {
2117 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2118 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2121 assert(IN_SET(argc
, 1, 2));
2124 r
= acquire_bus(&bus
, NULL
);
2126 return bus_log_connect_error(r
);
2129 /* get ServiceWatchdogs */
2130 r
= bus_get_property_trivial(bus
, bus_systemd_mgr
, "ServiceWatchdogs", &error
, 'b', &b
);
2132 return log_error_errno(r
, "Failed to get service-watchdog state: %s", bus_error_message(&error
, r
));
2134 printf("%s\n", yes_no(!!b
));
2137 /* set ServiceWatchdogs */
2138 b
= parse_boolean(argv
[1]);
2140 return log_error_errno(b
, "Failed to parse service-watchdogs argument: %m");
2142 r
= bus_set_property(bus
, bus_systemd_mgr
, "ServiceWatchdogs", &error
, "b", b
);
2144 return log_error_errno(r
, "Failed to set service-watchdog state: %s", bus_error_message(&error
, r
));
2150 static int do_condition(int argc
, char *argv
[], void *userdata
) {
2151 return verify_conditions(strv_skip(argv
, 1), arg_scope
);
2154 static int do_verify(int argc
, char *argv
[], void *userdata
) {
2155 return verify_units(strv_skip(argv
, 1), arg_scope
, arg_man
, arg_generators
);
2158 static int do_security(int argc
, char *argv
[], void *userdata
) {
2159 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
2162 r
= acquire_bus(&bus
, NULL
);
2164 return bus_log_connect_error(r
);
2166 (void) pager_open(arg_pager_flags
);
2168 return analyze_security(bus
, strv_skip(argv
, 1), 0);
2171 static int help(int argc
, char *argv
[], void *userdata
) {
2172 _cleanup_free_
char *link
= NULL
, *dot_link
= NULL
;
2175 (void) pager_open(arg_pager_flags
);
2177 r
= terminal_urlify_man("systemd-analyze", "1", &link
);
2181 /* Not using terminal_urlify_man() for this, since we don't want the "man page" text suffix in this case. */
2182 r
= terminal_urlify("man:dot(1)", "dot(1)", &dot_link
);
2186 printf("%s [OPTIONS...] COMMAND ...\n\n"
2187 "%sProfile systemd, show unit dependencies, check unit files.%s\n"
2189 " [time] Print time required to boot the machine\n"
2190 " blame Print list of running units ordered by time to init\n"
2191 " critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
2192 " plot Output SVG graphic showing service initialization\n"
2193 " dot [UNIT...] Output dependency graph in %s format\n"
2194 " dump Output state serialization of service manager\n"
2195 " cat-config Show configuration file and drop-ins\n"
2196 " unit-files List files and symlinks for units\n"
2197 " unit-paths List load directories for units\n"
2198 " exit-status [STATUS...] List exit status definitions\n"
2199 " capability [CAP...] List capability definitions\n"
2200 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
2201 " condition CONDITION... Evaluate conditions and asserts\n"
2202 " verify FILE... Check unit files for correctness\n"
2203 " calendar SPEC... Validate repetitive calendar time events\n"
2204 " timestamp TIMESTAMP... Validate a timestamp\n"
2205 " timespan SPAN... Validate a time span\n"
2206 " security [UNIT...] Analyze security of unit\n"
2208 " -h --help Show this help\n"
2209 " --version Show package version\n"
2210 " --no-pager Do not pipe output into a pager\n"
2211 " --system Operate on system systemd instance\n"
2212 " --user Operate on user systemd instance\n"
2213 " --global Operate on global user configuration\n"
2214 " -H --host=[USER@]HOST Operate on remote host\n"
2215 " -M --machine=CONTAINER Operate on local container\n"
2216 " --order Show only order in the graph\n"
2217 " --require Show only requirement in the graph\n"
2218 " --from-pattern=GLOB Show only origins in the graph\n"
2219 " --to-pattern=GLOB Show only destinations in the graph\n"
2220 " --fuzz=SECONDS Also print services which finished SECONDS earlier\n"
2221 " than the latest in the branch\n"
2222 " --man[=BOOL] Do [not] check for existence of man pages\n"
2223 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n"
2224 " --iterations=N Show the specified number of iterations\n"
2225 " --base-time=TIMESTAMP Calculate calendar times relative to specified time\n"
2226 "\nSee the %s for details.\n"
2227 , program_invocation_short_name
2234 /* When updating this list, including descriptions, apply changes to
2235 * shell-completion/bash/systemd-analyze and shell-completion/zsh/_systemd-analyze too. */
2240 static int parse_argv(int argc
, char *argv
[]) {
2242 ARG_VERSION
= 0x100,
2249 ARG_DOT_FROM_PATTERN
,
2259 static const struct option options
[] = {
2260 { "help", no_argument
, NULL
, 'h' },
2261 { "version", no_argument
, NULL
, ARG_VERSION
},
2262 { "order", no_argument
, NULL
, ARG_ORDER
},
2263 { "require", no_argument
, NULL
, ARG_REQUIRE
},
2264 { "root", required_argument
, NULL
, ARG_ROOT
},
2265 { "system", no_argument
, NULL
, ARG_SYSTEM
},
2266 { "user", no_argument
, NULL
, ARG_USER
},
2267 { "global", no_argument
, NULL
, ARG_GLOBAL
},
2268 { "from-pattern", required_argument
, NULL
, ARG_DOT_FROM_PATTERN
},
2269 { "to-pattern", required_argument
, NULL
, ARG_DOT_TO_PATTERN
},
2270 { "fuzz", required_argument
, NULL
, ARG_FUZZ
},
2271 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
2272 { "man", optional_argument
, NULL
, ARG_MAN
},
2273 { "generators", optional_argument
, NULL
, ARG_GENERATORS
},
2274 { "host", required_argument
, NULL
, 'H' },
2275 { "machine", required_argument
, NULL
, 'M' },
2276 { "iterations", required_argument
, NULL
, ARG_ITERATIONS
},
2277 { "base-time", required_argument
, NULL
, ARG_BASE_TIME
},
2286 while ((c
= getopt_long(argc
, argv
, "hH:M:", options
, NULL
)) >= 0)
2290 return help(0, NULL
, NULL
);
2300 arg_scope
= UNIT_FILE_SYSTEM
;
2304 arg_scope
= UNIT_FILE_USER
;
2308 arg_scope
= UNIT_FILE_GLOBAL
;
2312 arg_dot
= DEP_ORDER
;
2316 arg_dot
= DEP_REQUIRE
;
2319 case ARG_DOT_FROM_PATTERN
:
2320 if (strv_extend(&arg_dot_from_patterns
, optarg
) < 0)
2325 case ARG_DOT_TO_PATTERN
:
2326 if (strv_extend(&arg_dot_to_patterns
, optarg
) < 0)
2332 r
= parse_sec(optarg
, &arg_fuzz
);
2338 arg_pager_flags
|= PAGER_DISABLE
;
2342 arg_transport
= BUS_TRANSPORT_REMOTE
;
2347 arg_transport
= BUS_TRANSPORT_MACHINE
;
2353 r
= parse_boolean(optarg
);
2355 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2356 "Failed to parse --man= argument.");
2364 case ARG_GENERATORS
:
2366 r
= parse_boolean(optarg
);
2368 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2369 "Failed to parse --generators= argument.");
2373 arg_generators
= true;
2377 case ARG_ITERATIONS
:
2378 r
= safe_atou(optarg
, &arg_iterations
);
2380 return log_error_errno(r
, "Failed to parse iterations: %s", optarg
);
2385 r
= parse_timestamp(optarg
, &arg_base_time
);
2387 return log_error_errno(r
, "Failed to parse --base-time= parameter: %s", optarg
);
2395 assert_not_reached("Unhandled option code.");
2398 if (arg_scope
== UNIT_FILE_GLOBAL
&&
2399 !STR_IN_SET(argv
[optind
] ?: "time", "dot", "unit-paths", "verify"))
2400 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2401 "Option --global only makes sense with verbs dot, unit-paths, verify.");
2403 if (streq_ptr(argv
[optind
], "cat-config") && arg_scope
== UNIT_FILE_USER
)
2404 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2405 "Option --user is not supported for cat-config right now.");
2407 if (arg_root
&& !streq_ptr(argv
[optind
], "cat-config"))
2408 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2409 "Option --root is only supported for cat-config right now.");
2411 return 1; /* work to do */
2414 static int run(int argc
, char *argv
[]) {
2416 static const Verb verbs
[] = {
2417 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2418 { "time", VERB_ANY
, 1, VERB_DEFAULT
, analyze_time
},
2419 { "blame", VERB_ANY
, 1, 0, analyze_blame
},
2420 { "critical-chain", VERB_ANY
, VERB_ANY
, 0, analyze_critical_chain
},
2421 { "plot", VERB_ANY
, 1, 0, analyze_plot
},
2422 { "dot", VERB_ANY
, VERB_ANY
, 0, dot
},
2423 /* The following seven verbs are deprecated */
2424 { "log-level", VERB_ANY
, 2, 0, get_or_set_log_level
},
2425 { "log-target", VERB_ANY
, 2, 0, get_or_set_log_target
},
2426 { "set-log-level", 2, 2, 0, set_log_level
},
2427 { "get-log-level", VERB_ANY
, 1, 0, get_log_level
},
2428 { "set-log-target", 2, 2, 0, set_log_target
},
2429 { "get-log-target", VERB_ANY
, 1, 0, get_log_target
},
2430 { "service-watchdogs", VERB_ANY
, 2, 0, service_watchdogs
},
2431 { "dump", VERB_ANY
, 1, 0, dump
},
2432 { "cat-config", 2, VERB_ANY
, 0, cat_config
},
2433 { "unit-files", VERB_ANY
, VERB_ANY
, 0, do_unit_files
},
2434 { "unit-paths", 1, 1, 0, dump_unit_paths
},
2435 { "exit-status", VERB_ANY
, VERB_ANY
, 0, dump_exit_status
},
2436 { "syscall-filter", VERB_ANY
, VERB_ANY
, 0, dump_syscall_filters
},
2437 { "capability", VERB_ANY
, VERB_ANY
, 0, dump_capabilities
},
2438 { "condition", 2, VERB_ANY
, 0, do_condition
},
2439 { "verify", 2, VERB_ANY
, 0, do_verify
},
2440 { "calendar", 2, VERB_ANY
, 0, test_calendar
},
2441 { "timestamp", 2, VERB_ANY
, 0, test_timestamp
},
2442 { "timespan", 2, VERB_ANY
, 0, dump_timespan
},
2443 { "security", VERB_ANY
, VERB_ANY
, 0, do_security
},
2449 setlocale(LC_ALL
, "");
2450 setlocale(LC_NUMERIC
, "C"); /* we want to format/parse floats in C style */
2454 r
= parse_argv(argc
, argv
);
2458 return dispatch_verb(argc
, argv
, verbs
, NULL
);
2461 DEFINE_MAIN_FUNCTION(run
);