From: Yu Watanabe Date: Mon, 17 Jul 2017 07:22:25 +0000 (+0900) Subject: core: allow preserving contents of RuntimeDirectory= over process restart X-Git-Tag: v235~330^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=53f47dfc7b0b1e31878f7a17848fd925b1ab3c03;p=thirdparty%2Fsystemd.git core: allow preserving contents of RuntimeDirectory= over process restart This introduces RuntimeDirectoryPreserve= option which takes a boolean argument or 'restart'. Closes #6087. --- diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index d28de2d0f24..db491b49a07 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1658,7 +1658,10 @@ or more directories by the specified names will be created below /run (for system services) or below $XDG_RUNTIME_DIR (for user services) when - the unit is started, and removed when the unit is stopped. The + the unit is started, and removed when the unit is stopped. + It is possible to preserve the directories if + RuntimeDirectoryPreserve= is configured to + or . The directories will have the access mode specified in RuntimeDirectoryMode=, and will be owned by the user and group specified in User= and @@ -1686,6 +1689,21 @@ + + RuntimeDirectoryPreserve= + + Takes a boolean argument or . + If set to (the default), the directories specified in RuntimeDirectory= + are always removed when the service stops. If set to the directories are preserved + when the service is both automatically and manually restarted. Here, the automatic restart means the operation + specified in Restart=, and manual restart means the one triggered by + systemctl restart foo.service. If set to , then the directories are not + removed when the service is stopped. Note that since the runtime directory /run is a mount + point of tmpfs, then for system services the directories specified in + RuntimeDirectory= are removed when the system is rebooted. + + + MemoryDenyWriteExecute= diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index c041a7d94c4..0958c238f02 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -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; diff --git a/src/core/execute.c b/src/core/execute.c index d72e5bf08c4..d1d660ffed6 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3428,6 +3428,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 +4162,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); diff --git a/src/core/execute.h b/src/core/execute.h index 9f07aa4aef2..93713e48ccf 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -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_; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5b5a86250e8..f67494f059a 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -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)', diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 9d5c39b3dd2..15d392cdde3 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index fc27a079559..4174f194835 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -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); diff --git a/src/core/service.c b/src/core/service.c index 4c577db8d70..601ca2eb20e 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -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); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 5cbe663fa87..71451758a6a 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -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;