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