Permalink
Browse files

core: introduce new RuntimeDirectory= and RuntimeDirectoryMode= unit …

…settings

As discussed on the ML these are useful to manage runtime directories
below /run for services.
  • Loading branch information...
poettering committed Mar 3, 2014
1 parent b64a3d8 commit e66cf1a3f94fff48a572f6dbd19b43c9bcf7b8c7
@@ -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>
@@ -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>

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

@@ -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) {
@@ -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);
@@ -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')
@@ -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) {
@@ -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);
@@ -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");
}
@@ -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);
@@ -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);
}

@@ -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. */
@@ -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);
}

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

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

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

typedef enum ExitStatusLevel {
Oops, something went wrong.

0 comments on commit e66cf1a

Please sign in to comment.