Skip to contentSkip to conversation

lib: new tst_test field to save and restore proc|sys

PATCH Superseded Jan Stancek 2 participants 3 messages Nov 5, 2018 – Nov 6, 2018 Library code

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 lib: new tst_test field to save and restore proc|sys

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.

---
Changes in v3:
- make list single linked
- drop list parameter, all functions use one single list

 doc/test-writing-guidelines.txt |  37 +++++++++++++++
 include/tst_sys_conf.h          |  21 +++++++++
 include/tst_test.h              |   7 +++
 lib/newlib_tests/.gitignore     |   1 +
 lib/newlib_tests/test19.c       |  48 +++++++++++++++++++
 lib/tst_sys_conf.c              | 100 ++++++++++++++++++++++++++++++++++++++++
 lib/tst_test.c                  |  13 ++++++
 7 files changed, 227 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
doc/test-writing-guidelines.txt +37 -0
index f590896472d1..d0b91c36294c 100644
@@ -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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
include/tst_sys_conf.h new file +21 -0
new file mode 100644
index 000000000000..784a94dbe37c
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek <[email protected]>
+ */
+
+#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 *next;
+};
+
+int tst_sys_conf_save_str(const char *path, const char *value);
+int tst_sys_conf_save(const char *path);
+void tst_sys_conf_restore(int verbose);
+void tst_sys_conf_dump(void);
+
+#endif
include/tst_test.h +7 -0
index 080b0171d5f2..2ebf746eb720 100644
@@ -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;
 };
 
 /*
lib/newlib_tests/.gitignore +1 -0
index 76e89e438f55..c702644f0d1c 100644
@@ -20,6 +20,7 @@ tst_res_hexd
 tst_strstatus
 test17
 test18
+test19
 tst_expiration_timer
 test_exec
 test_exec_child
lib/newlib_tests/test19.c new file +48 -0

48 lines not on this page · raw message

lib/tst_sys_conf.c new file +100 -0

100 lines not on this page · raw message

lib/tst_test.c +13 -0
index 117e61462d30..661fbbfce003 100644
@@ -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"
@@ -809,6 +810,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(*name);
+			name++;
+		}
+	}
+
 	if (tst_test->mntpoint)
 		SAFE_MKDIR(tst_test->mntpoint, 0777);
 
@@ -885,6 +895,9 @@ static void do_cleanup(void)
 		tst_rmdir();
 	}
 
+	if (tst_test->save_restore)
+		tst_sys_conf_restore(0);
+
 	cleanup_ipc();
 }
 
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH v3 1/1 lib: new tst_test field to save and restore proc|sys

Quoted 168 lines · Jan Stancek

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.

---
Changes in v3:
- make list single linked
- drop list parameter, all functions use one single list

  doc/test-writing-guidelines.txt |  37 +++++++++++++++
  include/tst_sys_conf.h          |  21 +++++++++
  include/tst_test.h              |   7 +++
  lib/newlib_tests/.gitignore     |   1 +
  lib/newlib_tests/test19.c       |  48 +++++++++++++++++++
  lib/tst_sys_conf.c              | 100 ++++++++++++++++++++++++++++++++++++++++
  lib/tst_test.c                  |  13 ++++++
  7 files changed, 227 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

doc/test-writing-guidelines.txt +37 -0
index f590896472d1..d0b91c36294c 100644
@@ -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
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
include/tst_sys_conf.h new file +21 -0
new file mode 100644
index 000000000000..784a94dbe37c
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek<[email protected]>
+ */
+
+#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 *next;
+};
+
+int tst_sys_conf_save_str(const char *path, const char *value);
+int tst_sys_conf_save(const char *path);
+void tst_sys_conf_restore(int verbose);
+void tst_sys_conf_dump(void);
+
+#endif
include/tst_test.h +7 -0
index 080b0171d5f2..2ebf746eb720 100644
@@ -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;
  };
 
  /*
lib/newlib_tests/.gitignore +1 -0
index 76e89e438f55..c702644f0d1c 100644
@@ -20,6 +20,7 @@ tst_res_hexd
  tst_strstatus
  test17
  test18
+test19
  tst_expiration_timer
  test_exec
  test_exec_child
lib/newlib_tests/test19.c new file +26 -0
new file mode 100644
index 000000000000..37fc8e9e0227
@@ -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",

lib/newlib_tests/test19.c line 36

Quoted 10 lines
lib/newlib_tests/test19.c new file +10 -0
+	"!/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");
+}

Hi Jan,

Why do you save the value of core_pattern by SAFE_FILE_SCANF() ? It seems that the .save_restore flag has done it.

Quoted 42 lines
lib/newlib_tests/test19.c new file +12 -0
+
+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,
+};
lib/tst_sys_conf.c new file +24 -0
new file mode 100644
index 000000000000..893ef9d5c74b
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek<[email protected]>
+ */
+
+#include<limits.h>
+#include<stdio.h>
+#include<unistd.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_sys_conf.h"
+
+static struct tst_sys_conf save_restore_data;
+
+void tst_sys_conf_dump(void)
+{
+	struct tst_sys_conf *i = save_restore_data.next;
+
+	while (i) {
+		tst_res(TINFO, "%s ->  %s", i->path, i->value);
+		i = i->next;
+	}

lib/tst_sys_conf.c line 34

Quoted 10 lines
lib/tst_sys_conf.c new file +10 -0
+}
+
+int tst_sys_conf_save_str(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' */

Does this comment need to be updated?

Best Regards,
Xiao Yang

Quoted 104 lines
lib/tst_sys_conf.c new file +66 -0
+	n->next = save_restore_data.next;
+	save_restore_data.next = n;
+
+	return 0;
+}
+
+int tst_sys_conf_save(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(path, line);
+}
+
+void tst_sys_conf_restore(int verbose)
+{
+	struct tst_sys_conf *i = save_restore_data.next;
+
+	while (i) {
+		if (verbose) {
+			tst_res(TINFO, "Restoring conf.: %s ->  %s\n",
+				i->path, i->value);
+		}
+		FILE_PRINTF(i->path, "%s", i->value);
+		i = i->next;
+	}
+}
+
lib/tst_test.c +13 -0
index 117e61462d30..661fbbfce003 100644
@@ -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"
@@ -809,6 +810,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(*name);
+			name++;
+		}
+	}
+
  	if (tst_test->mntpoint)
  		SAFE_MKDIR(tst_test->mntpoint, 0777);
 
