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