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

Reply via email to