Jan Rybar ef4d31
diff -up ./NEWS.ori ./NEWS
Jan Rybar ef4d31
diff -up ./sysctl.c.ori ./sysctl.c
Jan Rybar ef4d31
--- ./sysctl.c.ori	2021-02-09 11:11:25.000000000 +0100
Jan Rybar ef4d31
+++ ./sysctl.c	2022-07-29 15:50:50.767380061 +0200
Jan Rybar ef4d31
@@ -17,6 +17,7 @@
Jan Rybar ef4d31
  * along with this program; if not, write to the Free Software
Jan Rybar ef4d31
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Jan Rybar ef4d31
  *
Jan Rybar ef4d31
+ * Part of this code comes from systemd, especially sysctl.c
Jan Rybar ef4d31
  * Changelog:
Jan Rybar ef4d31
  *            v1.01:
Jan Rybar ef4d31
  *                   - added -p <preload> to preload values from a file
Jan Rybar ef4d31
@@ -40,6 +41,7 @@
Jan Rybar ef4d31
 #include <sys/stat.h>
Jan Rybar ef4d31
 #include <sys/types.h>
Jan Rybar ef4d31
 #include <unistd.h>
Jan Rybar ef4d31
+#include <ctype.h>
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 #include "c.h"
Jan Rybar ef4d31
 #include "fileutils.h"
Jan Rybar ef4d31
@@ -66,12 +68,34 @@ static bool PrintName;
Jan Rybar ef4d31
 static bool PrintNewline;
Jan Rybar ef4d31
 static bool IgnoreError;
Jan Rybar ef4d31
 static bool Quiet;
Jan Rybar ef4d31
+static bool DryRun;
Jan Rybar ef4d31
 static char *pattern;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 #define LINELEN 4096
Jan Rybar ef4d31
 static char *iobuf;
Jan Rybar ef4d31
 static size_t iolen = LINELEN;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
