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