]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/systemd-analyze.c
honor SELinux labels, when creating and writing config files
[thirdparty/systemd.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 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <getopt.h>
25 #include <locale.h>
26 #include <sys/utsname.h>
27
28 #include "install.h"
29 #include "log.h"
30 #include "dbus-common.h"
31 #include "build.h"
32 #include "util.h"
33 #include "strxcpyx.h"
34 #include "fileio.h"
35
36 #define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
37 #define svg(...) printf(__VA_ARGS__)
38 #define svg_bar(class, x1, x2, y) \
39 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
40 (class), \
41 scale_x * (x1), scale_y * (y), \
42 scale_x * ((x2) - (x1)), scale_y - 1.0)
43 #define svg_text(x, y, format, ...) do {\
44 svg(" <text x=\"%.03f\" y=\"%.03f\">", scale_x * (x) + 5.0, scale_y * (y) + 14.0); \
45 svg(format, ## __VA_ARGS__); \
46 svg("</text>\n"); \
47 } while(false)
48
49 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
50 static enum dot {
51 DEP_ALL,
52 DEP_ORDER,
53 DEP_REQUIRE
54 } arg_dot = DEP_ALL;
55
56 double scale_x = 0.1; // pixels per ms
57 double scale_y = 20.0;
58
59 struct boot_times {
60 uint64_t firmware_time;
61 uint64_t loader_time;
62 uint64_t kernel_time;
63 uint64_t kernel_done_time;
64 uint64_t initrd_time;
65 uint64_t userspace_time;
66 uint64_t finish_time;
67 };
68 struct unit_times {
69 char *name;
70 uint64_t ixt;
71 uint64_t iet;
72 uint64_t axt;
73 uint64_t aet;
74 uint64_t time;
75 };
76
77 static int bus_get_uint64_property (DBusConnection *bus, const char *path, const char *interface, const char *property, uint64_t *val)
78 {
79 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
80 int r;
81 DBusMessageIter iter, sub;
82
83 r = bus_method_call_with_reply (
84 bus,
85 "org.freedesktop.systemd1",
86 path,
87 "org.freedesktop.DBus.Properties",
88 "Get",
89 &reply,
90 NULL,
91 DBUS_TYPE_STRING, &interface,
92 DBUS_TYPE_STRING, &property,
93 DBUS_TYPE_INVALID);
94 if (r < 0)
95 return r;
96
97 if (!dbus_message_iter_init(reply, &iter) ||
98 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
99 log_error("Failed to parse reply.");
100 return -EIO;
101 }
102
103 dbus_message_iter_recurse(&iter, &sub);
104
105 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64) {
106 log_error("Failed to parse reply.");
107 return -EIO;
108 }
109
110 dbus_message_iter_get_basic(&sub, val);
111
112 return 0;
113 }
114
115 static int compare_unit_time(const void *a, const void *b)
116 {
117 return compare(((struct unit_times *)b)->time,
118 ((struct unit_times *)a)->time);
119 }
120
121 static int compare_unit_start(const void *a, const void *b)
122 {
123 return compare(((struct unit_times *)a)->ixt,
124 ((struct unit_times *)b)->ixt);
125 }
126
127 static char *get_os_name(void)
128 {
129 char *n = NULL;
130
131 parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
132 return n;
133 }
134
135 static int acquire_time_data(DBusConnection *bus, struct unit_times **out)
136 {
137 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
138 DBusMessageIter iter, sub;
139 int r, c = 0, n_units = 0;
140 struct unit_times *unit_times = NULL;
141
142 r = bus_method_call_with_reply (
143 bus,
144 "org.freedesktop.systemd1",
145 "/org/freedesktop/systemd1",
146 "org.freedesktop.systemd1.Manager",
147 "ListUnits",
148 &reply,
149 NULL,
150 DBUS_TYPE_INVALID);
151 if (r)
152 goto fail;
153
154 if (!dbus_message_iter_init(reply, &iter) ||
155 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
156 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
157 log_error("Failed to parse reply.");
158 r = -EIO;
159 goto fail;
160 }
161
162 for (dbus_message_iter_recurse(&iter, &sub);
163 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
164 dbus_message_iter_next(&sub)) {
165 struct unit_info u;
166 struct unit_times *t;
167
168 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
169 log_error("Failed to parse reply.");
170 r = -EIO;
171 goto fail;
172 }
173
174 if (c >= n_units) {
175 struct unit_times *w;
176
177 n_units = MAX(2*c, 16);
178 w = realloc(unit_times, sizeof(struct unit_times) * n_units);
179
180 if (!w) {
181 r = log_oom();
182 goto fail;
183 }
184
185 unit_times = w;
186 }
187 t = unit_times+c;
188 t->name = NULL;
189
190 r = bus_parse_unit_info(&sub, &u);
191 if (r < 0)
192 goto fail;
193
194 if (bus_get_uint64_property(bus, u.unit_path,
195 "org.freedesktop.systemd1.Unit",
196 "InactiveExitTimestampMonotonic",
197 &t->ixt) < 0 ||
198 bus_get_uint64_property(bus, u.unit_path,
199 "org.freedesktop.systemd1.Unit",
200 "ActiveEnterTimestampMonotonic",
201 &t->aet) < 0 ||
202 bus_get_uint64_property(bus, u.unit_path,
203 "org.freedesktop.systemd1.Unit",
204 "ActiveExitTimestampMonotonic",
205 &t->axt) < 0 ||
206 bus_get_uint64_property(bus, u.unit_path,
207 "org.freedesktop.systemd1.Unit",
208 "InactiveEnterTimestampMonotonic",
209 &t->iet) < 0) {
210 r = -EIO;
211 goto fail;
212 }
213
214 t->iet /= 1000;
215 t->ixt /= 1000;
216 t->aet /= 1000;
217 t->axt /= 1000;
218
219 if (t->aet >= t->ixt)
220 t->time = t->aet - t->ixt;
221 else if (t->iet >= t->ixt)
222 t->time = t->iet - t->ixt;
223 else
224 t->time = 0;
225
226 if (t->ixt == 0)
227 continue;
228
229 t->name = strdup(u.id);
230 if (t->name == NULL) {
231 r = log_oom();
232 goto fail;
233 }
234 c++;
235 }
236
237 *out = unit_times;
238 return c;
239 fail:
240 for (; c >= 0; c--)
241 free(unit_times[c].name);
242 free(unit_times);
243 return r;
244 }
245
246 static struct boot_times *acquire_boot_times(DBusConnection *bus)
247 {
248 static struct boot_times times;
249 static bool cached = false;
250 if (cached)
251 return &times;
252
253 if (bus_get_uint64_property(bus,
254 "/org/freedesktop/systemd1",
255 "org.freedesktop.systemd1.Manager",
256 "FirmwareTimestampMonotonic",
257 &times.firmware_time) < 0 ||
258 bus_get_uint64_property(bus,
259 "/org/freedesktop/systemd1",
260 "org.freedesktop.systemd1.Manager",
261 "LoaderTimestampMonotonic",
262 &times.loader_time) < 0 ||
263 bus_get_uint64_property(bus,
264 "/org/freedesktop/systemd1",
265 "org.freedesktop.systemd1.Manager",
266 "KernelTimestamp",
267 &times.kernel_time) < 0 ||
268 bus_get_uint64_property(bus,
269 "/org/freedesktop/systemd1",
270 "org.freedesktop.systemd1.Manager",
271 "InitRDTimestampMonotonic",
272 &times.initrd_time) < 0 ||
273 bus_get_uint64_property(bus,
274 "/org/freedesktop/systemd1",
275 "org.freedesktop.systemd1.Manager",
276 "UserspaceTimestampMonotonic",
277 &times.userspace_time) < 0 ||
278 bus_get_uint64_property(bus,
279 "/org/freedesktop/systemd1",
280 "org.freedesktop.systemd1.Manager",
281 "FinishTimestampMonotonic",
282 &times.finish_time) < 0)
283 return NULL;
284
285 if (!times.finish_time) {
286 log_error("Bootup is not yet finished. Please try again later.");
287 return NULL;
288 }
289
290 times.firmware_time /= 1000;
291 times.loader_time /= 1000;
292 times.initrd_time /= 1000;
293 times.userspace_time /= 1000;
294 times.finish_time /= 1000;
295
296 if (times.initrd_time)
297 times.kernel_done_time = times.initrd_time;
298 else
299 times.kernel_done_time = times.userspace_time;
300
301 cached = true;
302 return &times;
303 }
304
305 static char *pretty_boot_time(DBusConnection *bus)
306 {
307 struct boot_times *t;
308 size_t size = 4096;
309 static char buf[4096];
310 char *ptr = buf;
311
312 t = acquire_boot_times(bus);
313 if (!t)
314 return NULL;
315
316 size = strpcpyf(&ptr, size, "Startup finished in ");
317 if (t->firmware_time)
318 size = strpcpyf(&ptr, size, "%llums (firmware) + ", (unsigned long long)(t->firmware_time - t->loader_time));
319 if (t->loader_time)
320 size = strpcpyf(&ptr, size, "%llums (loader) + ", (unsigned long long)t->loader_time);
321 if (t->kernel_time)
322 size = strpcpyf(&ptr, size, "%llums (kernel) + ", (unsigned long long)t->kernel_done_time);
323 if (t->initrd_time > 0)
324 size = strpcpyf(&ptr, size, "%llums (initrd) + ", (unsigned long long)(t->userspace_time - t->initrd_time));
325
326 size = strpcpyf(&ptr, size, "%llums (userspace) ", (unsigned long long)(t->finish_time - t->userspace_time));
327 if (t->kernel_time > 0)
328 size = strpcpyf(&ptr, size, "= %llums", (unsigned long long)(t->firmware_time + t->finish_time));
329 else
330 size = strpcpyf(&ptr, size, "= %llums", (unsigned long long)(t->finish_time - t->userspace_time));
331
332 return buf;
333 }
334
335 static void svg_graph_box(int height, int64_t begin, int64_t end)
336 {
337 /* outside box, fill */
338 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
339 scale_x * (end - begin), scale_y * height);
340
341 for (int i = (begin / 100) * 100; i <= end; i+=100) {
342 /* lines for each second */
343 if (i % 5000 == 0)
344 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
345 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
346 scale_x * i, scale_x * i, scale_y * height, scale_x * i, -5.0, 0.001 * i);
347 else if (i % 1000 == 0)
348 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
349 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
350 scale_x * i, scale_x * i, scale_y * height, scale_x * i, -5.0, 0.001 * i);
351 else
352 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
353 scale_x * i, scale_x * i, scale_y * height);
354 }
355 }
356
357 static int analyze_plot(DBusConnection *bus)
358 {
359 struct unit_times *times;
360 struct boot_times *boot;
361 struct utsname name;
362 int n, m = 1, y=0;
363 double width;
364 char *osname;
365 char *pretty_times;
366
367 boot = acquire_boot_times(bus);
368 if (!boot)
369 return -EIO;
370 pretty_times = pretty_boot_time(bus);
371 if (!pretty_times)
372 return -EIO;
373
374 osname = get_os_name();
375
376 n = uname(&name);
377 if (n < 0) {
378 log_error("Cannot get system name: %m");
379 return -errno;
380 }
381
382 n = acquire_time_data(bus, &times);
383 if (n<=0)
384 return n;
385
386 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
387
388 width = scale_x * (boot->firmware_time + boot->finish_time);
389 if (width < 800.0)
390 width = 800.0;
391
392 if (boot->firmware_time > boot->loader_time)
393 m++;
394 if (boot->loader_time) {
395 m++;
396 if (width < 1000.0)
397 width = 1000.0;
398 }
399 if (boot->initrd_time)
400 m++;
401 if (boot->kernel_time)
402 m++;
403
404 for (struct unit_times *u = times; u < times + n; u++) {
405 double len;
406 if (u->ixt < boot->userspace_time ||
407 u->ixt > boot->finish_time) {
408 free(u->name);
409 u->name = NULL;
410 continue;
411 }
412 len = ((boot->firmware_time + u->ixt) * scale_x)
413 + (10.0 * strlen(u->name));
414 if (len > width)
415 width = len;
416
417 if (u->iet > u->ixt && u->iet <= boot->finish_time
418 && u->aet == 0 && u->axt == 0)
419 u->aet = u->axt = u->iet;
420 if (u->aet < u->ixt || u->aet > boot->finish_time)
421 u->aet = boot->finish_time;
422 if (u->axt < u->aet || u->aet > boot->finish_time)
423 u->axt = boot->finish_time;
424 if (u->iet < u->axt || u->iet > boot->finish_time)
425 u->iet = boot->finish_time;
426 m++;
427 }
428
429 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
430 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
431 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
432
433 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
434 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
435 80.0 + width, 150.0 + (m * scale_y));
436
437 /* write some basic info as a comment, including some help */
438 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
439 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
440 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
441 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
442 "<!-- point your browser to this file. -->\n\n"
443 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION);
444
445 /* style sheet */
446 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
447 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
448 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
449 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
450 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
451 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
452 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
453 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
454 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
455 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
456 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
457 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
458 "// line.sec1 { }\n"
459 " line.sec5 { stroke-width: 2; }\n"
460 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
461 " text { font-family: Verdana, Helvetica; font-size: 10; }\n"
462 " text.sec { font-size: 8; }\n"
463 " ]]>\n </style>\n</defs>\n\n");
464
465 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
466 svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
467 isempty(osname)? "Linux" : osname,
468 name.nodename, name.release, name.version, name.machine);
469 svg("<text x=\"20\" y=\"%.0f\">Legend: Red = Activating; Pink = Active; Dark Pink = Deactivating</text>",
470 120.0 + (m *scale_y));
471
472 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (scale_x * boot->firmware_time));
473 svg_graph_box(m, -boot->firmware_time, boot->finish_time);
474
475 if (boot->firmware_time) {
476 svg_bar("firmware", -(int64_t) boot->firmware_time, -(int64_t) boot->loader_time, y);
477 svg_text(-(int64_t) boot->firmware_time, y, "firmware");
478 y++;
479 }
480 if (boot->loader_time) {
481 svg_bar("loader", -(int64_t) boot->loader_time, 0, y);
482 svg_text(-(int64_t) boot->loader_time, y, "loader");
483 y++;
484 }
485 if (boot->kernel_time) {
486 svg_bar("kernel", 0, boot->kernel_done_time, y);
487 svg_text(0, y, "kernel");
488 y++;
489 }
490 if (boot->initrd_time) {
491 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
492 svg_text(boot->initrd_time, y, "initrd");
493 y++;
494 }
495 svg_bar("userspace", boot->userspace_time, boot->finish_time, y);
496 svg_text(boot->userspace_time, y, "userspace");
497 y++;
498
499 for (struct unit_times *u = times; u < times + n; u++) {
500 if (!u->name)
501 continue;
502 svg_bar("activating", u->ixt, u->aet, y);
503 svg_bar("active", u->aet, u->axt, y);
504 svg_bar("deactivating", u->axt, u->iet, y);
505 svg_text(u->ixt, y, u->time? "%s (%llums)" : "%s", u->name, (unsigned long long)u->time);
506 y++;
507 }
508 svg("</g>\n\n");
509
510 svg("</svg>");
511 return 0;
512 }
513
514 static int analyze_blame(DBusConnection *bus)
515 {
516 struct unit_times *times;
517 int n = acquire_time_data(bus, &times);
518 if (n<=0)
519 return n;
520
521 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
522
523 for (int i = 0; i < n; i++) {
524 if (times[i].time)
525 printf("%6llums %s\n", (unsigned long long)times[i].time, times[i].name);
526 }
527 return 0;
528 }
529
530 static int analyze_time(DBusConnection *bus)
531 {
532 char *buf;
533 buf = pretty_boot_time(bus);
534 if (!buf)
535 return -EIO;
536 if (puts(buf) == EOF)
537 return -errno;
538 return 0;
539 }
540
541 static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
542
543 static const char * const colors[] = {
544 "Requires", "[color=\"black\"]",
545 "RequiresOverridable", "[color=\"black\"]",
546 "Requisite", "[color=\"darkblue\"]",
547 "RequisiteOverridable", "[color=\"darkblue\"]",
548 "Wants", "[color=\"grey66\"]",
549 "Conflicts", "[color=\"red\"]",
550 "ConflictedBy", "[color=\"red\"]",
551 "After", "[color=\"green\"]"
552 };
553
554 const char *c = NULL;
555 unsigned i;
556
557 assert(name);
558 assert(prop);
559 assert(iter);
560
561 for (i = 0; i < ELEMENTSOF(colors); i += 2)
562 if (streq(colors[i], prop)) {
563 c = colors[i+1];
564 break;
565 }
566
567 if (!c)
568 return 0;
569
570 if (arg_dot != DEP_ALL)
571 if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
572 return 0;
573
574 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
575 dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
576 DBusMessageIter sub;
577
578 dbus_message_iter_recurse(iter, &sub);
579
580 for (dbus_message_iter_recurse(iter, &sub);
581 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
582 dbus_message_iter_next(&sub)) {
583 const char *s;
584
585 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
586 dbus_message_iter_get_basic(&sub, &s);
587 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
588 }
589 }
590
591 return 0;
592 }
593
594 static int graph_one(DBusConnection *bus, const struct unit_info *u) {
595 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
596 const char *interface = "org.freedesktop.systemd1.Unit";
597 int r;
598 DBusMessageIter iter, sub, sub2, sub3;
599
600 assert(bus);
601 assert(u);
602
603 r = bus_method_call_with_reply(
604 bus,
605 "org.freedesktop.systemd1",
606 u->unit_path,
607 "org.freedesktop.DBus.Properties",
608 "GetAll",
609 &reply,
610 NULL,
611 DBUS_TYPE_STRING, &interface,
612 DBUS_TYPE_INVALID);
613 if (r < 0)
614 return r;
615
616 if (!dbus_message_iter_init(reply, &iter) ||
617 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
618 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
619 log_error("Failed to parse reply.");
620 return -EIO;
621 }
622
623 for (dbus_message_iter_recurse(&iter, &sub);
624 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
625 dbus_message_iter_next(&sub)) {
626 const char *prop;
627
628 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
629 dbus_message_iter_recurse(&sub, &sub2);
630
631 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
632 dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
633 log_error("Failed to parse reply.");
634 return -EIO;
635 }
636
637 dbus_message_iter_recurse(&sub2, &sub3);
638 r = graph_one_property(u->id, prop, &sub3);
639 if (r < 0)
640 return r;
641 }
642
643 return 0;
644 }
645
646 static int dot(DBusConnection *bus) {
647 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
648 DBusMessageIter iter, sub;
649 int r;
650
651 r = bus_method_call_with_reply(
652 bus,
653 "org.freedesktop.systemd1",
654 "/org/freedesktop/systemd1",
655 "org.freedesktop.systemd1.Manager",
656 "ListUnits",
657 &reply,
658 NULL,
659 DBUS_TYPE_INVALID);
660 if (r < 0)
661 return r;
662
663 if (!dbus_message_iter_init(reply, &iter) ||
664 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
665 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
666 log_error("Failed to parse reply.");
667 return -EIO;
668 }
669
670 printf("digraph systemd {\n");
671
672 for (dbus_message_iter_recurse(&iter, &sub);
673 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
674 dbus_message_iter_next(&sub)) {
675 struct unit_info u;
676
677 r = bus_parse_unit_info(&sub, &u);
678 if (r < 0)
679 return -EIO;
680
681 r = graph_one(bus, &u);
682 if (r < 0)
683 return r;
684 }
685
686 printf("}\n");
687
688 log_info(" Color legend: black = Requires\n"
689 " dark blue = Requisite\n"
690 " dark grey = Wants\n"
691 " red = Conflicts\n"
692 " green = After\n");
693
694 if (on_tty())
695 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
696 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
697
698 return 0;
699 }
700
701 static void analyze_help(void)
702 {
703 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
704 "Process systemd profiling information\n\n"
705 " -h --help Show this help\n"
706 " --version Show package version\n"
707 " --system Connect to system manager\n"
708 " --user Connect to user service manager\n"
709 " --order When generating a dependency graph, show only order\n"
710 " --require When generating a dependency graph, show only requirement\n\n"
711 "Commands:\n"
712 " time Print time spent in the kernel before reaching userspace\n"
713 " blame Print list of running units ordered by time to init\n"
714 " plot Output SVG graphic showing service initialization\n"
715 " dot Dump dependency graph (in dot(1) format)\n\n",
716 program_invocation_short_name);
717 }
718
719 static int parse_argv(int argc, char *argv[])
720 {
721 enum {
722 ARG_VERSION = 0x100,
723 ARG_ORDER,
724 ARG_REQUIRE,
725 ARG_USER,
726 ARG_SYSTEM
727 };
728
729 static const struct option options[] = {
730 { "help", no_argument, NULL, 'h' },
731 { "version", no_argument, NULL, ARG_VERSION },
732 { "order", no_argument, NULL, ARG_ORDER },
733 { "require", no_argument, NULL, ARG_REQUIRE },
734 { "user", no_argument, NULL, ARG_USER },
735 { "system", no_argument, NULL, ARG_SYSTEM },
736 { NULL, 0, NULL, 0 }
737 };
738
739 assert(argc >= 0);
740 assert(argv);
741
742 while (true) {
743 switch (getopt_long(argc, argv, "h", options, NULL)) {
744 case 'h':
745 analyze_help();
746 return 0;
747 case ARG_VERSION:
748 puts(PACKAGE_STRING "\n" SYSTEMD_FEATURES);
749 return 0;
750 case ARG_USER:
751 arg_scope = UNIT_FILE_USER;
752 break;
753 case ARG_SYSTEM:
754 arg_scope = UNIT_FILE_SYSTEM;
755 break;
756 case ARG_ORDER:
757 arg_dot = DEP_ORDER;
758 break;
759 case ARG_REQUIRE:
760 arg_dot = DEP_REQUIRE;
761 break;
762 case -1:
763 return 1;
764 case '?':
765 return -EINVAL;
766 default:
767 assert_not_reached("Unhandled option");
768 }
769 }
770 }
771
772 int main(int argc, char *argv[]) {
773 int r;
774 DBusConnection *bus = NULL;
775
776 setlocale(LC_ALL, "");
777 log_parse_environment();
778 log_open();
779
780 r = parse_argv(argc, argv);
781 if (r == 0)
782 return 0;
783 if (r < 0)
784 return 1;
785
786 bus = dbus_bus_get(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, NULL);
787 if (!bus)
788 return 1;
789
790 if (!argv[optind] || streq(argv[optind], "time"))
791 r = analyze_time(bus);
792 else if (streq(argv[optind], "blame"))
793 r = analyze_blame(bus);
794 else if (streq(argv[optind], "plot"))
795 r = analyze_plot(bus);
796 else if (streq(argv[optind], "dot"))
797 r = dot(bus);
798 else
799 log_error("Unknown operation '%s'.", argv[optind]);
800
801 dbus_connection_unref(bus);
802 if (r)
803 return 1;
804 return 0;
805 }