Merge pull request #6328 from yuwata/runtime-preserve
core: Allow preserving contents of RuntimeDirectory over process restart
This commit is contained in:
commit
7398320f9a
|
@ -1654,26 +1654,31 @@
|
|||
<varlistentry>
|
||||
<term><varname>RuntimeDirectory=</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 unprivileged 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>
|
||||
<listitem><para>Takes a whitespace-separated list of directory names. The specified directory names must be
|
||||
relative, and may not include <literal>.</literal> or <literal>..</literal>. If set, one or more directories
|
||||
including their parents 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. The
|
||||
lowest subdirectories are removed when the unit is stopped. It is possible to preserve the directories if
|
||||
<varname>RuntimeDirectoryPreserve=</varname> is configured to <option>restart</option> or <option>yes</option>.
|
||||
The lowest subdirectories will have the access mode specified in <varname>RuntimeDirectoryMode=</varname>,
|
||||
and be owned by the user and group specified in <varname>User=</varname> and <varname>Group=</varname>.
|
||||
This implies <varname>ReadWritePaths=</varname>, that is, the directories specified
|
||||
in this option are accessible with the access mode specified in <varname>RuntimeDirectoryMode=</varname>
|
||||
even if <varname>ProtectSystem=</varname> is set to <option>strict</option>.
|
||||
Use this to manage one or more runtime directories of the unit and bind their
|
||||
lifetime to the daemon runtime. This is particularly useful for unprivileged 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>
|
||||
|
||||
<para>Example: if a system service unit has the following,
|
||||
<programlisting>RuntimeDirectory=foo/bar baz</programlisting>
|
||||
the service manager creates <filename>/run/foo</filename> (if it does not exist), <filename>/run/foo/bar</filename>,
|
||||
and <filename>/run/baz</filename>. The directories <filename>/run/foo/bar</filename> and <filename>/run/baz</filename>
|
||||
except <filename>/run/foo</filename> are owned by the user and group specified in <varname>User=</varname> and
|
||||
<varname>Group=</varname>, and removed when the service is stopped.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -1682,7 +1687,23 @@
|
|||
<listitem><para>Specifies the access mode of the directories specified in
|
||||
<varname>RuntimeDirectory=</varname> as an octal number. Defaults to
|
||||
<constant>0755</constant>. See "Permissions" in
|
||||
<citerefentry project='man-pages'><refentrytitle>path_resolution</refentrytitle><manvolnum>7</manvolnum></citerefentry> for a discussion of the meaning of permission bits.
|
||||
<citerefentry project='man-pages'><refentrytitle>path_resolution</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for a discussion of the meaning of permission bits.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RuntimeDirectoryPreserve=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument or <option>restart</option>.
|
||||
If set to <option>no</option> (the default), the directories specified in <varname>RuntimeDirectory=</varname>
|
||||
are always removed when the service stops. If set to <option>restart</option> the directories are preserved
|
||||
when the service is both automatically and manually restarted. Here, the automatic restart means the operation
|
||||
specified in <varname>Restart=</varname>, and manual restart means the one triggered by
|
||||
<command>systemctl restart foo.service</command>. If set to <option>yes</option>, then the directories are not
|
||||
removed when the service is stopped. Note that since the runtime directory <filename>/run</filename> is a mount
|
||||
point of <literal>tmpfs</literal>, then for system services the directories specified in
|
||||
<varname>RuntimeDirectory=</varname> are removed when the system is rebooted.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInp
|
|||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode);
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode);
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem);
|
||||
|
||||
|
@ -850,6 +852,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
|||
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("RuntimeDirectoryPreserve", "s", property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
@ -1678,6 +1681,41 @@ int bus_exec_context_set_transient_property(
|
|||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "RuntimeDirectoryPreserve")) {
|
||||
const char *s;
|
||||
ExecPreserveMode m;
|
||||
|
||||
r = sd_bus_message_read(message, "s", &s);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m = exec_preserve_mode_from_string(s);
|
||||
if (m < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid preserve mode");
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->runtime_directory_preserve_mode = m;
|
||||
|
||||
unit_write_drop_in_private_format(u, mode, name, "RuntimeDirectoryPreserve=%s", exec_preserve_mode_to_string(m));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "RuntimeDirectoryMode")) {
|
||||
mode_t m;
|
||||
|
||||
r = sd_bus_message_read(message, "u", &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->runtime_directory_mode = m;
|
||||
|
||||
unit_write_drop_in_private_format(u, mode, name, "RuntimeDirectoryMode=%040o", m);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
} else if (streq(name, "RuntimeDirectory")) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
char **p;
|
||||
|
|
|
@ -1858,6 +1858,10 @@ static int setup_runtime_directory(
|
|||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = mkdir_parents_label(p, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = mkdir_p_label(p, context->runtime_directory_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -3428,6 +3432,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
|
|||
|
||||
fprintf(f, "%sRuntimeDirectoryMode: %04o\n", prefix, c->runtime_directory_mode);
|
||||
|
||||
fprintf(f, "%sRuntimeDirectoryPreserve: %s\n", prefix, exec_preserve_mode_to_string(c->runtime_directory_preserve_mode));
|
||||
|
||||
STRV_FOREACH(d, c->runtime_directory)
|
||||
fprintf(f, "%sRuntimeDirectory: %s\n", prefix, *d);
|
||||
|
||||
|
@ -4160,3 +4166,11 @@ static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
|
|||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);
|
||||
|
||||
static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
|
||||
[EXEC_PRESERVE_NO] = "no",
|
||||
[EXEC_PRESERVE_YES] = "yes",
|
||||
[EXEC_PRESERVE_RESTART] = "restart",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
|
||||
|
|
|
@ -72,6 +72,14 @@ typedef enum ExecOutput {
|
|||
_EXEC_OUTPUT_INVALID = -1
|
||||
} ExecOutput;
|
||||
|
||||
typedef enum ExecPreserveMode {
|
||||
EXEC_PRESERVE_NO,
|
||||
EXEC_PRESERVE_YES,
|
||||
EXEC_PRESERVE_RESTART,
|
||||
_EXEC_PRESERVE_MODE_MAX,
|
||||
_EXEC_PRESERVE_MODE_INVALID = -1
|
||||
} ExecPreserveMode;
|
||||
|
||||
struct ExecStatus {
|
||||
dual_timestamp start_timestamp;
|
||||
dual_timestamp exit_timestamp;
|
||||
|
@ -211,6 +219,7 @@ struct ExecContext {
|
|||
|
||||
char **runtime_directory;
|
||||
mode_t runtime_directory_mode;
|
||||
ExecPreserveMode runtime_directory_preserve_mode;
|
||||
|
||||
bool memory_deny_write_execute;
|
||||
bool restrict_realtime;
|
||||
|
@ -330,3 +339,6 @@ ExecInput exec_input_from_string(const char *s) _pure_;
|
|||
|
||||
const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_;
|
||||
ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_;
|
||||
|
||||
const char* exec_preserve_mode_to_string(ExecPreserveMode i) _const_;
|
||||
ExecPreserveMode exec_preserve_mode_from_string(const char *s) _pure_;
|
||||
|
|
|
@ -105,6 +105,7 @@ $1.MountFlags, config_parse_exec_mount_flags, 0,
|
|||
$1.MountAPIVFS, config_parse_bool, 0, offsetof($1, exec_context.mount_apivfs)
|
||||
$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.RuntimeDirectoryPreserve, config_parse_runtime_preserve_mode, 0, offsetof($1, exec_context.runtime_directory_preserve_mode)
|
||||
$1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory)
|
||||
m4_ifdef(`HAVE_PAM',
|
||||
`$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name)',
|
||||
|
|
|
@ -3701,6 +3701,8 @@ int config_parse_job_mode_isolate(
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
|
||||
|
||||
int config_parse_runtime_directory(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
@ -3750,7 +3752,7 @@ int config_parse_runtime_directory(
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!filename_is_valid(k)) {
|
||||
if (!path_is_safe(k) || path_is_absolute(k)) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0,
|
||||
"Runtime directory is not valid, ignoring assignment: %s", rvalue);
|
||||
continue;
|
||||
|
|
|
@ -102,6 +102,7 @@ int config_parse_exec_selinux_context(const char *unit, const char *filename, un
|
|||
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_exec_smack_process_label(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_preserve_mode(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);
|
||||
int config_parse_set_status(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_namespace_path_strv(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);
|
||||
|
|
|
@ -1480,6 +1480,18 @@ static bool service_shall_restart(Service *s) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool service_will_restart(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (s->state == SERVICE_AUTO_RESTART)
|
||||
return true;
|
||||
if (!UNIT(s)->job)
|
||||
return false;
|
||||
if (UNIT(s)->job->type == JOB_START)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
|
||||
int r;
|
||||
assert(s);
|
||||
|
@ -1510,8 +1522,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
|
|||
exec_runtime_destroy(s->exec_runtime);
|
||||
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
|
||||
|
||||
/* Also, remove the runtime directory */
|
||||
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
|
||||
if (s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_NO ||
|
||||
(s->exec_context.runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART && !service_will_restart(s)))
|
||||
/* Also, remove the runtime directory */
|
||||
exec_context_destroy_runtime_directory(&s->exec_context, manager_get_runtime_prefix(UNIT(s)->manager));
|
||||
|
||||
/* Get rid of the IPC bits of the user */
|
||||
unit_unref_uid_gid(UNIT(s), true);
|
||||
|
|
|
@ -267,7 +267,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
|||
"Description", "Slice", "Type", "WorkingDirectory",
|
||||
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
|
||||
"ProtectHome", "SELinuxContext", "Restart", "RootImage",
|
||||
"NotifyAccess"))
|
||||
"NotifyAccess", "RuntimeDirectoryPreserve"))
|
||||
r = sd_bus_message_append(m, "v", "s", eq);
|
||||
|
||||
else if (streq(field, "SyslogLevel")) {
|
||||
|
@ -548,6 +548,15 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
|||
|
||||
r = sd_bus_message_close_container(m);
|
||||
|
||||
} else if (streq(field, "RuntimeDirectoryMode")) {
|
||||
mode_t mode;
|
||||
|
||||
r = parse_mode(eq, &mode);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s value %s", field, eq);
|
||||
|
||||
r = sd_bus_message_append(m, "v", "u", mode);
|
||||
|
||||
} else if (streq(field, "RuntimeDirectory")) {
|
||||
const char *p;
|
||||
|
||||
|
|
Loading…
Reference in a new issue