chiark / gitweb /
analyze: change behaviour of combined --to/from--pattern
[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         bool match_patterns;
978
979         assert(u);
980         assert(prop);
981         assert(color);
982
983         match_patterns = strv_fnmatch(u->id, patterns, 0);
984
985         if (!strv_isempty(arg_dot_from_patterns) &&
986             !match_patterns &&
987             !strv_fnmatch(u->id, arg_dot_from_patterns, 0))
988                         return 0;
989
990         r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
991         if (r < 0)
992                 return r;
993
994         STRV_FOREACH(unit, units) {
995                 bool match_patterns2;
996
997                 match_patterns2 = strv_fnmatch(*unit, patterns, 0);
998
999                 if (!strv_isempty(arg_dot_to_patterns) &&
1000                     !match_patterns2 &&
1001                     !strv_fnmatch(*unit, arg_dot_to_patterns, 0))
1002                         continue;
1003
1004                 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
1005                         continue;
1006
1007                 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1008         }
1009
1010         return 0;
1011 }
1012
1013 static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) {
1014         int r;
1015
1016         assert(bus);
1017         assert(u);
1018
1019         if (arg_dot == DEP_ORDER ||arg_dot == DEP_ALL) {
1020                 r = graph_one_property(bus, u, "After", "green", patterns);
1021                 if (r < 0)
1022                         return r;
1023         }
1024
1025         if (arg_dot == DEP_REQUIRE ||arg_dot == DEP_ALL) {
1026                 r = graph_one_property(bus, u, "Requires", "black", patterns);
1027                 if (r < 0)
1028                         return r;
1029                 r = graph_one_property(bus, u, "RequiresOverridable", "black", patterns);
1030                 if (r < 0)
1031                         return r;
1032                 r = graph_one_property(bus, u, "RequisiteOverridable", "darkblue", patterns);
1033                 if (r < 0)
1034                         return r;
1035                 r = graph_one_property(bus, u, "Wants", "grey66", patterns);
1036                 if (r < 0)
1037                         return r;
1038                 r = graph_one_property(bus, u, "Conflicts", "red", patterns);
1039                 if (r < 0)
1040                         return r;
1041                 r = graph_one_property(bus, u, "ConflictedBy", "red", patterns);
1042                 if (r < 0)
1043                         return r;
1044         }
1045
1046         return 0;
1047 }
1048
1049 static int dot(sd_bus *bus, char* patterns[]) {
1050         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1051         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1052         int r;
1053         UnitInfo u;
1054
1055         r = sd_bus_call_method(
1056                         bus,
1057                        "org.freedesktop.systemd1",
1058                        "/org/freedesktop/systemd1",
1059                        "org.freedesktop.systemd1.Manager",
1060                        "ListUnits",
1061                        &error,
1062                        &reply,
1063                        "");
1064         if (r < 0) {
1065                 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1066                 return r;
1067         }
1068
1069         r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1070         if (r < 0)
1071                 return bus_log_parse_error(r);
1072
1073         printf("digraph systemd {\n");
1074
1075         while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1076
1077                 r = graph_one(bus, &u, patterns);
1078                 if (r < 0)
1079                         return r;
1080         }
1081         if (r < 0)
1082                 return bus_log_parse_error(r);
1083
1084         printf("}\n");
1085
1086         log_info("   Color legend: black     = Requires\n"
1087                  "                 dark blue = Requisite\n"
1088                  "                 dark grey = Wants\n"
1089                  "                 red       = Conflicts\n"
1090                  "                 green     = After\n");
1091
1092         if (on_tty())
1093                 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1094                            "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1095
1096         return 0;
1097 }
1098
1099 static int dump(sd_bus *bus, char **args) {
1100         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1101         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1102         const char *text = NULL;
1103         int r;
1104
1105         if (!strv_isempty(args)) {
1106                 log_error("Too many arguments.");
1107                 return -E2BIG;
1108         }
1109
1110         pager_open_if_enabled();
1111
1112         r = sd_bus_call_method(
1113                         bus,
1114                        "org.freedesktop.systemd1",
1115                        "/org/freedesktop/systemd1",
1116                        "org.freedesktop.systemd1.Manager",
1117                        "Dump",
1118                        &error,
1119                        &reply,
1120                        "");
1121         if (r < 0) {
1122                 log_error("Failed issue method call: %s", bus_error_message(&error, -r));
1123                 return r;
1124         }
1125
1126         r = sd_bus_message_read(reply, "s", &text);
1127         if (r < 0)
1128                 return bus_log_parse_error(r);
1129
1130         fputs(text, stdout);
1131         return 0;
1132 }
1133
1134 static int set_log_level(sd_bus *bus, char **args) {
1135         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1136         int r;
1137
1138         assert(bus);
1139         assert(args);
1140
1141         if (strv_length(args) != 1) {
1142                 log_error("This command expects one argument only.");
1143                 return -E2BIG;
1144         }
1145
1146         r = sd_bus_set_property(
1147                         bus,
1148                         "org.freedesktop.systemd1",
1149                         "/org/freedesktop/systemd1",
1150                         "org.freedesktop.systemd1.Manager",
1151                         "LogLevel",
1152                         &error,
1153                         "s",
1154                         args[0]);
1155         if (r < 0) {
1156                 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
1157                 return -EIO;
1158         }
1159
1160         return 0;
1161 }
1162
1163 static void help(void) {
1164
1165         pager_open_if_enabled();
1166
1167         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1168                "Profile systemd, show unit dependencies, check unit files.\n\n"
1169                "  -h --help               Show this help\n"
1170                "     --version            Show package version\n"
1171                "     --no-pager           Do not pipe output into a pager\n"
1172                "     --system             Operate on system systemd instance\n"
1173                "     --user               Operate on user systemd instance\n"
1174                "  -H --host=[USER@]HOST   Operate on remote host\n"
1175                "  -M --machine=CONTAINER  Operate on local container\n"
1176                "     --order              Show only order in the graph\n"
1177                "     --require            Show only requirement in the graph\n"
1178                "     --from-pattern=GLOB  Show only origins in the graph\n"
1179                "     --to-pattern=GLOB    Show only destinations in the graph\n"
1180                "     --fuzz=SECONDS       Also print also services which finished SECONDS\n"
1181                "                          earlier than the latest in the branch\n"
1182                "     --man[=BOOL]         Do [not] check for existence of man pages\n\n"
1183                "Commands:\n"
1184                "  time                    Print time spent in the kernel\n"
1185                "  blame                   Print list of running units ordered by time to init\n"
1186                "  critical-chain          Print a tree of the time critical chain of units\n"
1187                "  plot                    Output SVG graphic showing service initialization\n"
1188                "  dot                     Output dependency graph in dot(1) format\n"
1189                "  set-log-level LEVEL     Set logging threshold for systemd\n"
1190                "  dump                    Output state serialization of service manager\n"
1191                "  verify FILE...          Check unit files for correctness\n"
1192                , program_invocation_short_name);
1193
1194         /* When updating this list, including descriptions, apply
1195          * changes to shell-completion/bash/systemd-analyze and
1196          * shell-completion/zsh/_systemd-analyze too. */
1197 }
1198
1199 static int parse_argv(int argc, char *argv[]) {
1200         enum {
1201                 ARG_VERSION = 0x100,
1202                 ARG_ORDER,
1203                 ARG_REQUIRE,
1204                 ARG_USER,
1205                 ARG_SYSTEM,
1206                 ARG_DOT_FROM_PATTERN,
1207                 ARG_DOT_TO_PATTERN,
1208                 ARG_FUZZ,
1209                 ARG_NO_PAGER,
1210                 ARG_MAN,
1211         };
1212
1213         static const struct option options[] = {
1214                 { "help",         no_argument,       NULL, 'h'                  },
1215                 { "version",      no_argument,       NULL, ARG_VERSION          },
1216                 { "order",        no_argument,       NULL, ARG_ORDER            },
1217                 { "require",      no_argument,       NULL, ARG_REQUIRE          },
1218                 { "user",         no_argument,       NULL, ARG_USER             },
1219                 { "system",       no_argument,       NULL, ARG_SYSTEM           },
1220                 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1221                 { "to-pattern",   required_argument, NULL, ARG_DOT_TO_PATTERN   },
1222                 { "fuzz",         required_argument, NULL, ARG_FUZZ             },
1223                 { "no-pager",     no_argument,       NULL, ARG_NO_PAGER         },
1224                 { "man",          optional_argument, NULL, ARG_MAN              },
1225                 { "host",         required_argument, NULL, 'H'                  },
1226                 { "machine",      required_argument, NULL, 'M'                  },
1227                 {}
1228         };
1229
1230         int r, c;
1231
1232         assert(argc >= 0);
1233         assert(argv);
1234
1235         while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
1236                 switch (c) {
1237
1238                 case 'h':
1239                         help();
1240                         return 0;
1241
1242                 case ARG_VERSION:
1243                         puts(PACKAGE_STRING);
1244                         puts(SYSTEMD_FEATURES);
1245                         return 0;
1246
1247                 case ARG_USER:
1248                         arg_user = true;
1249                         break;
1250
1251                 case ARG_SYSTEM:
1252                         arg_user = false;
1253                         break;
1254
1255                 case ARG_ORDER:
1256                         arg_dot = DEP_ORDER;
1257                         break;
1258
1259                 case ARG_REQUIRE:
1260                         arg_dot = DEP_REQUIRE;
1261                         break;
1262
1263                 case ARG_DOT_FROM_PATTERN:
1264                         if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1265                                 return log_oom();
1266
1267                         break;
1268
1269                 case ARG_DOT_TO_PATTERN:
1270                         if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1271                                 return log_oom();
1272
1273                         break;
1274
1275                 case ARG_FUZZ:
1276                         r = parse_sec(optarg, &arg_fuzz);
1277                         if (r < 0)
1278                                 return r;
1279                         break;
1280
1281                 case ARG_NO_PAGER:
1282                         arg_no_pager = true;
1283                         break;
1284
1285                 case 'H':
1286                         arg_transport = BUS_TRANSPORT_REMOTE;
1287                         arg_host = optarg;
1288                         break;
1289
1290                 case 'M':
1291                         arg_transport = BUS_TRANSPORT_MACHINE;
1292                         arg_host = optarg;
1293                         break;
1294
1295                 case ARG_MAN:
1296                         if (optarg) {
1297                                 r = parse_boolean(optarg);
1298                                 if (r < 0) {
1299                                         log_error("Failed to parse --man= argument.");
1300                                         return -EINVAL;
1301                                 }
1302
1303                                 arg_man = !!r;
1304                         } else
1305                                 arg_man = true;
1306
1307                         break;
1308
1309                 case '?':
1310                         return -EINVAL;
1311
1312                 default:
1313                         assert_not_reached("Unhandled option code.");
1314                 }
1315
1316         return 1; /* work to do */
1317 }
1318
1319 int main(int argc, char *argv[]) {
1320         int r;
1321
1322         setlocale(LC_ALL, "");
1323         setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
1324         log_parse_environment();
1325         log_open();
1326
1327         r = parse_argv(argc, argv);
1328         if (r <= 0)
1329                 goto finish;
1330
1331         if (streq_ptr(argv[optind], "verify"))
1332                 r = verify_units(argv+optind+1,
1333                                  arg_user ? SYSTEMD_USER : SYSTEMD_SYSTEM,
1334                                  arg_man);
1335         else {
1336                 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1337
1338                 r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1339                 if (r < 0) {
1340                         log_error_errno(r, "Failed to create bus connection: %m");
1341                         goto finish;
1342                 }
1343
1344                 if (!argv[optind] || streq(argv[optind], "time"))
1345                         r = analyze_time(bus);
1346                 else if (streq(argv[optind], "blame"))
1347                         r = analyze_blame(bus);
1348                 else if (streq(argv[optind], "critical-chain"))
1349                         r = analyze_critical_chain(bus, argv+optind+1);
1350                 else if (streq(argv[optind], "plot"))
1351                         r = analyze_plot(bus);
1352                 else if (streq(argv[optind], "dot"))
1353                         r = dot(bus, argv+optind+1);
1354                 else if (streq(argv[optind], "dump"))
1355                         r = dump(bus, argv+optind+1);
1356                 else if (streq(argv[optind], "set-log-level"))
1357                         r = set_log_level(bus, argv+optind+1);
1358                 else
1359                         log_error("Unknown operation '%s'.", argv[optind]);
1360         }
1361
1362 finish:
1363         pager_close();
1364
1365         strv_free(arg_dot_from_patterns);
1366         strv_free(arg_dot_to_patterns);
1367
1368         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1369 }