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