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