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