api.c was pretty big already and quite hard to maintain. Split it apart
with a separate file in order to make it a bit easier to keep track of
the rules as opposed to anything else.

Signed-off-by: Dhaval Giani <dhaval.gi...@gmail.com>
Cc: Vivek Goyal <vgo...@redhat.com>
---
 src/Makefile.am          |    2 
 src/api.c                |  938 ---------------------------------------------
 src/cgrules.c            |  974 +++++++++++++++++++++++++++++++++++++++++++++++
 src/libcgroup-internal.h |    5 
 4 files changed, 981 insertions(+), 938 deletions(-)

Index: libcg/src/api.c
===================================================================
--- libcg.orig/src/api.c
+++ libcg/src/api.c
@@ -65,21 +65,6 @@ static __thread char errtext[MAXLEN];
 /* Task command name length */
 #define TASK_COMM_LEN 16
 
-/* Check if cgroup_init has been called or not. */
-static int cgroup_initialized;
-
-/* Check if the rules cache has been loaded or not. */
-static bool cgroup_rules_loaded;
-
-/* List of configuration rules */
-static struct cgroup_rule_list rl;
-
-/* Temporary list of configuration rules (for non-cache apps) */
-static struct cgroup_rule_list trl;
-
-/* Lock for the list of rules (rl) */
-static pthread_rwlock_t rl_lock = PTHREAD_RWLOCK_INITIALIZER;
-
 /* Namespace */
 __thread char *cg_namespace_table[CG_CONTROLLER_MAX];
 
@@ -264,7 +249,7 @@ err:
 }
 
 
