#include <stdlib.h>
#include <getopt.h>
#include <locale.h>
-#include <sys/utsname.h>
-#include <fnmatch.h>
#include "sd-bus.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "install.h"
#include "log.h"
#include "build.h"
#include "util.h"
#include "strxcpyx.h"
-#include "fileio.h"
#include "strv.h"
#include "unit-name.h"
#include "special.h"
#include "hashmap.h"
#include "pager.h"
+#include "analyze-verify.h"
+#include "terminal-util.h"
#define SCALE_X (0.1 / 1000.0) /* pixels per us */
-#define SCALE_Y 20.0
+#define SCALE_Y (20.0)
#define compare(a, b) (((a) > (b))? 1 : (((b) > (a))? -1 : 0))
static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
static char *arg_host = NULL;
static bool arg_user = false;
+static bool arg_man = true;
struct boot_times {
usec_t firmware_time;
usec_t generators_finish_time;
usec_t unitsload_start_time;
usec_t unitsload_finish_time;
+
+ /*
+ * 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;
};
struct unit_times {
usec_t time;
};
+struct host_info {
+ char *hostname;
+ char *kernel_name;
+ char *kernel_release;
+ char *kernel_version;
+ char *os_pretty_name;
+ char *virtualization;
+ char *architecture;
+};
+
static void pager_open_if_enabled(void) {
if (arg_no_pager)
((struct unit_times *)b)->activating);
}
-static int get_os_name(char **_n) {
- char *n = NULL;
- int r;
-
- r = parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &n, NULL);
- if (r < 0)
- return r;
-
- if (!n)
- return -ENOENT;
-
- *_n = n;
- return 0;
-}
-
static void free_unit_times(struct unit_times *t, unsigned n) {
struct unit_times *p;
free(t);
}
+static void subtract_timestamp(usec_t *a, usec_t b) {
+ assert(a);
+
+ if (*a > 0) {
+ assert(*a >= b);
+ *a -= b;
+ }
+}
+
+static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
+ static struct boot_times times;
+ static bool cached = false;
+
+ if (cached)
+ goto finish;
+
+ assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+
+ 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",
+ ×.finish_time) < 0 ||
+ 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 ||
+ 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",
+ ×.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)
+ return -EIO;
+
+ if (times.finish_time <= 0) {
+ log_error("Bootup is not yet finished. Please try again later.");
+ return -EINPROGRESS;
+ }
+
+ if (arg_user) {
+ /*
+ * User-instance-specific timestamps processing
+ * (see comment to reverse_offset in struct boot_times).
+ */
+ times.reverse_offset = times.userspace_time;
+
+ times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = times.userspace_time = 0;
+ subtract_timestamp(×.finish_time, times.reverse_offset);
+
+ subtract_timestamp(×.security_start_time, times.reverse_offset);
+ subtract_timestamp(×.security_finish_time, times.reverse_offset);
+
+ subtract_timestamp(×.generators_start_time, times.reverse_offset);
+ subtract_timestamp(×.generators_finish_time, times.reverse_offset);
+
+ subtract_timestamp(×.unitsload_start_time, times.reverse_offset);
+ subtract_timestamp(×.unitsload_finish_time, times.reverse_offset);
+ } else {
+ if (times.initrd_time)
+ times.kernel_done_time = times.initrd_time;
+ else
+ times.kernel_done_time = times.userspace_time;
+ }
+
+ cached = true;
+
+finish:
+ *bt = ×
+ return 0;
+}
+
+static void free_host_info(struct host_info *hi) {
+ 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);
+}
+
static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
int r, c = 0;
+ struct boot_times *boot_times = NULL;
struct unit_times *unit_times = NULL;
size_t size = 0;
UnitInfo u;
+ r = acquire_boot_times(bus, &boot_times);
+ if (r < 0)
+ goto fail;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
goto fail;
}
+ 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);
+
if (t->activated >= t->activating)
t->time = t->activated - t->activating;
else if (t->deactivated >= t->activating)
return c;
fail:
- free_unit_times(unit_times, (unsigned) c);
+ if (unit_times)
+ free_unit_times(unit_times, (unsigned) c);
return r;
}
-static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) {
- static struct boot_times times;
- static bool cached = false;
-
- if (cached)
- goto finish;
-
- assert_cc(sizeof(usec_t) == sizeof(uint64_t));
+static int acquire_host_info(sd_bus *bus, struct host_info **hi) {
+ int r;
+ struct host_info *host;
+
+ static const struct bus_properties_map hostname_map[] = {
+ { "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) },
+ { "OperatingSystemPrettyName", "s", NULL, offsetof(struct host_info, os_pretty_name) },
+ {}
+ };
- 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",
- ×.finish_time) < 0 ||
- 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 ||
- 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",
- ×.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)
- return -EIO;
+ static const struct bus_properties_map manager_map[] = {
+ { "Virtualization", "s", NULL, offsetof(struct host_info, virtualization) },
+ { "Architecture", "s", NULL, offsetof(struct host_info, architecture) },
+ {}
+ };
- if (times.finish_time <= 0) {
- log_error("Bootup is not yet finished. Please try again later.");
- return -EINPROGRESS;
- }
+ host = new0(struct host_info, 1);
+ if (!host)
+ return log_oom();
- if (times.initrd_time)
- times.kernel_done_time = times.initrd_time;
- else
- times.kernel_done_time = times.userspace_time;
+ r = bus_map_all_properties(bus,
+ "org.freedesktop.hostname1",
+ "/org/freedesktop/hostname1",
+ hostname_map,
+ host);
+ if (r < 0)
+ goto fail;
- cached = true;
+ r = bus_map_all_properties(bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ manager_map,
+ host);
+ if (r < 0)
+ goto fail;
-finish:
- *bt = ×
+ *hi = host;
return 0;
+fail:
+ free_host_info(host);
+ return r;
}
static int pretty_boot_time(sd_bus *bus, char **_buf) {
size = strpcpyf(&ptr, size, "%s (initrd) + ", format_timespan(ts, sizeof(ts), t->userspace_time - t->initrd_time, USEC_PER_MSEC));
size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
- if (t->kernel_time > 0)
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
- else
- strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC));
+ strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
ptr = strdup(buf);
if (!ptr)
static int analyze_plot(sd_bus *bus) {
struct unit_times *times;
struct boot_times *boot;
- struct utsname name;
+ struct host_info *host = NULL;
int n, m = 1, y=0;
double width;
- _cleanup_free_ char *pretty_times = NULL, *osname = NULL;
+ _cleanup_free_ char *pretty_times = NULL;
struct unit_times *u;
n = acquire_boot_times(bus, &boot);
if (n < 0)
return n;
- get_os_name(&osname);
- assert_se(uname(&name) >= 0);
+ n = acquire_host_info(bus, &host);
+ if (n < 0)
+ return n;
n = acquire_time_data(bus, ×);
if (n <= 0)
- return n;
+ goto out;
qsort(times, n, sizeof(struct unit_times), compare_unit_start);
if (u->activating < boot->userspace_time ||
u->activating > boot->finish_time) {
- free(u->name);
- u->name = NULL;
+ u->name = mfree(u->name);
continue;
}
svg("<rect class=\"background\" width=\"100%%\" height=\"100%%\" />\n");
svg("<text x=\"20\" y=\"50\">%s</text>", pretty_times);
- svg("<text x=\"20\" y=\"30\">%s %s (%s %s) %s</text>",
- isempty(osname) ? "Linux" : osname,
- name.nodename, name.release, name.version, name.machine);
+ svg("<text x=\"20\" y=\"30\">%s %s (%s %s %s) %s %s</text>",
+ isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name,
+ isempty(host->hostname) ? "" : host->hostname,
+ isempty(host->kernel_name) ? "" : host->kernel_name,
+ isempty(host->kernel_release) ? "" : host->kernel_release,
+ isempty(host->kernel_version) ? "" : host->kernel_version,
+ isempty(host->architecture) ? "" : host->architecture,
+ isempty(host->virtualization) ? "" : host->virtualization);
svg("<g transform=\"translate(%.3f,100)\">\n", 20.0 + (SCALE_X * boot->firmware_time));
- svg_graph_box(m, -boot->firmware_time, boot->finish_time);
+ svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time);
if (boot->firmware_time) {
svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y);
y++;
}
+ svg("</g>\n");
+
/* Legend */
+ svg("<g transform=\"translate(20,100)\">\n");
y++;
svg_bar("activating", 0, 300000, y);
svg_text(true, 400000, y, "Activating");
free_unit_times(times, (unsigned) n);
- return 0;
+ n = 0;
+out:
+ free_host_info(host);
+ return n;
}
static int list_dependencies_print(const char *name, unsigned int level, unsigned int branches,
char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX];
for (i = level; i != 0; i--)
- printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
+ printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
STRV_FOREACH(c, deps) {
times = hashmap_get(unit_times_hashmap, *c);
- if (times && times->activated
- && times->activated <= boot->finish_time
- && (service_longest - times->activated) <= arg_fuzz) {
+ if (times && times->activated && times->activated <= boot->finish_time && (service_longest - times->activated) <= arg_fuzz)
to_print++;
- }
}
if (!to_print)
char ts[FORMAT_TIMESPAN_MAX];
struct unit_times *times;
int r;
- const char *path, *id;
+ const char *id;
+ _cleanup_free_ char *path = NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
struct boot_times *boot;
if (n <= 0)
return n;
- h = hashmap_new(string_hash_func, string_compare_func);
+ h = hashmap_new(&string_hash_ops);
if (!h)
return -ENOMEM;
_cleanup_strv_free_ char **units = NULL;
char **unit;
int r;
+ bool match_patterns;
assert(u);
assert(prop);
assert(color);
+ match_patterns = strv_fnmatch(patterns, u->id, 0);
+
+ if (!strv_isempty(arg_dot_from_patterns) &&
+ !match_patterns &&
+ !strv_fnmatch(arg_dot_from_patterns, u->id, 0))
+ return 0;
+
r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units);
if (r < 0)
return r;
STRV_FOREACH(unit, units) {
- char **p;
- bool match_found;
+ bool match_patterns2;
- if (!strv_isempty(arg_dot_from_patterns)) {
- match_found = false;
+ match_patterns2 = strv_fnmatch(patterns, *unit, 0);
- STRV_FOREACH(p, arg_dot_from_patterns)
- if (fnmatch(*p, u->id, 0) == 0) {
- match_found = true;
- break;
- }
-
- if (!match_found)
- continue;
- }
-
- if (!strv_isempty(arg_dot_to_patterns)) {
- match_found = false;
-
- STRV_FOREACH(p, arg_dot_to_patterns)
- if (fnmatch(*p, *unit, 0) == 0) {
- match_found = true;
- break;
- }
-
- if (!match_found)
- continue;
- }
-
- if (!strv_isempty(patterns)) {
- match_found = false;
+ if (!strv_isempty(arg_dot_to_patterns) &&
+ !match_patterns2 &&
+ !strv_fnmatch(arg_dot_to_patterns, *unit, 0))
+ continue;
- STRV_FOREACH(p, patterns)
- if (fnmatch(*p, u->id, 0) == 0 || fnmatch(*p, *unit, 0) == 0) {
- match_found = true;
- break;
- }
- if (!match_found)
- continue;
- }
+ if (!strv_isempty(patterns) && !match_patterns && !match_patterns2)
+ continue;
printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color);
}
return 0;
}
+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) {
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _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;
+}
+
static int dot(sd_bus *bus, char* patterns[]) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_strv_free_ char **expanded_patterns = NULL;
int r;
UnitInfo u;
+ r = expand_patterns(bus, patterns, &expanded_patterns);
+ if (r < 0)
+ return r;
+
r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
- r = graph_one(bus, &u, patterns);
+ r = graph_one(bus, &u, expanded_patterns);
if (r < 0)
return r;
}
return 0;
}
-static int help(void) {
+static void help(void) {
pager_open_if_enabled();
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Process systemd profiling information.\n\n"
+ "Profile systemd, show unit dependencies, check unit files.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
- " --system Connect to system manager\n"
- " --user Connect to user manager\n"
+ " --system Operate on system systemd instance\n"
+ " --user Operate on user systemd instance\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
- " --order When generating a dependency graph, show only order\n"
- " --require When generating a dependency graph, show only requirement\n"
- " --from-pattern=GLOB, --to-pattern=GLOB\n"
- " When generating a dependency graph, filter only origins\n"
- " or destinations, respectively\n"
- " --fuzz=TIMESPAN When printing the tree of the critical chain, print also\n"
- " services, which finished TIMESPAN earlier, than the\n"
- " latest in the branch. The unit of TIMESPAN is seconds\n"
- " unless specified with a different unit, i.e. 50ms\n\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"
"Commands:\n"
- " time Print time spent in the kernel before reaching userspace\n"
+ " time Print time spent in the kernel\n"
" blame Print list of running units ordered by time to init\n"
" critical-chain Print a tree of the time critical chain of units\n"
" plot Output SVG graphic showing service initialization\n"
" dot Output dependency graph in dot(1) format\n"
" set-log-level LEVEL Set logging threshold for systemd\n"
- " dump Output state serialization of service manager\n",
- program_invocation_short_name);
+ " dump Output state serialization of service manager\n"
+ " verify FILE... Check unit files for correctness\n"
+ , program_invocation_short_name);
/* When updating this list, including descriptions, apply
- * changes to shell-completion/bash/systemd and
- * shell-completion/systemd-zsh-completion.zsh too. */
-
- return 0;
+ * changes to shell-completion/bash/systemd-analyze and
+ * shell-completion/zsh/_systemd-analyze too. */
}
static int parse_argv(int argc, char *argv[]) {
ARG_DOT_FROM_PATTERN,
ARG_DOT_TO_PATTERN,
ARG_FUZZ,
- ARG_NO_PAGER
+ ARG_NO_PAGER,
+ ARG_MAN,
};
static const struct option options[] = {
{ "to-pattern", required_argument, NULL, ARG_DOT_TO_PATTERN },
{ "fuzz", required_argument, NULL, ARG_FUZZ },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
+ { "man", optional_argument, NULL, ARG_MAN },
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{}
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) {
-
+ while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0)
switch (c) {
case 'h':
- return help();
+ help();
+ return 0;
case ARG_VERSION:
puts(PACKAGE_STRING);
break;
case 'M':
- arg_transport = BUS_TRANSPORT_CONTAINER;
+ arg_transport = BUS_TRANSPORT_MACHINE;
arg_host = optarg;
break;
+ case ARG_MAN:
+ if (optarg) {
+ r = parse_boolean(optarg);
+ if (r < 0) {
+ log_error("Failed to parse --man= argument.");
+ return -EINVAL;
+ }
+
+ arg_man = !!r;
+ } else
+ arg_man = true;
+
+ break;
+
case '?':
return -EINVAL;
default:
- assert_not_reached("Unhandled option");
+ assert_not_reached("Unhandled option code.");
}
- }
- return 1;
+ return 1; /* work to do */
}
int main(int argc, char *argv[]) {
- _cleanup_bus_unref_ sd_bus *bus = NULL;
int r;
setlocale(LC_ALL, "");
if (r <= 0)
goto finish;
- r = bus_open_transport(arg_transport, arg_host, arg_user, &bus);
- if (r < 0) {
- log_error("Failed to create bus connection: %s", strerror(-r));
- goto finish;
- }
+ if (streq_ptr(argv[optind], "verify"))
+ r = verify_units(argv+optind+1,
+ arg_user ? MANAGER_USER : MANAGER_SYSTEM,
+ arg_man);
+ else {
+ _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL;
+
+ r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create bus connection: %m");
+ goto finish;
+ }
- if (!argv[optind] || streq(argv[optind], "time"))
- r = analyze_time(bus);
- else if (streq(argv[optind], "blame"))
- r = analyze_blame(bus);
- else if (streq(argv[optind], "critical-chain"))
- r = analyze_critical_chain(bus, argv+optind+1);
- else if (streq(argv[optind], "plot"))
- r = analyze_plot(bus);
- else if (streq(argv[optind], "dot"))
- r = dot(bus, argv+optind+1);
- else if (streq(argv[optind], "dump"))
- r = dump(bus, argv+optind+1);
- else if (streq(argv[optind], "set-log-level"))
- r = set_log_level(bus, argv+optind+1);
- else
- log_error("Unknown operation '%s'.", argv[optind]);
+ if (!argv[optind] || streq(argv[optind], "time"))
+ r = analyze_time(bus);
+ else if (streq(argv[optind], "blame"))
+ r = analyze_blame(bus);
+ else if (streq(argv[optind], "critical-chain"))
+ r = analyze_critical_chain(bus, argv+optind+1);
+ else if (streq(argv[optind], "plot"))
+ r = analyze_plot(bus);
+ else if (streq(argv[optind], "dot"))
+ r = dot(bus, argv+optind+1);
+ else if (streq(argv[optind], "dump"))
+ r = dump(bus, argv+optind+1);
+ else if (streq(argv[optind], "set-log-level"))
+ r = set_log_level(bus, argv+optind+1);
+ else
+ log_error("Unknown operation '%s'.", argv[optind]);
+ }
finish:
pager_close();