From f8e05fc8c129a13fed256b03a23537ef94c77152 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Fri, 13 Mar 2009 15:16:19 +0100 Subject: Distribute files to various subdirectories Signed-off-by: Jan Safranek --- src/api.c | 2320 ++++++++++++++++++++++++++++++++++++++++++++++ src/config.c | 496 ++++++++++ src/daemon/cgrulesengd.c | 793 ++++++++++++++++ src/daemon/cgrulesengd.h | 127 +++ src/lex.l | 34 + src/libcgroup-internal.h | 105 +++ src/libcgroup.map | 56 ++ src/pam/pam_cgroup.c | 162 ++++ src/parse.y | 269 ++++++ src/tools/cgclassify.c | 219 +++++ src/tools/cgconfig.c | 74 ++ src/tools/cgexec.c | 122 +++ src/tools/tools-common.c | 88 ++ src/tools/tools-common.h | 54 ++ src/wrapper.c | 557 +++++++++++ 15 files changed, 5476 insertions(+) create mode 100644 src/api.c create mode 100644 src/config.c create mode 100644 src/daemon/cgrulesengd.c create mode 100644 src/daemon/cgrulesengd.h create mode 100644 src/lex.l create mode 100644 src/libcgroup-internal.h create mode 100644 src/libcgroup.map create mode 100644 src/pam/pam_cgroup.c create mode 100644 src/parse.y create mode 100644 src/tools/cgclassify.c create mode 100644 src/tools/cgconfig.c create mode 100644 src/tools/cgexec.c create mode 100644 src/tools/tools-common.c create mode 100644 src/tools/tools-common.h create mode 100644 src/wrapper.c (limited to 'src') diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000..28365b4 --- /dev/null +++ b/src/api.c @@ -0,0 +1,2320 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Author: Dhaval Giani + * Author: Balbir Singh + * + * 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. + * + * TODOs: + * 1. Convert comments to Docbook style. + * 2. Add more APIs for the control groups. + * 3. Handle the configuration related APIs. + * 4. Error handling. + * + * Code initiated and designed by Dhaval Giani. All faults are most likely + * his mistake. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PACKAGE_VERSION +#define PACKAGE_VERSION 0.01 +#endif + +#define VERSION(ver) #ver + +/* + * The errno which happend the last time (have to be thread specific) + */ +__thread int last_errno; + +#define MAXLEN 256 + +/* the value have to be thread specific */ +__thread char errtext[MAXLEN]; + +/* + * Remember to bump this up for major API changes. + */ +const static char cg_version[] = VERSION(PACKAGE_VERSION); + +struct cg_mount_table_s cg_mount_table[CG_CONTROLLER_MAX]; +static pthread_rwlock_t cg_mount_table_lock = PTHREAD_RWLOCK_INITIALIZER; + +/* 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; + +char *cgroup_strerror_codes[] = { + "Cgroup is not compiled in", + "Cgroup is not mounted", + "Cgroup does not exist", + "Cgroup has not been created", + "Cgroup one of the needed subsystems is not mounted", + "Cgroup, request came in from non owner", + "Cgroup controllers controllers are bound to different mount points", + "Cgroup, operation not allowed", + "Cgroup value set exceeds maximum", + "Cgroup controller already exists", + "Cgroup value already exists", + "Cgroup invalid operation", + "Cgroup, creation of controller failed", + "Cgroup operation failed", + "Cgroup not initialized", + "Cgroup trying to set value for control that does not exist", + "Cgroup generic error, see errno", + "Cgroup values are not equal", + "Cgroup controllers are different", + "Cgroup parsing failed", + "Cgroup, rules file does not exist", + "Cgroup mounting failed", + "The config file can not be opend", + "End of File or iterator", +}; + +static int cg_chown_file(FTS *fts, FTSENT *ent, uid_t owner, gid_t group) +{ + int ret = 0; + const char *filename = fts->fts_path; + cgroup_dbg("seeing file %s\n", filename); + switch (ent->fts_info) { + case FTS_ERR: + errno = ent->fts_errno; + break; + case FTS_D: + case FTS_DC: + case FTS_NSOK: + case FTS_NS: + case FTS_DNR: + case FTS_DP: + ret = chown(filename, owner, group); + if (ret) + goto fail_chown; + ret = chmod(filename, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | + S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); + break; + case FTS_F: + case FTS_DEFAULT: + ret = chown(filename, owner, group); + if (ret) + goto fail_chown; + ret = chmod(filename, S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP | S_IROTH); + break; + } +fail_chown: + if (ret < 0) { + last_errno = errno; + ret = ECGOTHER; + } + return ret; +} + +/* + * TODO: Need to decide a better place to put this function. + */ +static int cg_chown_recursive(char **path, uid_t owner, gid_t group) +{ + int ret = 0; + cgroup_dbg("path is %s\n", *path); + FTS *fts = fts_open(path, FTS_PHYSICAL | FTS_NOCHDIR | + FTS_NOSTAT, NULL); + while (1) { + FTSENT *ent; + ent = fts_read(fts); + if (!ent) { + cgroup_dbg("fts_read failed\n"); + break; + } + ret = cg_chown_file(fts, ent, owner, group); + } + fts_close(fts); + return ret; +} + +static int cgroup_test_subsys_mounted(const char *name) +{ + int i; + + pthread_rwlock_rdlock(&cg_mount_table_lock); + + for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) { + if (strncmp(cg_mount_table[i].name, name, + sizeof(cg_mount_table[i].name)) == 0) { + pthread_rwlock_unlock(&cg_mount_table_lock); + return 1; + } + } + pthread_rwlock_unlock(&cg_mount_table_lock); + return 0; +} + +/** + * 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; + } + + /* 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 *rl) +{ + /* Temporary pointer */ + struct cgroup_rule *tmp = NULL; + + /* Make sure we're not freeing NULL memory! */ + if (!(rl->head)) { + cgroup_dbg("Warning: Attempted to free NULL list.\n"); + return; + } + + while (rl->head) { + tmp = rl->head; + rl->head = tmp->next; + cgroup_free_rule(tmp); + } + + /* Don't leave wild pointers around! */ + rl->head = NULL; + rl->tail = NULL; +} + +/** + * 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) +{ + /* File descriptor for the configuration file */ + FILE *fp = NULL; + + /* Buffer to store the line we're working on */ + char *buff = NULL; + + /* Iterator for the line we're working on */ + char *itr = 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 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; + + /* 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, "r"); + if (!fp) { + cgroup_dbg("Failed to open configuration file %s with" + " error: %s\n", CGRULES_CONF_FILE, + strerror(errno)); + last_errno = errno; + goto finish; + } + + buff = calloc(CGROUP_RULE_MAXLINE, sizeof(char)); + if (!buff) { + cgroup_dbg("Out of memory? Error: %s\n", strerror(errno)); + last_errno = errno; + ret = ECGOTHER; + goto close_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 ((itr = fgets(buff, CGROUP_RULE_MAXLINE, fp)) != NULL) { + linenum++; + + /* We ignore anything after a # sign as comments. */ + if ((itr = strchr(buff, '#'))) + *itr = '\0'; + + /* We also need to remove the newline character. */ + if ((itr = strchr(buff, '\n'))) + *itr = '\0'; + + /* Now, skip any leading tabs and spaces. */ + itr = buff; + while (itr && isblank(*itr)) + itr++; + + /* If there's nothing left, we can ignore this line. */ + if (!strlen(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); + memset(buff, '\0', CGROUP_RULE_MAXLINE); + continue; + } + + /* + * If there is something left, it should be a rule. Otherwise, + * there's an error in the configuration file. + */ + skipped = false; + memset(user, '\0', LOGIN_NAME_MAX); + memset(controllers, '\0', CG_CONTROLLER_MAX); + memset(destination, '\0', FILENAME_MAX); + i = sscanf(itr, "%s%s%s", user, controllers, destination); + if (i != 3) { + cgroup_dbg("Failed to parse configuration file on" + " line %d.\n", linenum); + goto parsefail; + } + + /* + * 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 cleanup; + } + if (strncmp(user, "@", 1) == 0) { + /* New GID rule. */ + itr = &(user[1]); + if ((grp = getgrnam(itr))) { + uid = CGRULE_INVALID; + gid = grp->gr_gid; + } else { + cgroup_dbg("Warning: Entry for %s not" + "found. Skipping rule on line" + " %d.\n", itr, linenum); + memset(buff, '\0', CGROUP_RULE_MAXLINE); + skipped = true; + continue; + } + } else if (strncmp(user, "*", 1) == 0) { + /* Special wildcard rule. */ + uid = CGRULE_WILD; + gid = CGRULE_WILD; + } else if (*itr != '%') { + /* New UID rule. */ + if ((pwd = getpwnam(user))) { + uid = pwd->pw_uid; + gid = CGRULE_INVALID; + } else { + cgroup_dbg("Warning: Entry for %s not" + "found. Skipping rule on line" + " %d.\n", user, linenum); + memset(buff, '\0', CGROUP_RULE_MAXLINE); + 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 && !matched) + continue; + + /* + * 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 cleanup; + } + + newrule->uid = uid; + newrule->gid = gid; + strncpy(newrule->name, user, strlen(user)); + strncpy(newrule->destination, destination, strlen(destination)); + 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->name, 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. */ + memset(buff, '\0', CGROUP_RULE_MAXLINE); + 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 cleanup; + +destroyrule: + cgroup_free_rule(newrule); + +parsefail: + ret = ECGROUPPARSEFAIL; + +cleanup: + free(buff); + +close_unlock: + fclose(fp); + pthread_rwlock_unlock(&rl_lock); +finish: + return ret; +} + +/** + * cgroup_init(), initializes the MOUNT_POINT. + * + * This code is theoretically thread safe now. Its not really tested + * so it can blow up. If does for you, please let us know with your + * test case and we can really make it thread safe. + * + */ +int cgroup_init() +{ + FILE *proc_mount = NULL; + struct mntent *ent = NULL; + struct mntent *temp_ent = NULL; + int found_mnt = 0; + int ret = 0; + static char *controllers[CG_CONTROLLER_MAX]; + FILE *proc_cgroup = NULL; + char subsys_name[FILENAME_MAX]; + int hierarchy, num_cgroups, enabled; + int i=0; + char *mntopt = NULL; + int err; + char *buf = NULL; + char mntent_buffer[4 * FILENAME_MAX]; + char *strtok_buffer = NULL; + + pthread_rwlock_wrlock(&cg_mount_table_lock); + + proc_cgroup = fopen("/proc/cgroups", "r"); + + if (!proc_cgroup) { + last_errno = errno; + ret = ECGOTHER; + goto unlock_exit; + } + + /* + * The first line of the file has stuff we are not interested in. + * So just read it and discard the information. + * + * XX: fix the size for fgets + */ + buf = malloc(FILENAME_MAX); + if (!buf) { + last_errno = errno; + ret = ECGOTHER; + goto unlock_exit; + } + buf = fgets(buf, FILENAME_MAX, proc_cgroup); + if (!buf) { + last_errno = errno; + ret = ECGOTHER; + goto unlock_exit; + } + free(buf); + + while (!feof(proc_cgroup)) { + err = fscanf(proc_cgroup, "%s %d %d %d", subsys_name, + &hierarchy, &num_cgroups, &enabled); + if (err < 0) + break; + controllers[i] = strdup(subsys_name); + i++; + } + controllers[i] = NULL; + + proc_mount = fopen("/proc/mounts", "r"); + if (proc_mount == NULL) { + ret = ECGFAIL; + goto unlock_exit; + } + + temp_ent = (struct mntent *) malloc(sizeof(struct mntent)); + + if (!temp_ent) { + last_errno = errno; + ret = ECGOTHER; + goto unlock_exit; + } + + while ((ent = getmntent_r(proc_mount, temp_ent, + mntent_buffer, + sizeof(mntent_buffer))) != NULL) { + if (!strcmp(ent->mnt_type, "cgroup")) { + for (i = 0; controllers[i] != NULL; i++) { + mntopt = hasmntopt(ent, controllers[i]); + + if (!mntopt) + continue; + + mntopt = strtok_r(mntopt, ",", &strtok_buffer); + + if (strcmp(mntopt, controllers[i]) == 0) { + cgroup_dbg("matched %s:%s\n", mntopt, + controllers[i]); + strcpy(cg_mount_table[found_mnt].name, + controllers[i]); + strcpy(cg_mount_table[found_mnt].path, + ent->mnt_dir); + cgroup_dbg("Found cgroup option %s, " + " count %d\n", + ent->mnt_opts, found_mnt); + found_mnt++; + } + } + } + } + + free(temp_ent); + + if (!found_mnt) { + cg_mount_table[0].name[0] = '\0'; + ret = ECGROUPNOTMOUNTED; + goto unlock_exit; + } + + found_mnt++; + cg_mount_table[found_mnt].name[0] = '\0'; + + cgroup_initialized = 1; + +unlock_exit: + if (proc_cgroup) + fclose(proc_cgroup); + + if (proc_mount) + fclose(proc_mount); + + for (i = 0; controllers[i]; i++) { + free(controllers[i]); + controllers[i] = NULL; + } + + pthread_rwlock_unlock(&cg_mount_table_lock); + + return ret; +} + +static int cg_test_mounted_fs() +{ + FILE *proc_mount = NULL; + struct mntent *ent = NULL; + struct mntent *temp_ent = NULL; + char mntent_buff[4 * FILENAME_MAX]; + int ret = 1; + + proc_mount = fopen("/proc/mounts", "r"); + if (proc_mount == NULL) { + return 0; + } + + temp_ent = (struct mntent *) malloc(sizeof(struct mntent)); + if (!temp_ent) { + /* We just fail at the moment. */ + return 0; + } + + ent = getmntent_r(proc_mount, temp_ent, mntent_buff, + sizeof(mntent_buff)); + + if (!ent) + return 0; + + while (strcmp(ent->mnt_type, "cgroup") !=0) { + ent = getmntent_r(proc_mount, temp_ent, mntent_buff, + sizeof(mntent_buff)); + if (ent == NULL) { + ret = 0; + goto done; + } + } +done: + fclose(proc_mount); + free(temp_ent); + return ret; +} + +static inline pid_t cg_gettid() +{ + return syscall(__NR_gettid); +} + + +/* Call with cg_mount_table_lock taken */ +static char *cg_build_path_locked(char *name, char *path, char *type) +{ + int i; + for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) { + /* + * XX: Change to snprintf once you figure what n should be + */ + if (strcmp(cg_mount_table[i].name, type) == 0) { + sprintf(path, "%s/", cg_mount_table[i].path); + if (name) { + char *tmp; + tmp = strdup(path); + sprintf(path, "%s%s/", tmp, name); + free(tmp); + } + return path; + } + } + return NULL; +} + +char *cg_build_path(char *name, char *path, char *type) +{ + pthread_rwlock_rdlock(&cg_mount_table_lock); + path = cg_build_path_locked(name, path, type); + pthread_rwlock_unlock(&cg_mount_table_lock); + + return path; +} + +/** cgroup_attach_task_pid is used to assign tasks to a cgroup. + * struct cgroup *cgroup: The cgroup to assign the thread to. + * pid_t tid: The thread to be assigned to the cgroup. + * + * returns 0 on success. + * returns ECGROUPNOTOWNER if the caller does not have access to the cgroup. + * returns ECGROUPNOTALLOWED for other causes of failure. + */ +int cgroup_attach_task_pid(struct cgroup *cgroup, pid_t tid) +{ + char path[FILENAME_MAX]; + FILE *tasks = NULL; + int i, ret = 0; + + if (!cgroup_initialized) { + cgroup_dbg("libcgroup is not initialized\n"); + return ECGROUPNOTINITIALIZED; + } + if(!cgroup) + { + pthread_rwlock_rdlock(&cg_mount_table_lock); + for(i = 0; i < CG_CONTROLLER_MAX && + cg_mount_table[i].name[0]!='\0'; i++) { + if (!cg_build_path_locked(NULL, path, + cg_mount_table[i].name)) + continue; + strncat(path, "/tasks", sizeof(path) - strlen(path)); + + tasks = fopen(path, "w"); + if (!tasks) { + pthread_rwlock_unlock(&cg_mount_table_lock); + switch (errno) { + case EPERM: + return ECGROUPNOTOWNER; + case ENOENT: + return ECGROUPNOTEXIST; + default: + return ECGROUPNOTALLOWED; + } + } + ret = fprintf(tasks, "%d", tid); + if (ret < 0) { + cgroup_dbg("Error writing tid %d to %s:%s\n", + tid, path, strerror(errno)); + fclose(tasks); + last_errno = errno; + return ECGOTHER; + } + + ret = fflush(tasks); + if (ret) { + last_errno = errno; + cgroup_dbg("Error writing tid %d to %s:%s\n", + tid, path, strerror(errno)); + fclose(tasks); + return ECGOTHER; + } + fclose(tasks); + } + pthread_rwlock_unlock(&cg_mount_table_lock); + } else { + for (i = 0; i < cgroup->index; i++) { + if (!cgroup_test_subsys_mounted(cgroup->controller[i]->name)) { + cgroup_dbg("subsystem %s is not mounted\n", + cgroup->controller[i]->name); + return ECGROUPSUBSYSNOTMOUNTED; + } + } + + for (i = 0; i < cgroup->index; i++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + + strncat(path, "/tasks", sizeof(path) - strlen(path)); + + tasks = fopen(path, "w"); + if (!tasks) { + cgroup_dbg("fopen failed for %s:%s", path, + strerror(errno)); + + switch (errno) { + case EPERM: + return ECGROUPNOTOWNER; + case ENOENT: + return ECGROUPNOTEXIST; + default: + return ECGROUPNOTALLOWED; + } + } + ret = fprintf(tasks, "%d", tid); + if (ret < 0) { + last_errno = errno; + cgroup_dbg("Error writing tid %d to %s:%s\n", + tid, path, strerror(errno)); + fclose(tasks); + return ECGOTHER; + } + ret = fflush(tasks); + if (ret) { + last_errno = errno; + cgroup_dbg("Error writing tid %d to %s:%s\n", + tid, path, strerror(errno)); + fclose(tasks); + return ECGOTHER; + } + fclose(tasks); + } + } + return 0; +} + +/** cgroup_attach_task is used to attach the current thread to a cgroup. + * struct cgroup *cgroup: The cgroup to assign the current thread to. + * + * See cg_attach_task_pid for return values. + */ +int cgroup_attach_task(struct cgroup *cgroup) +{ + pid_t tid = cg_gettid(); + int error; + + error = cgroup_attach_task_pid(cgroup, tid); + + return error; +} + +/** + * cg_mkdir_p, emulate the mkdir -p command (recursively creating paths) + * @path: path to create + */ +static int cg_mkdir_p(const char *path) +{ + char *real_path = NULL; + char *wd = NULL; + int i = 0, j = 0; + char pos; + char *str = NULL; + int ret = 0; + char cwd[FILENAME_MAX]; + char *buf = NULL; + + buf = getcwd(cwd, FILENAME_MAX); + + if (!buf) { + last_errno = errno; + return ECGOTHER; + } + + real_path = strdup(path); + if (!real_path) { + last_errno = errno; + return ECGOTHER; + } + + do { + while (real_path[j] != '\0' && real_path[j] != '/') + j++; + while (real_path[j] != '\0' && real_path[j] == '/') + j++; + if (i == j) + continue; + pos = real_path[j]; + real_path[j] = '\0'; /* Temporarily overwrite "/" */ + str = &real_path[i]; + ret = mkdir(str, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + wd = strdup(str); + if (!wd) { + last_errno = errno; + ret = ECGOTHER; + break; + } + real_path[j] = pos; + if (ret) { + switch (errno) { + case EEXIST: + ret = 0; /* Not fatal really */ + break; + case EPERM: + ret = ECGROUPNOTOWNER; + free(wd); + goto done; + default: + ret = ECGROUPNOTALLOWED; + free(wd); + goto done; + } + } + i = j; + ret = chdir(wd); + if (ret) { + cgroup_dbg("could not chdir to child directory (%s)\n", + wd); + break; + } + free(wd); + } while (real_path[i]); + + ret = chdir(buf); + if (ret) { + last_errno = errno; + ret = ECGOTHER; + cgroup_dbg("could not go back to old directory (%s)\n", cwd); + } + +done: + free(real_path); + return ret; +} + +/* + * create_control_group() + * This is the basic function used to create the control group. This function + * just makes the group. It does not set any permissions, or any control values. + * The argument path is the fully qualified path name to make it generic. + */ +static int cg_create_control_group(char *path) +{ + int error; + if (!cg_test_mounted_fs()) + return ECGROUPNOTMOUNTED; + error = cg_mkdir_p(path); + return error; +} + +/* + * set_control_value() + * This is the low level function for putting in a value in a control file. + * This function takes in the complete path and sets the value in val in that + * file. + */ +static int cg_set_control_value(char *path, char *val) +{ + FILE *control_file = NULL; + if (!cg_test_mounted_fs()) + return ECGROUPNOTMOUNTED; + + control_file = fopen(path, "r+"); + + if (!control_file) { + if (errno == EPERM) { + /* + * We need to set the correct error value, does the + * group exist but we don't have the subsystem + * mounted at that point, or is it that the group + * does not exist. So we check if the tasks file + * exist. Before that, we need to extract the path. + */ + int len = strlen(path); + + while (*(path+len) != '/') + len--; + *(path+len+1) = '\0'; + strncat(path, "tasks", sizeof(path) - strlen(path)); + control_file = fopen(path, "r"); + if (!control_file) { + if (errno == ENOENT) + return ECGROUPSUBSYSNOTMOUNTED; + } + fclose(control_file); + return ECGROUPNOTALLOWED; + } + return ECGROUPVALUENOTEXIST; + } + + fprintf(control_file, "%s", val); + fclose(control_file); + return 0; +} + +/** cgroup_modify_cgroup modifies the cgroup control files. + * struct cgroup *cgroup: The name will be the cgroup to be modified. + * The values will be the values to be modified, those not mentioned + * in the structure will not be modified. + * + * The uids cannot be modified yet. + * + * returns 0 on success. + * + */ + +int cgroup_modify_cgroup(struct cgroup *cgroup) +{ + char *path, base[FILENAME_MAX]; + int i; + int error; + int ret; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + if (!cgroup) + return ECGROUPNOTALLOWED; + + for (i = 0; i < cgroup->index; i++) { + if (!cgroup_test_subsys_mounted(cgroup->controller[i]->name)) { + cgroup_dbg("subsystem %s is not mounted\n", + cgroup->controller[i]->name); + return ECGROUPSUBSYSNOTMOUNTED; + } + } + + for (i = 0; i < cgroup->index; i++) { + int j; + if (!cg_build_path(cgroup->name, base, + cgroup->controller[i]->name)) + continue; + for (j = 0; j < cgroup->controller[i]->index; j++) { + ret = asprintf(&path, "%s%s", base, + cgroup->controller[i]->values[j]->name); + if (ret < 0) { + last_errno = errno; + error = ECGOTHER; + goto err; + } + error = cg_set_control_value(path, + cgroup->controller[i]->values[j]->value); + free(path); + path = NULL; + if (error) + goto err; + } + } + if (path) + free(path); + return 0; +err: + if (path) + free(path); + return error; + +} + +/** + * @dst: Destination controller + * @src: Source controller from which values will be copied to dst + * + * Create a duplicate copy of values under the specified controller + */ +int cgroup_copy_controller_values(struct cgroup_controller *dst, + struct cgroup_controller *src) +{ + int i, ret = 0; + + if (!dst || !src) + return ECGFAIL; + + strncpy(dst->name, src->name, FILENAME_MAX); + for (i = 0; i < src->index; i++, dst->index++) { + struct control_value *src_val = src->values[i]; + struct control_value *dst_val; + + dst->values[i] = calloc(1, sizeof(struct control_value)); + if (!dst->values[i]) { + ret = ECGFAIL; + goto err; + } + + dst_val = dst->values[i]; + strncpy(dst_val->value, src_val->value, CG_VALUE_MAX); + strncpy(dst_val->name, src_val->name, FILENAME_MAX); + } +err: + return ret; +} + +/** + * @dst: Destination control group + * @src: Source from which values will be copied to dst + * + * Create a duplicate copy of src in dst. This will be useful for those who + * that intend to create new instances based on an existing control group + */ +int cgroup_copy_cgroup(struct cgroup *dst, struct cgroup *src) +{ + int ret = 0, i; + + if (!dst || !src) + return ECGROUPNOTEXIST; + + /* + * Should we just use the restrict keyword instead? + */ + if (dst == src) + return ECGFAIL; + + cgroup_free_controllers(dst); + + for (i = 0; i < src->index; i++, dst->index++) { + struct cgroup_controller *src_ctlr = src->controller[i]; + struct cgroup_controller *dst_ctlr; + + dst->controller[i] = calloc(1, sizeof(struct cgroup_controller)); + if (!dst->controller[i]) { + ret = ECGFAIL; + goto err; + } + + dst_ctlr = dst->controller[i]; + ret = cgroup_copy_controller_values(dst_ctlr, src_ctlr); + if (ret) + goto err; + } +err: + return ret; +} + +/** cgroup_create_cgroup creates a new control group. + * struct cgroup *cgroup: The control group to be created + * + * returns 0 on success. We recommend calling cg_delete_cgroup + * if this routine fails. That should do the cleanup operation. + */ +int cgroup_create_cgroup(struct cgroup *cgroup, int ignore_ownership) +{ + char *fts_path[2]; + char *base = NULL; + char *path = NULL; + int i, j, k; + int error = 0; + int retval = 0; + int ret; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + if (!cgroup) + return ECGROUPNOTALLOWED; + + for (i = 0; i < cgroup->index; i++) { + if (!cgroup_test_subsys_mounted(cgroup->controller[i]->name)) + return ECGROUPSUBSYSNOTMOUNTED; + } + + fts_path[0] = (char *)malloc(FILENAME_MAX); + if (!fts_path[0]) { + last_errno = errno; + return ECGOTHER; + } + fts_path[1] = NULL; + path = fts_path[0]; + + /* + * XX: One important test to be done is to check, if you have multiple + * subsystems mounted at one point, all of them *have* be on the cgroup + * data structure. If not, we fail. + */ + for (k = 0; k < cgroup->index; k++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[k]->name)) + continue; + + error = cg_create_control_group(path); + if (error) + goto err; + + base = strdup(path); + + if (!base) { + last_errno = errno; + error = ECGOTHER; + goto err; + } + + if (!ignore_ownership) { + cgroup_dbg("Changing ownership of %s\n", fts_path[0]); + error = cg_chown_recursive(fts_path, + cgroup->control_uid, cgroup->control_gid); + } + + if (error) + goto err; + + for (j = 0; j < cgroup->controller[k]->index; j++) { + ret = snprintf(path, FILENAME_MAX, "%s%s", base, + cgroup->controller[k]->values[j]->name); + cgroup_dbg("setting %s to %s, error %d\n", path, + cgroup->controller[k]->values[j]->name, ret); + if (ret < 0 || ret >= FILENAME_MAX) { + last_errno = errno; + error = ECGOTHER; + goto err; + } + error = cg_set_control_value(path, + cgroup->controller[k]->values[j]->value); + /* + * Should we undo, what we've done in the loops above? + * An error should not be treated as fatal, since we + * have several read-only files and several files that + * are only conditionally created in the child. + * + * A middle ground would be to track that there + * was an error and return that value. + */ + if (error) { + retval = error; + continue; + } + } + + if (!ignore_ownership) { + ret = snprintf(path, FILENAME_MAX, "%s/tasks", base); + if (ret < 0 || ret >= FILENAME_MAX) { + last_errno = errno; + error = ECGOTHER; + goto err; + } + error = chown(path, cgroup->tasks_uid, + cgroup->tasks_gid); + if (error) { + last_errno = errno; + error = ECGOTHER; + goto err; + } + } + free(base); + base = NULL; + } + +err: + if (path) + free(path); + if (base) + free(base); + if (retval && !error) + error = retval; + return error; +} + +/** + * Find the parent of the specified directory. It returns the parent (the + * parent is usually name/.. unless name is a mount point. + */ +char *cgroup_find_parent(char *name) +{ + char child[FILENAME_MAX]; + char *parent = NULL; + struct stat stat_child, stat_parent; + char *type = NULL; + char *dir = NULL; + + pthread_rwlock_rdlock(&cg_mount_table_lock); + type = cg_mount_table[0].name; + if (!cg_build_path_locked(name, child, type)) { + pthread_rwlock_unlock(&cg_mount_table_lock); + return NULL; + } + pthread_rwlock_unlock(&cg_mount_table_lock); + + cgroup_dbg("path is %s\n", child); + dir = dirname(child); + cgroup_dbg("directory name is %s\n", dir); + + if (asprintf(&parent, "%s/..", dir) < 0) + return NULL; + + cgroup_dbg("parent's name is %s\n", parent); + + if (stat(dir, &stat_child) < 0) + goto free_parent; + + if (stat(parent, &stat_parent) < 0) + goto free_parent; + + /* + * Is the specified "name" a mount point? + */ + if (stat_parent.st_dev != stat_child.st_dev) { + cgroup_dbg("parent is a mount point\n"); + strcpy(parent, "."); + } else { + dir = strdup(name); + if (!dir) + goto free_parent; + dir = dirname(dir); + if (strcmp(dir, ".") == 0) + strcpy(parent, ".."); + else + strcpy(parent, dir); + } + + return parent; + +free_parent: + free(parent); + return NULL; +} + +/** + * @cgroup: cgroup data structure to be filled with parent values and then + * passed down for creation + * @ignore_ownership: Ignore doing a chown on the newly created cgroup + */ +int cgroup_create_cgroup_from_parent(struct cgroup *cgroup, + int ignore_ownership) +{ + char *parent = NULL; + struct cgroup *parent_cgroup = NULL; + int ret = ECGFAIL; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + parent = cgroup_find_parent(cgroup->name); + if (!parent) + return ret; + + cgroup_dbg("parent is %s\n", parent); + parent_cgroup = cgroup_new_cgroup(parent); + if (!parent_cgroup) + goto err_nomem; + + if (cgroup_get_cgroup(parent_cgroup)) + goto err_parent; + + cgroup_dbg("got parent group for %s\n", parent_cgroup->name); + ret = cgroup_copy_cgroup(cgroup, parent_cgroup); + if (ret) + goto err_parent; + + cgroup_dbg("copied parent group %s to %s\n", parent_cgroup->name, + cgroup->name); + ret = cgroup_create_cgroup(cgroup, ignore_ownership); + +err_parent: + cgroup_free(&parent_cgroup); +err_nomem: + free(parent); + return ret; +} + +/** cgroup_delete cgroup deletes a control group. + * struct cgroup *cgroup takes the group which is to be deleted. + * + * returns 0 on success. + */ +int cgroup_delete_cgroup(struct cgroup *cgroup, int ignore_migration) +{ + FILE *delete_tasks = NULL, *base_tasks = NULL; + int tids; + char path[FILENAME_MAX]; + int error = ECGROUPNOTALLOWED; + int i, ret; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + if (!cgroup) + return ECGROUPNOTALLOWED; + + for (i = 0; i < cgroup->index; i++) { + if (!cgroup_test_subsys_mounted(cgroup->controller[i]->name)) + return ECGROUPSUBSYSNOTMOUNTED; + } + + for (i = 0; i < cgroup->index; i++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + strncat(path, "../tasks", sizeof(path) - strlen(path)); + + base_tasks = fopen(path, "w"); + if (!base_tasks) + goto open_err; + + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) { + fclose(base_tasks); + continue; + } + + strncat(path, "tasks", sizeof(path) - strlen(path)); + + delete_tasks = fopen(path, "r"); + if (!delete_tasks) { + fclose(base_tasks); + goto open_err; + } + + while (!feof(delete_tasks)) { + ret = fscanf(delete_tasks, "%d", &tids); + if (ret == EOF || ret < 1) + break; + fprintf(base_tasks, "%d", tids); + } + + fclose(delete_tasks); + fclose(base_tasks); + + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + error = rmdir(path); + last_errno = errno; + } +open_err: + if (ignore_migration) { + for (i = 0; i < cgroup->index; i++) { + if (!cg_build_path(cgroup->name, path, + cgroup->controller[i]->name)) + continue; + error = rmdir(path); + if (error < 0 && errno == ENOENT) { + last_errno = errno; + error = 0; + } + } + } + if (error) + return ECGOTHER; + + return error; +} + +/* + * This function should really have more checks, but this version + * will assume that the callers have taken care of everything. + * Including the locking. + */ +static int cg_rd_ctrl_file(char *subsys, char *cgroup, char *file, char **value) +{ + char path[FILENAME_MAX]; + FILE *ctrl_file = NULL; + int ret; + + if (!cg_build_path_locked(cgroup, path, subsys)) + return ECGFAIL; + + strncat(path, file, sizeof(path) - strlen(path)); + ctrl_file = fopen(path, "r"); + if (!ctrl_file) + return ECGROUPVALUENOTEXIST; + + *value = malloc(CG_VALUE_MAX); + if (!*value) { + last_errno = errno; + return ECGOTHER; + } + + /* + * using %as crashes when we try to read from files like + * memory.stat + */ + ret = fscanf(ctrl_file, "%s", *value); + if (ret == 0 || ret == EOF) { + free(*value); + *value = NULL; + } + + fclose(ctrl_file); + + return 0; +} + +/* + * Call this function with required locks taken. + */ +static int cgroup_fill_cgc(struct dirent *ctrl_dir, struct cgroup *cgroup, + struct cgroup_controller *cgc, int index) +{ + char *ctrl_name = NULL; + char *ctrl_file = NULL; + char *ctrl_value = NULL; + char *d_name = NULL; + char path[FILENAME_MAX+1]; + char *buffer = NULL; + int error = 0; + struct stat stat_buffer; + + d_name = strdup(ctrl_dir->d_name); + + if (!strcmp(d_name, ".") || !strcmp(d_name, "..")) { + error = ECGINVAL; + goto fill_error; + } + + + /* + * This part really needs to be optimized out. Probably use + * some sort of a flag, but this is fine for now. + */ + + cg_build_path_locked(cgroup->name, path, cg_mount_table[index].name); + strncat(path, d_name, sizeof(path) - strlen(path)); + + error = stat(path, &stat_buffer); + + if (error) { + error = ECGFAIL; + goto fill_error; + } + + cgroup->control_uid = stat_buffer.st_uid; + cgroup->control_gid = stat_buffer.st_gid; + + ctrl_name = strtok_r(d_name, ".", &buffer); + + if (!ctrl_name) { + error = ECGFAIL; + goto fill_error; + } + + ctrl_file = strtok_r(NULL, ".", &buffer); + + if (!ctrl_file) { + error = ECGINVAL; + goto fill_error; + } + + if (strcmp(ctrl_name, cg_mount_table[index].name) == 0) { + error = cg_rd_ctrl_file(cg_mount_table[index].name, + cgroup->name, ctrl_dir->d_name, &ctrl_value); + if (error || !ctrl_value) + goto fill_error; + + if (cgroup_add_value_string(cgc, ctrl_dir->d_name, + ctrl_value)) { + error = ECGFAIL; + goto fill_error; + } + } +fill_error: + if (ctrl_value) + free(ctrl_value); + free(d_name); + return error; +} + +/* + * cgroup_get_cgroup reads the cgroup data from the filesystem. + * struct cgroup has the name of the group to be populated + * + * return 0 on success. + */ +int cgroup_get_cgroup(struct cgroup *cgroup) +{ + int i; + char path[FILENAME_MAX]; + DIR *dir = NULL; + struct dirent *ctrl_dir = NULL; + char *control_path = NULL; + int error; + int ret; + + if (!cgroup_initialized) { + /* ECGROUPNOTINITIALIZED */ + return ECGROUPNOTINITIALIZED; + } + + if (!cgroup) { + /* ECGROUPNOTALLOWED */ + return ECGROUPNOTALLOWED; + } + + pthread_rwlock_rdlock(&cg_mount_table_lock); + for (i = 0; i < CG_CONTROLLER_MAX && + cg_mount_table[i].name[0] != '\0'; i++) { + /* + * cgc will not leak, since it has to be freed using + * cgroup_free_cgroup + */ + struct cgroup_controller *cgc; + struct stat stat_buffer; + int path_len; + + if (!cg_build_path_locked(NULL, path, + cg_mount_table[i].name)) + continue; + + path_len = strlen(path); + strncat(path, cgroup->name, FILENAME_MAX - path_len - 1); + + if (access(path, F_OK)) + continue; + + if (!cg_build_path_locked(cgroup->name, path, + cg_mount_table[i].name)) { + /* + * This fails when the cgroup does not exist + * for that controller. + */ + continue; + } + + /* + * Get the uid and gid information + */ + + ret = asprintf(&control_path, "%s/tasks", path); + + if (ret < 0) { + last_errno = errno; + error = ECGOTHER; + goto unlock_error; + } + + if (stat(control_path, &stat_buffer)) { + last_errno = errno; + free(control_path); + error = ECGOTHER; + goto unlock_error; + } + + cgroup->tasks_uid = stat_buffer.st_uid; + cgroup->tasks_gid = stat_buffer.st_gid; + + free(control_path); + + cgc = cgroup_add_controller(cgroup, + cg_mount_table[i].name); + if (!cgc) { + error = ECGINVAL; + goto unlock_error; + } + + dir = opendir(path); + if (!dir) { + last_errno = errno; + error = ECGOTHER; + goto unlock_error; + } + + while ((ctrl_dir = readdir(dir)) != NULL) { + /* + * Skip over non regular files + */ + if (ctrl_dir->d_type != DT_REG) + continue; + + error = cgroup_fill_cgc(ctrl_dir, cgroup, cgc, i); + if (error == ECGFAIL) { + closedir(dir); + goto unlock_error; + } + } + closedir(dir); + } + + /* Check if the group really exists or not */ + if (!cgroup->index) { + error = ECGROUPNOTEXIST; + goto unlock_error; + } + + pthread_rwlock_unlock(&cg_mount_table_lock); + return 0; + +unlock_error: + pthread_rwlock_unlock(&cg_mount_table_lock); + /* + * XX: Need to figure out how to cleanup? Cleanup just the stuff + * we added, or the whole structure. + */ + cgroup = NULL; + 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, + char *controllers[]) +{ + int ret = 0, i; + 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++) { + 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 (i = 0; i < CG_CONTROLLER_MAX && + cg_mount_table[i].name[0] != '\0'; i++) { + cgroup_dbg("Adding controller %s\n", + cg_mount_table[i].name); + cptr = cgroup_add_controller(cgroup, + cg_mount_table[i].name); + if (!cptr) { + cgroup_dbg("Adding controller '%s'" + " failed\n", + cg_mount_table[i].name); + pthread_rwlock_unlock(&cg_mount_table_lock); + 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); + return ECGROUPNOTALLOWED; + } + } + + return ret; +} + +/** + * Finds the first rule in the cached list that matches the given UID or GID, + * and returns a pointer to that rule. This function uses rl_lock. + * + * This function may NOT be thread safe. + * @param uid The UID to match + * @param gid The GID to match + * @return Pointer to the first matching rule, or NULL if no match + * TODO: Determine thread-safeness and fix if not safe. + */ +static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(const uid_t uid, + const gid_t gid) +{ + /* Return value */ + struct cgroup_rule *ret = rl.head; + + /* 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; + + pthread_rwlock_wrlock(&rl_lock); + while (ret) { + /* The wildcard rule always matches. */ + if ((ret->uid == CGRULE_WILD) && (ret->gid == CGRULE_WILD)) { + goto finished; + } + + /* This is the simple case of the UID matching. */ + if (ret->uid == uid) { + goto finished; + } + + /* This is the simple case of the GID matching. */ + if (ret->gid == gid) { + goto finished; + } + + /* If this is a group rule, the UID might be a member. */ + if (ret->name[0] == '@') { + /* Get the group data. */ + sp = &(ret->name[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]))) + goto finished; + } + } + + /* If we haven't matched, try the next rule. */ + ret = ret->next; + } + + /* If we get here, no rules matched. */ + ret = NULL; + +finished: + pthread_rwlock_unlock(&rl_lock); + return ret; +} + +/** + * Changes the cgroup of a program based on the rules in the config file. If a + * rule exists for the given UID or GID, then the given PID is placed into the + * correct group. By default, this function parses the configuration file each + * time it is called. + * + * The flags can alter the behavior of this function: + * CGFLAG_USECACHE: Use cached rules instead of parsing the config file + * + * This function may NOT be thread safe. + * @param uid The UID to match + * @param gid The GID to match + * @param pid The PID of the process to move + * @param flags Bit flags to change the behavior, as defined above + * @return 0 on success, > 0 on error + * TODO: Determine thread-safeness and fix of not safe. + */ +int cgroup_change_cgroup_uid_gid_flags(const uid_t uid, const gid_t gid, + const pid_t pid, const int flags) +{ + /* Temporary pointer to a rule */ + struct cgroup_rule *tmp = NULL; + + /* 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); + + /* 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(uid, gid); + 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->name, 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->name, pid); + ret = cgroup_change_cgroup_path(tmp->destination, + pid, 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->name[0] == '%')); + +finished: + return ret; +} + +/** + * 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(char *dest, pid_t pid, char *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); + return ret; + } + return 0; +} + +/** + * 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\n", itr->name); + + 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() +{ + /* Return codes */ + int ret = 0; + + cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE); + if ((ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID))) { + cgroup_dbg("Error parsing configuration file \"%s\": %d.\n", + CGRULES_CONF_FILE, ret); + ret = ECGROUPPARSEFAIL; + 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() +{ + /* Return codes */ + int ret = 0; + + /* Attempt to read the configuration file and cache the rules. */ + ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID); + 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 + * @pid: pid of the current process for which the path is to be determined + * @controller: name of the controller for which to determine current path + * @current_path: a pointer that is filled with the value of the current + * path as seen in /proc//cgroup + */ +int cgroup_get_current_controller_path(pid_t pid, const char *controller, + char **current_path) +{ + char *path = NULL; + int ret; + FILE *pid_cgroup_fd = NULL; + + if (!controller) + return ECGOTHER; + + if (!cgroup_initialized) { + cgroup_dbg("libcgroup is not initialized\n"); + return ECGROUPNOTINITIALIZED; + } + + ret = asprintf(&path, "/proc/%d/cgroup", pid); + if (ret <= 0) { + cgroup_dbg("cannot allocate memory (/proc/pid/cgroup) ret %d\n", + ret); + return ret; + } + + ret = ECGROUPNOTEXIST; + pid_cgroup_fd = fopen(path, "r"); + if (!pid_cgroup_fd) + goto cleanup_path; + + /* + * Why do we grab the cg_mount_table_lock?, the reason is that + * the cgroup of a pid can change via the cgroup_attach_task_pid() + * call. To make sure, we return consitent and safe results, + * we acquire the lock upfront. We can optimize by acquiring + * and releasing the lock in the while loop, but that + * will be more expensive. + */ + pthread_rwlock_rdlock(&cg_mount_table_lock); + while (!feof(pid_cgroup_fd)) { + char controllers[FILENAME_MAX]; + char cgroup_path[FILENAME_MAX]; + int num; + char *savedptr; + char *token; + + ret = fscanf(pid_cgroup_fd, "%d:%[^:]:%s\n", &num, controllers, + cgroup_path); + /* + * Magic numbers like "3" seem to be integrating into + * my daily life, I need some magic to help make them + * disappear :) + */ + if (ret != 3 || ret == EOF) { + cgroup_dbg("read failed for pid_cgroup_fd ret %d\n", + ret); + last_errno = errno; + ret = ECGOTHER; + goto done; + } + + token = strtok_r(controllers, ",", &savedptr); + do { + if (strncmp(controller, token, strlen(controller) + 1) + == 0) { + *current_path = strdup(cgroup_path); + if (!*current_path) { + last_errno = errno; + ret = ECGOTHER; + goto done; + } + ret = 0; + goto done; + } + token = strtok_r(NULL, ",", &savedptr); + } while (token); + } + +done: + pthread_rwlock_unlock(&cg_mount_table_lock); + fclose(pid_cgroup_fd); +cleanup_path: + free(path); + return ret; +} + +char *cgroup_strerror(int code) +{ + assert((code >= ECGROUPNOTCOMPILED) && (code < ECGSENTINEL)); + if (code == ECGOTHER) { + snprintf(errtext, MAXLEN, "%s: error message: %s", + cgroup_strerror_codes[code % ECGROUPNOTCOMPILED], + strerror(cgroup_get_last_errno())); + return errtext; + } + return cgroup_strerror_codes[code % ECGROUPNOTCOMPILED]; +} + +/** + * Return last errno, which caused ECGOTHER error. + */ +int cgroup_get_last_errno() +{ + return last_errno; +} + + +static int cg_walk_node(FTS *fts, FTSENT *ent, const int depth, + struct cgroup_file_info *info) +{ + int ret = 0; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + cgroup_dbg("seeing file %s\n", ent->fts_path); + + info->path = ent->fts_name; + info->parent = ent->fts_parent->fts_name; + info->full_path = ent->fts_path; + info->depth = ent->fts_level; + info->type = CGROUP_FILE_TYPE_OTHER; + + if (depth && (info->depth > depth)) + return 0; + + switch (ent->fts_info) { + case FTS_DNR: + case FTS_ERR: + errno = ent->fts_errno; + break; + case FTS_D: + info->type = CGROUP_FILE_TYPE_DIR; + break; + case FTS_DC: + case FTS_NSOK: + case FTS_NS: + case FTS_DP: + break; + case FTS_F: + info->type = CGROUP_FILE_TYPE_FILE; + break; + case FTS_DEFAULT: + break; + } + return ret; +} + +int cgroup_walk_tree_next(const int depth, void **handle, + struct cgroup_file_info *info, int base_level) +{ + int ret = 0; + FTS *fts = *(FTS **)handle; + FTSENT *ent; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + if (!handle) + return ECGINVAL; + ent = fts_read(fts); + if (!ent) + return ECGEOF; + if (!base_level && depth) + base_level = ent->fts_level + depth; + ret = cg_walk_node(fts, ent, base_level, info); + *handle = fts; + return ret; +} + +int cgroup_walk_tree_end(void **handle) +{ + FTS *fts = *(FTS **)handle; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + if (!handle) + return ECGINVAL; + fts_close(fts); + return 0; +} + +/* + * TODO: Need to decide a better place to put this function. + */ +int cgroup_walk_tree_begin(char *controller, char *base_path, const int depth, + void **handle, struct cgroup_file_info *info, + int *base_level) +{ + int ret = 0; + cgroup_dbg("path is %s\n", base_path); + char *cg_path[2]; + char full_path[FILENAME_MAX]; + FTSENT *ent; + FTS *fts; + + if (!cgroup_initialized) + return ECGROUPNOTINITIALIZED; + + if (!cg_build_path(base_path, full_path, controller)) + return ECGOTHER; + + *base_level = 0; + cg_path[0] = full_path; + cg_path[1] = NULL; + + fts = fts_open(cg_path, FTS_LOGICAL | FTS_NOCHDIR | + FTS_NOSTAT, NULL); + ent = fts_read(fts); + if (!ent) { + cgroup_dbg("fts_read failed\n"); + return ECGINVAL; + } + if (!*base_level && depth) + *base_level = ent->fts_level + depth; + ret = cg_walk_node(fts, ent, *base_level, info); + *handle = fts; + return ret; +} diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..b188985 --- /dev/null +++ b/src/config.c @@ -0,0 +1,496 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Authors: Balbir Singh + * Dhaval Giani + * + * 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. + * + * TODOs: + * 1. Implement our own hashing scheme + * 2. Add namespace support + * 3. Add support for parsing cgroup filesystem and creating a + * config out of it. + * + * Code initiated and designed by Balbir Singh. All faults are most likely + * his mistake. + * + * Cleanup and changes to use the "official" structures and functions made + * by Dhaval Giani. All faults will still be Balbir's mistake :) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX_CGROUPS 1024 + +extern FILE *yyin; +extern int yyparse(void); + +/* + * The basic global data structures. + * + * config_mount_table -> Where what controller is mounted + * table_index -> Where in the table are we. + * config_table_lock -> Serializing access to config_mount_table. + * cgroup_table -> Which cgroups have to be created. + * cgroup_table_index -> Where in the cgroup_table we are. + */ +static struct cg_mount_table_s config_mount_table[CG_CONTROLLER_MAX]; +static int config_table_index; +static pthread_rwlock_t config_table_lock = PTHREAD_RWLOCK_INITIALIZER; +static struct cgroup config_cgroup_table[MAX_CGROUPS]; +int cgroup_table_index; + +/* + * Needed for the type while mounting cgroupfs. + */ +static const char cgroup_filesystem[] = "cgroup"; + +/* + * NOTE: All these functions return 1 on success + * and not 0 as is the library convention + */ + +/* + * This call just sets the name of the cgroup. It will + * always be called in the end, because the parser will + * work bottom up. + */ +int cgroup_config_insert_cgroup(char *cg_name) +{ + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + + strncpy(config_cgroup->name, cg_name, FILENAME_MAX); + /* + * Since this will be the last part to be parsed. + */ + cgroup_table_index++; + free(cg_name); + return 1; +} + +/* + * This function sets the various controller's control + * files. It will always append values for cgroup_table_index + * entry in the cgroup_table. The index is incremented in + * cgroup_config_insert_cgroup + */ +int cgroup_config_parse_controller_options(char *controller, char *name_value) +{ + char *buffer = NULL; + char *name, *value; + struct cgroup_controller *cgc; + int error; + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + char *nm_pairs, *nv_buf; + + cgroup_dbg("Adding controller %s, value %s\n", controller, name_value); + cgc = cgroup_add_controller(config_cgroup, controller); + + if (!cgc) + goto parse_error; + + /* + * Did we just specify the controller to create the correct + * set of directories, without setting any values? + */ + if (!name_value) + goto done; + + nm_pairs = strtok_r(name_value, ":", &nv_buf); + cgroup_dbg("[1] name value pair being processed is %s\n", nm_pairs); + + name = strtok_r(nm_pairs, " ", &buffer); + + if (!name) + goto parse_error; + + value = strtok_r(NULL, " ", &buffer); + + if (!value) + goto parse_error; + + cgroup_dbg("name is %s, value is %s\n", name, value); + error = cgroup_add_value_string(cgc, name, value); + + if (error) + goto parse_error; + + while ((nm_pairs = strtok_r(NULL, ":", &nv_buf))) { + cgroup_dbg("[2] name value pair being processed is %s\n", + nm_pairs); + name = strtok_r(nm_pairs, " ", &buffer); + + if (!name) + goto parse_error; + + value = strtok_r(NULL, " ", &buffer); + + if (!value) + goto parse_error; + + cgroup_dbg("name is %s, value is %s\n", name, value); + error = cgroup_add_value_string(cgc, name, value); + + if (error) + goto parse_error; + } + +done: + free(controller); + free(name_value); + return 1; + +parse_error: + free(controller); + free(name_value); + cgroup_delete_cgroup(config_cgroup, 1); + cgroup_table_index--; + return 0; +} + +/* + * Sets the tasks file's uid and gid + */ +int cgroup_config_group_task_perm(char *perm_type, char *value) +{ + struct passwd *pw, *pw_buffer; + struct group *group, *group_buffer; + int error; + long val = atoi(value); + char buffer[CGROUP_BUFFER_LEN]; + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + + if (!strcmp(perm_type, "uid")) { + if (!val) { + pw = (struct passwd *) malloc(sizeof(struct passwd)); + + if (!pw) + goto group_task_error; + + error = getpwnam_r(value, pw, buffer, CGROUP_BUFFER_LEN, + &pw_buffer); + if (pw_buffer == NULL) { + free(pw); + goto group_task_error; + } + + val = pw->pw_uid; + free(pw); + } + config_cgroup->tasks_uid = val; + } + + if (!strcmp(perm_type, "gid")) { + if (!val) { + group = (struct group *) malloc(sizeof(struct group)); + + if (!group) + goto group_task_error; + + error = getgrnam_r(value, group, buffer, + CGROUP_BUFFER_LEN, &group_buffer); + + if (group_buffer == NULL) { + free(group); + goto group_task_error; + } + + val = group->gr_gid; + free(group); + } + config_cgroup->tasks_gid = val; + } + + free(perm_type); + free(value); + return 1; + +group_task_error: + free(perm_type); + free(value); + cgroup_delete_cgroup(config_cgroup, 1); + cgroup_table_index--; + return 0; +} + +/* + * Set the control file's uid/gid + */ +int cgroup_config_group_admin_perm(char *perm_type, char *value) +{ + struct passwd *pw, *pw_buffer; + struct group *group, *group_buffer; + int error; + struct cgroup *config_cgroup = + &config_cgroup_table[cgroup_table_index]; + long val = atoi(value); + char buffer[CGROUP_BUFFER_LEN]; + + if (!strcmp(perm_type, "uid")) { + if (!val) { + pw = (struct passwd *) malloc(sizeof(struct passwd)); + + if (!pw) + goto admin_error; + + error = getpwnam_r(value, pw, buffer, CGROUP_BUFFER_LEN, + &pw_buffer); + if (pw_buffer == NULL) { + free(pw); + goto admin_error; + } + + val = pw->pw_uid; + free(pw); + } + config_cgroup->control_uid = val; + } + + if (!strcmp(perm_type, "gid")) { + if (!val) { + group = (struct group *) malloc(sizeof(struct group)); + + if (!group) + goto admin_error; + + error = getgrnam_r(value, group, buffer, + CGROUP_BUFFER_LEN, &group_buffer); + + if (group_buffer == NULL) { + free(group); + goto admin_error; + } + + val = group->gr_gid; + free(group); + } + config_cgroup->control_gid = val; + } + + free(perm_type); + free(value); + return 1; + +admin_error: + free(perm_type); + free(value); + cgroup_delete_cgroup(config_cgroup, 1); + cgroup_table_index--; + return 0; +} + +/* + * The moment we have found the controller's information + * insert it into the config_mount_table. + */ +int cgroup_config_insert_into_mount_table(char *name, char *mount_point) +{ + int i; + + if (config_table_index >= CG_CONTROLLER_MAX) + return 0; + + pthread_rwlock_wrlock(&config_table_lock); + + /* + * Merge controller names with the same mount point + */ + for (i = 0; i < config_table_index; i++) { + if (strcmp(config_mount_table[i].path, mount_point) == 0) { + char *cname = config_mount_table[i].name; + strncat(cname, ",", FILENAME_MAX - strlen(cname) - 1); + strncat(cname, name, + FILENAME_MAX - strlen(cname) - 1); + goto done; + } + } + + strcpy(config_mount_table[config_table_index].name, name); + strcpy(config_mount_table[config_table_index].path, mount_point); + config_table_index++; +done: + pthread_rwlock_unlock(&config_table_lock); + free(name); + free(mount_point); + return 1; +} + +/* + * Cleanup all the data from the config_mount_table + */ +void cgroup_config_cleanup_mount_table(void) +{ + memset(&config_mount_table, 0, + sizeof(struct cg_mount_table_s) * CG_CONTROLLER_MAX); +} + +/* + * Start mounting the mount table. + */ +int cgroup_config_mount_fs() +{ + int ret; + struct stat buff; + int i; + + for (i = 0; i < config_table_index; i++) { + struct cg_mount_table_s *curr = &(config_mount_table[i]); + + ret = stat(curr->path, &buff); + + if (ret < 0 && errno != ENOENT) { + last_errno = errno; + return ECGOTHER; + } + + if (errno == ENOENT) { + ret = mkdir(curr->path, + S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (ret < 0) { + last_errno = errno; + return ECGOTHER; + } + } else if (!S_ISDIR(buff.st_mode)) { + errno = ENOTDIR; + last_errno = errno; + return ECGOTHER; + } + + ret = mount(cgroup_filesystem, curr->path, cgroup_filesystem, + 0, curr->name); + + if (ret < 0) + return ECGMOUNTFAIL; + } + return 0; +} + +/* + * Actually create the groups once the parsing has been finished. + */ +int cgroup_config_create_groups() +{ + int error = 0; + int i; + + for (i = 0; i < cgroup_table_index; i++) { + struct cgroup *cgroup = &config_cgroup_table[i]; + error = cgroup_create_cgroup(cgroup, 0); + cgroup_dbg("creating group %s, error %d\n", cgroup->name, + error); + if (error) + return error; + } + return error; +} + +/* + * Destroy the cgroups + */ +int cgroup_config_destroy_groups(void) +{ + int error = 0; + int i; + + for (i = 0; i < cgroup_table_index; i++) { + struct cgroup *cgroup = &config_cgroup_table[i]; + error = cgroup_delete_cgroup(cgroup, 0); + if (error) + return error; + } + return error; +} + +/* + * Unmount the controllers + */ +int cgroup_config_unmount_controllers(void) +{ + int i; + int error; + + for (i = 0; i < config_table_index; i++) { + /* + * We ignore failures and ensure that all mounted + * containers are unmounted + */ + error = umount(config_mount_table[i].path); + if (error < 0) + cgroup_dbg("Unmount failed\n"); + error = rmdir(config_mount_table[i].path); + if (error < 0) + cgroup_dbg("rmdir failed\n"); + } + + return 0; +} + +/* + * The main function which does all the setup of the data structures + * and finally creates the cgroups + */ +int cgroup_config_load_config(const char *pathname) +{ + int error; + yyin = fopen(pathname, "r"); + + if (!yyin) { + cgroup_dbg("Failed to open file %s\n", pathname); + last_errno = errno; + return ECGOTHER; + } + + if (yyparse() != 0) { + cgroup_dbg("Failed to parse file %s\n", pathname); + return ECGROUPPARSEFAIL; + } + + error = cgroup_config_mount_fs(); + if (error) + goto err_mnt; + + error = cgroup_init(); + if (error) + goto err_mnt; + + error = cgroup_config_create_groups(); + cgroup_dbg("creating all cgroups now, error=%d\n", error); + if (error) + goto err_grp; + + fclose(yyin); + return 0; +err_grp: + cgroup_config_destroy_groups(); +err_mnt: + cgroup_config_unmount_controllers(); + fclose(yyin); + return error; +} diff --git a/src/daemon/cgrulesengd.c b/src/daemon/cgrulesengd.c new file mode 100644 index 0000000..8efdce1 --- /dev/null +++ b/src/daemon/cgrulesengd.c @@ -0,0 +1,793 @@ +/* + * Copyright Red Hat Inc. 2008 + * + * Author: Steve Olivieri + * Author: Vivek Goyal + * + * Some part of the programs have been derived from Dhaval Giani's posting + * for daemon to place the task in right container. Original copyright notice + * follows. + * + * Copyright IBM Corporation, 2007 + * Author: Dhaval Giani linux.vnet.ibm.com> + * Derived from test_cn_proc.c by Matt Helsley + * Original copyright notice follows + * + * Copyright (C) Matt Helsley, IBM Corp. 2005 + * Derived from fcctl.c by Guillaume Thouvenin + * Original copyright notice follows: + * + * Copyright (C) 2005 BULL SA. + * Written by Guillaume Thouvenin bull.net> + * + * 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. + * + * TODO Stop using netlink for communication (or at least rewrite that part). + */ + +#include "libcgroup.h" +#include "cgrulesengd.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Log file, NULL if logging to file is disabled */ +FILE* logfile; + +/* Log facility, 0 if logging to syslog is disabled */ +int logfacility; + +/* Current log level */ +int loglevel; + +/** + * Prints the usage information for this program and, optionally, an error + * message. This function uses vfprintf. + * @param fd The file stream to print to + * @param msg The error message to print (printf style) + * @param ... Any args to msg (printf style) + */ +void usage(FILE* fd, const char* msg, ...) +{ + /* List of args to msg */ + va_list ap; + + /* Put all args after msg into the list. */ + va_start(ap, msg); + + if (msg) + vfprintf(fd, msg, ap); + fprintf(fd, "\n"); + fprintf(fd, "cgrulesengd -- a daemon for the cgroups rules engine\n\n"); + fprintf(fd, "Usage : cgrulesengd [options]\n\n"); + fprintf(fd, " options :\n"); + fprintf(fd, " -q | --quiet quiet mode\n" + " -v | --verbose verbose mode\n" + " -f | --logfile= write log to file\n" + " -s[facility] | --syslog=[facility] write log to syslog\n" + " -n | --nodaemom don't fork daemon\n" + " -d | --debug same as -v -v -n -f -\n" + " -Q | --nolog disable logging\n" + " -h | --help show this help\n\n" + ); + va_end(ap); +} + +/** + * Prints a formatted message (like printf()) to all log destinations. + * Flushes the file stream's buffer so that the message is immediately + * readable. + * @param level The log level (LOG_EMERG ... LOG_DEBUG) + * @param format The format for the message (printf style) + * @param ... Any args to format (printf style) + */ +void flog(int level, const char *format, ...) +{ + /* List of args to format */ + va_list ap; + + /* Check the log level */ + if (level > loglevel) + return; + + if (logfile) { + /* Print the message to the given stream. */ + va_start(ap, format); + vfprintf(logfile, format, ap); + va_end(ap); + fprintf(logfile, "\n"); + + /* + * Flush the stream's buffer, so the data is readable + * immediately. + */ + fflush(logfile); + } + + if (logfacility) { + va_start(ap, format); + vsyslog(LOG_MAKEPRI(logfacility, level), format, ap); + va_end(ap); + } +} + +/** + * Process an event from the kernel, and determine the correct UID/GID/PID to + * pass to libcgroup. Then, libcgroup will decide the cgroup to move the PID + * to, if any. + * @param ev The event to process + * @param type The type of event to process (part of ev) + * @return 0 on success, > 0 on failure + */ +int cgre_process_event(const struct proc_event *ev, const int type) +{ + /* Handle for the /proc/PID/status file */ + FILE *f; + + /* Path for /proc/PID/status file */ + char path[FILENAME_MAX]; + + /* Temporary buffer */ + char *buf = NULL; + + /* UID data */ + uid_t ruid, euid, suid, fsuid, log_uid = 0; + + /* GID data */ + gid_t rgid, egid, sgid, fsgid, log_gid = 0; + + /* PID, just for logging */ + pid_t log_pid = 0; + + /* Return codes */ + int ret = 0; + + /* + * First, we need to open the /proc/PID/status file so that we can + * get the effective UID and GID for the process that we're working + * on. This process is probably not us, so we can't just call + * geteuid() or getegid(). + */ + sprintf(path, "/proc/%d/status", ev->event_data.id.process_pid); + f = fopen(path, "r"); + if (!f) { + flog(LOG_WARNING, "Failed to open %s", path); + goto finished; + } + + /* Now, we need to find either the eUID or the eGID of the process. */ + buf = calloc(4096, sizeof(char)); + if (!buf) { + flog(LOG_WARNING, "Failed to process event, out of" + "memory? Error: %s", + strerror(errno)); + ret = errno; + fclose(f); + goto finished; + } + switch (type) { + case PROC_EVENT_UID: + /* Have the eUID, need to find the eGID. */ + while (fgets(buf, 4096, f)) { + if (!strncmp(buf, "Gid:", 4)) { + sscanf((buf + 5), "%d%d%d%d", &rgid, &egid, + &sgid, &fsgid); + break; + } + memset(buf, '\0', 4096); + } + break; + case PROC_EVENT_GID: + /* Have the eGID, need to find the eUID. */ + while (fgets(buf, 4096, f)) { + if (!strncmp(buf, "Uid:", 4)) { + sscanf((buf + 5), "%d%d%d%d", &ruid, &euid, + &suid, &fsuid); + break; + } + memset(buf, '\0', 4096); + } + break; + default: + flog(LOG_WARNING, "For some reason, we're processing a" + " non-UID/GID event. Something is wrong!"); + break; + } + free(buf); + fclose(f); + + /* + * Now that we have the UID, the GID, and the PID, we can make a call + * to libcgroup to change the cgroup for this PID. + */ + switch (type) { + case PROC_EVENT_UID: + log_uid = ev->event_data.id.e.euid; + log_gid = egid; + log_pid = ev->event_data.id.process_pid; + ret = cgroup_change_cgroup_uid_gid_flags( + ev->event_data.id.e.euid, + egid, ev->event_data.id.process_pid, + CGFLAG_USECACHE); + break; + case PROC_EVENT_GID: + log_uid = euid; + log_gid = ev->event_data.id.e.egid; + log_pid = ev->event_data.id.process_pid; + ret = cgroup_change_cgroup_uid_gid_flags(euid, + ev->event_data.id.e.egid, + ev->event_data.id.process_pid, + CGFLAG_USECACHE); + break; + default: + break; + } + + if (ret) { + /* + * TODO: add some supression, do not spam log when every group + * change fails + */ + flog(LOG_WARNING, "Cgroup change for PID: %d, UID: %d, GID: %d" + " FAILED! (Error Code: %d)", log_pid, log_uid, log_gid, + ret); + } else { + flog(LOG_INFO, "Cgroup change for PID: %d, UID: %d, GID: %d OK", + log_pid, log_uid, log_gid); + } + +finished: + return ret; +} + +/** + * Handle a netlink message. In the event of PROC_EVENT_UID or PROC_EVENT_GID, + * we pass the event along to cgre_process_event for further processing. All + * other events are ignored. + * @param cn_hdr The netlink message + * @return 0 on success, > 0 on error + */ +int cgre_handle_msg(struct cn_msg *cn_hdr) +{ + /* The event to consider */ + struct proc_event *ev; + + /* Return codes */ + int ret = 0; + + /* Get the event data. We only care about two event types. */ + ev = (struct proc_event*)cn_hdr->data; + switch (ev->what) { + case PROC_EVENT_UID: + flog(LOG_DEBUG, "UID Event: PID = %d, tGID = %d, rUID = %d," + " eUID = %d", ev->event_data.id.process_pid, + ev->event_data.id.process_tgid, + ev->event_data.id.r.ruid, + ev->event_data.id.e.euid); + ret = cgre_process_event(ev, PROC_EVENT_UID); + break; + case PROC_EVENT_GID: + flog(LOG_DEBUG, "GID Event: PID = %d, tGID = %d, rGID = %d," + " eGID = %d", ev->event_data.id.process_pid, + ev->event_data.id.process_tgid, + ev->event_data.id.r.rgid, + ev->event_data.id.e.egid); + ret = cgre_process_event(ev, PROC_EVENT_GID); + break; + default: + break; + } + + return ret; +} + +int cgre_create_netlink_socket_process_msg() +{ + int sk_nl; + int err; + struct sockaddr_nl my_nla, kern_nla, from_nla; + socklen_t from_nla_len; + char buff[BUFF_SIZE]; + int rc = -1; + struct nlmsghdr *nl_hdr; + struct cn_msg *cn_hdr; + enum proc_cn_mcast_op *mcop_msg; + size_t recv_len = 0; + + /* + * Create an endpoint for communication. Use the kernel user + * interface device (PF_NETLINK) which is a datagram oriented + * service (SOCK_DGRAM). The protocol used is the connector + * protocol (NETLINK_CONNECTOR) + */ + sk_nl = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (sk_nl == -1) { + cgroup_dbg("socket sk_nl error"); + return rc; + } + + my_nla.nl_family = AF_NETLINK; + my_nla.nl_groups = CN_IDX_PROC; + my_nla.nl_pid = getpid(); + my_nla.nl_pad = 0; + + kern_nla.nl_family = AF_NETLINK; + kern_nla.nl_groups = CN_IDX_PROC; + kern_nla.nl_pid = 1; + kern_nla.nl_pad = 0; + + err = bind(sk_nl, (struct sockaddr *)&my_nla, sizeof(my_nla)); + if (err == -1) { + cgroup_dbg("binding sk_nl error"); + goto close_and_exit; + } + + nl_hdr = (struct nlmsghdr *)buff; + cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr); + mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0]; + cgroup_dbg("sending proc connector: PROC_CN_MCAST_LISTEN... "); + memset(buff, 0, sizeof(buff)); + *mcop_msg = PROC_CN_MCAST_LISTEN; + + /* fill the netlink header */ + nl_hdr->nlmsg_len = SEND_MESSAGE_LEN; + nl_hdr->nlmsg_type = NLMSG_DONE; + nl_hdr->nlmsg_flags = 0; + nl_hdr->nlmsg_seq = 0; + nl_hdr->nlmsg_pid = getpid(); + + /* fill the connector header */ + cn_hdr->id.idx = CN_IDX_PROC; + cn_hdr->id.val = CN_VAL_PROC; + cn_hdr->seq = 0; + cn_hdr->ack = 0; + cn_hdr->len = sizeof(enum proc_cn_mcast_op); + cgroup_dbg("sending netlink message len=%d, cn_msg len=%d\n", + nl_hdr->nlmsg_len, (int) sizeof(struct cn_msg)); + if (send(sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) { + cgroup_dbg("failed to send proc connector mcast ctl op!\n"); + goto close_and_exit; + } + cgroup_dbg("sent\n"); + + for(memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla); + ; memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla)) { + struct nlmsghdr *nlh = (struct nlmsghdr*)buff; + memcpy(&from_nla, &kern_nla, sizeof(from_nla)); + recv_len = recvfrom(sk_nl, buff, BUFF_SIZE, 0, + (struct sockaddr*)&from_nla, &from_nla_len); + if (recv_len == ENOBUFS) { + flog(LOG_ERR, "ERROR: NETLINK BUFFER FULL, MESSAGE " + "DROPPED!"); + continue; + } + if (recv_len < 1) + continue; + while (NLMSG_OK(nlh, recv_len)) { + cn_hdr = NLMSG_DATA(nlh); + if (nlh->nlmsg_type == NLMSG_NOOP) + continue; + if ((nlh->nlmsg_type == NLMSG_ERROR) || + (nlh->nlmsg_type == NLMSG_OVERRUN)) + break; + if(cgre_handle_msg(cn_hdr) < 0) { + goto close_and_exit; + } + if (nlh->nlmsg_type == NLMSG_DONE) + break; + nlh = NLMSG_NEXT(nlh, recv_len); + } + } + +close_and_exit: + close(sk_nl); + return rc; +} + +/** + * Start logging. Opens syslog and/or log file and sets log level. + * @param logp Path of the log file, NULL if no log file was specified + * @param logf Syslog facility, NULL if no facility was specified + * @param logv Log verbosity, 2 is the default, 0 = no logging, 4 = everything + */ +static void cgre_start_log(const char *logp, int logf, int logv) +{ + /* Current system time */ + time_t tm; + + /* Log levels */ + int loglevels[] = { + LOG_EMERG, /* -qq */ + LOG_ERR, /* -q */ + LOG_NOTICE, /* default */ + LOG_INFO, /* -v */ + LOG_DEBUG /* -vv */ + }; + + /* Set default logging destination if nothing was specified */ + if (!logp && !logf) + logf = LOG_DAEMON; + + /* Open log file */ + if (logp) { + if (strcmp("-", logp) == 0) { + logfile = stdout; + } else { + logfile = fopen(logp, "a"); + if (!logfile) { + fprintf(stderr, "Failed to open log file %s," + " error: %s. Continuing anyway.\n", + logp, strerror(errno)); + logfile = stdout; + } + } + } else + logfile = NULL; + + /* Open syslog */ + if (logf) { + openlog("CGRE", LOG_CONS | LOG_PID, logf); + logfacility = logf; + } else + logfacility = 0; + + /* Set the log level */ + if (logv < 0) + logv = 0; + if (logv >= sizeof(loglevels)/sizeof(int)) + logv = sizeof(loglevels)/sizeof(int)-1; + + loglevel = loglevels[logv]; + + flog(LOG_DEBUG, "CGroup Rules Engine Daemon log started"); + tm = time(0); + flog(LOG_DEBUG, "Current time: %s", ctime(&tm)); + flog(LOG_DEBUG, "Opened log file: %s, log facility: %d, log level: %d", + logp, logfacility, loglevel); +} + + +/** + * Turns this program into a daemon. In doing so, we fork() and kill the + * parent process. Note too that stdout, stdin, and stderr are closed in + * daemon mode, and a file descriptor for a log file is opened. + * @param logp Path of the log file, NULL if no log file was specified + * @param logf Syslog facility, 0 if no facility was specified + * @param daemon False to turn off daemon mode (no fork, leave FDs open) + * @param logv Log verbosity, 2 is the default, 0 = no logging, 5 = everything + * @return 0 on success, > 0 on error + */ +int cgre_start_daemon(const char *logp, const int logf, + const unsigned char daemon, const int logv) +{ + /* PID returned from the fork() */ + pid_t pid; + + /* Fork and die. */ + if (daemon) { + pid = fork(); + if (pid < 0) { + openlog("CGRE", LOG_CONS, LOG_DAEMON|LOG_WARNING); + syslog(LOG_DAEMON|LOG_WARNING, "Failed to fork," + " error: %s", strerror(errno)); + closelog(); + fprintf(stderr, "Failed to fork(), %s\n", + strerror(errno)); + return 1; + } else if (pid > 0) { + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask. */ + umask(0); + } else { + cgroup_dbg("Not using daemon mode.\n"); + pid = getpid(); + } + + cgre_start_log(logp, logf, logv); + + if (!daemon) { + /* We can skip the rest, since we're not becoming a daemon. */ + flog(LOG_INFO, "Proceeding with PID %d", getpid()); + return 0; + } else { + /* Get a new SID for the child. */ + if (setsid() < 0) { + flog(LOG_ERR, "Failed to get a new SID, error: %s", + strerror(errno)); + return 2; + } + + /* Change to the root directory. */ + if (chdir("/") < 0) { + flog(LOG_ERR, "Failed to chdir to /, error: %s", + strerror(errno)); + return 3; + } + + /* Close standard file descriptors. */ + close(STDIN_FILENO); + if (logfile != stdout) + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + /* If we make it this far, we're a real daemon! Or we chose not to. */ + flog(LOG_INFO, "Proceeding with PID %d", getpid()); + return 0; +} + +/** + * Catch the SIGUSR2 signal and reload the rules configuration. This function + * makes use of the logfile and flog() to print the new rules. + * @param signum The signal that we caught (always SIGUSR2) + */ +void cgre_flash_rules(int signum) +{ + /* Current time */ + time_t tm = time(0); + + flog(LOG_NOTICE, "Reloading rules configuration."); + flog(LOG_DEBUG, "Current time: %s", ctime(&tm)); + + /* Ask libcgroup to reload the rules table. */ + cgroup_reload_cached_rules(); + + /* Print the results of the new table to our log file. */ + if (logfile && loglevel >= LOG_INFO) { + cgroup_print_rules_config(logfile); + fprintf(logfile, "\n"); + } +} + +/** + * Catch the SIGTERM and SIGINT signals so that we can exit gracefully. Before + * exiting, this function makes use of the logfile and flog(). + * @param signum The signal that we caught (SIGTERM, SIGINT) + */ +void cgre_catch_term(int signum) +{ + /* Current time */ + time_t tm = time(0); + + flog(LOG_NOTICE, "Stopped CGroup Rules Engine Daemon at %s", + ctime(&tm)); + + /* Close the log file, if we opened one */ + if (logfile && logfile != stdout) + fclose(logfile); + + /* Close syslog */ + if (logfacility) + closelog(); + + exit(EXIT_SUCCESS); +} + +/** + * Parse the syslog facility as received on command line. + * @param arg Command line argument with the syslog facility + * @return the syslog facility (e.g. LOG_DAEMON) or 0 on error + */ +static int cgre_parse_syslog_facility(const char *arg) +{ + if (arg == NULL) + return 0; + + if (strlen(arg) > 1) + return 0; + + switch (arg[0]) { + case '0': + return LOG_LOCAL0; + case '1': + return LOG_LOCAL1; + case '2': + return LOG_LOCAL2; + case '3': + return LOG_LOCAL3; + case '4': + return LOG_LOCAL4; + case '5': + return LOG_LOCAL5; + case '6': + return LOG_LOCAL6; + case '7': + return LOG_LOCAL7; + case 'D': + return LOG_DAEMON; + default: + return 0; + } +} + +int main(int argc, char *argv[]) +{ + /* Patch to the log file */ + const char *logp = NULL; + + /* Syslog facility */ + int facility = 0; + + /* Verbose level */ + int verbosity = 2; + + /* For catching signals */ + struct sigaction sa; + + /* Should we daemonize? */ + unsigned char daemon = 1; + + /* Return codes */ + int ret = 0; + + /* Command line arguments */ + const char *short_options = "hvqf:s::ndQ"; + struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {"quiet", no_argument, NULL, 'q'}, + {"logfile", required_argument, NULL, 'f'}, + {"syslog", optional_argument, NULL, 's'}, + {"nodaemon", no_argument, NULL, 'n'}, + {"debug", no_argument, NULL, 'd'}, + {"nolog", no_argument, NULL, 'Q'}, + {NULL, 0, NULL, 0} + }; + + /* Make sure the user is root. */ + if (getuid() != 0) { + fprintf(stderr, "Error: Only root can start/stop the control" + " group rules engine daemon\n"); + ret = 1; + goto finished; + } + + while (1) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': /* --help */ + usage(stdout, "Help:\n"); + ret = 0; + goto finished; + + case 'v': /* --verbose */ + verbosity++; + break; + + case 'q': /* --quiet */ + verbosity--; + break; + + case 'Q': /* --nolog */ + verbosity = 0; + break; + + case 'f': /* --logfile= */ + logp = optarg; + break; + + case 's': /* --syslog=[facility] */ + if (optarg) { + facility = cgre_parse_syslog_facility(optarg); + if (facility == 0) { + fprintf(stderr, + "Unknown syslog facility: %s\n", + optarg); + ret = 2; + goto finished; + } + } else { + facility = LOG_DAEMON; + } + break; + + case 'n': /* --no-fork */ + daemon = 0; + break; + + case 'd': /* --debug */ + /* same as -vvn */ + daemon = 0; + verbosity = 4; + logp = "-"; + break; + + default: + usage(stderr, ""); + ret = 2; + goto finished; + } + } + + /* Initialize libcgroup. */ + if ((ret = cgroup_init()) != 0) { + fprintf(stderr, "Error: libcgroup initialization failed, %d\n", + ret); + goto finished; + } + + /* Ask libcgroup to load the configuration rules. */ + if ((ret = cgroup_init_rules_cache()) != 0) { + fprintf(stderr, "Error: libcgroup failed to initialize rules" + "cache, %d\n", ret); + goto finished; + } + + /* Now, start the daemon. */ + ret = cgre_start_daemon(logp, facility, daemon, verbosity); + if (ret < 0) { + fprintf(stderr, "Error: Failed to launch the daemon, %d\n", + ret); + goto finished; + } + + /* + * Set up the signal handler to reload the cached rules upon reception + * of a SIGUSR2 signal. + */ + sa.sa_handler = &cgre_flash_rules; + sa.sa_flags = 0; + sa.sa_restorer = NULL; + sigemptyset(&sa.sa_mask); + if ((ret = sigaction(SIGUSR2, &sa, NULL))) { + flog(LOG_ERR, "Failed to set up signal handler for SIGUSR2." + " Error: %s", strerror(errno)); + goto finished; + } + + /* + * Set up the signal handler to catch SIGINT and SIGTERM so that we + * can exit gracefully. + */ + sa.sa_handler = &cgre_catch_term; + ret = sigaction(SIGINT, &sa, NULL); + ret |= sigaction(SIGTERM, &sa, NULL); + if (ret) { + flog(LOG_ERR, "Failed to set up the signal handler. Error:" + " %s", strerror(errno)); + goto finished; + } + + /* Print the configuration to the log file, or stdout. */ + if (logfile && loglevel >= LOG_INFO) + cgroup_print_rules_config(logfile); + + flog(LOG_NOTICE, "Started the CGroup Rules Engine Daemon."); + + /* We loop endlesly in this function, unless we encounter an error. */ + ret = cgre_create_netlink_socket_process_msg(); + +finished: + if (logfile && logfile != stdout) + fclose(logfile); + + return ret; +} diff --git a/src/daemon/cgrulesengd.h b/src/daemon/cgrulesengd.h new file mode 100644 index 0000000..1840143 --- /dev/null +++ b/src/daemon/cgrulesengd.h @@ -0,0 +1,127 @@ +/* + * Copyright Red Hat Inc. 2008 + * + * Author: Steve Olivieri + * + * 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. + */ + +#ifndef _CGRULESENGD_H +#define _CGRULESENGD_H + +#include + +__BEGIN_DECLS + +#include "config.h" +#include "libcgroup.h" +#include + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef __USE_GNU +#define __USE_GNU +#endif + +/* A simple macro for printing messages only when CGROUP_DEBUG is defined. */ +#ifdef CGROUP_DEBUG + #define cgroup_dbg(a...) printf(a) +#else + #define cgroup_dbg(a...) do {} while (0) +#endif /* CGROUP_DEBUG */ + +/* The following ten macros are all for the Netlink code. */ +#define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \ + sizeof(enum proc_cn_mcast_op))) +#define RECV_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \ + sizeof(struct proc_event))) + +#define SEND_MESSAGE_SIZE (NLMSG_SPACE(SEND_MESSAGE_LEN)) +#define RECV_MESSAGE_SIZE (NLMSG_SPACE(RECV_MESSAGE_LEN)) + +#define max(x,y) ((y)<(x)?(x):(y)) +#define min(x,y) ((y)>(x)?(x):(y)) + +#define BUFF_SIZE (max(max(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE), 1024)) +#define MIN_RECV_SIZE (min(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE)) + +#define PROC_CN_MCAST_LISTEN (1) +#define PROC_CN_MCAST_IGNORE (2) + +/** + * Prints the usage information for this program and, optionally, an error + * message. This function uses vfprintf. + * @param fd The file stream to print to + * @param msg The error message to print (printf style) + * @param ... Any args to msg (printf style) + */ +void cgre_usage(FILE *fd, const char *msg, ...); + +/** + * Prints a formatted message (like printf()) to all log destinations. + * Flushes the file stream's buffer so that the message is immediately + * readable. + * @param level The log level (LOG_EMERG ... LOG_DEBUG) + * @param format The format for the message (printf style) + * @param ... Any args to format (printf style) + */ +void flog(int level, const char *msg, ...); + +/** + * Process an event from the kernel, and determine the correct UID/GID/PID to + * pass to libcgroup. Then, libcgroup will decide the cgroup to move the PID + * to, if any. + * @param ev The event to process + * @param type The type of event to process (part of ev) + * @return 0 on success, > 0 on failure + */ +int cgre_process_event(const struct proc_event *ev, const int type); + +/** + * Handle a netlink message. In the event of PROC_EVENT_UID or PROC_EVENT_GID, + * we pass the event along to cgre_process_event for further processing. All + * other events are ignored. + * @param cn_hdr The netlink message + * @return 0 on success, > 0 on error + */ +int cgre_handle_message(struct cn_msg *cn_hdr); + +/** + * Turns this program into a daemon. In doing so, we fork() and kill the + * parent process. Note too that stdout, stdin, and stderr are closed in + * daemon mode, and a file descriptor for a log file is opened. + * @param logp Path of the log file, NULL if no log file was specified + * @param logf Syslog facility, NULL if no facility was specified + * @param daemon False to turn off daemon mode (no fork, leave FDs open) + * @param logv Log verbosity, 2 is the default, 0 = no logging, 5 = everything + * @return 0 on success, > 0 on error + */ +int cgre_start_daemon(const char *logp, const int logf, + const unsigned char daemon, const int logv); + +/** + * Catch the SIGUSR2 signal and reload the rules configuration. This function + * makes use of the logfile and flog() to print the new rules. + * @param signum The signal that we caught (always SIGUSR2) + */ +void cgre_flash_rules(int signum); + +/** + * Catch the SIGTERM and SIGINT signal so that we can exit gracefully. Before + * exiting, this function makes use of the logfile and flog(). + * @param signum The signal that we caught (SIGTERM, SIGINT) + */ +void cgre_catch_term(int signum); + +__END_DECLS + +#endif /* _CGRULESENGD_H */ + diff --git a/src/lex.l b/src/lex.l new file mode 100644 index 0000000..48a53b4 --- /dev/null +++ b/src/lex.l @@ -0,0 +1,34 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Authors: Balbir Singh + * 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 +#include "y.tab.h" +int line_no = 1; + +%} + +%% +\n {line_no++;} +[ \t] {/* DO NOTHING */} +^#.*[ \t]* {/* Comments */} +^\*.*[ \t]* {/* Comments */} +"mount" {return MOUNT;} +"task" {return TASK;} +"admin" {return ADMIN;} +"perm" {return PERM;} +"group" {return GROUP;} +[a-zA-Z0-9_\-\/\.]+ {yylval.name = strdup(yytext); return ID;} +. {return yytext[0];} +%% + diff --git a/src/libcgroup-internal.h b/src/libcgroup-internal.h new file mode 100644 index 0000000..001da1a --- /dev/null +++ b/src/libcgroup-internal.h @@ -0,0 +1,105 @@ +/* + * Copyright IBM Corporation. 2008 + * + * Author: Dhaval Giani + * + * 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. + * + */ +#ifndef __LIBCG_INTERNAL + +#define __LIBCG_INTERNAL + +__BEGIN_DECLS + +#include "config.h" +#include +#include + +#define CGRULES_CONF_FILE "/etc/cgrules.conf" +#define CGRULES_MAX_FIELDS_PER_LINE 3 + +#define CGROUP_BUFFER_LEN (5 * FILENAME_MAX) + +#ifdef CGROUP_DEBUG +#define cgroup_dbg(x...) printf(x) +#else +#define cgroup_dbg(x...) do {} while (0) +#endif + +struct control_value { + char name[FILENAME_MAX]; + char value[CG_VALUE_MAX]; +}; + +struct cgroup_controller { + char name[FILENAME_MAX]; + struct control_value *values[CG_NV_MAX]; + int index; +}; + +struct cgroup { + char name[FILENAME_MAX]; + struct cgroup_controller *controller[CG_CONTROLLER_MAX]; + int index; + uid_t tasks_uid; + gid_t tasks_gid; + uid_t control_uid; + gid_t control_gid; +}; + + +struct cg_mount_table_s { + char name[FILENAME_MAX]; + char path[FILENAME_MAX]; + int index; +}; + +struct cgroup_rules_data { + pid_t pid; /* pid of the process which needs to change group */ + + /* Details of user under consideration for destination cgroup */ + struct passwd *pw; + /* Gid of the process */ + gid_t gid; +}; + +/* A rule that maps UID/GID to a cgroup */ +struct cgroup_rule { + uid_t uid; + gid_t gid; + char name[LOGIN_NAME_MAX]; + char destination[FILENAME_MAX]; + char *controllers[MAX_MNT_ELEMENTS]; + struct cgroup_rule *next; +}; + +/* Container for a list of rules */ +struct cgroup_rule_list { + struct cgroup_rule *head; + struct cgroup_rule *tail; + int len; +}; + + +/* Internal API */ +char *cg_build_path(char *name, char *path, char *type); + +/* + * config related API + */ +int cgroup_config_insert_cgroup(char *cg_name); +int cgroup_config_parse_controller_options(char *controller, char *name_value); +int cgroup_config_group_task_perm(char *perm_type, char *value); +int cgroup_config_group_admin_perm(char *perm_type, char *value); +int cgroup_config_insert_into_mount_table(char *name, char *mount_point); +void cgroup_config_cleanup_mount_table(void); +__END_DECLS + +#endif diff --git a/src/libcgroup.map b/src/libcgroup.map new file mode 100644 index 0000000..1989f90 --- /dev/null +++ b/src/libcgroup.map @@ -0,0 +1,56 @@ +CGROUP_0.32 { +global: + cgroup_init; + cgroup_attach_task; + cgroup_modify_cgroup; + cgroup_create_cgroup; + cgroup_delete_cgroup; + cgroup_attach_task_pid; + cgroup_get_cgroup; + cgroup_create_cgroup_from_parent; + cgroup_copy_cgroup; + cgroup_change_cgroup_uid_gid; + cgroup_change_cgroup_path; + cgroup_new_cgroup; + cgroup_add_controller; + cgroup_free; + cgroup_free_controllers; + cgroup_add_value_string; + cgroup_add_value_int64; + cgroup_add_value_uint64; + cgroup_add_value_bool; + cgroup_compare_cgroup; + cgroup_compare_controllers; + cgroup_set_uid_gid; + cgroup_get_uid_gid; + cgroup_get_value_string; + cgroup_set_value_string; + cgroup_get_value_int64; + cgroup_set_value_int64; + cgroup_get_value_uint64; + cgroup_set_value_uint64; + cgroup_get_value_bool; + cgroup_set_value_bool; + cgroup_change_cgroup_uid_gid_flags; + cgroup_print_rules_config; + cgroup_reload_cached_rules; + cgroup_init_rules_cache; + cgroup_get_current_controller_path; + cgroup_config_load_config; +local: + *; +}; + +CGROUP_0.32.1 { +global: + cgroup_strerror; +} CGROUP_0.32; + +CGROUP_0.33 { +global: + cgroup_get_last_errno; + cgroup_walk_tree_begin; + cgroup_walk_tree_next; + cgroup_walk_tree_end; +} CGROUP_0.32.1; + diff --git a/src/pam/pam_cgroup.c b/src/pam/pam_cgroup.c new file mode 100644 index 0000000..c4ce633 --- /dev/null +++ b/src/pam/pam_cgroup.c @@ -0,0 +1,162 @@ +/* + * Copyright RedHat Inc. 2008 + * + * Author: Vivek Goyal + * + * Derived from pam_limits.c. Original Copyright notice follows. + * + * Copyright (c) Cristian Gafton, 1996-1997, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * End of original copyright notice. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Module defines + */ + +#define PAM_SM_SESSION + +#include +#include +#include +#include + +/* argument parsing */ + +#define PAM_DEBUG_ARG 0x0001 + +static int _pam_parse(const pam_handle_t *pamh, int argc, const char **argv) +{ + int ctrl = 0; + + /* step through arguments */ + for (ctrl = 0; argc-- > 0; ++argv) { + if (!strcmp(*argv, "debug")) + ctrl |= PAM_DEBUG_ARG; + else + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + + return ctrl; +} + +/* now the session stuff */ +PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + pid_t pid; + int ctrl, ret; + char *user_name; + struct passwd *pwd; + + D(("called.")); + + ctrl = _pam_parse(pamh, argc, argv); + + ret = pam_get_item(pamh, PAM_USER, (void *) &user_name); + if (user_name == NULL || ret != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "open_session - error recovering" + "username"); + return PAM_SESSION_ERR; + } + + pwd = pam_modutil_getpwnam(pamh, user_name); + if (!pwd) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_ERR, "open_session username" + " '%s' does not exist", user_name); + return PAM_SESSION_ERR; + } + + D(("user name is %s", user_name)); + + /* Initialize libcg */ + ret = cgroup_init(); + if (ret) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_ERR, "libcgroup initialization" + " failed"); + return PAM_SESSION_ERR; + } + + D(("Initialized libcgroup successfuly.")); + + /* Determine the pid of the task */ + pid = getpid(); + + /* Note: We are using default gid here. Is there a way to determine + * under what egid service will be provided? + */ + ret = cgroup_change_cgroup_uid_gid(pwd->pw_uid, pwd->pw_gid, pid); + if (ret) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_ERR, "Change of cgroup for process" + " with username %s failed.\n", user_name); + return PAM_SESSION_ERR; + } + + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "Changed cgroup for process %d" + " with username %s.\n", pid, user_name); + + return PAM_SUCCESS; +} + +PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + D(("called pam_cgroup close session")); + + /* nothing to do yet */ + return PAM_SUCCESS; +} diff --git a/src/parse.y b/src/parse.y new file mode 100644 index 0000000..8c7ae24 --- /dev/null +++ b/src/parse.y @@ -0,0 +1,269 @@ +/* + * Copyright IBM Corporation. 2007 + * + * Authors: Balbir Singh + * 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. + * + * NOTE: The grammar has been modified, not to be the most efficient, but + * to allow easy updation of internal data structures. + */ +%{ +#include +#include +#include +#include +#include + +int yylex(void); +extern int line_no; +extern char *yytext; + +void yyerror(char *s) +{ + fprintf(stderr, "error at line number %d at %c:%s", line_no, *yytext, + s); +} + +int yywrap(void) +{ + return 1; +} + +%} + +%token ID MOUNT GROUP PERM TASK ADMIN + +%union { + char *name; + char chr; + int val; +} +%type ID namevalue_conf +%type mountvalue_conf mount task_namevalue_conf admin_namevalue_conf +%type admin_conf task_conf task_or_admin group_conf group start +%start start +%% + +start : start group + { + $$ = $1; + } + | start mount + { + $$ = $1; + } + | + { + $$ = 1; + } + ; + +group : GROUP ID '{' group_conf '}' + { + $$ = $4; + if ($$) + cgroup_config_insert_cgroup($2); + else { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +group_conf + : ID '{' namevalue_conf '}' + { + $$ = cgroup_config_parse_controller_options($1, $3); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | group_conf ID '{' namevalue_conf '}' + { + $$ = cgroup_config_parse_controller_options($2, $4); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | PERM '{' task_or_admin '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +namevalue_conf + : ID '=' ID ';' + { + $1 = realloc($1, strlen($1) + strlen($3) + 2); + $1 = strncat($1, " ", strlen(" ")); + $$ = strncat($1, $3, strlen($3)); + free($3); + } + | namevalue_conf ID '=' ID ';' + { + int len = 0; + if ($1) + len = strlen($1); + $2 = realloc($2, len + strlen($2) + strlen($4) + 3); + $2 = strncat($2, " ", strlen(" ")); + $$ = strncat($2, $4, strlen($4)); + if ($1) { + $2 = strncat($2, ":", strlen(":")); + $$ = strncat($2, $1, strlen($1)); + } + free($4); + } + | + { + $$ = NULL; + } + ; + +task_namevalue_conf + : ID '=' ID ';' + { + $$ = cgroup_config_group_task_perm($1, $3); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | task_namevalue_conf ID '=' ID ';' + { + $$ = $1 && cgroup_config_group_task_perm($2, $4); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +admin_namevalue_conf + : ID '=' ID ';' + { + $$ = cgroup_config_group_admin_perm($1, $3); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | admin_namevalue_conf ID '=' ID ';' + { + $$ = $1 && cgroup_config_group_admin_perm($2, $4); + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +task_or_admin + : TASK '{' task_namevalue_conf '}' admin_conf + { + $$ = $3 && $5; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + | ADMIN '{' admin_namevalue_conf '}' task_conf + { + $$ = $3 && $5; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +admin_conf: ADMIN '{' admin_namevalue_conf '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +task_conf: TASK '{' task_namevalue_conf '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + +mountvalue_conf + : ID '=' ID ';' + { + if (!cgroup_config_insert_into_mount_table($1, $3)) { + cgroup_config_cleanup_mount_table(); + $$ = 0; + return $$; + } + $$ = 1; + } + | mountvalue_conf ID '=' ID ';' + { + if (!cgroup_config_insert_into_mount_table($2, $4)) { + cgroup_config_cleanup_mount_table(); + $$ = 0; + return $$; + } + $$ = 1; + } + ; + +mount : MOUNT '{' mountvalue_conf '}' + { + $$ = $3; + if (!$$) { + fprintf(stderr, "parsing failed at line number %d\n", + line_no); + $$ = 0; + return $$; + } + } + ; + + +%% diff --git a/src/tools/cgclassify.c b/src/tools/cgclassify.c new file mode 100644 index 0000000..c044608 --- /dev/null +++ b/src/tools/cgclassify.c @@ -0,0 +1,219 @@ +/* + * Copyright RedHat Inc. 2008 + * + * Authors: Vivek Goyal + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tools-common.h" + +#define TEMP_BUF 81 + +/* + * Go through /proc//status file to determine the euid of the + * process. + * It returns 0 on success and negative values on failure. + */ + +int euid_of_pid(pid_t pid) +{ + FILE *fp; + char path[FILENAME_MAX]; + char buf[TEMP_BUF]; + uid_t ruid, euid, suid, fsuid; + + sprintf(path, "/proc/%d/status", pid); + fp = fopen(path, "r"); + if (!fp) { + cgroup_dbg("Error in opening file %s:%s\n", path, + strerror(errno)); + return -1; + } + + while (fgets(buf, TEMP_BUF, fp)) { + if (!strncmp(buf, "Uid:", 4)) { + sscanf((buf + 5), "%d%d%d%d", (int *)&ruid, + (int *)&euid, (int *)&suid, (int *)&fsuid); + cgroup_dbg("Scanned proc values are %d %d %d %d\n", + ruid, euid, suid, fsuid); + return euid; + } + } + + /* If we are here, we could not find euid. Return error. */ + return -1; +} + +/* + * Go through /proc//status file to determine the egid of the + * process. + * It returns 0 on success and negative values on failure. + */ + +int egid_of_pid(pid_t pid) +{ + FILE *fp; + char path[FILENAME_MAX]; + char buf[TEMP_BUF]; + gid_t rgid, egid, sgid, fsgid; + + sprintf(path, "/proc/%d/status", pid); + fp = fopen(path, "r"); + if (!fp) { + cgroup_dbg("Error in opening file %s:%s\n", path, + strerror(errno)); + return -1; + } + + while (fgets(buf, TEMP_BUF, fp)) { + if (!strncmp(buf, "Gid:", 4)) { + sscanf((buf + 5), "%d%d%d%d", (int *)&rgid, + (int *)&egid, (int *)&sgid, (int *)&fsgid); + cgroup_dbg("Scanned proc values are %d %d %d %d\n", + rgid, egid, sgid, fsgid); + return egid; + } + } + + /* If we are here, we could not find egid. Return error. */ + return -1; +} + +/* + * Change process group as specified on command line. + */ +int change_group_path(pid_t pid, struct cgroup_group_spec *cgroup_list[]) +{ + int i; + int ret = 0; + + for (i = 0; i < CG_HIER_MAX; i++) { + if (!cgroup_list[i]) + break; + + ret = cgroup_change_cgroup_path(cgroup_list[i]->path, pid, + cgroup_list[i]->controllers); + if (ret) + fprintf(stderr, "Error changing group of pid %d: %s\n", + pid, cgroup_strerror(ret)); + return -1; + } + + return 0; +} + +/* + * Change process group as specified in cgrules.conf. + */ +int change_group_uid_gid(pid_t pid) +{ + uid_t euid; + gid_t egid; + int ret; + + /* Put pid into right cgroup as per rules in /etc/cgrules.conf */ + euid = euid_of_pid(pid); + if (euid == -1) { + fprintf(stderr, "Error in determining euid of" + " pid %d\n", pid); + return -1; + } + + egid = egid_of_pid(pid); + if (egid == -1) { + fprintf(stderr, "Error in determining egid of" + " pid %d\n", pid); + return -1; + } + + /* Change the cgroup by determining the rules based on uid */ + ret = cgroup_change_cgroup_uid_gid(euid, egid, pid); + if (ret) { + fprintf(stderr, "Error: change of cgroup failed for" + " pid %d: %s\n", + pid, cgroup_strerror(ret)); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret = 0, i, exit_code = 0; + pid_t pid; + int cg_specified = 0; + struct cgroup_group_spec *cgroup_list[CG_HIER_MAX]; + int c; + + + if (argc < 2) { + fprintf(stderr, "usage is %s " + "[-g :] " + " \n", + argv[0]); + exit(2); + } + + memset(cgroup_list, 0, sizeof(cgroup_list)); + while ((c = getopt(argc, argv, "+g:")) > 0) { + switch (c) { + case 'g': + if (parse_cgroup_spec(cgroup_list, optarg)) { + fprintf(stderr, "cgroup controller and path" + "parsing failed\n"); + return -1; + } + cg_specified = 1; + break; + default: + fprintf(stderr, "Invalid command line option\n"); + exit(2); + break; + } + } + + + /* Initialize libcg */ + ret = cgroup_init(); + if (ret) { + fprintf(stderr, "libcgroup initialization failed:%d\n", ret); + return ret; + } + + for (i = optind; i < argc; i++) { + pid = (uid_t) atoi(argv[i]); + + if (cg_specified) + ret = change_group_path(pid, cgroup_list); + else + ret = change_group_uid_gid(pid); + + /* if any group change fails */ + if (ret) + exit_code = 1; + } + return exit_code; + +} diff --git a/src/tools/cgconfig.c b/src/tools/cgconfig.c new file mode 100644 index 0000000..cc33ad9 --- /dev/null +++ b/src/tools/cgconfig.c @@ -0,0 +1,74 @@ + +/* + * Copyright IBM Corporation. 2007 + * + * Authors: Dhaval Giani + * Balbir Singh + * + * 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. + * + * Code initiated and designed by Dhaval Giani. All faults are most likely + * his mistake. + */ + +#include +#include +#include +#include +#include +#include +#include + + +static void usage(char *progname) +{ + printf("Usage: %s [OPTION] [FILE]\n", basename(progname)); + printf("Parse and load the specified cgroups configuration file\n"); + printf("\n"); + printf(" -h, --help Display this help\n"); + printf(" -l, --load=FILE Parse and load the cgroups configuration file\n"); + exit(2); +} + +int main(int argc, char *argv[]) +{ + int c; + char filename[PATH_MAX]; + int ret; + static struct option options[] = { + {"help", 0, 0, 'h'}, + {"load", 1, 0, 'l'}, + {0, 0, 0, 0} + }; + + if (argc < 2) + usage(argv[0]); /* usage() exits */ + + while ((c = getopt_long(argc, argv, "hl:", options, NULL)) > 0) { + switch (c) { + case 'h': + usage(argv[0]); + break; + case 'l': + strncpy(filename, optarg, PATH_MAX); + ret = cgroup_config_load_config(filename); + if (ret) { + printf("Loading configuration file %s " + "failed\n%s\n", filename, + cgroup_strerror(ret)); + exit(3); + } + return 0; + default: + usage(argv[0]); + break; + } + } + return 0; +} diff --git a/src/tools/cgexec.c b/src/tools/cgexec.c new file mode 100644 index 0000000..167d873 --- /dev/null +++ b/src/tools/cgexec.c @@ -0,0 +1,122 @@ +/* + * Copyright RedHat Inc. 2008 + * + * Authors: Vivek Goyal + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tools-common.h" + +int main(int argc, char *argv[]) +{ + int ret = 0, i; + int cg_specified = 0; + uid_t euid; + pid_t pid; + gid_t egid; + char c; + struct cgroup_group_spec *cgroup_list[CG_HIER_MAX]; + + if (argc < 2) { + fprintf(stderr, "Usage is %s" + " [-g :]" + " command [arguments] \n", + argv[0]); + exit(2); + } + + memset(cgroup_list, 0, sizeof(cgroup_list)); + + while ((c = getopt(argc, argv, "+g:")) > 0) { + switch (c) { + case 'g': + if (parse_cgroup_spec(cgroup_list, optarg)) { + fprintf(stderr, "cgroup controller and path" + "parsing failed\n"); + return -1; + } + cg_specified = 1; + break; + default: + fprintf(stderr, "Invalid command line option\n"); + exit(1); + break; + } + } + + /* Executable name */ + if (!argv[optind]) { + fprintf(stderr, "No command specified\n"); + exit(1); + } + + /* Initialize libcg */ + ret = cgroup_init(); + if (ret) { + fprintf(stderr, "libcgroup initialization failed:%d", ret); + return ret; + } + + euid = geteuid(); + egid = getegid(); + pid = getpid(); + + if (cg_specified) { + /* + * User has specified the list of control group and + * controllers + * */ + for (i = 0; i < CG_HIER_MAX; i++) { + if (!cgroup_list[i]) + break; + + ret = cgroup_change_cgroup_path(cgroup_list[i]->path, + pid, + cgroup_list[i]->controllers); + if (ret) { + fprintf(stderr, + "cgroup change of group failed\n"); + return ret; + } + } + } else { + + /* Change the cgroup by determining the rules based on euid */ + ret = cgroup_change_cgroup_uid_gid(euid, egid, pid); + if (ret) { + fprintf(stderr, "cgroup change of group failed\n"); + return ret; + } + } + + /* Now exec the new process */ + ret = execvp(argv[optind], &argv[optind]); + if (ret == -1) { + fprintf(stderr, "%s", strerror(errno)); + return -1; + } + return 0; +} diff --git a/src/tools/tools-common.c b/src/tools/tools-common.c new file mode 100644 index 0000000..0bb666f --- /dev/null +++ b/src/tools/tools-common.c @@ -0,0 +1,88 @@ +/* + * Copyright Red Hat, Inc. 2009 + * + * Author: Vivek Goyal + * Jan Safranek + * + * 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 +#include +#include +#include + +#include +#include "tools-common.h" + +int parse_cgroup_spec(struct cgroup_group_spec *cdptr[], char *optarg) +{ + struct cgroup_group_spec *ptr; + int i, j; + char *cptr, *pathptr, *temp; + + ptr = *cdptr; + + /* Find first free entry inside the cgroup data array */ + for (i = 0; i < CG_HIER_MAX; i++, ptr++) { + if (!cdptr[i]) + break; + } + + if (i == CG_HIER_MAX) { + /* No free slot found */ + fprintf(stderr, "Max allowed hierarchies %d reached\n", + CG_HIER_MAX); + return -1; + } + + /* Extract list of controllers */ + cptr = strtok(optarg, ":"); + cgroup_dbg("list of controllers is %s\n", cptr); + if (!cptr) + return -1; + + /* Extract cgroup path */ + pathptr = strtok(NULL, ":"); + cgroup_dbg("cgroup path is %s\n", pathptr); + if (!pathptr) + return -1; + + /* instanciate cgroup_data. */ + cdptr[i] = malloc(sizeof(struct cgroup_group_spec)); + if (!cdptr[i]) { + fprintf(stderr, "%s\n", strerror(errno)); + return -1; + } + /* Convert list of controllers into an array of strings. */ + j = 0; + do { + if (j == 0) + temp = strtok(cptr, ","); + else + temp = strtok(NULL, ","); + + if (temp) { + cdptr[i]->controllers[j] = strdup(temp); + if (!cdptr[i]->controllers[j]) { + free(cdptr[i]); + fprintf(stderr, "%s\n", strerror(errno)); + return -1; + } + } + j++; + } while (temp); + + /* Store path to the cgroup */ + strncpy(cdptr[i]->path, pathptr, FILENAME_MAX); + cdptr[i]->path[FILENAME_MAX-1] = '\0'; + + return 0; +} diff --git a/src/tools/tools-common.h b/src/tools/tools-common.h new file mode 100644 index 0000000..3437973 --- /dev/null +++ b/src/tools/tools-common.h @@ -0,0 +1,54 @@ +/* + * Copyright Red Hat, Inc. 2009 + * + * Author: Vivek Goyal + * Jan Safranek + * + * 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. + * + */ + +#ifndef __TOOLS_COMMON + +#define __TOOLS_COMMON + +#include "config.h" +#include + +#ifdef CGROUP_DEBUG +#define cgroup_dbg(x...) printf(x) +#else +#define cgroup_dbg(x...) do {} while (0) +#endif + +/** + * Auxiliary specifier of group, used to store parsed command line options. + */ +struct cgroup_group_spec { + char path[FILENAME_MAX]; + char *controllers[CG_CONTROLLER_MAX]; +}; + + +/** + * Parse command line option with group specifier into provided data structure. + * The option must have form of 'controller1,controller2,..:group_name'. + * + * The parsed list of controllers and group name is added at the end of + * provided cdptr. + * + * @param cdptr Target data structure to fill. New item is allocated and added + * at the end. + * @param optarg Argument to parse. + * @return 0 on success, != 0 on error. + */ +int parse_cgroup_spec(struct cgroup_group_spec *cdptr[], char *optarg); + + +#endif /* TOOLS_COMMON */ diff --git a/src/wrapper.c b/src/wrapper.c new file mode 100644 index 0000000..2f8fcf2 --- /dev/null +++ b/src/wrapper.c @@ -0,0 +1,557 @@ +/* + * Copyright IBM Corporation. 2008 + * + * Author: Dhaval Giani + * + * 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. + * + * Code initiated and designed by Dhaval Giani. All faults are most likely + * his mistake. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct cgroup *cgroup_new_cgroup(const char *name) +{ + struct cgroup *cgroup = calloc(1, sizeof(struct cgroup)); + + if (!cgroup) + return NULL; + + strncpy(cgroup->name, name, sizeof(cgroup->name)); + + return cgroup; +} + +struct cgroup_controller *cgroup_add_controller(struct cgroup *cgroup, + const char *name) +{ + int i; + struct cgroup_controller *controller; + + if (!cgroup) + return NULL; + + /* + * Still not sure how to handle the failure here. + */ + if (cgroup->index >= CG_CONTROLLER_MAX) + return NULL; + + /* + * Still not sure how to handle the failure here. + */ + for (i = 0; i < cgroup->index; i++) { + if (strncmp(name, cgroup->controller[i]->name, + sizeof(cgroup->controller[i]->name)) == 0) + return NULL; + } + + controller = calloc(1, sizeof(struct cgroup_controller)); + + if (!controller) + return NULL; + + strncpy(controller->name, name, sizeof(controller->name)); + controller->index = 0; + + cgroup->controller[cgroup->index] = controller; + cgroup->index++; + + return controller; +} + +void cgroup_free_controllers(struct cgroup *cgroup) +{ + int i, j; + + if (!cgroup) + return; + + for (i = 0; i < cgroup->index; i++) { + for (j = 0; j < cgroup->controller[i]->index; j++) + free(cgroup->controller[i]->values[j]); + free(cgroup->controller[i]); + } +} + +void cgroup_free(struct cgroup **cgroup) +{ + struct cgroup *cg = *cgroup; + + /* + * Passing NULL pointers is OK. We just return. + */ + if (!cg) + return; + + cgroup_free_controllers(cg); + free(cg); + *cgroup = NULL; +} + +int cgroup_add_value_string(struct cgroup_controller *controller, + const char *name, const char *value) +{ + int i; + struct control_value *cntl_value; + + if (!controller) + return ECGINVAL; + + if (controller->index >= CG_VALUE_MAX) + return ECGMAXVALUESEXCEEDED; + + for (i = 0; i < controller->index && i < CG_VALUE_MAX; i++) { + if (!strcmp(controller->values[i]->name, name)) + return ECGVALUEEXISTS; + } + + cntl_value = calloc(1, sizeof(struct control_value)); + + if (!cntl_value) + return ECGCONTROLLERCREATEFAILED; + + strncpy(cntl_value->name, name, sizeof(cntl_value->name)); + strncpy(cntl_value->value, value, sizeof(cntl_value->value)); + controller->values[controller->index] = cntl_value; + controller->index++; + + return 0; +} + +int cgroup_add_value_int64(struct cgroup_controller *controller, + const char *name, int64_t value) +{ + int i; + unsigned ret; + struct control_value *cntl_value; + + if (!controller) + return ECGINVAL; + + if (controller->index >= CG_VALUE_MAX) + return ECGMAXVALUESEXCEEDED; + + for (i = 0; i < controller->index && i < CG_VALUE_MAX; i++) { + if (!strcmp(controller->values[i]->name, name)) + return ECGVALUEEXISTS; + } + + cntl_value = calloc(1, sizeof(struct control_value)); + + if (!cntl_value) + return ECGCONTROLLERCREATEFAILED; + + strncpy(cntl_value->name, name, + sizeof(cntl_value->name)); + ret = snprintf(cntl_value->value, + sizeof(cntl_value->value), "%" PRId64, value); + + if (ret >= sizeof(cntl_value->value)) + return ECGINVAL; + + controller->values[controller->index] = cntl_value; + controller->index++; + + return 0; + +} + +int cgroup_add_value_uint64(struct cgroup_controller *controller, + const char *name, u_int64_t value) +{ + int i; + unsigned ret; + struct control_value *cntl_value; + + if (!controller) + return ECGINVAL; + + if (controller->index >= CG_VALUE_MAX) + return ECGMAXVALUESEXCEEDED; + + for (i = 0; i < controller->index && i < CG_VALUE_MAX; i++) { + if (!strcmp(controller->values[i]->name, name)) + return ECGVALUEEXISTS; + } + + cntl_value = calloc(1, sizeof(struct control_value)); + + if (!cntl_value) + return ECGCONTROLLERCREATEFAILED; + + strncpy(cntl_value->name, name, sizeof(cntl_value->name)); + ret = snprintf(cntl_value->value, sizeof(cntl_value->value), + "%" PRIu64, value); + + if (ret >= sizeof(cntl_value->value)) + return ECGINVAL; + + controller->values[controller->index] = cntl_value; + controller->index++; + + return 0; + +} + +int cgroup_add_value_bool(struct cgroup_controller *controller, + const char *name, bool value) +{ + int i; + unsigned ret; + struct control_value *cntl_value; + + if (!controller) + return ECGINVAL; + + if (controller->index >= CG_VALUE_MAX) + return ECGMAXVALUESEXCEEDED; + + for (i = 0; i < controller->index && i < CG_VALUE_MAX; i++) { + if (!strcmp(controller->values[i]->name, name)) + return ECGVALUEEXISTS; + } + + cntl_value = calloc(1, sizeof(struct control_value)); + + if (!cntl_value) + return ECGCONTROLLERCREATEFAILED; + + strncpy(cntl_value->name, name, sizeof(cntl_value->name)); + + if (value) + ret = snprintf(cntl_value->value, sizeof(cntl_value->value), "1"); + else + ret = snprintf(cntl_value->value, sizeof(cntl_value->value), "0"); + + if (ret >= sizeof(cntl_value->value)) + return ECGINVAL; + + controller->values[controller->index] = cntl_value; + controller->index++; + + return 0; +} + +int cgroup_compare_controllers(struct cgroup_controller *cgca, + struct cgroup_controller *cgcb) +{ + int i; + + if (!cgca || !cgcb) + return ECGINVAL; + + if (strcmp(cgca->name, cgcb->name)) + return ECGCONTROLLERNOTEQUAL; + + if (cgca->index != cgcb->index) + return ECGCONTROLLERNOTEQUAL; + + for (i = 0; i < cgca->index; i++) { + struct control_value *cva = cgca->values[i]; + struct control_value *cvb = cgcb->values[i]; + + if (strcmp(cva->name, cvb->name)) + return ECGCONTROLLERNOTEQUAL; + + if (strcmp(cva->value, cvb->value)) + return ECGCONTROLLERNOTEQUAL; + } + return 0; +} + +int cgroup_compare_cgroup(struct cgroup *cgroup_a, struct cgroup *cgroup_b) +{ + int i; + + if (!cgroup_a || !cgroup_b) + return ECGINVAL; + + if (strcmp(cgroup_a->name, cgroup_b->name)) + return ECGROUPNOTEQUAL; + + if (cgroup_a->tasks_uid != cgroup_b->tasks_uid) + return ECGROUPNOTEQUAL; + + if (cgroup_a->tasks_gid != cgroup_b->tasks_gid) + return ECGROUPNOTEQUAL; + + if (cgroup_a->control_uid != cgroup_b->control_uid) + return ECGROUPNOTEQUAL; + + if (cgroup_a->control_gid != cgroup_b->control_gid) + return ECGROUPNOTEQUAL; + + if (cgroup_a->index != cgroup_b->index) + return ECGROUPNOTEQUAL; + + for (i = 0; i < cgroup_a->index; i++) { + struct cgroup_controller *cgca = cgroup_a->controller[i]; + struct cgroup_controller *cgcb = cgroup_b->controller[i]; + + if (cgroup_compare_controllers(cgca, cgcb)) + return ECGCONTROLLERNOTEQUAL; + } + return 0; +} + +int cgroup_set_uid_gid(struct cgroup *cgroup, uid_t tasks_uid, gid_t tasks_gid, + uid_t control_uid, gid_t control_gid) +{ + if (!cgroup) + return ECGINVAL; + + cgroup->tasks_uid = tasks_uid; + cgroup->tasks_gid = tasks_gid; + cgroup->control_uid = control_uid; + cgroup->control_gid = control_gid; + + return 0; +} + +int cgroup_get_uid_gid(struct cgroup *cgroup, uid_t *tasks_uid, + gid_t *tasks_gid, uid_t *control_uid, gid_t *control_gid) +{ + if (!cgroup) + return ECGINVAL; + + *tasks_uid = cgroup->tasks_uid; + *tasks_gid = cgroup->tasks_gid; + *control_uid = cgroup->control_uid; + *control_gid = cgroup->control_uid; + + return 0; +} + +struct cgroup_controller *cgroup_get_controller(struct cgroup *cgroup, + const char *name) +{ + int i; + struct cgroup_controller *cgc; + + if (!cgroup) + return NULL; + + for (i = 0; i < cgroup->index; i++) { + cgc = cgroup->controller[i]; + + if (!strcmp(cgc->name, name)) + return cgc; + } + + return NULL; +} + +int cgroup_get_value_string(struct cgroup_controller *controller, + const char *name, char **value) +{ + int i; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + + if (!strcmp(val->name, name)) { + *value = strdup(val->value); + + if (!*value) + return ECGOTHER; + + return 0; + } + } + + return ECGROUPVALUENOTEXIST; + +} + +int cgroup_set_value_string(struct cgroup_controller *controller, + const char *name, const char *value) +{ + int i; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + if (!strcmp(val->name, name)) { + strncpy(val->value, value, CG_VALUE_MAX); + return 0; + } + } + + return cgroup_add_value_string(controller, name, value); +} + +int cgroup_get_value_int64(struct cgroup_controller *controller, + const char *name, int64_t *value) +{ + int i; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + + if (!strcmp(val->name, name)) { + + if (sscanf(val->value, "%" SCNd64, value) != 1) + return ECGINVAL; + + return 0; + } + } + + return ECGROUPVALUENOTEXIST; +} + +int cgroup_set_value_int64(struct cgroup_controller *controller, + const char *name, int64_t value) +{ + int i; + unsigned ret; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + + if (!strcmp(val->name, name)) { + ret = snprintf(val->value, + sizeof(val->value), "%" PRId64, value); + + if (ret >= sizeof(val->value) || ret < 0) + return ECGINVAL; + + return 0; + } + } + + return cgroup_add_value_int64(controller, name, value); +} + +int cgroup_get_value_uint64(struct cgroup_controller *controller, + const char *name, u_int64_t *value) +{ + int i; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + if (!strcmp(val->name, name)) { + + if (sscanf(val->value, "%" SCNu64, value) != 1) + return ECGINVAL; + + return 0; + } + } + + return ECGROUPVALUENOTEXIST; +} + +int cgroup_set_value_uint64(struct cgroup_controller *controller, + const char *name, u_int64_t value) +{ + int i; + unsigned ret; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + + if (!strcmp(val->name, name)) { + ret = snprintf(val->value, + sizeof(val->value), "%" PRIu64, value); + + if (ret >= sizeof(val->value) || ret < 0) + return ECGINVAL; + + return 0; + } + } + + return cgroup_add_value_uint64(controller, name, value); +} + +int cgroup_get_value_bool(struct cgroup_controller *controller, + const char *name, bool *value) +{ + int i; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + + if (!strcmp(val->name, name)) { + int cgc_val; + + if (sscanf(val->value, "%d", &cgc_val) != 1) + return ECGINVAL; + + if (cgc_val) + *value = true; + else + *value = false; + + return 0; + } + } + return ECGROUPVALUENOTEXIST; +} + +int cgroup_set_value_bool(struct cgroup_controller *controller, + const char *name, bool value) +{ + int i; + unsigned ret; + + if (!controller) + return ECGINVAL; + + for (i = 0; i < controller->index; i++) { + struct control_value *val = controller->values[i]; + + if (!strcmp(val->name, name)) { + if (value) { + ret = snprintf(val->value, + sizeof(val->value), "1"); + } else { + ret = snprintf(val->value, + sizeof(val->value), "0"); + } + + if (ret >= sizeof(val->value) || ret < 0) + return ECGINVAL; + + return 0; + + } + } + + return cgroup_add_value_bool(controller, name, value); +} -- cgit From 07ff50e0856fecd937fb6a5ce87871f19f20f004 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Fri, 13 Mar 2009 15:16:20 +0100 Subject: Add automake makefiles Add automake makefiles and prepare everything to run, but don't actually run the automake - it would generate lot of noise, where my manual changes would get lots. Signed-off-by: Jan Safranek --- src/Makefile.am | 11 +++++++++++ src/daemon/Makefile.am | 6 ++++++ src/pam/Makefile.am | 6 ++++++ src/tools/Makefile.am | 11 +++++++++++ 4 files changed, 34 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/daemon/Makefile.am create mode 100644 src/pam/Makefile.am create mode 100644 src/tools/Makefile.am (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..c30393a --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS = . daemon pam tools + +# generate parse.h from parse.y +AM_YFLAGS = -d + +INCLUDES = -I$(top_srcdir)/include +lib_LTLIBRARIES = libcgroup.la +libcgroup_la_SOURCES = parse.y lex.l api.c config.c libcgroup-internal.h libcgroup.map wrapper.c +libcgroup_la_LIBADD = -lpthread +libcgroup_la_LDFLAGS = -Wl,--version-script,libcgroup.map -version-number $(LIBRARY_VERSION) + diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am new file mode 100644 index 0000000..17d31d9 --- /dev/null +++ b/src/daemon/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I $(top_srcdir)/include +sbin_PROGRAMS = cgrulesengd + +cgrulesengd_SOURCES = cgrulesengd.c cgrulesengd.h +cgrulesengd_LDADD = $(top_srcdir)/src/.libs/libcgroup.la +cgrulesengd_LDFLAGS = -L$(top_srcdir)/src/.libs diff --git a/src/pam/Makefile.am b/src/pam/Makefile.am new file mode 100644 index 0000000..1a1e782 --- /dev/null +++ b/src/pam/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I $(top_srcdir)/include +lib_LTLIBRARIES = pam_cgroup.la + +pam_cgroup_la_SOURCES = pam_cgroup.c +pam_cgroup_la_LDFLAGS = -module +pam_cgroup_la_LIBADD = $(top_srcdir)/src/.libs/libcgroup.la -lpam diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am new file mode 100644 index 0000000..b237744 --- /dev/null +++ b/src/tools/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/include +LDADD = $(top_srcdir)/src/.libs/libcgroup.la + +bin_PROGRAMS = cgexec cgclassify cgconfigparser + +cgexec_SOURCES = cgexec.c tools-common.c tools-common.h + +cgclassify_SOURCES = cgclassify.c tools-common.c tools-common.h + +cgconfigparser_SOURCES = cgconfig.c + -- cgit From b9d14a8371e558e2609155aafb9f92808965b1e7 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 26 Mar 2009 15:04:41 +0100 Subject: Fix compilation of some stuff yacc does not generate y.tab.h, it has different name now. Signed-off-by: Jan Safranek --- src/lex.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lex.l b/src/lex.l index 48a53b4..817b6b6 100644 --- a/src/lex.l +++ b/src/lex.l @@ -13,7 +13,7 @@ %{ #include -#include "y.tab.h" +#include "parse.h" int line_no = 1; %} -- cgit From de11c7f7bb8a79c86e680d169ed414471e5ef7d2 Mon Sep 17 00:00:00 2001 From: Jan Safranek Date: Thu, 26 Mar 2009 15:04:43 +0100 Subject: Generate everything Finally run autoreconf -f -i to generate all the stuff that should be generated Signed-off-by: Jan Safranek --- src/Makefile.in | 616 +++++++++++++++++++++++++++++++++++++++++++++++++ src/daemon/Makefile.in | 468 +++++++++++++++++++++++++++++++++++++ src/pam/Makefile.in | 472 +++++++++++++++++++++++++++++++++++++ src/tools/Makefile.in | 486 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 2042 insertions(+) create mode 100644 src/Makefile.in create mode 100644 src/daemon/Makefile.in create mode 100644 src/pam/Makefile.in create mode 100644 src/tools/Makefile.in (limited to 'src') diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..01fa4c6 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,616 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in lex.c \ + parse.c parse.h +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +libcgroup_la_DEPENDENCIES = +am_libcgroup_la_OBJECTS = parse.lo lex.lo api.lo config.lo wrapper.lo +libcgroup_la_OBJECTS = $(am_libcgroup_la_OBJECTS) +libcgroup_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libcgroup_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS) +YLWRAP = $(top_srcdir)/ylwrap +YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS) +SOURCES = $(libcgroup_la_SOURCES) +DIST_SOURCES = $(libcgroup_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-dvi-recursive install-exec-recursive \ + install-html-recursive install-info-recursive \ + install-pdf-recursive install-ps-recursive install-recursive \ + installcheck-recursive installdirs-recursive pdf-recursive \ + ps-recursive uninstall-recursive +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBRARY_VERSION = @LIBRARY_VERSION@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . daemon pam tools + +# generate parse.h from parse.y +AM_YFLAGS = -d +INCLUDES = -I$(top_srcdir)/include +lib_LTLIBRARIES = libcgroup.la +libcgroup_la_SOURCES = parse.y lex.l api.c config.c libcgroup-internal.h libcgroup.map wrapper.c +libcgroup_la_LIBADD = -lpthread +libcgroup_la_LDFLAGS = -Wl,--version-script,libcgroup.map -version-number $(LIBRARY_VERSION) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .l .lo .o .obj .y +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +parse.h: parse.c + @if test ! -f $@; then \ + rm -f parse.c; \ + $(MAKE) $(AM_MAKEFLAGS) parse.c; \ + else :; fi +libcgroup.la: $(libcgroup_la_OBJECTS) $(libcgroup_la_DEPENDENCIES) + $(libcgroup_la_LINK) -rpath $(libdir) $(libcgroup_la_OBJECTS) $(libcgroup_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/api.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wrapper.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +.l.c: + $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +.y.c: + $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +$(RECURSIVE_CLEAN_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f lex.c + -rm -f parse.c + -rm -f parse.h +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-info: install-info-recursive + +install-man: + +install-pdf: install-pdf-recursive + +install-ps: install-ps-recursive + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \ + install-strip + +.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ + all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags ctags-recursive \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \ + uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/daemon/Makefile.in b/src/daemon/Makefile.in new file mode 100644 index 0000000..279d7a4 --- /dev/null +++ b/src/daemon/Makefile.in @@ -0,0 +1,468 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +sbin_PROGRAMS = cgrulesengd$(EXEEXT) +subdir = src/daemon +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" +sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(sbin_PROGRAMS) +am_cgrulesengd_OBJECTS = cgrulesengd.$(OBJEXT) +cgrulesengd_OBJECTS = $(am_cgrulesengd_OBJECTS) +cgrulesengd_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la +cgrulesengd_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(cgrulesengd_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(cgrulesengd_SOURCES) +DIST_SOURCES = $(cgrulesengd_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBRARY_VERSION = @LIBRARY_VERSION@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I $(top_srcdir)/include +cgrulesengd_SOURCES = cgrulesengd.c cgrulesengd.h +cgrulesengd_LDADD = $(top_srcdir)/src/.libs/libcgroup.la +cgrulesengd_LDFLAGS = -L$(top_srcdir)/src/.libs +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/daemon/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/daemon/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \ + rm -f "$(DESTDIR)$(sbindir)/$$f"; \ + done + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +cgrulesengd$(EXEEXT): $(cgrulesengd_OBJECTS) $(cgrulesengd_DEPENDENCIES) + @rm -f cgrulesengd$(EXEEXT) + $(cgrulesengd_LINK) $(cgrulesengd_OBJECTS) $(cgrulesengd_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgrulesengd.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-sbinPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-sbinPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-sbinPROGRAMS ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-sbinPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/pam/Makefile.in b/src/pam/Makefile.in new file mode 100644 index 0000000..205e1bc --- /dev/null +++ b/src/pam/Makefile.in @@ -0,0 +1,472 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/pam +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(libdir)" +libLTLIBRARIES_INSTALL = $(INSTALL) +LTLIBRARIES = $(lib_LTLIBRARIES) +pam_cgroup_la_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la +am_pam_cgroup_la_OBJECTS = pam_cgroup.lo +pam_cgroup_la_OBJECTS = $(am_pam_cgroup_la_OBJECTS) +pam_cgroup_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(pam_cgroup_la_LDFLAGS) $(LDFLAGS) -o $@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(pam_cgroup_la_SOURCES) +DIST_SOURCES = $(pam_cgroup_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBRARY_VERSION = @LIBRARY_VERSION@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I $(top_srcdir)/include +lib_LTLIBRARIES = pam_cgroup.la +pam_cgroup_la_SOURCES = pam_cgroup.c +pam_cgroup_la_LDFLAGS = -module +pam_cgroup_la_LIBADD = $(top_srcdir)/src/.libs/libcgroup.la -lpam +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/pam/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/pam/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p=$(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +pam_cgroup.la: $(pam_cgroup_la_OBJECTS) $(pam_cgroup_la_DEPENDENCIES) + $(pam_cgroup_la_LINK) -rpath $(libdir) $(pam_cgroup_la_OBJECTS) $(pam_cgroup_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pam_cgroup.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-libLTLIBRARIES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/tools/Makefile.in b/src/tools/Makefile.in new file mode 100644 index 0000000..e850034 --- /dev/null +++ b/src/tools/Makefile.in @@ -0,0 +1,486 @@ +# Makefile.in generated by automake 1.10.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = cgexec$(EXEEXT) cgclassify$(EXEEXT) \ + cgconfigparser$(EXEEXT) +subdir = src/tools +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_cgclassify_OBJECTS = cgclassify.$(OBJEXT) tools-common.$(OBJEXT) +cgclassify_OBJECTS = $(am_cgclassify_OBJECTS) +cgclassify_LDADD = $(LDADD) +cgclassify_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la +am_cgconfigparser_OBJECTS = cgconfig.$(OBJEXT) +cgconfigparser_OBJECTS = $(am_cgconfigparser_OBJECTS) +cgconfigparser_LDADD = $(LDADD) +cgconfigparser_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la +am_cgexec_OBJECTS = cgexec.$(OBJEXT) tools-common.$(OBJEXT) +cgexec_OBJECTS = $(am_cgexec_OBJECTS) +cgexec_LDADD = $(LDADD) +cgexec_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(cgclassify_SOURCES) $(cgconfigparser_SOURCES) \ + $(cgexec_SOURCES) +DIST_SOURCES = $(cgclassify_SOURCES) $(cgconfigparser_SOURCES) \ + $(cgexec_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBRARY_VERSION = @LIBRARY_VERSION@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/include +LDADD = $(top_srcdir)/src/.libs/libcgroup.la +cgexec_SOURCES = cgexec.c tools-common.c tools-common.h +cgclassify_SOURCES = cgclassify.c tools-common.c tools-common.h +cgconfigparser_SOURCES = cgconfig.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/tools/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/tools/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + || test -f $$p1 \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f $$p $$f"; \ + rm -f $$p $$f ; \ + done +cgclassify$(EXEEXT): $(cgclassify_OBJECTS) $(cgclassify_DEPENDENCIES) + @rm -f cgclassify$(EXEEXT) + $(LINK) $(cgclassify_OBJECTS) $(cgclassify_LDADD) $(LIBS) +cgconfigparser$(EXEEXT): $(cgconfigparser_OBJECTS) $(cgconfigparser_DEPENDENCIES) + @rm -f cgconfigparser$(EXEEXT) + $(LINK) $(cgconfigparser_OBJECTS) $(cgconfigparser_LDADD) $(LIBS) +cgexec$(EXEEXT): $(cgexec_OBJECTS) $(cgexec_DEPENDENCIES) + @rm -f cgexec$(EXEEXT) + $(LINK) $(cgexec_OBJECTS) $(cgexec_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgclassify.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgconfig.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgexec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tools-common.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonemtpy = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-info: install-info-am + +install-man: + +install-pdf: install-pdf-am + +install-ps: install-ps-am + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic clean-libtool ctags distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: -- cgit