-static char *cgroup_basename(const char *path)
+char *cgroup_basename(const char *path)
 {
        char *base;
        char *tmp_string;
@@ -299,426 +284,6 @@ static int cgroup_test_subsys_mounted(co
 }
 
 /**
- * Free a single cgroup_rule struct.
- *     @param r The rule to free from memory
- */
-static void cgroup_free_rule(struct cgroup_rule *r)
-{
-       /* Loop variable */
-       int i = 0;
-
-       /* Make sure our rule is not NULL, first. */
-       if (!r) {
-               cgroup_dbg("Warning: Attempted to free NULL rule.\n");
-               return;
-       }
-       if (r->procname) {
-               free(r->procname);
-               r->procname = NULL;
-       }
-       /* We must free any used controller strings, too. */
-       for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
-               if (r->controllers[i])
-                       free(r->controllers[i]);
-       }
-
-       free(r);
-}
-
-/**
- * Free a list of cgroup_rule structs.  If rl is the main list of rules,
- * the lock must be taken for writing before calling this function!
- *     @param rl Pointer to the list of rules to free from memory
- */
-static void cgroup_free_rule_list(struct cgroup_rule_list *cg_rl)
-{
-       /* Temporary pointer */
-       struct cgroup_rule *tmp = NULL;
-
-       /* Make sure we're not freeing NULL memory! */
-       if (!(cg_rl->head)) {
-               cgroup_dbg("Warning: Attempted to free NULL list.\n");
-               return;
-       }
-
-       while (cg_rl->head) {
-               tmp = cg_rl->head;
-               cg_rl->head = tmp->next;
-               cgroup_free_rule(tmp);
-       }
-
-       /* Don't leave wild pointers around! */
-       cg_rl->head = NULL;
-       cg_rl->tail = NULL;
-}
-
-static char *cg_skip_unused_charactors_in_rule(char *rule)
-{
-       char *itr;
-
-       /* We ignore anything after a # sign as comments. */
-       itr = strchr(rule, '#');
-       if (itr)
-               *itr = '\0';
-
-       /* We also need to remove the newline character. */
-       itr = strchr(rule, '\n');
-       if (itr)
-               *itr = '\0';
-
-       /* Now, skip any leading tabs and spaces. */
-       itr = rule;
-       while (itr && isblank(*itr))
-               itr++;
-
-       /* If there's nothing left, we can ignore this line. */
-       if (!strlen(itr))
-               return NULL;
-
-       return itr;
-}
-
-/**
- * Parse the configuration file that maps UID/GIDs to cgroups.  If ever the
- * configuration file is modified, applications should call this function to
- * load the new configuration rules.  The function caller is responsible for
- * calling free() on each rule in the list.
- *
- * The cache parameter alters the behavior of this function.  If true, this
- * function will read the entire configuration file and store the results in
- * rl (global rules list).  If false, this function will only parse until it
- * finds a rule matching the given UID or GID.  It will store this rule in rl,
- * as well as any children rules (rules that begin with a %) that it has.
- *
- * This function is NOT thread safe!
- *     @param cache True to cache rules, else false
- *     @param muid If cache is false, the UID to match against
- *     @param mgid If cache is false, the GID to match against
- *     @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, const char *mprocname)
-{
-       /* File descriptor for the configuration file */
-       FILE *fp = NULL;
-
-       /* Buffer to store the line we're working on */
-       char buff[CGROUP_RULE_MAXLINE] = { '\0' };
-
-       /* Iterator for the line we're working on */
-       char *itr = NULL;
-
-       /* Pointer to process name in a line of the configuration file */
-       char *procname = NULL;
-
-       /* Pointer to the list that we're using */
-       struct cgroup_rule_list *lst = NULL;
-
-       /* Rule to add to the list */
-       struct cgroup_rule *newrule = NULL;
-
-       /* Structure to get GID from group name */
-       struct group *grp = NULL;
-
-       /* Structure to get UID from user name */
-       struct passwd *pwd = NULL;
-
-       /* Temporary storage for a configuration rule */
-       char key[CGROUP_RULE_MAXKEY] = { '\0' };
-       char user[LOGIN_NAME_MAX] = { '\0' };
-       char controllers[CG_CONTROLLER_MAX] = { '\0' };
-       char destination[FILENAME_MAX] = { '\0' };
-       uid_t uid = CGRULE_INVALID;
-       gid_t gid = CGRULE_INVALID;
-       size_t len_username;
-       int len_procname;
-
-       /* The current line number */
-       unsigned int linenum = 0;
-
-       /* Did we skip the previous line? */
-       bool skipped = false;
-
-       /* Have we found a matching rule (non-cache mode)? */
-       bool matched = false;
-
-       /* Return codes */
-       int ret = 0;
-
-       /* Temporary buffer for strtok() */
-       char *stok_buff = NULL;
-
-       /* Loop variable. */
-       int i = 0;
-
-       /* Open the configuration file. */
-       pthread_rwlock_wrlock(&rl_lock);
-       fp = fopen(CGRULES_CONF_FILE, "re");
-       if (!fp) {
-               cgroup_dbg("Failed to open configuration file %s with"
-                               " error: %s\n", CGRULES_CONF_FILE,
-                               strerror(errno));
-               last_errno = errno;
-               ret = ECGOTHER;
-               goto unlock;
-       }
-
-       /* 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;
-
-               /*
-                * 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;
-               }
-
-               /*
-                * 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;
-               }
-               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;
-                               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;
-                               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 (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);
-                       }
-               }
-
-               /*
-                * 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;
-               }
-
-               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;
-               }
-
-               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;
-                       }
-                       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;
-
-destroyrule:
-       cgroup_free_rule(newrule);
-
-parsefail:
-       ret = ECGRULESPARSEFAIL;
-
-close:
-       fclose(fp);
-unlock:
-       pthread_rwlock_unlock(&rl_lock);
-       return ret;
-}
-
-/**
  * cgroup_init(), initializes the MOUNT_POINT.
  *
  * This code is theoretically thread safe now. Its not really tested
@@ -2212,507 +1777,6 @@ unlock_error:
        return error;
 }
 
-/** cg_prepare_cgroup
- * Process the selected rule. Prepare the cgroup structure which can be
- * used to add the task to destination cgroup.
- *
- *
- *  returns 0 on success.
- */
-static int cg_prepare_cgroup(struct cgroup *cgroup, pid_t pid,
-                                       const char *dest,
-                                       const char * const controllers[])
-{
-       int ret = 0, i;
-       const char *controller = NULL;
-       struct cgroup_controller *cptr = NULL;
-
-       /* Fill in cgroup details.  */
-       cgroup_dbg("Will move pid %d to cgroup '%s'\n", pid, dest);
-
-       strcpy(cgroup->name, dest);
-
-       /* Scan all the controllers */
-       for (i = 0; i < CG_CONTROLLER_MAX; i++) {
-               int j = 0;
-               if (!controllers[i])
-                       return 0;
-               controller = controllers[i];
-
-               /* If first string is "*" that means all the mounted
-                * controllers. */
-               if (strcmp(controller, "*") == 0) {
-                       pthread_rwlock_rdlock(&cg_mount_table_lock);
-                       for (j = 0; j < CG_CONTROLLER_MAX &&
-                               cg_mount_table[j].name[0] != '\0'; j++) {
-                               cgroup_dbg("Adding controller %s\n",
-                                       cg_mount_table[j].name);
-                               cptr = cgroup_add_controller(cgroup,
-                                               cg_mount_table[j].name);
-                               if (!cptr) {
-                                       cgroup_dbg("Adding controller '%s'"
-                                               " failed\n",
-                                               cg_mount_table[j].name);
-                                       
pthread_rwlock_unlock(&cg_mount_table_lock);
-                                       cgroup_free_controllers(cgroup);
-                                       return ECGROUPNOTALLOWED;
-                               }
-                       }
-                       pthread_rwlock_unlock(&cg_mount_table_lock);
-                       return ret;
-               }
-
-               /* it is individual controller names and not "*" */
-               cgroup_dbg("Adding controller %s\n", controller);
-               cptr = cgroup_add_controller(cgroup, controller);
-               if (!cptr) {
-                       cgroup_dbg("Adding controller '%s' failed\n",
-                               controller);
-                       cgroup_free_controllers(cgroup);
-                       return ECGROUPNOTALLOWED;
-               }
-       }
-
-       return ret;
-}
-
-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)
-                               continue;
-
-                       /* Get the data for UID. */
-                       usr = getpwuid(uid);
-                       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])))
-                                       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.
- *
- * 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
- *     @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_t uid,
-                               gid_t gid, const char *procname)
-{
-       /* Return value */
-       struct cgroup_rule *ret = rl.head;
-       char *base = NULL;
-
-       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;
-
-               base = cgroup_basename(procname);
-               if (!strcmp(ret->procname, base))
-                       /* Check a rule of basename. */
-                       break;
-               ret = ret->next;
-               free(base);
-               base = NULL;
-       }
-       pthread_rwlock_unlock(&rl_lock);
-
-       if (base)
-               free(base);
-
-       return ret;
-}
-
-int cgroup_change_cgroup_flags(uid_t uid, gid_t gid,
-               const char *procname, pid_t pid, int flags)
-{
-       /* Temporary pointer to a rule */
-       struct cgroup_rule *tmp = NULL;
-
-       /* Temporary variables for destination substitution */
-       char newdest[FILENAME_MAX];
-       int i, j;
-       int written;
-       int available;
-       struct passwd *user_info;
-       struct group *group_info;
-
-       /* Return codes */
-       int ret = 0;
-
-       /* We need to check this before doing anything else! */
-       if (!cgroup_initialized) {
-               cgroup_dbg("libcgroup is not initialized\n");
-               ret = ECGROUPNOTINITIALIZED;
-               goto finished;
-       }
-
-       /*
-        * If the user did not ask for cached rules, we must parse the
-        * configuration to find a matching rule (if one exists).  Else, we'll
-        * find the first match in the cached list (rl).
-        */
-       if (!(flags & CGFLAG_USECACHE)) {
-               cgroup_dbg("Not using cached rules for PID %d.\n", pid);
-               ret = cgroup_parse_rules(false, uid, gid, procname);
-
-               /* The configuration file has an error!  We must exit now. */
-               if (ret != -1 && ret != 0) {
-                       cgroup_dbg("Failed to parse the configuration"
-                               " rules.\n");
-                       goto finished;
-               }
-
-               /* We did not find a matching rule, so we're done. */
-               if (ret == 0) {
-                       cgroup_dbg("No rule found to match PID: %d, UID: %d, "
-                               "GID: %d\n", pid, uid, gid);
-                       goto finished;
-               }
-
-               /* Otherwise, we did match a rule and it's in trl. */
-               tmp = trl.head;
-       } else {
-               /* Find the first matching rule in the cached list. */
-               tmp = cgroup_find_matching_rule(uid, gid, procname);
-               if (!tmp) {
-                       cgroup_dbg("No rule found to match PID: %d, UID: %d, "
-                               "GID: %d\n", pid, uid, gid);
-                       ret = 0;
-                       goto finished;
-               }
-       }
-       cgroup_dbg("Found matching rule %s for PID: %d, UID: %d, GID: %d\n",
-                       tmp->username, pid, uid, gid);
-
-       /* If we are here, then we found a matching rule, so execute it. */
-       do {
-               cgroup_dbg("Executing rule %s for PID %d... ", tmp->username,
-                                                               pid);
-               /* Destination substitutions */
-               for(j = i = 0; i < strlen(tmp->destination) &&
-                       (j < FILENAME_MAX - 2); ++i, ++j) {
-                       if(tmp->destination[i] == '%') {
-                               /* How many bytes did we write / error check */
-                               written = 0;
-                               /* How many bytes can we write */
-                               available = FILENAME_MAX - j - 2;
-                               /* Substitution */
-                               switch(tmp->destination[++i]) {
-                               case 'u':
-                                       written = snprintf(newdest+j, available,
-                                               "%d", uid);
-                                       break;
-                               case 'U':
-                                       user_info = getpwuid(uid);
-                                       if(user_info) {
-                                               written = snprintf(newdest + j,
-                                                       available, "%s",
-                                                       user_info -> pw_name);
-                                       } else {
-                                               written = snprintf(newdest + j,
-                                                       available, "%d", uid);
-                                       }
-                                       break;
-                               case 'g':
-                                       written = snprintf(newdest + j,
-                                               available, "%d", gid);
-                                       break;
-                               case 'G':
-                                       group_info = getgrgid(gid);
-                                       if(group_info) {
-                                               written = snprintf(newdest + j,
-                                                       available, "%s",
-                                                       group_info -> gr_name);
-                                       } else {
-                                               written = snprintf(newdest + j,
-                                                       available, "%d", gid);
-                                       }
-                                       break;
-                               case 'p':
-                                       written = snprintf(newdest + j,
-                                               available, "%d", pid);
-                                       break;
-                               case 'P':
-                                       if(procname) {
-                                               written = snprintf(newdest + j,
-                                                       available, "%s",
-                                                       procname);
-                                       } else {
-                                               written = snprintf(newdest + j,
-                                                       available, "%d", pid);
-                                       }
-                                       break;
-                               }
-                               written = min(written, available);
-                               /*
-                                * written<1 only when either error occurred
-                                * during snprintf or if no substitution was
-                                * made at all. In both cases, we want to just
-                                * copy input string.
-                                */
-                               if(written<1) {
-                                       newdest[j] = '%';
-                                       if(available>1)
-                                               newdest[++j] =
-                                                       tmp->destination[i];
-                               } else {
-                                       /*
-                                        * In next iteration, we will write
-                                        * just after the substitution, but j
-                                        * will get incremented in the
-                                        * meantime.
-                                        */
-                                       j += written - 1;
-                               }
-                       } else {
-                               if(tmp->destination[i] == '\\')
-                                       ++i;
-                               newdest[j] = tmp->destination[i];
-                       }
-               }
-               newdest[j] = 0;
-
-               /* Apply the rule */
-               ret = cgroup_change_cgroup_path(newdest,
-                               pid, (const char * const *)tmp->controllers);
-               if (ret) {
-                       cgroup_dbg("FAILED! (Error Code: %d)\n", ret);
-                       goto finished;
-               }
-               cgroup_dbg("OK!\n");
-
-               /* Now, check for multi-line rules.  As long as the "next"
-                * rule starts with '%', it's actually part of the rule that
-                * we just executed.
-                */
-               tmp = tmp->next;
-       } while (tmp && (tmp->username[0] == '%'));
-
-finished:
-       return ret;
-}
-
-int cgroup_change_cgroup_uid_gid_flags(uid_t uid, gid_t gid,
-                               pid_t pid, 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
- * used instead.  In fact, this function simply calls the newer one with flags
- * set to 0 (none).
- *     @param uid The UID to match
- *     @param gid The GID to match
- *     @param pid The PID of the process to move
- *     @return 0 on success, > 0 on error
- *
- */
-int cgroup_change_cgroup_uid_gid(uid_t uid, gid_t gid, pid_t pid)
-{
-       return cgroup_change_cgroup_uid_gid_flags(uid, gid, pid, 0);
-}
-
-/**
- * Changes the cgroup of a program based on the path provided.  In this case,
- * the user must already know into which cgroup the task should be placed and
- * no rules will be parsed.
- *
- *  returns 0 on success.
- */
-int cgroup_change_cgroup_path(const char *dest, pid_t pid,
-                               const char *const controllers[])
-{
-       int ret;
-       struct cgroup cgroup;
-
-       if (!cgroup_initialized) {
-               cgroup_dbg("libcgroup is not initialized\n");
-               return ECGROUPNOTINITIALIZED;
-       }
-       memset(&cgroup, 0, sizeof(struct cgroup));
-
-       ret = cg_prepare_cgroup(&cgroup, pid, dest, controllers);
-       if (ret)
-               return ret;
-       /* Add task to cgroup */
-       ret = cgroup_attach_task_pid(&cgroup, pid);
-       if (ret)
-               cgroup_dbg("cgroup_attach_task_pid failed:%d\n", ret);
-       cgroup_free_controllers(&cgroup);
-       return ret;
-}
-
-/**
- * Print the cached rules table.  This function should be called only after
- * first calling cgroup_parse_config(), but it will work with an empty rule
- * list.
- *     @param fp The file stream to print to
- */
-void cgroup_print_rules_config(FILE *fp)
-{
-       /* Iterator */
-       struct cgroup_rule *itr = NULL;
-
-       /* Loop variable */
-       int i = 0;
-
-       pthread_rwlock_rdlock(&rl_lock);
-
-       if (!(rl.head)) {
-               fprintf(fp, "The rules table is empty.\n\n");
-               pthread_rwlock_unlock(&rl_lock);
-               return;
-       }
-
-       itr = rl.head;
-       while (itr) {
-               fprintf(fp, "Rule: %s", itr->username);
-               if (itr->procname)
-                       fprintf(fp, ":%s", itr->procname);
-               fprintf(fp, "\n");
-
-               if (itr->uid == CGRULE_WILD)
-                       fprintf(fp, "  UID: any\n");
-               else if (itr->uid == CGRULE_INVALID)
-                       fprintf(fp, "  UID: N/A\n");
-               else
-                       fprintf(fp, "  UID: %d\n", itr->uid);
-
-               if (itr->gid == CGRULE_WILD)
-                       fprintf(fp, "  GID: any\n");
-               else if (itr->gid == CGRULE_INVALID)
-                       fprintf(fp, "  GID: N/A\n");
-               else
-                       fprintf(fp, "  GID: %d\n", itr->gid);
-
-               fprintf(fp, "  DEST: %s\n", itr->destination);
-
-               fprintf(fp, "  CONTROLLERS:\n");
-               for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
-                       if (itr->controllers[i])
-                               fprintf(fp, "    %s\n", itr->controllers[i]);
-               }
-               fprintf(fp, "\n");
-               itr = itr->next;
-       }
-       pthread_rwlock_unlock(&rl_lock);
-}
-
-/**
- * Reloads the rules list, using the given configuration file.  This function
- * is probably NOT thread safe (calls cgroup_parse_rules()).
- *     @return 0 on success, > 0 on failure
- */
-int cgroup_reload_cached_rules(void)
-{
-       /* Return codes */
-       int ret = 0;
-
-       cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE);
-       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 = ECGRULESPARSEFAIL;
-               goto finished;
-       }
-
-       #ifdef CGROUP_DEBUG
-               cgroup_print_rules_config(stdout);
-       #endif
-
-finished:
-       return ret;
-}
-
-/**
- * Initializes the rules cache.
- *     @return 0 on success, > 0 on error
- */
-int cgroup_init_rules_cache(void)
-{
-       /* Return codes */
-       int ret = 0;
-
-       /* Attempt to read the configuration file and cache the rules. */
-       ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
-       if (ret) {
-               cgroup_dbg("Could not initialize rule cache, error was: %d\n",
-                       ret);
-               cgroup_rules_loaded = false;
-       } else {
-               cgroup_rules_loaded = true;
-       }
-
-       return ret;
-}
 
 /**
  * cgroup_get_current_controller_path
Index: libcg/src/cgrules.c
===================================================================
--- /dev/null
+++ libcg/src/cgrules.c
@@ -0,0 +1,974 @@
+/*
+ * XX: Correct copyrights and Steve's email address
+ *
+ * Copyright Red Hat Inc. 2008
+ *
+ * Authors:
+ * 1. Vivek Goyal <vgo...@redhat.com>
+ * 2. Steve Olivieri <s...@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <libcgroup.h>
+#include <libcgroup-internal.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <fts.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <libgen.h>
+#include <assert.h>
+#include <linux/un.h>
+#include <grp.h>
+
+/* Check if the rules cache has been loaded or not. */
+static bool cgroup_rules_loaded;
+
+/* List of configuration rules */
+static struct cgroup_rule_list rl;
+
+/* Temporary list of configuration rules (for non-cache apps) */
+static struct cgroup_rule_list trl;
+
+/* Lock for the list of rules (rl) */
+static pthread_rwlock_t rl_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+/**
+ * Free a single cgroup_rule struct.
+ *     @param r The rule to free from memory
+ */
+static void cgroup_free_rule(struct cgroup_rule *r)
+{
+       /* Loop variable */
+       int i = 0;
+
+       /* Make sure our rule is not NULL, first. */
+       if (!r) {
+               cgroup_dbg("Warning: Attempted to free NULL rule.\n");
+               return;
+       }
+       if (r->procname) {
+               free(r->procname);
+               r->procname = NULL;
+       }
+       /* We must free any used controller strings, too. */
+       for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
+               if (r->controllers[i])
+                       free(r->controllers[i]);
+       }
+
+       free(r);
+}
+
+/**
+ * Free a list of cgroup_rule structs.  If rl is the main list of rules,
+ * the lock must be taken for writing before calling this function!
+ *     @param rl Pointer to the list of rules to free from memory
+ */
+static void cgroup_free_rule_list(struct cgroup_rule_list *cg_rl)
+{
+       /* Temporary pointer */
+       struct cgroup_rule *tmp = NULL;
+
+       /* Make sure we're not freeing NULL memory! */
+       if (!(cg_rl->head)) {
+               cgroup_dbg("Warning: Attempted to free NULL list.\n");
+               return;
+       }
+
+       while (cg_rl->head) {
+               tmp = cg_rl->head;
+               cg_rl->head = tmp->next;
+               cgroup_free_rule(tmp);
+       }
+
+       /* Don't leave wild pointers around! */
+       cg_rl->head = NULL;
+       cg_rl->tail = NULL;
+}
+
+static char *cg_skip_unused_charactors_in_rule(char *rule)
+{
+       char *itr;
+
+       /* We ignore anything after a # sign as comments. */
+       itr = strchr(rule, '#');
+       if (itr)
+               *itr = '\0';
+
+       /* We also need to remove the newline character. */
+       itr = strchr(rule, '\n');
+       if (itr)
+               *itr = '\0';
+
+       /* Now, skip any leading tabs and spaces. */
+       itr = rule;
+       while (itr && isblank(*itr))
+               itr++;
+
+       /* If there's nothing left, we can ignore this line. */
+       if (!strlen(itr))
+               return NULL;
+
+       return itr;
+}
+
+/**
+ * Parse the configuration file that maps UID/GIDs to cgroups.  If ever the
+ * configuration file is modified, applications should call this function to
+ * load the new configuration rules.  The function caller is responsible for
+ * calling free() on each rule in the list.
+ *
+ * The cache parameter alters the behavior of this function.  If true, this
+ * function will read the entire configuration file and store the results in
+ * rl (global rules list).  If false, this function will only parse until it
+ * finds a rule matching the given UID or GID.  It will store this rule in rl,
+ * as well as any children rules (rules that begin with a %) that it has.
+ *
+ * This function is NOT thread safe!
+ *     @param cache True to cache rules, else false
+ *     @param muid If cache is false, the UID to match against
+ *     @param mgid If cache is false, the GID to match against
+ *     @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, const char *mprocname)
+{
+       /* File descriptor for the configuration file */
+       FILE *fp = NULL;
+
+       /* Buffer to store the line we're working on */
+       char buff[CGROUP_RULE_MAXLINE] = { '\0' };
+
+       /* Iterator for the line we're working on */
+       char *itr = NULL;
+
+       /* Pointer to process name in a line of the configuration file */
+       char *procname = NULL;
+
+       /* Pointer to the list that we're using */
+       struct cgroup_rule_list *lst = NULL;
+
+       /* Rule to add to the list */
+       struct cgroup_rule *newrule = NULL;
+
+       /* Structure to get GID from group name */
+       struct group *grp = NULL;
+
+       /* Structure to get UID from user name */
+       struct passwd *pwd = NULL;
+
+       /* Temporary storage for a configuration rule */
+       char key[CGROUP_RULE_MAXKEY] = { '\0' };
+       char user[LOGIN_NAME_MAX] = { '\0' };
+       char controllers[CG_CONTROLLER_MAX] = { '\0' };
+       char destination[FILENAME_MAX] = { '\0' };
+       uid_t uid = CGRULE_INVALID;
+       gid_t gid = CGRULE_INVALID;
+       size_t len_username;
+       int len_procname;
+
+       /* The current line number */
+       unsigned int linenum = 0;
+
+       /* Did we skip the previous line? */
+       bool skipped = false;
+
+       /* Have we found a matching rule (non-cache mode)? */
+       bool matched = false;
+
+       /* Return codes */
+       int ret = 0;
+
+       /* Temporary buffer for strtok() */
+       char *stok_buff = NULL;
+
+       /* Loop variable. */
+       int i = 0;
+
+       /* Open the configuration file. */
+       pthread_rwlock_wrlock(&rl_lock);
+       fp = fopen(CGRULES_CONF_FILE, "re");
+       if (!fp) {
+               cgroup_dbg("Failed to open configuration file %s with"
+                               " error: %s\n", CGRULES_CONF_FILE,
+                               strerror(errno));
+               last_errno = errno;
+               ret = ECGOTHER;
+               goto unlock;
+       }
+
+       /* 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;
+
+               /*
+                * 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;
+               }
+
+               /*
+                * 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;
+               }
+               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;
+                               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;
+                               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 (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);
+                       }
+               }
+
+               /*
+                * 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;
+               }
+
+               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;
+               }
+
+               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;
+                       }
+                       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;
+
+destroyrule:
+       cgroup_free_rule(newrule);
+
+parsefail:
+       ret = ECGRULESPARSEFAIL;
+
+close:
+       fclose(fp);
+unlock:
+       pthread_rwlock_unlock(&rl_lock);
+       return ret;
+}
+
+/** cg_prepare_cgroup
+ * Process the selected rule. Prepare the cgroup structure which can be
+ * used to add the task to destination cgroup.
+ *
+ *
+ *  returns 0 on success.
+ */
+static int cg_prepare_cgroup(struct cgroup *cgroup, pid_t pid,
+                                       const char *dest,
+                                       const char * const controllers[])
+{
+       int ret = 0, i;
+       const char *controller = NULL;
+       struct cgroup_controller *cptr = NULL;
+
+       /* Fill in cgroup details.  */
+       cgroup_dbg("Will move pid %d to cgroup '%s'\n", pid, dest);
+
+       strcpy(cgroup->name, dest);
+
+       /* Scan all the controllers */
+       for (i = 0; i < CG_CONTROLLER_MAX; i++) {
+               int j = 0;
+               if (!controllers[i])
+                       return 0;
+               controller = controllers[i];
+
+               /* If first string is "*" that means all the mounted
+                * controllers. */
+               if (strcmp(controller, "*") == 0) {
+                       pthread_rwlock_rdlock(&cg_mount_table_lock);
+                       for (j = 0; j < CG_CONTROLLER_MAX &&
+                               cg_mount_table[j].name[0] != '\0'; j++) {
+                               cgroup_dbg("Adding controller %s\n",
+                                       cg_mount_table[j].name);
+                               cptr = cgroup_add_controller(cgroup,
+                                               cg_mount_table[j].name);
+                               if (!cptr) {
+                                       cgroup_dbg("Adding controller '%s'"
+                                               " failed\n",
+                                               cg_mount_table[j].name);
+                                       
pthread_rwlock_unlock(&cg_mount_table_lock);
+                                       cgroup_free_controllers(cgroup);
+                                       return ECGROUPNOTALLOWED;
+                               }
+                       }
+                       pthread_rwlock_unlock(&cg_mount_table_lock);
+                       return ret;
+               }
+
+               /* it is individual controller names and not "*" */
+               cgroup_dbg("Adding controller %s\n", controller);
+               cptr = cgroup_add_controller(cgroup, controller);
+               if (!cptr) {
+                       cgroup_dbg("Adding controller '%s' failed\n",
+                               controller);
+                       cgroup_free_controllers(cgroup);
+                       return ECGROUPNOTALLOWED;
+               }
+       }
+
+       return ret;
+}
+
+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)
+                               continue;
+
+                       /* Get the data for UID. */
+                       usr = getpwuid(uid);
+                       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])))
+                                       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.
+ *
+ * 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
+ *     @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_t uid,
+                               gid_t gid, const char *procname)
+{
+       /* Return value */
+       struct cgroup_rule *ret = rl.head;
+       char *base = NULL;
+
+       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;
+
+               base = cgroup_basename(procname);
+               if (!strcmp(ret->procname, base))
+                       /* Check a rule of basename. */
+                       break;
+               ret = ret->next;
+               free(base);
+               base = NULL;
+       }
+       pthread_rwlock_unlock(&rl_lock);
+
+       if (base)
+               free(base);
+
+       return ret;
+}
+
+int cgroup_change_cgroup_flags(uid_t uid, gid_t gid,
+               const char *procname, pid_t pid, int flags)
+{
+       /* Temporary pointer to a rule */
+       struct cgroup_rule *tmp = NULL;
+
+       /* Temporary variables for destination substitution */
+       char newdest[FILENAME_MAX];
+       int i, j;
+       int written;
+       int available;
+       struct passwd *user_info;
+       struct group *group_info;
+
+       /* Return codes */
+       int ret = 0;
+
+       /* We need to check this before doing anything else! */
+       if (!cgroup_initialized) {
+               cgroup_dbg("libcgroup is not initialized\n");
+               ret = ECGROUPNOTINITIALIZED;
+               goto finished;
+       }
+
+       /*
+        * If the user did not ask for cached rules, we must parse the
+        * configuration to find a matching rule (if one exists).  Else, we'll
+        * find the first match in the cached list (rl).
+        */
+       if (!(flags & CGFLAG_USECACHE)) {
+               cgroup_dbg("Not using cached rules for PID %d.\n", pid);
+               ret = cgroup_parse_rules(false, uid, gid, procname);
+
+               /* The configuration file has an error!  We must exit now. */
+               if (ret != -1 && ret != 0) {
+                       cgroup_dbg("Failed to parse the configuration"
+                               " rules.\n");
+                       goto finished;
+               }
+
+               /* We did not find a matching rule, so we're done. */
+               if (ret == 0) {
+                       cgroup_dbg("No rule found to match PID: %d, UID: %d, "
+                               "GID: %d\n", pid, uid, gid);
+                       goto finished;
+               }
+
+               /* Otherwise, we did match a rule and it's in trl. */
+               tmp = trl.head;
+       } else {
+               /* Find the first matching rule in the cached list. */
+               tmp = cgroup_find_matching_rule(uid, gid, procname);
+               if (!tmp) {
+                       cgroup_dbg("No rule found to match PID: %d, UID: %d, "
+                               "GID: %d\n", pid, uid, gid);
+                       ret = 0;
+                       goto finished;
+               }
+       }
+       cgroup_dbg("Found matching rule %s for PID: %d, UID: %d, GID: %d\n",
+                       tmp->username, pid, uid, gid);
+
+       /* If we are here, then we found a matching rule, so execute it. */
+       do {
+               cgroup_dbg("Executing rule %s for PID %d... ", tmp->username,
+                                                               pid);
+               /* Destination substitutions */
+               for(j = i = 0; i < strlen(tmp->destination) &&
+                       (j < FILENAME_MAX - 2); ++i, ++j) {
+                       if(tmp->destination[i] == '%') {
+                               /* How many bytes did we write / error check */
+                               written = 0;
+                               /* How many bytes can we write */
+                               available = FILENAME_MAX - j - 2;
+                               /* Substitution */
+                               switch(tmp->destination[++i]) {
+                               case 'u':
+                                       written = snprintf(newdest+j, available,
+                                               "%d", uid);
+                                       break;
+                               case 'U':
+                                       user_info = getpwuid(uid);
+                                       if(user_info) {
+                                               written = snprintf(newdest + j,
+                                                       available, "%s",
+                                                       user_info -> pw_name);
+                                       } else {
+                                               written = snprintf(newdest + j,
+                                                       available, "%d", uid);
+                                       }
+                                       break;
+                               case 'g':
+                                       written = snprintf(newdest + j,
+                                               available, "%d", gid);
+                                       break;
+                               case 'G':
+                                       group_info = getgrgid(gid);
+                                       if(group_info) {
+                                               written = snprintf(newdest + j,
+                                                       available, "%s",
+                                                       group_info -> gr_name);
+                                       } else {
+                                               written = snprintf(newdest + j,
+                                                       available, "%d", gid);
+                                       }
+                                       break;
+                               case 'p':
+                                       written = snprintf(newdest + j,
+                                               available, "%d", pid);
+                                       break;
+                               case 'P':
+                                       if(procname) {
+                                               written = snprintf(newdest + j,
+                                                       available, "%s",
+                                                       procname);
+                                       } else {
+                                               written = snprintf(newdest + j,
+                                                       available, "%d", pid);
+                                       }
+                                       break;
+                               }
+                               written = min(written, available);
+                               /*
+                                * written<1 only when either error occurred
+                                * during snprintf or if no substitution was
+                                * made at all. In both cases, we want to just
+                                * copy input string.
+                                */
+                               if(written<1) {
+                                       newdest[j] = '%';
+                                       if(available>1)
+                                               newdest[++j] =
+                                                       tmp->destination[i];
+                               } else {
+                                       /*
+                                        * In next iteration, we will write
+                                        * just after the substitution, but j
+                                        * will get incremented in the
+                                        * meantime.
+                                        */
+                                       j += written - 1;
+                               }
+                       } else {
+                               if(tmp->destination[i] == '\\')
+                                       ++i;
+                               newdest[j] = tmp->destination[i];
+                       }
+               }
+               newdest[j] = 0;
+
+               /* Apply the rule */
+               ret = cgroup_change_cgroup_path(newdest,
+                               pid, (const char * const *)tmp->controllers);
+               if (ret) {
+                       cgroup_dbg("FAILED! (Error Code: %d)\n", ret);
+                       goto finished;
+               }
+               cgroup_dbg("OK!\n");
+
+               /* Now, check for multi-line rules.  As long as the "next"
+                * rule starts with '%', it's actually part of the rule that
+                * we just executed.
+                */
+               tmp = tmp->next;
+       } while (tmp && (tmp->username[0] == '%'));
+
+finished:
+       return ret;
+}
+
+int cgroup_change_cgroup_uid_gid_flags(uid_t uid, gid_t gid,
+                               pid_t pid, 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
+ * used instead.  In fact, this function simply calls the newer one with flags
+ * set to 0 (none).
+ *     @param uid The UID to match
+ *     @param gid The GID to match
+ *     @param pid The PID of the process to move
+ *     @return 0 on success, > 0 on error
+ *
+ */
+int cgroup_change_cgroup_uid_gid(uid_t uid, gid_t gid, pid_t pid)
+{
+       return cgroup_change_cgroup_uid_gid_flags(uid, gid, pid, 0);
+}
+
+/**
+ * Changes the cgroup of a program based on the path provided.  In this case,
+ * the user must already know into which cgroup the task should be placed and
+ * no rules will be parsed.
+ *
+ *  returns 0 on success.
+ */
+int cgroup_change_cgroup_path(const char *dest, pid_t pid,
+                               const char *const controllers[])
+{
+       int ret;
+       struct cgroup cgroup;
+
+       if (!cgroup_initialized) {
+               cgroup_dbg("libcgroup is not initialized\n");
+               return ECGROUPNOTINITIALIZED;
+       }
+       memset(&cgroup, 0, sizeof(struct cgroup));
+
+       ret = cg_prepare_cgroup(&cgroup, pid, dest, controllers);
+       if (ret)
+               return ret;
+       /* Add task to cgroup */
+       ret = cgroup_attach_task_pid(&cgroup, pid);
+       if (ret)
+               cgroup_dbg("cgroup_attach_task_pid failed:%d\n", ret);
+       cgroup_free_controllers(&cgroup);
+       return ret;
+}
+
+/**
+ * Print the cached rules table.  This function should be called only after
+ * first calling cgroup_parse_config(), but it will work with an empty rule
+ * list.
+ *     @param fp The file stream to print to
+ */
+void cgroup_print_rules_config(FILE *fp)
+{
+       /* Iterator */
+       struct cgroup_rule *itr = NULL;
+
+       /* Loop variable */
+       int i = 0;
+
+       pthread_rwlock_rdlock(&rl_lock);
+
+       if (!(rl.head)) {
+               fprintf(fp, "The rules table is empty.\n\n");
+               pthread_rwlock_unlock(&rl_lock);
+               return;
+       }
+
+       itr = rl.head;
+       while (itr) {
+               fprintf(fp, "Rule: %s", itr->username);
+               if (itr->procname)
+                       fprintf(fp, ":%s", itr->procname);
+               fprintf(fp, "\n");
+
+               if (itr->uid == CGRULE_WILD)
+                       fprintf(fp, "  UID: any\n");
+               else if (itr->uid == CGRULE_INVALID)
+                       fprintf(fp, "  UID: N/A\n");
+               else
+                       fprintf(fp, "  UID: %d\n", itr->uid);
+
+               if (itr->gid == CGRULE_WILD)
+                       fprintf(fp, "  GID: any\n");
+               else if (itr->gid == CGRULE_INVALID)
+                       fprintf(fp, "  GID: N/A\n");
+               else
+                       fprintf(fp, "  GID: %d\n", itr->gid);
+
+               fprintf(fp, "  DEST: %s\n", itr->destination);
+
+               fprintf(fp, "  CONTROLLERS:\n");
+               for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
+                       if (itr->controllers[i])
+                               fprintf(fp, "    %s\n", itr->controllers[i]);
+               }
+               fprintf(fp, "\n");
+               itr = itr->next;
+       }
+       pthread_rwlock_unlock(&rl_lock);
+}
+
+/**
+ * Reloads the rules list, using the given configuration file.  This function
+ * is probably NOT thread safe (calls cgroup_parse_rules()).
+ *     @return 0 on success, > 0 on failure
+ */
+int cgroup_reload_cached_rules(void)
+{
+       /* Return codes */
+       int ret = 0;
+
+       cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE);
+       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 = ECGRULESPARSEFAIL;
+               goto finished;
+       }
+
+       #ifdef CGROUP_DEBUG
+               cgroup_print_rules_config(stdout);
+       #endif
+
+finished:
+       return ret;
+}
+
+/**
+ * Initializes the rules cache.
+ *     @return 0 on success, > 0 on error
+ */
+int cgroup_init_rules_cache(void)
+{
+       /* Return codes */
+       int ret = 0;
+
+       /* Attempt to read the configuration file and cache the rules. */
+       ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
+       if (ret) {
+               cgroup_dbg("Could not initialize rule cache, error was: %d\n",
+                       ret);
+               cgroup_rules_loaded = false;
+       } else {
+               cgroup_rules_loaded = true;
+       }
+
+       return ret;
+}
Index: libcg/src/Makefile.am
===================================================================
--- libcg.orig/src/Makefile.am
+++ libcg/src/Makefile.am
@@ -7,7 +7,7 @@ CLEANFILES = lex.c parse.c parse.h
 
 INCLUDES = -I$(top_srcdir)/include
 lib_LTLIBRARIES = libcgroup.la
