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