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