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