mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-27 13:57:26 +03:00
exec: introduce ControlGroupPersistant= to make cgroups persistant
This commit is contained in:
parent
62f21ec91a
commit
8d53b4534a
2
TODO
2
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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
19
src/cgroup.c
19
src/cgroup.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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, }
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
24
src/util.c
24
src/util.c
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user