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