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