chiark / gitweb /
exec: introduce ControlGroupPersistant= to make cgroups persistant
authorLennart Poettering <lennart@poettering.net>
Wed, 18 Jan 2012 14:40:21 +0000 (15:40 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 18 Jan 2012 14:40:21 +0000 (15:40 +0100)
15 files changed:
TODO
man/systemd.exec.xml
src/cgroup-util.c
src/cgroup-util.h
src/cgroup.c
src/cgroup.h
src/conf-parser.c
src/conf-parser.h
src/dbus-execute.c
src/execute.c
src/execute.h
src/load-fragment-gperf.gperf.m4
src/login/logind-session.c
src/util.c
src/util.h

diff --git a/TODO b/TODO
index e2a19adef5d6ff0726be9417d0c089607f000f4a..0c3cc9dec461510bf1c4d9d166e4204df4878f3f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -84,8 +84,6 @@ Features:
 * tmpfiles: apply "x" on "D" too (see patch from William Douglas)
 * tmpfiles: support generation of char/block devices, symlinks and one-line files (think sysfs)
 
-* Introduce ControlGroupPersistant=yes to set +t on the tasks file when creating the cgroup
-
 * don't set $HOME in services unless requested
 
 * hide PAM/TCPWrap options in fragment parser when compile time disabled
index c1ab6b90106bc1b4771ef17cf53b54c5b6b3b488..97bdbba5604ccfe6e2a593f7d9b9552e4d12e5aa 100644 (file)
                                 the group.</para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><varname>ControlGroupPersistant=</varname></term>
+                                <listitem><para>Takes a boolean
+                                argument. If true, the control groups
+                                created for this unit will be marked
+                                to be persistant, i.e. systemd will
+                                not remove them when stopping the
+                                unit. The default is false, meaning
+                                that the control groups will be
+                                removed when the unit is stopped. For
+                                details about the semantics of this
+                                logic see <ulink
+                                url="http://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups">PaxControlGroups</ulink>.</para></listitem>
+                        </varlistentry>
+
                         <varlistentry>
                                 <term><varname>ControlGroupAttribute=</varname></term>
 
index f74280f4952f353623325f832a03fb30e36ebb25..904d30095226a6f027f1d527634cb4e9cc311b24 100644 (file)
@@ -173,7 +173,7 @@ int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
                         return -ENOMEM;
                 }
 
-                r = file_is_sticky(tasks);
+                r = file_is_priv_sticky(tasks);
                 free(tasks);
 
                 if (r > 0) {
@@ -571,7 +571,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
                 return 1;
         }
 
-        is_sticky = file_is_sticky(p) > 0;
+        is_sticky = file_is_priv_sticky(p) > 0;
         free(p);
 
         if (is_sticky)
@@ -606,7 +606,7 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
                         return -ENOMEM;
                 }
 
-                is_sticky = file_is_sticky(p) > 0;
+                is_sticky = file_is_priv_sticky(p) > 0;
                 free(p);
 
                 if (!is_sticky)
@@ -712,7 +712,11 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u
         assert(controller);
         assert(path);
 
-        if ((r = cg_get_path(controller, path, NULL, &fs)) < 0)
+        if (mode != (mode_t) -1)
+                mode &= 0777;
+
+        r = cg_get_path(controller, path, NULL, &fs);
+        if (r < 0)
                 return r;
 
         r = chmod_and_chown(fs, mode, uid, gid);
@@ -721,16 +725,47 @@ int cg_set_group_access(const char *controller, const char *path, mode_t mode, u
         return r;
 }
 
