[LTP] [PATCH v4 06/19] API/cgroup: Implement tst_cg_load_config

Luke Nowakowski-Krijger luke.nowakowskikrijger@canonical.com
Thu Apr 28 22:39:31 CEST 2022


Implement tst_cg_load_config which consumes the state given by
tst_cg_print_config to update the internal test state to reflect
the given config.

This allows for programs using the cgroup C API to load and reload
state, allowing functionality such as calling tst_cg_require and
tst_cg_cleanup to function properly between programs or between
invocations of a binary using the C API.

Signed-off-by: Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com>
---
v2: Add root null check in parse_root_config.
    Remove checking for ltp_drain_dir key from config as it was
    redundant.
    Remove unsued variable in parse_ctrl_config.
    Cleanup some compiler warnings.
v3: Rewrite to consume each line of the config with a scanf to make
    the parsing much simpler while using new config variables.
v4: Modify CONFIG_FORMAT to have variable delimits for sscanf to make it
    more safe. Add extra checks for sscanf reading.

 include/tst_cgroup.h |  7 ++++
 lib/tst_cgroup.c     | 77 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 81 insertions(+), 3 deletions(-)

diff --git a/include/tst_cgroup.h b/include/tst_cgroup.h
index 87e55f4df..9bad0d366 100644
--- a/include/tst_cgroup.h
+++ b/include/tst_cgroup.h
@@ -121,6 +121,13 @@ void tst_cg_scan(void);
  */
 void tst_cg_print_config(void);
 
+/* Load the config printed out by tst_cg_print_config and configure the internal
+ * libary state to match the config. Used to allow tst_cg_cleanup to properly
+ * cleanup mounts and directories created by tst_cg_require between program
+ * invocations.
+ */
+void tst_cg_load_config(const char *const config);
+
 /* Ensure the specified controller is available in the test's default
  * CGroup, mounting/enabling it if necessary. Usually this is not
  * necesary use tst_test.needs_cgroup_controllers instead.
diff --git a/lib/tst_cgroup.c b/lib/tst_cgroup.c
index 2d4e4a2fe..55c57b20d 100644
--- a/lib/tst_cgroup.c
+++ b/lib/tst_cgroup.c
@@ -319,8 +319,9 @@ opendir:
 				  O_PATH | O_DIRECTORY);
 }
 
+#define PATH_MAX_STRLEN 4095
 #define CONFIG_HEADER "ctrl_name ver we_require_it mnt_path we_mounted_it ltp_dir.we_created_it test_dir.dir_name"
-#define CONFIG_FORMAT "%s\t%d\t%d\t%s\t%d\t%d\t%s"
+#define CONFIG_FORMAT "%" TST_TOSTR(CTRL_NAME_MAX) "s\t%d\t%d\t%" TST_TOSTR(PATH_MAX_STRLEN) "s\t%d\t%d\t%" TST_TOSTR(NAME_MAX) "s"
 /* Prints out the state associated with each controller to be consumed by
  * tst_cg_load_config.
  *
@@ -339,7 +340,7 @@ void tst_cg_print_config(void)
 		if (!root)
 			continue;
 
-		printf(CONFIG_FORMAT,
+		printf("%s\t%d\t%d\t%s\t%d\t%d\t%s\n",
 			ctrl->ctrl_name,
 			root->ver,
 			ctrl->we_require_it,
@@ -347,7 +348,6 @@ void tst_cg_print_config(void)
 			root->we_mounted_it,
 			root->ltp_dir.we_created_it,
 			root->test_dir.dir_name ? root->test_dir.dir_name : "NULL");
-		printf("\n");
 	}
 }
 
@@ -376,6 +376,77 @@ static struct cgroup_root *cgroup_find_root(const char *const mnt_path)
 	return NULL;
 }
 
+static void cgroup_parse_config_line(const char *const config_entry)
+{
+	char ctrl_name[CTRL_NAME_MAX + 1], mnt_path[PATH_MAX_STRLEN + 1], test_dir_name[NAME_MAX + 1];
+	int ver, we_require_it, we_mounted_it, ltp_dir_we_created_it, vars_read;
+	size_t len;
+	struct cgroup_root *root;
+	struct cgroup_ctrl *ctrl;
+
+	vars_read = sscanf(config_entry, CONFIG_FORMAT,
+		ctrl_name, &ver, &we_require_it, mnt_path, &we_mounted_it,
+		&ltp_dir_we_created_it, test_dir_name);
+
+	if (vars_read != 7)
+		tst_brk(TBROK, "Incorrect number of vars read from config. Config possibly malformed?");
+
+	ctrl = cgroup_find_ctrl(ctrl_name);
+	if (!ctrl)
+		tst_brk(TBROK, "Could not find ctrl from config. Ctrls changing between calls?");
+
+	ctrl->we_require_it = we_require_it;
+
+	root = cgroup_find_root(mnt_path);
+	if (!root)
+		tst_brk(TBROK, "Could not find root from config. Config possibly malformed?");
+
+	if (we_mounted_it)
+		root->we_mounted_it = 1;
+
+	if (!root->ltp_dir.dir_name) {
+		cgroup_dir_mk(&root->mnt_dir, cgroup_ltp_dir, &root->ltp_dir);
+		cgroup_dir_mk(&root->ltp_dir, cgroup_ltp_drain_dir, &root->drain_dir);
+		if (ltp_dir_we_created_it) {
+			root->ltp_dir.we_created_it = 1;
+			root->drain_dir.we_created_it = 1;
+		}
+	}
+
+	if (!root->test_dir.dir_name && strcmp(test_dir_name, "NULL")) {
+		strncpy(cgroup_test_dir, test_dir_name, NAME_MAX);
+		cgroup_dir_mk(&root->ltp_dir, cgroup_test_dir, &root->test_dir);
+		root->test_dir.we_created_it = 1;
+	}
+}
+
+/* Load the test state config provided by tst_cg_print_config
+ *
+ * This will reload some internal tst_cgroup state given by the config
+ * that might otherwise have been lost between calls or between different
+ * processes. In particular this is used by testcases/lib/tst_cgctl to
+ * provide access to this C api to shell scripts.
+ *
+ * The config keeps track of the minimal state needed for tst_cg_cleanup
+ * to cleanup mounts and directories created by tst_cg_require.
+ */
+void tst_cg_load_config(const char *const config)
+{
+	char temp_config[BUFSIZ];
+	char *line;
+	const size_t config_len = strlen(config) + 1;
+
+	if (config_len >= BUFSIZ)
+		tst_brk(TBROK, "Config has exceeded buffer size?");
+
+	memcpy(temp_config, config, config_len);
+	temp_config[config_len] = '\0';
+
+	line = strtok(temp_config, "\n");
+	/* Make sure to consume the header. */
+	for (line = strtok(NULL, "\n"); line; line = strtok(NULL, "\n"))
+		cgroup_parse_config_line(line);
+}
 
 /* Determine if a mounted cgroup hierarchy is unique and record it if so.
  *
-- 
2.32.0



More information about the ltp mailing list