systemctl: hook up new install logic

This adds a number of new options to systemctl, for presets, reenabling,
masking/unmask, and runtime operations.
This commit is contained in:
Lennart Poettering 2011-07-25 04:58:02 +02:00
parent 4b7a6af440
commit 729e3769c3
11 changed files with 1099 additions and 1006 deletions

View file

@ -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) \

13
TODO
View file

@ -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)

View file

@ -351,27 +351,31 @@
immediate reboot.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--defaults</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>
</varlistentry>
<varlistentry>
<term><option>--root=</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), use alternative
root path when looking for unit
files.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--runtime</option></term>
<listitem><para>When used with
<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>

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -62,6 +62,14 @@
send_interface="org.freedesktop.systemd1.Manager"
send_member="ListUnits"/>
<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"/>

View file

@ -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();

120
src/spawn-agent.c Normal file
View file

@ -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;
}

28
src/spawn-agent.h Normal file
View file

@ -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

File diff suppressed because it is too large Load diff