exec: introduce ControlGroupPersistant= to make cgroups persistant

This commit is contained in:
Lennart Poettering 2012-01-18 15:40:21 +01:00
parent 62f21ec91a
commit 8d53b4534a
15 changed files with 135 additions and 39 deletions

2
TODO
View File

@ -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

View File

@ -776,6 +776,21 @@
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>

View 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);

View 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);

View 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;
}

View 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);

View 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,

View 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);

View 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, }
};

View 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)

View 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

View 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)

View 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);

View 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);
}

View 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);