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