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