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