summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2011-07-25 04:58:02 +0200
committerLennart Poettering <lennart@poettering.net>2011-07-25 04:58:02 +0200
commit729e3769c32242bbba26ea96900be005d52ce438 (patch)
treea8a6234044790254e3834e1e67971df93613b918
parent4b7a6af440ee1a957fd8fe1c6ca3b7f310fdf77c (diff)
systemctl: hook up new install logic
This adds a number of new options to systemctl, for presets, reenabling, masking/unmask, and runtime operations.
-rw-r--r--Makefile.am4
-rw-r--r--TODO13
-rw-r--r--man/systemctl.xml118
-rw-r--r--src/dbus-manager.c42
-rw-r--r--src/hashmap.c10
-rw-r--r--src/install.c8
-rw-r--r--src/org.freedesktop.systemd1.conf8
-rw-r--r--src/pager.c6
-rw-r--r--src/spawn-agent.c120
-rw-r--r--src/spawn-agent.h28
-rw-r--r--src/systemctl.c1538
11 files changed, 994 insertions, 901 deletions
diff --git a/Makefile.am b/Makefile.am
index 78c76d7a6b..2ebf53ce86 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1203,7 +1203,9 @@ systemctl_SOURCES = \
src/cgroup-util.c \
src/exit-status.c \
src/unit-name.c \
- src/pager.c
+ src/pager.c \
+ src/install.c \
+ src/spawn-agent.c
systemctl_CFLAGS = \
$(AM_CFLAGS) \
diff --git a/TODO b/TODO
index 82bc9950a6..feac225cb5 100644
--- a/TODO
+++ b/TODO
@@ -47,8 +47,6 @@ Features:
* make sure systemd-ask-password-wall does not shutdown systemd-ask-password-console too early
-* support presets
-
* kernel: add /proc/sys file exposing CAP_LAST_CAP?
* kernel: add device_type = "fb", "fbcon" to class "graphics"
@@ -86,17 +84,6 @@ Features:
* show enablement status in systemctl status
-* teach systemctl to enable unit files in arbitrary directories
-
-* In systemctl make sure both is-enabled and is-active print a string, or neither.
-
-* Implement:
- systemctl mask <unit>
- systemctl unmask <unit>
- Also support --temp to make this temporary by placing mask links in /run.
-
-* perhaps add "systemctl reenable" as combination of "systemctl disable" and "systemctl enable"
-
* add support for /bin/mount -s
* GC unreferenced jobs (such as .device jobs)
diff --git a/man/systemctl.xml b/man/systemctl.xml
index b60731ad56..468141c2bd 100644
--- a/man/systemctl.xml
+++ b/man/systemctl.xml
@@ -352,26 +352,30 @@
</varlistentry>
<varlistentry>
- <term><option>--defaults</option></term>
+ <term><option>--root=</option></term>
<listitem><para>When used with
- <command>disable</command>, ensures
- that only the symlinks created by
- <command>enable</command> are removed,
- not all symlinks pointing to the unit
- file that shall be
- disabled.</para></listitem>
+ <command>enable</command>/<command>disable</command>/<command>is-enabled</command> (and
+ related commands), use alternative
+ root path when looking for unit
+ files.</para></listitem>
</varlistentry>
<varlistentry>
- <term><option>--root=</option></term>
+ <term><option>--runtime</option></term>
<listitem><para>When used with
- <command>enable</command>/
- <command>disable</command>/
- <command>is-enabled</command>,
- use alternative root path for systemd
- install.</para></listitem>
+ <command>enable</command>/<command>disable</command>/<command>is-enabled</command> (and related commands), make
+ changes only temporarily, so that they
+ are dropped on the next reboot. This
+ will have the effect that changes are
+ not made in subdirectories of
+ <filename>/etc</filename> but in
+ <filename>/run</filename>, with
+ identical immediate effects, however,
+ since the latter is lost on reboot,
+ the changes are lost
+ too.</para></listitem>
</varlistentry>
<varlistentry>
@@ -674,14 +678,11 @@
configuration directory, and hence
undoes the changes made by
<command>enable</command>. Note
- however that this by default removes
+ however that this removes
all symlinks to the unit files
(i.e. including manual additions), not
just those actually created by
- <command>enable</command>. If only the
- symlinks that are suggested by default
- shall be removed, pass
- <option>--defaults</option>. This
+ <command>enable</command>. This call
implicitly reloads the systemd daemon
configuration after completing the
disabling of the units. Note that this
@@ -713,8 +714,85 @@
(as with
<command>enable</command>). Returns an
exit code of 0 if at least one is
- enabled, non-zero
- otherwise.</para></listitem>
+ enabled, non-zero otherwise. Prints
+ the current enable status. To suppress
+ this output use
+ <option>--quiet</option>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>reenable [NAME...]</command></term>
+
+ <listitem><para>Reenable one or more
+ unit files, as specified on the
+ command line. This is a combination of
+ <command>disable</command> and
+ <command>enable</command> and is
+ useful to reset the symlinks a unit is
+ enabled with to the defaults
+ configured in the
+ <literal>[Install]</literal> section
+ of the unit file.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>preset [NAME...]</command></term>
+
+ <listitem><para>Reset one or more unit
+ files, as specified on the command
+ line, to the defaults configured in a
+ preset file. This has the same effect
+ as <command>disable</command> or
+ <command>enable</command>, depending
+ how the unit is listed in the preset
+ files.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>mask [NAME...]</command></term>
+
+ <listitem><para>Mask one or more unit
+ files, as specified on the command
+ line. This will link these units to
+ <filename>/dev/null</filename>, making
+ it impossible to start them. This is a stronger version
+ of <command>disable</command>, since
+ it prohibits all kinds of activation
+ of the unit, including manual
+ activation. Use this option with
+ care.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>unmask [NAME...]</command></term>
+
+ <listitem><para>Unmask one or more
+ unit files, as specified on the
+ command line. This will undo the
+ effect of
+ <command>mask</command>.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><command>link [NAME...]</command></term>
+
+ <listitem><para>Link a unit file that
+ is not in the unit file search paths
+ into the unit file search path. This
+ requires an absolute path to a unit
+ file. The effect of this can be undone
+ with <command>disable</command>. The
+ effect of this command is that a unit
+ file is available for
+ <command>start</command> and other
+ commands although it isn't installed
+ directly in the unit search
+ path.</para>
+ </listitem>
</varlistentry>
<varlistentry>
diff --git a/src/dbus-manager.c b/src/dbus-manager.c
index 582de4bd42..66cf9ee67a 100644
--- a/src/dbus-manager.c
+++ b/src/dbus-manager.c
@@ -145,6 +145,7 @@
" <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \
" <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"DisableUnitFiles\">\n" \
@@ -156,6 +157,7 @@
" <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \
" <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"LinkUnitFiles\">\n" \
@@ -168,6 +170,7 @@
" <arg name=\"files\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"runtime\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"force\" type=\"b\" direction=\"in\"/>\n" \
+ " <arg name=\"carries_install_info\" type=\"b\" directrion=\"out\"/>\n" \
" <arg name=\"changes\" type=\"a(sss)\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"MaskUnitFiles\">\n" \
@@ -422,7 +425,12 @@ static const char *message_get_sender_with_fallback(DBusMessage *m) {
return ":no-sender";
}
-static DBusMessage *message_from_file_changes(DBusMessage *m, UnitFileChange *changes, unsigned n_changes) {
+static DBusMessage *message_from_file_changes(
+ DBusMessage *m,
+ UnitFileChange *changes,
+ unsigned n_changes,
+ int carries_install_info) {
+
DBusMessageIter iter, sub, sub2;
DBusMessage *reply;
unsigned i;
@@ -435,6 +443,14 @@ static DBusMessage *message_from_file_changes(DBusMessage *m, UnitFileChange *ch
dbus_message_iter_init_append(reply, &iter);
+ if (carries_install_info >= 0) {
+ dbus_bool_t b;
+
+ b = carries_install_info;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b))
+ goto oom;
+ }
+
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sss)", &sub))
goto oom;
@@ -446,9 +462,9 @@ static DBusMessage *message_from_file_changes(DBusMessage *m, UnitFileChange *ch
source = strempty(changes[i].source);
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &type) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &path) ||
- !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &source) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &type) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &path) ||
+ !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &source) ||
!dbus_message_iter_close_container(&sub, &sub2))
goto oom;
}
@@ -1304,6 +1320,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
UnitFileChange *changes = NULL;
unsigned n_changes = 0;
dbus_bool_t runtime, force;
+ bool carries_install_info = -1;
if (!dbus_message_iter_init(message, &iter))
goto oom;
@@ -1323,15 +1340,18 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
return bus_send_error_reply(connection, message, NULL, -EIO);
}
- if (streq(member, "EnableUnitFiles"))
+ if (streq(member, "EnableUnitFiles")) {
r = unit_file_enable(scope, runtime, NULL, l, force, &changes, &n_changes);
- else if (streq(member, "ReenableUnitFiles"))
+ carries_install_info = r;
+ } else if (streq(member, "ReenableUnitFiles")) {
r = unit_file_reenable(scope, runtime, NULL, l, force, &changes, &n_changes);
- else if (streq(member, "LinkUnitFiles"))
+ carries_install_info = r;
+ } else if (streq(member, "LinkUnitFiles"))
r = unit_file_link(scope, runtime, NULL, l, force, &changes, &n_changes);
- else if (streq(member, "PresetUnitFiles"))
+ else if (streq(member, "PresetUnitFiles")) {
r = unit_file_preset(scope, runtime, NULL, l, force, &changes, &n_changes);
- else if (streq(member, "MaskUnitFiles"))
+ carries_install_info = r;
+ } else if (streq(member, "MaskUnitFiles"))
r = unit_file_mask(scope, runtime, NULL, l, force, &changes, &n_changes);
else
assert_not_reached("Uh? Wrong method");
@@ -1344,7 +1364,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
return bus_send_error_reply(connection, message, NULL, r);
}
- reply = message_from_file_changes(message, changes, n_changes);
+ reply = message_from_file_changes(message, changes, n_changes, carries_install_info);
unit_file_changes_free(changes, n_changes);
if (!reply)
@@ -1392,7 +1412,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
return bus_send_error_reply(connection, message, NULL, r);
}
- reply = message_from_file_changes(message, changes, n_changes);
+ reply = message_from_file_changes(message, changes, n_changes, -1);
unit_file_changes_free(changes, n_changes);
if (!reply)
diff --git a/src/hashmap.c b/src/hashmap.c
index 53502576ad..ca83e93385 100644
--- a/src/hashmap.c
+++ b/src/hashmap.c
@@ -596,16 +596,16 @@ Hashmap *hashmap_copy(Hashmap *h) {
char **hashmap_get_strv(Hashmap *h) {
char **sv;
Iterator it;
- char *path;
+ char *item;
int n;
- sv = malloc((h->n_entries+1) * sizeof(char *));
- if (sv == NULL)
+ sv = new(char*, h->n_entries+1);
+ if (!sv)
return NULL;
n = 0;
- HASHMAP_FOREACH(path, h, it)
- sv[n++] = path;
+ HASHMAP_FOREACH(item, h, it)
+ sv[n++] = item;
sv[n] = NULL;
return sv;
diff --git a/src/install.c b/src/install.c
index b37bbbdc5a..b843ee156b 100644
--- a/src/install.c
+++ b/src/install.c
@@ -126,8 +126,6 @@ static int add_file_change(
UnitFileChange *c;
unsigned i;
- assert(type >= 0);
- assert(type < _UNIT_FILE_CHANGE_TYPE_MAX);
assert(path);
assert(!changes == !n_changes);
@@ -1414,6 +1412,10 @@ int unit_file_enable(
goto finish;
}
+ /* This will return the number of symlink rules that were
+ supposed to be created, not the ones actually created. This is
+ useful to determine whether the passed files hat any
+ installation data at all. */
r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
finish:
@@ -1514,6 +1516,7 @@ int unit_file_reenable(
r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+ /* Returns number of symlinks that where supposed to be installed. */
q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
if (r == 0)
r = q;
@@ -1763,6 +1766,7 @@ int unit_file_preset(
if (r == 0)
r = q;
+ /* Returns number of symlinks that where supposed to be installed. */
q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
if (r == 0)
r = q;
diff --git a/src/org.freedesktop.systemd1.conf b/src/org.freedesktop.systemd1.conf
index 8008f0f4ba..201afe65be 100644
--- a/src/org.freedesktop.systemd1.conf
+++ b/src/org.freedesktop.systemd1.conf
@@ -64,6 +64,14 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitFiles"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
+ send_member="GetUnitFileState"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListJobs"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/pager.c b/src/pager.c
index 6ea25ada65..be284da962 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -39,13 +39,13 @@ void pager_open(void) {
if (pager_pid > 0)
return;
- if (isatty(STDOUT_FILENO) <= 0)
- return;
-
if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER")))
if (!*pager || streq(pager, "cat"))
return;
+ if (isatty(STDOUT_FILENO) <= 0)
+ return;
+
/* Determine and cache number of columns before we spawn the
* pager so that we get the value from the actual tty */
columns();
diff --git a/src/spawn-agent.c b/src/spawn-agent.c
new file mode 100644
index 0000000000..2de2530885
--- /dev/null
+++ b/src/spawn-agent.c
@@ -0,0 +1,120 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "log.h"
+#include "util.h"
+#include "spawn-agent.h"
+
+static pid_t agent_pid = 0;
+
+void agent_open(void) {
+ pid_t parent_pid;
+
+ if (agent_pid > 0)
+ return;
+
+ /* We check STDIN here, not STDOUT, since this is about input,
+ * not output */
+ if (!isatty(STDIN_FILENO))
+ return;
+
+ parent_pid = getpid();
+
+ /* Spawns a temporary TTY agent, making sure it goes away when
+ * we go away */
+
+ agent_pid = fork();
+ if (agent_pid < 0) {
+ log_error("Failed to fork agent: %m");
+ return;
+ }
+
+ if (agent_pid == 0) {
+ /* In the child */
+
+ int fd;
+ bool stdout_is_tty, stderr_is_tty;
+
+ /* Make sure the agent goes away when the parent dies */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ _exit(EXIT_FAILURE);
+
+ /* Check whether our parent died before we were able
+ * to set the death signal */
+ if (getppid() != parent_pid)
+ _exit(EXIT_SUCCESS);
+
+ /* Don't leak fds to the agent */
+ close_all_fds(NULL, 0);
+
+ stdout_is_tty = isatty(STDOUT_FILENO);
+ stderr_is_tty = isatty(STDERR_FILENO);
+
+ if (!stdout_is_tty || !stderr_is_tty) {
+ /* Detach from stdout/stderr. and reopen
+ * /dev/tty for them. This is important to
+ * ensure that when systemctl is started via
+ * popen() or a similar call that expects to
+ * read EOF we actually do generate EOF and
+ * not delay this indefinitely by because we
+ * keep an unused copy of stdin around. */
+ fd = open("/dev/tty", O_WRONLY);
+ if (fd < 0) {
+ log_error("Failed to open /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stdout_is_tty)
+ dup2(fd, STDOUT_FILENO);
+
+ if (!stderr_is_tty)
+ dup2(fd, STDERR_FILENO);
+
+ if (fd > 2)
+ close(fd);
+ }
+
+ execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
+
+ log_error("Unable to execute agent: %m");
+ _exit(EXIT_FAILURE);
+ }
+}
+
+void agent_close(void) {
+
+ if (agent_pid <= 0)
+ return;
+
+ /* Inform agent that we are done */
+ kill(agent_pid, SIGTERM);
+ kill(agent_pid, SIGCONT);
+ wait_for_terminate(agent_pid, NULL);
+ agent_pid = 0;
+}
diff --git a/src/spawn-agent.h b/src/spawn-agent.h
new file mode 100644
index 0000000000..fd0a9109b8
--- /dev/null
+++ b/src/spawn-agent.h
@@ -0,0 +1,28 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef foospawnagenthfoo
+#define foospawnagenthfoo
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+void agent_open(void);
+void agent_close(void);
+
+#endif
diff --git a/src/systemctl.c b/src/systemctl.c
index 9f5ba8a202..620b91ff63 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -56,13 +56,14 @@
#include "build.h"
#include "unit-name.h"
#include "pager.h"
+#include "spawn-agent.h"
+#include "install.h"
static const char *arg_type = NULL;
static char **arg_property = NULL;
static bool arg_all = false;
static const char *arg_job_mode = "replace";
-static bool arg_user = false;
-static bool arg_global = false;
+static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
static bool arg_immediate = false;
static bool arg_no_block = false;
static bool arg_no_pager = false;
@@ -74,9 +75,9 @@ static bool arg_dry = false;
static bool arg_quiet = false;
static bool arg_full = false;
static bool arg_force = false;
-static bool arg_defaults = false;
static bool arg_ask_password = false;
static bool arg_failed = false;
+static bool arg_runtime = false;
static char **arg_wall = NULL;
static const char *arg_kill_who = NULL;
static const char *arg_kill_mode = NULL;
@@ -118,9 +119,7 @@ static const char *arg_host = NULL;
static bool private_bus = false;
-static pid_t agent_pid = 0;
-
-static int daemon_reload(DBusConnection *bus, char **args, unsigned n);
+static int daemon_reload(DBusConnection *bus, char **args);
static bool on_tty(void) {
static int t = -1;
@@ -140,84 +139,23 @@ static bool on_tty(void) {
static void pager_open_if_enabled(void) {
on_tty();
- if (!arg_no_pager)
- pager_open();
+ if (arg_no_pager)
+ return;
+
+ pager_open();
}
-static void spawn_ask_password_agent(void) {
- pid_t parent;
+static void agent_open_if_enabled(void) {
- if (agent_pid > 0)
- return;
-
- /* We check STDIN here, not STDOUT, since this is about input,
- * not output */
- if (!isatty(STDIN_FILENO))
- return;
+ /* Open the password agent as a child process if necessary */
if (!arg_ask_password)
return;
- if (arg_user)
+ if (arg_scope != UNIT_FILE_SYSTEM)
return;
- parent = getpid();
-
- /* Spawns a temporary TTY agent, making sure it goes away when
- * we go away */
-
- if ((agent_pid = fork()) < 0)
- return;
-
- if (agent_pid == 0) {
- /* In the child */
-
- int fd;
- bool stdout_is_tty, stderr_is_tty;
-
- /* Make sure the agent goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Check whether our parent died before we were able
- * to set the death signal */
- if (getppid() != parent)
- _exit(EXIT_SUCCESS);
-
- /* Don't leak fds to the agent */
- close_all_fds(NULL, 0);
-
- stdout_is_tty = isatty(STDOUT_FILENO);
- stderr_is_tty = isatty(STDERR_FILENO);
-
- if (!stdout_is_tty || !stderr_is_tty) {
- /* Detach from stdout/stderr. and reopen
- * /dev/tty for them. This is important to
- * ensure that when systemctl is started via
- * popen() or a similar call that expects to
- * read EOF we actually do generate EOF and
- * not delay this indefinitely by because we
- * keep an unused copy of stdin around. */
- if ((fd = open("/dev/tty", O_WRONLY)) < 0) {
- log_error("Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stdout_is_tty)
- dup2(fd, STDOUT_FILENO);
-
- if (!stderr_is_tty)
- dup2(fd, STDERR_FILENO);
-
- if (fd > 2)
- close(fd);
- }
-
- execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
-
- log_error("Unable to execute agent: %m");
- _exit(EXIT_FAILURE);
- }
+ agent_open();
}
static const char *ansi_highlight(bool b) {
@@ -315,6 +253,23 @@ static void warn_wall(enum action action) {
utmp_wall(table[action], NULL);
}
+static bool avoid_bus(void) {
+
+ if (running_in_chroot() > 0)
+ return true;
+
+ if (sd_booted() <= 0)
+ return true;
+
+ if (!isempty(arg_root))
+ return true;
+
+ if (arg_scope == UNIT_FILE_GLOBAL)
+ return true;
+
+ return false;
+}
+
struct unit_info {
const char *id;
const char *description;
@@ -452,7 +407,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
}
}
-static int list_units(DBusConnection *bus, char **args, unsigned n) {
+static int list_units(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
@@ -559,6 +514,210 @@ finish:
return r;
}
+static int compare_unit_file_list(const void *a, const void *b) {
+ const char *d1, *d2;
+ const UnitFileList *u = a, *v = b;
+
+ d1 = strrchr(u->path, '.');
+ d2 = strrchr(v->path, '.');
+
+ if (d1 && d2) {
+ int r;
+
+ r = strcasecmp(d1, d2);
+ if (r != 0)
+ return r;
+ }
+
+ return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
+}
+
+static bool output_show_unit_file(const UnitFileList *u) {
+ const char *dot;
+
+ return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
+}
+
+static void output_unit_file_list(const UnitFileList *units, unsigned c) {
+ unsigned n_shown = 0;
+ const UnitFileList *u;
+
+ if (on_tty())
+ printf("%-25s %-6s\n", "UNIT FILE", "STATE");
+
+ for (u = units; u < units + c; u++) {
+ char *e;
+ const char *on, *off;
+ const char *id;
+
+ if (!output_show_unit_file(u))
+ continue;
+
+ n_shown++;
+
+ if (u->state == UNIT_FILE_MASKED ||
+ u->state == UNIT_FILE_MASKED_RUNTIME ||
+ u->state == UNIT_FILE_DISABLED) {
+ on = ansi_highlight(true);
+ off = ansi_highlight(false);
+ } else if (u->state == UNIT_FILE_ENABLED) {
+ on = ansi_highlight_green(true);
+ off = ansi_highlight_green(false);
+ } else
+ on = off = "";
+
+ id = file_name_from_path(u->path);
+
+ e = arg_full ? NULL : ellipsize(id, 25, 33);
+
+ printf("%-25s %s%-6s%s\n",
+ e ? e : id,
+ on, unit_file_state_to_string(u->state), off);
+
+ free(e);
+ }
+
+ if (on_tty())
+ printf("\n%u unit files listed.\n", n_shown);
+}
+
+static int list_unit_files(DBusConnection *bus, char **args) {
+ DBusMessage *m = NULL, *reply = NULL;
+ DBusError error;
+ int r;
+ DBusMessageIter iter, sub, sub2;
+ unsigned c = 0, n_units = 0;
+ UnitFileList *units = NULL;
+
+ dbus_error_init(&error);
+
+ assert(bus);
+
+ pager_open_if_enabled();
+
+ if (avoid_bus()) {
+ Hashmap *h;
+ UnitFileList *u;
+ Iterator i;
+
+ h = hashmap_new(string_hash_func, string_compare_func);
+ if (!h) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ r = unit_file_get_list(arg_scope, arg_root, h);
+ if (r < 0) {
+ log_error("Failed to get unit file list: %s", strerror(-r));
+ return r;
+ }
+
+ n_units = hashmap_size(h);
+ units = new(UnitFileList, n_units);
+ if (!units) {
+ unit_file_list_free(h);
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ HASHMAP_FOREACH(u, h, i) {
+ memcpy(units + c++, u, sizeof(UnitFileList));
+ free(u);
+ }
+
+ hashmap_free(h);
+ } else {
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitFiles");
+ if (!m) {
+ log_error("Could not allocate message.");
+ return -ENOMEM;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
+
+ 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.");
+ r = -EIO;
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&iter, &sub);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ UnitFileList *u;
+ const char *state;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ if (c >= n_units) {
+ UnitFileList *w;
+
+ n_units = MAX(2*c, 16);
+ w = realloc(units, sizeof(struct UnitFileList) * n_units);
+
+ if (!w) {
+ log_error("Failed to allocate unit array.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ units = w;
+ }
+
+ u = units+c;
+
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
+ }
+
+ u->state = unit_file_state_from_string(state);
+
+ dbus_message_iter_next(&sub);
+ c++;
+ }
+ }
+
+ if (c > 0) {
+ qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
+ output_unit_file_list(units, c);
+ }
+
+ r = 0;
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ free(units);
+
+ dbus_error_free(&error);
+
+ return r;
+}
+
static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
static const char * const colors[] = {
"Requires", "[color=\"black\"]",
@@ -711,7 +870,7 @@ finish:
return r;
}
-static int dot(DBusConnection *bus, char **args, unsigned n) {
+static int dot(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
@@ -803,7 +962,7 @@ finish:
return r;
}
-static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
+static int list_jobs(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
@@ -894,18 +1053,18 @@ finish:
return r;
}
-static int load_unit(DBusConnection *bus, char **args, unsigned n) {
+static int load_unit(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
- unsigned i;
+ char **name;
dbus_error_init(&error);
assert(bus);
assert(args);
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
@@ -918,7 +1077,7 @@ static int load_unit(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_INVALID)) {
log_error("Could not append arguments to message.");
r = -ENOMEM;
@@ -951,21 +1110,21 @@ finish:
return r;
}
-static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
+static int cancel_job(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
- unsigned i;
+ char **name;
dbus_error_init(&error);
assert(bus);
assert(args);
- if (n <= 1)
- return daemon_reload(bus, args, n);
+ if (strv_length(args) <= 1)
+ return daemon_reload(bus, args);
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
unsigned id;
const char *path;
@@ -979,7 +1138,7 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
goto finish;
}
- if ((r = safe_atou(args[i], &id)) < 0) {
+ if ((r = safe_atou(*name, &id)) < 0) {
log_error("Failed to parse job id: %s", strerror(-r));
goto finish;
}
@@ -1327,8 +1486,8 @@ static int start_unit_one(
}
if (need_daemon_reload(bus, name))
- log_warning("Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
- arg_user ? "--user" : "--system");
+ log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
+ arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
if (!arg_no_block) {
char *p;
@@ -1379,7 +1538,7 @@ static enum action verb_to_action(const char *verb) {
return ACTION_INVALID;
}
-static int start_unit(DBusConnection *bus, char **args, unsigned n) {
+static int start_unit(DBusConnection *bus, char **args) {
static const char * const table[_ACTION_MAX] = {
[ACTION_HALT] = SPECIAL_HALT_TARGET,
@@ -1397,16 +1556,16 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
};
int r, ret = 0;
- unsigned i;
const char *method, *mode, *one_name;
Set *s = NULL;
DBusError error;
+ char **name;
dbus_error_init(&error);
assert(bus);
- spawn_ask_password_agent();
+ agent_open_if_enabled();
if (arg_action == ACTION_SYSTEMCTL) {
method =
@@ -1466,8 +1625,8 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
goto finish;
} else {
- for (i = 1; i < n; i++)
- if ((r = start_unit_one(bus, method, args[i], mode, &error, s)) != 0) {
+ STRV_FOREACH(name, args+1)
+ if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
ret = translate_bus_error_to_exit_status(r, &error);
dbus_error_free(&error);
}
@@ -1488,7 +1647,7 @@ finish:
return ret;
}
-static int start_special(DBusConnection *bus, char **args, unsigned n) {
+static int start_special(DBusConnection *bus, char **args) {
int r;
assert(bus);
@@ -1500,9 +1659,9 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
streq(args[0], "reboot") ||
streq(args[0], "kexec") ||
streq(args[0], "exit")))
- return daemon_reload(bus, args, n);
+ return daemon_reload(bus, args);
- r = start_unit(bus, args, n);
+ r = start_unit(bus, args);
if (r >= 0)
warn_wall(verb_to_action(args[0]));
@@ -1510,21 +1669,21 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
return r;
}
-static int check_unit(DBusConnection *bus, char **args, unsigned n) {
+static int check_unit(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
const char
*interface = "org.freedesktop.systemd1.Unit",
*property = "ActiveState";
int r = 3; /* According to LSB: "program is not running" */
DBusError error;
- unsigned i;
+ char **name;
assert(bus);
assert(args);
dbus_error_init(&error);
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
const char *path = NULL;
const char *state;
DBusMessageIter iter, sub;
@@ -1540,7 +1699,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_INVALID)) {
log_error("Could not append arguments to message.");
r = -ENOMEM;
@@ -1634,11 +1793,11 @@ finish:
return r;
}
-static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
+static int kill_unit(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
int r = 0;
DBusError error;
- unsigned i;
+ char **name;
assert(bus);
assert(args);
@@ -1651,7 +1810,7 @@ static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
if (!arg_kill_mode)
arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
@@ -1664,7 +1823,7 @@ static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_STRING, &arg_kill_who,
DBUS_TYPE_STRING, &arg_kill_mode,
DBUS_TYPE_INT32, &arg_signal,
@@ -2075,7 +2234,7 @@ static void print_status_info(UnitStatusInfo *i) {
printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
ansi_highlight(true),
ansi_highlight(false),
- arg_user ? "--user" : "--system");
+ arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
}
static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
@@ -2517,12 +2676,12 @@ finish:
return r;
}
-static int show(DBusConnection *bus, char **args, unsigned n) {
+static int show(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
int r, ret = 0;
DBusError error;
- unsigned i;
bool show_properties, new_line = false;
+ char **name;
assert(bus);
assert(args);
@@ -2534,7 +2693,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
if (show_properties)
pager_open_if_enabled();
- if (show_properties && n <= 1) {
+ if (show_properties && strv_length(args) <= 1) {
/* If not argument is specified inspect the manager
* itself */
@@ -2542,11 +2701,11 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
goto finish;
}
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
const char *path = NULL;
uint32_t id;
- if (safe_atou32(args[i], &id) < 0) {
+ if (safe_atou32(*name, &id) < 0) {
/* Interpret as unit name */
@@ -2561,7 +2720,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_INVALID)) {
log_error("Could not append arguments to message.");
ret = -ENOMEM;
@@ -2590,7 +2749,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_INVALID)) {
log_error("Could not append arguments to message.");
ret = -ENOMEM;
@@ -2692,7 +2851,7 @@ finish:
return ret;
}
-static int dump(DBusConnection *bus, char **args, unsigned n) {
+static int dump(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
@@ -2741,7 +2900,7 @@ finish:
return r;
}
-static int snapshot(DBusConnection *bus, char **args, unsigned n) {
+static int snapshot(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
@@ -2763,7 +2922,7 @@ static int snapshot(DBusConnection *bus, char **args, unsigned n) {
return -ENOMEM;
}
- if (n > 1)
+ if (strv_length(args) > 1)
name = args[1];
if (!dbus_message_append_args(m,
@@ -2848,18 +3007,18 @@ finish:
return r;
}
-static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
+static int delete_snapshot(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
int r;
DBusError error;
- unsigned i;
+ char **name;
assert(bus);
assert(args);
dbus_error_init(&error);
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
const char *path = NULL;
if (!(m = dbus_message_new_method_call(
@@ -2873,7 +3032,7 @@ static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, &args[i],
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_INVALID)) {
log_error("Could not append arguments to message.");
r = -ENOMEM;
@@ -2931,7 +3090,7 @@ finish:
return r;
}
-static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
+static int daemon_reload(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
@@ -3003,19 +3162,19 @@ finish:
return r;
}
-static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
+static int reset_failed(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
- unsigned i;
int r;
DBusError error;
+ char **name;
assert(bus);
dbus_error_init(&error);
- if (n <= 1)
- return daemon_reload(bus, args, n);
+ if (strv_length(args) <= 1)
+ return daemon_reload(bus, args);
- for (i = 1; i < n; i++) {
+ STRV_FOREACH(name, args+1) {
if (!(m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
@@ -3028,7 +3187,7 @@ static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
}
if (!dbus_message_append_args(m,
- DBUS_TYPE_STRING, args + i,
+ DBUS_TYPE_STRING, name,
DBUS_TYPE_INVALID)) {
log_error("Could not append arguments to message.");
r = -ENOMEM;
@@ -3060,7 +3219,7 @@ finish:
return r;
}
-static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
+static int show_enviroment(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
DBusMessageIter iter, sub, sub2;
@@ -3144,13 +3303,13 @@ finish:
return r;
}
-static int set_environment(DBusConnection *bus, char **args, unsigned n) {
+static int set_environment(DBusConnection *bus, char **args) {
DBusMessage *m = NULL, *reply = NULL;
DBusError error;
int r;
const char *method;
DBusMessageIter iter, sub;
- unsigned i;
+ char **name;
dbus_error_init(&error);
@@ -3176,8 +3335,8 @@ static int set_environment(DBusConnection *bus, char **args, unsigned n) {
goto finish;
}
- for (i = 1; i < n; i++)
- if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
+ STRV_FOREACH(name, args+1)
+ if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
log_error("Could not append arguments to message.");
r = -ENOMEM;
goto finish;
@@ -3209,801 +3368,479 @@ finish:
return r;
}
-typedef struct {
- char *name;
- char *path;
-
- char **aliases;
- char **wanted_by;
-} InstallInfo;
-
-static Hashmap *will_install = NULL, *have_installed = NULL;
-static Set *remove_symlinks_to = NULL;
-static unsigned n_symlinks = 0;
-
-static void install_info_free(InstallInfo *i) {
- assert(i);
-
- free(i->name);
- free(i->path);
- strv_free(i->aliases);
- strv_free(i->wanted_by);
- free(i);
-}
-
-static void install_info_hashmap_free(Hashmap *m) {
- InstallInfo *i;
-
- while ((i = hashmap_steal_first(m)))
- install_info_free(i);
-
- hashmap_free(m);
-}
-
-static int install_info_add(const char *name) {
- InstallInfo *i;
- int r;
-
- assert(will_install);
+static int enable_sysv_units(char **args) {
+ int r = 0;
- if (!unit_name_is_valid_no_type(name, true)) {
- log_warning("Unit name %s is not a valid unit name.", name);
- return -EINVAL;
- }
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX))
+ const char *verb = args[0];
+ unsigned f = 1, t = 1;
+ LookupPaths paths;
- if (hashmap_get(have_installed, name) ||
- hashmap_get(will_install, name))
+ if (arg_scope != UNIT_FILE_SYSTEM)
return 0;
- if (!(i = new0(InstallInfo, 1))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if (!(i->name = strdup(name))) {
- r = -ENOMEM;
- goto fail;
- }
-
- if ((r = hashmap_put(will_install, i->name, i)) < 0)
- goto fail;
-
- return 0;
-
-fail:
- if (i)
- install_info_free(i);
-
- return r;
-}
-
-static int config_parse_also(
- const char *filename,
- unsigned line,
- const char *section,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char *w;
- size_t l;
- char *state;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- FOREACH_WORD_QUOTED(w, l, rvalue, state) {
- char *n;
- int r;
-
- if (!(n = strndup(w, l)))
- return -ENOMEM;
-
- if ((r = install_info_add(n)) < 0) {
- log_warning("Cannot install unit %s: %s", n, strerror(-r));
- free(n);
- return r;
- }
-
- free(n);
- }
-
- return 0;
-}
-
-static int mark_symlink_for_removal(const char *p) {
- char *n;
- int r;
-
- assert(p);
- assert(path_is_absolute(p));
-
- if (!remove_symlinks_to)
+ if (!streq(verb, "enable") &&
+ !streq(verb, "disable") &&
+ !streq(verb, "is-enabled"))
return 0;
- if (!(n = strdup(p)))
- return -ENOMEM;
-
- path_kill_slashes(n);
-
- if ((r = set_put(remove_symlinks_to, n)) < 0) {
- free(n);
- return r == -EEXIST ? 0 : r;
- }
-
- return 0;
-}
-
-static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
- int r = 0;
- DIR *d;
- struct dirent *de;
+ /* Processes all SysV units, and reshuffles the array so that
+ * afterwards only the native units remain */
- assert(fd >= 0);
- assert(root);
- assert(deleted);
+ zero(paths);
+ r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
+ if (r < 0)
+ return r;
- if (!(d = fdopendir(fd))) {
- close_nointr_nofail(fd);
- return -errno;
- }
+ r = 0;
- rewinddir(d);
+ for (f = 1; args[f]; f++) {
+ const char *name;
+ char *p;
+ bool found_native = false, found_sysv;
+ unsigned c = 1;
+ const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
+ char **k, *l, *q = NULL;
+ int j;
+ pid_t pid;
+ siginfo_t status;
- while ((de = readdir(d))) {
- bool is_dir = false, is_link = false;
+ name = args[f];
- if (ignore_file(de->d_name))
+ if (!endswith(name, ".service"))
continue;
- if (de->d_type == DT_LNK)
- is_link = true;
- else if (de->d_type == DT_DIR)
- is_dir = true;
- else if (de->d_type == DT_UNKNOWN) {
- struct stat st;
-
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- log_error("Failed to stat %s/%s: %m", root, de->d_name);
-
- if (r == 0)
- r = -errno;
- continue;
- }
-
- is_link = S_ISLNK(st.st_mode);
- is_dir = S_ISDIR(st.st_mode);
- } else
+ if (path_is_absolute(name))
continue;
- if (is_dir) {
- int nfd, q;
- char *p;
-
- if ((nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0) {
- log_error("Failed to open %s/%s: %m", root, de->d_name);
+ STRV_FOREACH(k, paths.unit_path) {
+ p = NULL;
- if (r == 0)
- r = -errno;
- continue;
- }
+ if (!isempty(arg_root))
+ asprintf(&p, "%s/%s/%s", arg_root, *k, name);
+ else
+ asprintf(&p, "%s/%s", *k, name);
- if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
- log_error("Failed to allocate directory string.");
- close_nointr_nofail(nfd);
+ if (!p) {
+ log_error("No memory");
r = -ENOMEM;
- break;
+ goto finish;
}
- /* This will close nfd, regardless whether it succeeds or not */
- q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
+ found_native = access(p, F_OK) >= 0;
free(p);
- if (r == 0)
- r = q;
-
- } else if (is_link) {
- char *p, *dest, *c;
- int q;
-
- if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
- log_error("Failed to allocate symlink string.");
- r = -ENOMEM;
+ if (found_native)
break;
- }
-
- if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
- log_error("Cannot read symlink %s: %s", p, strerror(-q));
- free(p);
-
- if (r == 0)
- r = q;
- continue;
- }
-
- if ((c = canonicalize_file_name(dest))) {
- /* This might fail if the destination
- * is already removed */
-
- free(dest);
- dest = c;
- }
-
- path_kill_slashes(dest);
- if (set_get(remove_symlinks_to, dest)) {
-
- if (!arg_quiet)
- log_info("rm '%s'", p);
-
- if (unlink(p) < 0) {
- log_error("Cannot unlink symlink %s: %m", p);
+ }
- if (r == 0)
- r = -errno;
- } else {
- rmdir_parents(p, config_path);
- path_kill_slashes(p);
+ if (found_native)
+ continue;
- if (!set_get(remove_symlinks_to, p)) {
+ p = NULL;
+ if (!isempty(arg_root))
+ asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
+ else
+ asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
+ if (!p) {
+ log_error("No memory");
+ r = -ENOMEM;
+ goto finish;
+ }
- if ((r = mark_symlink_for_removal(p)) < 0) {
- if (r == 0)
- r = q;
- } else
- *deleted = true;
- }
- }
- }
+ p[strlen(p) - sizeof(".service") + 1] = 0;
+ found_sysv = access(p, F_OK) >= 0;
+ if (!found_sysv) {
free(p);
- free(dest);
+ continue;
}
- }
- closedir(d);
+ /* Mark this entry, so that we don't try enabling it as native unit */
+ args[f] = (char*) "";
- return r;
-}
+ log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
-static int remove_marked_symlinks(const char *config_path) {
- int fd, r = 0;
- bool deleted;
+ if (!isempty(arg_root))
+ argv[c++] = q = strappend("--root=", arg_root);
- assert(config_path);
-
- if (set_size(remove_symlinks_to) <= 0)
- return 0;
+ argv[c++] = file_name_from_path(p);
+ argv[c++] =
+ streq(verb, "enable") ? "on" :
+ streq(verb, "disable") ? "off" : "--level=5";
+ argv[c] = NULL;
- if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
- return -errno;
+ l = strv_join((char**)argv, " ");
+ if (!l) {
+ log_error("No memory.");
+ free(q);
+ free(p);
+ r = -ENOMEM;
+ goto finish;
+ }
- do {
- int q, cfd;
- deleted = false;
+ log_info("Executing %s", l);
+ free(l);
- if ((cfd = dup(fd)) < 0) {
+ pid = fork();
+ if (pid < 0) {
+ log_error("Failed to fork: %m");
+ free(p);
+ free(q);
r = -errno;
- break;
- }
+ goto finish;
+ } else if (pid == 0) {
+ /* Child */
- /* This takes possession of cfd and closes it */
- if ((q = remove_marked_symlinks_fd(cfd, config_path, config_path, &deleted)) < 0) {
- if (r == 0)
- r = q;
+ execv(argv[0], (char**) argv);
+ _exit(EXIT_FAILURE);
}
- } while (deleted);
-
- close_nointr_nofail(fd);
-
- return r;
-}
-
-static int create_symlink(const char *verb, const char *orig_old_path, const char *new_path) {
- int r;
- const char *old_path;
- if (arg_root)
- old_path = orig_old_path+strlen(arg_root);
- else
- old_path = orig_old_path;
-
- assert(old_path);
- assert(new_path);
- assert(verb);
-
- if (streq(verb, "enable")) {
- char *dest;
-
- mkdir_parents(new_path, 0755);
-
- if (symlink(old_path, new_path) >= 0) {
-
- if (!arg_quiet)
- log_info("ln -s '%s' '%s'", old_path, new_path);
-
- return 0;
- }
+ free(p);
+ free(q);
- if (errno != EEXIST) {
- log_error("Cannot link %s to %s: %m", old_path, new_path);
- return -errno;
+ j = wait_for_terminate(pid, &status);
+ if (j < 0) {
+ log_error("Failed to wait for child: %s", strerror(-r));
+ r = j;
+ goto finish;
}
- if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
+ if (status.si_code == CLD_EXITED) {
+ if (streq(verb, "is-enabled")) {
+ if (status.si_status == 0) {
+ if (!arg_quiet)
+ puts("enabled");
+ r = 1;
+ } else {
+ if (!arg_quiet)
+ puts("disabled");
+ }
- if (errno == EINVAL) {
- log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
- return -EEXIST;
+ } else if (status.si_status != 0) {
+ r = -EINVAL;
+ goto finish;
}
-
- log_error("readlink() failed: %s", strerror(-r));
- return r;
- }
-
- if (streq(dest, old_path)) {
- free(dest);
- return 0;
- }
-
- if (!arg_force) {
- log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
- free(dest);
- return -EEXIST;
+ } else {
+ r = -EPROTO;
+ goto finish;
}
+ }
- free(dest);
- unlink(new_path);
-
- if (!arg_quiet)
- log_info("ln -s '%s' '%s'", old_path, new_path);
-
- if (symlink(old_path, new_path) >= 0)
- return 0;
-
- log_error("Cannot link %s to %s: %m", old_path, new_path);
- return -errno;
-
- } else if (streq(verb, "disable")) {
- char *dest;
-
- if ((r = mark_symlink_for_removal(old_path)) < 0)
- return r;
-
- if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
- if (errno == ENOENT)
- return 0;
+finish:
+ lookup_paths_free(&paths);
- if (errno == EINVAL) {
- log_warning("File %s not a symlink, ignoring.", old_path);
- return 0;
- }
+ /* Drop all SysV units */
+ for (f = 1, t = 1; args[f]; f++) {
- log_error("readlink() failed: %s", strerror(-r));
- return r;
- }
-
- if (!streq(dest, old_path)) {
- log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
- free(dest);
- return 0;
- }
-
- free(dest);
+ if (isempty(args[f]))
+ continue;
- if ((r = mark_symlink_for_removal(new_path)) < 0)
- return r;
+ args[t++] = args[f];
+ }
- if (!arg_quiet)
- log_info("rm '%s'", new_path);
+ args[t] = NULL;
- if (unlink(new_path) >= 0)
- return 0;
+#endif
+ return r;
+}
- log_error("Cannot unlink %s: %m", new_path);
- return -errno;
+static int enable_unit(DBusConnection *bus, char **args) {
+ const char *verb = args[0];
+ UnitFileChange *changes = NULL;
+ unsigned n_changes = 0, i;
+ int carries_install_info = -1;
+ DBusMessage *m = NULL, *reply = NULL;
+ int r;
+ DBusError error;
- } else if (streq(verb, "is-enabled")) {
- char *dest;
+ dbus_error_init(&error);
- if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
+ r = enable_sysv_units(args);
+ if (r < 0)
+ return r;
- if (errno == ENOENT || errno == EINVAL)
- return 0;
+ if (!bus || avoid_bus()) {
+ if (streq(verb, "enable")) {
+ r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(verb, "disable"))
+ r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+ else if (streq(verb, "reenable")) {
+ r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(verb, "link"))
+ r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ else if (streq(verb, "preset")) {
+ r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ carries_install_info = r;
+ } else if (streq(verb, "mask"))
+ r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+ else if (streq(verb, "unmask"))
+ r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+ else
+ assert_not_reached("Unknown verb");
- log_error("readlink() failed: %s", strerror(-r));
- return r;
+ if (r < 0) {
+ log_error("Operation failed: %s", strerror(-r));
+ goto finish;
}
- if (streq(dest, old_path)) {
- free(dest);
- return 1;
+ for (i = 0; i < n_changes; i++) {
+ if (changes[i].type == UNIT_FILE_SYMLINK)
+ log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+ else
+ log_info("rm '%s'", changes[i].path);
}
- free(dest);
- return 0;
- }
-
- assert_not_reached("Unknown action.");
-}
-
-static int install_info_symlink_alias(const char *verb, InstallInfo *i, const char *config_path) {
- char **s;
- char *alias_path = NULL;
- int r;
-
- assert(verb);
- assert(i);
- assert(config_path);
-
- STRV_FOREACH(s, i->aliases) {
-
- free(alias_path);
- if (!(alias_path = path_make_absolute(*s, config_path))) {
+ } else {
+ const char *method;
+ bool send_force = true, expect_carries_install_info = false;
+ dbus_bool_t a, b;
+ DBusMessageIter iter, sub, sub2;
+
+ if (streq(verb, "enable")) {
+ method = "EnableUnitFiles";
+ expect_carries_install_info = true;
+ } else if (streq(verb, "disable")) {
+ method = "DisableUnitFiles";
+ send_force = false;
+ } else if (streq(verb, "reenable")) {
+ method = "ReenableUnitFiles";
+ expect_carries_install_info = true;
+ } else if (streq(verb, "link"))
+ method = "LinkUnitFiles";
+ else if (streq(verb, "preset")) {
+ method = "PresetUnitFiles";
+ expect_carries_install_info = true;
+ } else if (streq(verb, "mask"))
+ method = "MaskUnitFiles";
+ else if (streq(verb, "unmask")) {
+ method = "UnmaskUnitFiles";
+ send_force = false;
+ } else
+ assert_not_reached("Unknown verb");
+
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method);
+ if (!m) {
log_error("Out of memory");
r = -ENOMEM;
goto finish;
}
- if ((r = create_symlink(verb, i->path, alias_path)) != 0)
- goto finish;
-
- if (streq(verb, "disable"))
- rmdir_parents(alias_path, config_path);
- }
- r = 0;
-
-finish:
- free(alias_path);
-
- return r;
-}
+ dbus_message_iter_init_append(m, &iter);
-static int install_info_symlink_wants(const char *verb, InstallInfo *i, const char *config_path) {
- char **s;
- char *alias_path = NULL;
- int r;
-
- assert(verb);
- assert(i);
- assert(config_path);
-
- STRV_FOREACH(s, i->wanted_by) {
- if (!unit_name_is_valid_no_type(*s, true)) {
- log_error("Invalid name %s.", *s);
- r = -EINVAL;
+ r = bus_append_strv_iter(&iter, args+1);
+ if (r < 0) {
+ log_error("Failed to append unit files.");
goto finish;
}
- free(alias_path);
- alias_path = NULL;
-
- if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
- log_error("Out of memory");
+ a = arg_runtime;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
+ log_error("Failed to append runtime boolean.");
r = -ENOMEM;
goto finish;
}
- if ((r = create_symlink(verb, i->path, alias_path)) != 0)
- goto finish;
-
- if (streq(verb, "disable"))
- rmdir_parents(alias_path, config_path);
- }
-
- r = 0;
-
-finish:
- free(alias_path);
-
- return r;
-}
+ if (send_force) {
+ b = arg_force;
-static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo *i, const char *config_path) {
-
- const ConfigItem items[] = {
- { "Alias", config_parse_strv, 0, &i->aliases, "Install" },
- { "WantedBy", config_parse_strv, 0, &i->wanted_by, "Install" },
- { "Also", config_parse_also, 0, NULL, "Install" },
-
- { NULL, NULL, 0, NULL, NULL }
- };
-
- char **p;
- char *filename = NULL;
- FILE *f = NULL;
- int r;
-
- assert(paths);
- assert(i);
-
- STRV_FOREACH(p, paths->unit_path) {
- int fd;
- char *path, *should_free;
-
- if (arg_root)
- should_free = path = strappend(arg_root, *p);
- else {
- should_free = NULL;
- path = *p;
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
+ log_error("Failed to append force boolean.");
+ r = -ENOMEM;
+ goto finish;
+ }
}
- if (!(filename = path_make_absolute(i->name, path))) {
- log_error("Out of memory");
- return -ENOMEM;
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
}
- if (should_free)
- free(should_free);
+ if (!dbus_message_iter_init(reply, &iter)) {
+ log_error("Failed to initialize iterator.");
+ goto finish;
+ }
- /* Ensure that we don't follow symlinks */
- if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
- if ((f = fdopen(fd, "re")))
- break;
+ if (expect_carries_install_info) {
+ r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
+ if (r < 0) {
+ log_error("Failed to parse reply.");
+ goto finish;
+ }
- if (errno == ELOOP) {
- log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
- free(filename);
- return -errno;
+ carries_install_info = b;
}
- if (errno != ENOENT) {
- log_error("Failed to open %s: %m", filename);
- free(filename);
- return -errno;
+ if (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.");
+ r = -EIO;
+ goto finish;
}
- free(filename);
- filename = NULL;
- }
-
- if (!f) {
-#if (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX)) && defined (HAVE_SYSV_COMPAT)
-
- if (endswith(i->name, ".service")) {
- char *sysv;
- bool exists;
+ dbus_message_iter_recurse(&iter, &sub);
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ const char *type, *path, *source;
- if (asprintf(&sysv, SYSTEM_SYSVINIT_PATH "/%s", i->name) < 0) {
- log_error("Out of memory");
- return -ENOMEM;
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
}
- sysv[strlen(sysv) - sizeof(".service") + 1] = 0;
- if (arg_root) {
- char *tmp_path;
-
- tmp_path = strappend (arg_root, sysv);
- exists = access (tmp_path, F_OK) >= 0;
- free (tmp_path);
- } else
- exists = access(sysv, F_OK) >= 0;
-
- if (exists) {
- pid_t pid;
- siginfo_t status;
-
- const char *argv[] = {
- "/sbin/chkconfig",
- NULL,
- NULL,
- NULL,
- NULL
- };
-
- log_info("%s is not a native service, redirecting to /sbin/chkconfig.", i->name);
-
- argv[1] = file_name_from_path(sysv);
- argv[2] =
- streq(verb, "enable") ? "on" :
- streq(verb, "disable") ? "off" : "--level=5";
- if (arg_root)
- argv[3] = strappend("--root=", arg_root);
-
- log_info("Executing %s %s %s %s", argv[0], argv[1], strempty(argv[2]), strempty(argv[3]));
-
- if ((pid = fork()) < 0) {
- log_error("Failed to fork: %m");
- free(sysv);
- return -errno;
- } else if (pid == 0) {
- execv(argv[0], (char**) argv);
- _exit(EXIT_FAILURE);
- }
-
- free(sysv);
-
- if ((r = wait_for_terminate(pid, &status)) < 0)
- return r;
-
- if (status.si_code == CLD_EXITED) {
+ dbus_message_iter_recurse(&sub, &sub2);
- if (streq(verb, "is-enabled"))
- return status.si_status == 0 ? 1 : 0;
-
- if (status.si_status == 0)
- n_symlinks ++;
-
- return status.si_status == 0 ? 0 : -EINVAL;
-
- } else
- return -EPROTO;
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+ bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
+ log_error("Failed to parse reply.");
+ r = -EIO;
+ goto finish;
}
- free(sysv);
- }
-
-#endif
-
- log_error("Couldn't find %s.", i->name);
- return -ENOENT;
- }
-
- i->path = filename;
+ if (streq(type, "symlink"))
+ log_info("ln -s '%s' '%s'", source, path);
+ else
+ log_info("rm '%s'", path);
- if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
- fclose(f);
- return r;
- }
+ dbus_message_iter_next(&sub);
+ }
- /* Consider unit files stored in /lib and /usr always enabled
- * if they have no [Install] data. */
- if (streq(verb, "is-enabled") &&
- strv_isempty(i->aliases) &&
- strv_isempty(i->wanted_by) &&
- !path_startswith(filename, "/etc")) {
- fclose(f);
- return 1;
+ /* Try to reload if enabeld */
+ if (!arg_no_reload)
+ r = daemon_reload(bus, args);
}
- n_symlinks += strv_length(i->aliases);
- n_symlinks += strv_length(i->wanted_by);
+ if (carries_install_info == 0)
+ log_warning("Warning: unit files do not carry install information. No operation executed.");
- fclose(f);
-
- if ((r = install_info_symlink_alias(verb, i, config_path)) != 0)
- return r;
-
- if ((r = install_info_symlink_wants(verb, i, config_path)) != 0)
- return r;
-
- if ((r = mark_symlink_for_removal(filename)) < 0)
- return r;
-
- if ((r = remove_marked_symlinks(config_path)) < 0)
- return r;
-
- return 0;
-}
-
-static char *get_config_path(void) {
- char *ret;
+finish:
+ if (m)
+ dbus_message_unref(m);
- if (arg_user && arg_global)
- ret = strdup(USER_CONFIG_UNIT_PATH);
+ if (reply)
+ dbus_message_unref(reply);
- if (arg_user) {
- if (user_config_home(&ret) < 0)
- return NULL;
- }
+ unit_file_changes_free(changes, n_changes);
- ret = strdup(SYSTEM_CONFIG_UNIT_PATH);
- if (arg_root) {
- char *p;
- p = strappend (arg_root, ret);
- free (ret);
- return p;
- } else
- return ret;
+ dbus_error_free(&error);
+ return r;
}
-static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
+static int unit_is_enabled(DBusConnection *bus, char **args) {
DBusError error;
int r;
- LookupPaths paths;
- char *config_path = NULL;
- unsigned j;
- InstallInfo *i;
- const char *verb = args[0];
+ DBusMessage *m = NULL, *reply = NULL;
+ bool enabled;
+ char **name;
dbus_error_init(&error);
- zero(paths);
- if ((r = lookup_paths_init(&paths, arg_user ? MANAGER_USER : MANAGER_SYSTEM, true)) < 0) {
- log_error("Failed to determine lookup paths: %s", strerror(-r));
- goto finish;
- }
+ r = enable_sysv_units(args);
+ if (r < 0)
+ return r;
- if (!(config_path = get_config_path())) {
- log_error("Failed to determine config path");
- r = -ENOMEM;
- goto finish;
- }
+ enabled = r > 0;
- will_install = hashmap_new(string_hash_func, string_compare_func);
- have_installed = hashmap_new(string_hash_func, string_compare_func);
+ if (!bus || avoid_bus()) {
- if (!will_install || !have_installed) {
- log_error("Failed to allocate unit sets.");
- r = -ENOMEM;
- goto finish;
- }
+ STRV_FOREACH(name, args+1) {
+ UnitFileState state;
- if (!arg_defaults && streq(verb, "disable"))
- if (!(remove_symlinks_to = set_new(string_hash_func, string_compare_func))) {
- log_error("Failed to allocate symlink sets.");
- r = -ENOMEM;
- goto finish;
- }
+ state = unit_file_get_state(arg_scope, arg_root, *name);
+ if (state < 0) {
+ r = state;
+ goto finish;
+ }
- for (j = 1; j < n; j++)
- if ((r = install_info_add(args[j])) < 0) {
- log_warning("Cannot install unit %s: %s", args[j], strerror(-r));
- goto finish;
+ if (state == UNIT_FILE_ENABLED ||
+ state == UNIT_FILE_ENABLED_RUNTIME ||
+ state == UNIT_FILE_STATIC)
+ enabled = true;
+
+ if (!arg_quiet)
+ puts(unit_file_state_to_string(state));
}
- r = 0;
+ } else {
+ STRV_FOREACH(name, args+1) {
+ const char *s;
- while ((i = hashmap_first(will_install))) {
- int q;
+ m = dbus_message_new_method_call(
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitFileState");
+ if (!m) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
- assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
+ if (!dbus_message_append_args(m,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_INVALID)) {
+ log_error("Could not append arguments to message.");
+ r = -ENOMEM;
+ goto finish;
+ }
- if ((q = install_info_apply(verb, &paths, i, config_path)) != 0) {
+ reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+ if (!reply) {
+ log_error("Failed to issue method call: %s", bus_error_message(&error));
+ r = -EIO;
+ goto finish;
+ }
- if (q < 0) {
- if (r == 0)
- r = q;
+ if (!dbus_message_get_args(reply, &error,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID)) {
+ log_error("Failed to parse reply: %s", bus_error_message(&error));
+ r = -EIO;
goto finish;
}
- /* In test mode and found something */
- r = 1;
- break;
- }
- }
+ dbus_message_unref(m);
+ dbus_message_unref(reply);
+ m = reply = NULL;
- if (streq(verb, "is-enabled"))
- r = r > 0 ? 0 : -ENOENT;
- else {
- if (n_symlinks <= 0)
- log_warning("Unit files contain no applicable installation information. Ignoring.");
-
- if (bus &&
- /* Don't try to reload anything if the user asked us to not do this */
- !arg_no_reload &&
- /* Don't try to reload anything when updating a unit globally */
- !arg_global &&
- /* Don't try to reload anything if we are called for system changes but the system wasn't booted with systemd */
- (arg_user || sd_booted() > 0) &&
- /* Don't try to reload anything if we are running in a chroot environment */
- (arg_user || running_in_chroot() <= 0 || arg_root) ) {
- int q;
-
- if ((q = daemon_reload(bus, args, n)) < 0)
- r = q;
+ if (streq(s, "enabled") ||
+ streq(s, "enabled-runtime") ||
+ streq(s, "static"))
+ enabled = true;
+
+ if (!arg_quiet)
+ puts(s);
}
}
-finish:
- install_info_hashmap_free(will_install);
- install_info_hashmap_free(have_installed);
+ r = enabled ? 0 : 1;
- set_free_free(remove_symlinks_to);
-
- lookup_paths_free(&paths);
+finish:
+ if (m)
+ dbus_message_unref(m);
- free(config_path);
+ if (reply)
+ dbus_message_unref(reply);
+ dbus_error_free(&error);
return r;
}
static int systemctl_help(void) {
+ pager_open_if_enabled();
+
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
- "Send control commands to or query the systemd manager.\n\n"
+ "Query or send control commands to the systemd manager.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -t --type=TYPE List only units of a particular type\n"
@@ -4030,15 +3867,15 @@ static int systemctl_help(void) {
" 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"
- " --root=path Use <root> as the root file system\n"
" --system Connect to system manager\n"
" --user Connect to user service manager\n"
" --global Enable/disable unit files globally\n"
" -f --force When enabling unit files, override existing symlinks\n"
" When shutting down, execute action immediately\n"
- " --defaults When disabling unit files, remove default symlinks only\n\n"
+ " --root=PATH Enable unit files in the specified root directory\n"
+ " --runtime Enable unit files only temporarily until next reboot\n\n"
"Unit Commands:\n"
- " list-units List units\n"
+ " list-units List loaded units\n"
" start [NAME...] Start (activate) one or more units\n"
" stop [NAME...] Stop (deactivate) one or more units\n"
" reload [NAME...] Reload one or more units\n"
@@ -4058,8 +3895,16 @@ static int systemctl_help(void) {
" units\n"
" load [NAME...] Load one or more units\n\n"
"Unit File Commands:\n"
+ " list-unit-files List installed unit files\n"
" enable [NAME...] Enable one or more unit files\n"
" disable [NAME...] Disable one or more unit files\n"
+ " reenable [NAME...] Reenable one or more unit files\n"
+ " preset [NAME...] Enable/disable one or more unit files\n"
+ " based on preset configuration\n"
+ " mask [NAME...] Mask one or more units\n"
+ " unmask [NAME...] Unmask one or more units\n"
+ " link [PATH...] Link one or more units files into\n"
+ " the search path\n"
" is-enabled [NAME...] Check whether unit files are enabled\n\n"
"Job Commands:\n"
" list-jobs List jobs\n"
@@ -4174,11 +4019,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_ROOT,
ARG_FULL,
ARG_NO_RELOAD,
- ARG_DEFAULTS,
ARG_KILL_MODE,
ARG_KILL_WHO,
ARG_NO_ASK_PASSWORD,
- ARG_FAILED
+ ARG_FAILED,
+ ARG_RUNTIME
};
static const struct option options[] = {
@@ -4203,13 +4048,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "root", required_argument, NULL, ARG_ROOT },
{ "force", no_argument, NULL, 'f' },
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
- { "defaults", no_argument, NULL, ARG_DEFAULTS },
{ "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
{ "kill-who", required_argument, NULL, ARG_KILL_WHO },
{ "signal", required_argument, NULL, 's' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "host", required_argument, NULL, 'H' },
{ "privileged",no_argument, NULL, 'P' },
+ { "runtime", no_argument, NULL, ARG_RUNTIME },
{ NULL, 0, NULL, 0 }
};
@@ -4268,11 +4113,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
break;
case ARG_USER:
- arg_user = true;
+ arg_scope = UNIT_FILE_USER;
break;
case ARG_SYSTEM:
- arg_user = false;
+ arg_scope = UNIT_FILE_SYSTEM;
+ break;
+
+ case ARG_GLOBAL:
+ arg_scope = UNIT_FILE_GLOBAL;
break;
case ARG_NO_BLOCK:
@@ -4319,15 +4168,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_no_reload = true;
break;
- case ARG_GLOBAL:
- arg_global = true;
- arg_user = true;
- break;
-
- case ARG_DEFAULTS:
- arg_defaults = true;
- break;
-
case ARG_KILL_WHO:
arg_kill_who = optarg;
break;
@@ -4356,6 +4196,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_host = optarg;
break;
+ case ARG_RUNTIME:
+ arg_runtime = true;
+ break;
+
case '?':
return -EINVAL;
@@ -4365,7 +4209,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
}
}
- if (arg_transport != TRANSPORT_NORMAL && arg_user) {
+ if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
log_error("Cannot access user instance remotely.");
return -EINVAL;
}
@@ -4955,9 +4799,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
EQUAL
} argc_cmp;
const int argc;
- int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
+ int (* const dispatch)(DBusConnection *bus, char **args);
} verbs[] = {
{ "list-units", LESS, 1, list_units },
+ { "list-unit-files", EQUAL, 1, list_unit_files },
{ "list-jobs", EQUAL, 1, list_jobs },
{ "clear-jobs", EQUAL, 1, daemon_reload },
{ "load", MORE, 2, load_unit },
@@ -4999,7 +4844,12 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
{ "reset-failed", MORE, 1, reset_failed },
{ "enable", MORE, 2, enable_unit },
{ "disable", MORE, 2, enable_unit },
- { "is-enabled", MORE, 2, enable_unit }
+ { "is-enabled", MORE, 2, unit_is_enabled },
+ { "reenable", MORE, 2, enable_unit },
+ { "preset", MORE, 2, enable_unit },
+ { "mask", MORE, 2, enable_unit },
+ { "unmask", MORE, 2, enable_unit },
+ { "link", MORE, 2, enable_unit }
};
int left;
@@ -5062,20 +4912,27 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
/* Require a bus connection for all operations but
* enable/disable */
- if (!streq(verbs[i].verb, "enable") && !streq(verbs[i].verb, "disable")) {
+ if (!streq(verbs[i].verb, "enable") &&
+ !streq(verbs[i].verb, "disable") &&
+ !streq(verbs[i].verb, "is-enable") &&
+ !streq(verbs[i].verb, "reenable") &&
+ !streq(verbs[i].verb, "preset") &&
+ !streq(verbs[i].verb, "mask") &&
+ !streq(verbs[i].verb, "unmask") &&
+ !streq(verbs[i].verb, "link")) {
if (running_in_chroot() > 0) {
log_info("Running in chroot, ignoring request.");
return 0;
}
- if (!bus) {
+ if (!bus && !avoid_bus()) {
log_error("Failed to get D-Bus connection: %s", error->message);
return -EIO;
}
}
- return verbs[i].dispatch(bus, argv + optind, left);
+ return verbs[i].dispatch(bus, argv + optind);
}
static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
@@ -5126,7 +4983,7 @@ static int reload_with_fallback(DBusConnection *bus) {
if (bus) {
/* First, try systemd via D-Bus. */
- if (daemon_reload(bus, NULL, 0) > 0)
+ if (daemon_reload(bus, NULL) > 0)
return 0;
}
@@ -5145,7 +5002,7 @@ static int start_with_fallback(DBusConnection *bus) {
if (bus) {
/* First, try systemd via D-Bus. */
- if (start_unit(bus, NULL, 0) >= 0)
+ if (start_unit(bus, NULL) >= 0)
goto done;
}
@@ -5246,8 +5103,9 @@ static int halt_main(DBusConnection *bus) {
static int runlevel_main(void) {
int r, runlevel, previous;
- if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
- printf("unknown\n");
+ r = utmp_get_runlevel(&runlevel, &previous);
+ if (r < 0) {
+ puts("unknown");
return r;
}
@@ -5258,19 +5116,6 @@ static int runlevel_main(void) {
return 0;
}
-static void agent_close(void) {
- siginfo_t dummy;
-
- if (agent_pid <= 0)
- return;
-
- /* Inform agent that we are done */
- kill(agent_pid, SIGTERM);
- kill(agent_pid, SIGCONT);
- wait_for_terminate(agent_pid, &dummy);
- agent_pid = 0;
-}
-
int main(int argc, char*argv[]) {
int r, retval = EXIT_FAILURE;
DBusConnection *bus = NULL;
@@ -5302,16 +5147,18 @@ int main(int argc, char*argv[]) {
goto finish;
}
- if (arg_transport == TRANSPORT_NORMAL)
- bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
- else if (arg_transport == TRANSPORT_POLKIT) {
- bus_connect_system_polkit(&bus, &error);
- private_bus = false;
- } else if (arg_transport == TRANSPORT_SSH) {
- bus_connect_system_ssh(NULL, arg_host, &bus, &error);
- private_bus = false;
- } else
- assert_not_reached("Uh, invalid transport...");
+ if (!avoid_bus()) {
+ if (arg_transport == TRANSPORT_NORMAL)
+ bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
+ else if (arg_transport == TRANSPORT_POLKIT) {
+ bus_connect_system_polkit(&bus, &error);
+ private_bus = false;
+ } else if (arg_transport == TRANSPORT_SSH) {
+ bus_connect_system_ssh(NULL, arg_host, &bus, &error);
+ private_bus = false;
+ } else
+ assert_not_reached("Uh, invalid transport...");
+ }
switch (arg_action) {
@@ -5354,7 +5201,6 @@ int main(int argc, char*argv[]) {
retval = r < 0 ? EXIT_FAILURE : r;
finish:
-
if (bus) {
dbus_connection_flush(bus);
dbus_connection_close(bus);