Blame SOURCES/sysctl-support-systemd-globs.patch

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