mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-01-08 21:17:47 +03:00
Move systemctl dot to systemd-analyze dot
This commit is contained in:
parent
2265fbf7e5
commit
1700761b06
2
TODO
2
TODO
@ -80,8 +80,6 @@ Features:
|
||||
|
||||
* introduce ExecCondition= in services
|
||||
|
||||
* if we have systemd-analyze in C "systemctl dot" should move there too
|
||||
|
||||
* EFI:
|
||||
- fsck hookup for the ESP mount is missing
|
||||
- write man page for efi boot generator
|
||||
|
@ -271,32 +271,6 @@
|
||||
manager of the calling user.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--order</option></term>
|
||||
<term><option>--require</option></term>
|
||||
|
||||
<listitem><para>When used in
|
||||
conjunction with the
|
||||
<command>dot</command> command (see
|
||||
below), selects which dependencies are
|
||||
shown in the dependency graph. If
|
||||
<option>--order</option> is passed
|
||||
only dependencies of type
|
||||
<varname>After=</varname> or
|
||||
<varname>Before=</varname> are
|
||||
shown. If <option>--require</option>
|
||||
is passed only dependencies of type
|
||||
<varname>Requires=</varname>,
|
||||
<varname>RequiresOverridable=</varname>,
|
||||
<varname>Requisite=</varname>,
|
||||
<varname>RequisiteOverridable=</varname>,
|
||||
<varname>Wants=</varname> and
|
||||
<varname>Conflicts=</varname> are
|
||||
shown. If neither is passed, shows
|
||||
dependencies of all these
|
||||
types.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--no-wall</option></term>
|
||||
|
||||
@ -978,24 +952,6 @@
|
||||
be parsed by
|
||||
applications.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><command>dot</command></term>
|
||||
|
||||
<listitem><para>Generate textual
|
||||
dependency graph description in dot
|
||||
format for further processing with the
|
||||
GraphViz
|
||||
<citerefentry><refentrytitle>dot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
tool. Use a command line like
|
||||
<command>systemctl dot | dot -Tsvg >
|
||||
systemd.svg</command> to generate a
|
||||
graphical dependency tree. Unless
|
||||
<option>--order</option> or
|
||||
<option>--require</option> is passed
|
||||
the generated graph will show both
|
||||
ordering and requirement
|
||||
dependencies.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><command>list-dependencies [NAME]</command></term>
|
||||
|
||||
|
@ -57,6 +57,9 @@
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze <arg choice="opt" rep="repeat">OPTIONS</arg> plot <arg choice="opt">> file.svg</arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-analyze <arg choice="opt" rep="repeat">OPTIONS</arg> dot </command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
@ -90,6 +93,17 @@
|
||||
been started at what time, highlighting the time they
|
||||
spent on initialization.</para>
|
||||
|
||||
<para><command>systemd-analyze dot</command>
|
||||
Generate textual dependency graph description in dot
|
||||
format for further processing with the GraphViz
|
||||
<citerefentry><refentrytitle>dot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
tool. Use a command line like <command>systemd-analyze
|
||||
dot | dot -Tsvg > systemd.svg</command> to generate
|
||||
a graphical dependency tree. Unless
|
||||
<option>--order</option> or <option>--require</option>
|
||||
is passed the generated graph will show both ordering
|
||||
and requirement dependencies.</para>
|
||||
|
||||
<para>If no command is passed <command>systemd-analyze
|
||||
time</command> is implied.</para>
|
||||
|
||||
@ -116,6 +130,32 @@
|
||||
of user sessions instead of the system
|
||||
manager.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--order</option></term>
|
||||
<term><option>--require</option></term>
|
||||
|
||||
<listitem><para>When used in
|
||||
conjunction with the
|
||||
<command>dot</command> command (see
|
||||
above), selects which dependencies are
|
||||
shown in the dependency graph. If
|
||||
<option>--order</option> is passed
|
||||
only dependencies of type
|
||||
<varname>After=</varname> or
|
||||
<varname>Before=</varname> are
|
||||
shown. If <option>--require</option>
|
||||
is passed only dependencies of type
|
||||
<varname>Requires=</varname>,
|
||||
<varname>RequiresOverridable=</varname>,
|
||||
<varname>Requisite=</varname>,
|
||||
<varname>RequisiteOverridable=</varname>,
|
||||
<varname>Wants=</varname> and
|
||||
<varname>Conflicts=</varname> are
|
||||
shown. If neither is passed, shows
|
||||
dependencies of all these
|
||||
types.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
@ -60,7 +60,7 @@ _systemctl () {
|
||||
local -A OPTS=(
|
||||
[STANDALONE]='--all -a --defaults --fail --ignore-dependencies --failed --force -f --full --global
|
||||
--help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall
|
||||
--order --require --quiet -q --privileged -P --system --user --version --runtime'
|
||||
--quiet -q --privileged -P --system --user --version --runtime'
|
||||
[ARG]='--host -H --kill-mode --kill-who --property -p --signal -s --type -t --root'
|
||||
)
|
||||
|
||||
@ -113,7 +113,7 @@ _systemctl () {
|
||||
[JOBS]='cancel'
|
||||
[SNAPSHOTS]='delete'
|
||||
[ENVS]='set-environment unset-environment'
|
||||
[STANDALONE]='daemon-reexec daemon-reload default dot dump
|
||||
[STANDALONE]='daemon-reexec daemon-reload default dump
|
||||
emergency exit halt hibernate hybrid-sleep kexec list-jobs
|
||||
list-units list-unit-files poweroff reboot rescue
|
||||
show-environment suspend'
|
||||
|
@ -27,8 +27,6 @@ _ctls()
|
||||
'--no-legend[Do not print a legend, i.e. the column headers and the footer with hints]' \
|
||||
'--no-pager[Do not pipe output into a pager]' \
|
||||
'--no-ask-password[Do not ask for system passwords]' \
|
||||
'--order[When generating graph for dot, show only order]' \
|
||||
'--require[When generating graph for dot, show only requirement]' \
|
||||
'--system[Connect to system manager]' \
|
||||
'--user[Connect to user service manager]' \
|
||||
'--global[Enable/disable unit files globally]' \
|
||||
@ -136,6 +134,8 @@ _ctls()
|
||||
_arguments \
|
||||
{-h,--help}'[Show help text.]' \
|
||||
'--user[Shows performance data of user sessions instead of the system manager.]' \
|
||||
'--order[When generating graph for dot, show only order]' \
|
||||
'--require[When generating graph for dot, show only requirement]' \
|
||||
'*::systemd-analyze commands:_systemd_analyze_command'
|
||||
;;
|
||||
systemd-ask-password)
|
||||
@ -293,6 +293,7 @@ _systemd_analyze_command(){
|
||||
'time:Print the time taken to start'
|
||||
'blame:prints a list of all running units, ordered by the time they took to initialize'
|
||||
'plot:prints an SVG graphic detailing which system services have been started at what time'
|
||||
'dot:Dump dependency graph for dot(1)'
|
||||
)
|
||||
|
||||
if (( CURRENT == 1 )); then
|
||||
@ -352,7 +353,6 @@ _outputmodes() {
|
||||
"list-jobs:List jobs"
|
||||
"cancel:Cancel all, one, or more jobs"
|
||||
"dump:Dump server status"
|
||||
"dot:Dump dependency graph for dot(1)"
|
||||
"snapshot:Create a snapshot"
|
||||
"delete:Remove one or more snapshots"
|
||||
"show-environment:Dump environment"
|
||||
@ -575,7 +575,7 @@ done
|
||||
(( $+functions[_systemctl_link] )) || _systemctl_link() { _files }
|
||||
|
||||
# no systemctl completion for:
|
||||
# [STANDALONE]='daemon-reexec daemon-reload default dot dump
|
||||
# [STANDALONE]='daemon-reexec daemon-reload default dump
|
||||
# emergency exit halt kexec list-jobs list-units
|
||||
# list-unit-files poweroff reboot rescue show-environment'
|
||||
# [NAME]='snapshot load'
|
||||
|
@ -46,6 +46,11 @@
|
||||
} while(false)
|
||||
|
||||
static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
|
||||
static enum dot {
|
||||
DEP_ALL,
|
||||
DEP_ORDER,
|
||||
DEP_REQUIRE
|
||||
} arg_dot = DEP_ALL;
|
||||
|
||||
double scale_x = 0.1; // pixels per ms
|
||||
double scale_y = 20.0;
|
||||
@ -532,6 +537,166 @@ static int analyze_time(DBusConnection *bus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
|
||||
|
||||
static const char * const colors[] = {
|
||||
"Requires", "[color=\"black\"]",
|
||||
"RequiresOverridable", "[color=\"black\"]",
|
||||
"Requisite", "[color=\"darkblue\"]",
|
||||
"RequisiteOverridable", "[color=\"darkblue\"]",
|
||||
"Wants", "[color=\"grey66\"]",
|
||||
"Conflicts", "[color=\"red\"]",
|
||||
"ConflictedBy", "[color=\"red\"]",
|
||||
"After", "[color=\"green\"]"
|
||||
};
|
||||
|
||||
const char *c = NULL;
|
||||
unsigned i;
|
||||
|
||||
assert(name);
|
||||
assert(prop);
|
||||
assert(iter);
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(colors); i += 2)
|
||||
if (streq(colors[i], prop)) {
|
||||
c = colors[i+1];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
if (arg_dot != DEP_ALL)
|
||||
if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
|
||||
return 0;
|
||||
|
||||
if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
|
||||
dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
|
||||
DBusMessageIter sub;
|
||||
|
||||
dbus_message_iter_recurse(iter, &sub);
|
||||
|
||||
for (dbus_message_iter_recurse(iter, &sub);
|
||||
dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
|
||||
dbus_message_iter_next(&sub)) {
|
||||
const char *s;
|
||||
|
||||
assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
|
||||
dbus_message_iter_get_basic(&sub, &s);
|
||||
printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int graph_one(DBusConnection *bus, const struct unit_info *u) {
|
||||
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
|
||||
const char *interface = "org.freedesktop.systemd1.Unit";
|
||||
int r;
|
||||
DBusMessageIter iter, sub, sub2, sub3;
|
||||
|
||||
assert(bus);
|
||||
assert(u);
|
||||
|
||||
r = bus_method_call_with_reply(
|
||||
bus,
|
||||
"org.freedesktop.systemd1",
|
||||
u->unit_path,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"GetAll",
|
||||
&reply,
|
||||
NULL,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!dbus_message_iter_init(reply, &iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
|
||||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
|
||||
log_error("Failed to parse reply.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (dbus_message_iter_recurse(&iter, &sub);
|
||||
dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
|
||||
dbus_message_iter_next(&sub)) {
|
||||
const char *prop;
|
||||
|
||||
assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
|
||||
dbus_message_iter_recurse(&sub, &sub2);
|
||||
|
||||
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
|
||||
dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
|
||||
log_error("Failed to parse reply.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&sub2, &sub3);
|
||||
r = graph_one_property(u->id, prop, &sub3);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dot(DBusConnection *bus) {
|
||||
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
|
||||
DBusMessageIter iter, sub;
|
||||
int r;
|
||||
|
||||
r = bus_method_call_with_reply(
|
||||
bus,
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager",
|
||||
"ListUnits",
|
||||
&reply,
|
||||
NULL,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!dbus_message_iter_init(reply, &iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
|
||||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
|
||||
log_error("Failed to parse reply.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
printf("digraph systemd {\n");
|
||||
|
||||
for (dbus_message_iter_recurse(&iter, &sub);
|
||||
dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
|
||||
dbus_message_iter_next(&sub)) {
|
||||
struct unit_info u;
|
||||
|
||||
r = bus_parse_unit_info(&sub, &u);
|
||||
if (r < 0)
|
||||
return -EIO;
|
||||
|
||||
r = graph_one(bus, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void analyze_help(void)
|
||||
{
|
||||
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
||||
@ -539,11 +704,14 @@ static void analyze_help(void)
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" --system Connect to system manager\n"
|
||||
" --user Connect to user service manager\n\n"
|
||||
" --user Connect to user service manager\n"
|
||||
" --order When generating a dependency graph, show only order\n"
|
||||
" --require When generating a dependency graph, show only requirement\n\n"
|
||||
"Commands:\n"
|
||||
" time print time spent in the kernel before reaching userspace\n"
|
||||
" blame print list of running units ordered by time to init\n"
|
||||
" plot output SVG graphic showing service initialization\n\n",
|
||||
" time Print time spent in the kernel before reaching userspace\n"
|
||||
" blame Print list of running units ordered by time to init\n"
|
||||
" plot Output SVG graphic showing service initialization\n"
|
||||
" dot Dump dependency graph (in dot(1) format)\n\n",
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
||||
@ -551,6 +719,8 @@ static int parse_argv(int argc, char *argv[])
|
||||
{
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_ORDER,
|
||||
ARG_REQUIRE,
|
||||
ARG_USER,
|
||||
ARG_SYSTEM
|
||||
};
|
||||
@ -558,6 +728,8 @@ static int parse_argv(int argc, char *argv[])
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "order", no_argument, NULL, ARG_ORDER },
|
||||
{ "require", no_argument, NULL, ARG_REQUIRE },
|
||||
{ "user", no_argument, NULL, ARG_USER },
|
||||
{ "system", no_argument, NULL, ARG_SYSTEM },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
@ -580,6 +752,12 @@ static int parse_argv(int argc, char *argv[])
|
||||
case ARG_SYSTEM:
|
||||
arg_scope = UNIT_FILE_SYSTEM;
|
||||
break;
|
||||
case ARG_ORDER:
|
||||
arg_dot = DEP_ORDER;
|
||||
break;
|
||||
case ARG_REQUIRE:
|
||||
arg_dot = DEP_REQUIRE;
|
||||
break;
|
||||
case -1:
|
||||
return 1;
|
||||
case '?':
|
||||
@ -614,6 +792,8 @@ int main(int argc, char *argv[]) {
|
||||
r = analyze_blame(bus);
|
||||
else if (streq(argv[optind], "plot"))
|
||||
r = analyze_plot(bus);
|
||||
else if (streq(argv[optind], "dot"))
|
||||
r = dot(bus);
|
||||
else
|
||||
log_error("Unknown operation '%s'.", argv[optind]);
|
||||
|
||||
|
@ -116,11 +116,6 @@ static enum action {
|
||||
ACTION_CANCEL_SHUTDOWN,
|
||||
_ACTION_MAX
|
||||
} arg_action = ACTION_SYSTEMCTL;
|
||||
static enum dot {
|
||||
DOT_ALL,
|
||||
DOT_ORDER,
|
||||
DOT_REQUIRE
|
||||
} arg_dot = DOT_ALL;
|
||||
static enum transport {
|
||||
TRANSPORT_NORMAL,
|
||||
TRANSPORT_SSH,
|
||||
@ -891,176 +886,6 @@ static int list_dependencies(DBusConnection *bus, char **args) {
|
||||
return list_dependencies_one(bus, u, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
|
||||
|
||||
static const char * const colors[] = {
|
||||
"Requires", "[color=\"black\"]",
|
||||
"RequiresOverridable", "[color=\"black\"]",
|
||||
"Requisite", "[color=\"darkblue\"]",
|
||||
"RequisiteOverridable", "[color=\"darkblue\"]",
|
||||
"Wants", "[color=\"grey66\"]",
|
||||
"Conflicts", "[color=\"red\"]",
|
||||
"ConflictedBy", "[color=\"red\"]",
|
||||
"After", "[color=\"green\"]"
|
||||
};
|
||||
|
||||
const char *c = NULL;
|
||||
unsigned i;
|
||||
|
||||
assert(name);
|
||||
assert(prop);
|
||||
assert(iter);
|
||||
|
||||
for (i = 0; i < ELEMENTSOF(colors); i += 2)
|
||||
if (streq(colors[i], prop)) {
|
||||
c = colors[i+1];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
if (arg_dot != DOT_ALL)
|
||||
if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
|
||||
return 0;
|
||||
|
||||
switch (dbus_message_iter_get_arg_type(iter)) {
|
||||
|
||||
case DBUS_TYPE_ARRAY:
|
||||
|
||||
if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
|
||||
DBusMessageIter sub;
|
||||
|
||||
dbus_message_iter_recurse(iter, &sub);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
|
||||
const char *s;
|
||||
|
||||
assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
|
||||
dbus_message_iter_get_basic(&sub, &s);
|
||||
printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
|
||||
|
||||
dbus_message_iter_next(&sub);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dot_one(DBusConnection *bus, const struct unit_info *u) {
|
||||
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
|
||||
const char *interface = "org.freedesktop.systemd1.Unit";
|
||||
int r;
|
||||
DBusMessageIter iter, sub, sub2, sub3;
|
||||
|
||||
assert(bus);
|
||||
assert(u);
|
||||
|
||||
r = bus_method_call_with_reply(
|
||||
bus,
|
||||
"org.freedesktop.systemd1",
|
||||
u->unit_path,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"GetAll",
|
||||
&reply,
|
||||
NULL,
|
||||
DBUS_TYPE_STRING, &interface,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!dbus_message_iter_init(reply, &iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
|
||||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
|
||||
log_error("Failed to parse reply.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&iter, &sub);
|
||||
|
||||
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
|
||||
const char *prop;
|
||||
|
||||
assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
|
||||
dbus_message_iter_recurse(&sub, &sub2);
|
||||
|
||||
if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
|
||||
dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
|
||||
log_error("Failed to parse reply.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dbus_message_iter_recurse(&sub2, &sub3);
|
||||
r = dot_one_property(u->id, prop, &sub3);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dbus_message_iter_next(&sub);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dot(DBusConnection *bus, char **args) {
|
||||
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
|
||||
DBusMessageIter iter, sub;
|
||||
int r;
|
||||
|
||||
r = bus_method_call_with_reply(
|
||||
bus,
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager",
|
||||
"ListUnits",
|
||||
&reply,
|
||||
NULL,
|
||||
DBUS_TYPE_INVALID);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!dbus_message_iter_init(reply, &iter) ||
|
||||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
|
||||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
|
||||
log_error("Failed to parse reply.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
printf("digraph systemd {\n");
|
||||
|
||||
dbus_message_iter_recurse(&iter, &sub);
|
||||
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
|
||||
struct unit_info u;
|
||||
|
||||
r = bus_parse_unit_info(&sub, &u);
|
||||
if (r < 0)
|
||||
return -EIO;
|
||||
|
||||
r = dot_one(bus, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* printf("\t\"%s\";\n", u.id); */
|
||||
dbus_message_iter_next(&sub);
|
||||
}
|
||||
|
||||
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 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int list_jobs(DBusConnection *bus, char **args) {
|
||||
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
|
||||
DBusMessageIter iter, sub, sub2;
|
||||
@ -4276,8 +4101,6 @@ static int systemctl_help(void) {
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --no-ask-password\n"
|
||||
" Do not ask for system passwords\n"
|
||||
" --order When generating graph for dot, show only order\n"
|
||||
" --require When generating graph for dot, show only requirement\n"
|
||||
" --system Connect to system manager\n"
|
||||
" --user Connect to user service manager\n"
|
||||
" --global Enable/disable unit files globally\n"
|
||||
@ -4337,7 +4160,6 @@ static int systemctl_help(void) {
|
||||
" cancel [JOB...] Cancel all, one, or more jobs\n\n"
|
||||
"Status Commands:\n"
|
||||
" dump Dump server status\n"
|
||||
" dot Dump dependency graph for dot(1)\n\n"
|
||||
"Snapshot Commands:\n"
|
||||
" snapshot [NAME] Create a snapshot\n"
|
||||
" delete [NAME...] Remove one or more snapshots\n\n"
|
||||
@ -4460,8 +4282,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
ARG_NO_LEGEND,
|
||||
ARG_NO_PAGER,
|
||||
ARG_NO_WALL,
|
||||
ARG_ORDER,
|
||||
ARG_REQUIRE,
|
||||
ARG_ROOT,
|
||||
ARG_FULL,
|
||||
ARG_NO_RELOAD,
|
||||
@ -4491,8 +4311,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "no-wall", no_argument, NULL, ARG_NO_WALL },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "order", no_argument, NULL, ARG_ORDER },
|
||||
{ "require", no_argument, NULL, ARG_REQUIRE },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "force", no_argument, NULL, ARG_FORCE },
|
||||
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||
@ -4599,14 +4417,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
arg_no_wall = true;
|
||||
break;
|
||||
|
||||
case ARG_ORDER:
|
||||
arg_dot = DOT_ORDER;
|
||||
break;
|
||||
|
||||
case ARG_REQUIRE:
|
||||
arg_dot = DOT_REQUIRE;
|
||||
break;
|
||||
|
||||
case ARG_ROOT:
|
||||
arg_root = optarg;
|
||||
break;
|
||||
@ -5307,7 +5117,6 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
|
||||
{ "status", MORE, 2, show },
|
||||
{ "help", MORE, 2, show },
|
||||
{ "dump", EQUAL, 1, dump },
|
||||
{ "dot", EQUAL, 1, dot },
|
||||
{ "snapshot", LESS, 2, snapshot },
|
||||
{ "delete", MORE, 2, delete_snapshot },
|
||||
{ "daemon-reload", EQUAL, 1, daemon_reload },
|
||||
|
Loading…
Reference in New Issue
Block a user