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