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