-int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
+int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) {
         char *fs;
         int r;
 
         assert(controller);
         assert(path);
 
-        if ((r = cg_get_path(controller, path, "tasks", &fs)) < 0)
+        if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
+                return 0;
+
+        if (mode != (mode_t) -1)
+                mode &= 0666;
+
+        r = cg_get_path(controller, path, "tasks", &fs);
+        if (r < 0)
                 return r;
 
+        if (sticky >= 0 && mode != (mode_t) -1)
+                /* Both mode and sticky param are passed */
+                mode |= (sticky ? S_ISVTX : 0);
+        else if ((sticky >= 0 && mode == (mode_t) -1) ||
+                 (mode != (mode_t) -1 && sticky < 0)) {
+                struct stat st;
+
+                /* Only one param is passed, hence read the current
+                 * mode from the file itself */
+
+                r = lstat(fs, &st);
+                if (r < 0) {
+                        free(fs);
+                        return -errno;
+                }
+
+                if (mode == (mode_t) -1)
+                        /* No mode set, we just shall set the sticky bit */
+                        mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0);
+                else
+                        /* Only mode set, leave sticky bit untouched */
+                        mode = (st.st_mode & ~0777) | mode;
+        }
+
         r = chmod_and_chown(fs, mode, uid, gid);
         free(fs);
 
index f09373bd06e7f423e55cd5ef6ba325a8592d4ecb..37e4255a9c4dc1b47470057a64a986d886a7160a 100644 (file)
@@ -60,7 +60,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid);
 int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
 
 int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
-int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
+int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky);
 
 int cg_install_release_agent(const char *controller, const char *agent);
 
index e141b4153ddb86292b603814ed9fdc71e951df47..9aff02e7bc1084f323a6267fe301887e82b92f2c 100644 (file)
@@ -60,7 +60,7 @@ int cgroup_bonding_realize_list(CGroupBonding *first) {
         return 0;
 }
 
-void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim) {
+void cgroup_bonding_free(CGroupBonding *b, bool trim) {
         assert(b);
 
         if (b->unit) {
@@ -79,13 +79,8 @@ void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim) {
                 }
         }
 
-        if (b->realized && b->ours && remove_or_trim) {
-
-                if (cgroup_bonding_is_empty(b) > 0)
-                        cg_delete(b->controller, b->path);
-                else
-                        cg_trim(b->controller, b->path, false);
-        }
+        if (b->realized && b->ours && trim)
+                cg_trim(b->controller, b->path, false);
 
         free(b->controller);
         free(b->path);
@@ -159,21 +154,21 @@ int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_
         return 0;
 }
 
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
+int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
         assert(b);
 
         if (!b->realized)
                 return -EINVAL;
 
-        return cg_set_task_access(b->controller, b->path, mode, uid, gid);
+        return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
 }
 
-int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
+int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
         CGroupBonding *b;
         int r;
 
         LIST_FOREACH(by_unit, b, first) {
-                r = cgroup_bonding_set_task_access(b, mode, uid, gid);
+                r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
                 if (r < 0)
                         return r;
         }
index f33d8440e6d4f3b225e21fa226b18b2da09a2274..db4feb916e003f2e5599bc1c96084da75d0d756b 100644 (file)
@@ -53,8 +53,8 @@ struct CGroupBonding {
 int cgroup_bonding_realize(CGroupBonding *b);
 int cgroup_bonding_realize_list(CGroupBonding *first);
 
-void cgroup_bonding_free(CGroupBonding *b, bool remove_or_trim);
-void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim);
+void cgroup_bonding_free(CGroupBonding *b, bool trim);
+void cgroup_bonding_free_list(CGroupBonding *first, bool trim);
 
 int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
 int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
@@ -62,8 +62,8 @@ int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
 int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
 int cgroup_bonding_set_group_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
 
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
-int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid);
+int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
+int cgroup_bonding_set_task_access_list(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky);
 
 int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, Set *s);
 int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, Set *s);
index 3ccd1c067a20c13b0aa085fa8d9a4093af3d6a63..ac8d9f5ac501cc766f767fb30aa66e0dfc75b612 100644 (file)
@@ -509,6 +509,36 @@ int config_parse_bool(
         return 0;
 }
 
