]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze.c
Merge pull request #5420 from OpenDZ/tixxdz/namespace-fixes-v2
[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 &error,
465 host);
466 if (r < 0)
467 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
468
469 r = bus_map_all_properties(bus,
470 "org.freedesktop.systemd1",
471 "/org/freedesktop/systemd1",
472 manager_map,
473 &error,
474 host);
475 if (r < 0)
476 return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
477
478 *hi = host;
479 host = NULL;
480
481 return 0;
482 }
483
484 static int pretty_boot_time(sd_bus *bus, char **_buf) {
485 char ts[FORMAT_TIMESPAN_MAX];
486 struct boot_times *t;
487 static char buf[4096];
488 size_t size;
489 char *ptr;
490 int r;
491
492 r = acquire_boot_times(bus, &t);
493 if (r < 0)
494 return r;
495
496 ptr = buf;
497 size = sizeof(buf);
498
499 size = strpcpyf(&ptr, size, "Startup finished in ");
500 if (t->firmware_time)
501 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
502 if (t->loader_time)
503 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
504 if (t->kernel_time)
505 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
506 if (t->initrd_time > 0)
507 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
508
509 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
510 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
511
512 ptr = strdup(buf);
513 if (!ptr)
514 return log_oom();
515
516 *_buf = ptr;
517 return 0;
518 }
519
520 static void svg_graph_box(double height, double begin, double end) {
521 long long i;
522
523 /* outside box, fill */
524 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
525 SCALE_X * (end - begin), SCALE_Y * height);
526
527 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
528 /* lines for each second */
529 if (i % 5000000 == 0)
530 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
531 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
532 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
533 else if (i % 1000000 == 0)
534 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
535 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
536 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
537 else
538 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
539 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
540 }
541 }
542
543 static int analyze_plot(sd_bus *bus) {
544 _cleanup_(free_host_infop) struct host_info *host = NULL;
545 struct unit_times *times;
546 struct boot_times *boot;
547 int n, m = 1, y=0;
548 double width;
549 _cleanup_free_ char *pretty_times = NULL;
550 struct unit_times *u;
551
552 n = acquire_boot_times(bus, &boot);
553 if (n < 0)
554 return n;
555
556 n = pretty_boot_time(bus, &pretty_times);
557 if (n < 0)
558 return n;
559
560 n = acquire_host_info(bus, &host);
561 if (n < 0)
562 return n;
563
564 n = acquire_time_data(bus, &times);
565 if (n <= 0)
566 return n;
567
568 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
569
570 width = SCALE_X * (boot->firmware_time + boot->finish_time);
571 if (width < 800.0)
572 width = 800.0;
573
574 if (boot->firmware_time > boot->loader_time)
575 m++;
576 if (boot->loader_time) {
577 m++;
578 if (width < 1000.0)
579 width = 1000.0;
580 }
581 if (boot->initrd_time)
582 m++;
583 if (boot->kernel_time)
584 m++;
585
586 for (u = times; u < times + n; u++) {
587 double text_start, text_width;
588
589 if (u->activating < boot->userspace_time ||
590 u->activating > boot->finish_time) {
591 u->name = mfree(u->name);
592 continue;
593 }
594
595 /* If the text cannot fit on the left side then
596 * increase the svg width so it fits on the right.
597 * TODO: calculate the text width more accurately */
598 text_width = 8.0 * strlen(u->name);
599 text_start = (boot->firmware_time + u->activating) * SCALE_X;
600 if (text_width > text_start && text_width + text_start > width)
601 width = text_width + text_start;
602
603 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
604 && u->activated == 0 && u->deactivating == 0)
605 u->activated = u->deactivating = u->deactivated;
606 if (u->activated < u->activating || u->activated > boot->finish_time)
607 u->activated = boot->finish_time;
608 if (u->deactivating < u->activated || u->activated > boot->finish_time)
609 u->deactivating = boot->finish_time;
610 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
611 u->deactivated = boot->finish_time;
612 m++;
613 }
614
615 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
616 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
617 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
618
619 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
620 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
621 80.0 + width, 150.0 + (m * SCALE_Y) +
622 5 * SCALE_Y /* legend */);
623
624 /* write some basic info as a comment, including some help */
625 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
626 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
627 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
628 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
629 "<!-- point your browser to this file. -->\n\n"
630 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION);
631
632 /* style sheet */
633 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
634 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
635 " rect.background { fill: rgb(255,255,255); }\n"
636 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
637 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
638 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
639 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
640 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
641 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
642 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
643 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
644 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
645 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
646 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
647 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
648 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
649 "// line.sec1 { }\n"
650 " line.sec5 { stroke-width: 2; }\n"
651 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
652 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
653 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
654 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
655 " text.sec { font-size: 10px; }\n"
656 " ]]>\n </style>\n</defs>\n\n");
657
658 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
659 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
660 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
661 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
662 strempty(host->hostname),
663 strempty(host->kernel_name),
664 strempty(host->kernel_release),
665 strempty(host->kernel_version),
666 strempty(host->architecture),
667 strempty(host->virtualization));
668
669 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
670 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
671
672 if (boot->firmware_time) {
673 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
674 svg_text(true, -(double) boot->firmware_time, y, "firmware");
675 y++;
676 }
677 if (boot->loader_time) {
678 svg_bar("loader", -(double) boot->loader_time, 0, y);
679 svg_text(true, -(double) boot->loader_time, y, "loader");
680 y++;
681 }
682 if (boot->kernel_time) {
683 svg_bar("kernel", 0, boot->kernel_done_time, y);
684 svg_text(true, 0, y, "kernel");
685 y++;
686 }
687 if (boot->initrd_time) {
688 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
689 svg_text(true, boot->initrd_time, y, "initrd");
690 y++;
691 }
692 svg_bar("active", boot->userspace_time, boot->finish_time, y);
693 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
694 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
695 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
696 svg_text(true, boot->userspace_time, y, "systemd");
697 y++;
698
699 for (u = times; u < times + n; u++) {
700 char ts[FORMAT_TIMESPAN_MAX];
701 bool b;
702
703 if (!u->name)
704 continue;
705
706 svg_bar("activating", u->activating, u->activated, y);
707 svg_bar("active", u->activated, u->deactivating, y);
708 svg_bar("deactivating", u->deactivating, u->deactivated, y);
709
710 /* place the text on the left if we have passed the half of the svg width */
711 b = u->activating * SCALE_X < width / 2;
712 if (u->time)
713 svg_text(b, u->activating, y, "%s (%s)",
714 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
715 else
716 svg_text(b, u->activating, y, "%s", u->name);
717 y++;
718 }
719
720 svg("</g>\n");
721
722 /* Legend */
723 svg("<g transform=\"translate(20,100)\">\n");
724 y++;
725 svg_bar("activating", 0, 300000, y);
726 svg_text(true, 400000, y, "Activating");
727 y++;
728 svg_bar("active", 0, 300000, y);
729 svg_text(true, 400000, y, "Active");
730 y++;
731 svg_bar("deactivating", 0, 300000, y);
732 svg_text(true, 400000, y, "Deactivating");
733 y++;
734 svg_bar("security", 0, 300000, y);
735 svg_text(true, 400000, y, "Setting up security module");
736 y++;
737 svg_bar("generators", 0, 300000, y);
738 svg_text(true, 400000, y, "Generators");
739 y++;
740 svg_bar("unitsload", 0, 300000, y);
741 svg_text(true, 400000, y, "Loading unit files");
742 y++;
743
744 svg("</g>\n\n");
745
746 svg("</svg>\n");
747
748 free_unit_times(times, (unsigned) n);
749
750 n = 0;
751 return n;
752 }
753
754 static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
755 bool last, struct unit_times *times, struct boot_times *boot) {
756 unsigned int i;
757 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
758
759 for (i = level; i != 0; i--)
760 printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
761
762 printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
763
764 if (times) {
765 if (times->time)
766 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
767 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
768 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
769 else if (times->activated > boot->userspace_time)
770 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
771 else
772 printf("%s", name);
773 } else
774 printf("%s", name);
775 printf("\n");
776
777 return 0;
778 }
779
780 static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
781 _cleanup_free_ char *path = NULL;
782
783 assert(bus);
784 assert(name);
785 assert(deps);
786
787 path = unit_dbus_path_from_name(name);
788 if (path == NULL)
789 return -ENOMEM;
790
791 return bus_get_unit_property_strv(bus, path, "After", deps);
792 }
793
794 static Hashmap *unit_times_hashmap;
795
796 static int list_dependencies_compare(const void *_a, const void *_b) {
797 const char **a = (const char**) _a, **b = (const char**) _b;
798 usec_t usa = 0, usb = 0;
799 struct unit_times *times;
800
801 times = hashmap_get(unit_times_hashmap, *a);
802 if (times)
803 usa = times->activated;
804 times = hashmap_get(unit_times_hashmap, *b);
805 if (times)
806 usb = times->activated;
807
808 return usb - usa;
809 }
810
811 static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
812 unsigned int branches) {
813 _cleanup_strv_free_ char **deps = NULL;
814 char **c;
815 int r = 0;
816 usec_t service_longest = 0;
817 int to_print = 0;
818 struct unit_times *times;
819 struct boot_times *boot;
820
821 if (strv_extend(units, name))
822 return log_oom();
823
824 r = list_dependencies_get_dependencies(bus, name, &deps);
825 if (r < 0)
826 return r;
827
828 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
829
830 r = acquire_boot_times(bus, &boot);
831 if (r < 0)
832 return r;
833
834 STRV_FOREACH(c, deps) {
835 times = hashmap_get(unit_times_hashmap, *c);
836 if (times
837 && times->activated
838 && times->activated <= boot->finish_time
839 && (times->activated >= service_longest
840 || service_longest == 0)) {
841 service_longest = times->activated;
842 break;
843 }
844 }
845
846 if (service_longest == 0 )
847 return r;
848
849 STRV_FOREACH(c, deps) {
850 times = hashmap_get(unit_times_hashmap, *c);
851 if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz)
852 to_print++;
853 }
854
855 if (!to_print)
856 return r;
857
858 STRV_FOREACH(c, deps) {
859 times = hashmap_get(unit_times_hashmap, *c);
860 if (!times
861 || !times->activated
862 || times->activated > boot->finish_time
863 || service_longest - times->activated > arg_fuzz)
864 continue;
865
866 to_print--;
867
868 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
869 if (r < 0)
870 return r;
871
872 if (strv_contains(*units, *c)) {
873 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
874 true, NULL, boot);
875 if (r < 0)
876 return r;
877 continue;
878 }
879
880 r = list_dependencies_one(bus, *c, level + 1, units,
881 (branches << 1) | (to_print ? 1 : 0));
882 if (r < 0)
883 return r;
884
885 if (!to_print)
886 break;
887 }
888 return 0;
889 }
890
891 static int list_dependencies(sd_bus *bus, const char *name) {
892 _cleanup_strv_free_ char **units = NULL;
893 char ts[FORMAT_TIMESPAN_MAX];
894 struct unit_times *times;
895 int r;
896 const char *id;
897 _cleanup_free_ char *path = NULL;
898 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
899 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
900 struct boot_times *boot;
901
902 assert(bus);
903
904 path = unit_dbus_path_from_name(name);
905 if (path == NULL)
906 return -ENOMEM;
907
908 r = sd_bus_get_property(
909 bus,
910 "org.freedesktop.systemd1",
911 path,
912 "org.freedesktop.systemd1.Unit",
913 "Id",
914 &error,
915 &reply,
916 "s");
917 if (r < 0) {
918 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
919 return r;
920 }
921
922 r = sd_bus_message_read(reply, "s", &id);
923 if (r < 0)
924 return bus_log_parse_error(r);
925
926 times = hashmap_get(unit_times_hashmap, id);
927
928 r = acquire_boot_times(bus, &boot);
929 if (r < 0)
930 return r;
931
932 if (times) {
933 if (times->time)
934 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
935 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
936 else if (times->activated > boot->userspace_time)
937 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
938 else
939 printf("%s\n", id);
940 }
941
942 return list_dependencies_one(bus, name, 0, &units, 0);
943 }
944
945 static int analyze_critical_chain(sd_bus *bus, char *names[]) {
946 struct unit_times *times;
947 unsigned int i;
948 Hashmap *h;
949 int n, r;
950
951 n = acquire_time_data(bus, &times);
952 if (n <= 0)
953 return n;
954
955 h = hashmap_new(&string_hash_ops);
956 if (!h)
957 return -ENOMEM;
958
959 for (i = 0; i < (unsigned)n; i++) {
960 r = hashmap_put(h, times[i].name, &times[i]);
961 if (r < 0)
962 return r;
963 }
964 unit_times_hashmap = h;
965
966 pager_open(arg_no_pager, false);
967
968 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
969 "The time the unit takes to start is printed after the \"+\" character.\n");
970
971 if (!strv_isempty(names)) {
972 char **name;
973 STRV_FOREACH(name, names)
974 list_dependencies(bus, *name);
975 } else
976 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
977
978 hashmap_free(h);
979 free_unit_times(times, (unsigned) n);
980 return 0;
981 }
982
983 static int analyze_blame(sd_bus *bus) {
984 struct unit_times *times;
985 unsigned i;
986 int n;
987
988 n = acquire_time_data(bus, &times);
989 if (n <= 0)
990 return n;
991
992 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
993
994 pager_open(arg_no_pager, false);
995
996 for (i = 0; i < (unsigned) n; i++) {
997 char ts[FORMAT_TIMESPAN_MAX];
998
999 if (times[i].time > 0)
1000 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
1001 }
1002
1003 free_unit_times(times, (unsigned) n);
1004 return 0;
1005 }
1006
1007 static int analyze_time(sd_bus *bus) {
1008 _cleanup_free_ char *buf = NULL;
1009 int r;
1010
1011 r = pretty_boot_time(bus, &buf);
1012 if (r < 0)
1013 return r;
1014
1015 puts(buf);
1016 return 0;
1017 }
1018
1019 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[]) {
1020 _cleanup_strv_free_ char **units = NULL;
1021 char **unit;
1022 int r;
1023 bool match_patterns;
1024
1025 assert(u);
1026 assert(prop);
1027 assert(color);
1028
1029 match_patterns = strv_fnmatch(patterns, u->id, 0);
1030
1031 if (!strv_isempty(from_patterns) &&
1032 !match_patterns &&
1033 !strv_fnmatch(from_patterns, u->id, 0))
1034 return 0;
1035
1036 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
1037 if (r < 0)
1038 return r;
1039
1040 STRV_FOREACH(unit, units) {
1041 bool match_patterns2;
1042
1043 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
1044
1045 if (!strv_isempty(to_patterns) &&
1046 !match_patterns2 &&
1047 !strv_fnmatch(to_patterns, *unit, 0))
1048 continue;
1049
1050 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1051 continue;
1052
1053 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1054 }
1055
1056 return 0;
1057 }
1058
1059 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1060 int r;
1061
1062 assert(bus);
1063 assert(u);
1064
1065 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
1066 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
1067 if (r < 0)
1068 return r;
1069 }
1070
1071 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
1072 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
1073 if (r < 0)
1074 return r;
1075 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
1076 if (r < 0)
1077 return r;
1078 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1079 if (r < 0)
1080 return r;
1081 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1082 if (r < 0)
1083 return r;
1084 }
1085
1086 return 0;
1087 }
1088
1089 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1090 _cleanup_strv_free_ char **expanded_patterns = NULL;
1091 char **pattern;
1092 int r;
1093
1094 STRV_FOREACH(pattern, patterns) {
1095 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1096 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1097
1098 if (strv_extend(&expanded_patterns, *pattern) < 0)
1099 return log_oom();
1100
1101 if (string_is_glob(*pattern))
1102 continue;
1103
1104 unit = unit_dbus_path_from_name(*pattern);
1105 if (!unit)
1106 return log_oom();
1107
1108 r = sd_bus_get_property_string(
1109 bus,
1110 "org.freedesktop.systemd1",
1111 unit,
1112 "org.freedesktop.systemd1.Unit",
1113 "Id",
1114 &error,
1115 &unit_id);
1116 if (r < 0)
1117 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1118
1119 if (!streq(*pattern, unit_id)) {
1120 if (strv_extend(&expanded_patterns, unit_id) < 0)
1121 return log_oom();
1122 }
1123 }
1124
1125 *ret = expanded_patterns;
1126 expanded_patterns = NULL; /* do not free */
1127
1128 return 0;
1129 }
1130
1131 static int dot(sd_bus *bus, char* patterns[]) {
1132 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1133 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1134 _cleanup_strv_free_ char **expanded_patterns = NULL;
1135 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1136 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1137 int r;
1138 UnitInfo u;
1139
1140 r = expand_patterns(bus, patterns, &expanded_patterns);
1141 if (r < 0)
1142 return r;
1143
1144 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1145 if (r < 0)
1146 return r;
1147
1148 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1149 if (r < 0)
1150 return r;
1151
1152 r = sd_bus_call_method(
1153 bus,
1154 "org.freedesktop.systemd1",
1155 "/org/freedesktop/systemd1",
1156 "org.freedesktop.systemd1.Manager",
1157 "ListUnits",
1158 &error,
1159 &reply,
1160 "");
1161 if (r < 0) {
1162 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1163 return r;
1164 }
1165
1166 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1167 if (r < 0)
1168 return bus_log_parse_error(r);
1169
1170 printf("digraph systemd {\n");
1171
1172 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1173
1174 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1175 if (r < 0)
1176 return r;
1177 }
1178 if (r < 0)
1179 return bus_log_parse_error(r);
1180
1181 printf("}\n");
1182
1183 log_info(" Color legend: black = Requires\n"
1184 " dark blue = Requisite\n"
1185 " dark grey = Wants\n"
1186 " red = Conflicts\n"
1187 " green = After\n");
1188
1189 if (on_tty())
1190 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1191 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1192
1193 return 0;
1194 }
1195
1196 static int dump(sd_bus *bus, char **args) {
1197 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1198 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1199 const char *text = NULL;
1200 int r;
1201
1202 if (!strv_isempty(args)) {
1203 log_error("Too many arguments.");
1204 return -E2BIG;
1205 }
1206
1207 pager_open(arg_no_pager, false);
1208
1209 r = sd_bus_call_method(
1210 bus,
1211 "org.freedesktop.systemd1",
1212 "/org/freedesktop/systemd1",
1213 "org.freedesktop.systemd1.Manager",
1214 "Dump",
1215 &error,
1216 &reply,
1217 "");
1218 if (r < 0)
1219 return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
1220
1221 r = sd_bus_message_read(reply, "s", &text);
1222 if (r < 0)
1223 return bus_log_parse_error(r);
1224
1225 fputs(text, stdout);
1226 return 0;
1227 }
1228
1229 static int set_log_level(sd_bus *bus, char **args) {
1230 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1231 int r;
1232
1233 assert(bus);
1234 assert(args);
1235
1236 if (strv_length(args) != 1) {
1237 log_error("This command expects one argument only.");
1238 return -E2BIG;
1239 }
1240
1241 r = sd_bus_set_property(
1242 bus,
1243 "org.freedesktop.systemd1",
1244 "/org/freedesktop/systemd1",
1245 "org.freedesktop.systemd1.Manager",
1246 "LogLevel",
1247 &error,
1248 "s",
1249 args[0]);
1250 if (r < 0)
1251 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1252
1253 return 0;
1254 }
1255
1256 static int set_log_target(sd_bus *bus, char **args) {
1257 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1258 int r;
1259
1260 assert(bus);
1261 assert(args);
1262
1263 if (strv_length(args) != 1) {
1264 log_error("This command expects one argument only.");
1265 return -E2BIG;
1266 }
1267
1268 r = sd_bus_set_property(
1269 bus,
1270 "org.freedesktop.systemd1",
1271 "/org/freedesktop/systemd1",
1272 "org.freedesktop.systemd1.Manager",
1273 "LogTarget",
1274 &error,
1275 "s",
1276 args[0]);
1277 if (r < 0)
1278 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1279
1280 return 0;
1281 }
1282
1283 #ifdef HAVE_SECCOMP
1284 static void dump_syscall_filter(const SyscallFilterSet *set) {
1285 const char *syscall;
1286
1287 printf("%s\n", set->name);
1288 printf(" # %s\n", set->help);
1289 NULSTR_FOREACH(syscall, set->value)
1290 printf(" %s\n", syscall);
1291 }
1292
1293 static int dump_syscall_filters(char** names) {
1294 bool first = true;
1295
1296 pager_open(arg_no_pager, false);
1297
1298 if (strv_isempty(names)) {
1299 int i;
1300
1301 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1302 if (!first)
1303 puts("");
1304 dump_syscall_filter(syscall_filter_sets + i);
1305 first = false;
1306 }
1307 } else {
1308 char **name;
1309
1310 STRV_FOREACH(name, names) {
1311 const SyscallFilterSet *set;
1312
1313 if (!first)
1314 puts("");
1315
1316 set = syscall_filter_set_find(*name);
1317 if (!set) {
1318 /* make sure the error appears below normal output */
1319 fflush(stdout);
1320
1321 log_error("Filter set \"%s\" not found.", *name);
1322 return -ENOENT;
1323 }
1324
1325 dump_syscall_filter(set);
1326 first = false;
1327 }
1328 }
1329
1330 return 0;
1331 }
1332
1333 #else
1334 static int dump_syscall_filters(char** names) {
1335 log_error("Not compiled with syscall filters, sorry.");
1336 return -EOPNOTSUPP;
1337 }
1338 #endif
1339
1340 static void help(void) {
1341
1342 pager_open(arg_no_pager, false);
1343
1344 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1345 "Profile systemd, show unit dependencies, check unit files.\n\n"
1346 " -h --help Show this help\n"
1347 " --version Show package version\n"
1348 " --no-pager Do not pipe output into a pager\n"
1349 " --system Operate on system systemd instance\n"
1350 " --user Operate on user systemd instance\n"
1351 " -H --host=[USER@]HOST Operate on remote host\n"
1352 " -M --machine=CONTAINER Operate on local container\n"
1353 " --order Show only order in the graph\n"
1354 " --require Show only requirement in the graph\n"
1355 " --from-pattern=GLOB Show only origins in the graph\n"
1356 " --to-pattern=GLOB Show only destinations in the graph\n"
1357 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1358 " earlier than the latest in the branch\n"
1359 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1360 "Commands:\n"
1361 " time Print time spent in the kernel\n"
1362 " blame Print list of running units ordered by time to init\n"
1363 " critical-chain Print a tree of the time critical chain of units\n"
1364 " plot Output SVG graphic showing service initialization\n"
1365 " dot Output dependency graph in man:dot(1) format\n"
1366 " set-log-level LEVEL Set logging threshold for manager\n"
1367 " set-log-target TARGET Set logging target for manager\n"
1368 " dump Output state serialization of service manager\n"
1369 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1370 " verify FILE... Check unit files for correctness\n"
1371 , program_invocation_short_name);
1372
1373 /* When updating this list, including descriptions, apply
1374 * changes to shell-completion/bash/systemd-analyze and
1375 * shell-completion/zsh/_systemd-analyze too. */
1376 }
1377
1378 static int parse_argv(int argc, char *argv[]) {
1379 enum {
1380 ARG_VERSION = 0x100,
1381 ARG_ORDER,
1382 ARG_REQUIRE,
1383 ARG_USER,
1384 ARG_SYSTEM,
1385 ARG_DOT_FROM_PATTERN,
1386 ARG_DOT_TO_PATTERN,
1387 ARG_FUZZ,
1388 ARG_NO_PAGER,
1389 ARG_MAN,
1390 };
1391
1392 static const struct option options[] = {
1393 { "help", no_argument, NULL, 'h' },
1394 { "version", no_argument, NULL, ARG_VERSION },
1395 { "order", no_argument, NULL, ARG_ORDER },
1396 { "require", no_argument, NULL, ARG_REQUIRE },
1397 { "user", no_argument, NULL, ARG_USER },
1398 { "system", no_argument, NULL, ARG_SYSTEM },
1399 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1400 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1401 { "fuzz", required_argument, NULL, ARG_FUZZ },
1402 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1403 { "man", optional_argument, NULL, ARG_MAN },
1404 { "host", required_argument, NULL, 'H' },
1405 { "machine", required_argument, NULL, 'M' },
1406 {}
1407 };
1408
1409 int r, c;
1410
1411 assert(argc >= 0);
1412 assert(argv);
1413
1414 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1415 switch (c) {
1416
1417 case 'h':
1418 help();
1419 return 0;
1420
1421 case ARG_VERSION:
1422 return version();
1423
1424 case ARG_USER:
1425 arg_user = true;
1426 break;
1427
1428 case ARG_SYSTEM:
1429 arg_user = false;
1430 break;
1431
1432 case ARG_ORDER:
1433 arg_dot = DEP_ORDER;
1434 break;
1435
1436 case ARG_REQUIRE:
1437 arg_dot = DEP_REQUIRE;
1438 break;
1439
1440 case ARG_DOT_FROM_PATTERN:
1441 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1442 return log_oom();
1443
1444 break;
1445
1446 case ARG_DOT_TO_PATTERN:
1447 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1448 return log_oom();
1449
1450 break;
1451
1452 case ARG_FUZZ:
1453 r = parse_sec(optarg, &arg_fuzz);
1454 if (r < 0)
1455 return r;
1456 break;
1457
1458 case ARG_NO_PAGER:
1459 arg_no_pager = true;
1460 break;
1461
1462 case 'H':
1463 arg_transport = BUS_TRANSPORT_REMOTE;
1464 arg_host = optarg;
1465 break;
1466
1467 case 'M':
1468 arg_transport = BUS_TRANSPORT_MACHINE;
1469 arg_host = optarg;
1470 break;
1471
1472 case ARG_MAN:
1473 if (optarg) {
1474 r = parse_boolean(optarg);
1475 if (r < 0) {
1476 log_error("Failed to parse --man= argument.");
1477 return -EINVAL;
1478 }
1479
1480 arg_man = !!r;
1481 } else
1482 arg_man = true;
1483
1484 break;
1485
1486 case '?':
1487 return -EINVAL;
1488
1489 default:
1490 assert_not_reached("Unhandled option code.");
1491 }
1492
1493 return 1; /* work to do */
1494 }
1495
1496 int main(int argc, char *argv[]) {
1497 int r;
1498
1499 setlocale(LC_ALL, "");
1500 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1501 log_parse_environment();
1502 log_open();
1503
1504 r = parse_argv(argc, argv);
1505 if (r <= 0)
1506 goto finish;
1507
1508 if (streq_ptr(argv[optind], "verify"))
1509 r = verify_units(argv+optind+1,
1510 arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
1511 arg_man);
1512 else {
1513 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1514
1515 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1516 if (r < 0) {
1517 log_error_errno(r, "Failed to create bus connection: %m");
1518 goto finish;
1519 }
1520
1521 if (!argv[optind] || streq(argv[optind], "time"))
1522 r = analyze_time(bus);
1523 else if (streq(argv[optind], "blame"))
1524 r = analyze_blame(bus);
1525 else if (streq(argv[optind], "critical-chain"))
1526 r = analyze_critical_chain(bus, argv+optind+1);
1527 else if (streq(argv[optind], "plot"))
1528 r = analyze_plot(bus);
1529 else if (streq(argv[optind], "dot"))
1530 r = dot(bus, argv+optind+1);
1531 else if (streq(argv[optind], "dump"))
1532 r = dump(bus, argv+optind+1);
1533 else if (streq(argv[optind], "set-log-level"))
1534 r = set_log_level(bus, argv+optind+1);
1535 else if (streq(argv[optind], "set-log-target"))
1536 r = set_log_target(bus, argv+optind+1);
1537 else if (streq(argv[optind], "syscall-filter"))
1538 r = dump_syscall_filters(argv+optind+1);
1539 else
1540 log_error("Unknown operation '%s'.", argv[optind]);
1541 }
1542
1543 finish:
1544 pager_close();
1545
1546 strv_free(arg_dot_from_patterns);
1547 strv_free(arg_dot_to_patterns);
1548
1549 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1550 }