From 495c45c844522c29ee4d0a26942e894f1346c237 Mon Sep 17 00:00:00 2001 From: Ken'ichi Ohmichi Date: Fri, 26 Jun 2009 14:49:50 +0900 Subject: Add the key "process name" to find a matching rule. Hi, Changelog of v6: ================ * No change. Changelog of v5.1: ================== * BUGFIX: Clear the flags meaning "found a matching rule" when a process name does not match. There was a problem that cgexec and cgclassify didn't work correctly if a user executes cgexec/cgclassify based on /etc/cgrules.conf. For example, if a root user executes `cgclassify $$` on the following /etc/cgrules.conf, the process ($$) should be moved to users/root on cpuset and memory subsystems. But the process was moved to users/root/cp on memory subsystem only. Example of /etc/cgrules.conf: ============================= root:cp cpuset users/root/cp % memory users/root/cp root cpuset users/root % memory users/root The cause is why the flags meaning "found a matching rule" (uid, gid, and matched) is not cleared when a process name does not match. This problem is fixed on this patch. Changelog of v5: ================ * Rebase the patch to the latest code. Changelog of v4: ================ * No change. Changelog of v3: ================ * BUGFIX: Fix the handling of '%' in /etc/cgrules.conf. Changelog of v2: ================ * Use strcmp() instead of strncmp() for checking a process name strictly. * Some cleanups. Description: ============ This patch adds the key "process name" to find a matching rule. Thanks Ken'ichi Ohmichi Signed-off-by: Ken'ichi Ohmichi Signed-off-by: Dhaval Giani --- include/libcgroup.h | 21 +++++++ src/api.c | 154 ++++++++++++++++++++++++++++++++-------------------- src/libcgroup.map | 1 + 3 files changed, 117 insertions(+), 59 deletions(-) diff --git a/include/libcgroup.h b/include/libcgroup.h index 6155f09..a9799ab 100644 --- a/include/libcgroup.h +++ b/include/libcgroup.h @@ -148,6 +148,27 @@ int cgroup_get_cgroup(struct cgroup *cgroup); int cgroup_create_cgroup_from_parent(struct cgroup *cgroup, int ignore_ownership); int cgroup_copy_cgroup(struct cgroup *dst, struct cgroup *src); +/** + * Changes the cgroup of a program based on the rules in the config file. + * If a rule exists for the given UID, GID or PROCESS NAME, then the given + * PID is placed into the correct group. By default, this function parses + * the configuration file each time it is called. + * + * The flags can alter the behavior of this function: + * CGFLAG_USECACHE: Use cached rules instead of parsing the config file + * + * This function may NOT be thread safe. + * @param uid The UID to match + * @param gid The GID to match + * @param procname The PROCESS NAME to match + * @param pid The PID of the process to move + * @param flags Bit flags to change the behavior, as defined above + * @return 0 on success, > 0 on error + * TODO: Determine thread-safeness and fix of not safe. + */ +int cgroup_change_cgroup_flags(const uid_t uid, const gid_t gid, + char *procname, const pid_t pid, const int flags); + /** * Changes the cgroup of a program based on the rules in the config file. If a * rule exists for the given UID or GID, then the given PID is placed into the diff --git a/src/api.c b/src/api.c index c5a3579..92bdebd 100644 --- a/src/api.c +++ b/src/api.c @@ -294,7 +294,8 @@ static char *cg_skip_unused_charactors_in_rule(char *rule) * @return 0 on success, -1 if no cache and match found, > 0 on error. * TODO: Make this function thread safe! */ -static int cgroup_parse_rules(bool cache, uid_t muid, gid_t mgid) +static int cgroup_parse_rules(bool cache, uid_t muid, + gid_t mgid, char *mprocname) { /* File descriptor for the configuration file */ FILE *fp = NULL; @@ -486,8 +487,29 @@ static int cgroup_parse_rules(bool cache, uid_t muid, gid_t mgid) matched = true; } - if (!cache && !matched) - continue; + if (!cache) { + if (!matched) + continue; + if (len_procname) { + /* + * If there is a rule based on process name, + * it should be matched with mprocname. + */ + if (!mprocname) { + uid = CGRULE_INVALID; + gid = CGRULE_INVALID; + matched = false; + continue; + } + if (strcmp(mprocname, procname) && + strcmp(basename(mprocname), procname)) { + uid = CGRULE_INVALID; + gid = CGRULE_INVALID; + matched = false; + continue; + } + } + } /* * Now, we're either caching rules or we found a match. Either @@ -1821,22 +1843,9 @@ static int cg_prepare_cgroup(struct cgroup *cgroup, pid_t pid, return ret; } -/** - * Finds the first rule in the cached list that matches the given UID or GID, - * and returns a pointer to that rule. This function uses rl_lock. - * - * This function may NOT be thread safe. - * @param uid The UID to match - * @param gid The GID to match - * @return Pointer to the first matching rule, or NULL if no match - * TODO: Determine thread-safeness and fix if not safe. - */ static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(const uid_t uid, - const gid_t gid) + const gid_t gid, struct cgroup_rule *rule) { - /* Return value */ - struct cgroup_rule *ret = rl.head; - /* Temporary user data */ struct passwd *usr = NULL; @@ -1849,76 +1858,96 @@ static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(const uid_t uid, /* Loop variable */ int i = 0; - pthread_rwlock_wrlock(&rl_lock); - while (ret) { - /* The wildcard rule always matches. */ - if ((ret->uid == CGRULE_WILD) && (ret->gid == CGRULE_WILD)) { - goto finished; + 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 (ret->uid == uid) { - goto finished; - } + if (rule->uid == uid) + return rule; /* This is the simple case of the GID matching. */ - if (ret->gid == gid) { - goto finished; - } + if (rule->gid == gid) + return rule; /* If this is a group rule, the UID might be a member. */ - if (ret->username[0] == '@') { + if (rule->username[0] == '@') { /* Get the group data. */ - sp = &(ret->username[1]); + sp = &(rule->username[1]); grp = getgrnam(sp); - if (!grp) { + if (!grp) continue; - } /* Get the data for UID. */ usr = getpwuid(uid); - if (!usr) { + if (!usr) 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]))) - goto finished; + return rule; } } /* If we haven't matched, try the next rule. */ - ret = ret->next; + rule = rule->next; } /* If we get here, no rules matched. */ - ret = NULL; - -finished: - pthread_rwlock_unlock(&rl_lock); - return ret; + return NULL; } /** - * Changes the cgroup of a program based on the rules in the config file. If a - * rule exists for the given UID or GID, then the given PID is placed into the - * correct group. By default, this function parses the configuration file each - * time it is called. - * - * The flags can alter the behavior of this function: - * CGFLAG_USECACHE: Use cached rules instead of parsing the config file + * 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. * - * This function may NOT be thread safe. + * This function may NOT be thread safe. * @param uid The UID to match * @param gid The GID to match - * @param pid The PID of the process to move - * @param flags Bit flags to change the behavior, as defined above - * @return 0 on success, > 0 on error - * TODO: Determine thread-safeness and fix of not safe. + * @param procname The PROCESS NAME to match + * @return Pointer to the first matching rule, or NULL if no match + * TODO: Determine thread-safeness and fix if not safe. */ -int cgroup_change_cgroup_uid_gid_flags(const uid_t uid, const gid_t gid, - const pid_t pid, const int flags) +static struct cgroup_rule *cgroup_find_matching_rule(const uid_t uid, + const gid_t gid, char *procname) +{ + /* Return value */ + struct cgroup_rule *ret = rl.head; + + pthread_rwlock_wrlock(&rl_lock); + while (ret) { + ret = cgroup_find_matching_rule_uid_gid(uid, gid, ret); + if (!ret) + break; + 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; + if (!strcmp(ret->procname, basename(procname))) + /* Check a rule of basename. */ + break; + ret = ret->next; + } + pthread_rwlock_unlock(&rl_lock); + + return ret; +} + +int cgroup_change_cgroup_flags(const uid_t uid, const gid_t gid, + char *procname, const pid_t pid, const int flags) { /* Temporary pointer to a rule */ struct cgroup_rule *tmp = NULL; @@ -1940,7 +1969,7 @@ int cgroup_change_cgroup_uid_gid_flags(const uid_t uid, const gid_t gid, */ if (!(flags & CGFLAG_USECACHE)) { cgroup_dbg("Not using cached rules for PID %d.\n", pid); - ret = cgroup_parse_rules(false, uid, gid); + ret = cgroup_parse_rules(false, uid, gid, procname); /* The configuration file has an error! We must exit now. */ if (ret != -1 && ret != 0) { @@ -1960,7 +1989,7 @@ int cgroup_change_cgroup_uid_gid_flags(const uid_t uid, const gid_t gid, tmp = trl.head; } else { /* Find the first matching rule in the cached list. */ - tmp = cgroup_find_matching_rule_uid_gid(uid, gid); + tmp = cgroup_find_matching_rule_uid_gid(uid, gid, procname); if (!tmp) { cgroup_dbg("No rule found to match PID: %d, UID: %d, " "GID: %d\n", pid, uid, gid); @@ -1994,6 +2023,12 @@ finished: return ret; } +int cgroup_change_cgroup_uid_gid_flags(const uid_t uid, const gid_t gid, + const pid_t pid, const int flags) +{ + return cgroup_change_cgroup_flags(uid, gid, NULL, pid, flags); +} + /** * Provides backwards-compatibility with older versions of the API. This * function is deprecated, and cgroup_change_cgroup_uid_gid_flags() should be @@ -2107,7 +2142,8 @@ int cgroup_reload_cached_rules() int ret = 0; cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE); - if ((ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID))) { + ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL); + if (ret) { cgroup_dbg("Error parsing configuration file \"%s\": %d.\n", CGRULES_CONF_FILE, ret); ret = ECGROUPPARSEFAIL; @@ -2132,7 +2168,7 @@ int cgroup_init_rules_cache() int ret = 0; /* Attempt to read the configuration file and cache the rules. */ - ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID); + ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL); if (ret) { cgroup_dbg("Could not initialize rule cache, error was: %d\n", ret); diff --git a/src/libcgroup.map b/src/libcgroup.map index a101ca1..2935d48 100644 --- a/src/libcgroup.map +++ b/src/libcgroup.map @@ -71,4 +71,5 @@ global: cgroup_get_uid_gid_from_procfs; cgroup_get_subsys_mount_point; cgroup_get_procname_from_procfs; + cgroup_change_cgroup_flags; } CGROUP_0.33; -- cgit