2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2013-02-06 15:21:03 +01:00
|
|
|
/***
|
2018-06-12 19:00:24 +02:00
|
|
|
Copyright © 2013 Simon Peeters
|
2013-02-06 15:21:03 +01:00
|
|
|
***/
|
|
|
|
|
|
|
|
#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"
|
2018-04-27 12:50:07 +02:00
|
|
|
#include "def.h"
|
2018-04-26 13:49:50 +02:00
|
|
|
#include "conf-files.h"
|
2018-05-09 09:35:52 +02:00
|
|
|
#include "copy.h"
|
|
|
|
#include "fd-util.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"
|
2018-04-26 13:49:50 +02:00
|
|
|
#include "path-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;
|
2018-02-08 11:53:00 +01:00
|
|
|
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
|
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;
|
2018-04-27 08:55:16 +02:00
|
|
|
static const char *arg_root = NULL;
|
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 {
|
2018-06-06 18:43:37 +02:00
|
|
|
bool has_data;
|
2013-02-06 15:21:03 +01:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
static int acquire_bus(sd_bus **bus, bool *use_full_bus) {
|
2018-04-07 20:02:20 +02:00
|
|
|
bool user = arg_scope != UNIT_FILE_SYSTEM;
|
2018-05-09 10:06:46 +02:00
|
|
|
int r;
|
2018-04-07 20:02:20 +02:00
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
if (use_full_bus && *use_full_bus) {
|
2018-05-09 10:06:46 +02:00
|
|
|
r = bus_connect_transport(arg_transport, arg_host, user, bus);
|
|
|
|
if (IN_SET(r, 0, -EHOSTDOWN))
|
|
|
|
return r;
|
2018-04-07 20:02:20 +02:00
|
|
|
|
|
|
|
*use_full_bus = false;
|
|
|
|
}
|
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
return bus_connect_transport_systemd(arg_transport, arg_host, user, bus);
|
2017-12-18 19:35:03 +01:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
static void unit_times_free(struct unit_times *t) {
|
2013-03-08 18:58:08 +01:00
|
|
|
struct unit_times *p;
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
for (p = t; p->has_data; p++)
|
2013-03-08 18:58:08 +01:00
|
|
|
free(p->name);
|
|
|
|
free(t);
|
|
|
|
}
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
DEFINE_TRIVIAL_CLEANUP_FUNC(struct unit_times *, unit_times_free);
|
|
|
|
|
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) {
|
2018-04-07 18:43:47 +02:00
|
|
|
log_error("Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n"
|
|
|
|
"Please try again later.\n"
|
|
|
|
"Hint: Use 'systemctl%s list-jobs' to see active jobs",
|
|
|
|
times.finish_time,
|
|
|
|
arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
|
2013-11-07 22:06:29 +01:00
|
|
|
return -EINPROGRESS;
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
|
|
|
|
2018-02-08 11:53:00 +01:00
|
|
|
if (arg_scope == UNIT_FILE_SYSTEM) {
|
2018-02-08 22:20:01 +01:00
|
|
|
if (times.initrd_time > 0)
|
2018-02-08 11:53:00 +01:00
|
|
|
times.kernel_done_time = times.initrd_time;
|
|
|
|
else
|
|
|
|
times.kernel_done_time = times.userspace_time;
|
|
|
|
} else {
|
2015-07-27 18:40:36 +02:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
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;
|
2018-06-06 18:43:37 +02:00
|
|
|
_cleanup_(unit_times_freep) struct unit_times *unit_times = NULL;
|
2015-07-28 00:01:10 +02:00
|
|
|
size_t size = 0;
|
|
|
|
UnitInfo u;
|
|
|
|
|
2015-07-27 18:40:36 +02:00
|
|
|
r = acquire_boot_times(bus, &boot_times);
|
|
|
|
if (r < 0)
|
2018-06-06 18:43:37 +02:00
|
|
|
return r;
|
2015-07-27 18:40:36 +02:00
|
|
|
|
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));
|
2018-06-06 18:43:37 +02:00
|
|
|
return r;
|
2015-07-28 00:01:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
|
2018-06-06 18:43:37 +02:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2015-07-28 00:01:10 +02:00
|
|
|
|
|
|
|
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
|
|
|
|
struct unit_times *t;
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
if (!GREEDY_REALLOC(unit_times, size, c+2))
|
|
|
|
return log_oom();
|
2015-07-28 00:01:10 +02:00
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
unit_times[c+1].has_data = false;
|
|
|
|
t = &unit_times[c];
|
2015-07-28 00:01:10 +02:00
|
|
|
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",
|
2018-06-06 18:43:37 +02:00
|
|
|
&t->deactivated) < 0)
|
|
|
|
return -EIO;
|
2015-07-28 00:01:10 +02:00
|
|
|
|
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);
|
2018-06-06 18:43:37 +02:00
|
|
|
if (!t->name)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
t->has_data = true;
|
2015-07-28 00:01:10 +02:00
|
|
|
c++;
|
|
|
|
}
|
2018-06-06 18:43:37 +02:00
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
2015-07-28 00:01:10 +02:00
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
*out = TAKE_PTR(unit_times);
|
2015-07-28 00:01:10 +02:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
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,
|
2018-03-28 13:37:27 +02:00
|
|
|
BUS_MAP_STRDUP,
|
2017-02-08 17:59:58 +01:00
|
|
|
&error,
|
2018-03-19 15:46:29 +01:00
|
|
|
NULL,
|
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,
|
2018-03-28 13:37:27 +02:00
|
|
|
BUS_MAP_STRDUP,
|
2017-02-08 17:59:58 +01:00
|
|
|
&error,
|
2018-03-19 15:46:29 +01:00
|
|
|
NULL,
|
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
|
|
|
|
2018-04-05 07:26:26 +02:00
|
|
|
*hi = TAKE_PTR(host);
|
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) {
|
2018-01-03 15:52:13 +01:00
|
|
|
log_info_errno(r, "Could not get time to reach default.target. Continuing...");
|
2017-12-05 10:20:40 +01:00
|
|
|
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 ");
|
2018-02-08 22:20:01 +01:00
|
|
|
if (t->firmware_time > 0)
|
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));
|
2018-02-08 22:20:01 +01:00
|
|
|
if (t->loader_time > 0)
|
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));
|
2018-02-08 22:20:01 +01:00
|
|
|
if (t->kernel_time > 0)
|
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));
|
2018-02-02 15:58:40 +01:00
|
|
|
if (t->kernel_time > 0)
|
|
|
|
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
|
|
|
|
2018-02-08 22:20:01 +01:00
|
|
|
if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY)
|
2017-12-05 10:20:40 +01:00
|
|
|
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));
|
2018-01-03 15:52:13 +01:00
|
|
|
else if (unit_id && activated_time == 0)
|
|
|
|
size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id);
|
|
|
|
else if (unit_id && activated_time == USEC_INFINITY)
|
|
|
|
size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id);
|
|
|
|
else if (!unit_id)
|
|
|
|
size = strpcpyf(&ptr, size, "\ncould not find default.target");
|
|
|
|
|
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;
|
2018-06-06 18:43:37 +02:00
|
|
|
_cleanup_(unit_times_freep) struct unit_times *times = NULL;
|
2013-02-06 15:21:03 +01:00
|
|
|
struct boot_times *boot;
|
2017-12-26 15:58:06 +01:00
|
|
|
int n, m = 1, y = 0, r;
|
2018-04-07 20:02:20 +02:00
|
|
|
bool use_full_bus = true;
|
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
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, &use_full_bus);
|
2017-12-26 15:58:06 +01:00
|
|
|
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
|
|
|
|
2018-04-07 20:02:20 +02:00
|
|
|
if (use_full_bus) {
|
|
|
|
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++;
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->loader_time > 0) {
|
2013-02-06 15:21:03 +01:00
|
|
|
m++;
|
|
|
|
if (width < 1000.0)
|
|
|
|
width = 1000.0;
|
|
|
|
}
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->initrd_time > 0)
|
2013-02-06 15:21:03 +01:00
|
|
|
m++;
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->kernel_time > 0)
|
2013-02-06 15:21:03 +01:00
|
|
|
m++;
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
for (u = times; u->has_data; 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);
|
2018-04-07 20:02:20 +02:00
|
|
|
if (use_full_bus)
|
|
|
|
svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
|
|
|
|
isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
|
|
|
|
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
|
|
|
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->firmware_time > 0) {
|
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++;
|
|
|
|
}
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->loader_time > 0) {
|
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++;
|
|
|
|
}
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->kernel_time > 0) {
|
2013-02-06 15:21:03 +01:00
|
|
|
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++;
|
|
|
|
}
|
2018-02-08 22:20:01 +01:00
|
|
|
if (boot->initrd_time > 0) {
|
2013-02-06 15:21:03 +01:00
|
|
|
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++;
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
for (u = times; u->has_data; u++) {
|
2013-03-08 18:58:08 +01:00
|
|
|
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
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
return 0;
|
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) {
|
2018-02-08 22:20:01 +01:00
|
|
|
if (times->time > 0)
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-08 22:20:19 +01:00
|
|
|
static bool times_in_range(const struct unit_times *times, const struct boot_times *boot) {
|
|
|
|
return times &&
|
|
|
|
times->activated > 0 && times->activated <= boot->finish_time;
|
|
|
|
}
|
|
|
|
|
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);
|
2018-02-08 22:20:19 +01:00
|
|
|
if (times_in_range(times, boot) &&
|
|
|
|
(times->activated >= service_longest
|
|
|
|
|| 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);
|
2018-02-08 22:20:19 +01:00
|
|
|
if (times_in_range(times, boot) &&
|
|
|
|
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);
|
2018-02-08 22:20:19 +01:00
|
|
|
if (!times_in_range(times, boot) ||
|
|
|
|
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;
|
|
|
|
|
2018-02-08 22:20:01 +01:00
|
|
|
if (to_print == 0)
|
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;
|
2018-06-06 18:43:37 +02:00
|
|
|
_cleanup_(unit_times_freep) struct unit_times *times = NULL;
|
|
|
|
struct unit_times *u;
|
2013-04-23 11:11:10 +02:00
|
|
|
Hashmap *h;
|
2013-11-07 22:06:29 +01:00
|
|
|
int n, r;
|
2013-04-23 11:11:10 +02:00
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
for (u = times; u->has_data; u++) {
|
|
|
|
r = hashmap_put(h, u->name, u);
|
2013-04-23 11:11:10 +02:00
|
|
|
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;
|
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) 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
|
|
|
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;
|
2018-06-06 18:43:37 +02:00
|
|
|
_cleanup_(unit_times_freep) struct unit_times *times = NULL;
|
|
|
|
struct unit_times *u;
|
2017-12-26 15:58:06 +01:00
|
|
|
int n, r;
|
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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);
|
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
for (u = times; u->has_data; u++) {
|
2013-03-08 18:58:08 +01:00
|
|
|
char ts[FORMAT_TIMESPAN_MAX];
|
|
|
|
|
2018-06-06 18:43:37 +02:00
|
|
|
if (u->time > 0)
|
|
|
|
printf("%16s %s\n", format_timespan(ts, sizeof(ts), u->time, USEC_PER_MSEC), u->name);
|
2013-02-06 15:21:03 +01:00
|
|
|
}
|
2013-03-08 18:58:08 +01:00
|
|
|
|
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;
|
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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
|
|
|
|
2018-05-09 09:35:52 +02:00
|
|
|
static int dump_fallback(sd_bus *bus) {
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
|
|
|
const char *text = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(bus);
|
|
|
|
|
|
|
|
r = sd_bus_call_method(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"Dump",
|
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "s", &text);
|
|
|
|
if (r < 0)
|
|
|
|
return bus_log_parse_error(r);
|
|
|
|
|
|
|
|
fputs(text, stdout);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
2018-05-09 09:35:52 +02:00
|
|
|
int fd = -1;
|
2013-07-26 16:34:52 +02:00
|
|
|
int r;
|
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
2013-07-26 16:59:55 +02:00
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2018-05-09 09:35:52 +02:00
|
|
|
if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
|
|
|
|
return dump_fallback(bus);
|
|
|
|
|
2013-10-31 23:08:16 +01:00
|
|
|
r = sd_bus_call_method(
|
|
|
|
bus,
|
2017-12-26 16:01:43 +01:00
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
2018-05-09 09:35:52 +02:00
|
|
|
"DumpByFileDescriptor",
|
2017-12-26 16:01:43 +01:00
|
|
|
&error,
|
|
|
|
&reply,
|
|
|
|
NULL);
|
2018-05-09 09:35:52 +02:00
|
|
|
if (r < 0) {
|
|
|
|
/* fall back to Dump if DumpByFileDescriptor is not supported */
|
|
|
|
if (!IN_SET(r, -EACCES, -EBADR))
|
|
|
|
return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s", bus_error_message(&error, r));
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2018-05-09 09:35:52 +02:00
|
|
|
return dump_fallback(bus);
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_read(reply, "h", &fd);
|
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
|
|
|
|
2018-05-09 09:35:52 +02:00
|
|
|
fflush(stdout);
|
|
|
|
return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0);
|
2013-07-26 16:34:52 +02:00
|
|
|
}
|
|
|
|
|
2018-04-26 13:49:50 +02:00
|
|
|
static int cat_config(int argc, char *argv[], void *userdata) {
|
|
|
|
char **arg;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
(void) pager_open(arg_no_pager, false);
|
|
|
|
|
|
|
|
STRV_FOREACH(arg, argv + 1) {
|
2018-04-27 12:50:07 +02:00
|
|
|
const char *t = NULL;
|
|
|
|
|
2018-04-26 13:49:50 +02:00
|
|
|
if (arg != argv + 1)
|
2018-05-10 20:28:33 +02:00
|
|
|
print_separator();
|
2018-04-26 13:49:50 +02:00
|
|
|
|
|
|
|
if (path_is_absolute(*arg)) {
|
2018-04-27 12:50:07 +02:00
|
|
|
const char *dir;
|
|
|
|
|
|
|
|
NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) {
|
|
|
|
t = path_startswith(*arg, dir);
|
|
|
|
if (t)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!t) {
|
|
|
|
log_error("Path %s does not start with any known prefix.", *arg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
t = *arg;
|
|
|
|
|
|
|
|
r = conf_files_cat(arg_root, t);
|
2018-04-26 13:49:50 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-01-27 13:51:32 +01:00
|
|
|
static int get_or_set_log_level(int argc, char *argv[], void *userdata) {
|
|
|
|
return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2017-12-26 15:58:06 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-01-27 13:51:32 +01:00
|
|
|
static int get_or_set_log_target(int argc, char *argv[], void *userdata) {
|
|
|
|
return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata);
|
|
|
|
}
|
|
|
|
|
2018-02-09 12:27:27 +01:00
|
|
|
static int dump_unit_paths(int argc, char *argv[], void *userdata) {
|
tree-wide: drop redundant _cleanup_ macros (#8810)
This drops a good number of type-specific _cleanup_ macros, and patches
all users to just use the generic ones.
In most recent code we abstained from defining type-specific macros, and
this basically removes all those added already, with the exception of
the really low-level ones.
Having explicit macros for this is not too useful, as the expression
without the extra macro is generally just 2ch wider. We should generally
emphesize generic code, unless there are really good reasons for
specific code, hence let's follow this in this case too.
Note that _cleanup_free_ and similar really low-level, libc'ish, Linux
API'ish macros continue to be defined, only the really high-level OO
ones are dropped. From now on this should really be the rule: for really
low-level stuff, such as memory allocation, fd handling and so one, go
ahead and define explicit per-type macros, but for high-level, specific
program code, just use the generic _cleanup_() macro directly, in order
to keep things simple and as readable as possible for the uninitiated.
Note that before this patch some of the APIs (notable libudev ones) were
already used with the high-level macros at some places and with the
generic _cleanup_ macro at others. With this patch we hence unify on the
latter.
2018-04-25 12:31:45 +02:00
|
|
|
_cleanup_(lookup_paths_free) LookupPaths paths = {};
|
2018-02-09 12:27:27 +01:00
|
|
|
int r;
|
|
|
|
char **p;
|
|
|
|
|
|
|
|
r = lookup_paths_init(&paths, arg_scope, 0, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "lookup_paths_init() failed: %m");
|
|
|
|
|
|
|
|
STRV_FOREACH(p, paths.search_path)
|
|
|
|
puts(*p);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) pager_open(arg_no_pager, false);
|
2016-11-02 16:58:18 +01:00
|
|
|
|
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
|
2018-01-01 05:24:41 +01:00
|
|
|
static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
|
2016-11-03 19:33:49 +01:00
|
|
|
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) {
|
2018-03-01 01:31:32 +01:00
|
|
|
ret = log_error_errno(r, "Failed to format calendar specification '%s': %m", *p);
|
2017-11-17 10:33:22 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-01-11 10:44:38 +01:00
|
|
|
static int service_watchdogs(int argc, char *argv[], void *userdata) {
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
|
|
|
int b, r;
|
|
|
|
|
2018-01-27 13:51:32 +01:00
|
|
|
assert(IN_SET(argc, 1, 2));
|
2018-01-11 10:44:38 +01:00
|
|
|
assert(argv);
|
|
|
|
|
2018-05-09 07:56:40 +02:00
|
|
|
r = acquire_bus(&bus, NULL);
|
2018-01-27 13:51:32 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to create bus connection: %m");
|
|
|
|
|
|
|
|
/* get ServiceWatchdogs */
|
|
|
|
if (argc == 1) {
|
|
|
|
r = sd_bus_get_property_trivial(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"ServiceWatchdogs",
|
|
|
|
&error,
|
|
|
|
'b',
|
|
|
|
&b);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
printf("%s\n", yes_no(!!b));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set ServiceWatchdogs */
|
2018-01-11 10:44:38 +01:00
|
|
|
b = parse_boolean(argv[1]);
|
|
|
|
if (b < 0) {
|
|
|
|
log_error("Failed to parse service-watchdogs argument.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_set_property(
|
|
|
|
bus,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"ServiceWatchdogs",
|
|
|
|
&error,
|
|
|
|
"b",
|
|
|
|
b);
|
|
|
|
if (r < 0)
|
2018-01-27 13:51:32 +01:00
|
|
|
return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
|
2018-01-11 10:44:38 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-26 15:58:06 +01:00
|
|
|
static int do_verify(int argc, char *argv[], void *userdata) {
|
2018-02-08 11:53:00 +01:00
|
|
|
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
|
2017-12-26 15:58:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int help(int argc, char *argv[], void *userdata) {
|
2013-07-26 16:34:52 +02:00
|
|
|
|
2018-03-14 09:48:29 +01:00
|
|
|
(void) 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"
|
2018-02-08 11:53:00 +01:00
|
|
|
" --global Operate on global user configuration\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" -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"
|
2017-12-26 16:01:49 +01:00
|
|
|
" critical-chain [UNIT...] Print a tree of the time critical chain of units\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" plot Output SVG graphic showing service initialization\n"
|
2017-12-26 16:01:49 +01:00
|
|
|
" dot [UNIT...] Output dependency graph in man:dot(1) format\n"
|
2018-01-27 13:51:32 +01:00
|
|
|
" log-level [LEVEL] Get/set logging threshold for manager\n"
|
|
|
|
" log-target [TARGET] Get/set logging target for manager\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" dump Output state serialization of service manager\n"
|
2018-04-26 13:49:50 +02:00
|
|
|
" cat-config Show configuration file and drop-ins\n"
|
2018-02-09 12:27:27 +01:00
|
|
|
" unit-paths List load directories for units\n"
|
2016-11-02 16:58:18 +01:00
|
|
|
" 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"
|
2018-01-27 13:51:32 +01:00
|
|
|
" service-watchdogs [BOOL] Get/set service watchdog state\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,
|
2018-04-27 08:55:16 +02:00
|
|
|
ARG_ROOT,
|
2013-04-06 15:40:56 +02:00
|
|
|
ARG_SYSTEM,
|
2018-02-08 11:53:00 +01:00
|
|
|
ARG_USER,
|
|
|
|
ARG_GLOBAL,
|
2013-04-06 15:40:56 +02:00
|
|
|
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 },
|
2018-04-27 08:55:16 +02:00
|
|
|
{ "root", required_argument, NULL, ARG_ROOT },
|
2013-07-26 16:34:52 +02:00
|
|
|
{ "system", no_argument, NULL, ARG_SYSTEM },
|
2018-02-08 11:53:00 +01:00
|
|
|
{ "user", no_argument, NULL, ARG_USER },
|
|
|
|
{ "global", no_argument, NULL, ARG_GLOBAL },
|
2013-07-26 16:34:52 +02:00
|
|
|
{ "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
|
|
|
|
2018-04-27 08:55:16 +02:00
|
|
|
case ARG_ROOT:
|
|
|
|
arg_root = optarg;
|
|
|
|
break;
|
|
|
|
|
2018-02-08 11:53:00 +01:00
|
|
|
case ARG_SYSTEM:
|
|
|
|
arg_scope = UNIT_FILE_SYSTEM;
|
|
|
|
break;
|
|
|
|
|
2013-03-08 18:58:08 +01:00
|
|
|
case ARG_USER:
|
2018-02-08 11:53:00 +01:00
|
|
|
arg_scope = UNIT_FILE_USER;
|
2013-03-08 18:58:08 +01:00
|
|
|
break;
|
|
|
|
|
2018-02-08 11:53:00 +01:00
|
|
|
case ARG_GLOBAL:
|
|
|
|
arg_scope = UNIT_FILE_GLOBAL;
|
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;
|
|
|
|
}
|
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_man = r;
|
2014-08-20 00:15:05 +02:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2018-06-11 16:02:03 +02:00
|
|
|
arg_generators = r;
|
2017-09-16 11:29:34 +02:00
|
|
|
} 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
|
|
|
|
2018-02-09 12:27:27 +01:00
|
|
|
if (arg_scope == UNIT_FILE_GLOBAL &&
|
|
|
|
!STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) {
|
|
|
|
log_error("Option --global only makes sense with verbs dot, unit-paths, verify.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-27 08:55:16 +02:00
|
|
|
if (arg_root && !streq_ptr(argv[optind], "cat-config")) {
|
|
|
|
log_error("Option --root is only supported for cat-config right now.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
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[] = {
|
2018-01-11 10:44:38 +01:00
|
|
|
{ "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 },
|
2018-01-27 13:51:32 +01:00
|
|
|
{ "log-level", VERB_ANY, 2, 0, get_or_set_log_level },
|
|
|
|
{ "log-target", VERB_ANY, 2, 0, get_or_set_log_target },
|
|
|
|
/* The following four verbs are deprecated aliases */
|
2018-01-11 10:44:38 +01:00
|
|
|
{ "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 },
|
2018-04-26 13:49:50 +02:00
|
|
|
{ "cat-config", 2, VERB_ANY, 0, cat_config },
|
2018-02-09 12:27:27 +01:00
|
|
|
{ "unit-paths", 1, 1, 0, dump_unit_paths },
|
2018-01-11 10:44:38 +01:00
|
|
|
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
|
|
|
|
{ "verify", 2, VERB_ANY, 0, do_verify },
|
|
|
|
{ "calendar", 2, VERB_ANY, 0, test_calendar },
|
2018-01-27 13:51:32 +01:00
|
|
|
{ "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs },
|
2017-12-26 15:58:06 +01:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
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
|
|
|
}
|