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