chiark / gitweb /
core: introduce new RuntimeDirectory= and RuntimeDirectoryMode= unit settings
authorLennart Poettering <lennart@poettering.net>
Mon, 3 Mar 2014 16:14:07 +0000 (17:14 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 3 Mar 2014 16:55:32 +0000 (17:55 +0100)
As discussed on the ML these are useful to manage runtime directories
below /run for services.

17 files changed:
man/systemd.exec.xml
man/tmpfiles.d.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/manager.c
src/core/manager.h
src/core/mount.c
src/core/service.c
src/core/socket.c
src/core/swap.c
src/shared/exit-status.c
src/shared/exit-status.h
src/shared/mkdir.c

index 9224f1ef3d5e38afcb6e2d83b4fc08f3a1f24244..f1bcf9b7bd645f2931fe96699db04ce833c4d947 100644 (file)
                                 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>
 
                           <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>
index 0a006d17abcdef60bd2fd7b94988b05f22cdbd53..343fad163c6c6371b91cb0588bcae73453b7d094 100644 (file)
                 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>
 
index 02e2a6d3df8bb7319259dacc56238e1a0a6c2152..6d0bdce9dc4551eb4befced6d4801b6718583175 100644 (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
 };
 
index 3312885b8775833d9b2f7219d88fb284b6a9f7b4..fec4b3b13a0cfe2f714ccd818bc13d58ea61fcc1 100644 (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) {
index efda23f9d8a7f7d61c01252350363e53e31c8f32..2c5d8bbf76ffb15a5f901143f897ef7d1d0ee367 100644 (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);
index beff290251fc6d073914a57096186ee470860047..c9ab5c33e23125e315bad2d12930067a340598b6 100644 (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')
index 478d22c4b517f366db8b014f5c3539418f8c2122..6f0027bf9b9baa8a260c5757357f5fb31ef8d743 100644 (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) {
index c5dbe6157ef35863eeed98297cb2c4b82c60e420..5488f1d7045cd3002f13b588aa74b07811b4c437 100644 (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);
index 9172a244aef2f358398779324b710a3281ac18e4..822c7ccfee43d3231db758374f6528fbed29ee22 100644 (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");
+}
index 398c8e642ed41d81b2566f54edbc57505ae16fec..3192b63dfde10762025ff00c8d6530d22e80e5dd 100644 (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);
index 7a92e1ca60133052baba6d46993fa9070a267585..60067d4d75c2b65e777cad13c8e2d261fa431bde 100644 (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);
 }
 
index 6de24ec5bd320e74b400fab3f22d8ec50b6aa03d..121ddec6ad3b72e6c8f233c066bf89154ca70f74 100644 (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. */
index 8e39032db6b4bffe361405249d5f46f611734726..35531edb751e3cd0c5635b1791f7bfcb6849c5eb 100644 (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);
 }
 
index 96cf38aae2b0a12cc87abb16baa04de76f9792c5..7004ede70e91daf7b54b30b3aed07b7493e9ffb5 100644 (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);
 }
 
index c1b04a38688cd96055f74c39a4a1555552ab3da5..c1c0861168dfb3eed3d6e570735f7755adaacca1 100644 (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";
                 }
         }
 
index e7f12032ec3ad1e5f731b0142d60778499e2e1af..9dc3fce0bf72ed7378f98ab8695a8d7fc5b082a1 100644 (file)
@@ -72,6 +72,7 @@ typedef enum ExitStatus {
         EXIT_PERSONALITY,  /* 230 */
         EXIT_APPARMOR_PROFILE,
         EXIT_ADDRESS_FAMILIES,
+        EXIT_RUNTIME_DIRECTORY
 } ExitStatus;
 
 typedef enum ExitStatusLevel {
index 43c6ea6d4a1616bc85775fb48418be8c52fbc6e8..4a2cd5e662b499398d964a90ba7773f2e17ca73e 100644 (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;