@@ -885,6 +895,9 @@ static void do_cleanup(void)
  		tst_rmdir();
  	}
 
+	if (tst_test->save_restore)
+		tst_sys_conf_restore(0);
+
  	cleanup_ipc();
  }
 
#2 lore raw

PATCH v3 1/1 lib: new tst_test field to save and restore proc|sys

----- Original Message -----

Quoted 184 lines · Jan Stancek
Quoted 171 lines

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.

---
Changes in v3:
- make list single linked
- drop list parameter, all functions use one single list

  doc/test-writing-guidelines.txt |  37 +++++++++++++++
  include/tst_sys_conf.h          |  21 +++++++++
  include/tst_test.h              |   7 +++
  lib/newlib_tests/.gitignore     |   1 +
  lib/newlib_tests/test19.c       |  48 +++++++++++++++++++
  lib/tst_sys_conf.c              | 100
  ++++++++++++++++++++++++++++++++++++++++
  lib/tst_test.c                  |  13 ++++++
  7 files changed, 227 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

doc/test-writing-guidelines.txt +37 -0
index f590896472d1..d0b91c36294c 100644
@@ -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
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/tst_sys_conf.h new file +21 -0
new file mode 100644
index 000000000000..784a94dbe37c
@@ -0,0 +1,21 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek<[email protected]>
+ */
+
+#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 *next;
+};
+
+int tst_sys_conf_save_str(const char *path, const char *value);
+int tst_sys_conf_save(const char *path);
+void tst_sys_conf_restore(int verbose);
+void tst_sys_conf_dump(void);
+
+#endif
include/tst_test.h +7 -0
index 080b0171d5f2..2ebf746eb720 100644
@@ -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;
  };
 
  /*
lib/newlib_tests/.gitignore +1 -0
index 76e89e438f55..c702644f0d1c 100644
@@ -20,6 +20,7 @@ tst_res_hexd
  tst_strstatus
  test17
  test18
+test19
  tst_expiration_timer
  test_exec
  test_exec_child
lib/newlib_tests/test19.c new file +26 -0
new file mode 100644
index 000000000000..37fc8e9e0227
@@ -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",

lib/newlib_tests/test19.c line 36

Quoted 10 lines
lib/newlib_tests/test19.c new file +10 -0
+	"!/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");
+}

Hi Jan,

Quoted 2 lines

Why do you save the value of core_pattern by SAFE_FILE_SCANF() ? It seems that the .save_restore flag has done it.

Old code was checking that value has been restored properly, but that was before save/restore moved to lib.

You're right to point out, that it's useless now.

Quoted 45 lines
Quoted 45 lines
lib/newlib_tests/test19.c new file +12 -0
+
+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,
+};
lib/tst_sys_conf.c new file +27 -0
new file mode 100644
index 000000000000..893ef9d5c74b
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright (c) 2018 Jan Stancek<[email protected]>
+ */
+
+#include<limits.h>
+#include<stdio.h>
+#include<unistd.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_sys_conf.h"
+
+static struct tst_sys_conf save_restore_data;
+
+void tst_sys_conf_dump(void)
+{
+	struct tst_sys_conf *i = save_restore_data.next;
+
+	while (i) {
+		tst_res(TINFO, "%s ->  %s", i->path, i->value);
+		i = i->next;
+	}
+}
+
+int tst_sys_conf_save_str(const char *path, const char *value)
Quoted 8 lines

lib/tst_sys_conf.c line 34

Quoted 7 lines
lib/tst_sys_conf.c new file +7 -0
+{
+	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' */

Does this comment need to be updated?

I'll drop this too, v3 is easier to follow with single linked list.

Regards,
Jan

Quoted 111 lines

Best Regards,
Xiao Yang

Quoted 104 lines
lib/tst_sys_conf.c new file +66 -0
+	n->next = save_restore_data.next;
+	save_restore_data.next = n;
+
+	return 0;
+}
+
+int tst_sys_conf_save(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(path, line);
+}
+
+void tst_sys_conf_restore(int verbose)
+{
+	struct tst_sys_conf *i = save_restore_data.next;
+
+	while (i) {
+		if (verbose) {
+			tst_res(TINFO, "Restoring conf.: %s ->  %s\n",
+				i->path, i->value);
+		}
+		FILE_PRINTF(i->path, "%s", i->value);
+		i = i->next;
+	}
+}
+
lib/tst_test.c +13 -0
index 117e61462d30..661fbbfce003 100644
@@ -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"
@@ -809,6 +810,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(*name);
+			name++;
+		}
+	}
+
  	if (tst_test->mntpoint)
  		SAFE_MKDIR(tst_test->mntpoint, 0777);
 
@@ -885,6 +895,9 @@ static void do_cleanup(void)
  		tst_rmdir();
  	}
 
+	if (tst_test->save_restore)
+		tst_sys_conf_restore(0);
+
  	cleanup_ipc();
  }