* Ivana Varekova <[email protected]> [2010-07-30 13:54:02]:
> SYNOPSIS:
> cgsnapshot [-h] [-n] [controller] [...]
> Generate the configuration file from the current hierarchie(s) of control
> groups
> -h, --help Display this help
> -n, --no-errors Omit the output
> the output have the same format as cgconfig.conf file (the values are in
> commas)
>
Thanks for working on this, its been on my TODO list.
> EXAMPLE:
> $ /home/varekova/bg/libcgroup/20100719/libcg/src/tools/cgsnapshot -n
> # Configuration file generated by cgsnapshot
> mount {
> cpuset = /cgroup/cpuset;
> cpu = /cgroup/devices;
> cpuacct = /cgroup/memoryd;
> memory = /cgroup/memory;
> devices = /cgroup/devices;
> freezer = /cgroup/freezer;
> net_cls = /cgroup/net_cls;
> }
>
> group daemons {
> perm {
> admin {
> uid = root;
> gid = root;
> }
> task {
> uid = root;
> gid = root;
> }
> }
> cpuset {
> cpuset.memory_spread_slab="0";
> cpuset.memory_spread_page="0";
> cpuset.memory_migrate="0";
> cpuset.sched_relax_domain_level="-1";
> cpuset.sched_load_balance="1";
> cpuset.mem_hardwall="0";
> cpuset.mem_exclusive="0";
> cpuset.cpu_exclusive="0";
> }
> }
>
>
>
> Signed-off-by: Ivana Hutarova Varekova <[email protected]>
> ---
>
> src/tools/Makefile.am | 4
> src/tools/cgsnapshot.c | 519
> ++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 522 insertions(+), 1 deletions(-)
> create mode 100644 src/tools/cgsnapshot.c
>
> diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am
> index f755037..bebdc81 100644
> --- a/src/tools/Makefile.am
> +++ b/src/tools/Makefile.am
> @@ -3,7 +3,7 @@ LDADD = $(top_builddir)/src/.libs/libcgroup.la
>
> if WITH_TOOLS
>
> -bin_PROGRAMS = cgexec cgclassify cgcreate cgset cgget cgdelete lssubsys
> lscgroup
> +bin_PROGRAMS = cgexec cgclassify cgcreate cgset cgget cgdelete lssubsys
> lscgroup cgsnapshot
>
> sbin_PROGRAMS = cgconfigparser cgclear
>
> @@ -27,6 +27,8 @@ lssubsys_SOURCES = lssubsys.c
>
> lscgroup_SOURCES = tools-common.c lscgroup.c
>
> +cgsnapshot_SOURCES = cgsnapshot.c
> +
> install-exec-hook:
> chmod u+s $(DESTDIR)$(bindir)/cgexec
>
> diff --git a/src/tools/cgsnapshot.c b/src/tools/cgsnapshot.c
> new file mode 100644
> index 0000000..6d27c31
> --- /dev/null
> +++ b/src/tools/cgsnapshot.c
> @@ -0,0 +1,519 @@
> +/* " Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
> + * " Written by Ivana Hutarova Varekova <[email protected]>
> + *
> + * 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 <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <getopt.h>
> +
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <libcgroup.h>
> +#include <libcgroup-internal.h>
> +#include <pwd.h>
> +#include <grp.h>
> +
> +enum flag{
> + FL_LIST = 1,
> + FL_NOERR = 2, /* don't show the error messages */
> +};
> +
> +typedef char cont_name_t[FILENAME_MAX];
> +
> +/*
> + * display the usage
> + */
> +static void usage(int status, const char *program_name)
> +{
> + if (status != 0) {
> + fprintf(stderr, "Wrong input parameters,"
> + " try %s -h' for more information.\n",
> + program_name);
> + } else {
> + fprintf(stdout, "Usage: %s [-h] [-n] [controller] [...]\n",
> + program_name);
> + fprintf(stdout, "Generate the configuration file from "\
> + "the given controllers of control groups\n");
> + fprintf(stdout, " -h, --help Display this help\n");
> + fprintf(stdout, " -n, --no-errors Omit the error "\
> + "output\n");
> + }
> +}
> +
> +/* display the variable name and its value */
> +static int display_variable_data(struct cgroup_file_info info,
> + int prefix_len, char cont_old[FILENAME_MAX],
> + const char *program_name)
> +{
> + char *end;
> + char path[FILENAME_MAX];
> + char cont[FILENAME_MAX];
> +
> + struct cgroup *group = NULL;
> + struct cgroup_controller *group_controller = NULL;
> +
> + char *value = NULL;
> +
> + int ret;
> + char rel_path[FILENAME_MAX];
> + struct stat sb;
> +
> + /* get the controller name
> + - it is the part before "." sign in variable name */
> + strncpy(cont, info.path, FILENAME_MAX);
> + cont[FILENAME_MAX-1] = '\0';
> + end = strchr(cont, '.');
> + end[0] = '\0';
> +
> + /* get the relative path to the control group
> + - remove the mount poin prefix */
> + strncpy(path, info.full_path+prefix_len, FILENAME_MAX);
> + path[FILENAME_MAX-1] = '\0';
This is turning out to be pain, I'll write a wrapper to encapsulate
some of the strncpy parts.
> +
> + /* get the relative path to the control group
> + - remove the file name suffix*/
> + end = strrchr(path, '/');
> + if (end != NULL)
> + end[0] = '\0';
> +
> + /* open the control group for the relevant path */
> + group = cgroup_new_cgroup(path);
> + if (group == NULL) {
> + fprintf(stderr, "%s: cannot create group '%s'\n", program_name,
> + path);
> + return -1;
> + }
> +
> + /* get the data for this control group */
> + ret = cgroup_get_cgroup(group);
> + if (ret != 0) {
> + fprintf(stderr, "%s: cannot read group '%s': %s\n",
> + program_name, path, cgroup_strerror(ret));
> + goto err;
> + }
> +
> + /* find the relevant controller */
> + group_controller = cgroup_get_controller(group, cont);
> + if (group_controller == NULL) {
> + fprintf(stderr, "%s: cannot find controller " \
> + "'%s' in group '%s'\n", program_name,
> + cont, path);
> + ret = -1;
> + goto err;
> + }
> +
> + ret = cgroup_get_value_string(group_controller, info.path, &value);
> +
> + /* variable can not be read */
> + if (ret != 0) {
> + ret = 0;
> + goto err;
> + }
> +
> + if (strcmp(cont_old, cont)) {
> + if (cont_old[0] != '\0')
> + printf("\t}\n");
> + printf("\t%s { \n", cont);
> + strcpy(cont_old, cont);
> + }
> +
> + /* if the variable is nonempty */
> + if (strlen(value) != 0) {
> + /* test whether the variable file is writable */
> + strncpy(rel_path, info.full_path, FILENAME_MAX);
> + rel_path[prefix_len] = '\0';
> + strncat(rel_path, info.path, FILENAME_MAX);
> + ret = stat(rel_path, &sb);
> +
> + /*
> + * freezer.state is not in root group so ret != 0,
> + * but it should be listed
> + */
> + if ((ret == 0) && ((sb.st_mode & S_IWUSR) == 0)) {
> + /* variable is not writable */
> + /*
> + * TODO - device variables
> + * should be handle in some different way
> + */
Is this because they have special characters as a part of the values?
> + goto err_val;
> + }
> +
> + printf("\t\t%s=\"%s\";\n", info.path, value);
> + }
> +
> + strcpy(cont_old, cont);
> +
> +err_val:
> + if (value)
> + free(value);
> +err:
> + cgroup_free(&group);
> +
> + return ret;
> +}
> +
> +static int display_controller_data(char controller[CG_CONTROLLER_MAX],
> + const char *program_name)
Would appreciate more comments on the function and parameters, helps
code readability.
> +{
> + int ret;
> + void *handle;
> +
> + struct cgroup_file_info info;
> + int lvl;
> +
> + char cont_old[FILENAME_MAX];
> + int start = 0;
> + int triv = 1;
> +
> + int prefix_len;
> + char rel_path[FILENAME_MAX];
> + char *end;
> + struct stat sb;
> + struct passwd *pw;
> + struct group *gr;
> +
> + /* start to parse the structure for the first controller -
> + controller[0] attached to hierarchie */
^^^ typo
> + ret = cgroup_walk_tree_begin(controller, "/", 0,
> + &handle, &info, &lvl);
> + if (ret != 0)
> + return ret;
> +
> + prefix_len = strlen(info.full_path);
> + cont_old[0] = '\0';
> + /* go through all files and directories */
> + while ((ret = cgroup_walk_tree_next(0, &handle, &info, lvl)) == 0) {
> + /* subgroup of / starts here */
> + if (info.type != CGROUP_FILE_TYPE_FILE) {
> + /* group output was finished */
> + start = 0;
> + }
> + if ((info.depth > 1) && (info.type == CGROUP_FILE_TYPE_FILE)
> + && (start == 0)) {
> + /* close the previous group here, if there was some */
> + if (triv == 1)
> + triv = 0;
Usage of start and triv is confusing, could you please add some more
comments on what they do.
> + else
> + printf("\t}\n}\n");
> + start = 1;
> +
> + strncpy(rel_path, &info.full_path[prefix_len],
> + FILENAME_MAX);
> + rel_path[FILENAME_MAX-1] = '\0';
> +
> + end = strrchr(rel_path, '/');
> + if (end != NULL)
> + end[0] = '\0';
> +
> + printf("group %s {\n", rel_path);
> + cont_old[0] = '\0';
> +
> + /* set the permission tags */
> + printf("\tperm {\n");
> +
> + /* admin permissions */
> + strncpy(rel_path, info.full_path, FILENAME_MAX-1);
> + rel_path[FILENAME_MAX-1] = '\0';
> + end = strrchr(rel_path, '/');
> + if (end != NULL)
> + end[0] = '\0';
> +
> + /* get the directory statistic */
> + ret = stat(rel_path, &sb);
> + if (ret)
> + goto err;
> +
> + /*
> + * from the statistic find out the
> + * user and group name
> + */
> + pw = getpwuid(sb.st_uid);
> + if (pw == NULL) {
> + ret = -1;
> + goto err;
> + }
> + gr = getgrgid(sb.st_gid);
> +
> + if (gr == NULL) {
> + ret = -1;
> + goto err;
> + }
> +
> + printf("\t\tadmin {\n"\
> + "\t\t\tuid = %s;\n"\
> + "\t\t\tgid = %s;\n"\
> + "\t\t}\n", pw->pw_name, gr->gr_name);
> +
> + /* tasks permissions */
> + strncat(rel_path, "/tasks", NAME_MAX);
> + /* get tasks file statistic */
> + ret = stat(rel_path, &sb);
> + if (ret)
> + goto err;
> +
> + /*
> + * from the statistic find out the
> + * user and group name
> + */
> + pw = getpwuid(sb.st_uid);
> + if (pw == NULL) {
> + ret = -1;
> + goto err;
> + }
> +
> + gr = getgrgid(sb.st_gid);
> + if (gr == NULL) {
> + ret = -1;
> + goto err;
> + }
> +
> + printf("\t\ttask {\n" \
> + "\t\t\tuid = %s;\n"\
> + "\t\t\tgid = %s;\n"\
> + "\t\t}\n", pw->pw_name, gr->gr_name);
> + printf("\t}\n");
> + }
> + /* no output for nonvariable files */
> + if ((info.type == CGROUP_FILE_TYPE_FILE) &&
> + /* files for non-root control group */
> + (info.depth > 1) &&
> + /* not variable-name file */
> + ((strchr(info.path, '.')) != NULL) &&
> + /* not variable-name file */
> + (strncmp(info.path, "cgroup.", 7) != 0) &&
> + /* files which reports data */
> + (strcmp(info.path, "memory.stat") != 0) &&
> + (strcmp(info.path, "cpuacct.stat") != 0) &&
> + /*
> + * TODO this file have to be handled
> + * in some special way
> + */
OK, .stat is read-only anway, I think the S_IWUSR check will filter it
out anyway.
> + (strcmp(info.path, "cpuacct.usage") != 0)) {
> + ret = display_variable_data(info,
> + prefix_len, cont_old, program_name);
> + }
> +
> + }
> +
> +
> +err:
> + if (cont_old[0] != 0)
> + printf("\t}\n");
> +
> + if (triv == 0)
> + printf("}\n");
> +
cont_old, triv are confusing, could you please document the order of
parsing and which variables are used for detecting termination,
parsing, etc.
> + cgroup_walk_tree_end(&handle);
> + if (ret == ECGEOF)
> + ret = 0;
> +
> + return ret;
> +
> +}
> +
> +static int is_ctlr_on_list(char controllers[CG_CONTROLLER_MAX][FILENAME_MAX],
> + cont_name_t wanted_conts[FILENAME_MAX])
> +{
> + int i = 0;
> + int j = 0;
> +
> + while (controllers[i][0] != '\0') {
> + while (wanted_conts[j][0] != '\0') {
> + if (strcmp(controllers[i], wanted_conts[j]) == 0)
> + return 1;
> + j++;
> + }
> + j = 0;
> + i++;
> + }
> +
> + return 0;
> +}
> +
> +
> +/* print data about input cont_name controller */
> +static int parse_controllers(cont_name_t cont_names[CG_CONTROLLER_MAX],
> + int flags, const char *program_name)
> +{
> + int ret = 0;
> + void *handle;
> + char path[FILENAME_MAX];
> + struct cgroup_mount_point controller;
> +
> + char controllers[CG_CONTROLLER_MAX][FILENAME_MAX];
> + int max = 0;
> +
> + path[0] = '\0';
> +
> + ret = cgroup_get_controller_begin(&handle, &controller);
> +
> + /* go through the list of controllers/mount point pairs */
> + while (ret == 0) {
> + if (strcmp(path, controller.path) == 0) {
> + /* if it is still the same mount point */
> + if (max < CG_CONTROLLER_MAX) {
> + strncpy(controllers[max],
> + controller.name, FILENAME_MAX);
> + (controllers[max])[FILENAME_MAX-1] = '\0';
> + max++;
> + }
> + } else {
> +
> + /* we got new mount point, print it if needed */
> + if ((!(flags & FL_LIST) ||
> + (is_ctlr_on_list(controllers, cont_names)))
> + && (max != 0)) {
> + (controllers[max])[0] = '\0';
> + ret = display_controller_data(
> + controllers[0], program_name);
> + }
> +
> + strncpy(controllers[0], controller.name, FILENAME_MAX);
> + (controllers[0])[FILENAME_MAX-1] = '\0';
> +
> + strncpy(path, controller.path, FILENAME_MAX);
> + path[FILENAME_MAX-1] = '\0';
> + max = 1;
> + }
> +
> + /* the actual controller should not be printed */
> + ret = cgroup_get_controller_next(&handle, &controller);
> + }
> +
> + if (max != 0)
> + ret = display_controller_data(
> + controllers[0], program_name);
> +
> + cgroup_get_controller_end(&handle);
> + if (ret != ECGEOF)
> + return ret;
> + return 0;
> +}
> +
> +/* print data about input mount points */
> +/* TODO only wanted ones */
> +static int parse_mountpoints(cont_name_t cont_names[CG_CONTROLLER_MAX],
> + int flags, const char *program_name)
> +{
> + int ret;
> + void *handle;
> + struct controller_data info;
> + char *mount_point;
> +
> + /* start mount section */
> + printf("mount {\n");
> +
> + /* go through the controller list */
> + ret = cgroup_get_all_controller_begin(&handle, &info);
> + while (ret != ECGEOF) {
> +
> + /* the controller attached to some hierarchy */
> + if (info.hierarchy != 0) {
> + ret = cgroup_get_subsys_mount_point(info.name,
> + &mount_point);
> + if (ret != 0) {
> + /* the controller is not mounted */
> + if ((flags & FL_NOERR) == 0) {
> + fprintf(stderr,
> + "%s hierarchy not mounted\n",
> + info.name);
> + }
> + } else
> + printf("\t%s = %s;\n", info.name, mount_point);
> + }
> +
> + /* next controller */
> + ret = cgroup_get_all_controller_next(&handle, &info);
> + }
> + if (ret != ECGEOF) {
> + if ((flags & FL_NOERR) == 0) {
> + fprintf(stderr,
> + "error: in get next controller %s\n",
> + cgroup_strerror(ret));
> + return ret;
> + }
> + }
> +
> + ret = cgroup_get_all_controller_end(&handle);
> +
> + /* finish mount section*/
> + printf("}\n\n");
> + return ret;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + int ret = 0;
> + int c;
> +
> + int flags = 0;
> +
> + int i;
> + int c_number = 0;
> + cont_name_t wanted_cont[CG_CONTROLLER_MAX];
> +
> + static struct option options[] = {
> + {"help", 0, 0, 'h'},
> + {"no-error" , 0, 0, 'n'},
> + {0, 0, 0, 0}
> + };
> +
> + for (i = 0; i < CG_CONTROLLER_MAX; i++)
> + wanted_cont[i][0] = '\0';
> +
> + /* parse arguments */
> + while ((c = getopt_long(argc, argv, "nh", options, NULL)) > 0) {
> + switch (c) {
> + case 'h':
> + usage(0, argv[0]);
> + return 0;
> + case 'n':
> + flags |= FL_NOERR;
> + break;
> + default:
> + usage(1, argv[0]);
> + return -1;
> + }
> + }
> +
> + /* read the list of controllers */
> + while (optind < argc) {
> + flags |= FL_LIST;
> + strncpy(wanted_cont[c_number], argv[optind], FILENAME_MAX);
> + (wanted_cont[c_number])[FILENAME_MAX-1] = '\0';
> + c_number++;
> + optind++;
> + if (optind == CG_CONTROLLER_MAX-1) {
> + fprintf(stderr, "too many parameters\n");
> + break;
> + }
> + }
> +
> + /* print the header */
> + printf("# Configuration file generated by cgsnapshot\n");
> +
> + /* initialize libcgroup */
> + ret = cgroup_init();
> +
> + if (ret) {
> + /* empty configuration file */
> + return ret;
> + }
> +
> + /* print mount points section */
> + ret = parse_mountpoints(wanted_cont, flags, argv[0]);
> +
> + /* print hierarchies section */
> + ret = parse_controllers(wanted_cont, flags, argv[0]);
> + return ret;
> +}
>
--
Three Cheers,
Balbir
------------------------------------------------------------------------------
The Palm PDK Hot Apps Program offers developers who use the
Plug-In Development Kit to bring their C/C++ apps to Palm for a share
of $1 Million in cash or HP Products. Visit us here for more details:
http://p.sf.net/sfu/dev2dev-palm
_______________________________________________
Libcg-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libcg-devel