core: introduce new RuntimeDirectory= and RuntimeDirectoryMode= unit settings

As discussed on the ML these are useful to manage runtime directories
below /run for services.
This commit is contained in:
Lennart Poettering 2014-03-03 17:14:07 +01:00
parent b64a3d86bc
commit e66cf1a3f9
17 changed files with 201 additions and 4 deletions

View File

@ -1195,6 +1195,46 @@
kernel.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RuntimeDirectory=</varname></term>
<term><varname>RuntimeDirectoryMode=</varname></term>
<listitem><para>Takes a list of
directory names. If set one or more
directories by the specified names
will be created below
<filename>/run</filename> (for system
services) or below
<varname>$XDG_RUNTIME_DIR</varname>
(for user services) when the unit is
started and removed when the unit is
stopped. The directories will have the
access mode specified in
<varname>RuntimeDirectoryMode=</varname>,
and will be owned by the user and
group specified in
<varname>User=</varname> and
<varname>Group=</varname>. Use this to
manage one or more runtime directories
of the unit and bind their lifetime to
the daemon runtime. The specified
directory names must be relative, and
may not include a
<literal>/</literal>, i.e. must refer
to simple directories to create or
remove. This is particularly useful
for unpriviliges daemons that cannot
create runtime directories in
<filename>/run</filename> due to lack
of privileges, and to make sure the
runtime directory is cleaned up
automatically after use. For runtime
directories that require more complex
or different configuration or lifetime
guarantees, please consider using
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -1352,6 +1392,7 @@
<citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>exec</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -61,6 +61,23 @@
temporary files and directories which usually reside
in directories such as <filename>/run</filename>
or <filename>/tmp</filename>.</para>
<para>Volatile and temporary files and directories are
those located in <filename>/run</filename> (and its
alias <filename>/var/run</filename>),
<filename>/tmp</filename>,
<filename>/var/tmp</filename>, the API file systems
such as <filename>/sys</filename> or
<filename>/proc</filename>, as well as some other
directories below <filename>/var</filename>.</para>
<para>System daemons frequently require private
runtime directories below <filename>/run</filename> to
place communication sockets and similar in. For these
consider declaring them in their unit files using
<varname>RuntimeDirectory=</varname>
(see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details),
if this is feasible.</para>
</refsect1>
<refsect1>
@ -458,7 +475,8 @@ x /var/tmp/abrt/*</programlisting>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-tmpfiles</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-delta</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd-delta</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -635,6 +635,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SystemCallErrorNumber", "i", property_get_syscall_errno, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Personality", "s", property_get_personality, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, runtime_directory_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};

View File

@ -82,6 +82,7 @@
#include "selinux-util.h"
#include "errno-list.h"
#include "af-list.h"
#include "mkdir.h"
#include "apparmor-util.h"
#ifdef HAVE_SECCOMP
@ -1247,6 +1248,7 @@ int exec_spawn(ExecCommand *command,
bool confirm_spawn,
CGroupControllerMask cgroup_supported,
const char *cgroup_path,
const char *runtime_prefix,
const char *unit_id,
usec_t watchdog_usec,
int idle_pipe[4],
@ -1544,6 +1546,27 @@ int exec_spawn(ExecCommand *command,
}
#endif
if (!strv_isempty(context->runtime_directory) && runtime_prefix) {
char **rt;
STRV_FOREACH(rt, context->runtime_directory) {
_cleanup_free_ char *p;
p = strjoin(runtime_prefix, "/", *rt, NULL);
if (!p) {
r = EXIT_RUNTIME_DIRECTORY;
err = -ENOMEM;
goto fail_child;
}
err = mkdir_safe(p, context->runtime_directory_mode, uid, gid);
if (err < 0) {
r = EXIT_RUNTIME_DIRECTORY;
goto fail_child;
}
}
}
if (apply_permissions) {
err = enforce_groups(context, username, gid);
if (err < 0) {
@ -1840,6 +1863,7 @@ void exec_context_init(ExecContext *c) {
c->ignore_sigpipe = true;
c->timer_slack_nsec = (nsec_t) -1;
c->personality = 0xffffffffUL;
c->runtime_directory_mode = 0755;
}
void exec_context_done(ExecContext *c) {
@ -1918,6 +1942,33 @@ void exec_context_done(ExecContext *c) {
set_free(c->address_families);
c->address_families = NULL;
strv_free(c->runtime_directory);
c->runtime_directory = NULL;
}
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
char **i;
assert(c);
if (!runtime_prefix)
return 0;
STRV_FOREACH(i, c->runtime_directory) {
_cleanup_free_ char *p;
p = strjoin(runtime_prefix, "/", *i, NULL);
if (!p)
return -ENOMEM;
/* We execute this synchronously, since we need to be
* sure this is gone when we start the service
* next. */
rm_rf_dangerous(p, false, true, false);
}
return 0;
}
void exec_command_done(ExecCommand *c) {

View File

@ -177,6 +177,9 @@ struct ExecContext {
Set *address_families;
bool address_families_whitelist:1;
char **runtime_directory;
mode_t runtime_directory_mode;
bool oom_score_adjust_set:1;
bool nice_set:1;
bool ioprio_set:1;
@ -196,6 +199,7 @@ int exec_spawn(ExecCommand *command,
bool confirm_spawn,
CGroupControllerMask cgroup_mask,
const char *cgroup_path,
const char *runtime_prefix,
const char *unit_id,
usec_t watchdog_usec,
int pipe_fd[2],
@ -219,6 +223,8 @@ void exec_context_init(ExecContext *c);
void exec_context_done(ExecContext *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_root);
int exec_context_load_environment(const ExecContext *c, char ***l);
bool exec_context_may_touch_console(ExecContext *c);

View File

@ -82,6 +82,8 @@ $1.PrivateNetwork, config_parse_bool, 0,
$1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices)
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context)
$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality)
$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode)
$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory)
m4_ifdef(`HAVE_LIBWRAP',
`$1.TCPWrapName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.tcpwrap_name)',
`$1.TCPWrapName, config_parse_warn_compat, 0, 0')

View File

@ -2719,6 +2719,56 @@ int config_parse_personality(
return 0;
}
int config_parse_runtime_directory(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
char***rt = data, *w, *state;
size_t l;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
/* Empty assignment resets the list */
strv_free(*rt);
*rt = NULL;
return 0;
}
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
_cleanup_free_ char *n;
n = strndup(w, l);
if (!n)
return log_oom();
if (!filename_is_safe(n)) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue);
continue;
}
r = strv_push(rt, n);
if (r < 0)
return log_oom();
n = NULL;
}
return 0;
}
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {

View File

@ -91,6 +91,7 @@ int config_parse_exec_selinux_context(const char *unit, const char *filename, un
int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_address_families(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_runtime_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);

View File

@ -2851,3 +2851,10 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) {
return hashmap_get(m->units_requiring_mounts_for, streq(p, "/") ? "" : p);
}
const char *manager_get_runtime_prefix(Manager *m) {
return m->running_as == SYSTEMD_SYSTEM ?
"/run" :
getenv("XDG_RUNTIME_DIR");
}

View File

@ -318,3 +318,5 @@ void manager_status_printf(Manager *m, bool ephemeral, const char *status, const
void manager_flip_auto_status(Manager *m, bool enable);
Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
const char *manager_get_runtime_prefix(Manager *m);

View File

@ -788,6 +788,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
UNIT(m)->manager->confirm_spawn,
UNIT(m)->manager->cgroup_supported,
UNIT(m)->cgroup_path,
manager_get_runtime_prefix(UNIT(m)->manager),
UNIT(m)->id,
0,
NULL,
@ -820,6 +821,8 @@ static void mount_enter_dead(Mount *m, MountResult f) {
exec_runtime_destroy(m->exec_runtime);
m->exec_runtime = exec_runtime_unref(m->exec_runtime);
exec_context_destroy_runtime_directory(&m->exec_context, manager_get_runtime_prefix(UNIT(m)->manager));
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
}

View File

@ -1770,6 +1770,7 @@ static int service_spawn(
UNIT(s)->manager->confirm_spawn,
UNIT(s)->manager->cgroup_supported,
path,
manager_get_runtime_prefix(UNIT(s)->manager),
UNIT(s)->id,
s->watchdog_usec,
s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
@ -1871,10 +1872,13 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
s->forbid_restart = false;
/* we want fresh tmpdirs in case service is started again immediately */
/* We want fresh tmpdirs in case service is started again immediately */
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
/* Also, remove the runtime directory in */
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
/* Try to delete the pid file. At this point it will be
* out-of-date, and some software might be confused by it, so
* let's remove it. */

View File

@ -1255,6 +1255,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
UNIT(s)->manager->confirm_spawn,
UNIT(s)->manager->cgroup_supported,
UNIT(s)->cgroup_path,
manager_get_runtime_prefix(UNIT(s)->manager),
UNIT(s)->id,
0,
NULL,
@ -1289,6 +1290,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
}

View File

@ -646,6 +646,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
UNIT(s)->manager->confirm_spawn,
UNIT(s)->manager->cgroup_supported,
UNIT(s)->cgroup_path,
manager_get_runtime_prefix(UNIT(s)->manager),
UNIT(s)->id,
0,
NULL,
@ -678,6 +679,8 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
}

View File

@ -142,6 +142,9 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
case EXIT_ADDRESS_FAMILIES:
return "ADDRESS_FAMILIES";
case EXIT_RUNTIME_DIRECTORY:
return "RUNTIME_DIRECTORY";
}
}

View File

@ -72,6 +72,7 @@ typedef enum ExitStatus {
EXIT_PERSONALITY, /* 230 */
EXIT_APPARMOR_PROFILE,
EXIT_ADDRESS_FAMILIES,
EXIT_RUNTIME_DIRECTORY
} ExitStatus;
typedef enum ExitStatusLevel {

View File

@ -42,8 +42,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkd
return -errno;
if ((st.st_mode & 0777) != mode ||
st.st_uid != uid ||
st.st_gid != gid ||
(uid != (uid_t) -1 && st.st_uid != uid) ||
(gid != (gid_t) -1 && st.st_gid != gid) ||
!S_ISDIR(st.st_mode)) {
errno = EEXIST;
return -errno;