Subscribe();
Unsubscribe();
Dump(out s output);
+ DumpPatterns(in as patterns,
+ out s output);
DumpByFileDescriptor(out h fd);
Reload();
@org.freedesktop.DBus.Method.NoReply("true")
<variablelist class="dbus-method" generated="True" extra-ref="Dump()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="DumpPatterns()"/>
+
<variablelist class="dbus-method" generated="True" extra-ref="DumpByFileDescriptor()"/>
<variablelist class="dbus-method" generated="True" extra-ref="Reload()"/>
string guaranteed, and new fields may be added any time, and old fields removed. The general structure
may be rearranged drastically between releases. This is exposed by
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
- <command>dump</command> command. The <function>DumpByFileDescriptor()</function> method is identical to
+ <command>dump</command> command. Similarly, <function>DumpPatterns()</function> returns the internal
+ state of units whose names match the glob expressions specified in the <varname>patterns</varname>
+ argument. The <function>DumpByFileDescriptor()</function> method is identical to
<function>Dump()</function> but returns the data serialized into a file descriptor (the client should
read the text data from it until hitting EOF). Given the size limits on D-Bus messages and the possibly
large size of the returned string, <function>DumpByFileDescriptor()</function> is usually the
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">dump</arg>
+ <arg choice="opt" rep="repeat"><replaceable>PATTERN</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
</refsect2>
<refsect2>
- <title><command>systemd-analyze dump</command></title>
+ <title><command>systemd-analyze dump [<replaceable>pattern</replaceable>…]</command></title>
- <para>This command outputs a (usually very long) human-readable serialization of the complete server
- state. Its format is subject to change without notice and should not be parsed by applications.</para>
+ <para>Without any parameter, this command outputs a (usually very long) human-readable serialization of
+ the complete service manager state. Optional glob pattern may be specified, causing the output to be
+ limited to units whose names match one of the patterns. The output format is subject to change without
+ notice and should not be parsed by applications.</para>
<example>
<title>Show the internal state of user manager</title>
)
local -A VERBS=(
- [STANDALONE]='time blame plot dump unit-paths exit-status calendar timestamp timespan'
+ [STANDALONE]='time blame plot unit-paths exit-status calendar timestamp timespan'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
+ [DUMP]='dump'
[VERIFY]='verify'
[SECCOMP_FILTER]='syscall-filter'
[CAT_CONFIG]='cat-config'
comps='--help --version --system --user --global --from-pattern --to-pattern --order --require'
fi
+ elif __contains_word "$verb" ${VERBS[DUMP]}; then
+ if [[ $cur = -* ]]; then
+ comps='--help --version --system --user --no-pager'
+ else
+ comps=$( __get_units_all )
+ fi
+
elif __contains_word "$verb" ${VERBS[SECCOMP_FILTER]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager'
return 0;
}
+static int dump_patterns(sd_bus *bus, char **patterns) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
+ _cleanup_strv_free_ char **mangled = NULL;
+ const char *text;
+ int r;
+
+ r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "DumpPatterns");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ STRV_FOREACH(pattern, patterns) {
+ char *t;
+
+ r = unit_name_mangle_with_suffix(*pattern, NULL, UNIT_NAME_MANGLE_GLOB, ".service", &t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to mangle name: %m");
+
+ r = strv_consume(&mangled, t);
+ if (r < 0)
+ return log_oom();
+ }
+
+ r = sd_bus_message_append_strv(m, mangled);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Failed to issue method call DumpPatterns: %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 r;
+}
+
int verb_dump(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
pager_open(arg_pager_flags);
+ if (argc > 1)
+ return dump_patterns(bus, strv_skip(argv, 1));
+
if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
return dump_fallback(bus);
" plot Output SVG graphic showing service\n"
" initialization\n"
" dot [UNIT...] Output dependency graph in %s format\n"
- " dump Output state serialization of service\n"
+ " dump [PATTERN...] Output state serialization of service\n"
" manager\n"
" cat-config Show configuration file and drop-ins\n"
" unit-files List files and symlinks for units\n"
{ "get-log-target", VERB_ANY, 1, 0, verb_log_control },
{ "service-watchdogs", VERB_ANY, 2, 0, verb_service_watchdogs },
/* ↑ … until here ↑ */
- { "dump", VERB_ANY, 1, 0, verb_dump },
+ { "dump", VERB_ANY, VERB_ANY, 0, verb_dump },
{ "cat-config", 2, VERB_ANY, 0, verb_cat_config },
{ "unit-files", VERB_ANY, VERB_ANY, 0, verb_unit_files },
{ "unit-paths", 1, 1, 0, verb_unit_paths },
return sd_bus_reply_method_return(message, NULL);
}
-static int dump_impl(sd_bus_message *message, void *userdata, sd_bus_error *error, int (*reply)(sd_bus_message *, char *)) {
+static int dump_impl(
+ sd_bus_message *message,
+ void *userdata,
+ sd_bus_error *error,
+ char **patterns,
+ int (*reply)(sd_bus_message *, char *)) {
+
_cleanup_free_ char *dump = NULL;
Manager *m = ASSERT_PTR(userdata);
int r;
if (r < 0)
return r;
- r = manager_get_dump_string(m, &dump);
+ r = manager_get_dump_string(m, patterns, &dump);
if (r < 0)
return r;
}
static int method_dump(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return dump_impl(message, userdata, error, reply_dump);
+ return dump_impl(message, userdata, error, NULL, reply_dump);
}
static int reply_dump_by_fd(sd_bus_message *message, char *dump) {
}
static int method_dump_by_fd(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return dump_impl(message, userdata, error, reply_dump_by_fd);
+ return dump_impl(message, userdata, error, NULL, reply_dump_by_fd);
+}
+
+static int method_dump_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return dump_impl(message, userdata, error, patterns, reply_dump);
}
static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bus_error *error) {
SD_BUS_RESULT("s", output),
method_dump,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("DumpPatterns",
+ SD_BUS_ARGS("as", patterns),
+ SD_BUS_RESULT("s", output),
+ method_dump_patterns,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("DumpByFileDescriptor",
SD_BUS_NO_ARGS,
SD_BUS_RESULT("h", fd),
u->id, u->description, cmdline);
continue; /* ask again */
case 'j':
- manager_dump_jobs(u->manager, stdout, " ");
+ manager_dump_jobs(u->manager, stdout, /* patterns= */ NULL, " ");
continue; /* ask again */
case 'n':
/* 'n' was removed in favor of 'f'. */
assert_se(g);
unit_dump(u, g, "");
- manager_dump(m, g, ">>>");
+ manager_dump(m, g, /* patterns= */ NULL, ">>>");
return 0;
}
#include "manager-dump.h"
#include "unit-serialize.h"
-void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) {
+void manager_dump_jobs(Manager *s, FILE *f, char **patterns, const char *prefix) {
Job *j;
assert(s);
assert(f);
- HASHMAP_FOREACH(j, s->jobs)
+ HASHMAP_FOREACH(j, s->jobs) {
+
+ if (!strv_fnmatch_or_empty(patterns, j->unit->id, FNM_NOESCAPE))
+ continue;
+
job_dump(j, f, prefix);
+ }
}
-void manager_dump_units(Manager *s, FILE *f, const char *prefix) {
+void manager_dump_units(Manager *s, FILE *f, char **patterns, const char *prefix) {
Unit *u;
const char *t;
assert(s);
assert(f);
- HASHMAP_FOREACH_KEY(u, t, s->units)
- if (u->id == t)
- unit_dump(u, f, prefix);
+ HASHMAP_FOREACH_KEY(u, t, s->units) {
+ if (u->id != t)
+ continue;
+
+ if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
+ continue;
+
+ unit_dump(u, f, prefix);
+ }
}
-void manager_dump(Manager *m, FILE *f, const char *prefix) {
- assert(m);
- assert(f);
+static void manager_dump_header(Manager *m, FILE *f, const char *prefix) {
/* NB: this is a debug interface for developers. It's not supposed to be machine readable or be
* stable between versions. We take the liberty to restructure it entirely between versions and
timestamp_is_set(t->realtime) ? FORMAT_TIMESTAMP(t->realtime) :
FORMAT_TIMESPAN(t->monotonic, 1));
}
+}
+
+void manager_dump(Manager *m, FILE *f, char **patterns, const char *prefix) {
+ assert(m);
+ assert(f);
+
+ /* If no pattern is provided, dump the full manager state including the manager version, features and
+ * so on. Otherwise limit the dump to the units/jobs matching the specified patterns. */
+ if (!patterns)
+ manager_dump_header(m, f, prefix);
- manager_dump_units(m, f, prefix);
- manager_dump_jobs(m, f, prefix);
+ manager_dump_units(m, f, patterns, prefix);
+ manager_dump_jobs(m, f, patterns, prefix);
}
-int manager_get_dump_string(Manager *m, char **ret) {
+int manager_get_dump_string(Manager *m, char **patterns, char **ret) {
_cleanup_free_ char *dump = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t size;
if (!f)
return -errno;
- manager_dump(m, f, NULL);
+ manager_dump(m, f, patterns, NULL);
r = fflush_and_check(f);
if (r < 0)
assert(m);
printf("-> By units:\n");
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("-> By jobs:\n");
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
}
#include "manager.h"
-void manager_dump_jobs(Manager *s, FILE *f, const char *prefix);
-void manager_dump_units(Manager *s, FILE *f, const char *prefix);
-void manager_dump(Manager *s, FILE *f, const char *prefix);
-int manager_get_dump_string(Manager *m, char **ret);
+void manager_dump_jobs(Manager *s, FILE *f, char **patterns, const char *prefix);
+void manager_dump_units(Manager *s, FILE *f, char **patterns, const char *prefix);
+void manager_dump(Manager *s, FILE *f, char **patterns, const char *prefix);
+int manager_get_dump_string(Manager *m, char **patterns, char **ret);
void manager_test_summary(Manager *m);
case SIGUSR2: {
_cleanup_free_ char *dump = NULL;
- r = manager_get_dump_string(m, &dump);
+ r = manager_get_dump_string(m, /* patterns= */ NULL, &dump);
if (r < 0) {
log_warning_errno(errno, "Failed to acquire manager dump: %m");
break;
send_interface="org.freedesktop.systemd1.Manager"
send_member="DumpByFileDescriptor"/>
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="DumpPatterns"/>
+
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
send_member="ListUnitFiles"/>
assert_se(manager_load_startable_unit_or_warn(m, "a.service", NULL, &a) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "b.service", NULL, &b) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "c.service", NULL, &c) >= 0);
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("Test1: (Trivial)\n");
r = manager_add_job(m, JOB_START, c, JOB_REPLACE, NULL, &err, &j);
if (sd_bus_error_is_set(&err))
log_error("error: %s: %s", err.name, err.message);
assert_se(r == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Load2:\n");
manager_clear_jobs(m);
assert_se(manager_load_startable_unit_or_warn(m, "d.service", NULL, &d) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "e.service", NULL, &e) >= 0);
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("Test2: (Cyclic Order, Unfixable)\n");
assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n");
assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, NULL, &j) == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Test4: (Identical transaction)\n");
assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, NULL, &j) == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Load3:\n");
assert_se(manager_load_startable_unit_or_warn(m, "g.service", NULL, &g) >= 0);
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("Test5: (Colliding transaction, fail)\n");
assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, NULL, &j) == -EDEADLK);
printf("Test6: (Colliding transaction, replace)\n");
assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, NULL, NULL, &j) == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Test7: (Unmergeable job type, fail)\n");
assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, NULL, NULL, &j) == -EDEADLK);
printf("Test8: (Mergeable job type, fail)\n");
assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, NULL, &j) == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Test9: (Unmergeable job type, replace)\n");
assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, NULL, &j) == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Load4:\n");
assert_se(manager_load_startable_unit_or_warn(m, "h.service", NULL, &h) >= 0);
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("Test10: (Unmergeable job type of auxiliary job, fail)\n");
assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, NULL, &j) == 0);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Load5:\n");
manager_clear_jobs(m);
assert_se(manager_load_startable_unit_or_warn(m, "i.service", NULL, &i) >= 0);
SERVICE(a)->state = SERVICE_RUNNING;
SERVICE(d)->state = SERVICE_RUNNING;
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("Test11: (Start/stop job ordering, execution cycle)\n");
assert_se(manager_add_job(m, JOB_START, i, JOB_FAIL, NULL, NULL, &j) == 0);
assert_se(unit_has_job_type(a, JOB_STOP));
assert_se(unit_has_job_type(d, JOB_STOP));
assert_se(unit_has_job_type(b, JOB_START));
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
printf("Load6:\n");
manager_clear_jobs(m);
assert_se(manager_load_startable_unit_or_warn(m, "a-conj.service", NULL, &a_conj) >= 0);
SERVICE(a)->state = SERVICE_DEAD;
- manager_dump_units(m, stdout, "\t");
+ manager_dump_units(m, stdout, /* patterns= */ NULL, "\t");
printf("Test12: (Trivial cycle, Unfixable)\n");
assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
- manager_dump_jobs(m, stdout, "\t");
+ manager_dump_jobs(m, stdout, /* patterns= */ NULL, "\t");
assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b));
assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a));