I noticed a bug in my patched code. A better patch below.
Cheers, Paul
diff -r -u -p a-3.1.0-2/src/api.c b-3.1.0-2/src/api.c
--- a-3.1.0-2/src/api.c 2023-07-29 06:05:21.000000000 +1000
+++ b-3.1.0-2/src/api.c 2025-09-06 06:05:40.144138927 +1000
@@ -3982,148 +3982,6 @@ static int cgroup_find_matching_controll
}
/**
- * 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 The 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' };
- int rule_matching_controller_idx;
- int cgroup_list_matching_idx;
- bool found_match = false;
- char *token, *saveptr;
- 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;
- }
-
- if (cgroup_compare_wildcard_procname(rule->procname, procname))
- found_match = true;
-
-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)
-{
- /* Temporary user data */
- struct passwd *usr = NULL;
-
- /* Temporary group data */
- struct group *grp = NULL;
-
- /* Temporary string pointer */
- char *sp = NULL;
-
- /* Loop variable */
- int i = 0;
-
- while (rule) {
- /* Skip "%" which indicates continuation of previous rule. */
- if (rule->username[0] == '%') {
- rule = rule->next;
- continue;
- }
- /* The wildcard rule always matches. */
- if ((rule->uid == CGRULE_WILD) && (rule->gid == CGRULE_WILD))
- return rule;
-
- /* This is the simple case of the UID matching. */
- if (rule->uid == uid)
- return rule;
-
- /* This is the simple case of the GID matching. */
- if (rule->gid == gid)
- return rule;
-
- /* If this is a group rule, the UID might be a member. */
- if (rule->username[0] == '@') {
- /* Get the group data. */
- sp = &(rule->username[1]);
- grp = getgrnam(sp);
- if (!grp) {
- rule = rule->next;
- continue;
- }
-
- /* Get the data for UID. */
- usr = getpwuid(uid);
- if (!usr) {
- rule = rule->next;
- continue;
- }
-
- /* If UID is a member of group, we matched. */
- for (i = 0; grp->gr_mem[i]; i++) {
- if (!(strcmp(usr->pw_name, grp->gr_mem[i])))
- return rule;
- }
- }
-
- /* If we haven't matched, try the next rule. */
- rule = rule->next;
- }
-
- /* If we get here, no rules matched. */
- return NULL;
-}
-
-/**
* Finds the first rule in the cached list that matches the given UID, GID
* or PROCESS NAME, and returns a pointer to that rule.
* This function uses rl_lock.
@@ -4138,53 +3996,131 @@ static struct cgroup_rule *cgroup_find_m
static struct cgroup_rule *cgroup_find_matching_rule(uid_t uid, gid_t gid, pid_t pid,
const char *procname)
{
- /* Return value */
+ /* Return value (rule) */
struct cgroup_rule *ret = rl.head;
+
+ /* Temporary data */
+ struct passwd *usr = NULL;
+ struct group *grp = NULL;
+ char *sp = NULL;
+ int i = 0;
char *base = NULL;
pthread_rwlock_wrlock(&rl_lock);
while (ret) {
- ret = cgroup_find_matching_rule_uid_gid(uid, gid, ret);
- if (!ret)
- break;
- if (cgroup_compare_ignore_rule(ret, pid, procname))
+ /* PSz Do UID/GID matching inline, no need for separate routine */
+
+ /* Skip "%" which indicates continuation of previous rule. */
+ /*
+ * PSz BUG ALERT: simply skip % lines: not evaluate and
+ * "add" to previous, not handle as multi-line rule.
+ * Was like that, is like that also in upstream V3.2.
+ * I guess that the idea of % with separate controllers
+ * or destinations is invalid with CGROUPv2, anyway.
+ */
+ if (ret->username[0] == '%') {
+ goto trynext;
+ }
+
+ /* UID/GID matching - in a bogus while loop so can break */
+ while(1) {
+ /* The wildcard rule always matches. */
+ if ((ret->uid == CGRULE_WILD) && (ret->gid == CGRULE_WILD))
+ break;
+
+ /* This is the simple case of the UID matching. */
+ if (ret->uid == uid)
+ break;
+
+ /* This is the simple case of the GID matching. */
+ if (ret->gid == gid)
+ break;
+
+ /* If this is a group rule, the UID might be a member. */
/*
- * This pid matched a rule that instructs the
- * cgrules daemon to ignore this process.
+ * PSz DOCUMENTATION BUG ALERT - Should document that "default"
+ * membership is checked, not the result of any setgroups()
*/
+ if (ret->username[0] == '@') {
+ /* Get the group data. */
+ sp = &(ret->username[1]);
+ grp = getgrnam(sp);
+ if (!grp)
+ goto trynext;
+
+ /* Get the data for UID. */
+ usr = getpwuid(uid);
+ if (!usr)
+ goto trynext;
+
+ /* If UID is a member of group, we matched. */
+ for (i = 0; grp->gr_mem[i]; i++) {
+ if (!(strcmp(usr->pw_name, grp->gr_mem[i])))
+ goto grpmatched;
+ }
+ }
+ /* Did not match */
+ goto trynext;
+ grpmatched:
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. */
- break;
- if (!ret->procname)
- /* If no process name in a rule, that means wildcard */
- break;
- if (!strcmp(ret->procname, procname))
- break;
- base = cgroup_basename(procname);
- if (!strcmp(ret->procname, base))
- /* Check a rule of basename. */
- break;
- if (cgroup_compare_wildcard_procname(ret->procname, procname))
- break;
+ /* procname matching - in a bogus while loop so can break */
+ while(1) {
+ /* If procname is NULL, we take it as matched */
+ if (!procname)
+ break;
+ /* If no process name in a rule then any will match */
+ if (!ret->procname)
+ break;
+ /* Simple match */
+ if (!strcmp(ret->procname, procname))
+ break;
+ /* Wildcard match */
+ if (cgroup_compare_wildcard_procname(ret->procname, procname))
+ break;
+
+ /* Check basename (as rules are most commonly written) */
+ /* Try both "simple" and wildcard matches */
+ base = cgroup_basename(procname);
+ if (!strcmp(ret->procname, base))
+ break;
+ if (cgroup_compare_wildcard_procname(ret->procname, base))
+ break;
+
+ /* Did not match */
+ free(base);
+ base = NULL;
+ goto trynext;
+ }
+ if (base) {
+ free(base);
+ base = NULL;
+ }
+
+ /*
+ * PSz Upstream V3.2 knows about ignore and ignore_rt options,
+ * and should check whether the process matches. Documentation
+ * is unclear about what to do if both ignore and ignore_rt are
+ * specified, or if the RT status of process does not match.
+ * What upstream V3.2 should do:
+ * if ((rule->is_ignore & CGRULE_OPT_IGNORE))
+ * break;
+ * if ((rule->is_ignore & CGRULE_OPT_IGNORE_RT) && cgroup_is_rt_task(pid) == true)
+ * break;
+ *
+ * V3.1 knows about a single ignore option, to match all processes:
+ * nothing to check here.
+ * Done all checks - we matched, found the right rule.
+ */
+ break;
+
+
+trynext:
ret = ret->next;
- free(base);
- base = NULL;
}
pthread_rwlock_unlock(&rl_lock);
- if (base)
- free(base);
return ret;
}
@@ -4682,6 +4618,8 @@ finished:
* This function may be called after creating new control groups to move
* running PIDs into the newly created control groups.
* @return 0 on success, < 0 on error
+ * PSz: Do not use if caller would not have CGFLAG_USECACHE.
+ * PSz: Used from cgrulesengd.c only, so it is OK to use CGFLAG_USECACHE below.
*/
int cgroup_change_all_cgroups(void)
{