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