]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
cgget: Major refactor 26/head
authorTom Hromatka <tom.hromatka@oracle.com>
Wed, 10 Feb 2021 17:25:10 +0000 (17:25 +0000)
committerTom Hromatka <tom.hromatka@oracle.com>
Mon, 1 Mar 2021 17:33:09 +0000 (10:33 -0700)
Previously cgget used data structures unlike anywhere else
within libcgroup.  cgget had arrays of strings to hold the
list of controllers, settings, and values.

Update cgget to utilize struct cgroup and struct
cgroup_controller.

This patch is intended to have no functional changes.

Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>
src/api.c
src/libcgroup-internal.h
src/libcgroup.map
src/tools/Makefile.am
src/tools/cgget.c
src/wrapper.c

index 0e9f98578693c38baed6eab0cadb5d85b7fd7f7e..40637a5305cc515f09927f4ab38883979fe5d2b2 100644 (file)
--- a/src/api.c
+++ b/src/api.c
@@ -2180,9 +2180,35 @@ static int cgroup_copy_controller_values(struct cgroup_controller *dst,
                dst_val = dst->values[i];
                strncpy(dst_val->value, src_val->value, CG_CONTROL_VALUE_MAX);
                strncpy(dst_val->name, src_val->name, FILENAME_MAX);
+
+               if (src_val->multiline_value) {
+                       dst_val->multiline_value =
+                               strdup(src_val->multiline_value);
+                       if (!dst_val->multiline_value) {
+                               last_errno = errno;
+                               ret = ECGOTHER;
+                               goto err;
+                       }
+               } else {
+                       dst_val->multiline_value = NULL;
+               }
+
                dst_val->dirty = src_val->dirty;
        }
+
+       return ret;
+
 err:
+       dst->index = 0;
+       for (i = 0; i < src->index; i++) {
+               if (dst->values[i]) {
+                       if (dst->values[i]->multiline_value)
+                               free(dst->values[i]->multiline_value);
+
+                       free(dst->values[i]);
+               }
+       }
+
        return ret;
 }
 
index c2f75f55602105510cb69b33823ff038da1323dc..0367c60a91ec829ce6d26e1a1ce0142ccd2f7991 100644 (file)
@@ -86,6 +86,9 @@ __BEGIN_DECLS
 struct control_value {
        char name[FILENAME_MAX];
        char value[CG_CONTROL_VALUE_MAX];
+
+       /* cgget uses this field for values that span multiple lines */
+       char *multiline_value;
        bool dirty;
 };
 
index 6eb22f0527010c27939546a095cc046e61212b8d..8967965665269181f613e4122d1690a0add7f03b 100644 (file)
@@ -135,4 +135,6 @@ CGROUP_0.43 {
        cg_build_path_locked;
        cgroup_fill_cgc;
        cgroup_test_subsys_mounted;
+       cg_mount_table;
+       cg_mount_table_lock;
 } CGROUP_0.42;
index 2f3534ce01252e36e7307506bca457961b31a5c8..fd792853a27e142a3f18736d2d375b2a3acf21e5 100644 (file)
@@ -1,7 +1,7 @@
 @CODE_COVERAGE_RULES@
 
 INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/include
-LDADD = $(top_builddir)/src/.libs/libcgroup.la
+LDADD = $(top_builddir)/src/.libs/libcgroup.la -lpthread
 
 if WITH_TOOLS
 
index 4764f143ee7e26f06f7ee9ee930fc0b239bbcffb..08c2286ca098018e8911cf9387c5d9f9704f1dae 100644 (file)
@@ -1,17 +1,18 @@
 #include <libcgroup.h>
 #include <libcgroup-internal.h>
 
+#include <dirent.h>
+#include <getopt.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <getopt.h>
 
 #include "tools-common.h"
 
 #define MODE_SHOW_HEADERS              1
 #define MODE_SHOW_NAMES                        2
-#define MODE_SHOW_ALL_CONTROLLERS      4
 
 #define LL_MAX                         100
 
@@ -24,13 +25,6 @@ static struct option const long_options[] =
        {NULL, 0, NULL, 0}
 };
 
-enum group{
-    GR_NOTSET = 0,     /* 0 .. path not specified -g<g>  or -a not used */
-    GR_GROUP,          /* 1 .. path not specified -g<g>  or -a used */
-    GR_LIST            /* 2 .. path specified using -g<g>:<p> */
-};
-
-
 static void usage(int status, const char *program_name)
 {
        if (status != 0) {
@@ -57,410 +51,697 @@ static void usage(int status, const char *program_name)
                "parameter names\n");
 }
 
