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