+typedef struct SysctlSetting {
Jan Rybar ef4d31
+    char *key;
Jan Rybar ef4d31
+    char *path;
Jan Rybar ef4d31
+    char *value;
Jan Rybar ef4d31
+    bool ignore_failure;
Jan Rybar ef4d31
+    bool glob_exclude;
Jan Rybar ef4d31
+    struct SysctlSetting *next;
Jan Rybar ef4d31
+} SysctlSetting;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+typedef struct SettingList {
Jan Rybar ef4d31
+    struct SysctlSetting *head;
Jan Rybar ef4d31
+    struct SysctlSetting *tail;
Jan Rybar ef4d31
+} SettingList;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+#define GLOB_CHARS "*?["
Jan Rybar ef4d31
+static inline bool string_is_glob(const char *p)
Jan Rybar ef4d31
+{
Jan Rybar ef4d31
+    return !!strpbrk(p, GLOB_CHARS);
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+
Jan Rybar ef4d31
 /* Function prototypes. */
Jan Rybar ef4d31
 static int pattern_match(const char *string, const char *pat);
Jan Rybar ef4d31
 static int DisplayAll(const char *restrict const path);
Jan Rybar ef4d31
@@ -100,6 +124,81 @@ static void slashdot(char *restrict p, c
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 }
Jan Rybar ef4d31
 
Jan Rybar ef4d31
+static void setting_free(SysctlSetting *s) {
Jan Rybar ef4d31
+    if (!s)
Jan Rybar ef4d31
+	return;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    free(s->key);
Jan Rybar ef4d31
+    free(s->path);
Jan Rybar ef4d31
+    free(s->value);
Jan Rybar ef4d31
+    free(s);
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+static SysctlSetting *setting_new(
Jan Rybar ef4d31
+	const char *key,
Jan Rybar ef4d31
+	const char *value,
Jan Rybar ef4d31
+	bool ignore_failure,
Jan Rybar ef4d31
+    bool glob_exclude) {
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    SysctlSetting *s = NULL;
Jan Rybar ef4d31
+    char *path = NULL;
Jan Rybar ef4d31
+    int proc_len;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    proc_len = strlen(PROC_PATH);
Jan Rybar ef4d31
+    /* used to open the file */
Jan Rybar ef4d31
+    path = xmalloc(strlen(key) + proc_len + 2);
Jan Rybar ef4d31
+    strcpy(path, PROC_PATH);
Jan Rybar ef4d31
+    if (key[0] == '-')
Jan Rybar ef4d31
+        strcat(path + proc_len, key+1);
Jan Rybar ef4d31
+    else
Jan Rybar ef4d31
+        strcat(path + proc_len, key);
Jan Rybar ef4d31
+    /* change . to / */
Jan Rybar ef4d31
+    slashdot(path + proc_len, '.', '/');
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    s = xmalloc(sizeof(SysctlSetting));
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    *s = (SysctlSetting) {
Jan Rybar ef4d31
+        .key = strdup(key),
Jan Rybar ef4d31
+        .path = path,
Jan Rybar ef4d31
+        .value = value? strdup(value): NULL,
Jan Rybar ef4d31
+        .ignore_failure = ignore_failure,
Jan Rybar ef4d31
+        .glob_exclude = glob_exclude,
Jan Rybar ef4d31
+        .next = NULL,
Jan Rybar ef4d31
+    };
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    return s;
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+static void settinglist_add(SettingList *l, SysctlSetting *s) {
Jan Rybar ef4d31
+    SysctlSetting *old_tail;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if (!l)
Jan Rybar ef4d31
+        return;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if (l->head == NULL)
Jan Rybar ef4d31
+        l->head = s;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if (l->tail != NULL) {
Jan Rybar ef4d31
+        old_tail = l->tail;
Jan Rybar ef4d31
+        old_tail->next = s;
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+    l->tail = s;
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+static SysctlSetting *settinglist_findpath(const SettingList *l, const char *path) {
Jan Rybar ef4d31
+    SysctlSetting *node;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    for (node=l->head; node != NULL; node = node->next) {
Jan Rybar ef4d31
+        if (strcmp(node->path, path) == 0)
Jan Rybar ef4d31
+            return node;
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+    return NULL;
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+/* Function prototypes. */
Jan Rybar ef4d31
+static int pattern_match(const char *string, const char *pat);
Jan Rybar ef4d31
+static int DisplayAll(const char *restrict const path);
Jan Rybar ef4d31
+
Jan Rybar ef4d31
 /*
Jan Rybar ef4d31
  * Display the usage format
Jan Rybar ef4d31
  */
Jan Rybar ef4d31
@@ -115,6 +214,7 @@ static void __attribute__ ((__noreturn__
Jan Rybar ef4d31
 	fputs(_("  -A                   alias of -a\n"), out);
Jan Rybar ef4d31
 	fputs(_("  -X                   alias of -a\n"), out);
Jan Rybar ef4d31
 	fputs(_("      --deprecated     include deprecated parameters to listing\n"), out);
Jan Rybar ef4d31
+	fputs(_("      --dry-run        Print the key and values but do not write\n"), out);
Jan Rybar ef4d31
 	fputs(_("  -b, --binary         print value without new line\n"), out);
Jan Rybar ef4d31
 	fputs(_("  -e, --ignore         ignore unknown variables errors\n"), out);
Jan Rybar ef4d31
 	fputs(_("  -N, --names          print variable names without values\n"), out);
Jan Rybar ef4d31
@@ -138,6 +238,39 @@ static void __attribute__ ((__noreturn__
Jan Rybar ef4d31
 }
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 /*
Jan Rybar ef4d31
+ * Strip left/leading side of a string
Jan Rybar ef4d31
+ */
Jan Rybar ef4d31
+static char *lstrip(char *line)
Jan Rybar ef4d31
+{
Jan Rybar ef4d31
+    char *start;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if (!line || !*line)
Jan Rybar ef4d31
+        return line;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    start = line;
Jan Rybar ef4d31
+    while(isspace(*start)) start++;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    return start;
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+/*
Jan Rybar ef4d31
+ * Strip right/trailing side of a string
Jan Rybar ef4d31
+ * by placing a \0
Jan Rybar ef4d31
+ */
Jan Rybar ef4d31
+static void rstrip(char *line)
Jan Rybar ef4d31
+{
Jan Rybar ef4d31
+    char *end;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if (!line || !*line)
Jan Rybar ef4d31
+        return;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    end = line + strlen(line) - 1;
Jan Rybar ef4d31
+    while(end > line && isspace(*end)) end--;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    end[1] = '\0';
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+/*
Jan Rybar ef4d31
  * Strip the leading and trailing spaces from a string
Jan Rybar ef4d31
  */
Jan Rybar ef4d31
 static char *StripLeadingAndTrailingSpaces(char *oneline)
Jan Rybar ef4d31
@@ -166,7 +299,7 @@ static char *StripLeadingAndTrailingSpac
Jan Rybar ef4d31
  */
Jan Rybar ef4d31
 static int ReadSetting(const char *restrict const name)
Jan Rybar ef4d31
 {
Jan Rybar ef4d31
-	int rc = 0;
Jan Rybar ef4d31
+	int rc = EXIT_SUCCESS;
Jan Rybar ef4d31
 	char *restrict tmpname;
Jan Rybar ef4d31
 	char *restrict outname;
Jan Rybar ef4d31
 	ssize_t rlen;
Jan Rybar ef4d31
@@ -198,7 +331,7 @@ static int ReadSetting(const char *restr
Jan Rybar ef4d31
 	if (stat(tmpname, &ts) < 0) {
Jan Rybar ef4d31
 		if (!IgnoreError) {
Jan Rybar ef4d31
 			xwarn(_("cannot stat %s"), tmpname);
Jan Rybar ef4d31
-			rc = -1;
Jan Rybar ef4d31
+			rc = EXIT_FAILURE;
Jan Rybar ef4d31
 		}
Jan Rybar ef4d31
 		goto out;
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
@@ -215,7 +348,7 @@ static int ReadSetting(const char *restr
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	if (pattern && !pattern_match(outname, pattern)) {
Jan Rybar ef4d31
-		rc = 0;
Jan Rybar ef4d31
+		rc = EXIT_SUCCESS;
Jan Rybar ef4d31
 		goto out;
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
@@ -231,19 +364,19 @@ static int ReadSetting(const char *restr
Jan Rybar ef4d31
 		case ENOENT:
Jan Rybar ef4d31
 			if (!IgnoreError) {
Jan Rybar ef4d31
 				xwarnx(_("\"%s\" is an unknown key"), outname);
Jan Rybar ef4d31
-				rc = -1;
Jan Rybar ef4d31
+				rc = EXIT_FAILURE;
Jan Rybar ef4d31
 			}
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
 		case EACCES:
Jan Rybar ef4d31
 			xwarnx(_("permission denied on key '%s'"), outname);
Jan Rybar ef4d31
-			rc = -1;
Jan Rybar ef4d31
+			rc = EXIT_FAILURE;
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
 		case EIO:	    /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
Jan Rybar ef4d31
-			rc = -1;
Jan Rybar ef4d31
+			rc = EXIT_FAILURE;
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
 		default:
Jan Rybar ef4d31
 			xwarn(_("reading key \"%s\""), outname);
Jan Rybar ef4d31
-			rc = -1;
Jan Rybar ef4d31
+			rc = EXIT_FAILURE;
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
 		}
Jan Rybar ef4d31
 	} else {
Jan Rybar ef4d31
@@ -279,7 +412,7 @@ static int ReadSetting(const char *restr
Jan Rybar ef4d31
 			case EACCES:
Jan Rybar ef4d31
 				xwarnx(_("permission denied on key '%s'"),
Jan Rybar ef4d31
 				       outname);
Jan Rybar ef4d31
-				rc = -1;
Jan Rybar ef4d31
+				rc = EXIT_FAILURE;
Jan Rybar ef4d31
 				break;
Jan Rybar ef4d31
 			case EISDIR: {
Jan Rybar ef4d31
 					size_t len;
Jan Rybar ef4d31
@@ -291,11 +424,11 @@ static int ReadSetting(const char *restr
Jan Rybar ef4d31
 					goto out;
Jan Rybar ef4d31
 				}
Jan Rybar ef4d31
 			case EIO:	    /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
Jan Rybar ef4d31
-				rc = -1;
Jan Rybar ef4d31
+				rc = EXIT_FAILURE;
Jan Rybar ef4d31
 				break;
Jan Rybar ef4d31
 			default:
Jan Rybar ef4d31
 				xwarnx(_("reading key \"%s\""), outname);
Jan Rybar ef4d31
-				rc = -1;
Jan Rybar ef4d31
+				rc = EXIT_FAILURE;
Jan Rybar ef4d31
 			case 0:
Jan Rybar ef4d31
 				break;
Jan Rybar ef4d31
 			}
Jan Rybar ef4d31
@@ -323,7 +456,7 @@ static int is_deprecated(char *filename)
Jan Rybar ef4d31
  */
Jan Rybar ef4d31
 static int DisplayAll(const char *restrict const path)
Jan Rybar ef4d31
 {
Jan Rybar ef4d31
-	int rc = 0;
Jan Rybar ef4d31
+	int rc = EXIT_SUCCESS;
Jan Rybar ef4d31
 	int rc2;
Jan Rybar ef4d31
 	DIR *restrict dp;
Jan Rybar ef4d31
 	struct dirent *restrict de;
Jan Rybar ef4d31
@@ -333,7 +466,7 @@ static int DisplayAll(const char *restri
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	if (!dp) {
Jan Rybar ef4d31
 		xwarnx(_("unable to open directory \"%s\""), path);
Jan Rybar ef4d31
-		rc = -1;
Jan Rybar ef4d31
+		rc = EXIT_FAILURE;
Jan Rybar ef4d31
 	} else {
Jan Rybar ef4d31
 		readdir(dp);	/* skip .  */
Jan Rybar ef4d31
 		readdir(dp);	/* skip .. */
Jan Rybar ef4d31
@@ -369,130 +502,183 @@ static int DisplayAll(const char *restri
Jan Rybar ef4d31
 /*
Jan Rybar ef4d31
  * Write a sysctl setting
Jan Rybar ef4d31
  */
Jan Rybar ef4d31
-static int WriteSetting(const char *setting)
Jan Rybar ef4d31
-{
Jan Rybar ef4d31
-	int rc = 0;
Jan Rybar ef4d31
-	const char *name = setting;
Jan Rybar ef4d31
-	const char *value;
Jan Rybar ef4d31
-	const char *equals;
Jan Rybar ef4d31
-	char *tmpname;
Jan Rybar ef4d31
-	char *outname;
Jan Rybar ef4d31
-	char *last_dot;
Jan Rybar ef4d31
-	bool ignore_failure;
Jan Rybar ef4d31
+static int WriteSetting(
Jan Rybar ef4d31
+    const char *key,
Jan Rybar ef4d31
+    const char *path,
Jan Rybar ef4d31
+    const char *value,
Jan Rybar ef4d31
+    const bool ignore_failure) {
Jan Rybar ef4d31
 
Jan Rybar ef4d31
-	FILE *fp;
Jan Rybar ef4d31
+    int rc = EXIT_SUCCESS;
Jan Rybar ef4d31
+    FILE *fp;
Jan Rybar ef4d31
 	struct stat ts;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
-	if (!name)
Jan Rybar ef4d31
-		/* probably don't want to display this err */
Jan Rybar ef4d31
-		return 0;
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	equals = strchr(setting, '=');
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	if (!equals) {
Jan Rybar ef4d31
-		xwarnx(_("\"%s\" must be of the form name=value"),
Jan Rybar ef4d31
-		       setting);
Jan Rybar ef4d31
-		return -1;
Jan Rybar ef4d31
-	}
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	/* point to the value in name=value */
Jan Rybar ef4d31
-	value = equals + 1;
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	if (!*name || name == equals) {
Jan Rybar ef4d31
-		xwarnx(_("malformed setting \"%s\""), setting);
Jan Rybar ef4d31
-		return -2;
Jan Rybar ef4d31
-	}
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	ignore_failure = name[0] == '-';
Jan Rybar ef4d31
-	if (ignore_failure)
Jan Rybar ef4d31
-	    name++;
Jan Rybar ef4d31
+    if (!key || !path)
Jan Rybar ef4d31
+        return rc;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
-	/* used to open the file */
Jan Rybar ef4d31
-	tmpname = xmalloc(equals - name + 1 + strlen(PROC_PATH));
Jan Rybar ef4d31
-	strcpy(tmpname, PROC_PATH);
Jan Rybar ef4d31
-	strncat(tmpname, name, (int) (equals - name));
Jan Rybar ef4d31
-	tmpname[equals - name + strlen(PROC_PATH)] = 0;
Jan Rybar ef4d31
-	/* change . to / */
Jan Rybar ef4d31
-	slashdot(tmpname + strlen(PROC_PATH), '.', '/');
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	/* used to display the output */
Jan Rybar ef4d31
-	outname = xmalloc(equals - name + 1);
Jan Rybar ef4d31
-	strncpy(outname, name, (int) (equals - name));
Jan Rybar ef4d31
-	outname[equals - name] = 0;
Jan Rybar ef4d31
-	/* change / to . */
Jan Rybar ef4d31
-	slashdot(outname, '/', '.');
Jan Rybar ef4d31
-	last_dot = strrchr(outname, '.');
Jan Rybar ef4d31
-	if (last_dot != NULL && is_deprecated(last_dot + 1)) {
Jan Rybar ef4d31
-		xwarnx(_("%s is deprecated, value not set"), outname);
Jan Rybar ef4d31
-		goto out;
Jan Rybar ef4d31
-        }
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	if (stat(tmpname, &ts) < 0) {
Jan Rybar ef4d31
+	if (stat(path, &ts) < 0) {
Jan Rybar ef4d31
 		if (!IgnoreError) {
Jan Rybar ef4d31
-			xwarn(_("cannot stat %s"), tmpname);
Jan Rybar ef4d31
-			rc = -1;
Jan Rybar ef4d31
+			xwarn(_("cannot stat %s"), path);
Jan Rybar ef4d31
+			rc = EXIT_FAILURE;
Jan Rybar ef4d31
 		}
Jan Rybar ef4d31
-		goto out;
Jan Rybar ef4d31
+        return rc;
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	if ((ts.st_mode & S_IWUSR) == 0) {
Jan Rybar ef4d31
-		xwarn(_("setting key \"%s\""), outname);
Jan Rybar ef4d31
-		goto out;
Jan Rybar ef4d31
+		xwarn(_("setting key \"%s\""), key);
Jan Rybar ef4d31
+        return rc;
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	if (S_ISDIR(ts.st_mode)) {
Jan Rybar ef4d31
-		xwarn(_("setting key \"%s\""), outname);
Jan Rybar ef4d31
-		goto out;
Jan Rybar ef4d31
+		xwarn(_("setting key \"%s\""), key);
Jan Rybar ef4d31
+        return rc;
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
-	fp = fprocopen(tmpname, "w");
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-	if (!fp) {
Jan Rybar ef4d31
-		switch (errno) {
Jan Rybar ef4d31
-		case ENOENT:
Jan Rybar ef4d31
-			if (!IgnoreError) {
Jan Rybar ef4d31
-				xwarnx(_("\"%s\" is an unknown key%s"), outname, (ignore_failure?_(", ignoring"):""));
Jan Rybar ef4d31
+    if (!DryRun) {
Jan Rybar ef4d31
+        if ((fp = fprocopen(path, "w")) == NULL) {
Jan Rybar ef4d31
+            switch (errno) {
Jan Rybar ef4d31
+            case ENOENT:
Jan Rybar ef4d31
+                if (!IgnoreError) {
Jan Rybar ef4d31
+                    xwarnx(_("\"%s\" is an unknown key%s"),
Jan Rybar ef4d31
+                           key, (ignore_failure?_(", ignoring"):""));
Jan Rybar ef4d31
 				if (!ignore_failure)
Jan Rybar ef4d31
-				    rc = -1;
Jan Rybar ef4d31
+				    rc = EXIT_FAILURE;
Jan Rybar ef4d31
 			}
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
-		case EPERM:
Jan Rybar ef4d31
-		case EROFS:
Jan Rybar ef4d31
-		case EACCES:
Jan Rybar ef4d31
-			xwarnx(_("permission denied on key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
Jan Rybar ef4d31
-			break;
Jan Rybar ef4d31
-		default:
Jan Rybar ef4d31
-			xwarn(_("setting key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
Jan Rybar ef4d31
-			break;
Jan Rybar ef4d31
-		}
Jan Rybar ef4d31
-		if (!ignore_failure && errno != ENOENT)
Jan Rybar ef4d31
-		    rc = -1;
Jan Rybar ef4d31
-	} else {
Jan Rybar ef4d31
-		rc = fprintf(fp, "%s\n", value);
Jan Rybar ef4d31
-		if (0 < rc)
Jan Rybar ef4d31
-			rc = 0;
Jan Rybar ef4d31
-		if (close_stream(fp) != 0)
Jan Rybar ef4d31
-			xwarn(_("setting key \"%s\""), outname);
Jan Rybar ef4d31
-		else if (rc == 0 && !Quiet) {
Jan Rybar ef4d31
-			if (NameOnly) {
Jan Rybar ef4d31
-				fprintf(stdout, "%s\n", outname);
Jan Rybar ef4d31
-			} else {
Jan Rybar ef4d31
-				if (PrintName) {
Jan Rybar ef4d31
-					fprintf(stdout, "%s = %s\n",
Jan Rybar ef4d31
-						outname, value);
Jan Rybar ef4d31
-				} else {
Jan Rybar ef4d31
-					if (PrintNewline)
Jan Rybar ef4d31
-						fprintf(stdout, "%s\n", value);
Jan Rybar ef4d31
-					else
Jan Rybar ef4d31
-						fprintf(stdout, "%s", value);
Jan Rybar ef4d31
-				}
Jan Rybar ef4d31
-			}
Jan Rybar ef4d31
-		}
Jan Rybar ef4d31
-	}
Jan Rybar ef4d31
-      out:
Jan Rybar ef4d31
-	free(tmpname);
Jan Rybar ef4d31
-	free(outname);
Jan Rybar ef4d31
-	return rc;
Jan Rybar ef4d31
+            case EPERM:
Jan Rybar ef4d31
+            case EROFS:
Jan Rybar ef4d31
+            case EACCES:
Jan Rybar ef4d31
+                xwarnx(_("permission denied on key \"%s\"%s"),
Jan Rybar ef4d31
+                       key, (ignore_failure?_(", ignoring"):""));
Jan Rybar ef4d31
+                break;
Jan Rybar ef4d31
+            default:
Jan Rybar ef4d31
+                xwarn(_("setting key \"%s\"%s"),
Jan Rybar ef4d31
+                      key, (ignore_failure?_(", ignoring"):""));
Jan Rybar ef4d31
+                break;
Jan Rybar ef4d31
+            }
Jan Rybar ef4d31
+            if (!ignore_failure && errno != ENOENT)
Jan Rybar ef4d31
+		    rc = EXIT_FAILURE;
Jan Rybar ef4d31
+        } else {
Jan Rybar ef4d31
+	    if (0 < fprintf(fp, "%s\n", value))
Jan Rybar ef4d31
+		rc = EXIT_SUCCESS;
Jan Rybar ef4d31
+            if (close_stream(fp) != 0) {
Jan Rybar ef4d31
+                xwarn(_("setting key \"%s\""), path);
Jan Rybar ef4d31
+                return rc;
Jan Rybar ef4d31
+            }
Jan Rybar ef4d31
+        }
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+    if ((rc == EXIT_SUCCESS && !Quiet) || DryRun) {
Jan Rybar ef4d31
+        if (NameOnly) {
Jan Rybar ef4d31
+            printf("%s\n", value);
Jan Rybar ef4d31
+        } else {
Jan Rybar ef4d31
+            if (PrintName) {
Jan Rybar ef4d31
+                printf("%s = %s\n", path, value);
Jan Rybar ef4d31
+            } else {
Jan Rybar ef4d31
+                if (PrintNewline)
Jan Rybar ef4d31
+                    printf("%s\n", value);
Jan Rybar ef4d31
+                else
Jan Rybar ef4d31
+                    printf("%s", value);
Jan Rybar ef4d31
+            }
Jan Rybar ef4d31
+        }
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+    return rc;
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+/*
Jan Rybar ef4d31
+ * parse each configuration line, there are multiple ways of specifying
Jan Rybar ef4d31
+ * a key/value here:
Jan Rybar ef4d31
+ *
Jan Rybar ef4d31
+ * key = value                               simple setting
Jan Rybar ef4d31
+ * -key = value                              ignore errors
Jan Rybar ef4d31
+ * key.pattern.*.with.glob = value           set keys that match glob
Jan Rybar ef4d31
+ * -key.pattern.exclude.with.glob            dont set this value
Jan Rybar ef4d31
+ * key.pattern.override.with.glob = value    set this glob match to value
Jan Rybar ef4d31
+ *
Jan Rybar ef4d31
+ */
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+static SysctlSetting *parse_setting_line(
Jan Rybar ef4d31
+    const char *path,
Jan Rybar ef4d31
+    const int linenum,
Jan Rybar ef4d31
+    char *line)
Jan Rybar ef4d31
+{
Jan Rybar ef4d31
+    SysctlSetting *s;
Jan Rybar ef4d31
+    char *key;
Jan Rybar ef4d31
+    char *value;
Jan Rybar ef4d31
+    bool glob_exclude = FALSE;
Jan Rybar ef4d31
+    bool ignore_failure = FALSE;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    key = lstrip(line);
Jan Rybar ef4d31
+    if (strlen(key) < 2)
Jan Rybar ef4d31
+        return NULL;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    /* skip over comments */
Jan Rybar ef4d31
+    if (key[0] == '#' || key[0] == ';')
Jan Rybar ef4d31
+        return NULL;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if (pattern && !pattern_match(key, pattern))
Jan Rybar ef4d31
+        return NULL;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    value = strchr(key, '=');
Jan Rybar ef4d31
+    if (value == NULL) {
Jan Rybar ef4d31
+        if (key[0] == '-') {
Jan Rybar ef4d31
+            glob_exclude = TRUE;
Jan Rybar ef4d31
+            key++;
Jan Rybar ef4d31
+            value = NULL;
Jan Rybar ef4d31
+            rstrip(key);
Jan Rybar ef4d31
+        } else {
Jan Rybar ef4d31
+            xwarnx(_("%s(%d): invalid syntax, continuing..."),
Jan Rybar ef4d31
+                   path, linenum);
Jan Rybar ef4d31
+            return NULL;
Jan Rybar ef4d31
+        }
Jan Rybar ef4d31
+    } else {
Jan Rybar ef4d31
+        value[0]='\0';
Jan Rybar ef4d31
+        if (key[0] == '-') {
Jan Rybar ef4d31
+            ignore_failure = TRUE;
Jan Rybar ef4d31
+            key++;
Jan Rybar ef4d31
+        }
Jan Rybar ef4d31
+        value++; // skip over =
Jan Rybar ef4d31
+        value=lstrip(value);
Jan Rybar ef4d31
+        rstrip(value);
Jan Rybar ef4d31
+        rstrip(key);
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+    return setting_new(key, value, ignore_failure, glob_exclude);
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+/* Go through the setting list, expand and sort out
Jan Rybar ef4d31
+ * setting globs and actually write the settings out
Jan Rybar ef4d31
+ */
Jan Rybar ef4d31
+static int write_setting_list(const SettingList *sl)
Jan Rybar ef4d31
+{
Jan Rybar ef4d31
+    SysctlSetting *node;
Jan Rybar ef4d31
+    int rc = EXIT_SUCCESS;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    for (node=sl->head; node != NULL; node=node->next) {
Jan Rybar ef4d31
+        if (node->glob_exclude)
Jan Rybar ef4d31
+            continue;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+        if (string_is_glob(node->path)) {
Jan Rybar ef4d31
+            char *gl_path;
Jan Rybar ef4d31
+            glob_t globbuf;
Jan Rybar ef4d31
+            int i;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+            if (glob(node->path, 0, NULL, &globbuf) != 0)
Jan Rybar ef4d31
+                continue;
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+            for(i=0; i < globbuf.gl_pathc; i++) {
Jan Rybar ef4d31
+                if (settinglist_findpath(sl, globbuf.gl_pathv[i]))
Jan Rybar ef4d31
+                    continue; // override or exclude
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+                rc |= WriteSetting(node->key, globbuf.gl_pathv[i], node->value,
Jan Rybar ef4d31
+                                   node->ignore_failure);
Jan Rybar ef4d31
+            }
Jan Rybar ef4d31
+        } else {
Jan Rybar ef4d31
+            rc |= WriteSetting(node->key, node->path, node->value,
Jan Rybar ef4d31
+                               node->ignore_failure);
Jan Rybar ef4d31
+        }
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    return rc;
Jan Rybar ef4d31
 }
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 static int pattern_match(const char *string, const char *pat)
Jan Rybar ef4d31
@@ -513,12 +699,12 @@ static int pattern_match(const char *str
Jan Rybar ef4d31
  * Preload the sysctl's from the conf file.  We parse the file and then
Jan Rybar ef4d31
  * reform it (strip out whitespace).
Jan Rybar ef4d31
  */
Jan Rybar ef4d31
-static int Preload(const char *restrict const filename)
Jan Rybar ef4d31
+static int Preload(SettingList *setlist, const char *restrict const filename)
Jan Rybar ef4d31
 {
Jan Rybar ef4d31
 	FILE *fp;
Jan Rybar ef4d31
 	char *t;
Jan Rybar ef4d31
 	int n = 0;
Jan Rybar ef4d31
-	int rc = 0;
Jan Rybar ef4d31
+	int rc = EXIT_SUCCESS;
Jan Rybar ef4d31
 	ssize_t rlen;
Jan Rybar ef4d31
 	char *name, *value;
Jan Rybar ef4d31
 	glob_t globbuf;
Jan Rybar ef4d31
@@ -547,62 +733,26 @@ static int Preload(const char *restrict
Jan Rybar ef4d31
 		    ? stdin : fopen(globbuf.gl_pathv[j], "r");
Jan Rybar ef4d31
 		if (!fp) {
Jan Rybar ef4d31
 			xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
Jan Rybar ef4d31
-			rc = -1;
Jan Rybar ef4d31
-			goto out;
Jan Rybar ef4d31
+            return EXIT_FAILURE;
Jan Rybar ef4d31
 		}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 		while ((rlen =  getline(&iobuf, &iolen, fp)) > 0) {
Jan Rybar ef4d31
 			size_t offset;
Jan Rybar ef4d31
+            SysctlSetting *setting;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 			n++;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 			if (rlen < 2)
Jan Rybar ef4d31
 				continue;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
-			t = StripLeadingAndTrailingSpaces(iobuf);
Jan Rybar ef4d31
-			if (strlen(t) < 2)
Jan Rybar ef4d31
-				continue;
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			if (*t == '#' || *t == ';')
Jan Rybar ef4d31
-				continue;
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			name = strtok(t, "=");
Jan Rybar ef4d31
-			if (!name || !*name) {
Jan Rybar ef4d31
-				xwarnx(_("%s(%d): invalid syntax, continuing..."),
Jan Rybar ef4d31
-				       globbuf.gl_pathv[j], n);
Jan Rybar ef4d31
-				continue;
Jan Rybar ef4d31
-			}
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			StripLeadingAndTrailingSpaces(name);
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			if (pattern && !pattern_match(name, pattern))
Jan Rybar ef4d31
-				continue;
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			offset = strlen(name);
Jan Rybar ef4d31
-			memmove(&iobuf[0], name, offset);
Jan Rybar ef4d31
-			iobuf[offset++] = '=';
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			value = strtok(NULL, "\n\r");
Jan Rybar ef4d31
-			if (!value || !*value) {
Jan Rybar ef4d31
-				xwarnx(_("%s(%d): invalid syntax, continuing..."),
Jan Rybar ef4d31
-				       globbuf.gl_pathv[j], n);
Jan Rybar ef4d31
-				continue;
Jan Rybar ef4d31
-			}
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			while ((*value == ' ' || *value == '\t') && *value != 0)
Jan Rybar ef4d31
-				value++;
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			/* should NameOnly affect this? */
Jan Rybar ef4d31
-			memmove(&iobuf[offset], value, strlen(value));
Jan Rybar ef4d31
-			offset += strlen(value);
Jan Rybar ef4d31
-			iobuf[offset] = '\0';
Jan Rybar ef4d31
-
Jan Rybar ef4d31
-			rc |= WriteSetting(iobuf);
Jan Rybar ef4d31
+            if ( (setting = parse_setting_line(globbuf.gl_pathv[j], n, iobuf))
Jan Rybar ef4d31
+                 == NULL)
Jan Rybar ef4d31
+                continue;
Jan Rybar ef4d31
+            settinglist_add(setlist, setting);
Jan Rybar ef4d31
 		}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 		fclose(fp);
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
-out:
Jan Rybar ef4d31
 	return rc;
Jan Rybar ef4d31
 }
Jan Rybar ef4d31
 
Jan Rybar ef4d31
@@ -618,7 +768,7 @@ static int sortpairs(const void *A, cons
Jan Rybar ef4d31
 	return strcmp(a->name, b->name);
Jan Rybar ef4d31
 }
Jan Rybar ef4d31
 
Jan Rybar ef4d31
-static int PreloadSystem(void)
Jan Rybar ef4d31
+static int PreloadSystem(SettingList *setlist)
Jan Rybar ef4d31
 {
Jan Rybar ef4d31
 	unsigned di, i;
Jan Rybar ef4d31
 	const char *dirs[] = {
Jan Rybar ef4d31
@@ -630,7 +780,7 @@ static int PreloadSystem(void)
Jan Rybar ef4d31
 	};
Jan Rybar ef4d31
 	struct pair **cfgs = NULL;
Jan Rybar ef4d31
 	unsigned ncfgs = 0;
Jan Rybar ef4d31
-	int rc = 0;
Jan Rybar ef4d31
+	int rc = EXIT_SUCCESS;
Jan Rybar ef4d31
 	struct stat ts;
Jan Rybar ef4d31
 	enum { nprealloc = 16 };
Jan Rybar ef4d31
 
Jan Rybar ef4d31
@@ -688,14 +838,14 @@ static int PreloadSystem(void)
Jan Rybar ef4d31
 	for (i = 0; i < ncfgs; ++i) {
Jan Rybar ef4d31
 		if (!Quiet)
Jan Rybar ef4d31
 			printf(_("* Applying %s ...\n"), cfgs[i]->value);
Jan Rybar ef4d31
-		rc |= Preload(cfgs[i]->value);
Jan Rybar ef4d31
+		rc |= Preload(setlist, cfgs[i]->value);
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
Jan Rybar ef4d31
 		if (!Quiet)
Jan Rybar ef4d31
 			printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
Jan Rybar ef4d31
-		rc |= Preload(DEFAULT_PRELOAD);
Jan Rybar ef4d31
+		rc |= Preload(setlist, DEFAULT_PRELOAD);
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	/* cleaning */
Jan Rybar ef4d31
@@ -717,15 +867,19 @@ int main(int argc, char *argv[])
Jan Rybar ef4d31
 	bool preloadfileOpt = false;
Jan Rybar ef4d31
 	int ReturnCode = 0;
Jan Rybar ef4d31
 	int c;
Jan Rybar ef4d31
+    int rc;
Jan Rybar ef4d31
 	const char *preloadfile = NULL;
Jan Rybar ef4d31
+    SettingList *setlist;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	enum {
Jan Rybar ef4d31
 		DEPRECATED_OPTION = CHAR_MAX + 1,
Jan Rybar ef4d31
-		SYSTEM_OPTION
Jan Rybar ef4d31
+		SYSTEM_OPTION,
Jan Rybar ef4d31
+        DRYRUN_OPTION
Jan Rybar ef4d31
 	};
Jan Rybar ef4d31
 	static const struct option longopts[] = {
Jan Rybar ef4d31
 		{"all", no_argument, NULL, 'a'},
Jan Rybar ef4d31
 		{"deprecated", no_argument, NULL, DEPRECATED_OPTION},
Jan Rybar ef4d31
+		{"dry-run", no_argument, NULL, DRYRUN_OPTION},
Jan Rybar ef4d31
 		{"binary", no_argument, NULL, 'b'},
Jan Rybar ef4d31
 		{"ignore", no_argument, NULL, 'e'},
Jan Rybar ef4d31
 		{"names", no_argument, NULL, 'N'},
Jan Rybar ef4d31
@@ -753,6 +907,10 @@ int main(int argc, char *argv[])
Jan Rybar ef4d31
 	IgnoreError = false;
Jan Rybar ef4d31
 	Quiet = false;
Jan Rybar ef4d31
 	IgnoreDeprecated = true;
Jan Rybar ef4d31
+    DryRun = false;
Jan Rybar ef4d31
+    setlist = xmalloc(sizeof(SettingList));
Jan Rybar ef4d31
+    setlist->head = NULL;
Jan Rybar ef4d31
+    setlist->tail = NULL;
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	if (argc < 2)
Jan Rybar ef4d31
 		Usage(stderr);
Jan Rybar ef4d31
@@ -805,7 +963,12 @@ int main(int argc, char *argv[])
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
 		case SYSTEM_OPTION:
Jan Rybar ef4d31
 			IgnoreError = true;
Jan Rybar ef4d31
-			return PreloadSystem();
Jan Rybar ef4d31
+			rc |= PreloadSystem(setlist);
Jan Rybar ef4d31
+            rc |= write_setting_list(setlist);
Jan Rybar ef4d31
+            return rc;
Jan Rybar ef4d31
+        case DRYRUN_OPTION:
Jan Rybar ef4d31
+            DryRun = true;
Jan Rybar ef4d31
+            break;
Jan Rybar ef4d31
 		case 'r':
Jan Rybar ef4d31
 			pattern = xstrdup(optarg);
Jan Rybar ef4d31
 			break;
Jan Rybar ef4d31
@@ -833,15 +996,16 @@ int main(int argc, char *argv[])
Jan Rybar ef4d31
 		int ret = EXIT_SUCCESS, i;
Jan Rybar ef4d31
 		if (!preloadfile) {
Jan Rybar ef4d31
 			if (!argc) {
Jan Rybar ef4d31
-				ret |= Preload(DEFAULT_PRELOAD);
Jan Rybar ef4d31
+				ret |= Preload(setlist, DEFAULT_PRELOAD);
Jan Rybar ef4d31
 			}
Jan Rybar ef4d31
 		} else {
Jan Rybar ef4d31
 			/* This happens when -pfile option is
Jan Rybar ef4d31
 			 * used without space. */
Jan Rybar ef4d31
-			ret |= Preload(preloadfile);
Jan Rybar ef4d31
+			ret |= Preload(setlist, preloadfile);
Jan Rybar ef4d31
 		}
Jan Rybar ef4d31
 		for (i = 0; i < argc; i++)
Jan Rybar ef4d31
-			ret |= Preload(argv[i]);
Jan Rybar ef4d31
+			ret |= Preload(setlist, argv[i]);
Jan Rybar ef4d31
+        ret |= write_setting_list(setlist);
Jan Rybar ef4d31
 		return ret;
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 
Jan Rybar ef4d31
@@ -855,9 +1019,14 @@ int main(int argc, char *argv[])
Jan Rybar ef4d31
 		      program_invocation_short_name);
Jan Rybar ef4d31
 
Jan Rybar ef4d31
 	for ( ; *argv; argv++) {
Jan Rybar ef4d31
-		if (WriteMode || strchr(*argv, '='))
Jan Rybar ef4d31
-			ReturnCode += WriteSetting(*argv);
Jan Rybar ef4d31
-		else
Jan Rybar ef4d31
+		if (WriteMode || strchr(*argv, '=')) {
Jan Rybar ef4d31
+            SysctlSetting *s;
Jan Rybar ef4d31
+            if ( (s = parse_setting_line("command line", 0, *argv)) != NULL)
Jan Rybar ef4d31
+                ReturnCode |= WriteSetting(s->key, s->path, s->value,
Jan Rybar ef4d31
+                                           s->ignore_failure);
Jan Rybar ef4d31
+            else
Jan Rybar ef4d31
+                ReturnCode |= EXIT_FAILURE;
Jan Rybar ef4d31
+        } else
Jan Rybar ef4d31
 			ReturnCode += ReadSetting(*argv);
Jan Rybar ef4d31
 	}
Jan Rybar ef4d31
 	return ReturnCode;
Jan Rybar ef4d31
diff -up ./testsuite/config/unix.exp.ori ./testsuite/config/unix.exp
Jan Rybar ef4d31
--- ./testsuite/config/unix.exp.ori	2021-02-09 11:11:25.000000000 +0100
Jan Rybar ef4d31
+++ ./testsuite/config/unix.exp	2022-07-29 15:50:50.768380067 +0200
Jan Rybar ef4d31
@@ -136,6 +136,15 @@ proc expect_table_dsc { test match_heade
Jan Rybar ef4d31
     #}
Jan Rybar ef4d31
 }
Jan Rybar ef4d31
 
Jan Rybar ef4d31
+proc expect_spawn_retval { test retval } {
Jan Rybar ef4d31
+    foreach {pid spawnid os_error_flag value} [wait] break
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    if {$value == $retval} {
Jan Rybar ef4d31
+      return
Jan Rybar ef4d31
+    }
Jan Rybar ef4d31
+    fail "$test (exit value)"
Jan Rybar ef4d31
+}
Jan Rybar ef4d31
+
Jan Rybar ef4d31
 proc make_pipeproc { } {
Jan Rybar ef4d31
     global pipeproc_pid pipeproc_spawnid topdir
Jan Rybar ef4d31
 
Jan Rybar ef4d31
diff -up ./testsuite/sysctl_glob_test.conf.ori ./testsuite/sysctl_glob_test.conf
Jan Rybar ef4d31
--- ./testsuite/sysctl_glob_test.conf.ori	2022-07-29 15:50:50.768380067 +0200
Jan Rybar ef4d31
+++ ./testsuite/sysctl_glob_test.conf	2022-07-29 15:50:50.768380067 +0200
Jan Rybar ef4d31
@@ -0,0 +1,6 @@
Jan Rybar ef4d31
+#
Jan Rybar ef4d31
+# Test configuration for for glob in sysctl
Jan Rybar ef4d31
+#
Jan Rybar ef4d31
+fs.protected_* = 2
Jan Rybar ef4d31
+fs.protected_hardlinks = 1
Jan Rybar ef4d31
+-fs.protected_regular
Jan Rybar ef4d31
diff -up ./testsuite/sysctl.test/sysctl_write.exp.ori ./testsuite/sysctl.test/sysctl_write.exp
Jan Rybar ef4d31
--- ./testsuite/sysctl.test/sysctl_write.exp.ori	2022-07-29 15:50:50.768380067 +0200
Jan Rybar ef4d31
+++ ./testsuite/sysctl.test/sysctl_write.exp	2022-07-29 15:50:50.768380067 +0200
Jan Rybar ef4d31
@@ -0,0 +1,29 @@
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+set sysctl ${topdir}sysctl
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+set test "sysctl write from command line"
Jan Rybar ef4d31
+spawn $sysctl --dry-run kernel.hostname=procps-test
Jan Rybar ef4d31
+expect_pass "$test" "/proc/sys/kernel/hostname = procps-test"
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+set test "sysctl write from configuration file"
Jan Rybar ef4d31
+spawn $sysctl --dry-run -f ${topdir}testsuite/sysctl_glob_test.conf
Jan Rybar ef4d31
+expect_pass "$test" "/proc/sys/fs/protected_fifos = 2\\s+/proc/sys/fs/protected_symlinks = 2\\s+/proc/sys/fs/protected_hardlinks = 1"
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+set hostname_file "/proc/sys/kernel/hostname"
Jan Rybar ef4d31
+if {[file exists ${hostname_file}]} {
Jan Rybar ef4d31
+  if {[file writable ${hostname_file}]} {
Jan Rybar ef4d31
+    unsupported "sysctl write: hostname file is writable"
Jan Rybar ef4d31
+  } else {
Jan Rybar ef4d31
+    set test "sysctl write unwritable file"
Jan Rybar ef4d31
+    spawn $sysctl -q kernel.hostname=procpstest
Jan Rybar ef4d31
+    expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\"\\s*$"
Jan Rybar ef4d31
+    expect_spawn_retval "$test" 1
Jan Rybar ef4d31
+
Jan Rybar ef4d31
+    set test "sysctl write unwritable file ignored"
Jan Rybar ef4d31
+    spawn $sysctl -q -- -kernel.hostname=procpstest
Jan Rybar ef4d31
+    expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\", ignoring\\s*$"
Jan Rybar ef4d31
+    expect_spawn_retval "$test" 0
Jan Rybar ef4d31
+  }
Jan Rybar ef4d31
+} else {
Jan Rybar ef4d31
+  unsupported "sysctl write: hostname file doe not exist"
Jan Rybar ef4d31
+}