]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze.c
Merge pull request #1501 from fbuihuu/fix-requires-mounts-for-directives
[thirdparty/systemd.git] / src / analyze / analyze.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010-2013 Lennart Poettering
7 Copyright 2013 Simon Peeters
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <getopt.h>
24 #include <locale.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #include "sd-bus.h"
29
30 #include "analyze-verify.h"
31 #include "bus-error.h"
32 #include "bus-util.h"
33 #include "hashmap.h"
34 #include "log.h"
35 #include "pager.h"
36 #include "special.h"
37 #include "strv.h"
38 #include "strxcpyx.h"
39 #include "terminal-util.h"
40 #include "unit-name.h"
41 #include "util.h"
42
43 #define SCALE_X (0.1 / 1000.0) /* pixels per us */
44 #define SCALE_Y (20.0)
45
46 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
47
48 #define svg(...) printf(__VA_ARGS__)
49
50 #define svg_bar(class, x1, x2, y) \
51 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
52 (class), \
53 SCALE_X * (x1), SCALE_Y * (y), \
54 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
55
56 #define svg_text(b, x, y, format, ...) \
57 do { \
58 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
59 svg(format, ## __VA_ARGS__); \
60 svg("</text>\n"); \
61 } while(false)
62
63 static enum dot {
64 DEP_ALL,
65 DEP_ORDER,
66 DEP_REQUIRE
67 } arg_dot = DEP_ALL;
68 static char** arg_dot_from_patterns = NULL;
69 static char** arg_dot_to_patterns = NULL;
70 static usec_t arg_fuzz = 0;
71 static bool arg_no_pager = false;
72 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
73 static char *arg_host = NULL;
74 static bool arg_user = false;
75 static bool arg_man = true;
76
77 struct boot_times {
78 usec_t firmware_time;
79 usec_t loader_time;
80 usec_t kernel_time;
81 usec_t kernel_done_time;
82 usec_t initrd_time;
83 usec_t userspace_time;
84 usec_t finish_time;
85 usec_t security_start_time;
86 usec_t security_finish_time;
87 usec_t generators_start_time;
88 usec_t generators_finish_time;
89 usec_t unitsload_start_time;
90 usec_t unitsload_finish_time;
91
92 /*
93 * If we're analyzing the user instance, all timestamps will be offset
94 * by its own start-up timestamp, which may be arbitrarily big.
95 * With "plot", this causes arbitrarily wide output SVG files which almost
96 * completely consist of empty space. Thus we cancel out this offset.
97 *
98 * This offset is subtracted from times above by acquire_boot_times(),
99 * but it still needs to be subtracted from unit-specific timestamps
100 * (so it is stored here for reference).
101 */
102 usec_t reverse_offset;
103 };
104
105 struct unit_times {
106 char *name;
107 usec_t activating;
108 usec_t activated;
109 usec_t deactivated;
110 usec_t deactivating;
111 usec_t time;
112 };
113
114 struct host_info {
115 char *hostname;
116 char *kernel_name;
117 char *kernel_release;
118 char *kernel_version;
119 char *os_pretty_name;
120 char *virtualization;
121 char *architecture;
122 };
123
124 static void pager_open_if_enabled(void) {
125
126 if (arg_no_pager)
127 return;
128
129 pager_open(false);
130 }
131
132 static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
133 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
134 int r;
135
136 assert(bus);
137 assert(path);
138 assert(interface);
139 assert(property);
140 assert(val);
141
142 r = sd_bus_get_property_trivial(
143 bus,
144 "org.freedesktop.systemd1",
145 path,
146 interface,
147 property,
148 &error,
149 't', val);
150
151 if (r < 0) {
152 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
153 return r;
154 }
155
156 return 0;
157 }
158
159 static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
160 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
161 int r;
162
163 assert(bus);
164 assert(path);
165 assert(property);
166 assert(strv);
167
168 r = sd_bus_get_property_strv(
169 bus,
170 "org.freedesktop.systemd1",
171 path,
172 "org.freedesktop.systemd1.Unit",
173 property,
174 &error,
175 strv);
176 if (r < 0) {
177 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
178 return r;
179 }
180
181 return 0;
182 }
183
184 static int compare_unit_time(const void *a, const void *b) {
185 return compare(((struct unit_times *)b)->time,
186 ((struct unit_times *)a)->time);
187 }
188
189 static int compare_unit_start(const void *a, const void *b) {
190 return compare(((struct unit_times *)a)->activating,
191 ((struct unit_times *)b)->activating);
192 }
193
194 static void free_unit_times(struct unit_times *t, unsigned n) {
195 struct unit_times *p;
196
197 for (p = t; p < t + n; p++)
198 free(p->name);
199
200 free(t);
201 }
202
203 static void subtract_timestamp(usec_t *a, usec_t b) {
204 assert(a);
205
206 if (*a > 0) {
207 assert(*a >= b);
208 *a -= b;
209 }
210 }
211
212 static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
213 static struct boot_times times;
214 static bool cached = false;
215
216 if (cached)
217 goto finish;
218
219 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
220
221 if (bus_get_uint64_property(bus,
222 "/org/freedesktop/systemd1",
223 "org.freedesktop.systemd1.Manager",
224 "FirmwareTimestampMonotonic",
225 &times.firmware_time) < 0 ||
226 bus_get_uint64_property(bus,
227 "/org/freedesktop/systemd1",
228 "org.freedesktop.systemd1.Manager",
229 "LoaderTimestampMonotonic",
230 &times.loader_time) < 0 ||
231 bus_get_uint64_property(bus,
232 "/org/freedesktop/systemd1",
233 "org.freedesktop.systemd1.Manager",
234 "KernelTimestamp",
235 &times.kernel_time) < 0 ||
236 bus_get_uint64_property(bus,
237 "/org/freedesktop/systemd1",
238 "org.freedesktop.systemd1.Manager",
239 "InitRDTimestampMonotonic",
240 &times.initrd_time) < 0 ||
241 bus_get_uint64_property(bus,
242 "/org/freedesktop/systemd1",
243 "org.freedesktop.systemd1.Manager",
244 "UserspaceTimestampMonotonic",
245 &times.userspace_time) < 0 ||
246 bus_get_uint64_property(bus,
247 "/org/freedesktop/systemd1",
248 "org.freedesktop.systemd1.Manager",
249 "FinishTimestampMonotonic",
250 &times.finish_time) < 0 ||
251 bus_get_uint64_property(bus,
252 "/org/freedesktop/systemd1",
253 "org.freedesktop.systemd1.Manager",
254 "SecurityStartTimestampMonotonic",
255 &times.security_start_time) < 0 ||
256 bus_get_uint64_property(bus,
257 "/org/freedesktop/systemd1",
258 "org.freedesktop.systemd1.Manager",
259 "SecurityFinishTimestampMonotonic",
260 &times.security_finish_time) < 0 ||
261 bus_get_uint64_property(bus,
262 "/org/freedesktop/systemd1",
263 "org.freedesktop.systemd1.Manager",
264 "GeneratorsStartTimestampMonotonic",
265 &times.generators_start_time) < 0 ||
266 bus_get_uint64_property(bus,
267 "/org/freedesktop/systemd1",
268 "org.freedesktop.systemd1.Manager",
269 "GeneratorsFinishTimestampMonotonic",
270 &times.generators_finish_time) < 0 ||
271 bus_get_uint64_property(bus,
272 "/org/freedesktop/systemd1",
273 "org.freedesktop.systemd1.Manager",
274 "UnitsLoadStartTimestampMonotonic",
275 &times.unitsload_start_time) < 0 ||
276 bus_get_uint64_property(bus,
277 "/org/freedesktop/systemd1",
278 "org.freedesktop.systemd1.Manager",
279 "UnitsLoadFinishTimestampMonotonic",
280 &times.unitsload_finish_time) < 0)
281 return -EIO;
282
283 if (times.finish_time <= 0) {
284 log_error("Bootup is not yet finished. Please try again later.");
285 return -EINPROGRESS;
286 }
287
288 if (arg_user) {
289 /*
290 * User-instance-specific timestamps processing
291 * (see comment to reverse_offset in struct boot_times).
292 */
293 times.reverse_offset = times.userspace_time;
294
295 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
296 subtract_timestamp(&times.finish_time, times.reverse_offset);
297
298 subtract_timestamp(&times.security_start_time, times.reverse_offset);
299 subtract_timestamp(&times.security_finish_time, times.reverse_offset);
300
301 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
302 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
303
304 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
305 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
306 } else {
307 if (times.initrd_time)
308 times.kernel_done_time = times.initrd_time;
309 else
310 times.kernel_done_time = times.userspace_time;
311 }
312
313 cached = true;
314
315 finish:
316 *bt = &times;
317 return 0;
318 }
319
320 static void free_host_info(struct host_info *hi) {
321
322 if (!hi)
323 return;
324
325 free(hi->hostname);
326 free(hi->kernel_name);
327 free(hi->kernel_release);
328 free(hi->kernel_version);
329 free(hi->os_pretty_name);
330 free(hi->virtualization);
331 free(hi->architecture);
332 free(hi);
333 }
334
335 DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
336
337 static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
338 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
339 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
340 int r, c = 0;
341 struct boot_times *boot_times = NULL;
342 struct unit_times *unit_times = NULL;
343 size_t size = 0;
344 UnitInfo u;
345
346 r = acquire_boot_times(bus, &boot_times);
347 if (r < 0)
348 goto fail;
349
350 r = sd_bus_call_method(
351 bus,
352 "org.freedesktop.systemd1",
353 "/org/freedesktop/systemd1",
354 "org.freedesktop.systemd1.Manager",
355 "ListUnits",
356 &error, &reply,
357 NULL);
358 if (r < 0) {
359 log_error("Failed to list units: %s", bus_error_message(&error, -r));
360 goto fail;
361 }
362
363 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
364 if (r < 0) {
365 bus_log_parse_error(r);
366 goto fail;
367 }
368
369 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
370 struct unit_times *t;
371
372 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
373 r = log_oom();
374 goto fail;
375 }
376
377 t = unit_times+c;
378 t->name = NULL;
379
380 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
381
382 if (bus_get_uint64_property(bus, u.unit_path,
383 "org.freedesktop.systemd1.Unit",
384 "InactiveExitTimestampMonotonic",
385 &t->activating) < 0 ||
386 bus_get_uint64_property(bus, u.unit_path,
387 "org.freedesktop.systemd1.Unit",
388 "ActiveEnterTimestampMonotonic",
389 &t->activated) < 0 ||
390 bus_get_uint64_property(bus, u.unit_path,
391 "org.freedesktop.systemd1.Unit",
392 "ActiveExitTimestampMonotonic",
393 &t->deactivating) < 0 ||
394 bus_get_uint64_property(bus, u.unit_path,
395 "org.freedesktop.systemd1.Unit",
396 "InactiveEnterTimestampMonotonic",
397 &t->deactivated) < 0) {
398 r = -EIO;
399 goto fail;
400 }
401
402 subtract_timestamp(&t->activating, boot_times->reverse_offset);
403 subtract_timestamp(&t->activated, boot_times->reverse_offset);
404 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
405 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
406
407 if (t->activated >= t->activating)
408 t->time = t->activated - t->activating;
409 else if (t->deactivated >= t->activating)
410 t->time = t->deactivated - t->activating;
411 else
412 t->time = 0;
413
414 if (t->activating == 0)
415 continue;
416
417 t->name = strdup(u.id);
418 if (t->name == NULL) {
419 r = log_oom();
420 goto fail;
421 }
422 c++;
423 }
424 if (r < 0) {
425 bus_log_parse_error(r);
426 goto fail;
427 }
428
429 *out = unit_times;
430 return c;
431
432 fail:
433 if (unit_times)
434 free_unit_times(unit_times, (unsigned) c);
435 return r;
436 }
437
438 static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
439 static const struct bus_properties_map hostname_map[] = {
440 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
441 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
442 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
443 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
444 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
445 {}
446 };
447
448 static const struct bus_properties_map manager_map[] = {
449 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
450 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
451 {}
452 };
453
454 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
455 _cleanup_(free_host_infop) struct host_info *host;
456 int r;
457
458 host = new0(struct host_info, 1);
459 if (!host)
460 return log_oom();
461
462 r = bus_map_all_properties(bus,
463 "org.freedesktop.hostname1",
464 "/org/freedesktop/hostname1",
465 hostname_map,
466 host);
467 if (r < 0)
468 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
469
470 r = bus_map_all_properties(bus,
471 "org.freedesktop.systemd1",
472 "/org/freedesktop/systemd1",
473 manager_map,
474 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", 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", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
761
762 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_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_bus_message_unref_ sd_bus_message *reply = NULL;
899 _cleanup_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_if_enabled();
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_if_enabled();
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 (arg_dot == DEP_ORDER ||arg_dot == 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 (arg_dot == DEP_REQUIRE ||arg_dot == 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, "RequiresOverridable", "black", patterns, from_patterns, to_patterns);
1076 if (r < 0)
1077 return r;
1078 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns, from_patterns, to_patterns);
1079 if (r < 0)
1080 return r;
1081 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
1082 if (r < 0)
1083 return r;
1084 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
1085 if (r < 0)
1086 return r;
1087 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns, from_patterns, to_patterns);
1088 if (r < 0)
1089 return r;
1090 }
1091
1092 return 0;
1093 }
1094
1095 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1096 _cleanup_strv_free_ char **expanded_patterns = NULL;
1097 char **pattern;
1098 int r;
1099
1100 STRV_FOREACH(pattern, patterns) {
1101 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1102 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1103
1104 if (strv_extend(&expanded_patterns, *pattern) < 0)
1105 return log_oom();
1106
1107 if (string_is_glob(*pattern))
1108 continue;
1109
1110 unit = unit_dbus_path_from_name(*pattern);
1111 if (!unit)
1112 return log_oom();
1113
1114 r = sd_bus_get_property_string(
1115 bus,
1116 "org.freedesktop.systemd1",
1117 unit,
1118 "org.freedesktop.systemd1.Unit",
1119 "Id",
1120 &error,
1121 &unit_id);
1122 if (r < 0)
1123 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1124
1125 if (!streq(*pattern, unit_id)) {
1126 if (strv_extend(&expanded_patterns, unit_id) < 0)
1127 return log_oom();
1128 }
1129 }
1130
1131 *ret = expanded_patterns;
1132 expanded_patterns = NULL; /* do not free */
1133
1134 return 0;
1135 }
1136
1137 static int dot(sd_bus *bus, char* patterns[]) {
1138 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1139 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1140 _cleanup_strv_free_ char **expanded_patterns = NULL;
1141 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1142 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1143 int r;
1144 UnitInfo u;
1145
1146 r = expand_patterns(bus, patterns, &expanded_patterns);
1147 if (r < 0)
1148 return r;
1149
1150 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1151 if (r < 0)
1152 return r;
1153
1154 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1155 if (r < 0)
1156 return r;
1157
1158 r = sd_bus_call_method(
1159 bus,
1160 "org.freedesktop.systemd1",
1161 "/org/freedesktop/systemd1",
1162 "org.freedesktop.systemd1.Manager",
1163 "ListUnits",
1164 &error,
1165 &reply,
1166 "");
1167 if (r < 0) {
1168 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1169 return r;
1170 }
1171
1172 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1173 if (r < 0)
1174 return bus_log_parse_error(r);
1175
1176 printf("digraph systemd {\n");
1177
1178 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1179
1180 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1181 if (r < 0)
1182 return r;
1183 }
1184 if (r < 0)
1185 return bus_log_parse_error(r);
1186
1187 printf("}\n");
1188
1189 log_info(" Color legend: black = Requires\n"
1190 " dark blue = Requisite\n"
1191 " dark grey = Wants\n"
1192 " red = Conflicts\n"
1193 " green = After\n");
1194
1195 if (on_tty())
1196 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1197 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1198
1199 return 0;
1200 }
1201
1202 static int dump(sd_bus *bus, char **args) {
1203 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1204 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1205 const char *text = NULL;
1206 int r;
1207
1208 if (!strv_isempty(args)) {
1209 log_error("Too many arguments.");
1210 return -E2BIG;
1211 }
1212
1213 pager_open_if_enabled();
1214
1215 r = sd_bus_call_method(
1216 bus,
1217 "org.freedesktop.systemd1",
1218 "/org/freedesktop/systemd1",
1219 "org.freedesktop.systemd1.Manager",
1220 "Dump",
1221 &error,
1222 &reply,
1223 "");
1224 if (r < 0)
1225 return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
1226
1227 r = sd_bus_message_read(reply, "s", &text);
1228 if (r < 0)
1229 return bus_log_parse_error(r);
1230
1231 fputs(text, stdout);
1232 return 0;
1233 }
1234
1235 static int set_log_level(sd_bus *bus, char **args) {
1236 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1237 int r;
1238
1239 assert(bus);
1240 assert(args);
1241
1242 if (strv_length(args) != 1) {
1243 log_error("This command expects one argument only.");
1244 return -E2BIG;
1245 }
1246
1247 r = sd_bus_set_property(
1248 bus,
1249 "org.freedesktop.systemd1",
1250 "/org/freedesktop/systemd1",
1251 "org.freedesktop.systemd1.Manager",
1252 "LogLevel",
1253 &error,
1254 "s",
1255 args[0]);
1256 if (r < 0)
1257 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1258
1259 return 0;
1260 }
1261
1262 static int set_log_target(sd_bus *bus, char **args) {
1263 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1264 int r;
1265
1266 assert(bus);
1267 assert(args);
1268
1269 if (strv_length(args) != 1) {
1270 log_error("This command expects one argument only.");
1271 return -E2BIG;
1272 }
1273
1274 r = sd_bus_set_property(
1275 bus,
1276 "org.freedesktop.systemd1",
1277 "/org/freedesktop/systemd1",
1278 "org.freedesktop.systemd1.Manager",
1279 "LogTarget",
1280 &error,
1281 "s",
1282 args[0]);
1283 if (r < 0)
1284 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1285
1286 return 0;
1287 }
1288
1289 static void help(void) {
1290
1291 pager_open_if_enabled();
1292
1293 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1294 "Profile systemd, show unit dependencies, check unit files.\n\n"
1295 " -h --help Show this help\n"
1296 " --version Show package version\n"
1297 " --no-pager Do not pipe output into a pager\n"
1298 " --system Operate on system systemd instance\n"
1299 " --user Operate on user systemd instance\n"
1300 " -H --host=[USER@]HOST Operate on remote host\n"
1301 " -M --machine=CONTAINER Operate on local container\n"
1302 " --order Show only order in the graph\n"
1303 " --require Show only requirement in the graph\n"
1304 " --from-pattern=GLOB Show only origins in the graph\n"
1305 " --to-pattern=GLOB Show only destinations in the graph\n"
1306 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1307 " earlier than the latest in the branch\n"
1308 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
1309 "Commands:\n"
1310 " time Print time spent in the kernel\n"
1311 " blame Print list of running units ordered by time to init\n"
1312 " critical-chain Print a tree of the time critical chain of units\n"
1313 " plot Output SVG graphic showing service initialization\n"
1314 " dot Output dependency graph in dot(1) format\n"
1315 " set-log-level LEVEL Set logging threshold for manager\n"
1316 " set-log-target TARGET Set logging target for manager\n"
1317 " dump Output state serialization of service manager\n"
1318 " verify FILE... Check unit files for correctness\n"
1319 , program_invocation_short_name);
1320
1321 /* When updating this list, including descriptions, apply
1322 * changes to shell-completion/bash/systemd-analyze and
1323 * shell-completion/zsh/_systemd-analyze too. */
1324 }
1325
1326 static int parse_argv(int argc, char *argv[]) {
1327 enum {
1328 ARG_VERSION = 0x100,
1329 ARG_ORDER,
1330 ARG_REQUIRE,
1331 ARG_USER,
1332 ARG_SYSTEM,
1333 ARG_DOT_FROM_PATTERN,
1334 ARG_DOT_TO_PATTERN,
1335 ARG_FUZZ,
1336 ARG_NO_PAGER,
1337 ARG_MAN,
1338 };
1339
1340 static const struct option options[] = {
1341 { "help", no_argument, NULL, 'h' },
1342 { "version", no_argument, NULL, ARG_VERSION },
1343 { "order", no_argument, NULL, ARG_ORDER },
1344 { "require", no_argument, NULL, ARG_REQUIRE },
1345 { "user", no_argument, NULL, ARG_USER },
1346 { "system", no_argument, NULL, ARG_SYSTEM },
1347 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1348 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1349 { "fuzz", required_argument, NULL, ARG_FUZZ },
1350 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1351 { "man", optional_argument, NULL, ARG_MAN },
1352 { "host", required_argument, NULL, 'H' },
1353 { "machine", required_argument, NULL, 'M' },
1354 {}
1355 };
1356
1357 int r, c;
1358
1359 assert(argc >= 0);
1360 assert(argv);
1361
1362 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1363 switch (c) {
1364
1365 case 'h':
1366 help();
1367 return 0;
1368
1369 case ARG_VERSION:
1370 return version();
1371
1372 case ARG_USER:
1373 arg_user = true;
1374 break;
1375
1376 case ARG_SYSTEM:
1377 arg_user = false;
1378 break;
1379
1380 case ARG_ORDER:
1381 arg_dot = DEP_ORDER;
1382 break;
1383
1384 case ARG_REQUIRE:
1385 arg_dot = DEP_REQUIRE;
1386 break;
1387
1388 case ARG_DOT_FROM_PATTERN:
1389 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1390 return log_oom();
1391
1392 break;
1393
1394 case ARG_DOT_TO_PATTERN:
1395 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1396 return log_oom();
1397
1398 break;
1399
1400 case ARG_FUZZ:
1401 r = parse_sec(optarg, &arg_fuzz);
1402 if (r < 0)
1403 return r;
1404 break;
1405
1406 case ARG_NO_PAGER:
1407 arg_no_pager = true;
1408 break;
1409
1410 case 'H':
1411 arg_transport = BUS_TRANSPORT_REMOTE;
1412 arg_host = optarg;
1413 break;
1414
1415 case 'M':
1416 arg_transport = BUS_TRANSPORT_MACHINE;
1417 arg_host = optarg;
1418 break;
1419
1420 case ARG_MAN:
1421 if (optarg) {
1422 r = parse_boolean(optarg);
1423 if (r < 0) {
1424 log_error("Failed to parse --man= argument.");
1425 return -EINVAL;
1426 }
1427
1428 arg_man = !!r;
1429 } else
1430 arg_man = true;
1431
1432 break;
1433
1434 case '?':
1435 return -EINVAL;
1436
1437 default:
1438 assert_not_reached("Unhandled option code.");
1439 }
1440
1441 return 1; /* work to do */
1442 }
1443
1444 int main(int argc, char *argv[]) {
1445 int r;
1446
1447 setlocale(LC_ALL, "");
1448 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1449 log_parse_environment();
1450 log_open();
1451
1452 r = parse_argv(argc, argv);
1453 if (r <= 0)
1454 goto finish;
1455
1456 if (streq_ptr(argv[optind], "verify"))
1457 r = verify_units(argv+optind+1,
1458 arg_user ? MANAGER_USER : MANAGER_SYSTEM,
1459 arg_man);
1460 else {
1461 _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
1462
1463 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1464 if (r < 0) {
1465 log_error_errno(r, "Failed to create bus connection: %m");
1466 goto finish;
1467 }
1468
1469 if (!argv[optind] || streq(argv[optind], "time"))
1470 r = analyze_time(bus);
1471 else if (streq(argv[optind], "blame"))
1472 r = analyze_blame(bus);
1473 else if (streq(argv[optind], "critical-chain"))
1474 r = analyze_critical_chain(bus, argv+optind+1);
1475 else if (streq(argv[optind], "plot"))
1476 r = analyze_plot(bus);
1477 else if (streq(argv[optind], "dot"))
1478 r = dot(bus, argv+optind+1);
1479 else if (streq(argv[optind], "dump"))
1480 r = dump(bus, argv+optind+1);
1481 else if (streq(argv[optind], "set-log-level"))
1482 r = set_log_level(bus, argv+optind+1);
1483 else if (streq(argv[optind], "set-log-target"))
1484 r = set_log_target(bus, argv+optind+1);
1485 else
1486 log_error("Unknown operation '%s'.", argv[optind]);
1487 }
1488
1489 finish:
1490 pager_close();
1491
1492 strv_free(arg_dot_from_patterns);
1493 strv_free(arg_dot_to_patterns);
1494
1495 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1496 }