We need to make it possible to use multiple configuration files so that packages can just insert their own configuration files at the time of installation as opposed to doing a messy appending/removing from a common file.
This patch makes it possible. By default it searchesf or the configuration files in /etc/cgrules.d but that still needs to be discussed out. Signed-off-by: Dhaval Giani <dhaval.gi...@gmail.com> --- src/cgrules.c | 480 +++++++++++++++++++++++++---------------------- src/daemon/cgrulesengd.c | 14 - src/libcgroup-internal.h | 4 3 files changed, 270 insertions(+), 228 deletions(-) Index: libcg/src/cgrules.c =================================================================== --- libcg.orig/src/cgrules.c +++ libcg/src/cgrules.c @@ -38,6 +38,7 @@ #include <assert.h> #include <linux/un.h> #include <grp.h> +#include <stddef.h> /* Check if the rules cache has been loaded or not. */ static bool cgroup_rules_loaded; @@ -205,269 +206,310 @@ static int cgroup_parse_rules(bool cache /* Loop variable. */ int i = 0; - /* Open the configuration file. */ - pthread_rwlock_wrlock(&rl_lock); - fp = fopen(cgrules_config_file, "re"); - if (!fp) { - cgroup_dbg("Failed to open configuration file %s with" - " error: %s\n", cgrules_config_file_file, - strerror(errno)); + DIR *cgrules_dir; + struct dirent *curr_ent = NULL; + int len; + struct dirent *dir_buff; + int read_ret; + + cgrules_dir = opendir(cgrules_config_dir); + if (!cgrules_dir) { last_errno = errno; ret = ECGOTHER; - goto unlock; + goto fail_dir; } - /* Determine which list we're using. */ - if (cache) - lst = &rl; - else - lst = &trl; - - /* If our list already exists, clean it. */ - if (lst->head) - cgroup_free_rule_list(lst); - - /* Now, parse the configuration file one line at a time. */ - cgroup_dbg("Parsing configuration file.\n"); - while (fgets(buff, sizeof(buff), fp) != NULL) { - linenum++; + len = offsetof(struct dirent, d_name) + + pathconf(cgrules_config_dir, _PC_NAME_MAX) + 1; + dir_buff = malloc(len); + if (!dir_buff) { + last_errno = errno; + ret = ECGOTHER; + goto fail_dir; + } - itr = cg_skip_unused_charactors_in_rule(buff); - if (!itr) - continue; + + /* Open the configuration file. */ + pthread_rwlock_wrlock(&rl_lock); + + read_ret = readdir_r(cgrules_dir, curr_ent, &dir_buff); + + while (read_ret == 0 && curr_ent) { + char *cgrules_config_file; /* - * If we skipped the last rule and this rule is a continuation - * of it (begins with %), then we should skip this rule too. + * Well, we need a better way to handle DT_UNKNOWN, but for now + * we just pray to $DEITY that we can handle it :-) */ - if (skipped && *itr == '%') { - cgroup_dbg("Warning: Skipped child of invalid rule," - " line %d.\n", linenum); + if (curr_ent->d_type != DT_REG || curr_ent->d_type != DT_UNKNOWN) continue; - } - /* - * If there is something left, it should be a rule. Otherwise, - * there's an error in the configuration file. - */ - skipped = false; - i = sscanf(itr, "%s%s%s", key, controllers, destination); - if (i != 3) { - cgroup_dbg("Failed to parse configuration file on" - " line %d.\n", linenum); - goto parsefail; + cgrules_config_file = strdup(curr_ent->d_name); + fp = fopen(cgrules_config_file, "re"); + if (!fp) { + cgroup_dbg("Failed to open configuration file %s with" + " error: %s\n", cgrules_config_file, + strerror(errno)); + last_errno = errno; + ret = ECGOTHER; + goto unlock; } - procname = strchr(key, ':'); - if (procname) { - /* <user>:<procname> <subsystem> <destination> */ - procname++; /* skip ':' */ - len_username = procname - key - 1; - len_procname = strlen(procname); - if (len_procname < 0) { - cgroup_dbg("Failed to parse configuration file" - " on line %d.\n", linenum); - goto parsefail; - } - } else { - len_username = strlen(key); - len_procname = 0; - } - len_username = min(len_username, sizeof(user) - 1); - memset(user, '\0', sizeof(user)); - strncpy(user, key, len_username); - /* - * Next, check the user/group. If it's a % sign, then we - * are continuing another rule and UID/GID should not be - * reset. If it's a @, we're dealing with a GID rule. If - * it's a *, then we do not need to do a lookup because the - * rule always applies (it's a wildcard). If we're using - * non-cache mode and we've found a matching rule, we only - * continue to parse if we're looking at a child rule. - */ - if ((!cache) && matched && (strncmp(user, "%", 1) != 0)) { - /* If we make it here, we finished (non-cache). */ - cgroup_dbg("Parsing of configuration file" - " complete.\n\n"); - ret = -1; - goto close; - } - if (strncmp(user, "@", 1) == 0) { - /* New GID rule. */ - itr = &(user[1]); - grp = getgrnam(itr); - if (grp) { - uid = CGRULE_INVALID; - gid = grp->gr_gid; - } else { - cgroup_dbg("Warning: Entry for %s not" - "found. Skipping rule on line" - " %d.\n", itr, linenum); - skipped = true; + /* Determine which list we're using. */ + if (cache) + lst = &rl; + else + lst = &trl; + + /* If our list already exists, clean it. */ + if (lst->head) + cgroup_free_rule_list(lst); + + /* Now, parse the configuration file one line at a time. */ + cgroup_dbg("Parsing configuration file.\n"); + while (fgets(buff, sizeof(buff), fp) != NULL) { + linenum++; + + itr = cg_skip_unused_charactors_in_rule(buff); + if (!itr) continue; - } - } else if (strncmp(user, "*", 1) == 0) { - /* Special wildcard rule. */ - uid = CGRULE_WILD; - gid = CGRULE_WILD; - } else if (*itr != '%') { - /* New UID rule. */ - pwd = getpwnam(user); - if (pwd) { - uid = pwd->pw_uid; - gid = CGRULE_INVALID; - } else { - cgroup_dbg("Warning: Entry for %s not" - "found. Skipping rule on line" - " %d.\n", user, linenum); - skipped = true; + + /* + * If we skipped the last rule and this rule is a continuation + * of it (begins with %), then we should skip this rule too. + */ + if (skipped && *itr == '%') { + cgroup_dbg("Warning: Skipped child of invalid rule," + " line %d.\n", linenum); continue; } - } /* Else, we're continuing another rule (UID/GID are okay). */ - /* - * If we are not caching rules, then we need to check for a - * match before doing anything else. We consider four cases: - * The UID matches, the GID matches, the UID is a member of the - * GID, or we're looking at the wildcard rule, which always - * matches. If none of these are true, we simply continue to - * the next line in the file. - */ - if (grp && muid != CGRULE_INVALID) { - pwd = getpwuid(muid); - for (i = 0; grp->gr_mem[i]; i++) { - if (!(strcmp(pwd->pw_name, grp->gr_mem[i]))) - matched = true; + /* + * If there is something left, it should be a rule. Otherwise, + * there's an error in the configuration file. + */ + skipped = false; + i = sscanf(itr, "%s%s%s", key, controllers, destination); + if (i != 3) { + cgroup_dbg("Failed to parse configuration file on" + " line %d.\n", linenum); + goto parsefail; } - } - - if (uid == muid || gid == mgid || uid == CGRULE_WILD) - matched = true; - - if (!cache) { - if (!matched) - continue; - if (len_procname) { - char *mproc_base; - /* - * If there is a rule based on process name, - * it should be matched with mprocname. - */ - if (!mprocname) { + procname = strchr(key, ':'); + if (procname) { + /* <user>:<procname> <subsystem> <destination> */ + procname++; /* skip ':' */ + len_username = procname - key - 1; + len_procname = strlen(procname); + if (len_procname < 0) { + cgroup_dbg("Failed to parse configuration file" + " on line %d.\n", linenum); + goto parsefail; + } + } else { + len_username = strlen(key); + len_procname = 0; + } + len_username = min(len_username, sizeof(user) - 1); + memset(user, '\0', sizeof(user)); + strncpy(user, key, len_username); + + /* + * Next, check the user/group. If it's a % sign, then we + * are continuing another rule and UID/GID should not be + * reset. If it's a @, we're dealing with a GID rule. If + * it's a *, then we do not need to do a lookup because the + * rule always applies (it's a wildcard). If we're using + * non-cache mode and we've found a matching rule, we only + * continue to parse if we're looking at a child rule. + */ + if ((!cache) && matched && (strncmp(user, "%", 1) != 0)) { + /* If we make it here, we finished (non-cache). */ + cgroup_dbg("Parsing of configuration file" + " complete.\n\n"); + ret = -1; + goto close; + } + if (strncmp(user, "@", 1) == 0) { + /* New GID rule. */ + itr = &(user[1]); + grp = getgrnam(itr); + if (grp) { uid = CGRULE_INVALID; - gid = CGRULE_INVALID; - matched = false; + gid = grp->gr_gid; + } else { + cgroup_dbg("Warning: Entry for %s not" + "found. Skipping rule on line" + " %d.\n", itr, linenum); + skipped = true; continue; } - - mproc_base = cgroup_basename(mprocname); - if (strcmp(mprocname, procname) && - strcmp(mproc_base, procname)) { - uid = CGRULE_INVALID; + } else if (strncmp(user, "*", 1) == 0) { + /* Special wildcard rule. */ + uid = CGRULE_WILD; + gid = CGRULE_WILD; + } else if (*itr != '%') { + /* New UID rule. */ + pwd = getpwnam(user); + if (pwd) { + uid = pwd->pw_uid; gid = CGRULE_INVALID; - matched = false; - free(mproc_base); + } else { + cgroup_dbg("Warning: Entry for %s not" + "found. Skipping rule on line" + " %d.\n", user, linenum); + skipped = true; continue; } - free(mproc_base); + } /* Else, we're continuing another rule (UID/GID are okay). */ + + /* + * If we are not caching rules, then we need to check for a + * match before doing anything else. We consider four cases: + * The UID matches, the GID matches, the UID is a member of the + * GID, or we're looking at the wildcard rule, which always + * matches. If none of these are true, we simply continue to + * the next line in the file. + */ + if (grp && muid != CGRULE_INVALID) { + pwd = getpwuid(muid); + for (i = 0; grp->gr_mem[i]; i++) { + if (!(strcmp(pwd->pw_name, grp->gr_mem[i]))) + matched = true; + } } - } - /* - * Now, we're either caching rules or we found a match. Either - * way, copy everything into a new rule and push it into the - * list. - */ - newrule = calloc(1, sizeof(struct cgroup_rule)); - if (!newrule) { - cgroup_dbg("Out of memory? Error: %s\n", - strerror(errno)); - last_errno = errno; - ret = ECGOTHER; - goto close; - } + if (uid == muid || gid == mgid || uid == CGRULE_WILD) + matched = true; + + if (!cache) { + if (!matched) + continue; + if (len_procname) { + char *mproc_base; + /* + * 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; + } + + mproc_base = cgroup_basename(mprocname); + if (strcmp(mprocname, procname) && + strcmp(mproc_base, procname)) { + uid = CGRULE_INVALID; + gid = CGRULE_INVALID; + matched = false; + free(mproc_base); + continue; + } + free(mproc_base); + } + } - newrule->uid = uid; - newrule->gid = gid; - len_username = min(len_username, - sizeof(newrule->username) - 1); - strncpy(newrule->username, user, len_username); - if (len_procname) { - newrule->procname = strdup(procname); - if (!newrule->procname) { + /* + * Now, we're either caching rules or we found a match. Either + * way, copy everything into a new rule and push it into the + * list. + */ + newrule = calloc(1, sizeof(struct cgroup_rule)); + if (!newrule) { + cgroup_dbg("Out of memory? Error: %s\n", + strerror(errno)); last_errno = errno; ret = ECGOTHER; goto close; } - } else { - newrule->procname = NULL; - } - strncpy(newrule->destination, destination, - sizeof(newrule->destination) - 1); - newrule->next = NULL; - - /* Parse the controller list, and add that to newrule too. */ - stok_buff = strtok(controllers, ","); - if (!stok_buff) { - cgroup_dbg("Failed to parse controllers on line" - " %d\n", linenum); - goto destroyrule; - } - i = 0; - do { - if (i >= MAX_MNT_ELEMENTS) { - cgroup_dbg("Too many controllers listed" - " on line %d\n", linenum); + newrule->uid = uid; + newrule->gid = gid; + len_username = min(len_username, + sizeof(newrule->username) - 1); + strncpy(newrule->username, user, len_username); + if (len_procname) { + newrule->procname = strdup(procname); + if (!newrule->procname) { + last_errno = errno; + ret = ECGOTHER; + goto close; + } + } else { + newrule->procname = NULL; + } + strncpy(newrule->destination, destination, + sizeof(newrule->destination) - 1); + newrule->next = NULL; + + /* Parse the controller list, and add that to newrule too. */ + stok_buff = strtok(controllers, ","); + if (!stok_buff) { + cgroup_dbg("Failed to parse controllers on line" + " %d\n", linenum); goto destroyrule; } - newrule->controllers[i] = strndup(stok_buff, + i = 0; + do { + if (i >= MAX_MNT_ELEMENTS) { + cgroup_dbg("Too many controllers listed" + " on line %d\n", linenum); + goto destroyrule; + } + + newrule->controllers[i] = strndup(stok_buff, strlen(stok_buff) + 1); - if (!(newrule->controllers[i])) { - cgroup_dbg("Out of memory? Error was: %s\n", - strerror(errno)); - goto destroyrule; + if (!(newrule->controllers[i])) { + cgroup_dbg("Out of memory? Error was: %s\n", + strerror(errno)); + goto destroyrule; + } + i++; + } while ((stok_buff = strtok(NULL, ","))); + + /* Now, push the rule. */ + if (lst->head == NULL) { + lst->head = newrule; + lst->tail = newrule; + } else { + lst->tail->next = newrule; + lst->tail = newrule; } - i++; - } while ((stok_buff = strtok(NULL, ","))); - /* Now, push the rule. */ - if (lst->head == NULL) { - lst->head = newrule; - lst->tail = newrule; - } else { - lst->tail->next = newrule; - lst->tail = newrule; - } - - cgroup_dbg("Added rule %s (UID: %d, GID: %d) -> %s for" - " controllers:", lst->tail->username, lst->tail->uid, - lst->tail->gid, lst->tail->destination); - for (i = 0; lst->tail->controllers[i]; i++) - cgroup_dbg(" %s", lst->tail->controllers[i]); - cgroup_dbg("\n"); - - /* Finally, clear the buffer. */ - grp = NULL; - pwd = NULL; - } - - /* If we make it here, there were no errors. */ - cgroup_dbg("Parsing of configuration file complete.\n\n"); - ret = (matched && !cache) ? -1 : 0; - goto close; + cgroup_dbg("Added rule %s (UID: %d, GID: %d) -> %s for" + " controllers:", lst->tail->username, lst->tail->uid, + lst->tail->gid, lst->tail->destination); + for (i = 0; lst->tail->controllers[i]; i++) + cgroup_dbg(" %s", lst->tail->controllers[i]); + cgroup_dbg("\n"); + + /* Finally, clear the buffer. */ + grp = NULL; + pwd = NULL; + } + + /* If we make it here, there were no errors. */ + cgroup_dbg("Parsing of configuration file complete.\n\n"); + ret = (matched && !cache) ? -1 : 0; + fclose(fp); + read_ret = readdir_r(cgrules_dir, curr_ent, &dir_buff); + } + closedir(cgrules_dir); + goto unlock; destroyrule: cgroup_free_rule(newrule); parsefail: ret = ECGRULESPARSEFAIL; - close: fclose(fp); unlock: pthread_rwlock_unlock(&rl_lock); +fail_dir: return ret; } @@ -841,11 +883,11 @@ int cgroup_reload_cached_rules(void) /* Return codes */ int ret = 0; - cgroup_dbg("Reloading cached rules from %s.\n", cgrules_config_file); + cgroup_dbg("Reloading cached rules from %s.\n", cgrules_config_dir); ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL); if (ret) { - cgroup_dbg("Error parsing configuration file \"%s\": %d.\n", - cgrules_config_file, ret); + cgroup_dbg("Error parsing configuration directory\"%s\": %d.\n", + cgrules_config_dir, ret); ret = ECGRULESPARSEFAIL; goto finished; } Index: libcg/src/daemon/cgrulesengd.c =================================================================== --- libcg.orig/src/daemon/cgrulesengd.c +++ libcg/src/daemon/cgrulesengd.c @@ -976,7 +976,7 @@ int main(int argc, char *argv[]) {NULL, 0, NULL, 0} }; - cgrules_config_file = NULL; + cgrules_config_dir = NULL; /* Make sure the user is root. */ if (getuid() != 0) { @@ -1063,8 +1063,8 @@ int main(int argc, char *argv[]) optarg, (int)socket_group); break; case 'c': /* config_file */ - cgrules_config_file = strdup(optarg); - if (!cgrules_config_file) { + cgrules_config_dir = strdup(optarg); + if (!cgrules_config_dir) { ret = 4; goto finished; } @@ -1076,9 +1076,9 @@ int main(int argc, char *argv[]) } } - if (!cgrules_config_file) { - cgrules_config_file = strdup(CGRULES_DEFAULT_CONFIG); - if (!cgrules_config_file) { + if (!cgrules_config_dir) { + cgrules_config_dir = strdup(CGRULES_DEFAULT_CONFIG); + if (!cgrules_config_dir) { fprintf(stderr, "Failed to set the correct"\ " configuration\n"); goto finished; @@ -1095,7 +1095,7 @@ int main(int argc, char *argv[]) /* Ask libcgroup to load the configuration rules. */ if ((ret = cgroup_init_rules_cache()) != 0) { fprintf(stderr, "Error: libcgroup failed to initialize rules" - "cache from %s. %s\n", cgrules_config_file, + "cache from %s. %s\n", cgrules_config_dir, cgroup_strerror(ret)); goto finished; } Index: libcg/src/libcgroup-internal.h =================================================================== --- libcg.orig/src/libcgroup-internal.h +++ libcg/src/libcgroup-internal.h @@ -46,7 +46,7 @@ __BEGIN_DECLS #define CGRULE_SUCCESS_STORE_PID "SUCCESS_STORE_PID" -#define CGRULES_DEFAULT_CONFIG "/etc/cgrules.conf" +#define CGRULES_DEFAULT_CONFIG "/etc/cgrules.d/" #define CGRULES_MAX_FIELDS_PER_LINE 3 #define CGROUP_BUFFER_LEN (5 * FILENAME_MAX) @@ -67,7 +67,7 @@ __BEGIN_DECLS #define max(x,y) ((y)<(x)?(x):(y)) #define min(x,y) ((y)>(x)?(x):(y)) -char *cgrules_config_file; +char *cgrules_config_dir; /* Check if cgroup_init has been called or not. */ int cgroup_initialized; ------------------------------------------------------------------------------ Forrester recently released a report on the Return on Investment (ROI) of Google Apps. They found a 300% ROI, 38%-56% cost savings, and break-even within 7 months. Over 3 million businesses have gone Google with Google Apps: an online email calendar, and document program that's accessible from your browser. Read the Forrester report: http://p.sf.net/sfu/googleapps-sfnew _______________________________________________ Libcg-devel mailing list Libcg-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libcg-devel