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