]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze.c
resolved: fix wrong error code (#7601)
[thirdparty/systemd.git] / src / analyze / analyze.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2265fbf7
SP
2/***
3 This file is part of systemd.
4
ffaa0e25
LP
5 Copyright 2010-2013 Lennart Poettering
6 Copyright 2013 Simon Peeters
2265fbf7
SP
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
2265fbf7
SP
22#include <getopt.h>
23#include <locale.h>
3f6fd1ba
LP
24#include <stdio.h>
25#include <stdlib.h>
2265fbf7 26
048ecf5b 27#include "sd-bus.h"
3f6fd1ba 28
b5efdb8a 29#include "alloc-util.h"
3f6fd1ba 30#include "analyze-verify.h"
048ecf5b 31#include "bus-error.h"
20b16441 32#include "bus-unit-util.h"
3f6fd1ba 33#include "bus-util.h"
6d86f4bd 34#include "calendarspec.h"
7d50b32a 35#include "glob-util.h"
bb150966 36#include "hashmap.h"
8752c575 37#include "locale-util.h"
3f6fd1ba 38#include "log.h"
9ea9d4cf 39#include "pager.h"
6bedfcbb 40#include "parse-util.h"
349cc4a5 41#if HAVE_SECCOMP
869feb33 42#include "seccomp-util.h"
0f734bdc 43#endif
3f6fd1ba
LP
44#include "special.h"
45#include "strv.h"
46#include "strxcpyx.h"
288a74cc 47#include "terminal-util.h"
3f6fd1ba
LP
48#include "unit-name.h"
49#include "util.h"
2265fbf7 50
2f6eb835 51#define SCALE_X (0.1 / 1000.0) /* pixels per us */
a213b7e9 52#define SCALE_Y (20.0)
2f6eb835 53
2265fbf7 54#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
c170f3a4 55
2265fbf7 56#define svg(...) printf(__VA_ARGS__)
c170f3a4
LP
57
58#define svg_bar(class, x1, x2, y) \
2265fbf7 59 svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
c170f3a4 60 (class), \
2f6eb835
LP
61 SCALE_X * (x1), SCALE_Y * (y), \
62 SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
c170f3a4
LP
63
64#define svg_text(b, x, y, format, ...) \
65 do { \
2f6eb835 66 svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
c170f3a4
LP
67 svg(format, ## __VA_ARGS__); \
68 svg("</text>\n"); \
9ed794a3 69 } while (false)
2265fbf7 70
1700761b
SP
71static enum dot {
72 DEP_ALL,
73 DEP_ORDER,
74 DEP_REQUIRE
75} arg_dot = DEP_ALL;
e55933db
ŁS
76static char** arg_dot_from_patterns = NULL;
77static char** arg_dot_to_patterns = NULL;
9ea9d4cf
LP
78static usec_t arg_fuzz = 0;
79static bool arg_no_pager = false;
3cd26e7c
LP
80static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
81static char *arg_host = NULL;
82static bool arg_user = false;
1d3bc017 83static bool arg_man = true;
641c0fd1 84static bool arg_generators = false;
bb150966 85
2265fbf7 86struct boot_times {
c170f3a4
LP
87 usec_t firmware_time;
88 usec_t loader_time;
89 usec_t kernel_time;
90 usec_t kernel_done_time;
91 usec_t initrd_time;
92 usec_t userspace_time;
93 usec_t finish_time;
c2e0d600
TA
94 usec_t security_start_time;
95 usec_t security_finish_time;
518d10e9
UTL
96 usec_t generators_start_time;
97 usec_t generators_finish_time;
d9acfb71
TA
98 usec_t unitsload_start_time;
99 usec_t unitsload_finish_time;
06bef033
IS
100
101 /*
102 * If we're analyzing the user instance, all timestamps will be offset
103 * by its own start-up timestamp, which may be arbitrarily big.
104 * With "plot", this causes arbitrarily wide output SVG files which almost
105 * completely consist of empty space. Thus we cancel out this offset.
106 *
107 * This offset is subtracted from times above by acquire_boot_times(),
108 * but it still needs to be subtracted from unit-specific timestamps
109 * (so it is stored here for reference).
110 */
111 usec_t reverse_offset;
2265fbf7 112};
9ea9d4cf 113
2265fbf7
SP
114struct unit_times {
115 char *name;
cc27380c
TA
116 usec_t activating;
117 usec_t activated;
118 usec_t deactivated;
119 usec_t deactivating;
c170f3a4 120 usec_t time;
2265fbf7
SP
121};
122
7e690cef
DH
123struct host_info {
124 char *hostname;
125 char *kernel_name;
126 char *kernel_release;
127 char *kernel_version;
128 char *os_pretty_name;
129 char *virtualization;
130 char *architecture;
131};
132
048ecf5b 133static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
4afd3348 134 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4 135 int r;
2265fbf7 136
988b9df2
LP
137 assert(bus);
138 assert(path);
139 assert(interface);
140 assert(property);
141 assert(val);
142
a936124f
TA
143 r = sd_bus_get_property_trivial(
144 bus,
145 "org.freedesktop.systemd1",
146 path,
147 interface,
148 property,
149 &error,
150 't', val);
048ecf5b
TA
151
152 if (r < 0) {
153 log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
2265fbf7 154 return r;
2265fbf7
SP
155 }
156
2265fbf7
SP
157 return 0;
158}
159
988b9df2 160static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
4afd3348 161 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
988b9df2
LP
162 int r;
163
164 assert(bus);
165 assert(path);
166 assert(property);
167 assert(strv);
168
169 r = sd_bus_get_property_strv(
170 bus,
171 "org.freedesktop.systemd1",
172 path,
173 "org.freedesktop.systemd1.Unit",
174 property,
175 &error,
176 strv);
177 if (r < 0) {
178 log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
179 return r;
180 }
181
182 return 0;
183}
184
c170f3a4 185static int compare_unit_time(const void *a, const void *b) {
2265fbf7
SP
186 return compare(((struct unit_times *)b)->time,
187 ((struct unit_times *)a)->time);
188}
189
c170f3a4 190static int compare_unit_start(const void *a, const void *b) {
cc27380c
TA
191 return compare(((struct unit_times *)a)->activating,
192 ((struct unit_times *)b)->activating);
2265fbf7
SP
193}
194
c170f3a4
LP
195static void free_unit_times(struct unit_times *t, unsigned n) {
196 struct unit_times *p;
197
198 for (p = t; p < t + n; p++)
199 free(p->name);
200
201 free(t);
202}
203
06bef033
IS
204static void subtract_timestamp(usec_t *a, usec_t b) {
205 assert(a);
206
207 if (*a > 0) {
208 assert(*a >= b);
209 *a -= b;
210 }
211}
212
048ecf5b 213static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
2265fbf7
SP
214 static struct boot_times times;
215 static bool cached = false;
c170f3a4 216
2265fbf7 217 if (cached)
c170f3a4
LP
218 goto finish;
219
220 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
2265fbf7
SP
221
222 if (bus_get_uint64_property(bus,
223 "/org/freedesktop/systemd1",
224 "org.freedesktop.systemd1.Manager",
225 "FirmwareTimestampMonotonic",
226 &times.firmware_time) < 0 ||
227 bus_get_uint64_property(bus,
228 "/org/freedesktop/systemd1",
229 "org.freedesktop.systemd1.Manager",
230 "LoaderTimestampMonotonic",
231 &times.loader_time) < 0 ||
232 bus_get_uint64_property(bus,
233 "/org/freedesktop/systemd1",
234 "org.freedesktop.systemd1.Manager",
235 "KernelTimestamp",
236 &times.kernel_time) < 0 ||
237 bus_get_uint64_property(bus,
238 "/org/freedesktop/systemd1",
239 "org.freedesktop.systemd1.Manager",
240 "InitRDTimestampMonotonic",
241 &times.initrd_time) < 0 ||
242 bus_get_uint64_property(bus,
243 "/org/freedesktop/systemd1",
244 "org.freedesktop.systemd1.Manager",
245 "UserspaceTimestampMonotonic",
246 &times.userspace_time) < 0 ||
247 bus_get_uint64_property(bus,
248 "/org/freedesktop/systemd1",
249 "org.freedesktop.systemd1.Manager",
250 "FinishTimestampMonotonic",
518d10e9 251 &times.finish_time) < 0 ||
c2e0d600
TA
252 bus_get_uint64_property(bus,
253 "/org/freedesktop/systemd1",
254 "org.freedesktop.systemd1.Manager",
255 "SecurityStartTimestampMonotonic",
256 &times.security_start_time) < 0 ||
257 bus_get_uint64_property(bus,
258 "/org/freedesktop/systemd1",
259 "org.freedesktop.systemd1.Manager",
260 "SecurityFinishTimestampMonotonic",
261 &times.security_finish_time) < 0 ||
518d10e9
UTL
262 bus_get_uint64_property(bus,
263 "/org/freedesktop/systemd1",
264 "org.freedesktop.systemd1.Manager",
265 "GeneratorsStartTimestampMonotonic",
266 &times.generators_start_time) < 0 ||
267 bus_get_uint64_property(bus,
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "GeneratorsFinishTimestampMonotonic",
d9acfb71
TA
271 &times.generators_finish_time) < 0 ||
272 bus_get_uint64_property(bus,
273 "/org/freedesktop/systemd1",
274 "org.freedesktop.systemd1.Manager",
275 "UnitsLoadStartTimestampMonotonic",
276 &times.unitsload_start_time) < 0 ||
277 bus_get_uint64_property(bus,
278 "/org/freedesktop/systemd1",
279 "org.freedesktop.systemd1.Manager",
280 "UnitsLoadFinishTimestampMonotonic",
281 &times.unitsload_finish_time) < 0)
c170f3a4 282 return -EIO;
2265fbf7 283
c170f3a4 284 if (times.finish_time <= 0) {
2265fbf7 285 log_error("Bootup is not yet finished. Please try again later.");
988b9df2 286 return -EINPROGRESS;
2265fbf7
SP
287 }
288
06bef033
IS
289 if (arg_user) {
290 /*
291 * User-instance-specific timestamps processing
292 * (see comment to reverse_offset in struct boot_times).
293 */
294 times.reverse_offset = times.userspace_time;
295
296 times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
297 subtract_timestamp(&times.finish_time, times.reverse_offset);
298
299 subtract_timestamp(&times.security_start_time, times.reverse_offset);
300 subtract_timestamp(&times.security_finish_time, times.reverse_offset);
301
302 subtract_timestamp(&times.generators_start_time, times.reverse_offset);
303 subtract_timestamp(&times.generators_finish_time, times.reverse_offset);
304
305 subtract_timestamp(&times.unitsload_start_time, times.reverse_offset);
306 subtract_timestamp(&times.unitsload_finish_time, times.reverse_offset);
307 } else {
308 if (times.initrd_time)
309 times.kernel_done_time = times.initrd_time;
310 else
311 times.kernel_done_time = times.userspace_time;
312 }
2265fbf7
SP
313
314 cached = true;
c170f3a4
LP
315
316finish:
317 *bt = &times;
318 return 0;
2265fbf7
SP
319}
320
7e690cef 321static void free_host_info(struct host_info *hi) {
b1b533a0
LP
322
323 if (!hi)
19f462f2 324 return;
b1b533a0 325
7e690cef
DH
326 free(hi->hostname);
327 free(hi->kernel_name);
328 free(hi->kernel_release);
329 free(hi->kernel_version);
330 free(hi->os_pretty_name);
331 free(hi->virtualization);
332 free(hi->architecture);
333 free(hi);
334}
b1b533a0 335
19f462f2 336DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
7e690cef 337
29b8b5ce 338static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
4afd3348
LP
339 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
340 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
29b8b5ce 341 int r, c = 0;
06bef033 342 struct boot_times *boot_times = NULL;
29b8b5ce
IS
343 struct unit_times *unit_times = NULL;
344 size_t size = 0;
345 UnitInfo u;
346
06bef033
IS
347 r = acquire_boot_times(bus, &boot_times);
348 if (r < 0)
349 goto fail;
350
29b8b5ce
IS
351 r = sd_bus_call_method(
352 bus,
353 "org.freedesktop.systemd1",
354 "/org/freedesktop/systemd1",
355 "org.freedesktop.systemd1.Manager",
356 "ListUnits",
357 &error, &reply,
358 NULL);
359 if (r < 0) {
360 log_error("Failed to list units: %s", bus_error_message(&error, -r));
361 goto fail;
362 }
363
364 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
365 if (r < 0) {
366 bus_log_parse_error(r);
367 goto fail;
368 }
369
370 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
371 struct unit_times *t;
372
373 if (!GREEDY_REALLOC(unit_times, size, c+1)) {
374 r = log_oom();
375 goto fail;
376 }
377
378 t = unit_times+c;
379 t->name = NULL;
380
381 assert_cc(sizeof(usec_t) == sizeof(uint64_t));
382
383 if (bus_get_uint64_property(bus, u.unit_path,
384 "org.freedesktop.systemd1.Unit",
385 "InactiveExitTimestampMonotonic",
386 &t->activating) < 0 ||
387 bus_get_uint64_property(bus, u.unit_path,
388 "org.freedesktop.systemd1.Unit",
389 "ActiveEnterTimestampMonotonic",
390 &t->activated) < 0 ||
391 bus_get_uint64_property(bus, u.unit_path,
392 "org.freedesktop.systemd1.Unit",
393 "ActiveExitTimestampMonotonic",
394 &t->deactivating) < 0 ||
395 bus_get_uint64_property(bus, u.unit_path,
396 "org.freedesktop.systemd1.Unit",
397 "InactiveEnterTimestampMonotonic",
398 &t->deactivated) < 0) {
399 r = -EIO;
400 goto fail;
401 }
402
06bef033
IS
403 subtract_timestamp(&t->activating, boot_times->reverse_offset);
404 subtract_timestamp(&t->activated, boot_times->reverse_offset);
405 subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
406 subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
407
29b8b5ce
IS
408 if (t->activated >= t->activating)
409 t->time = t->activated - t->activating;
410 else if (t->deactivated >= t->activating)
411 t->time = t->deactivated - t->activating;
412 else
413 t->time = 0;
414
415 if (t->activating == 0)
416 continue;
417
418 t->name = strdup(u.id);
419 if (t->name == NULL) {
420 r = log_oom();
421 goto fail;
422 }
423 c++;
424 }
425 if (r < 0) {
426 bus_log_parse_error(r);
427 goto fail;
428 }
429
430 *out = unit_times;
431 return c;
432
433fail:
434 if (unit_times)
435 free_unit_times(unit_times, (unsigned) c);
436 return r;
437}
438
7e690cef 439static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
7e690cef 440 static const struct bus_properties_map hostname_map[] = {
b1b533a0
LP
441 { "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
442 { "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
443 { "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
444 { "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
7e690cef
DH
445 { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
446 {}
447 };
448
449 static const struct bus_properties_map manager_map[] = {
b1b533a0
LP
450 { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
451 { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
7e690cef
DH
452 {}
453 };
454
4afd3348 455 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
b1b533a0
LP
456 _cleanup_(free_host_infop) struct host_info *host;
457 int r;
458
7e690cef
DH
459 host = new0(struct host_info, 1);
460 if (!host)
461 return log_oom();
462
463 r = bus_map_all_properties(bus,
464 "org.freedesktop.hostname1",
465 "/org/freedesktop/hostname1",
466 hostname_map,
f9e0eefc 467 &error,
7e690cef
DH
468 host);
469 if (r < 0)
b1b533a0 470 log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
7e690cef
DH
471
472 r = bus_map_all_properties(bus,
473 "org.freedesktop.systemd1",
474 "/org/freedesktop/systemd1",
475 manager_map,
f9e0eefc 476 &error,
7e690cef 477 host);
febda62a 478 if (r < 0)
b1b533a0 479 return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
7e690cef
DH
480
481 *hi = host;
febda62a 482 host = NULL;
b1b533a0 483
7e690cef 484 return 0;
7e690cef
DH
485}
486
048ecf5b 487static int pretty_boot_time(sd_bus *bus, char **_buf) {
c170f3a4 488 char ts[FORMAT_TIMESPAN_MAX];
2265fbf7 489 struct boot_times *t;
2265fbf7 490 static char buf[4096];
c170f3a4
LP
491 size_t size;
492 char *ptr;
493 int r;
bd07d3d0
JR
494 usec_t activated_time = USEC_INFINITY;
495 _cleanup_free_ char* path = NULL, *unit_id = NULL;
496 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
c170f3a4
LP
497
498 r = acquire_boot_times(bus, &t);
499 if (r < 0)
500 return r;
2265fbf7 501
bd07d3d0
JR
502 path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
503 if (!path)
504 return log_oom();
505
506 r = sd_bus_get_property_string(
507 bus,
508 "org.freedesktop.systemd1",
509 path,
510 "org.freedesktop.systemd1.Unit",
511 "Id",
512 &error,
513 &unit_id);
514 if (r < 0) {
515 log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
516 unit_id = NULL;
517 }
518
519 r = bus_get_uint64_property(bus, path,
520 "org.freedesktop.systemd1.Unit",
521 "ActiveEnterTimestampMonotonic",
522 &activated_time);
523 if (r < 0) {
524 log_info_errno(r, "default.target seems not to be started. Continuing...");
525 activated_time = USEC_INFINITY;
526 }
527
c170f3a4
LP
528 ptr = buf;
529 size = sizeof(buf);
2265fbf7
SP
530
531 size = strpcpyf(&ptr, size, "Startup finished in ");
532 if (t->firmware_time)
2fa4092c 533 size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
2265fbf7 534 if (t->loader_time)
2fa4092c 535 size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
2265fbf7 536 if (t->kernel_time)
2fa4092c 537 size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
2265fbf7 538 if (t->initrd_time > 0)
2fa4092c 539 size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
2265fbf7 540
2fa4092c 541 size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
06bef033 542 strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
2265fbf7 543
bd07d3d0
JR
544 if (unit_id && activated_time != USEC_INFINITY)
545 size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC));
546
c170f3a4
LP
547 ptr = strdup(buf);
548 if (!ptr)
549 return log_oom();
550
551 *_buf = ptr;
552 return 0;
2265fbf7
SP
553}
554
c170f3a4
LP
555static void svg_graph_box(double height, double begin, double end) {
556 long long i;
557
2265fbf7
SP
558 /* outside box, fill */
559 svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
2f6eb835 560 SCALE_X * (end - begin), SCALE_Y * height);
2265fbf7 561
c170f3a4 562 for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
2265fbf7 563 /* lines for each second */
c170f3a4 564 if (i % 5000000 == 0)
2265fbf7
SP
565 svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
566 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 567 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
c170f3a4 568 else if (i % 1000000 == 0)
2265fbf7
SP
569 svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
570 " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
2f6eb835 571 SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
2265fbf7
SP
572 else
573 svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
2f6eb835 574 SCALE_X * i, SCALE_X * i, SCALE_Y * height);
2265fbf7
SP
575 }
576}
577
048ecf5b 578static int analyze_plot(sd_bus *bus) {
b1b533a0 579 _cleanup_(free_host_infop) struct host_info *host = NULL;
2265fbf7
SP
580 struct unit_times *times;
581 struct boot_times *boot;
2265fbf7
SP
582 int n, m = 1, y=0;
583 double width;
7e690cef 584 _cleanup_free_ char *pretty_times = NULL;
c170f3a4 585 struct unit_times *u;
2265fbf7 586
c170f3a4
LP
587 n = acquire_boot_times(bus, &boot);
588 if (n < 0)
589 return n;
2265fbf7 590
c170f3a4
LP
591 n = pretty_boot_time(bus, &pretty_times);
592 if (n < 0)
593 return n;
2265fbf7 594
7e690cef
DH
595 n = acquire_host_info(bus, &host);
596 if (n < 0)
597 return n;
2265fbf7
SP
598
599 n = acquire_time_data(bus, &times);
c170f3a4 600 if (n <= 0)
19f462f2 601 return n;
2265fbf7
SP
602
603 qsort(times, n, sizeof(struct unit_times), compare_unit_start);
604
2f6eb835 605 width = SCALE_X * (boot->firmware_time + boot->finish_time);
2265fbf7
SP
606 if (width < 800.0)
607 width = 800.0;
608
609 if (boot->firmware_time > boot->loader_time)
610 m++;
611 if (boot->loader_time) {
612 m++;
613 if (width < 1000.0)
614 width = 1000.0;
615 }
616 if (boot->initrd_time)
617 m++;
618 if (boot->kernel_time)
619 m++;
620
c170f3a4 621 for (u = times; u < times + n; u++) {
95168f7d 622 double text_start, text_width;
c170f3a4 623
cc27380c
TA
624 if (u->activating < boot->userspace_time ||
625 u->activating > boot->finish_time) {
a1e58e8e 626 u->name = mfree(u->name);
2265fbf7
SP
627 continue;
628 }
95168f7d
TA
629
630 /* If the text cannot fit on the left side then
631 * increase the svg width so it fits on the right.
632 * TODO: calculate the text width more accurately */
633 text_width = 8.0 * strlen(u->name);
cc27380c 634 text_start = (boot->firmware_time + u->activating) * SCALE_X;
95168f7d
TA
635 if (text_width > text_start && text_width + text_start > width)
636 width = text_width + text_start;
2265fbf7 637
cc27380c
TA
638 if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
639 && u->activated == 0 && u->deactivating == 0)
640 u->activated = u->deactivating = u->deactivated;
641 if (u->activated < u->activating || u->activated > boot->finish_time)
642 u->activated = boot->finish_time;
643 if (u->deactivating < u->activated || u->activated > boot->finish_time)
644 u->deactivating = boot->finish_time;
645 if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
646 u->deactivated = boot->finish_time;
2265fbf7
SP
647 m++;
648 }
649
650 svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
651 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
652 "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
653
654 svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
655 "xmlns=\"http://www.w3.org/2000/svg\">\n\n",
518d10e9 656 80.0 + width, 150.0 + (m * SCALE_Y) +
d9acfb71 657 5 * SCALE_Y /* legend */);
2265fbf7
SP
658
659 /* write some basic info as a comment, including some help */
660 svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
661 "<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
662 "<!-- that render these files properly but much slower are ImageMagick, -->\n"
663 "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
664 "<!-- point your browser to this file. -->\n\n"
948aaa7c 665 "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION);
2265fbf7
SP
666
667 /* style sheet */
668 svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
669 " rect { stroke-width: 1; stroke-opacity: 0; }\n"
418e3750 670 " rect.background { fill: rgb(255,255,255); }\n"
2265fbf7
SP
671 " rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
672 " rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
673 " rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
674 " rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
675 " rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
676 " rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
677 " rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
678 " rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
c2e0d600 679 " rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
518d10e9 680 " rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
d9acfb71 681 " rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
2265fbf7
SP
682 " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
683 " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
684 "// line.sec1 { }\n"
685 " line.sec5 { stroke-width: 2; }\n"
686 " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
2b7d6965
TA
687 " text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
688 " text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
689 " text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
690 " text.sec { font-size: 10px; }\n"
2265fbf7
SP
691 " ]]>\n </style>\n</defs>\n\n");
692
418e3750 693 svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
2265fbf7 694 svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
7e690cef 695 svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
febda62a 696 isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
caeacefe
ŁS
697 strempty(host->hostname),
698 strempty(host->kernel_name),
699 strempty(host->kernel_release),
700 strempty(host->kernel_version),
701 strempty(host->architecture),
702 strempty(host->virtualization));
2265fbf7 703
2f6eb835 704 svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
b5cfa740 705 svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
2265fbf7
SP
706
707 if (boot->firmware_time) {
c170f3a4
LP
708 svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
709 svg_text(true, -(double) boot->firmware_time, y, "firmware");
2265fbf7
SP
710 y++;
711 }
712 if (boot->loader_time) {
c170f3a4
LP
713 svg_bar("loader", -(double) boot->loader_time, 0, y);
714 svg_text(true, -(double) boot->loader_time, y, "loader");
2265fbf7
SP
715 y++;
716 }
717 if (boot->kernel_time) {
718 svg_bar("kernel", 0, boot->kernel_done_time, y);
c170f3a4 719 svg_text(true, 0, y, "kernel");
2265fbf7
SP
720 y++;
721 }
722 if (boot->initrd_time) {
723 svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
c170f3a4 724 svg_text(true, boot->initrd_time, y, "initrd");
2265fbf7
SP
725 y++;
726 }
518d10e9 727 svg_bar("active", boot->userspace_time, boot->finish_time, y);
c2e0d600 728 svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
518d10e9 729 svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
d9acfb71 730 svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
95168f7d 731 svg_text(true, boot->userspace_time, y, "systemd");
2265fbf7
SP
732 y++;
733
c170f3a4
LP
734 for (u = times; u < times + n; u++) {
735 char ts[FORMAT_TIMESPAN_MAX];
fd4a5ec6 736 bool b;
c170f3a4 737
2265fbf7
SP
738 if (!u->name)
739 continue;
c170f3a4 740
cc27380c
TA
741 svg_bar("activating", u->activating, u->activated, y);
742 svg_bar("active", u->activated, u->deactivating, y);
743 svg_bar("deactivating", u->deactivating, u->deactivated, y);
c170f3a4 744
95168f7d 745 /* place the text on the left if we have passed the half of the svg width */
cc27380c 746 b = u->activating * SCALE_X < width / 2;
fd4a5ec6 747 if (u->time)
cc27380c 748 svg_text(b, u->activating, y, "%s (%s)",
2fa4092c 749 u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
c170f3a4 750 else
cc27380c 751 svg_text(b, u->activating, y, "%s", u->name);
2265fbf7
SP
752 y++;
753 }
518d10e9 754
b5cfa740
TA
755 svg("</g>\n");
756
518d10e9 757 /* Legend */
b5cfa740 758 svg("<g transform=\"translate(20,100)\">\n");
518d10e9
UTL
759 y++;
760 svg_bar("activating", 0, 300000, y);
95168f7d 761 svg_text(true, 400000, y, "Activating");
518d10e9
UTL
762 y++;
763 svg_bar("active", 0, 300000, y);
95168f7d 764 svg_text(true, 400000, y, "Active");
518d10e9
UTL
765 y++;
766 svg_bar("deactivating", 0, 300000, y);
95168f7d 767 svg_text(true, 400000, y, "Deactivating");
518d10e9 768 y++;
c2e0d600
TA
769 svg_bar("security", 0, 300000, y);
770 svg_text(true, 400000, y, "Setting up security module");
771 y++;
518d10e9 772 svg_bar("generators", 0, 300000, y);
95168f7d 773 svg_text(true, 400000, y, "Generators");
518d10e9 774 y++;
d9acfb71 775 svg_bar("unitsload", 0, 300000, y);
95168f7d 776 svg_text(true, 400000, y, "Loading unit files");
d9acfb71 777 y++;
518d10e9 778
2265fbf7
SP
779 svg("</g>\n\n");
780
988b9df2 781 svg("</svg>\n");
c170f3a4
LP
782
783 free_unit_times(times, (unsigned) n);
784
7e690cef 785 n = 0;
7e690cef 786 return n;
2265fbf7
SP
787}
788
bb150966
HH
789static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
790 bool last, struct unit_times *times, struct boot_times *boot) {
791 unsigned int i;
792 char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
793
794 for (i = level; i != 0; i--)
323b7dc9 795 printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
bb150966 796
323b7dc9 797 printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
bb150966
HH
798
799 if (times) {
800 if (times->time)
54f8c958 801 printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
cc27380c 802 format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
54f8c958 803 format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
cc27380c
TA
804 else if (times->activated > boot->userspace_time)
805 printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
806 else
807 printf("%s", name);
988b9df2
LP
808 } else
809 printf("%s", name);
bb150966
HH
810 printf("\n");
811
812 return 0;
813}
814
048ecf5b 815static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
988b9df2 816 _cleanup_free_ char *path = NULL;
bb150966
HH
817
818 assert(bus);
819 assert(name);
820 assert(deps);
821
822 path = unit_dbus_path_from_name(name);
6e6ca4a5 823 if (path == NULL)
988b9df2 824 return -ENOMEM;
bb150966 825
988b9df2 826 return bus_get_unit_property_strv(bus, path, "After", deps);
bb150966
HH
827}
828
829static Hashmap *unit_times_hashmap;
830
831static int list_dependencies_compare(const void *_a, const void *_b) {
832 const char **a = (const char**) _a, **b = (const char**) _b;
833 usec_t usa = 0, usb = 0;
834 struct unit_times *times;
835
836 times = hashmap_get(unit_times_hashmap, *a);
837 if (times)
cc27380c 838 usa = times->activated;
bb150966
HH
839 times = hashmap_get(unit_times_hashmap, *b);
840 if (times)
cc27380c 841 usb = times->activated;
bb150966
HH
842
843 return usb - usa;
844}
845
048ecf5b 846static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
bb150966
HH
847 unsigned int branches) {
848 _cleanup_strv_free_ char **deps = NULL;
849 char **c;
850 int r = 0;
851 usec_t service_longest = 0;
852 int to_print = 0;
853 struct unit_times *times;
854 struct boot_times *boot;
855
988b9df2 856 if (strv_extend(units, name))
bb150966
HH
857 return log_oom();
858
859 r = list_dependencies_get_dependencies(bus, name, &deps);
860 if (r < 0)
861 return r;
862
7ff7394d 863 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
bb150966
HH
864
865 r = acquire_boot_times(bus, &boot);
866 if (r < 0)
867 return r;
868
869 STRV_FOREACH(c, deps) {
870 times = hashmap_get(unit_times_hashmap, *c);
871 if (times
cc27380c
TA
872 && times->activated
873 && times->activated <= boot->finish_time
874 && (times->activated >= service_longest
bb150966 875 || service_longest == 0)) {
cc27380c 876 service_longest = times->activated;
bb150966
HH
877 break;
878 }
879 }
880
881 if (service_longest == 0 )
882 return r;
883
884 STRV_FOREACH(c, deps) {
885 times = hashmap_get(unit_times_hashmap, *c);
ece174c5 886 if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz)
bb150966 887 to_print++;
bb150966
HH
888 }
889
f168c273 890 if (!to_print)
bb150966
HH
891 return r;
892
893 STRV_FOREACH(c, deps) {
894 times = hashmap_get(unit_times_hashmap, *c);
895 if (!times
cc27380c
TA
896 || !times->activated
897 || times->activated > boot->finish_time
898 || service_longest - times->activated > arg_fuzz)
bb150966
HH
899 continue;
900
901 to_print--;
902
903 r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
904 if (r < 0)
905 return r;
906
907 if (strv_contains(*units, *c)) {
908 r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
909 true, NULL, boot);
872c8faa
ZJS
910 if (r < 0)
911 return r;
bb150966
HH
912 continue;
913 }
914
915 r = list_dependencies_one(bus, *c, level + 1, units,
916 (branches << 1) | (to_print ? 1 : 0));
872c8faa 917 if (r < 0)
bb150966
HH
918 return r;
919
872c8faa 920 if (!to_print)
bb150966 921 break;
bb150966
HH
922 }
923 return 0;
924}
925
048ecf5b 926static int list_dependencies(sd_bus *bus, const char *name) {
bb150966
HH
927 _cleanup_strv_free_ char **units = NULL;
928 char ts[FORMAT_TIMESPAN_MAX];
929 struct unit_times *times;
930 int r;
0ee9613d
TA
931 const char *id;
932 _cleanup_free_ char *path = NULL;
4afd3348
LP
933 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
934 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
bb150966
HH
935 struct boot_times *boot;
936
937 assert(bus);
938
805bf39c 939 path = unit_dbus_path_from_name(name);
bb150966 940 if (path == NULL)
988b9df2 941 return -ENOMEM;
bb150966 942
a936124f
TA
943 r = sd_bus_get_property(
944 bus,
945 "org.freedesktop.systemd1",
946 path,
947 "org.freedesktop.systemd1.Unit",
948 "Id",
949 &error,
950 &reply,
951 "s");
048ecf5b 952 if (r < 0) {
5b30bef8 953 log_error("Failed to get ID: %s", bus_error_message(&error, -r));
bb150966 954 return r;
bb150966
HH
955 }
956
048ecf5b 957 r = sd_bus_message_read(reply, "s", &id);
5b30bef8
LP
958 if (r < 0)
959 return bus_log_parse_error(r);
bb150966 960
bb150966
HH
961 times = hashmap_get(unit_times_hashmap, id);
962
963 r = acquire_boot_times(bus, &boot);
964 if (r < 0)
965 return r;
966
967 if (times) {
968 if (times->time)
54f8c958
LP
969 printf("%s%s +%s%s\n", ansi_highlight_red(), id,
970 format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
cc27380c
TA
971 else if (times->activated > boot->userspace_time)
972 printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
bb150966
HH
973 else
974 printf("%s\n", id);
975 }
976
805bf39c 977 return list_dependencies_one(bus, name, 0, &units, 0);
bb150966
HH
978}
979
048ecf5b 980static int analyze_critical_chain(sd_bus *bus, char *names[]) {
bb150966 981 struct unit_times *times;
bb150966
HH
982 unsigned int i;
983 Hashmap *h;
988b9df2 984 int n, r;
bb150966
HH
985
986 n = acquire_time_data(bus, &times);
987 if (n <= 0)
988 return n;
989
d5099efc 990 h = hashmap_new(&string_hash_ops);
bb150966
HH
991 if (!h)
992 return -ENOMEM;
993
994 for (i = 0; i < (unsigned)n; i++) {
995 r = hashmap_put(h, times[i].name, &times[i]);
996 if (r < 0)
997 return r;
998 }
999 unit_times_hashmap = h;
1000
ea4b98e6 1001 pager_open(arg_no_pager, false);
9ea9d4cf 1002
bb150966
HH
1003 puts("The time after the unit is active or started is printed after the \"@\" character.\n"
1004 "The time the unit takes to start is printed after the \"+\" character.\n");
1005
805bf39c
GP
1006 if (!strv_isempty(names)) {
1007 char **name;
1008 STRV_FOREACH(name, names)
1009 list_dependencies(bus, *name);
9ea9d4cf 1010 } else
805bf39c 1011 list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
bb150966
HH
1012
1013 hashmap_free(h);
1014 free_unit_times(times, (unsigned) n);
1015 return 0;
1016}
1017
048ecf5b 1018static int analyze_blame(sd_bus *bus) {
2265fbf7 1019 struct unit_times *times;
c170f3a4
LP
1020 unsigned i;
1021 int n;
1022
1023 n = acquire_time_data(bus, &times);
1024 if (n <= 0)
2265fbf7
SP
1025 return n;
1026
1027 qsort(times, n, sizeof(struct unit_times), compare_unit_time);
1028
ea4b98e6 1029 pager_open(arg_no_pager, false);
9ea9d4cf 1030
c170f3a4
LP
1031 for (i = 0; i < (unsigned) n; i++) {
1032 char ts[FORMAT_TIMESPAN_MAX];
1033
1034 if (times[i].time > 0)
2fa4092c 1035 printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
2265fbf7 1036 }
c170f3a4
LP
1037
1038 free_unit_times(times, (unsigned) n);
2265fbf7
SP
1039 return 0;
1040}
1041
048ecf5b 1042static int analyze_time(sd_bus *bus) {
c170f3a4
LP
1043 _cleanup_free_ char *buf = NULL;
1044 int r;
1045
1046 r = pretty_boot_time(bus, &buf);
1047 if (r < 0)
1048 return r;
1049
1050 puts(buf);
2265fbf7
SP
1051 return 0;
1052}
1053
4387795e 1054static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) {
048ecf5b 1055 _cleanup_strv_free_ char **units = NULL;
048ecf5b
TA
1056 char **unit;
1057 int r;
6ecb6cec 1058 bool match_patterns;
1700761b 1059
048ecf5b 1060 assert(u);
1700761b 1061 assert(prop);
048ecf5b
TA
1062 assert(color);
1063
2404701e 1064 match_patterns = strv_fnmatch(patterns, u->id, 0);
6ecb6cec 1065
4387795e 1066 if (!strv_isempty(from_patterns) &&
6ecb6cec 1067 !match_patterns &&
4387795e 1068 !strv_fnmatch(from_patterns, u->id, 0))
6ecb6cec
ZJS
1069 return 0;
1070
6e6ca4a5 1071 r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
07d0eaa0 1072 if (r < 0)
988b9df2 1073 return r;
1700761b 1074
048ecf5b 1075 STRV_FOREACH(unit, units) {
6ecb6cec
ZJS
1076 bool match_patterns2;
1077
2404701e 1078 match_patterns2 = strv_fnmatch(patterns, *unit, 0);
816f25e8 1079
4387795e 1080 if (!strv_isempty(to_patterns) &&
6ecb6cec 1081 !match_patterns2 &&
4387795e 1082 !strv_fnmatch(to_patterns, *unit, 0))
bceccd5e 1083 continue;
e55933db 1084
6ecb6cec 1085 if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
bceccd5e 1086 continue;
048ecf5b
TA
1087
1088 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
1700761b
SP
1089 }
1090
1091 return 0;
1092}
1093
4387795e 1094static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
1700761b 1095 int r;
1700761b
SP
1096
1097 assert(bus);
1098 assert(u);
1099
8901b405 1100 if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
4387795e 1101 r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
048ecf5b
TA
1102 if (r < 0)
1103 return r;
1700761b
SP
1104 }
1105
8901b405 1106 if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
4387795e 1107 r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
8901b405
MS
1108 if (r < 0)
1109 return r;
1110 r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
048ecf5b
TA
1111 if (r < 0)
1112 return r;
4387795e 1113 r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
048ecf5b
TA
1114 if (r < 0)
1115 return r;
4387795e 1116 r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
048ecf5b
TA
1117 if (r < 0)
1118 return r;
1700761b
SP
1119 }
1120
1121 return 0;
1122}
1123
83efb7c2
EV
1124static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
1125 _cleanup_strv_free_ char **expanded_patterns = NULL;
1126 char **pattern;
1127 int r;
1128
1129 STRV_FOREACH(pattern, patterns) {
4afd3348 1130 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
83efb7c2
EV
1131 _cleanup_free_ char *unit = NULL, *unit_id = NULL;
1132
1133 if (strv_extend(&expanded_patterns, *pattern) < 0)
1134 return log_oom();
1135
1136 if (string_is_glob(*pattern))
1137 continue;
1138
1139 unit = unit_dbus_path_from_name(*pattern);
1140 if (!unit)
1141 return log_oom();
1142
1143 r = sd_bus_get_property_string(
1144 bus,
1145 "org.freedesktop.systemd1",
1146 unit,
1147 "org.freedesktop.systemd1.Unit",
1148 "Id",
1149 &error,
1150 &unit_id);
1151 if (r < 0)
1152 return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
1153
1154 if (!streq(*pattern, unit_id)) {
1155 if (strv_extend(&expanded_patterns, unit_id) < 0)
1156 return log_oom();
1157 }
1158 }
1159
1160 *ret = expanded_patterns;
1161 expanded_patterns = NULL; /* do not free */
1162
1163 return 0;
1164}
1165
048ecf5b 1166static int dot(sd_bus *bus, char* patterns[]) {
4afd3348
LP
1167 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1168 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
83efb7c2 1169 _cleanup_strv_free_ char **expanded_patterns = NULL;
4387795e
EV
1170 _cleanup_strv_free_ char **expanded_from_patterns = NULL;
1171 _cleanup_strv_free_ char **expanded_to_patterns = NULL;
1700761b 1172 int r;
f459b602 1173 UnitInfo u;
048ecf5b 1174
83efb7c2
EV
1175 r = expand_patterns(bus, patterns, &expanded_patterns);
1176 if (r < 0)
1177 return r;
1178
4387795e
EV
1179 r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
1180 if (r < 0)
1181 return r;
1182
1183 r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
1184 if (r < 0)
1185 return r;
1186
a936124f
TA
1187 r = sd_bus_call_method(
1188 bus,
1189 "org.freedesktop.systemd1",
1190 "/org/freedesktop/systemd1",
1191 "org.freedesktop.systemd1.Manager",
1192 "ListUnits",
1193 &error,
1194 &reply,
1195 "");
048ecf5b 1196 if (r < 0) {
988b9df2
LP
1197 log_error("Failed to list units: %s", bus_error_message(&error, -r));
1198 return r;
048ecf5b 1199 }
1700761b 1200
048ecf5b 1201 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
1700761b 1202 if (r < 0)
988b9df2 1203 return bus_log_parse_error(r);
1700761b
SP
1204
1205 printf("digraph systemd {\n");
1206
048ecf5b 1207 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
f459b602 1208
4387795e 1209 r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
1700761b
SP
1210 if (r < 0)
1211 return r;
1212 }
f459b602
MAP
1213 if (r < 0)
1214 return bus_log_parse_error(r);
1700761b
SP
1215
1216 printf("}\n");
1217
1218 log_info(" Color legend: black = Requires\n"
1219 " dark blue = Requisite\n"
1220 " dark grey = Wants\n"
1221 " red = Conflicts\n"
1222 " green = After\n");
1223
1224 if (on_tty())
1225 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
1226 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
1227
1228 return 0;
1229}
c8a8806e 1230
048ecf5b 1231static int dump(sd_bus *bus, char **args) {
4afd3348
LP
1232 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1233 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
048ecf5b 1234 const char *text = NULL;
9ea9d4cf 1235 int r;
9ea9d4cf 1236
a65615ca
LP
1237 if (!strv_isempty(args)) {
1238 log_error("Too many arguments.");
1239 return -E2BIG;
1240 }
1241
ea4b98e6 1242 pager_open(arg_no_pager, false);
9ea9d4cf 1243
a936124f
TA
1244 r = sd_bus_call_method(
1245 bus,
1246 "org.freedesktop.systemd1",
1247 "/org/freedesktop/systemd1",
1248 "org.freedesktop.systemd1.Manager",
1249 "Dump",
1250 &error,
1251 &reply,
1252 "");
2ca2a91c
LP
1253 if (r < 0)
1254 return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
9ea9d4cf 1255
048ecf5b 1256 r = sd_bus_message_read(reply, "s", &text);
5b30bef8
LP
1257 if (r < 0)
1258 return bus_log_parse_error(r);
9ea9d4cf
LP
1259
1260 fputs(text, stdout);
1261 return 0;
1262}
1263
048ecf5b 1264static int set_log_level(sd_bus *bus, char **args) {
4afd3348 1265 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
048ecf5b 1266 int r;
a65615ca
LP
1267
1268 assert(bus);
1269 assert(args);
1270
1271 if (strv_length(args) != 1) {
1272 log_error("This command expects one argument only.");
1273 return -E2BIG;
1274 }
1275
a936124f
TA
1276 r = sd_bus_set_property(
1277 bus,
1278 "org.freedesktop.systemd1",
1279 "/org/freedesktop/systemd1",
1280 "org.freedesktop.systemd1.Manager",
1281 "LogLevel",
1282 &error,
1283 "s",
d3eba116 1284 args[0]);
2ca2a91c
LP
1285 if (r < 0)
1286 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1287
1288 return 0;
1289}
1290
ef5a8cb1
LW
1291static int get_log_level(sd_bus *bus, char **args) {
1292 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1293 int r;
1294 _cleanup_free_ char *level = NULL;
1295
1296 assert(bus);
1297 assert(args);
1298
1299 if (!strv_isempty(args)) {
1300 log_error("Too many arguments.");
1301 return -E2BIG;
1302 }
1303
1304 r = sd_bus_get_property_string(
1305 bus,
1306 "org.freedesktop.systemd1",
1307 "/org/freedesktop/systemd1",
1308 "org.freedesktop.systemd1.Manager",
1309 "LogLevel",
1310 &error,
1311 &level);
1312 if (r < 0)
1313 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
1314
1315 puts(level);
1316 return 0;
1317}
1318
2ca2a91c 1319static int set_log_target(sd_bus *bus, char **args) {
4afd3348 1320 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2ca2a91c
LP
1321 int r;
1322
1323 assert(bus);
1324 assert(args);
1325
1326 if (strv_length(args) != 1) {
1327 log_error("This command expects one argument only.");
1328 return -E2BIG;
a65615ca
LP
1329 }
1330
2ca2a91c
LP
1331 r = sd_bus_set_property(
1332 bus,
1333 "org.freedesktop.systemd1",
1334 "/org/freedesktop/systemd1",
1335 "org.freedesktop.systemd1.Manager",
1336 "LogTarget",
1337 &error,
1338 "s",
1339 args[0]);
1340 if (r < 0)
1341 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
1342
a65615ca
LP
1343 return 0;
1344}
1345
ef5a8cb1
LW
1346static int get_log_target(sd_bus *bus, char **args) {
1347 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1348 int r;
1349 _cleanup_free_ char *target = NULL;
1350
1351 assert(bus);
1352 assert(args);
1353
1354 if (!strv_isempty(args)) {
1355 log_error("Too many arguments.");
1356 return -E2BIG;
1357 }
1358
1359 r = sd_bus_get_property_string(
1360 bus,
1361 "org.freedesktop.systemd1",
1362 "/org/freedesktop/systemd1",
1363 "org.freedesktop.systemd1.Manager",
1364 "LogTarget",
1365 &error,
1366 &target);
1367 if (r < 0)
1368 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
1369
1370 puts(target);
1371 return 0;
1372}
1373
349cc4a5 1374#if HAVE_SECCOMP
869feb33
ZJS
1375static void dump_syscall_filter(const SyscallFilterSet *set) {
1376 const char *syscall;
1377
1378 printf("%s\n", set->name);
d5efc18b 1379 printf(" # %s\n", set->help);
869feb33
ZJS
1380 NULSTR_FOREACH(syscall, set->value)
1381 printf(" %s\n", syscall);
1382}
1383
1384static int dump_syscall_filters(char** names) {
1385 bool first = true;
1386
1387 pager_open(arg_no_pager, false);
1388
1389 if (strv_isempty(names)) {
1390 int i;
1391
1392 for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
1393 if (!first)
1394 puts("");
1395 dump_syscall_filter(syscall_filter_sets + i);
1396 first = false;
1397 }
1398 } else {
1399 char **name;
1400
1401 STRV_FOREACH(name, names) {
1402 const SyscallFilterSet *set;
1403
1404 if (!first)
1405 puts("");
1406
1407 set = syscall_filter_set_find(*name);
1408 if (!set) {
1409 /* make sure the error appears below normal output */
1410 fflush(stdout);
1411
1412 log_error("Filter set \"%s\" not found.", *name);
1413 return -ENOENT;
1414 }
1415
1416 dump_syscall_filter(set);
1417 first = false;
1418 }
1419 }
1420
1421 return 0;
1422}
1423
1720590b
ZJS
1424#else
1425static int dump_syscall_filters(char** names) {
1426 log_error("Not compiled with syscall filters, sorry.");
1427 return -EOPNOTSUPP;
1428}
1429#endif
1430
6d86f4bd
LP
1431static int test_calendar(char **args) {
1432 int ret = 0, r;
1433 char **p;
1434 usec_t n;
1435
1436 if (strv_isempty(args)) {
1437 log_error("Expected at least one calendar specification string as argument.");
1438 return -EINVAL;
1439 }
1440
1441 n = now(CLOCK_REALTIME);
1442
1443 STRV_FOREACH(p, args) {
1444 _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
1445 _cleanup_free_ char *t = NULL;
1446 usec_t next;
1447
1448 r = calendar_spec_from_string(*p, &spec);
1449 if (r < 0) {
1450 ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p);
1451 continue;
1452 }
1453
1454 r = calendar_spec_normalize(spec);
1455 if (r < 0) {
1456 ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p);
1457 continue;
1458 }
1459
1460 r = calendar_spec_to_string(spec, &t);
1461 if (r < 0) {
1462 ret = log_error_errno(r, "Failed to fomat calendar specification '%s': %m", *p);
1463 continue;
1464 }
1465
1466 if (!streq(t, *p))
1467 printf(" Original form: %s\n", *p);
1468
1469 printf("Normalized form: %s\n", t);
1470
1471 r = calendar_spec_next_usec(spec, n, &next);
1472 if (r == -ENOENT)
1473 printf(" Next elapse: never\n");
1474 else if (r < 0) {
1475 ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p);
1476 continue;
1477 } else {
1478 char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
1479
1480 printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next));
1481
1482 if (!in_utc_timezone())
1483 printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
1484
1485 printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
1486 }
1487
1488 if (*(p+1))
1489 putchar('\n');
1490 }
1491
1492 return ret;
1493}
1494
1d3bc017 1495static void help(void) {
9ea9d4cf 1496
ea4b98e6 1497 pager_open(arg_no_pager, false);
9ea9d4cf 1498
2265fbf7 1499 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1d3bc017 1500 "Profile systemd, show unit dependencies, check unit files.\n\n"
869feb33
ZJS
1501 " -h --help Show this help\n"
1502 " --version Show package version\n"
1503 " --no-pager Do not pipe output into a pager\n"
1504 " --system Operate on system systemd instance\n"
1505 " --user Operate on user systemd instance\n"
1506 " -H --host=[USER@]HOST Operate on remote host\n"
1507 " -M --machine=CONTAINER Operate on local container\n"
1508 " --order Show only order in the graph\n"
1509 " --require Show only requirement in the graph\n"
1510 " --from-pattern=GLOB Show only origins in the graph\n"
1511 " --to-pattern=GLOB Show only destinations in the graph\n"
1512 " --fuzz=SECONDS Also print also services which finished SECONDS\n"
1513 " earlier than the latest in the branch\n"
1514 " --man[=BOOL] Do [not] check for existence of man pages\n\n"
641c0fd1 1515 " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
2265fbf7 1516 "Commands:\n"
869feb33
ZJS
1517 " time Print time spent in the kernel\n"
1518 " blame Print list of running units ordered by time to init\n"
1519 " critical-chain Print a tree of the time critical chain of units\n"
1520 " plot Output SVG graphic showing service initialization\n"
cc7de2ba 1521 " dot Output dependency graph in man:dot(1) format\n"
869feb33
ZJS
1522 " set-log-level LEVEL Set logging threshold for manager\n"
1523 " set-log-target TARGET Set logging target for manager\n"
ef5a8cb1
LW
1524 " get-log-level Get logging threshold for manager\n"
1525 " get-log-target Get logging target for manager\n"
869feb33
ZJS
1526 " dump Output state serialization of service manager\n"
1527 " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
1528 " verify FILE... Check unit files for correctness\n"
6d86f4bd 1529 " calendar SPEC... Validate repetitive calendar time events\n"
1d3bc017 1530 , program_invocation_short_name);
96de7c04
ZJS
1531
1532 /* When updating this list, including descriptions, apply
1d3bc017
ZJS
1533 * changes to shell-completion/bash/systemd-analyze and
1534 * shell-completion/zsh/_systemd-analyze too. */
2265fbf7
SP
1535}
1536
9ea9d4cf 1537static int parse_argv(int argc, char *argv[]) {
2265fbf7
SP
1538 enum {
1539 ARG_VERSION = 0x100,
1700761b
SP
1540 ARG_ORDER,
1541 ARG_REQUIRE,
2265fbf7 1542 ARG_USER,
e55933db
ŁS
1543 ARG_SYSTEM,
1544 ARG_DOT_FROM_PATTERN,
bb150966 1545 ARG_DOT_TO_PATTERN,
9ea9d4cf 1546 ARG_FUZZ,
1d3bc017 1547 ARG_NO_PAGER,
dad29dff 1548 ARG_MAN,
641c0fd1 1549 ARG_GENERATORS,
2265fbf7
SP
1550 };
1551
1552 static const struct option options[] = {
9ea9d4cf
LP
1553 { "help", no_argument, NULL, 'h' },
1554 { "version", no_argument, NULL, ARG_VERSION },
1555 { "order", no_argument, NULL, ARG_ORDER },
1556 { "require", no_argument, NULL, ARG_REQUIRE },
1557 { "user", no_argument, NULL, ARG_USER },
1558 { "system", no_argument, NULL, ARG_SYSTEM },
1559 { "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
1560 { "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
1561 { "fuzz", required_argument, NULL, ARG_FUZZ },
1562 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
dad29dff 1563 { "man", optional_argument, NULL, ARG_MAN },
641c0fd1 1564 { "generators", optional_argument, NULL, ARG_GENERATORS },
8fe12d88
LP
1565 { "host", required_argument, NULL, 'H' },
1566 { "machine", required_argument, NULL, 'M' },
eb9da376 1567 {}
2265fbf7
SP
1568 };
1569
eb9da376
LP
1570 int r, c;
1571
2265fbf7
SP
1572 assert(argc >= 0);
1573 assert(argv);
1574
601185b4 1575 while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
eb9da376 1576 switch (c) {
c170f3a4
LP
1577
1578 case 'h':
1d3bc017
ZJS
1579 help();
1580 return 0;
c170f3a4
LP
1581
1582 case ARG_VERSION:
3f6fd1ba 1583 return version();
c170f3a4
LP
1584
1585 case ARG_USER:
3cd26e7c 1586 arg_user = true;
c170f3a4
LP
1587 break;
1588
1589 case ARG_SYSTEM:
3cd26e7c 1590 arg_user = false;
c170f3a4
LP
1591 break;
1592
1593 case ARG_ORDER:
1594 arg_dot = DEP_ORDER;
1595 break;
1596
1597 case ARG_REQUIRE:
1598 arg_dot = DEP_REQUIRE;
1599 break;
1600
e55933db 1601 case ARG_DOT_FROM_PATTERN:
903a0b07
LP
1602 if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
1603 return log_oom();
1604
e55933db
ŁS
1605 break;
1606
1607 case ARG_DOT_TO_PATTERN:
903a0b07
LP
1608 if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
1609 return log_oom();
1610
e55933db
ŁS
1611 break;
1612
bb150966
HH
1613 case ARG_FUZZ:
1614 r = parse_sec(optarg, &arg_fuzz);
1615 if (r < 0)
1616 return r;
1617 break;
1618
9ea9d4cf
LP
1619 case ARG_NO_PAGER:
1620 arg_no_pager = true;
1621 break;
1622
3cd26e7c
LP
1623 case 'H':
1624 arg_transport = BUS_TRANSPORT_REMOTE;
1625 arg_host = optarg;
1626 break;
1627
1628 case 'M':
de33fc62 1629 arg_transport = BUS_TRANSPORT_MACHINE;
3cd26e7c
LP
1630 arg_host = optarg;
1631 break;
1632
dad29dff
LP
1633 case ARG_MAN:
1634 if (optarg) {
1635 r = parse_boolean(optarg);
1636 if (r < 0) {
1637 log_error("Failed to parse --man= argument.");
1638 return -EINVAL;
1639 }
1640
1641 arg_man = !!r;
1642 } else
1643 arg_man = true;
1644
1d3bc017
ZJS
1645 break;
1646
641c0fd1
ZJS
1647 case ARG_GENERATORS:
1648 if (optarg) {
1649 r = parse_boolean(optarg);
1650 if (r < 0) {
1651 log_error("Failed to parse --generators= argument.");
1652 return -EINVAL;
1653 }
1654
1655 arg_generators = !!r;
1656 } else
1657 arg_generators = true;
1658
1659 break;
1660
c170f3a4
LP
1661 case '?':
1662 return -EINVAL;
1663
1664 default:
1d3bc017 1665 assert_not_reached("Unhandled option code.");
2265fbf7 1666 }
eb9da376 1667
1d3bc017 1668 return 1; /* work to do */
2265fbf7
SP
1669}
1670
1671int main(int argc, char *argv[]) {
5220a6f3 1672 int r;
2265fbf7
SP
1673
1674 setlocale(LC_ALL, "");
c170f3a4 1675 setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
2265fbf7
SP
1676 log_parse_environment();
1677 log_open();
1678
1679 r = parse_argv(argc, argv);
9ea9d4cf
LP
1680 if (r <= 0)
1681 goto finish;
2265fbf7 1682
5b6f6ebd 1683 if (streq_ptr(argv[optind], "verify"))
1d3bc017 1684 r = verify_units(argv+optind+1,
463d0d15 1685 arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
641c0fd1
ZJS
1686 arg_man,
1687 arg_generators);
1d3bc017 1688 else {
4afd3348 1689 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1d3bc017 1690
266f3e26 1691 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
1d3bc017 1692 if (r < 0) {
da927ba9 1693 log_error_errno(r, "Failed to create bus connection: %m");
1d3bc017
ZJS
1694 goto finish;
1695 }
2265fbf7 1696
1d3bc017
ZJS
1697 if (!argv[optind] || streq(argv[optind], "time"))
1698 r = analyze_time(bus);
1699 else if (streq(argv[optind], "blame"))
1700 r = analyze_blame(bus);
1701 else if (streq(argv[optind], "critical-chain"))
1702 r = analyze_critical_chain(bus, argv+optind+1);
1703 else if (streq(argv[optind], "plot"))
1704 r = analyze_plot(bus);
1705 else if (streq(argv[optind], "dot"))
1706 r = dot(bus, argv+optind+1);
1707 else if (streq(argv[optind], "dump"))
1708 r = dump(bus, argv+optind+1);
1709 else if (streq(argv[optind], "set-log-level"))
1710 r = set_log_level(bus, argv+optind+1);
ef5a8cb1
LW
1711 else if (streq(argv[optind], "get-log-level"))
1712 r = get_log_level(bus, argv+optind+1);
2ca2a91c
LP
1713 else if (streq(argv[optind], "set-log-target"))
1714 r = set_log_target(bus, argv+optind+1);
ef5a8cb1
LW
1715 else if (streq(argv[optind], "get-log-target"))
1716 r = get_log_target(bus, argv+optind+1);
869feb33
ZJS
1717 else if (streq(argv[optind], "syscall-filter"))
1718 r = dump_syscall_filters(argv+optind+1);
6d86f4bd
LP
1719 else if (streq(argv[optind], "calendar"))
1720 r = test_calendar(argv+optind+1);
1d3bc017
ZJS
1721 else
1722 log_error("Unknown operation '%s'.", argv[optind]);
1723 }
2265fbf7 1724
9ea9d4cf
LP
1725finish:
1726 pager_close();
1727
e55933db
ŁS
1728 strv_free(arg_dot_from_patterns);
1729 strv_free(arg_dot_to_patterns);
c170f3a4
LP
1730
1731 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
2265fbf7 1732}