-static int display_record(char *name,
-       struct cgroup_controller *group_controller,
-       const char *group_name, const char *program_name, int mode)
+static int get_controller_from_name(const char * const name,
+                                    char **controller)
 {
-       int ret = 0;
-       void *handle;
-       char line[LL_MAX];
-       int ind = 0;
-
-       if (mode & MODE_SHOW_NAMES)
-               printf("%s: ", name);
+       char *dot;
 
-       /* start the reading of the variable value */
-       ret = cgroup_read_value_begin(group_controller->name,
-               group_name, name, &handle, line, LL_MAX);
+       *controller = strdup(name);
+       if (*controller == NULL)
+               return ECGOTHER;
 
-       if (ret == ECGEOF) {
-               printf("\n");
-               goto read_end;
+       dot = strchr(*controller, '.');
+       if (dot == NULL) {
+               fprintf(stderr, "cgget: error parsing parameter name\n" \
+                       " '%s'", name);
+               return ECGINVAL;
        }
 
-       if (ret != 0)
-               goto end;
-
-       printf("%s", line);
-       if (line[strlen(line)-1] == '\n')
-               /* if value continue on the next row. indent it */
-               ind = 1;
+       *dot = '\0';
+       return 0;
+}
 
+static int create_cg(struct cgroup **cg_list[], int * const cg_list_len)
+{
+       *cg_list = realloc(*cg_list,
+                       ((*cg_list_len) + 1) * sizeof(struct cgroup *));
+       if ((*cg_list) == NULL)
+               return ECGCONTROLLERCREATEFAILED;
+       memset(&(*cg_list)[*cg_list_len], 0, sizeof(struct cgroup *));
+
+       (*cg_list)[*cg_list_len] = cgroup_new_cgroup("");
+       if ((*cg_list)[*cg_list_len] == NULL)
+               return ECGCONTROLLERCREATEFAILED;
+
+       (*cg_list_len)++;
+       return 0;
+}
 
-       /* read iteratively the whole value  */
-       while ((ret = cgroup_read_value_next(&handle, line, LL_MAX)) == 0) {
-               if (ind == 1)
-                       printf("\t");
-               printf("%s", line);
-               ind = 0;
+static int parse_a_flag(struct cgroup **cg_list[], int * const cg_list_len)
+{
+       struct cgroup_mount_point controller;
+       struct cgroup_controller *cgc;
+       struct cgroup *cg = NULL;
+       void *handle;
+       int ret = 0;
 
-               /* if value continue on the next row. indent it */
-               if (line[strlen(line)-1] == '\n')
-                       ind = 1;
+       if ((*cg_list_len) == 0) {
+               ret = create_cg(cg_list, cg_list_len);
+               if (ret)
+                       goto out;
        }
 
-read_end:
-       cgroup_read_value_end(&handle);
+       /* if "-r" was provided, then we know that the cgroup(s) will be an
+        * optarg at the end with no flag.  Let's temporarily populate the
+        * first cgroup with the requested control values.
+        */
+       cg = (*cg_list)[0];
+
+       ret = cgroup_get_controller_begin(&handle, &controller);
+       while (ret == 0) {
+               cgc = cgroup_get_controller(cg, controller.name);
+               if (!cgc) {
+                       cgc = cgroup_add_controller(cg, controller.name);
+                       if (!cgc) {
+                               ret = ECGCONTROLLERCREATEFAILED;
+                               goto out;
+                       }
+               }
+
+               ret = cgroup_get_controller_next(&handle, &controller);
+       }
        if (ret == ECGEOF)
+               /* we successfully reached the end of the controller list;
+                * this is not an error
+                */
                ret = 0;
 
-end:
-       if (ret != 0)
-               fprintf(stderr, "variable file read failed %s\n",
-                       cgroup_strerror(ret));
-       return ret;
-}
+       cgroup_get_controller_end(&handle);
 
+       return ret;
 
+out:
+       cgroup_get_controller_end(&handle);
+       return ret;
+}
 
-static int display_name_values(char **names, const char* group_name,
-               const char *program_name, int mode)
+static int parse_r_flag(struct cgroup **cg_list[], int * const cg_list_len,
+                       const char * const cntl_value)
 {
-       int i;
-       struct cgroup_controller *group_controller = NULL;
-       struct cgroup *group = NULL;
+       char *cntl_value_controller = NULL;
+       struct cgroup_controller *cgc;
+       struct cgroup *cg = NULL;
        int ret = 0;
-       char *controller = NULL, *dot;
 
-       group = cgroup_new_cgroup(group_name);
-       if (group == NULL) {
-               fprintf(stderr, "%s: cannot create group '%s'\n", program_name,
-                               group_name);
-               return -1;
-       }
-       ret = cgroup_get_cgroup(group);
-       if (ret != 0) {
-               fprintf(stderr, "%s: cannot read group '%s': %s\n",
-                               program_name, group_name, cgroup_strerror(ret));
-               goto err;
+       if ((*cg_list_len) == 0) {
+               ret = create_cg(cg_list, cg_list_len);
+               if (ret)
+                       goto out;
        }
 
-       i = 0;
-       while (names[i] != NULL) {
-               /* Parse the controller out of 'controller.parameter'. */
-               free(controller);
-               controller = strdup(names[i]);
-               if (controller == NULL) {
-                       fprintf(stderr, "%s: out of memory\n", program_name);
-                       ret = -1;
-                       goto err;
-               }
-               dot = strchr(controller, '.');
-               if (dot == NULL) {
-                       fprintf(stderr, "%s: error parsing parameter name\n" \
-                                       " '%s'", program_name, names[i]);
-                       ret = -1;
-                       goto err;
+       /* if "-r" was provided, then we know that the cgroup(s) will be an
+        * optarg at the end with no flag.  Let's temporarily populate the
+        * first cgroup with the requested control values.
+        */
+       cg = (*cg_list)[0];
+
+       ret = get_controller_from_name(cntl_value, &cntl_value_controller);
+       if (ret)
+               goto out;
+
+       cgc = cgroup_get_controller(cg, cntl_value_controller);
+       if (!cgc) {
+               cgc = cgroup_add_controller(cg, cntl_value_controller);
+               if (!cgc) {
+                       ret = ECGCONTROLLERCREATEFAILED;
+                       goto out;
                }
-               *dot = '\0';
-
-               /* Find appropriate controller. */
-               group_controller = cgroup_get_controller(group, controller);
-               if (group_controller == NULL) {
-                       fprintf(stderr, "%s: cannot find controller " \
-                                       "'%s' in group '%s'\n", program_name,
-                                       controller, group_name);
-                       ret = -1;
-                       goto err;
-               }
-
-               /* Finally read the parameter value.*/
-               ret = display_record(names[i], group_controller,
-                       group_name, program_name, mode);
-               if (ret != 0)
-                       goto err;
-               i++;
        }
-err:
-       if (controller)
-               free(controller);
-       if (group)
-               cgroup_free(&group);
+
+       ret = cgroup_add_value_string(cgc, cntl_value, NULL);
+
+out:
+       if (cntl_value_controller)
+               free(cntl_value_controller);
        return ret;
 }
 
-static int display_controller_values(char **controllers, const char *group_name,
-       const char *program_name, int mode)
+static int parse_g_flag_no_colon(struct cgroup **cg_list[],
+               int * const cg_list_len, const char * const ctrl_str)
 {
-       struct cgroup *group = NULL;
-       struct cgroup_controller *group_controller = NULL;
-       char *name;
-       int i, j;
-       int name_count;
+       struct cgroup_controller *cgc;
+       struct cgroup *cg = NULL;
        int ret = 0;
-       int result = 0;
 
-       /* initialize group_name variable */
-       group = cgroup_new_cgroup(group_name);
-       if (group == NULL) {
-               fprintf(stderr, "%s:cannot create group '%s'\n",
-                       program_name, group_name);
-               return -1;
+       if ((*cg_list_len) > 1) {
+               ret = ECGMAXVALUESEXCEEDED;
+               goto out;
        }
 
-       ret = cgroup_get_cgroup(group);
-       if (ret != 0) {
-               if (!(mode & MODE_SHOW_ALL_CONTROLLERS))
-                       fprintf(stderr, "%s: cannot read group '%s': %s\n",
-                               program_name, group_name, cgroup_strerror(ret));
-       }
-
-       /* for all wanted controllers */
-       j = 0;
-       while (controllers[j] != NULL) {
-               /* read the controller group data */
-               group_controller = cgroup_get_controller(group, controllers[j]);
-               if (group_controller == NULL) {
-                       if (!(mode & MODE_SHOW_ALL_CONTROLLERS)) {
-                               fprintf(stderr, "%s: cannot find controller "\
-                                       "'%s' in group '%s'\n", program_name,
-                                       controllers[j], group_name);
-                               result = -1;
-                       }
-               }
+       if ((*cg_list_len) == 0) {
+               ret = create_cg(cg_list, cg_list_len);
+               if (ret)
+                       goto out;
+       }
 
-               /* for each variable of given group print the statistic */
-               name_count = cgroup_get_value_name_count(group_controller);
-               for (i = 0; i < name_count; i++) {
-                       name = cgroup_get_value_name(group_controller, i);
-                       if (name != NULL) {
-                               ret = display_record(name, group_controller,
-                                       group_name, program_name, mode);
-                               if (ret) {
-                                       result = ret;
-                                       goto err;
-                               }
-                       }
+       /* if "-g <controller>" was provided, then we know that the cgroup(s)
+        * will be an optarg at the end with no flag.  Let's temporarily
+        * populate the first cgroup with the requested control values.
+        */
+       cg = *cg_list[0];
+
+       cgc = cgroup_get_controller(cg, ctrl_str);
+       if (!cgc) {
+               cgc = cgroup_add_controller(cg, ctrl_str);
+               if (!cgc) {
+                       ret = ECGCONTROLLERCREATEFAILED;
+                       goto out;
                }
-               j = j+1;
        }
 
-err:
-       cgroup_free(&group);
-       return result;
-
+out:
+       return ret;
 }
 
-static int display_values(char **controllers, int max, const char *group_name,
-       char **names, int mode, const char *program_name)
+static int split_cgroup_name(const char * const ctrl_str, char *cg_name)
 {
-       int ret, result = 0;
+       char *colon;
 
-       /* display the directory if needed */
-       if (mode & MODE_SHOW_HEADERS)
-               printf("%s:\n", group_name);
-
-       /* display all wanted variables */
-       if (names[0] != NULL) {
-               ret = display_name_values(names, group_name, program_name,
-                       mode);
-               if (ret)
-                       result = ret;
+       colon = strchr(ctrl_str, ':');
+       if (colon == NULL) {
+               /* ctrl_str doesn't contain a ":" */
+               cg_name[0] = '\0';
+               return ECGINVAL;
        }
 
-       /* display all wanted controllers */
-       if (controllers[0] != NULL) {
-               ret = display_controller_values(controllers, group_name,
-                       program_name, mode);
-               if (ret)
-                       result = ret;
-       }
-
-       /* separate each group with empty line. */
-       if (mode & MODE_SHOW_HEADERS)
-               printf("\n");
+       strncpy(cg_name, &colon[1], FILENAME_MAX - 1);
 
-       return result;
+       return 0;
 }
 
-int add_record_to_buffer(char **buffer, char *record, int capacity)
+static int split_controllers(const char * const in,
+                            char **ctrl[], int * const ctrl_len)
 {
-       int i;
+       char *copy, *tok, *colon, *saveptr = NULL;
+       char **tmp;
+       int ret = 0;
 
-       /* find first free entry inside the cgroup data array */
-       for (i = 0; i < capacity; i++) {
-               if (!buffer[i])
-                       break;
-       }
+       copy = strdup(in);
+       if (!copy)
+               goto out;
+       saveptr = copy;
+
+       colon = strchr(copy, ':');
+       if (colon)
+               colon[0] = '\0';
+
+       while ((tok = strtok_r(copy, ",", &copy))) {
+               tmp = realloc(*ctrl, sizeof(char *) * ((*ctrl_len) + 1));
+               if (!tmp) {
+                       ret = ECGOTHER;
+                       goto out;
+               }
+
+               *ctrl = tmp;
+               (*ctrl)[*ctrl_len] = strdup(tok);
+               if ((*ctrl)[*ctrl_len] == NULL) {
+                       ret = ECGOTHER;
+                       goto out;
+               }
 
-       if (i < capacity) {
-               buffer[i] = strdup(record);
-               if (buffer[i] == NULL)
-                       return 1;
-               return 0;
+               (*ctrl_len)++;
        }
-       return 1;
-}
 
+out:
+       if (saveptr)
+               free(saveptr);
+       return ret;
+}
 
-int main(int argc, char *argv[])
+static int parse_g_flag_with_colon(struct cgroup **cg_list[],
+               int * const cg_list_len, const char * const ctrl_str)
 {
-       int ret = 0;
-       int result = 0;
-       int c, i;
-       int group_needed = GR_NOTSET;
+       struct cgroup_controller *cgc;
+       struct cgroup *cg = NULL;
+       char **controllers = NULL;
+       int controllers_len = 0;
+       int i, ret = 0;
+
+       ret = create_cg(cg_list, cg_list_len);
+       if (ret)
+               goto out;
+
+       cg = (*cg_list)[(*cg_list_len) - 1];
+
+       ret = split_cgroup_name(ctrl_str, cg->name);
+       if (ret)
+               goto out;
+
+       ret = split_controllers(ctrl_str, &controllers, &controllers_len);
+       if (ret)
+               goto out;
+
+       for (i = 0; i < controllers_len; i++) {
+               cgc = cgroup_get_controller(cg, controllers[i]);
+               if (!cgc) {
+                       cgc = cgroup_add_controller(cg, controllers[i]);
+                       if (!cgc) {
+                               ret = ECGCONTROLLERCREATEFAILED;
+                               goto out;
+                       }
+               }
+       }
 
-       int capacity = argc + CG_CONTROLLER_MAX; /* maximal number of records */
-       struct cgroup_group_spec **cgroup_list; /* list of all groups */
-       char **names;                   /* list of wanted variable names */
-       char **controllers;             /* list of wanted controllers*/
+out:
+       for (i = 0; i < controllers_len; i++)
+               free(controllers[i]);
+       return ret;
+}
 
-       void *handle;
-       struct cgroup_mount_point controller;
+static int parse_opt_args(int argc, char *argv[], struct cgroup **cg_list[],
+                         int * const cg_list_len, bool first_cg_is_dummy)
+{
+       struct cgroup *cg = NULL;
+       int ret = 0;
 
-       int mode = MODE_SHOW_NAMES | MODE_SHOW_HEADERS;
+       /* The first cgroup was temporarily populated and requires
+        * the user to provide a cgroup name as an opt */
+       if (argv[optind] == NULL && first_cg_is_dummy) {
+               usage(1, argv[0]);
+               exit(-1);
+       }
 
-       /* No parameter on input? */
-       if (argc < 2) {
+       /* The user has provided a cgroup and controller via the
+        * -g <controller>:<cgroup> flag and has also provided a cgroup via
+        * the optind.  This was not supported by the previous cgget
+        * implementation.  Continue that approach.
+        *
+        * Example of a command that will hit this code:
+        *      $ cgget -g cpu:mycgroup mycgroup
+        */
+       if (argv[optind] != NULL && (*cg_list_len) > 0 &&
+           strcmp((*cg_list)[0]->name, "") != 0) {
                usage(1, argv[0]);
-               return 1;
+               exit(-1);
        }
 
-       names = (char **)calloc(capacity+1, sizeof(char *));
-       controllers = (char **)calloc(capacity+1, sizeof(char *));
-       cgroup_list = (struct cgroup_group_spec **)calloc(capacity,
-               sizeof(struct cgroup_group_spec *));
-       if ((names == NULL) || (controllers == NULL) ||
-               (cgroup_list == NULL)) {
-               fprintf(stderr, "%s: out of memory\n", argv[0]);
-               ret = -1;
-               goto err_free;
+       while (argv[optind] != NULL) {
+               cg = (*cg_list)[(*cg_list_len) - 1];
+
+               if (strlen(cg->name) == 0) {
+                       /* this cgroup was created based upon control/value
+                        * pairs or with a -g <controller> option.  we'll
+                        * populate it with the parameter provided by the
+                        * user
+                        */
+                       strncpy(cg->name, argv[optind], sizeof(cg->name) - 1);
+               } else {
+                       ret = create_cg(cg_list, cg_list_len);
+                       if (ret)
+                               goto out;
+
+                       ret = cgroup_copy_cgroup((*cg_list)[(*cg_list_len) - 1],
+                                                (*cg_list)[(*cg_list_len) - 2]);
+                       if (ret)
+                               goto out;
+
+                       strncpy((*cg_list)[(*cg_list_len) - 1]->name,
+                               argv[optind],
+                               sizeof((*cg_list)[(*cg_list_len) - 1]->name) - 1);
+               }
+
+               optind++;
        }
 
+out:
+       return ret;
+}
+
+static int parse_opts(int argc, char *argv[], struct cgroup **cg_list[],
+                     int * const cg_list_len, int * const mode)
+{
+       bool do_not_fill_controller = false;
+       bool fill_controller = false;
+       bool first_cgroup_is_dummy = false;
+       int ret = 0;
+       int c;
+
        /* Parse arguments. */
        while ((c = getopt_long(argc, argv, "r:hnvg:a", long_options, NULL))
                > 0) {
                switch (c) {
                case 'h':
                        usage(0, argv[0]);
-                       result = 0;
-                       goto err;
-                       break;
-
+                       exit(0);
                case 'n':
                        /* Do not show headers. */
-                       mode = mode & (INT_MAX ^ MODE_SHOW_HEADERS);
+                       *mode = (*mode) & (INT_MAX ^ MODE_SHOW_HEADERS);
                        break;
-
                case 'v':
                        /* Do not show parameter names. */
-                       mode = mode & (INT_MAX ^ MODE_SHOW_NAMES);
+                       *mode = (*mode) & (INT_MAX ^ MODE_SHOW_NAMES);
                        break;
-
                case 'r':
-                       /* Add name to buffer. */
-                       ret = add_record_to_buffer(names, optarg, capacity);
-                       if (ret) {
-                               result = ret;
+                       do_not_fill_controller = true;
+                       first_cgroup_is_dummy = true;
+                       ret = parse_r_flag(cg_list, cg_list_len, optarg);
+                       if (ret)
                                goto err;
-                       }
                        break;
                case 'g':
+                       fill_controller = true;
                        if (strchr(optarg, ':') == NULL) {
-                               /* -g <group> */
-                               if (group_needed == GR_LIST) {
-                                       usage(1, argv[0]);
-                                       result = -1;
+                               first_cgroup_is_dummy = true;
+                               ret = parse_g_flag_no_colon(cg_list,
+                                       cg_list_len, optarg);
+                               if (ret)
                                        goto err;
-                               }
-                               group_needed = GR_GROUP;
-                               add_record_to_buffer(controllers, optarg,
-                                       capacity);
                        } else {
-                               /* -g <group>:<path> */
-                               if (group_needed == GR_GROUP) {
-                                       usage(1, argv[0]);
-                                       result = -1;
-                                       goto err;
-                               }
-                               group_needed = GR_LIST;
-                               ret = parse_cgroup_spec(cgroup_list, optarg,
-                                       capacity);
-                               if (ret) {
-                                       fprintf(stderr, "%s: cgroup controller/"
-                                               "path parsing failed (%s)\n",
-                                               argv[0], argv[optind]);
-                                       ret = -1;
+                               ret = parse_g_flag_with_colon(cg_list,
+                                       cg_list_len, optarg);
+                               if (ret)
                                        goto err;
-                               }
                        }
                        break;
                case 'a':
-                       if (group_needed == GR_LIST) {
-                               usage(1, argv[0]);
-                               result = -1;
+                       fill_controller = true;
+                       ret = parse_a_flag(cg_list, cg_list_len);
+                       if (ret)
                                goto err;
-                       }
-                       group_needed = GR_GROUP;
-                       /* go through cgroups for all possible controllers */
-                       mode |=  MODE_SHOW_ALL_CONTROLLERS;
                        break;
                default:
                        usage(1, argv[0]);
-                       result = -1;
-                       goto err;
+                       exit(1);
                }
        }
 
-       if (((group_needed == GR_LIST) && (argv[optind])) ||
-           ((group_needed != GR_LIST) && (!argv[optind]))) {
-               /* mixed -g <controllers>:<path> and <path> or path not set */
+       /* Don't allow '-r' and ('-g' or '-a') */
+       if (fill_controller && do_not_fill_controller) {
                usage(1, argv[0]);
-               result = -1;
-               goto err;
+               exit(1);
        }
 
-       /* Initialize libcgroup. */
-       ret = cgroup_init();
-       if (ret) {
-               fprintf(stderr, "%s: libcgroup initialization failed: %s\n",
-                       argv[0], cgroup_strerror(ret));
-               result = ret;
+       ret = parse_opt_args(argc, argv, cg_list, cg_list_len,
+                            first_cgroup_is_dummy);
+       if (ret)
                goto err;
+
+err:
+       return ret;
+}
+
+static int get_cv_value(struct control_value * const cv,
+                       const char * const cg_name,
+                       const char * const controller_name)
+{
+       bool is_multiline = false;
+       char tmp_line[LL_MAX];
+       void *handle, *tmp;
+       int ret;
+
+       ret = cgroup_read_value_begin(controller_name, cg_name, cv->name,
+                       &handle, tmp_line, LL_MAX);
+       if (ret == ECGEOF)
+               goto read_end;
+       if (ret != 0) {
+               if (ret == ECGOTHER) {
+                       int tmp_ret;
+
+                       /* to maintain compatibility with an earlier version
+                        * of cgget, try to determine if the failure was due
+                        * to an invalid controller
+                        */
+                       tmp_ret = cgroup_test_subsys_mounted(controller_name);
+                       if (tmp_ret == 0)
+                               fprintf(stderr, "cgget: cannot find controller " \
+                                       "'%s' in group '%s'\n", controller_name,
+                                       cg_name);
+                       else
+                               fprintf(stderr, "variable file read failed %s\n",
+                                       cgroup_strerror(ret));
+               }
+
+               goto end;
        }
 
-       if ((mode & MODE_SHOW_ALL_CONTROLLERS) ||
-               ((controllers[0] == NULL) && (names[0] == NULL)
-               && (cgroup_list[0] == NULL))) {
-               /* show info about all controllers */
-               ret = cgroup_get_controller_begin(&handle, &controller);
-               /* go through list of controllers, add them to the list */
-               while (ret == 0) {
-                       add_record_to_buffer(controllers, controller.name,
-                               capacity);
-                       ret = cgroup_get_controller_next(&handle, &controller);
+       /* remove the newline character */
+       tmp_line[strcspn(tmp_line, "\n")] = '\0';
+
+       strncpy(cv->value, tmp_line, CG_CONTROL_VALUE_MAX - 1);
+       cv->multiline_value = strdup(cv->value);
+       if (cv->multiline_value == NULL)
+               goto end;
+
+       while((ret = cgroup_read_value_next(&handle, tmp_line, LL_MAX)) == 0) {
+               if (ret == 0) {
+                       is_multiline = true;
+                       cv->value[0] = '\0';
+
+                       /* remove the newline character */
+                       tmp_line[strcspn(tmp_line, "\n")] = '\0';
+
+                       tmp = realloc(cv->multiline_value, sizeof(char) *
+                               (strlen(cv->multiline_value) + strlen(tmp_line) + 3));
+                       if (tmp == NULL)
+                               goto end;
+
+                       cv->multiline_value = tmp;
+                       strcat(cv->multiline_value, "\n\t");
+                       strcat(cv->multiline_value, tmp_line);
                }
-               cgroup_get_controller_end(&handle);
        }
 
-       /* Parse control groups set by -g<c>:<p> pairs */
-       for (i = 0; i < capacity; i++) {
-               if (!cgroup_list[i])
+read_end:
+       cgroup_read_value_end(&handle);
+       if (ret == ECGEOF)
+               ret = 0;
+
+end:
+       if (is_multiline == false && cv->multiline_value) {
+               free(cv->multiline_value);
+               cv->multiline_value = NULL;
+       }
+
+       return ret;
+}
+
+static int indent_multiline_value(struct control_value * const cv)
+{
+       char tmp_val[CG_CONTROL_VALUE_MAX] = {0};
+       char *tok, *saveptr = NULL;
+
+       tok = strtok_r(cv->value, "\n", &saveptr);
+       strcat(tmp_val, tok);
+       /* don't indent the first value */
+       while ((tok = strtok_r(NULL, "\n", &saveptr))) {
+               strcat(tmp_val, "\n\t");
+               strcat(tmp_val, tok);
+       }
+
+       cv->multiline_value = strdup(tmp_val);
+       if (!cv->multiline_value)
+               return ECGOTHER;
+
+       return 0;
+}
+
+static int fill_empty_controller(struct cgroup * const cg,
+                                struct cgroup_controller * const cgc)
+{
+       struct dirent *ctrl_dir = NULL;
+       char path[FILENAME_MAX];
+       bool found_mount = false;
+       int i, path_len, ret = 0;
+       DIR *dir = NULL;
+
+       pthread_rwlock_rdlock(&cg_mount_table_lock);
+
+       for (i = 0; i < CG_CONTROLLER_MAX &&
+                       cg_mount_table[i].name[0] != '\0'; i++) {
+               if (strlen(cgc->name) == strlen(cg_mount_table[i].name) &&
+                   strncmp(cgc->name, cg_mount_table[i].name,
+                           strlen(cgc->name)) == 0) {
+                       found_mount = true;
                        break;
-               ret |= display_values(cgroup_list[i]->controllers, capacity,
-                       cgroup_list[i]->path, names, mode, argv[0]);
+               }
        }
 
-       /* Parse control groups and print them .*/
-       for (i = optind; i < argc; i++) {
-               ret |= display_values(controllers, capacity,
-                       argv[i], names, mode, argv[0]);
+       if (found_mount == false)
+               goto out;
+
+       if (!cg_build_path_locked(NULL, path,
+                                 cg_mount_table[i].name)) {
+               goto out;
        }
 
-err:
-       for (i = 0; i < capacity; i++) {
-               if (cgroup_list[i])
-                       cgroup_free_group_spec(cgroup_list[i]);
-               if (controllers[i])
-                       free(controllers[i]);
-               if (names[i])
-                       free(names[i]);
+       path_len = strlen(path);
+       strncat(path, cg->name, FILENAME_MAX - path_len - 1);
+
+       if (access(path, F_OK))
+               goto out;
+
+       if (!cg_build_path_locked(cg->name, path,
+               cg_mount_table[i].name))
+               goto out;
+
+       dir = opendir(path);
+       if (!dir) {
+               ret = ECGOTHER;
+               goto out;
+       }
+
+       while ((ctrl_dir = readdir(dir)) != NULL) {
+               /*
+                * Skip over non regular files
+                */
+               if (ctrl_dir->d_type != DT_REG)
+                       continue;
+
+               ret = cgroup_fill_cgc(ctrl_dir, cg, cgc, i);
+               if (ret == ECGFAIL) {
+                       closedir(dir);
+                       goto out;
+               }
+
+               if (cgc->index > 0) {
+                       cgc->values[cgc->index - 1]->dirty = false;
+
+                       /* previous versions of cgget indented the second and
+                        * all subsequent lines.  continue that behavior
+                        */
+                       if (strchr(cgc->values[cgc->index - 1]->value, '\n')) {
+                               ret = indent_multiline_value(
+                                       cgc->values[cgc->index - 1]);
+                               if (ret)
+                                       goto out;
+                       }
+               }
        }
 
-err_free:
-       free(cgroup_list);
-       free(controllers);
-       free(names);
+       closedir(dir);
+
+out:
+       pthread_rwlock_unlock(&cg_mount_table_lock);
+       return ret;
+}
+
+static int get_controller_values(struct cgroup * const cg,
+                                struct cgroup_controller * const cgc)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < cgc->index; i++) {
+               ret = get_cv_value(cgc->values[i], cg->name, cgc->name);
+               if (ret)
+                       goto out;
+       }
 
-       return result;
+       if (cgc->index == 0) {
+               /* fill the entire controller since no values were provided */
+               ret = fill_empty_controller(cg, cgc);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       return ret;
+}
+
+static int get_cgroup_values(struct cgroup * const cg)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < cg->index; i++) {
+               ret = get_controller_values(cg, cg->controller[i]);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static int get_values(struct cgroup *cg_list[], int cg_list_len)
+{
+       int ret;
+       int i;
+
+       for (i = 0; i < cg_list_len; i++) {
+               ret = get_cgroup_values(cg_list[i]);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+void print_control_values(const struct control_value * const cv, int mode)
+{
+       if (mode & MODE_SHOW_NAMES)
+               printf("%s: ", cv->name);
+
+       if (cv->multiline_value)
+               printf("%s\n", cv->multiline_value);
+       else
+               printf("%s\n", cv->value);
+}
+
+void print_controller(const struct cgroup_controller * const cgc, int mode)
+{
+       int i;
+
+       for (i = 0; i < cgc->index; i++) {
+               print_control_values(cgc->values[i], mode);
+       }
+}
+
+static void print_cgroup(const struct cgroup * const cg, int mode)
+{
+       int i;
+
+       if (mode & MODE_SHOW_HEADERS)
+               printf("%s:\n", cg->name);
+
+       for (i = 0; i < cg->index; i++)
+               print_controller(cg->controller[i], mode);
+
+       if (mode & MODE_SHOW_HEADERS)
+               printf("\n");
+}
+
+static void print_cgroups(struct cgroup *cg_list[], int cg_list_len, int mode)
+{
+       int i;
+
+       for (i = 0; i < cg_list_len; i++) {
+               print_cgroup(cg_list[i], mode);
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       struct cgroup **cg_list = NULL;
+       int cg_list_len = 0;
+       int ret = 0, i;
+       int mode = MODE_SHOW_NAMES | MODE_SHOW_HEADERS;
+
+       /* No parameter on input? */
+       if (argc < 2) {
+               usage(1, argv[0]);
+               return 1;
+       }
+
+       ret = cgroup_init();
+       if (ret) {
+               fprintf(stderr, "%s: libcgroup initialization failed: %s\n",
+                       argv[0], cgroup_strerror(ret));
+               goto err;
+       }
+
+       ret = parse_opts(argc, argv, &cg_list, &cg_list_len, &mode);
+       if (ret)
+               goto err;
+
+       ret = get_values(cg_list, cg_list_len);
+       if (ret)
+               goto err;
+
+       print_cgroups(cg_list, cg_list_len, mode);
+
+err:
+       for (i = 0; i < cg_list_len; i++)
+               cgroup_free(&(cg_list[i]));
+
+       return ret;
 }
index 35f5dbd8afa581c7122012c71d1fc94dabe7301f..966cb8490102048c989abfa1e47366079a883d46 100644 (file)
@@ -150,8 +150,12 @@ void cgroup_free_controllers(struct cgroup *cgroup)
                return;
 
        for (i = 0; i < cgroup->index; i++) {
-               for (j = 0; j < cgroup->controller[i]->index; j++)
+               for (j = 0; j < cgroup->controller[i]->index; j++) {
+                       if (cgroup->controller[i]->values[j]->multiline_value)
+                               free(cgroup->controller[i]->values[j]->multiline_value);
+
                        free(cgroup->controller[i]->values[j]);
+               }
                cgroup->controller[i]->index = 0;
                free(cgroup->controller[i]);
        }