From ae4e12d0bfa82e6c0208cba5de2700ced911ed01 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Fri, 28 Jan 2022 10:22:49 -0700 Subject: [PATCH] cgxset: Introduce cgxset Add a new libcgroup tool - cgxset. cgxset is based upon cgset, but it supports converting from cgroup version to another. For example, a request like the following will work on a system running the cgroup v1 cpu controller or the cgroup v2 cpu controller. $ cgxset -2 -r cpu.weight=42 MyCgroup Signed-off-by: Tom Hromatka Reviewed-by: Kamalesh Babulal --- src/tools/Makefile.am | 6 +- src/tools/cgxset.c | 334 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 src/tools/cgxset.c diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 268630b9..60e69690 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -5,7 +5,7 @@ LDADD = $(top_builddir)/src/libcgroup.la -lpthread if WITH_TOOLS -bin_PROGRAMS = cgexec cgclassify cgcreate cgset cgget cgxget cgdelete \ +bin_PROGRAMS = cgexec cgclassify cgcreate cgset cgxset cgget cgxget cgdelete \ lssubsys lscgroup cgsnapshot sbin_PROGRAMS = cgconfigparser @@ -33,6 +33,10 @@ cgset_SOURCES = cgset.c tools-common.c tools-common.h cgset_LIBS = $(CODE_COVERAGE_LIBS) cgset_CFLAGS = $(CODE_COVERAGE_CFLAGS) -DSTATIC=static +cgxset_SOURCES = cgxset.c tools-common.c tools-common.h +cgxset_LIBS = $(CODE_COVERAGE_LIBS) +cgxset_CFLAGS = $(CODE_COVERAGE_CFLAGS) -DSTATIC=static + cgget_SOURCES = cgget.c tools-common.c tools-common.h cgget_LIBS = $(CODE_COVERAGE_LIBS) cgget_CFLAGS = $(CODE_COVERAGE_CFLAGS) diff --git a/src/tools/cgxset.c b/src/tools/cgxset.c new file mode 100644 index 00000000..efd050a5 --- /dev/null +++ b/src/tools/cgxset.c @@ -0,0 +1,334 @@ +/** + * Libcgroup extended cgset. Works with both cgroup v1 and v2 + * + * Copyright (c) 2021-2022 Oracle and/or its affiliates. + * Author: Tom Hromatka + */ + +/* + * This library 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 library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, see . + */ +#include +#include + +#include +#include +#include +#include +#include + +#include "tools-common.h" + +#include "abstraction-common.h" + +#define FL_RULES 1 +#define FL_COPY 2 + +enum { + COPY_FROM_OPTION = CHAR_MAX + 1 +}; + +#ifndef UNIT_TEST +static struct option const long_options[] = +{ + {"v1", no_argument, NULL, '1'}, + {"v2", no_argument, NULL, '2'}, + {"rule", required_argument, NULL, 'r'}, + {"help", no_argument, NULL, 'h'}, + {"copy-from", required_argument, NULL, COPY_FROM_OPTION}, + {NULL, 0, NULL, 0} +}; + +int flags; /* used input method */ + +static struct cgroup *copy_name_value_from_cgroup(char src_cg_path[FILENAME_MAX]) +{ + int ret = 0; + struct cgroup *src_cgroup; + + /* create source cgroup */ + src_cgroup = cgroup_new_cgroup(src_cg_path); + if (!src_cgroup) { + fprintf(stderr, "can't create cgroup: %s\n", + cgroup_strerror(ECGFAIL)); + goto scgroup_err; + } + + /* copy the name-version values to the cgroup structure */ + ret = cgroup_get_cgroup(src_cgroup); + if (ret != 0) { + fprintf(stderr, "cgroup %s error: %s \n", + src_cg_path, cgroup_strerror(ret)); + goto scgroup_err; + } + + return src_cgroup; + +scgroup_err: + cgroup_free(&src_cgroup); + return NULL; +} + + +static void usage(int status, const char *program_name) +{ + if (status != 0) { + fprintf(stderr, "Wrong input parameters," + " try %s --help' for more information.\n", + program_name); + return; + } + printf("Usage: %s [-r ] ...\n" + " or: %s --copy-from "\ + " ...\n", program_name, program_name); + printf("Set the parameters of given cgroup(s)\n"); + printf(" -1, --v1 Provided parameters are in " + "v1 format\n"); + printf(" -2, --v2 Provided parameters are in " + "v2 format\n"); + printf(" -r, --variable Define parameter "\ + "to set\n"); + printf(" --copy-from Control group whose "\ + "parameters will be copied\n"); +} +#endif /* !UNIT_TEST */ + +STATIC int parse_r_flag(const char * const program_name, + const char * const name_value_str, + struct control_value * const name_value) +{ + int ret = 0; + char *copy, *buf; + + copy = strdup(name_value_str); + if (copy == NULL) { + fprintf(stderr, "%s: not enough memory\n", program_name); + ret = -1; + goto err; + } + + /* parse optarg value */ + buf = strtok(copy, "="); + if (buf == NULL) { + fprintf(stderr, "%s: " + "wrong parameter of option -r: %s\n", + program_name, optarg); + ret = -1; + goto err; + } + + strncpy(name_value->name, buf, FILENAME_MAX); + name_value->name[FILENAME_MAX-1] = '\0'; + + buf = strchr(name_value_str, '='); + /* we don't need to check the return value of strchr because we + * know there's already an '=' character in the string. + */ + buf++; + + if (strlen(buf) == 0) { + fprintf(stderr, "%s: " + "wrong parameter of option -r: %s\n", + program_name, optarg); + ret = -1; + goto err; + } + + strncpy(name_value->value, buf, CG_CONTROL_VALUE_MAX); + name_value->value[CG_CONTROL_VALUE_MAX-1] = '\0'; + +err: + if (copy) + free(copy); + + return ret; +} + +#ifndef UNIT_TEST +int main(int argc, char *argv[]) +{ + int ret = 0; + int c; + + struct control_value *name_value = NULL; + int nv_number = 0; + int nv_max = 0; + + char src_cg_path[FILENAME_MAX]; + struct cgroup *src_cgroup; + struct cgroup *cgroup; + struct cgroup *converted_src_cgroup; + enum cg_version_t src_version = CGROUP_UNK; + + /* no parametr on input */ + if (argc < 2) { + fprintf(stderr, "Usage is %s -r " + "\n", argv[0]); + return -1; + } + + /* parse arguments */ + while ((c = getopt_long (argc, argv, + "r:h12", long_options, NULL)) != -1) { + switch (c) { + case 'h': + usage(0, argv[0]); + ret = 0; + goto err; + break; + + case 'r': + if ((flags & FL_COPY) != 0) { + usage(1, argv[0]); + ret = -1; + goto err; + } + flags |= FL_RULES; + + /* add name-value pair to buffer + (= name_value variable) */ + if (nv_number >= nv_max) { + nv_max += CG_NV_MAX; + name_value = (struct control_value *) + realloc(name_value, + nv_max * sizeof(struct control_value)); + if (!name_value) { + fprintf(stderr, "%s: " + "not enough memory\n", + argv[0]); + ret = -1; + goto err; + } + } + + ret = parse_r_flag(argv[0], optarg, + &name_value[nv_number]); + if (ret) + goto err; + + nv_number++; + break; + case COPY_FROM_OPTION: + if (flags != 0) { + usage(1, argv[0]); + ret = -1; + goto err; + } + flags |= FL_COPY; + strncpy(src_cg_path, optarg, FILENAME_MAX); + src_cg_path[FILENAME_MAX-1] = '\0'; + break; + case '1': + src_version = CGROUP_V1; + break; + case '2': + src_version = CGROUP_V2; + break; + default: + usage(1, argv[0]); + ret = -1; + goto err; + break; + } + } + + /* no cgroup name */ + if (!argv[optind]) { + fprintf(stderr, "%s: no cgroup specified\n", argv[0]); + ret = -1; + goto err; + } + + if (flags == 0) { + fprintf(stderr, "%s: no name-value pair was set\n", argv[0]); + ret = -1; + goto err; + } + + /* initialize libcgroup */ + ret = cgroup_init(); + if (ret) { + fprintf(stderr, "%s: libcgroup initialization failed: %s\n", + argv[0], cgroup_strerror(ret)); + goto err; + } + + /* copy the name-value pairs from -r options */ + if ((flags & FL_RULES) != 0) { + src_cgroup = create_cgroup_from_name_value_pairs( + "tmp", name_value, nv_number); + if (src_cgroup == NULL) + goto err; + } + + /* copy the name-value from the given group */ + if ((flags & FL_COPY) != 0) { + src_cgroup = copy_name_value_from_cgroup(src_cg_path); + if (src_cgroup == NULL) + goto err; + } + + while (optind < argc) { + /* create new cgroup */ + cgroup = cgroup_new_cgroup(argv[optind]); + if (!cgroup) { + ret = ECGFAIL; + fprintf(stderr, "%s: can't add new cgroup: %s\n", + argv[0], cgroup_strerror(ret)); + goto cgroup_free_err; + } + + /* copy the values from the source cgroup to new one */ + ret = cgroup_copy_cgroup(cgroup, src_cgroup); + if (ret != 0) { + fprintf(stderr, "%s: cgroup %s error: %s \n", + argv[0], src_cg_path, cgroup_strerror(ret)); + goto cgroup_free_err; + } + + converted_src_cgroup = cgroup_new_cgroup(cgroup->name); + if (converted_src_cgroup == NULL) { + ret = ECGCONTROLLERCREATEFAILED; + goto err; + } + + ret = cgroup_convert_cgroup(converted_src_cgroup, CGROUP_DISK, + src_cgroup, src_version); + if (ret) + goto err; + + cgroup_free(&cgroup); + cgroup = converted_src_cgroup; + + /* modify cgroup based on values of the new one */ + ret = cgroup_modify_cgroup(cgroup); + if (ret) { + fprintf(stderr, "%s: cgroup modify error: %s \n", + argv[0], cgroup_strerror(ret)); + goto cgroup_free_err; + } + + optind++; + cgroup_free(&cgroup); + } + +cgroup_free_err: + if (cgroup) + cgroup_free(&cgroup); + cgroup_free(&src_cgroup); + +err: + free(name_value); + return ret; +} +#endif /* !UNIT_TEST */ -- 2.47.2