diff mbox series

[RFC,v2] lib: new tst_test field to save and restore proc|sys

Message ID 3a4b56e5194eb853e882357826c335d416614c86.1539966340.git.jstancek@redhat.com
State Superseded
Headers show
Series [RFC,v2] lib: new tst_test field to save and restore proc|sys | expand

Commit Message

Jan Stancek Oct. 19, 2018, 4:25 p.m. UTC
To avoid adding specially crafted functions for every feature
where we need to save/restore some proc/sys config, this patch
introduces a new field to tst_test struct where user specifies
NULL terminated array of strings whose values should be saved
before test and restored after.

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 doc/test-writing-guidelines.txt |  37 +++++++++++++
 include/tst_sys_conf.h          |  27 ++++++++++
 include/tst_test.h              |   7 +++
 lib/newlib_tests/.gitignore     |   1 +
 lib/newlib_tests/test19.c       |  48 +++++++++++++++++
 lib/tst_sys_conf.c              | 112 ++++++++++++++++++++++++++++++++++++++++
 lib/tst_test.c                  |  14 +++++
 7 files changed, 246 insertions(+)
 create mode 100644 include/tst_sys_conf.h
 create mode 100644 lib/newlib_tests/test19.c
 create mode 100644 lib/tst_sys_conf.c

Comments

Cyril Hrubis Nov. 1, 2018, 4 p.m. UTC | #1
Hi!
> diff --git a/include/tst_sys_conf.h b/include/tst_sys_conf.h
> new file mode 100644
> index 000000000000..ed558f3cc8fd
> --- /dev/null
> +++ b/include/tst_sys_conf.h
> @@ -0,0 +1,27 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright (c) 2018 Jan Stancek <jstancek@redhat.com>
> + */
> +
> +#ifndef TST_SYS_CONF_H__
> +#define TST_SYS_CONF_H__
> +
> +struct tst_sys_conf {
> +	char path[PATH_MAX];
> +	char value[PATH_MAX];
> +	struct tst_sys_conf *prev;
> +	struct tst_sys_conf *next;
> +};
> +
> +#define SYS_CONF_HEAD_INIT(name) { .prev = &(name), .next = &(name) }
> +#define TST_SYS_CONF_INIT(name) \
> +	struct tst_sys_conf name = SYS_CONF_HEAD_INIT(name)

Well we can use ordinary (single link) linked list unless we want to
delete from it I guess. Or is there a reason to have double linked one?

Also I suppose that we do not need explicit declaration for the root
pointer, there would be only one list in existence. I would just put one
static pointer into the tst_sys_conf.c.

Other than these minor things it looks very good.
diff mbox series

Patch

diff --git a/doc/test-writing-guidelines.txt b/doc/test-writing-guidelines.txt
index f590896472d1..d0b91c36294c 100644
--- a/doc/test-writing-guidelines.txt
+++ b/doc/test-writing-guidelines.txt
@@ -1468,6 +1468,43 @@  first missing driver.
 Since it relies on modprobe command, the check will be skipped if the command
 itself is not available on the system.
 
