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