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