2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-02-06 15:21:03 +01:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
2013-03-08 19:03:50 +01:00
|
|
|
Copyright 2010-2013 Lennart Poettering
|
|
|
|
Copyright 2013 Simon Peeters
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
systemd is distributed in the hope that it will be useful, but
|
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <locale.h>
|
2015-09-23 03:01:06 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
#include "sd-bus.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "analyze-verify.h"
|
2013-10-25 22:56:19 +02:00
|
|
|
#include "bus-error.h"
|
2016-04-20 17:43:13 +02:00
|
|
|
#include "bus-unit-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "bus-util.h"
|
2017-11-17 10:33:22 +01:00
|
|
|
#include "calendarspec.h"
|
2015-10-27 01:48:17 +01:00
|
|
|
#include "glob-util.h"
|
2013-04-23 11:11:10 +02:00
|
|
|
#include "hashmap.h"
|
2015-10-26 23:01:30 +01:00
|
|
|
#include "locale-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "log.h"
|
2013-07-26 16:34:52 +02:00
|
|
|
#include "pager.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2016-11-02 16:58:18 +01:00
|
|
|
#include "seccomp-util.h"
|
2016-11-08 14:18:33 +01:00
|
|
|
#endif
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "special.h"
|
|
|
|
#include "strv.h"
|
|
|
|
#include "strxcpyx.h"
|
2015-04-10 23:15:59 +02:00
|
|
|
#include "terminal-util.h"
|
2015-09-23 03:01:06 +02:00
|
|
|
#include "unit-name.h"
|
|
|
|
#include "util.h"
|
2017-12-26 15:58:06 +01:00
|
|
|
#include "verbs.h"
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-03-08 19:10:24 +01:00
|
|
|
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
|
2014-04-23 22:37:43 +02:00
|
|
|
#define SCALE_Y (20.0)
|
2013-03-08 19:10:24 +01:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
|
2013-03-08 18:58:08 +01:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
#define svg(...) printf(__VA_ARGS__)
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
#define svg_bar(class, x1, x2, y) \
|
2013-02-06 15:21:03 +01:00
|
|
|
svg(" <rect class=\"%s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", \
|
2013-03-08 18:58:08 +01:00
|
|
|
(class), \
|
2013-03-08 19:10:24 +01:00
|
|
|
SCALE_X * (x1), SCALE_Y * (y), \
|
|
|
|
SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0)
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
#define svg_text(b, x, y, format, ...) \
|
|
|
|
do { \
|
2013-03-08 19:10:24 +01:00
|
|
|
svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \
|
2013-03-08 18:58:08 +01:00
|
|
|
svg(format, ## __VA_ARGS__); \
|
|
|
|
svg("</text>\n"); \
|
2016-02-23 18:52:52 +01:00
|
|
|
} while (false)
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-02-13 23:17:28 +01:00
|
|
|
static enum dot {
|
|
|
|
DEP_ALL,
|
|
|
|
DEP_ORDER,
|
|
|
|
DEP_REQUIRE
|
|
|
|
} arg_dot = DEP_ALL;
|
2013-04-06 15:40:56 +02:00
|
|
|
static char** arg_dot_from_patterns = NULL;
|
|
|
|
static char** arg_dot_to_patterns = NULL;
|
2013-07-26 16:34:52 +02:00
|
|
|
static usec_t arg_fuzz = 0;
|
|
|
|
static bool arg_no_pager = false;
|
2013-11-06 17:30:39 +01:00
|
|
|
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
|
2017-12-26 16:00:54 +01:00
|
|
|
static const char *arg_host = NULL;
|
2013-11-06 17:30:39 +01:00
|
|
|
static bool arg_user = false;
|
2014-07-22 03:11:54 +02:00
|
|
|
static bool arg_man = true;
|
2017-09-16 11:29:34 +02:00
|
|
|
static bool arg_generators = false;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
struct boot_times {
|
2013-03-08 18:58:08 +01:00
|
|
|
usec_t firmware_time;
|
|
|
|
usec_t loader_time;
|
|
|
|
usec_t kernel_time;
|
|
|
|
usec_t kernel_done_time;
|
|
|
|
usec_t initrd_time;
|
|
|
|
usec_t userspace_time;
|
|
|
|
usec_t finish_time;
|
2013-11-10 23:21:15 +01:00
|
|
|
usec_t security_start_time;
|
|
|
|
usec_t security_finish_time;
|
2013-05-08 14:29:12 +02:00
|
|
|
usec_t generators_start_time;
|
|
|
|
usec_t generators_finish_time;
|
2013-05-15 21:18:26 +02:00
|
|
|
usec_t unitsload_start_time;
|
|
|
|
usec_t unitsload_finish_time;
|
2015-07-27 18:40:36 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we're analyzing the user instance, all timestamps will be offset
|
|
|
|
* by its own start-up timestamp, which may be arbitrarily big.
|
|
|
|
* With "plot", this causes arbitrarily wide output SVG files which almost
|
|
|
|
* completely consist of empty space. Thus we cancel out this offset.
|
|
|
|
*
|
|
|
|
* This offset is subtracted from times above by acquire_boot_times(),
|
|
|
|
* but it still needs to be subtracted from unit-specific timestamps
|
|
|
|
* (so it is stored here for reference).
|
|
|
|
*/
|
|
|
|
usec_t reverse_offset;
|
2013-02-06 15:21:03 +01:00
|
|
|
};
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
struct unit_times {
|
|
|
|
char *name;
|
2013-11-01 23:11:37 +01:00
|
|
|
usec_t activating;
|
|
|
|
usec_t activated;
|
|
|
|
usec_t deactivated;
|
|
|
|
usec_t deactivating;
|
2013-03-08 18:58:08 +01:00
|
|
|
usec_t time;
|
2013-02-06 15:21:03 +01:00
|
|
|
};
|
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
struct host_info {
|
|
|
|
char *hostname;
|
|
|
|
char *kernel_name;
|
|
|
|
char *kernel_release;
|
|
|
|
char *kernel_version;
|
|
|
|
char *os_pretty_name;
|
|
|
|
char *virtualization;
|
|
|
|
char *architecture;
|
|
|
|
};
|
|
|
|
|
2017-12-18 19:35:03 +01:00
|
|
|
static int acquire_bus(bool need_full_bus, sd_bus **bus) {
|
|
|
|
if (need_full_bus)
|
|
|
|
return bus_connect_transport(arg_transport, arg_host, arg_user, bus);
|
|
|
|
else
|
|
|
|
return bus_connect_transport_systemd(arg_transport, arg_host, arg_user, bus);
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-03-08 18:58:08 +01:00
|
|
|
int r;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-11-07 22:06:29 +01:00
|
|
|
assert(bus);
|
|
|
|
assert(path);
|
|
|
|
assert(interface);
|
|
|
|
assert(property);
|
|
|
|
assert(val);
|
|
|
|
|
2013-10-31 23:08:16 +01:00
|
|
|
r = sd_bus_get_property_trivial(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
path,
|
|
|
|
interface,
|
|
|
|
property,
|
|
|
|
&error,
|
|
|
|
't', val);
|
2013-10-25 22:56:19 +02:00
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("Failed to parse reply: %s", bus_error_message(&error, -r));
|
2013-02-06 15:21:03 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-07 22:06:29 +01:00
|
|
|
static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-11-07 22:06:29 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(path);
|
|
|
|
assert(property);
|
|
|
|
assert(strv);
|
|
|
|
|
|
|
|
r = sd_bus_get_property_strv(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
property,
|
|
|
|
&error,
|
|
|
|
strv);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("Failed to get unit property %s: %s", property, bus_error_message(&error, -r));
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
static int compare_unit_time(const void *a, const void *b) {
|
2013-02-06 15:21:03 +01:00
|
|
|
return compare(((struct unit_times *)b)->time,
|
|
|
|
((struct unit_times *)a)->time);
|
|
|
|
}
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
static int compare_unit_start(const void *a, const void *b) {
|
2013-11-01 23:11:37 +01:00
|
|
|
return compare(((struct unit_times *)a)->activating,
|
|
|
|
((struct unit_times *)b)->activating);
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
static void free_unit_times(struct unit_times *t, unsigned n) {
|
|
|
|
struct unit_times *p;
|
|
|
|
|
|
|
|
for (p = t; p < t + n; p++)
|
|
|
|
free(p->name);
|
|
|
|
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
|
2015-07-27 18:40:36 +02:00
|
|
|
static void subtract_timestamp(usec_t *a, usec_t b) {
|
|
|
|
assert(a);
|
|
|
|
|
|
|
|
if (*a > 0) {
|
|
|
|
assert(*a >= b);
|
|
|
|
*a -= b;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
|
2013-02-06 15:21:03 +01:00
|
|
|
static struct boot_times times;
|
|
|
|
static bool cached = false;
|
2013-03-08 18:58:08 +01:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
if (cached)
|
2013-03-08 18:58:08 +01:00
|
|
|
goto finish;
|
|
|
|
|
|
|
|
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
if (bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"FirmwareTimestampMonotonic",
|
|
|
|
×.firmware_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LoaderTimestampMonotonic",
|
|
|
|
×.loader_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"KernelTimestamp",
|
|
|
|
×.kernel_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"InitRDTimestampMonotonic",
|
|
|
|
×.initrd_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"UserspaceTimestampMonotonic",
|
|
|
|
×.userspace_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"FinishTimestampMonotonic",
|
2013-05-08 14:29:12 +02:00
|
|
|
×.finish_time) < 0 ||
|
2013-11-10 23:21:15 +01:00
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"SecurityStartTimestampMonotonic",
|
|
|
|
×.security_start_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"SecurityFinishTimestampMonotonic",
|
|
|
|
×.security_finish_time) < 0 ||
|
2013-05-08 14:29:12 +02:00
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"GeneratorsStartTimestampMonotonic",
|
|
|
|
×.generators_start_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"GeneratorsFinishTimestampMonotonic",
|
2013-05-15 21:18:26 +02:00
|
|
|
×.generators_finish_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"UnitsLoadStartTimestampMonotonic",
|
|
|
|
×.unitsload_start_time) < 0 ||
|
|
|
|
bus_get_uint64_property(bus,
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"UnitsLoadFinishTimestampMonotonic",
|
|
|
|
×.unitsload_finish_time) < 0)
|
2013-03-08 18:58:08 +01:00
|
|
|
return -EIO;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
if (times.finish_time <= 0) {
|
2013-02-06 15:21:03 +01:00
|
|
|
log_error("Bootup is not yet finished. Please try again later.");
|
2013-11-07 22:06:29 +01:00
|
|
|
return -EINPROGRESS;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2015-07-27 18:40:36 +02:00
|
|
|
if (arg_user) {
|
|
|
|
/*
|
|
|
|
* User-instance-specific timestamps processing
|
|
|
|
* (see comment to reverse_offset in struct boot_times).
|
|
|
|
*/
|
|
|
|
times.reverse_offset = times.userspace_time;
|
|
|
|
|
|
|
|
times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
|
|
|
|
subtract_timestamp(×.finish_time, times.reverse_offset);
|
|
|
|
|
|
|
|
subtract_timestamp(×.security_start_time, times.reverse_offset);
|
|
|
|
subtract_timestamp(×.security_finish_time, times.reverse_offset);
|
|
|
|
|
|
|
|
subtract_timestamp(×.generators_start_time, times.reverse_offset);
|
|
|
|
subtract_timestamp(×.generators_finish_time, times.reverse_offset);
|
|
|
|
|
|
|
|
subtract_timestamp(×.unitsload_start_time, times.reverse_offset);
|
|
|
|
subtract_timestamp(×.unitsload_finish_time, times.reverse_offset);
|
|
|
|
} else {
|
|
|
|
if (times.initrd_time)
|
|
|
|
times.kernel_done_time = times.initrd_time;
|
|
|
|
else
|
|
|
|
times.kernel_done_time = times.userspace_time;
|
|
|
|
}
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
cached = true;
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
finish:
|
|
|
|
*bt = ×
|
|
|
|
return 0;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
static void free_host_info(struct host_info *hi) {
|
2015-10-07 15:45:25 +02:00
|
|
|
|
|
|
|
if (!hi)
|
2015-10-06 15:57:14 +02:00
|
|
|
return;
|
2015-10-07 15:45:25 +02:00
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
free(hi->hostname);
|
|
|
|
free(hi->kernel_name);
|
|
|
|
free(hi->kernel_release);
|
|
|
|
free(hi->kernel_version);
|
|
|
|
free(hi->os_pretty_name);
|
|
|
|
free(hi->virtualization);
|
|
|
|
free(hi->architecture);
|
|
|
|
free(hi);
|
|
|
|
}
|
2015-10-07 15:45:25 +02:00
|
|
|
|
2015-10-06 15:57:14 +02:00
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(struct host_info*, free_host_info);
|
2014-04-24 00:03:35 +02:00
|
|
|
|
2015-07-28 00:01:10 +02:00
|
|
|
static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2015-07-28 00:01:10 +02:00
|
|
|
int r, c = 0;
|
2015-07-27 18:40:36 +02:00
|
|
|
struct boot_times *boot_times = NULL;
|
2015-07-28 00:01:10 +02:00
|
|
|
struct unit_times *unit_times = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
UnitInfo u;
|
|
|
|
|
2015-07-27 18:40:36 +02:00
|
|
|
r = acquire_boot_times(bus, &boot_times);
|
|
|
|
if (r < 0)
|
|
|
|
goto fail;
|
|
|
|
|
2015-07-28 00:01:10 +02:00
|
|
|
r = sd_bus_call_method(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"ListUnits",
|
|
|
|
&error, &reply,
|
|
|
|
NULL);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("Failed to list units: %s", bus_error_message(&error, -r));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
|
|
|
|
if (r < 0) {
|
|
|
|
bus_log_parse_error(r);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
|
|
|
|
struct unit_times *t;
|
|
|
|
|
|
|
|
if (!GREEDY_REALLOC(unit_times, size, c+1)) {
|
|
|
|
r = log_oom();
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
t = unit_times+c;
|
|
|
|
t->name = NULL;
|
|
|
|
|
|
|
|
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
|
|
|
|
|
|
|
|
if (bus_get_uint64_property(bus, u.unit_path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"InactiveExitTimestampMonotonic",
|
|
|
|
&t->activating) < 0 ||
|
|
|
|
bus_get_uint64_property(bus, u.unit_path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"ActiveEnterTimestampMonotonic",
|
|
|
|
&t->activated) < 0 ||
|
|
|
|
bus_get_uint64_property(bus, u.unit_path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"ActiveExitTimestampMonotonic",
|
|
|
|
&t->deactivating) < 0 ||
|
|
|
|
bus_get_uint64_property(bus, u.unit_path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"InactiveEnterTimestampMonotonic",
|
|
|
|
&t->deactivated) < 0) {
|
|
|
|
r = -EIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-07-27 18:40:36 +02:00
|
|
|
subtract_timestamp(&t->activating, boot_times->reverse_offset);
|
|
|
|
subtract_timestamp(&t->activated, boot_times->reverse_offset);
|
|
|
|
subtract_timestamp(&t->deactivating, boot_times->reverse_offset);
|
|
|
|
subtract_timestamp(&t->deactivated, boot_times->reverse_offset);
|
|
|
|
|
2015-07-28 00:01:10 +02:00
|
|
|
if (t->activated >= t->activating)
|
|
|
|
t->time = t->activated - t->activating;
|
|
|
|
else if (t->deactivated >= t->activating)
|
|
|
|
t->time = t->deactivated - t->activating;
|
|
|
|
else
|
|
|
|
t->time = 0;
|
|
|
|
|
|
|
|
if (t->activating == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
t->name = strdup(u.id);
|
2017-12-08 20:52:38 +01:00
|
|
|
if (!t->name) {
|
2015-07-28 00:01:10 +02:00
|
|
|
r = log_oom();
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
if (r < 0) {
|
|
|
|
bus_log_parse_error(r);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out = unit_times;
|
|
|
|
return c;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (unit_times)
|
|
|
|
free_unit_times(unit_times, (unsigned) c);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
|
|
|
|
static const struct bus_properties_map hostname_map[] = {
|
2015-10-07 15:45:25 +02:00
|
|
|
{ "Hostname", "s", NULL, offsetof(struct host_info, hostname) },
|
|
|
|
{ "KernelName", "s", NULL, offsetof(struct host_info, kernel_name) },
|
|
|
|
{ "KernelRelease", "s", NULL, offsetof(struct host_info, kernel_release) },
|
|
|
|
{ "KernelVersion", "s", NULL, offsetof(struct host_info, kernel_version) },
|
2014-04-24 00:03:35 +02:00
|
|
|
{ "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct bus_properties_map manager_map[] = {
|
2015-10-07 15:45:25 +02:00
|
|
|
{ "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
|
|
|
|
{ "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
|
2014-04-24 00:03:35 +02:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2015-10-07 15:45:25 +02:00
|
|
|
_cleanup_(free_host_infop) struct host_info *host;
|
|
|
|
int r;
|
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
host = new0(struct host_info, 1);
|
|
|
|
if (!host)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = bus_map_all_properties(bus,
|
|
|
|
"org.freedesktop.hostname1",
|
|
|
|
"/org/freedesktop/hostname1",
|
|
|
|
hostname_map,
|
2017-02-08 17:59:58 +01:00
|
|
|
&error,
|
2014-04-24 00:03:35 +02:00
|
|
|
host);
|
|
|
|
if (r < 0)
|
2015-10-07 15:45:25 +02:00
|
|
|
log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r));
|
2014-04-24 00:03:35 +02:00
|
|
|
|
|
|
|
r = bus_map_all_properties(bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
manager_map,
|
2017-02-08 17:59:58 +01:00
|
|
|
&error,
|
2014-04-24 00:03:35 +02:00
|
|
|
host);
|
2015-10-07 08:02:31 +02:00
|
|
|
if (r < 0)
|
2015-10-07 15:45:25 +02:00
|
|
|
return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r));
|
2014-04-24 00:03:35 +02:00
|
|
|
|
|
|
|
*hi = host;
|
2015-10-07 08:02:31 +02:00
|
|
|
host = NULL;
|
2015-10-07 15:45:25 +02:00
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
static int pretty_boot_time(sd_bus *bus, char **_buf) {
|
2013-03-08 18:58:08 +01:00
|
|
|
char ts[FORMAT_TIMESPAN_MAX];
|
2013-02-06 15:21:03 +01:00
|
|
|
struct boot_times *t;
|
|
|
|
static char buf[4096];
|
2013-03-08 18:58:08 +01:00
|
|
|
size_t size;
|
|
|
|
char *ptr;
|
|
|
|
int r;
|
2017-12-05 10:20:40 +01:00
|
|
|
usec_t activated_time = USEC_INFINITY;
|
|
|
|
_cleanup_free_ char* path = NULL, *unit_id = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
r = acquire_boot_times(bus, &t);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2017-12-05 10:20:40 +01:00
|
|
|
path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET);
|
|
|
|
if (!path)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = sd_bus_get_property_string(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"Id",
|
|
|
|
&error,
|
|
|
|
&unit_id);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r));
|
|
|
|
unit_id = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = bus_get_uint64_property(bus, path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"ActiveEnterTimestampMonotonic",
|
|
|
|
&activated_time);
|
|
|
|
if (r < 0) {
|
|
|
|
log_info_errno(r, "default.target seems not to be started. Continuing...");
|
|
|
|
activated_time = USEC_INFINITY;
|
|
|
|
}
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
ptr = buf;
|
|
|
|
size = sizeof(buf);
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
size = strpcpyf(&ptr, size, "Startup finished in ");
|
|
|
|
if (t->firmware_time)
|
2013-04-04 02:56:56 +02:00
|
|
|
size = strpcpyf(&ptr, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), t->firmware_time - t->loader_time, USEC_PER_MSEC));
|
2013-02-06 15:21:03 +01:00
|
|
|
if (t->loader_time)
|
2013-04-04 02:56:56 +02:00
|
|
|
size = strpcpyf(&ptr, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), t->loader_time, USEC_PER_MSEC));
|
2013-02-06 15:21:03 +01:00
|
|
|
if (t->kernel_time)
|
2013-04-04 02:56:56 +02:00
|
|
|
size = strpcpyf(&ptr, size, "%s (kernel) + ", format_timespan(ts, sizeof(ts), t->kernel_done_time, USEC_PER_MSEC));
|
2013-02-06 15:21:03 +01:00
|
|
|
if (t->initrd_time > 0)
|
2013-04-04 02:56:56 +02:00
|
|
|
size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-04-04 02:56:56 +02:00
|
|
|
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
|
2015-07-27 18:40:36 +02:00
|
|
|
strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2017-12-05 10:20:40 +01:00
|
|
|
if (unit_id && activated_time != USEC_INFINITY)
|
|
|
|
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));
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
ptr = strdup(buf);
|
|
|
|
if (!ptr)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
*_buf = ptr;
|
|
|
|
return 0;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
static void svg_graph_box(double height, double begin, double end) {
|
|
|
|
long long i;
|
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
/* outside box, fill */
|
|
|
|
svg("<rect class=\"box\" x=\"0\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n",
|
2013-03-08 19:10:24 +01:00
|
|
|
SCALE_X * (end - begin), SCALE_Y * height);
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
for (i = ((long long) (begin / 100000)) * 100000; i <= end; i+=100000) {
|
2013-02-06 15:21:03 +01:00
|
|
|
/* lines for each second */
|
2013-03-08 18:58:08 +01:00
|
|
|
if (i % 5000000 == 0)
|
2013-02-06 15:21:03 +01:00
|
|
|
svg(" <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
|
|
|
|
" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
|
2013-03-08 19:10:24 +01:00
|
|
|
SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
|
2013-03-08 18:58:08 +01:00
|
|
|
else if (i % 1000000 == 0)
|
2013-02-06 15:21:03 +01:00
|
|
|
svg(" <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n"
|
|
|
|
" <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n",
|
2013-03-08 19:10:24 +01:00
|
|
|
SCALE_X * i, SCALE_X * i, SCALE_Y * height, SCALE_X * i, -5.0, 0.000001 * i);
|
2013-02-06 15:21:03 +01:00
|
|
|
else
|
|
|
|
svg(" <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n",
|
2013-03-08 19:10:24 +01:00
|
|
|
SCALE_X * i, SCALE_X * i, SCALE_Y * height);
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int analyze_plot(int argc, char *argv[], void *userdata) {
|
2015-10-07 15:45:25 +02:00
|
|
|
_cleanup_(free_host_infop) struct host_info *host = NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-02-06 15:21:03 +01:00
|
|
|
struct unit_times *times;
|
|
|
|
struct boot_times *boot;
|
2017-12-26 15:58:06 +01:00
|
|
|
int n, m = 1, y = 0, r;
|
2013-02-06 15:21:03 +01:00
|
|
|
double width;
|
2014-04-24 00:03:35 +02:00
|
|
|
_cleanup_free_ char *pretty_times = NULL;
|
2013-03-08 18:58:08 +01:00
|
|
|
struct unit_times *u;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(true, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
n = acquire_boot_times(bus, &boot);
|
|
|
|
if (n < 0)
|
|
|
|
return n;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
n = pretty_boot_time(bus, &pretty_times);
|
|
|
|
if (n < 0)
|
|
|
|
return n;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
n = acquire_host_info(bus, &host);
|
|
|
|
if (n < 0)
|
|
|
|
return n;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
n = acquire_time_data(bus, ×);
|
2013-03-08 18:58:08 +01:00
|
|
|
if (n <= 0)
|
2015-10-06 15:57:14 +02:00
|
|
|
return n;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
qsort(times, n, sizeof(struct unit_times), compare_unit_start);
|
|
|
|
|
2013-03-08 19:10:24 +01:00
|
|
|
width = SCALE_X * (boot->firmware_time + boot->finish_time);
|
2013-02-06 15:21:03 +01:00
|
|
|
if (width < 800.0)
|
|
|
|
width = 800.0;
|
|
|
|
|
|
|
|
if (boot->firmware_time > boot->loader_time)
|
|
|
|
m++;
|
|
|
|
if (boot->loader_time) {
|
|
|
|
m++;
|
|
|
|
if (width < 1000.0)
|
|
|
|
width = 1000.0;
|
|
|
|
}
|
|
|
|
if (boot->initrd_time)
|
|
|
|
m++;
|
|
|
|
if (boot->kernel_time)
|
|
|
|
m++;
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
for (u = times; u < times + n; u++) {
|
2013-11-01 22:57:47 +01:00
|
|
|
double text_start, text_width;
|
2013-03-08 18:58:08 +01:00
|
|
|
|
2013-11-01 23:11:37 +01:00
|
|
|
if (u->activating < boot->userspace_time ||
|
|
|
|
u->activating > boot->finish_time) {
|
2015-09-08 18:43:11 +02:00
|
|
|
u->name = mfree(u->name);
|
2013-02-06 15:21:03 +01:00
|
|
|
continue;
|
|
|
|
}
|
2013-11-01 22:57:47 +01:00
|
|
|
|
|
|
|
/* If the text cannot fit on the left side then
|
|
|
|
* increase the svg width so it fits on the right.
|
|
|
|
* TODO: calculate the text width more accurately */
|
|
|
|
text_width = 8.0 * strlen(u->name);
|
2013-11-01 23:11:37 +01:00
|
|
|
text_start = (boot->firmware_time + u->activating) * SCALE_X;
|
2013-11-01 22:57:47 +01:00
|
|
|
if (text_width > text_start && text_width + text_start > width)
|
|
|
|
width = text_width + text_start;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-11-01 23:11:37 +01:00
|
|
|
if (u->deactivated > u->activating && u->deactivated <= boot->finish_time
|
|
|
|
&& u->activated == 0 && u->deactivating == 0)
|
|
|
|
u->activated = u->deactivating = u->deactivated;
|
|
|
|
if (u->activated < u->activating || u->activated > boot->finish_time)
|
|
|
|
u->activated = boot->finish_time;
|
|
|
|
if (u->deactivating < u->activated || u->activated > boot->finish_time)
|
|
|
|
u->deactivating = boot->finish_time;
|
|
|
|
if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time)
|
|
|
|
u->deactivated = boot->finish_time;
|
2013-02-06 15:21:03 +01:00
|
|
|
m++;
|
|
|
|
}
|
|
|
|
|
|
|
|
svg("<?xml version=\"1.0\" standalone=\"no\"?>\n"
|
|
|
|
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
|
|
|
|
"\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
|
|
|
|
|
|
|
|
svg("<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" "
|
|
|
|
"xmlns=\"http://www.w3.org/2000/svg\">\n\n",
|
2013-05-08 14:29:12 +02:00
|
|
|
80.0 + width, 150.0 + (m * SCALE_Y) +
|
2013-05-15 21:18:26 +02:00
|
|
|
5 * SCALE_Y /* legend */);
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
/* write some basic info as a comment, including some help */
|
|
|
|
svg("<!-- This file is a systemd-analyze SVG file. It is best rendered in a -->\n"
|
|
|
|
"<!-- browser such as Chrome, Chromium or Firefox. Other applications -->\n"
|
|
|
|
"<!-- that render these files properly but much slower are ImageMagick, -->\n"
|
|
|
|
"<!-- gimp, inkscape, etc. To display the files on your system, just -->\n"
|
|
|
|
"<!-- point your browser to this file. -->\n\n"
|
2017-04-03 18:15:33 +02:00
|
|
|
"<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION);
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
/* style sheet */
|
|
|
|
svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n"
|
|
|
|
" rect { stroke-width: 1; stroke-opacity: 0; }\n"
|
2013-10-21 21:29:23 +02:00
|
|
|
" rect.background { fill: rgb(255,255,255); }\n"
|
2013-02-06 15:21:03 +01:00
|
|
|
" rect.activating { fill: rgb(255,0,0); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.active { fill: rgb(200,150,150); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.deactivating { fill: rgb(150,100,100); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.kernel { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.initrd { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.firmware { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.loader { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
|
|
|
|
" rect.userspace { fill: rgb(150,150,150); fill-opacity: 0.7; }\n"
|
2013-11-10 23:21:15 +01:00
|
|
|
" rect.security { fill: rgb(144,238,144); fill-opacity: 0.7; }\n"
|
2013-05-08 14:29:12 +02:00
|
|
|
" rect.generators { fill: rgb(102,204,255); fill-opacity: 0.7; }\n"
|
2013-05-15 21:18:26 +02:00
|
|
|
" rect.unitsload { fill: rgb( 82,184,255); fill-opacity: 0.7; }\n"
|
2013-02-06 15:21:03 +01:00
|
|
|
" rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"
|
|
|
|
" line { stroke: rgb(64,64,64); stroke-width: 1; }\n"
|
|
|
|
"// line.sec1 { }\n"
|
|
|
|
" line.sec5 { stroke-width: 2; }\n"
|
|
|
|
" line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"
|
2013-05-15 21:52:07 +02:00
|
|
|
" text { font-family: Verdana, Helvetica; font-size: 14px; }\n"
|
|
|
|
" text.left { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: start; }\n"
|
|
|
|
" text.right { font-family: Verdana, Helvetica; font-size: 14px; text-anchor: end; }\n"
|
|
|
|
" text.sec { font-size: 10px; }\n"
|
2013-02-06 15:21:03 +01:00
|
|
|
" ]]>\n </style>\n</defs>\n\n");
|
|
|
|
|
2013-10-21 21:29:23 +02:00
|
|
|
svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
|
2013-02-06 15:21:03 +01:00
|
|
|
svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
|
2014-04-24 00:03:35 +02:00
|
|
|
svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
|
2015-10-07 08:02:31 +02:00
|
|
|
isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
|
2015-10-02 09:45:49 +02:00
|
|
|
strempty(host->hostname),
|
|
|
|
strempty(host->kernel_name),
|
|
|
|
strempty(host->kernel_release),
|
|
|
|
strempty(host->kernel_version),
|
|
|
|
strempty(host->architecture),
|
|
|
|
strempty(host->virtualization));
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-03-08 19:10:24 +01:00
|
|
|
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
|
2014-01-31 07:07:20 +01:00
|
|
|
svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
if (boot->firmware_time) {
|
2013-03-08 18:58:08 +01:00
|
|
|
svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
|
|
|
|
svg_text(true, -(double) boot->firmware_time, y, "firmware");
|
2013-02-06 15:21:03 +01:00
|
|
|
y++;
|
|
|
|
}
|
|
|
|
if (boot->loader_time) {
|
2013-03-08 18:58:08 +01:00
|
|
|
svg_bar("loader", -(double) boot->loader_time, 0, y);
|
|
|
|
svg_text(true, -(double) boot->loader_time, y, "loader");
|
2013-02-06 15:21:03 +01:00
|
|
|
y++;
|
|
|
|
}
|
|
|
|
if (boot->kernel_time) {
|
|
|
|
svg_bar("kernel", 0, boot->kernel_done_time, y);
|
2013-03-08 18:58:08 +01:00
|
|
|
svg_text(true, 0, y, "kernel");
|
2013-02-06 15:21:03 +01:00
|
|
|
y++;
|
|
|
|
}
|
|
|
|
if (boot->initrd_time) {
|
|
|
|
svg_bar("initrd", boot->initrd_time, boot->userspace_time, y);
|
2013-03-08 18:58:08 +01:00
|
|
|
svg_text(true, boot->initrd_time, y, "initrd");
|
2013-02-06 15:21:03 +01:00
|
|
|
y++;
|
|
|
|
}
|
2013-05-08 14:29:12 +02:00
|
|
|
svg_bar("active", boot->userspace_time, boot->finish_time, y);
|
2013-11-10 23:21:15 +01:00
|
|
|
svg_bar("security", boot->security_start_time, boot->security_finish_time, y);
|
2013-05-08 14:29:12 +02:00
|
|
|
svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y);
|
2013-05-15 21:18:26 +02:00
|
|
|
svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y);
|
2013-11-01 22:57:47 +01:00
|
|
|
svg_text(true, boot->userspace_time, y, "systemd");
|
2013-02-06 15:21:03 +01:00
|
|
|
y++;
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
for (u = times; u < times + n; u++) {
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX];
|
2013-03-20 23:22:20 +01:00
|
|
|
bool b;
|
2013-03-08 18:58:08 +01:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
if (!u->name)
|
|
|
|
continue;
|
2013-03-08 18:58:08 +01:00
|
|
|
|
2013-11-01 23:11:37 +01:00
|
|
|
svg_bar("activating", u->activating, u->activated, y);
|
|
|
|
svg_bar("active", u->activated, u->deactivating, y);
|
|
|
|
svg_bar("deactivating", u->deactivating, u->deactivated, y);
|
2013-03-08 18:58:08 +01:00
|
|
|
|
2013-11-01 22:57:47 +01:00
|
|
|
/* place the text on the left if we have passed the half of the svg width */
|
2013-11-01 23:11:37 +01:00
|
|
|
b = u->activating * SCALE_X < width / 2;
|
2013-03-20 23:22:20 +01:00
|
|
|
if (u->time)
|
2013-11-01 23:11:37 +01:00
|
|
|
svg_text(b, u->activating, y, "%s (%s)",
|
2013-04-04 02:56:56 +02:00
|
|
|
u->name, format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC));
|
2013-03-08 18:58:08 +01:00
|
|
|
else
|
2013-11-01 23:11:37 +01:00
|
|
|
svg_text(b, u->activating, y, "%s", u->name);
|
2013-02-06 15:21:03 +01:00
|
|
|
y++;
|
|
|
|
}
|
2013-05-08 14:29:12 +02:00
|
|
|
|
2014-01-31 07:07:20 +01:00
|
|
|
svg("</g>\n");
|
|
|
|
|
2013-05-08 14:29:12 +02:00
|
|
|
/* Legend */
|
2014-01-31 07:07:20 +01:00
|
|
|
svg("<g transform=\"translate(20,100)\">\n");
|
2013-05-08 14:29:12 +02:00
|
|
|
y++;
|
|
|
|
svg_bar("activating", 0, 300000, y);
|
2013-11-01 22:57:47 +01:00
|
|
|
svg_text(true, 400000, y, "Activating");
|
2013-05-08 14:29:12 +02:00
|
|
|
y++;
|
|
|
|
svg_bar("active", 0, 300000, y);
|
2013-11-01 22:57:47 +01:00
|
|
|
svg_text(true, 400000, y, "Active");
|
2013-05-08 14:29:12 +02:00
|
|
|
y++;
|
|
|
|
svg_bar("deactivating", 0, 300000, y);
|
2013-11-01 22:57:47 +01:00
|
|
|
svg_text(true, 400000, y, "Deactivating");
|
2013-05-08 14:29:12 +02:00
|
|
|
y++;
|
2013-11-10 23:21:15 +01:00
|
|
|
svg_bar("security", 0, 300000, y);
|
|
|
|
svg_text(true, 400000, y, "Setting up security module");
|
|
|
|
y++;
|
2013-05-08 14:29:12 +02:00
|
|
|
svg_bar("generators", 0, 300000, y);
|
2013-11-01 22:57:47 +01:00
|
|
|
svg_text(true, 400000, y, "Generators");
|
2013-05-08 14:29:12 +02:00
|
|
|
y++;
|
2013-05-15 21:18:26 +02:00
|
|
|
svg_bar("unitsload", 0, 300000, y);
|
2013-11-01 22:57:47 +01:00
|
|
|
svg_text(true, 400000, y, "Loading unit files");
|
2013-05-15 21:18:26 +02:00
|
|
|
y++;
|
2013-05-08 14:29:12 +02:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
svg("</g>\n\n");
|
|
|
|
|
2013-11-07 22:06:29 +01:00
|
|
|
svg("</svg>\n");
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
free_unit_times(times, (unsigned) n);
|
|
|
|
|
2014-04-24 00:03:35 +02:00
|
|
|
n = 0;
|
|
|
|
return n;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2013-04-23 11:11:10 +02:00
|
|
|
static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
|
|
|
|
bool last, struct unit_times *times, struct boot_times *boot) {
|
|
|
|
unsigned int i;
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
|
|
|
|
|
|
|
|
for (i = level; i != 0; i--)
|
2016-05-07 23:30:18 +02:00
|
|
|
printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE));
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2016-05-07 23:30:18 +02:00
|
|
|
printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH));
|
2013-04-23 11:11:10 +02:00
|
|
|
|
|
|
|
if (times) {
|
|
|
|
if (times->time)
|
2016-05-30 18:22:16 +02:00
|
|
|
printf("%s%s @%s +%s%s", ansi_highlight_red(), name,
|
2013-11-01 23:11:37 +01:00
|
|
|
format_timespan(ts, sizeof(ts), times->activating - boot->userspace_time, USEC_PER_MSEC),
|
2016-05-30 18:22:16 +02:00
|
|
|
format_timespan(ts2, sizeof(ts2), times->time, USEC_PER_MSEC), ansi_normal());
|
2013-11-01 23:11:37 +01:00
|
|
|
else if (times->activated > boot->userspace_time)
|
|
|
|
printf("%s @%s", name, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
|
2013-04-23 11:11:10 +02:00
|
|
|
else
|
|
|
|
printf("%s", name);
|
2013-11-07 22:06:29 +01:00
|
|
|
} else
|
|
|
|
printf("%s", name);
|
2013-04-23 11:11:10 +02:00
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
|
2013-11-07 22:06:29 +01:00
|
|
|
_cleanup_free_ char *path = NULL;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(name);
|
|
|
|
assert(deps);
|
|
|
|
|
|
|
|
path = unit_dbus_path_from_name(name);
|
2017-12-08 20:52:38 +01:00
|
|
|
if (!path)
|
2013-11-07 22:06:29 +01:00
|
|
|
return -ENOMEM;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2013-11-07 22:06:29 +01:00
|
|
|
return bus_get_unit_property_strv(bus, path, "After", deps);
|
2013-04-23 11:11:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static Hashmap *unit_times_hashmap;
|
|
|
|
|
|
|
|
static int list_dependencies_compare(const void *_a, const void *_b) {
|
|
|
|
const char **a = (const char**) _a, **b = (const char**) _b;
|
|
|
|
usec_t usa = 0, usb = 0;
|
|
|
|
struct unit_times *times;
|
|
|
|
|
|
|
|
times = hashmap_get(unit_times_hashmap, *a);
|
|
|
|
if (times)
|
2013-11-01 23:11:37 +01:00
|
|
|
usa = times->activated;
|
2013-04-23 11:11:10 +02:00
|
|
|
times = hashmap_get(unit_times_hashmap, *b);
|
|
|
|
if (times)
|
2013-11-01 23:11:37 +01:00
|
|
|
usb = times->activated;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
|
|
|
return usb - usa;
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
static int list_dependencies_one(sd_bus *bus, const char *name, unsigned int level, char ***units,
|
2013-04-23 11:11:10 +02:00
|
|
|
unsigned int branches) {
|
|
|
|
_cleanup_strv_free_ char **deps = NULL;
|
|
|
|
char **c;
|
|
|
|
int r = 0;
|
|
|
|
usec_t service_longest = 0;
|
|
|
|
int to_print = 0;
|
|
|
|
struct unit_times *times;
|
|
|
|
struct boot_times *boot;
|
|
|
|
|
2013-11-07 22:06:29 +01:00
|
|
|
if (strv_extend(units, name))
|
2013-04-23 11:11:10 +02:00
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = list_dependencies_get_dependencies(bus, name, &deps);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-12 01:33:13 +02:00
|
|
|
qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
|
2013-04-23 11:11:10 +02:00
|
|
|
|
|
|
|
r = acquire_boot_times(bus, &boot);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
STRV_FOREACH(c, deps) {
|
|
|
|
times = hashmap_get(unit_times_hashmap, *c);
|
|
|
|
if (times
|
2013-11-01 23:11:37 +01:00
|
|
|
&& times->activated
|
|
|
|
&& times->activated <= boot->finish_time
|
|
|
|
&& (times->activated >= service_longest
|
2013-04-23 11:11:10 +02:00
|
|
|
|| service_longest == 0)) {
|
2013-11-01 23:11:37 +01:00
|
|
|
service_longest = times->activated;
|
2013-04-23 11:11:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-08 20:52:38 +01:00
|
|
|
if (service_longest == 0)
|
2013-04-23 11:11:10 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
STRV_FOREACH(c, deps) {
|
|
|
|
times = hashmap_get(unit_times_hashmap, *c);
|
2015-09-08 23:03:38 +02:00
|
|
|
if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz)
|
2013-04-23 11:11:10 +02:00
|
|
|
to_print++;
|
|
|
|
}
|
|
|
|
|
2013-12-03 22:27:45 +01:00
|
|
|
if (!to_print)
|
2013-04-23 11:11:10 +02:00
|
|
|
return r;
|
|
|
|
|
|
|
|
STRV_FOREACH(c, deps) {
|
|
|
|
times = hashmap_get(unit_times_hashmap, *c);
|
|
|
|
if (!times
|
2013-11-01 23:11:37 +01:00
|
|
|
|| !times->activated
|
|
|
|
|| times->activated > boot->finish_time
|
|
|
|
|| service_longest - times->activated > arg_fuzz)
|
2013-04-23 11:11:10 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
to_print--;
|
|
|
|
|
|
|
|
r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (strv_contains(*units, *c)) {
|
|
|
|
r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0),
|
|
|
|
true, NULL, boot);
|
2013-10-12 01:34:17 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-04-23 11:11:10 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = list_dependencies_one(bus, *c, level + 1, units,
|
|
|
|
(branches << 1) | (to_print ? 1 : 0));
|
2013-10-12 01:34:17 +02:00
|
|
|
if (r < 0)
|
2013-04-23 11:11:10 +02:00
|
|
|
return r;
|
|
|
|
|
2013-10-12 01:34:17 +02:00
|
|
|
if (!to_print)
|
2013-04-23 11:11:10 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
static int list_dependencies(sd_bus *bus, const char *name) {
|
2013-04-23 11:11:10 +02:00
|
|
|
_cleanup_strv_free_ char **units = NULL;
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX];
|
|
|
|
struct unit_times *times;
|
|
|
|
int r;
|
2014-09-13 12:29:43 +02:00
|
|
|
const char *id;
|
|
|
|
_cleanup_free_ char *path = NULL;
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2013-04-23 11:11:10 +02:00
|
|
|
struct boot_times *boot;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
2013-06-11 22:29:32 +02:00
|
|
|
path = unit_dbus_path_from_name(name);
|
2017-12-08 20:52:38 +01:00
|
|
|
if (!path)
|
2013-11-07 22:06:29 +01:00
|
|
|
return -ENOMEM;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2013-10-31 23:08:16 +01:00
|
|
|
r = sd_bus_get_property(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
path,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"Id",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"s");
|
2013-10-25 22:56:19 +02:00
|
|
|
if (r < 0) {
|
2013-11-07 21:26:31 +01:00
|
|
|
log_error("Failed to get ID: %s", bus_error_message(&error, -r));
|
2013-04-23 11:11:10 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
r = sd_bus_message_read(reply, "s", &id);
|
2013-11-07 21:26:31 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2013-04-23 11:11:10 +02:00
|
|
|
|
|
|
|
times = hashmap_get(unit_times_hashmap, id);
|
|
|
|
|
|
|
|
r = acquire_boot_times(bus, &boot);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (times) {
|
|
|
|
if (times->time)
|
2016-05-30 18:22:16 +02:00
|
|
|
printf("%s%s +%s%s\n", ansi_highlight_red(), id,
|
|
|
|
format_timespan(ts, sizeof(ts), times->time, USEC_PER_MSEC), ansi_normal());
|
2013-11-01 23:11:37 +01:00
|
|
|
else if (times->activated > boot->userspace_time)
|
|
|
|
printf("%s @%s\n", id, format_timespan(ts, sizeof(ts), times->activated - boot->userspace_time, USEC_PER_MSEC));
|
2013-04-23 11:11:10 +02:00
|
|
|
else
|
|
|
|
printf("%s\n", id);
|
|
|
|
}
|
|
|
|
|
2013-06-11 22:29:32 +02:00
|
|
|
return list_dependencies_one(bus, name, 0, &units, 0);
|
2013-04-23 11:11:10 +02:00
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-04-23 11:11:10 +02:00
|
|
|
struct unit_times *times;
|
|
|
|
unsigned int i;
|
|
|
|
Hashmap *h;
|
2013-11-07 22:06:29 +01:00
|
|
|
int n, r;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
|
|
|
|
2013-04-23 11:11:10 +02:00
|
|
|
n = acquire_time_data(bus, ×);
|
|
|
|
if (n <= 0)
|
|
|
|
return n;
|
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
h = hashmap_new(&string_hash_ops);
|
2013-04-23 11:11:10 +02:00
|
|
|
if (!h)
|
2017-12-26 16:01:14 +01:00
|
|
|
return log_oom();
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2017-12-26 16:01:14 +01:00
|
|
|
for (i = 0; i < (unsigned) n; i++) {
|
2013-04-23 11:11:10 +02:00
|
|
|
r = hashmap_put(h, times[i].name, ×[i]);
|
|
|
|
if (r < 0)
|
2017-12-26 16:01:14 +01:00
|
|
|
return log_error_errno(r, "Failed to add entry to hashmap: %m");
|
2013-04-23 11:11:10 +02:00
|
|
|
}
|
|
|
|
unit_times_hashmap = h;
|
|
|
|
|
2016-02-19 19:25:13 +01:00
|
|
|
pager_open(arg_no_pager, false);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2013-04-23 11:11:10 +02:00
|
|
|
puts("The time after the unit is active or started is printed after the \"@\" character.\n"
|
|
|
|
"The time the unit takes to start is printed after the \"+\" character.\n");
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
if (argc > 1) {
|
2013-06-11 22:29:32 +02:00
|
|
|
char **name;
|
2017-12-26 15:58:06 +01:00
|
|
|
STRV_FOREACH(name, strv_skip(argv, 1))
|
2013-06-11 22:29:32 +02:00
|
|
|
list_dependencies(bus, *name);
|
2013-07-26 16:34:52 +02:00
|
|
|
} else
|
2013-06-11 22:29:32 +02:00
|
|
|
list_dependencies(bus, SPECIAL_DEFAULT_TARGET);
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
h = hashmap_free(h);
|
2013-04-23 11:11:10 +02:00
|
|
|
free_unit_times(times, (unsigned) n);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int analyze_blame(int argc, char *argv[], void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-02-06 15:21:03 +01:00
|
|
|
struct unit_times *times;
|
2013-03-08 18:58:08 +01:00
|
|
|
unsigned i;
|
2017-12-26 15:58:06 +01:00
|
|
|
int n, r;
|
|
|
|
|
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
n = acquire_time_data(bus, ×);
|
|
|
|
if (n <= 0)
|
2013-02-06 15:21:03 +01:00
|
|
|
return n;
|
|
|
|
|
|
|
|
qsort(times, n, sizeof(struct unit_times), compare_unit_time);
|
|
|
|
|
2016-02-19 19:25:13 +01:00
|
|
|
pager_open(arg_no_pager, false);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
for (i = 0; i < (unsigned) n; i++) {
|
|
|
|
char ts[FORMAT_TIMESPAN_MAX];
|
|
|
|
|
|
|
|
if (times[i].time > 0)
|
2013-04-04 02:56:56 +02:00
|
|
|
printf("%16s %s\n", format_timespan(ts, sizeof(ts), times[i].time, USEC_PER_MSEC), times[i].name);
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
free_unit_times(times, (unsigned) n);
|
2013-02-06 15:21:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int analyze_time(int argc, char *argv[], void *userdata) {
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-03-08 18:58:08 +01:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
|
|
|
int r;
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
r = pretty_boot_time(bus, &buf);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
puts(buf);
|
2013-02-06 15:21:03 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-07 00:34:33 +02:00
|
|
|
static int graph_one_property(sd_bus *bus, const UnitInfo *u, const char* prop, const char *color, char* patterns[], char* from_patterns[], char* to_patterns[]) {
|
2013-10-25 22:56:19 +02:00
|
|
|
_cleanup_strv_free_ char **units = NULL;
|
|
|
|
char **unit;
|
|
|
|
int r;
|
2015-02-14 00:38:33 +01:00
|
|
|
bool match_patterns;
|
2013-02-13 23:17:28 +01:00
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
assert(u);
|
2013-02-13 23:17:28 +01:00
|
|
|
assert(prop);
|
2013-10-25 22:56:19 +02:00
|
|
|
assert(color);
|
|
|
|
|
2015-02-16 20:04:36 +01:00
|
|
|
match_patterns = strv_fnmatch(patterns, u->id, 0);
|
2015-02-14 00:38:33 +01:00
|
|
|
|
2015-09-07 00:34:33 +02:00
|
|
|
if (!strv_isempty(from_patterns) &&
|
2015-02-14 00:38:33 +01:00
|
|
|
!match_patterns &&
|
2015-09-07 00:34:33 +02:00
|
|
|
!strv_fnmatch(from_patterns, u->id, 0))
|
2015-02-14 00:38:33 +01:00
|
|
|
return 0;
|
|
|
|
|
2013-10-31 23:11:39 +01:00
|
|
|
r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
|
2013-11-06 16:38:08 +01:00
|
|
|
if (r < 0)
|
2013-11-07 22:06:29 +01:00
|
|
|
return r;
|
2013-02-13 23:17:28 +01:00
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
STRV_FOREACH(unit, units) {
|
2015-02-14 00:38:33 +01:00
|
|
|
bool match_patterns2;
|
|
|
|
|
2015-02-16 20:04:36 +01:00
|
|
|
match_patterns2 = strv_fnmatch(patterns, *unit, 0);
|
2013-04-08 19:42:48 +02:00
|
|
|
|
2015-09-07 00:34:33 +02:00
|
|
|
if (!strv_isempty(to_patterns) &&
|
2015-02-14 00:38:33 +01:00
|
|
|
!match_patterns2 &&
|
2015-09-07 00:34:33 +02:00
|
|
|
!strv_fnmatch(to_patterns, *unit, 0))
|
2015-02-14 00:37:43 +01:00
|
|
|
continue;
|
2013-04-06 15:40:56 +02:00
|
|
|
|
2015-02-14 00:38:33 +01:00
|
|
|
if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
|
2015-02-14 00:37:43 +01:00
|
|
|
continue;
|
2013-10-25 22:56:19 +02:00
|
|
|
|
|
|
|
printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
|
2013-02-13 23:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-07 00:34:33 +02:00
|
|
|
static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) {
|
2013-02-13 23:17:28 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
assert(u);
|
|
|
|
|
2015-11-13 14:30:07 +01:00
|
|
|
if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) {
|
2015-09-07 00:34:33 +02:00
|
|
|
r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns);
|
2013-10-25 22:56:19 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-02-13 23:17:28 +01:00
|
|
|
}
|
|
|
|
|
2015-11-13 14:30:07 +01:00
|
|
|
if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) {
|
2015-09-07 00:34:33 +02:00
|
|
|
r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns);
|
2015-11-13 14:30:07 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns);
|
2013-10-25 22:56:19 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2015-09-07 00:34:33 +02:00
|
|
|
r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns);
|
2013-10-25 22:56:19 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2015-09-07 00:34:33 +02:00
|
|
|
r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns);
|
2013-10-25 22:56:19 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-02-13 23:17:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-05 07:18:08 +02:00
|
|
|
static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
|
|
|
|
_cleanup_strv_free_ char **expanded_patterns = NULL;
|
|
|
|
char **pattern;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
STRV_FOREACH(pattern, patterns) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2015-09-05 07:18:08 +02:00
|
|
|
_cleanup_free_ char *unit = NULL, *unit_id = NULL;
|
|
|
|
|
|
|
|
if (strv_extend(&expanded_patterns, *pattern) < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
if (string_is_glob(*pattern))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
unit = unit_dbus_path_from_name(*pattern);
|
|
|
|
if (!unit)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
r = sd_bus_get_property_string(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
unit,
|
|
|
|
"org.freedesktop.systemd1.Unit",
|
|
|
|
"Id",
|
|
|
|
&error,
|
|
|
|
&unit_id);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
if (!streq(*pattern, unit_id)) {
|
|
|
|
if (strv_extend(&expanded_patterns, unit_id) < 0)
|
|
|
|
return log_oom();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = expanded_patterns;
|
|
|
|
expanded_patterns = NULL; /* do not free */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int dot(int argc, char *argv[], void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2015-09-05 07:18:08 +02:00
|
|
|
_cleanup_strv_free_ char **expanded_patterns = NULL;
|
2015-09-07 00:34:33 +02:00
|
|
|
_cleanup_strv_free_ char **expanded_from_patterns = NULL;
|
|
|
|
_cleanup_strv_free_ char **expanded_to_patterns = NULL;
|
2013-02-13 23:17:28 +01:00
|
|
|
int r;
|
2013-11-07 05:49:04 +01:00
|
|
|
UnitInfo u;
|
2013-10-25 22:56:19 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
|
|
|
|
|
|
|
r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
|
2015-09-05 07:18:08 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-09-07 00:34:33 +02:00
|
|
|
r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-10-31 23:08:16 +01:00
|
|
|
r = sd_bus_call_method(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"ListUnits",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"");
|
2013-10-25 22:56:19 +02:00
|
|
|
if (r < 0) {
|
2013-11-07 22:06:29 +01:00
|
|
|
log_error("Failed to list units: %s", bus_error_message(&error, -r));
|
|
|
|
return r;
|
2013-10-25 22:56:19 +02:00
|
|
|
}
|
2013-02-13 23:17:28 +01:00
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
|
2013-02-13 23:17:28 +01:00
|
|
|
if (r < 0)
|
2013-11-07 22:06:29 +01:00
|
|
|
return bus_log_parse_error(r);
|
2013-02-13 23:17:28 +01:00
|
|
|
|
|
|
|
printf("digraph systemd {\n");
|
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
|
2013-11-07 05:49:04 +01:00
|
|
|
|
2015-09-07 00:34:33 +02:00
|
|
|
r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns);
|
2013-02-13 23:17:28 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
2013-11-07 05:49:04 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2013-02-13 23:17:28 +01:00
|
|
|
|
|
|
|
printf("}\n");
|
|
|
|
|
|
|
|
log_info(" Color legend: black = Requires\n"
|
|
|
|
" dark blue = Requisite\n"
|
|
|
|
" dark grey = Wants\n"
|
|
|
|
" red = Conflicts\n"
|
|
|
|
" green = After\n");
|
|
|
|
|
|
|
|
if (on_tty())
|
|
|
|
log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
|
|
|
|
"-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2013-11-07 03:20:40 +01:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int dump(int argc, char *argv[], void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-10-25 22:56:19 +02:00
|
|
|
const char *text = NULL;
|
2013-07-26 16:34:52 +02:00
|
|
|
int r;
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2013-07-26 16:59:55 +02:00
|
|
|
|
2016-02-19 19:25:13 +01:00
|
|
|
pager_open(arg_no_pager, false);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2013-10-31 23:08:16 +01:00
|
|
|
r = sd_bus_call_method(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"Dump",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
"");
|
2015-09-30 15:01:01 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r));
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2013-10-25 22:56:19 +02:00
|
|
|
r = sd_bus_message_read(reply, "s", &text);
|
2013-11-07 21:26:31 +01:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
|
|
|
fputs(text, stdout);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int set_log_level(int argc, char *argv[], void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2013-10-25 22:56:19 +02:00
|
|
|
int r;
|
2013-07-26 16:59:55 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
assert(argc == 2);
|
|
|
|
assert(argv);
|
2013-07-26 16:59:55 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2013-07-26 16:59:55 +02:00
|
|
|
|
2013-10-31 23:08:16 +01:00
|
|
|
r = sd_bus_set_property(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LogLevel",
|
|
|
|
&error,
|
|
|
|
"s",
|
2017-12-26 15:58:06 +01:00
|
|
|
argv[1]);
|
2015-09-30 15:01:01 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int get_log_level(int argc, char *argv[], void *userdata) {
|
2017-09-07 23:41:20 +02:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2017-09-07 23:41:20 +02:00
|
|
|
_cleanup_free_ char *level = NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
int r;
|
2017-09-07 23:41:20 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2017-09-07 23:41:20 +02:00
|
|
|
|
|
|
|
r = sd_bus_get_property_string(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LogLevel",
|
|
|
|
&error,
|
|
|
|
&level);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
puts(level);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int set_log_target(int argc, char *argv[], void *userdata) {
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2015-09-30 15:01:01 +02:00
|
|
|
int r;
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
assert(argc == 2);
|
|
|
|
assert(argv);
|
2015-09-30 15:01:01 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2013-07-26 16:59:55 +02:00
|
|
|
|
2015-09-30 15:01:01 +02:00
|
|
|
r = sd_bus_set_property(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LogTarget",
|
|
|
|
&error,
|
|
|
|
"s",
|
2017-12-26 15:58:06 +01:00
|
|
|
argv[1]);
|
2015-09-30 15:01:01 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
|
|
|
|
|
2013-07-26 16:59:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int get_log_target(int argc, char *argv[], void *userdata) {
|
2017-09-07 23:41:20 +02:00
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
2017-09-07 23:41:20 +02:00
|
|
|
_cleanup_free_ char *target = NULL;
|
2017-12-26 15:58:06 +01:00
|
|
|
int r;
|
2017-09-07 23:41:20 +02:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = acquire_bus(false, &bus);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2017-09-07 23:41:20 +02:00
|
|
|
|
|
|
|
r = sd_bus_get_property_string(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"LogTarget",
|
|
|
|
&error,
|
|
|
|
&target);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
puts(target);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-03 10:41:51 +02:00
|
|
|
#if HAVE_SECCOMP
|
2016-11-02 16:58:18 +01:00
|
|
|
static void dump_syscall_filter(const SyscallFilterSet *set) {
|
|
|
|
const char *syscall;
|
|
|
|
|
|
|
|
printf("%s\n", set->name);
|
2016-11-02 17:24:34 +01:00
|
|
|
printf(" # %s\n", set->help);
|
2016-11-02 16:58:18 +01:00
|
|
|
NULSTR_FOREACH(syscall, set->value)
|
|
|
|
printf(" %s\n", syscall);
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
|
2016-11-02 16:58:18 +01:00
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
pager_open(arg_no_pager, false);
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
if (strv_isempty(strv_skip(argv, 1))) {
|
2016-11-02 16:58:18 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
|
|
|
|
if (!first)
|
|
|
|
puts("");
|
|
|
|
dump_syscall_filter(syscall_filter_sets + i);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
char **name;
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
STRV_FOREACH(name, strv_skip(argv, 1)) {
|
2016-11-02 16:58:18 +01:00
|
|
|
const SyscallFilterSet *set;
|
|
|
|
|
|
|
|
if (!first)
|
|
|
|
puts("");
|
|
|
|
|
|
|
|
set = syscall_filter_set_find(*name);
|
|
|
|
if (!set) {
|
|
|
|
/* make sure the error appears below normal output */
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
log_error("Filter set \"%s\" not found.", *name);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
dump_syscall_filter(set);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-03 19:33:49 +01:00
|
|
|
#else
|
|
|
|
static int dump_syscall_filters(char** names) {
|
|
|
|
log_error("Not compiled with syscall filters, sorry.");
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int test_calendar(int argc, char *argv[], void *userdata) {
|
2017-11-17 10:33:22 +01:00
|
|
|
int ret = 0, r;
|
|
|
|
char **p;
|
|
|
|
usec_t n;
|
|
|
|
|
|
|
|
n = now(CLOCK_REALTIME);
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
STRV_FOREACH(p, strv_skip(argv, 1)) {
|
2017-11-17 10:33:22 +01:00
|
|
|
_cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL;
|
|
|
|
_cleanup_free_ char *t = NULL;
|
|
|
|
usec_t next;
|
|
|
|
|
|
|
|
r = calendar_spec_from_string(*p, &spec);
|
|
|
|
if (r < 0) {
|
|
|
|
ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = calendar_spec_normalize(spec);
|
|
|
|
if (r < 0) {
|
|
|
|
ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = calendar_spec_to_string(spec, &t);
|
|
|
|
if (r < 0) {
|
|
|
|
ret = log_error_errno(r, "Failed to fomat calendar specification '%s': %m", *p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!streq(t, *p))
|
|
|
|
printf(" Original form: %s\n", *p);
|
|
|
|
|
|
|
|
printf("Normalized form: %s\n", t);
|
|
|
|
|
|
|
|
r = calendar_spec_next_usec(spec, n, &next);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
printf(" Next elapse: never\n");
|
|
|
|
else if (r < 0) {
|
|
|
|
ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)];
|
|
|
|
|
|
|
|
printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next));
|
|
|
|
|
|
|
|
if (!in_utc_timezone())
|
|
|
|
printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next));
|
|
|
|
|
|
|
|
printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*(p+1))
|
|
|
|
putchar('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int do_verify(int argc, char *argv[], void *userdata) {
|
|
|
|
return verify_units(strv_skip(argv, 1),
|
|
|
|
arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM,
|
|
|
|
arg_man,
|
|
|
|
arg_generators);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int help(int argc, char *argv[], void *userdata) {
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2016-02-19 19:25:13 +01:00
|
|
|
pager_open(arg_no_pager, false);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
2014-07-22 03:11:54 +02:00
|
|
|
"Profile systemd, show unit dependencies, check unit files.\n\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" -h --help Show this help\n"
|
|
|
|
" --version Show package version\n"
|
|
|
|
" --no-pager Do not pipe output into a pager\n"
|
|
|
|
" --system Operate on system systemd instance\n"
|
|
|
|
" --user Operate on user systemd instance\n"
|
|
|
|
" -H --host=[USER@]HOST Operate on remote host\n"
|
|
|
|
" -M --machine=CONTAINER Operate on local container\n"
|
|
|
|
" --order Show only order in the graph\n"
|
|
|
|
" --require Show only requirement in the graph\n"
|
|
|
|
" --from-pattern=GLOB Show only origins in the graph\n"
|
|
|
|
" --to-pattern=GLOB Show only destinations in the graph\n"
|
|
|
|
" --fuzz=SECONDS Also print also services which finished SECONDS\n"
|
|
|
|
" earlier than the latest in the branch\n"
|
|
|
|
" --man[=BOOL] Do [not] check for existence of man pages\n\n"
|
2017-09-16 11:29:34 +02:00
|
|
|
" --generators[=BOOL] Do [not] run unit generators (requires privileges)\n\n"
|
2013-02-06 15:21:03 +01:00
|
|
|
"Commands:\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" time Print time spent in the kernel\n"
|
|
|
|
" blame Print list of running units ordered by time to init\n"
|
|
|
|
" critical-chain Print a tree of the time critical chain of units\n"
|
|
|
|
" plot Output SVG graphic showing service initialization\n"
|
2017-02-21 00:45:35 +01:00
|
|
|
" dot Output dependency graph in man:dot(1) format\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" set-log-level LEVEL Set logging threshold for manager\n"
|
|
|
|
" set-log-target TARGET Set logging target for manager\n"
|
2017-09-07 23:41:20 +02:00
|
|
|
" get-log-level Get logging threshold for manager\n"
|
|
|
|
" get-log-target Get logging target for manager\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" dump Output state serialization of service manager\n"
|
|
|
|
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
|
|
|
|
" verify FILE... Check unit files for correctness\n"
|
2017-11-17 10:33:22 +01:00
|
|
|
" calendar SPEC... Validate repetitive calendar time events\n"
|
2014-07-22 03:11:54 +02:00
|
|
|
, program_invocation_short_name);
|
2013-04-30 04:02:57 +02:00
|
|
|
|
|
|
|
/* When updating this list, including descriptions, apply
|
2014-07-22 03:11:54 +02:00
|
|
|
* changes to shell-completion/bash/systemd-analyze and
|
|
|
|
* shell-completion/zsh/_systemd-analyze too. */
|
2017-12-26 15:58:06 +01:00
|
|
|
|
|
|
|
return 0;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2013-07-26 16:34:52 +02:00
|
|
|
static int parse_argv(int argc, char *argv[]) {
|
2013-02-06 15:21:03 +01:00
|
|
|
enum {
|
|
|
|
ARG_VERSION = 0x100,
|
2013-02-13 23:17:28 +01:00
|
|
|
ARG_ORDER,
|
|
|
|
ARG_REQUIRE,
|
2013-02-06 15:21:03 +01:00
|
|
|
ARG_USER,
|
2013-04-06 15:40:56 +02:00
|
|
|
ARG_SYSTEM,
|
|
|
|
ARG_DOT_FROM_PATTERN,
|
2013-04-23 11:11:10 +02:00
|
|
|
ARG_DOT_TO_PATTERN,
|
2013-07-26 16:34:52 +02:00
|
|
|
ARG_FUZZ,
|
2014-07-22 03:11:54 +02:00
|
|
|
ARG_NO_PAGER,
|
2014-08-20 00:15:05 +02:00
|
|
|
ARG_MAN,
|
2017-09-16 11:29:34 +02:00
|
|
|
ARG_GENERATORS,
|
2013-02-06 15:21:03 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option options[] = {
|
2013-07-26 16:34:52 +02:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "version", no_argument, NULL, ARG_VERSION },
|
|
|
|
{ "order", no_argument, NULL, ARG_ORDER },
|
|
|
|
{ "require", no_argument, NULL, ARG_REQUIRE },
|
|
|
|
{ "user", no_argument, NULL, ARG_USER },
|
|
|
|
{ "system", no_argument, NULL, ARG_SYSTEM },
|
|
|
|
{ "from-pattern", required_argument, NULL, ARG_DOT_FROM_PATTERN },
|
|
|
|
{ "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
|
|
|
|
{ "fuzz", required_argument, NULL, ARG_FUZZ },
|
|
|
|
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
2014-08-20 00:15:05 +02:00
|
|
|
{ "man", optional_argument, NULL, ARG_MAN },
|
2017-09-16 11:29:34 +02:00
|
|
|
{ "generators", optional_argument, NULL, ARG_GENERATORS },
|
2013-11-08 13:54:00 +01:00
|
|
|
{ "host", required_argument, NULL, 'H' },
|
|
|
|
{ "machine", required_argument, NULL, 'M' },
|
2013-11-06 18:28:39 +01:00
|
|
|
{}
|
2013-02-06 15:21:03 +01:00
|
|
|
};
|
|
|
|
|
2013-11-06 18:28:39 +01:00
|
|
|
int r, c;
|
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
assert(argc >= 0);
|
|
|
|
assert(argv);
|
|
|
|
|
2014-08-02 17:12:21 +02:00
|
|
|
while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
|
2013-11-06 18:28:39 +01:00
|
|
|
switch (c) {
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
case 'h':
|
2017-12-26 15:58:06 +01:00
|
|
|
return help(0, NULL, NULL);
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
case ARG_VERSION:
|
2015-09-23 03:01:06 +02:00
|
|
|
return version();
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
case ARG_USER:
|
2013-11-06 17:30:39 +01:00
|
|
|
arg_user = true;
|
2013-03-08 18:58:08 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_SYSTEM:
|
2013-11-06 17:30:39 +01:00
|
|
|
arg_user = false;
|
2013-03-08 18:58:08 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_ORDER:
|
|
|
|
arg_dot = DEP_ORDER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_REQUIRE:
|
|
|
|
arg_dot = DEP_REQUIRE;
|
|
|
|
break;
|
|
|
|
|
2013-04-06 15:40:56 +02:00
|
|
|
case ARG_DOT_FROM_PATTERN:
|
2013-04-08 19:42:29 +02:00
|
|
|
if (strv_extend(&arg_dot_from_patterns, optarg) < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
2013-04-06 15:40:56 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ARG_DOT_TO_PATTERN:
|
2013-04-08 19:42:29 +02:00
|
|
|
if (strv_extend(&arg_dot_to_patterns, optarg) < 0)
|
|
|
|
return log_oom();
|
|
|
|
|
2013-04-06 15:40:56 +02:00
|
|
|
break;
|
|
|
|
|
2013-04-23 11:11:10 +02:00
|
|
|
case ARG_FUZZ:
|
|
|
|
r = parse_sec(optarg, &arg_fuzz);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
break;
|
|
|
|
|
2013-07-26 16:34:52 +02:00
|
|
|
case ARG_NO_PAGER:
|
|
|
|
arg_no_pager = true;
|
|
|
|
break;
|
|
|
|
|
2013-11-06 17:30:39 +01:00
|
|
|
case 'H':
|
|
|
|
arg_transport = BUS_TRANSPORT_REMOTE;
|
|
|
|
arg_host = optarg;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'M':
|
2014-12-23 23:38:13 +01:00
|
|
|
arg_transport = BUS_TRANSPORT_MACHINE;
|
2013-11-06 17:30:39 +01:00
|
|
|
arg_host = optarg;
|
|
|
|
break;
|
|
|
|
|
2014-08-20 00:15:05 +02:00
|
|
|
case ARG_MAN:
|
|
|
|
if (optarg) {
|
|
|
|
r = parse_boolean(optarg);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("Failed to parse --man= argument.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg_man = !!r;
|
|
|
|
} else
|
|
|
|
arg_man = true;
|
|
|
|
|
2014-07-22 03:11:54 +02:00
|
|
|
break;
|
|
|
|
|
2017-09-16 11:29:34 +02:00
|
|
|
case ARG_GENERATORS:
|
|
|
|
if (optarg) {
|
|
|
|
r = parse_boolean(optarg);
|
|
|
|
if (r < 0) {
|
|
|
|
log_error("Failed to parse --generators= argument.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg_generators = !!r;
|
|
|
|
} else
|
|
|
|
arg_generators = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
case '?':
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
default:
|
2014-07-22 03:11:54 +02:00
|
|
|
assert_not_reached("Unhandled option code.");
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
2013-11-06 18:28:39 +01:00
|
|
|
|
2014-07-22 03:11:54 +02:00
|
|
|
return 1; /* work to do */
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
2017-12-26 15:58:06 +01:00
|
|
|
|
|
|
|
static const Verb verbs[] = {
|
|
|
|
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
|
|
|
{ "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time },
|
|
|
|
{ "blame", VERB_ANY, 1, 0, analyze_blame },
|
|
|
|
{ "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain },
|
|
|
|
{ "plot", VERB_ANY, 1, 0, analyze_plot },
|
|
|
|
{ "dot", VERB_ANY, VERB_ANY, 0, dot },
|
|
|
|
{ "set-log-level", 2, 2, 0, set_log_level },
|
|
|
|
{ "get-log-level", VERB_ANY, 1, 0, get_log_level },
|
|
|
|
{ "set-log-target", 2, 2, 0, set_log_target },
|
|
|
|
{ "get-log-target", VERB_ANY, 1, 0, get_log_target },
|
|
|
|
{ "dump", VERB_ANY, 1, 0, dump },
|
|
|
|
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
|
|
|
|
{ "verify", 2, VERB_ANY, 0, do_verify },
|
|
|
|
{ "calendar", 2, VERB_ANY, 0, test_calendar },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2013-10-30 16:13:21 +01:00
|
|
|
int r;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
|
|
|
setlocale(LC_ALL, "");
|
2013-03-08 18:58:08 +01:00
|
|
|
setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
|
2017-12-26 15:58:06 +01:00
|
|
|
|
2013-02-06 15:21:03 +01:00
|
|
|
log_parse_environment();
|
|
|
|
log_open();
|
|
|
|
|
|
|
|
r = parse_argv(argc, argv);
|
2013-07-26 16:34:52 +02:00
|
|
|
if (r <= 0)
|
|
|
|
goto finish;
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
r = dispatch_verb(argc, argv, verbs, NULL);
|
2013-02-06 15:21:03 +01:00
|
|
|
|
2013-07-26 16:34:52 +02:00
|
|
|
finish:
|
|
|
|
pager_close();
|
|
|
|
|
2013-04-06 15:40:56 +02:00
|
|
|
strv_free(arg_dot_from_patterns);
|
|
|
|
strv_free(arg_dot_to_patterns);
|
2013-03-08 18:58:08 +01:00
|
|
|
|
|
|
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|