+2.2.27 Saving & restoring /proc|sys values
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+LTP library can be instructed to save and restore value of specified
+(/proc|sys) files. This is achieved by initialized tst_test struct
+field 'save_restore'. It is a NULL terminated array of strings where
+each string represents a file, whose value is saved at the beginning
+and restored at the end of the test. Only first line of a specified
+file is saved and restored.
+
+Pathnames can be optionally prefixed to specify how strictly (during
+'store') are handled files that don't exist:
+  (no prefix) - test ends with TCONF
+  '?'         - test prints info message and continues
+  '!'         - test ends with TBROK
+
+'restore' is always strict and will TWARN if it encounters any error.
+
+Example:
+
+static const char *save_restore[] = {
+	"/proc/sys/kernel/core_pattern",
+	NULL,
+};
+
+static void setup(void)
+{
+	FILE_PRINTF("/proc/sys/kernel/core_pattern", "/mypath");
+}
+
+static struct tst_test test = {
+	...
+	.setup = setup,
+	.save_restore = save_restore,
+};
+
+
 2.3 Writing a testcase in shell
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
diff --git a/include/tst_sys_conf.h b/include/tst_sys_conf.h
new file mode 100644
index 000000000000..ed558f3cc8fd
--- /dev/null
+++ b/include/tst_sys_conf.h
@@ -0,0 +1,27 @@ 
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek <jstancek@redhat.com>
+ */
+
+#ifndef TST_SYS_CONF_H__
+#define TST_SYS_CONF_H__
+
+struct tst_sys_conf {
+	char path[PATH_MAX];
+	char value[PATH_MAX];
+	struct tst_sys_conf *prev;
+	struct tst_sys_conf *next;
+};
+
+#define SYS_CONF_HEAD_INIT(name) { .prev = &(name), .next = &(name) }
+#define TST_SYS_CONF_INIT(name) \
+	struct tst_sys_conf name = SYS_CONF_HEAD_INIT(name)
+
+int tst_sys_conf_save_str(struct tst_sys_conf *c, const char *path,
+			  const char *value);
+int tst_sys_conf_save(struct tst_sys_conf *c, const char *path);
+void tst_sys_conf_restore(struct tst_sys_conf *c, int verbose);
+void tst_sys_conf_dump(struct tst_sys_conf *c);
+
+#endif
diff --git a/include/tst_test.h b/include/tst_test.h
index 080b0171d5f2..2ebf746eb720 100644
--- a/include/tst_test.h
+++ b/include/tst_test.h
@@ -42,6 +42,7 @@ 
 #include "tst_minmax.h"
 #include "tst_get_bad_addr.h"
 #include "tst_path_has_mnt_flags.h"
+#include "tst_sys_conf.h"
 
 /*
  * Reports testcase result.
@@ -175,6 +176,12 @@  struct tst_test {
 
 	/* NULL terminated array of needed kernel drivers */
 	const char * const *needs_drivers;
