This commit updates the cgrulesengd logic that matches the new pid to its corresponding rule by adding support for ignore rules. If a user has created an ignore rule and the pid matches that rule completely, then cgrulesengd will perform no further processing (e.g. moving the pid to a different cgroup) on that pid. It is assumed that a separate process - outside of libcgroup - will manage that process and its cgroups.
A few examples: <user> <controller> <destination> <options> * cpuset IgnoreCgroup ignore * cpuset DefaultCgroup For the above example: * A new process named "foo" spawned in the IgnoreCgroup will be ignored by cgrulesengd and will not be moved to DefaultCgroup * A new process named "bar" spawned in the root cgroup will not match the ignore rule and will be moved to the DefaultCgroup by the second rule * Any processes not in IgnoreCgroup will be automatically moved to the default cgroup <user> <controller> <destination> <options> * memory IgnoreCgroup2/* ignore * memory DefaultCgroup For the above example: * A new process named "bar" spawned in IgnoreCgroup2/childcg will match the first rule and thus be ignored by cgrulesengd and will not be moved * In fact, any process in IgnoreCgroup2 or its children cgroup(s) will be ignored by cgrulesengd * cgrulesengd will attempt to move all other processes that don't match the ignore rule to the default cgroup <user> <controller> <destination> <options> jdoe:foo cpu IgnoreCgroup ignore * cpu DefaultCgroup For the above example: * If a process named "foo" is spawned by user jdoe and is in the IgnoreCgroup cgroup, then cgrulesengd will ignore it and it will not be moved * If a process named "bar" is spawned in IgnoreCgroup, it will not match the first rule and will fall into the default rule. Thus it will be moved to the DefaultCgroup Signed-off-by: Tom Hromatka <tom.hroma...@oracle.com> --- src/api.c | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) diff --git a/src/api.c b/src/api.c index be70fe05e890..b8749c887e44 100644 --- a/src/api.c +++ b/src/api.c @@ -116,6 +116,12 @@ const char const *cgroup_strerror_codes[] = { static const char const *cgroup_ignored_tasks_files[] = { "tasks", NULL }; +#ifndef UNIT_TEST +static int cg_get_cgroups_from_proc_cgroups(pid_t pid, char *cgroup_list[], + char *controller_list[], + int list_len); +#endif + static int cg_chown(const char *filename, uid_t owner, gid_t group) { if (owner == NO_UID_GID) @@ -2795,6 +2801,160 @@ static int cg_prepare_cgroup(struct cgroup *cgroup, pid_t pid, return ret; } +static int cgroup_find_matching_destination(char *cgroup_list[], + const char * const rule_dest, + int *matching_index) +{ + size_t wildcard_strlen, rule_strlen = strlen(rule_dest); + int ret = -ENODATA; + int i; + + for (i = 0; i < MAX_MNT_ELEMENTS; i++) { + if (cgroup_list[i] == NULL) + break; + + wildcard_strlen = rule_strlen - 1; + if (rule_dest[rule_strlen - 1] == '*') { + /* + * This rule has a wildcard at the end. Therefore + * we need to match strings up until that point + */ + + if (wildcard_strlen >= 2 && + rule_dest[wildcard_strlen - 1] == '/') + /* + * Strip off the trailing '/' for non-root + * destinations. The cgroups in cgroup_list + * don't have trailing '/'s. + */ + wildcard_strlen--; + + if (strncmp(rule_dest, cgroup_list[i], + wildcard_strlen) == 0) { + *matching_index = i; + ret = 0; + break; + } + } + + if (strlen(cgroup_list[i]) != rule_strlen) + continue; + + if (strncmp(rule_dest, cgroup_list[i], + rule_strlen) == 0) { + *matching_index = i; + ret = 0; + break; + } + + } + + return ret; +} + +static int cgroup_find_matching_controller(char * const *rule_controllers, + const char * const pid_controller, + int *matching_index) +{ + int ret = -ENODATA; + int i; + + for (i = 0; i < MAX_MNT_ELEMENTS; i++) { + if (rule_controllers[i] == NULL) + break; + + if (strlen(rule_controllers[i]) != strlen(pid_controller)) + continue; + + if (strncmp(pid_controller, rule_controllers[i], + strlen(pid_controller)) == 0) { + *matching_index = i; + ret = 0; + break; + } + + } + + return ret; +} + +/** + * Evaluates if rule is an ignore rule and the pid/procname match this rule. + * If rule is an ignore rule and the pid/procname match this rule, then this + * function returns true. Otherwise it returns false. + * + * @param rule Rule being evaluated + * @param pid PID of the process being compared + * @param procname Process name of the process being compared + * @return True if the rule is an ignore rule and this pid/procname + * match the rule. False otherwise + */ +static bool cgroup_compare_ignore_rule(const struct cgroup_rule * const rule, + pid_t pid, const char * const procname) +{ + char *controller_list[MAX_MNT_ELEMENTS] = { '\0' }; + char *cgroup_list[MAX_MNT_ELEMENTS] = { '\0' }; + char *token, *saveptr; + bool found_match = false; + int rule_matching_controller_idx; + int cgroup_list_matching_idx; + int ret, i; + + if (!rule->is_ignore) + /* immediately return if the 'ignore' option is not set */ + return false; + + ret = cg_get_cgroups_from_proc_cgroups(pid, cgroup_list, + controller_list, + MAX_MNT_ELEMENTS); + if (ret < 0) + goto out; + + ret = cgroup_find_matching_destination(cgroup_list, rule->destination, + &cgroup_list_matching_idx); + if (ret < 0) + /* no cgroups matched */ + goto out; + + + token = strtok_r(controller_list[cgroup_list_matching_idx], + ",", &saveptr); + while (token != NULL) { + + ret = cgroup_find_matching_controller(rule->controllers, + token, &rule_matching_controller_idx); + if (ret == 0) + /* we found a matching controller */ + break; + + token = strtok_r(NULL, ",", &saveptr); + } + + if (!rule->procname) { + /* + * The rule procname is empty, thus it's a wildcard and all + * processes match. + */ + found_match = true; + goto out; + } + + if (!strcmp(rule->procname, procname)) { + found_match = true; + goto out; + } + +out: + for (i = 0; i < MAX_MNT_ELEMENTS; i++) { + if (controller_list[i]) + free(controller_list[i]); + if (cgroup_list[i]) + free(cgroup_list[i]); + } + + return found_match; +} + static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(uid_t uid, gid_t gid, struct cgroup_rule *rule) { @@ -2873,7 +3033,7 @@ static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(uid_t uid, * TODO: Determine thread-safeness and fix if not safe. */ static struct cgroup_rule *cgroup_find_matching_rule(uid_t uid, - gid_t gid, const char *procname) + gid_t gid, pid_t pid, const char *procname) { /* Return value */ struct cgroup_rule *ret = rl.head; @@ -2884,6 +3044,21 @@ static struct cgroup_rule *cgroup_find_matching_rule(uid_t uid, ret = cgroup_find_matching_rule_uid_gid(uid, gid, ret); if (!ret) break; + if (cgroup_compare_ignore_rule(ret, pid, procname)) + /* + * This pid matched a rule that instructs the cgrules + * daemon to ignore this process. + */ + break; + if (ret->is_ignore) { + /* + * The rule currently being examined is an ignore + * rule, but it didn't match this pid. Move on to + * the next rule + */ + ret = ret->next; + continue; + } if (!procname) /* If procname is NULL, return a rule matching * UID or GID */ @@ -3151,7 +3326,7 @@ int cgroup_change_cgroup_flags(uid_t uid, gid_t gid, tmp = trl.head; } else { /* Find the first matching rule in the cached list. */ - tmp = cgroup_find_matching_rule(uid, gid, procname); + tmp = cgroup_find_matching_rule(uid, gid, pid, procname); if (!tmp) { cgroup_dbg("No rule found to match PID: %d, UID: %d, " "GID: %d\n", pid, uid, gid); @@ -3162,6 +3337,16 @@ int cgroup_change_cgroup_flags(uid_t uid, gid_t gid, cgroup_dbg("Found matching rule %s for PID: %d, UID: %d, GID: %d\n", tmp->username, pid, uid, gid); + if (tmp->is_ignore) { + /* + * This rule has instructed us that this pid is not to be + * processed and should be ignored + */ + cgroup_dbg("Matching rule is an ignore rule\n"); + ret = 0; + goto finished; + } + /* If we are here, then we found a matching rule, so execute it. */ do { cgroup_dbg("Executing rule %s for PID %d... ", tmp->username, -- 1.8.3.1 _______________________________________________ Libcg-devel mailing list Libcg-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libcg-devel