-libcgroup_la_SOURCES = parse.h parse.y lex.l api.c config.c 
libcgroup-internal.h libcgroup.map wrapper.c
+libcgroup_la_SOURCES = parse.h parse.y lex.l api.c config.c 
libcgroup-internal.h libcgroup.map wrapper.c cgrules.c
 libcgroup_la_LIBADD = -lpthread
 libcgroup_la_LDFLAGS = -Wl,--version-script,$(srcdir)/libcgroup.map \
        -version-number 
$(LIBRARY_VERSION_MAJOR):$(LIBRARY_VERSION_MINOR):$(LIBRARY_VERSION_RELEASE)
Index: libcg/src/libcgroup-internal.h
===================================================================
--- libcg.orig/src/libcgroup-internal.h
+++ libcg/src/libcgroup-internal.h
@@ -67,6 +67,11 @@ __BEGIN_DECLS
 #define max(x,y) ((y)<(x)?(x):(y))
 #define min(x,y) ((y)>(x)?(x):(y))
 
+/* Check if cgroup_init has been called or not. */
+int cgroup_initialized;
+
+char *cgroup_basename(const char *path);
+
 struct control_value {
        char name[FILENAME_MAX];
        char value[CG_VALUE_MAX];



------------------------------------------------------------------------------
The modern datacenter depends on network connectivity to access resources
and provide services. The best practices for maximizing a physical server's
connectivity to a physical network are well understood - see how these
rules translate into the virtual world? 
http://p.sf.net/sfu/oracle-sfdevnlfb
_______________________________________________
Libcg-devel mailing list
Libcg-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libcg-devel

Reply via email to