+
+	/*
+	 * NULL terminated array of (/proc, /sys) files to save
+	 * before setup and restore after cleanup
+	 */
+	const char * const *save_restore;
 };
 
 /*
diff --git a/lib/newlib_tests/.gitignore b/lib/newlib_tests/.gitignore
index 76e89e438f55..c702644f0d1c 100644
--- a/lib/newlib_tests/.gitignore
+++ b/lib/newlib_tests/.gitignore
@@ -20,6 +20,7 @@  tst_res_hexd
 tst_strstatus
 test17
 test18
+test19
 tst_expiration_timer
 test_exec
 test_exec_child
diff --git a/lib/newlib_tests/test19.c b/lib/newlib_tests/test19.c
new file mode 100644
index 000000000000..37fc8e9e0227
--- /dev/null
+++ b/lib/newlib_tests/test19.c
@@ -0,0 +1,48 @@ 
+/*
+ * Copyright (c) 2018, Linux Test Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include "tst_test.h"
+#include "tst_sys_conf.h"
+
+static char orig[1024];
+
+static const char * const save_restore[] = {
+	"?/proc/nonexistent",
+	"!/proc/sys/kernel/numa_balancing",
+	"/proc/sys/kernel/core_pattern",
+	NULL,
+};
+
+static void setup(void)
+{
+	SAFE_FILE_SCANF("/proc/sys/kernel/core_pattern", "%s", orig);
+	SAFE_FILE_PRINTF("/proc/sys/kernel/core_pattern", "changed");
+}
+
+static void run(void)
+{
+	tst_res(TPASS, "OK");
+}
+
+static struct tst_test test = {
+	.needs_root = 1,
+	.test_all = run,
+	.setup = setup,
+	.save_restore = save_restore,
+};
diff --git a/lib/tst_sys_conf.c b/lib/tst_sys_conf.c
new file mode 100644
index 000000000000..39a2ce475f76
--- /dev/null
+++ b/lib/tst_sys_conf.c
@@ -0,0 +1,112 @@ 
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek <jstancek@redhat.com>
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_sys_conf.h"
+
+struct tst_sys_conf *tst_sys_conf_new(void)
+{
+	struct tst_sys_conf *p = SAFE_MALLOC(sizeof(*p));
+
+	p->prev = p;
+	p->next = p;
+
+	return p;
+}
+
+void tst_sys_conf_dump(struct tst_sys_conf *c)
+{
+	struct tst_sys_conf *i = c->next;
+
+	while (i != c) {
+		tst_res(TINFO, "%s -> %s", i->path, i->value);
+		i = i->next;
+	}
+}
+
+int tst_sys_conf_save_str(struct tst_sys_conf *c, const char *path,
+			  const char *value)
+{
+	struct tst_sys_conf *n = SAFE_MALLOC(sizeof(*n));
+
+	strncpy(n->path, path, sizeof(n->path));
+	strncpy(n->value, value, sizeof(n->value));
+
+	/* add new entry at the beginning, right after 'c' */
+	n->next = c->next;
+	c->next->prev = n;
+
+	c->next = n;
+	n->prev = c;
+
+	return 0;
+}
+
+int tst_sys_conf_save(struct tst_sys_conf *c, const char *path)
+{
+	char line[PATH_MAX];
+	FILE *fp;
+	void *ret;
+	char flag;
+
+	if (!path)
+		tst_brk(TBROK, "path is empty");
+
+	flag = path[0];
+	if (flag  == '?' || flag == '!')
+		path++;
+
+	if (access(path, F_OK) != 0) {
+		switch (flag) {
+		case '?':
+			tst_res(TINFO, "Path not found: '%s'", path);
+			break;
+		case '!':
+			tst_brk(TBROK|TERRNO, "Path not found: '%s'", path);
+			break;
+		default:
+			tst_brk(TCONF|TERRNO, "Path not found: '%s'", path);
+		}
+		return 1;
+	}
+
+	fp = fopen(path, "r");
+	if (fp == NULL) {
+		tst_brk(TBROK | TERRNO, "Failed to open FILE '%s' for reading",
+			path);
+		return 1;
+	}
+
+	ret = fgets(line, sizeof(line), fp);
+	fclose(fp);
+
+	if (ret == NULL) {
+		tst_brk(TBROK | TERRNO, "Failed to read anything from '%s'",
+			path);
+	}
+
+	return tst_sys_conf_save_str(c, path, line);
+}
+
+void tst_sys_conf_restore(struct tst_sys_conf *c, int verbose)
+{
+	struct tst_sys_conf *i = c->next;
+
+	while (i != c) {
+		if (verbose) {
+			tst_res(TINFO, "Restoring conf.: %s -> %s\n",
+				i->path, i->value);
+		}
+		FILE_PRINTF(i->path, "%s", i->value);
+		i = i->next;
+	}
+}
+
diff --git a/lib/tst_test.c b/lib/tst_test.c
index 117e61462d30..8ca1aa8e9ffc 100644
--- a/lib/tst_test.c
+++ b/lib/tst_test.c
@@ -35,6 +35,7 @@ 
 #include "tst_timer_test.h"
 #include "tst_clocks.h"
 #include "tst_timer.h"
+#include "tst_sys_conf.h"
 
 #include "old_resource.h"
 #include "old_device.h"
@@ -48,6 +49,7 @@  static float duration = -1;
 static pid_t main_pid, lib_pid;
 static int mntpoint_mounted;
 static struct timespec tst_start_time; /* valid only for test pid */
+static TST_SYS_CONF_INIT(save_restore_data);
 
 struct results {
 	int passed;
@@ -809,6 +811,15 @@  static void do_setup(int argc, char *argv[])
 	if (needs_tmpdir() && !tst_tmpdir_created())
 		tst_tmpdir();
 
+	if (tst_test->save_restore) {
+		const char * const *name = tst_test->save_restore;
+
+		while (*name) {
+			tst_sys_conf_save(&save_restore_data, *name);
+			name++;
+		}
+	}
+
 	if (tst_test->mntpoint)
 		SAFE_MKDIR(tst_test->mntpoint, 0777);
 
@@ -885,6 +896,9 @@  static void do_cleanup(void)
 		tst_rmdir();
 	}
 
+	if (tst_test->save_restore)
+		tst_sys_conf_restore(&save_restore_data, 0);
+
 	cleanup_ipc();
 }