core: allow preserving contents of RuntimeDirectory= over process restart
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 17 Jul 2017 07:22:25 +0000 (16:22 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 17 Jul 2017 07:22:25 +0000 (16:22 +0900)
This introduces RuntimeDirectoryPreserve= option which takes a boolean
argument or 'restart'.

Closes #6087.

man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/service.c
src/shared/bus-unit-util.c

index d28de2d..db491b4 100644 (file)
         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
+        the unit is started, and 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
         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
       </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>
+
+      <varlistentry>
         <term><varname>MemoryDenyWriteExecute=</varname></term>
 
         <listitem><para>Takes a boolean argument. If set, attempts to create memory mappings that are writable and
index c041a7d..0958c23 100644 (file)
@@ -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;
index d72e5bf..d1d660f 100644 (file)
@@ -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);
index 9f07aa4..93713e4 100644 (file)
@@ -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_;
index 5b5a862..f67494f 100644 (file)
@@ -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)',
index 9d5c39b..15d392c 100644 (file)
@@ -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,
index fc27a07..4174f19 100644 (file)
@@ -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);
index 4c577db..601ca2e 100644 (file)
@@ -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);
index 5cbe663..7145175 100644 (file)
@@ -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;