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

Reply via email to