+int config_parse_tristate(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        int k;
+        int *b = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
+
+        k = parse_boolean(rvalue);
+        if (k < 0) {
+                log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *b = !!k;
+        return 0;
+}
+
 int config_parse_string(
                 const char *filename,
                 unsigned line,
index e970ee28345c5df466e65a29faa5e6423b360737..35edcb63aff5b1174d51f90c6ee1fda9c6a0d3a2 100644 (file)
@@ -95,6 +95,7 @@ int config_parse_long(const char *filename, unsigned line, const char *section,
 int config_parse_uint64(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_tristate(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index caeaf77931996518380452926fbb5379283a1010..e02d61ca8bf163edc51b6e1fedbd5fe3c073359c 100644 (file)
@@ -416,5 +416,6 @@ const BusProperty bus_exec_context_properties[] = {
         { "KillSignal",               bus_property_append_int,               "i", offsetof(ExecContext, kill_signal)                  },
         { "UtmpIdentifier",           bus_property_append_string,            "s", offsetof(ExecContext, utmp_id),                true },
         { "ControlGroupModify",       bus_property_append_bool,              "b", offsetof(ExecContext, control_group_modify)         },
+        { "ControlGroupModify",       bus_property_append_bool,              "b", offsetof(ExecContext, control_group_persistant)     },
         { NULL, }
 };
index cacc8a73c555a4b881255893845c0d32fe7f5455..650c6c1436979328207aae765468e0f58a5bf64f 100644 (file)
@@ -993,7 +993,7 @@ int exec_spawn(ExecCommand *command,
                 char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
                 unsigned n_env = 0;
                 int saved_stdout = -1, saved_stdin = -1;
-                bool keep_stdout = false, keep_stdin = false;
+                bool keep_stdout = false, keep_stdin = false, set_access = false;
 
                 /* child */
 
@@ -1218,11 +1218,21 @@ int exec_spawn(ExecCommand *command,
                         if (cgroup_bondings && context->control_group_modify) {
                                 err = cgroup_bonding_set_group_access_list(cgroup_bondings, 0755, uid, gid);
                                 if (err >= 0)
-                                        err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid);
+                                        err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistant);
                                 if (err < 0) {
                                         r = EXIT_CGROUP;
                                         goto fail_child;
                                 }
+
+                                set_access = true;
+                        }
+                }
+
+                if (cgroup_bondings && !set_access && context->control_group_persistant >= 0)  {
+                        err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistant);
+                        if (err < 0) {
+                                r = EXIT_CGROUP;
+                                goto fail_child;
                         }
                 }
 
@@ -1488,6 +1498,7 @@ void exec_context_init(ExecContext *c) {
         c->mount_flags = MS_SHARED;
         c->kill_signal = SIGTERM;
         c->send_sigkill = true;
+        c->control_group_persistant = -1;
 }
 
 void exec_context_done(ExecContext *c) {
@@ -1673,6 +1684,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 "%sNonBlocking: %s\n"
                 "%sPrivateTmp: %s\n"
                 "%sControlGroupModify: %s\n"
+                "%sControlGroupPersistant: %s\n"
                 "%sPrivateNetwork: %s\n",
                 prefix, c->umask,
                 prefix, c->working_directory ? c->working_directory : "/",
@@ -1680,6 +1692,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 prefix, yes_no(c->non_blocking),
                 prefix, yes_no(c->private_tmp),
                 prefix, yes_no(c->control_group_modify),
+                prefix, yes_no(c->control_group_persistant),
                 prefix, yes_no(c->private_network));
 
         STRV_FOREACH(e, c->environment)
index 187e8d22742fc6aefe858608bb3f2703b9720bee..ff33fa92dfb838534f55a210e3ef5a14619c3633 100644 (file)
@@ -163,6 +163,7 @@ struct ExecContext {
         bool private_network;
 
         bool control_group_modify;
+        int control_group_persistant;
 
         /* This is not exposed to the user but available
          * internally. We need it to make sure that whenever we spawn
index c3f295710d22df32a8be077253c0472190f8dde5..8adedc59e06772f9801063aba759f132bca05193 100644 (file)
@@ -86,7 +86,8 @@ $1.KillMode,                     config_parse_kill_mode,             0,
 $1.KillSignal,                   config_parse_kill_signal,           0,                             offsetof($1, exec_context.kill_signal)
 $1.SendSIGKILL,                  config_parse_bool,                  0,                             offsetof($1, exec_context.send_sigkill)
 $1.UtmpIdentifier,               config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.utmp_id)
-$1.ControlGroupModify,           config_parse_bool,                  0,                             offsetof($1, exec_context.control_group_modify)'
+$1.ControlGroupModify,           config_parse_bool,                  0,                             offsetof($1, exec_context.control_group_modify)
+$1.ControlGroupPersistant,       config_parse_tristate,              0,                             offsetof($1, exec_context.control_group_persistant)'
 )m4_dnl
 Unit.Names,                      config_parse_unit_names,            0,                             0
 Unit.Description,                config_parse_unit_string_printf,    0,                             offsetof(Unit, description)
index bb802e5fc293176cbe9da97bb9fafc6f3205f53b..5ea7e260a7d3d0bf2fa4a8e3e45d136927d4c1b0 100644 (file)
@@ -434,7 +434,7 @@ static int session_create_one_group(Session *s, const char *controller, const ch
         if (r < 0)
                 return r;
 
-        r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid);
+        r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
         if (r >= 0)
                 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
 
index b6e490d457a3562ad1e97037bdd9fbe7a9687bd9..8004bebbd1c0f0af997924e8d95a97e7deaef66a 100644 (file)
@@ -3483,7 +3483,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                         }
 
                         if (honour_sticky)
-                                keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+                                keep_around =
+                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                        (st.st_mode & S_ISVTX);
 
                         is_dir = S_ISDIR(st.st_mode);
 
@@ -3497,7 +3499,9 @@ static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
                                         continue;
                                 }
 
-                                keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+                                keep_around =
+                                        (st.st_uid == 0 || st.st_uid == getuid()) &&
+                                        (st.st_mode & S_ISVTX);
                         }
 
                         is_dir = de->d_type == DT_DIR;
@@ -3559,7 +3563,7 @@ int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky
 
         if (delete_root) {
 
-                if (honour_sticky && file_is_sticky(path) > 0)
+                if (honour_sticky && file_is_priv_sticky(path) > 0)
                         return r;
 
                 if (rmdir(path) < 0 && errno != ENOENT) {
@@ -3578,11 +3582,13 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
          * first change the access mode and only then hand out
          * ownership to avoid a window where access is too open. */
 
-        if (chmod(path, mode) < 0)
-                return -errno;
+        if (mode != (mode_t) -1)
+                if (chmod(path, mode) < 0)
+                        return -errno;
 
-        if (chown(path, uid, gid) < 0)
-                return -errno;
+        if (uid != (uid_t) -1 || gid != (gid_t) -1)
+                if (chown(path, uid, gid) < 0)
+                        return -errno;
 
         return 0;
 }
@@ -5810,7 +5816,7 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
         return -ENOENT;
 }
 
-int file_is_sticky(const char *p) {
+int file_is_priv_sticky(const char *p) {
         struct stat st;
 
         assert(p);
@@ -5819,7 +5825,7 @@ int file_is_sticky(const char *p) {
                 return -errno;
 
         return
-                st.st_uid == 0 &&
+                (st.st_uid == 0 || st.st_uid == getuid()) &&
                 (st.st_mode & S_ISVTX);
 }
 
index 590dc1781ecc1c0a6fb64800027bfb861286d478..6acfcc83738cf40b479dc30a572a17b06d928244 100644 (file)
@@ -478,7 +478,7 @@ bool in_charset(const char *s, const char* charset);
 
 int block_get_whole_disk(dev_t d, dev_t *ret);
 
-int file_is_sticky(const char *p);
+int file_is_priv_sticky(const char *p);
 
 int strdup_or_null(const char *a, char **b);