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