[packages/procps] Rel 2; support for systemd globs in sysctl config - https://gitlab.com/procps-ng/procps/-/issues/191

arekm arekm at pld-linux.org
Wed Sep 15 13:55:29 CEST 2021


commit ff0f153f544ea1a960232366b85c86429969ceda
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Wed Sep 15 13:55:18 2021 +0200

    Rel 2; support for systemd globs in sysctl config - https://gitlab.com/procps-ng/procps/-/issues/191

 procps.spec        |   4 +-
 systemd-glob.patch | 857 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 860 insertions(+), 1 deletion(-)
---
diff --git a/procps.spec b/procps.spec
index e345059..92fdb47 100644
--- a/procps.spec
+++ b/procps.spec
@@ -22,7 +22,7 @@ Summary(pt_BR.UTF-8):	Utilitários de monitoração de processos
 Summary(tr.UTF-8):	Süreç izleme araçları
 Name:		procps
 Version:	3.3.17
-Release:	1
+Release:	2
 Epoch:		1
 License:	GPL v2+
 Group:		Applications/System
@@ -37,6 +37,7 @@ Source4:	XConsole.sh
 Patch0:		%{name}-missing-symbol.patch
 Patch1:		%{name}-FILLBUG_backport.patch
 Patch2:		%{name}-pl.po-update.patch
+Patch3:		systemd-glob.patch
 URL:		https://gitlab.com/procps-ng/procps
 BuildRequires:	autoconf >= 2.69
 BuildRequires:	automake >= 1:1.11
@@ -149,6 +150,7 @@ Statyczna wersja biblioteki libproc.
 %patch0 -p1
 %patch1 -p1
 %patch2 -p1
+%patch3 -p1
 
 %{__sed} -i -e "s#usrbin_execdir=.*#usrbin_execdir='\${bindir}'#g" configure.ac
 
diff --git a/systemd-glob.patch b/systemd-glob.patch
new file mode 100644
index 0000000..2aa6a98
--- /dev/null
+++ b/systemd-glob.patch
@@ -0,0 +1,857 @@
+commit 474847ed35dda5fd4c33a717d8cc7c4d17b90232
+Author: Craig Small <csmall at dropbear.xyz>
+Date:   Mon Sep 13 22:07:37 2021 +1000
+
+    sysctl: Support systemd glob patterns
+    
+    systemd-sysctl handles glob patterns along with overrides and
+    exceptions. Now the procps sysctl does it too.
+    
+    The return value for sysctl is consistently either 0 or 1.
+    
+    Added tests to check sysctl functions.
+    
+    References:
+     procps-ng/procps#191
+    
+    Signed-off-by: Craig Small <csmall at dropbear.xyz>
+
+diff --git a/sysctl.c b/sysctl.c
+index bbca0b9..d26cd11 100644
+--- a/sysctl.c
++++ b/sysctl.c
+@@ -17,6 +17,7 @@
+  * along with this program; if not, write to the Free Software
+  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+  *
++ * Part of this code comes from systemd, especially sysctl.c
+  * Changelog:
+  *            v1.01:
+  *                   - added -p <preload> to preload values from a file
+@@ -40,6 +41,7 @@
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <unistd.h>
++#include <ctype.h>
+ 
+ #include "c.h"
+ #include "fileutils.h"
+@@ -66,12 +68,34 @@ static bool PrintName;
+ static bool PrintNewline;
+ static bool IgnoreError;
+ static bool Quiet;
++static bool DryRun;
+ static char *pattern;
+ 
+ #define LINELEN 4096
+ static char *iobuf;
+ static size_t iolen = LINELEN;
+ 
++typedef struct SysctlSetting {
++    char *key;
++    char *path;
++    char *value;
++    bool ignore_failure;
++    bool glob_exclude;
++    struct SysctlSetting *next;
++} SysctlSetting;
++
++typedef struct SettingList {
++    struct SysctlSetting *head;
++    struct SysctlSetting *tail;
++} SettingList;
++
++#define GLOB_CHARS "*?["
++static inline bool string_is_glob(const char *p)
++{
++    return !!strpbrk(p, GLOB_CHARS);
++}
++
++
+ /* Function prototypes. */
+ static int pattern_match(const char *string, const char *pat);
+ static int DisplayAll(const char *restrict const path);
+@@ -100,6 +124,81 @@ static void slashdot(char *restrict p, char old, char new)
+ 	}
+ }
+ 
++static void setting_free(SysctlSetting *s) {
++    if (!s)
++	return;
++
++    free(s->key);
++    free(s->path);
++    free(s->value);
++    free(s);
++}
++
++static SysctlSetting *setting_new(
++	const char *key,
++	const char *value,
++	bool ignore_failure,
++    bool glob_exclude) {
++
++    SysctlSetting *s = NULL;
++    char *path = NULL;
++    int proc_len;
++
++    proc_len = strlen(PROC_PATH);
++    /* used to open the file */
++    path = xmalloc(strlen(key) + proc_len + 2);
++    strcpy(path, PROC_PATH);
++    if (key[0] == '-')
++        strcat(path + proc_len, key+1);
++    else
++        strcat(path + proc_len, key);
++    /* change . to / */
++    slashdot(path + proc_len, '.', '/');
++
++    s = xmalloc(sizeof(SysctlSetting));
++
++    *s = (SysctlSetting) {
++        .key = strdup(key),
++        .path = path,
++        .value = value? strdup(value): NULL,
++        .ignore_failure = ignore_failure,
++        .glob_exclude = glob_exclude,
++        .next = NULL,
++    };
++
++    return s;
++}
++
++static void settinglist_add(SettingList *l, SysctlSetting *s) {
++    SysctlSetting *old_tail;
++
++    if (!l)
++        return;
++
++    if (l->head == NULL)
++        l->head = s;
++
++    if (l->tail != NULL) {
++        old_tail = l->tail;
++        old_tail->next = s;
++    }
++    l->tail = s;
++}
++
++static SysctlSetting *settinglist_findpath(const SettingList *l, const char *path) {
++    SysctlSetting *node;
++
++    for (node=l->head; node != NULL; node = node->next) {
++        if (strcmp(node->path, path) == 0)
++            return node;
++    }
++    return NULL;
++}
++
++/* Function prototypes. */
++static int pattern_match(const char *string, const char *pat);
++static int DisplayAll(const char *restrict const path);
++
+ /*
+  * Display the usage format
+  */
+@@ -115,6 +214,7 @@ static void __attribute__ ((__noreturn__))
+ 	fputs(_("  -A                   alias of -a\n"), out);
+ 	fputs(_("  -X                   alias of -a\n"), out);
+ 	fputs(_("      --deprecated     include deprecated parameters to listing\n"), out);
++	fputs(_("      --dry-run        Print the key and values but do not write\n"), out);
+ 	fputs(_("  -b, --binary         print value without new line\n"), out);
+ 	fputs(_("  -e, --ignore         ignore unknown variables errors\n"), out);
+ 	fputs(_("  -N, --names          print variable names without values\n"), out);
+@@ -137,6 +237,39 @@ static void __attribute__ ((__noreturn__))
+ 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+ 
++/*
++ * Strip left/leading side of a string
++ */
++static char *lstrip(char *line)
++{
++    char *start;
++
++    if (!line || !*line)
++        return line;
++
++    start = line;
++    while(isspace(*start)) start++;
++
++    return start;
++}
++
++/*
++ * Strip right/trailing side of a string
++ * by placing a \0
++ */
++static void rstrip(char *line)
++{
++    char *end;
++
++    if (!line || !*line)
++        return;
++
++    end = line + strlen(line) - 1;
++    while(end > line && isspace(*end)) end--;
++
++    end[1] = '\0';
++}
++
+ /*
+  * Strip the leading and trailing spaces from a string
+  */
+@@ -166,7 +299,7 @@ static char *StripLeadingAndTrailingSpaces(char *oneline)
+  */
+ static int ReadSetting(const char *restrict const name)
+ {
+-	int rc = 0;
++	int rc = EXIT_SUCCESS;
+ 	char *restrict tmpname;
+ 	char *restrict outname;
+ 	ssize_t rlen;
+@@ -198,7 +331,7 @@ static int ReadSetting(const char *restrict const name)
+ 	if (stat(tmpname, &ts) < 0) {
+ 		if (!IgnoreError) {
+ 			xwarn(_("cannot stat %s"), tmpname);
+-			rc = -1;
++			rc = EXIT_FAILURE;
+ 		}
+ 		goto out;
+ 	}
+@@ -215,7 +348,7 @@ static int ReadSetting(const char *restrict const name)
+ 	}
+ 
+ 	if (pattern && !pattern_match(outname, pattern)) {
+-		rc = 0;
++		rc = EXIT_SUCCESS;
+ 		goto out;
+ 	}
+ 
+@@ -231,19 +364,19 @@ static int ReadSetting(const char *restrict const name)
+ 		case ENOENT:
+ 			if (!IgnoreError) {
+ 				xwarnx(_("\"%s\" is an unknown key"), outname);
+-				rc = -1;
++				rc = EXIT_FAILURE;
+ 			}
+ 			break;
+ 		case EACCES:
+ 			xwarnx(_("permission denied on key '%s'"), outname);
+-			rc = -1;
++			rc = EXIT_FAILURE;
+ 			break;
+ 		case EIO:	    /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
+-			rc = -1;
++			rc = EXIT_FAILURE;
+ 			break;
+ 		default:
+ 			xwarn(_("reading key \"%s\""), outname);
+-			rc = -1;
++			rc = EXIT_FAILURE;
+ 			break;
+ 		}
+ 	} else {
+@@ -279,7 +412,7 @@ static int ReadSetting(const char *restrict const name)
+ 			case EACCES:
+ 				xwarnx(_("permission denied on key '%s'"),
+ 				       outname);
+-				rc = -1;
++				rc = EXIT_FAILURE;
+ 				break;
+ 			case EISDIR: {
+ 					size_t len;
+@@ -291,11 +424,11 @@ static int ReadSetting(const char *restrict const name)
+ 					goto out;
+ 				}
+ 			case EIO:	    /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
+-				rc = -1;
++				rc = EXIT_FAILURE;
+ 				break;
+ 			default:
+ 				xwarnx(_("reading key \"%s\""), outname);
+-				rc = -1;
++				rc = EXIT_FAILURE;
+ 			case 0:
+ 				break;
+ 			}
+@@ -323,7 +456,7 @@ static int is_deprecated(char *filename)
+  */
+ static int DisplayAll(const char *restrict const path)
+ {
+-	int rc = 0;
++	int rc = EXIT_SUCCESS;
+ 	int rc2;
+ 	DIR *restrict dp;
+ 	struct dirent *restrict de;
+@@ -333,7 +466,7 @@ static int DisplayAll(const char *restrict const path)
+ 
+ 	if (!dp) {
+ 		xwarnx(_("unable to open directory \"%s\""), path);
+-		rc = -1;
++		rc = EXIT_FAILURE;
+ 	} else {
+ 		readdir(dp);	/* skip .  */
+ 		readdir(dp);	/* skip .. */
+@@ -369,130 +502,183 @@ static int DisplayAll(const char *restrict const path)
+ /*
+  * Write a sysctl setting
+  */
+-static int WriteSetting(const char *setting)
+-{
+-	int rc = 0;
+-	const char *name = setting;
+-	const char *value;
+-	const char *equals;
+-	char *tmpname;
+-	char *outname;
+-	char *last_dot;
+-	bool ignore_failure;
+-
+-	FILE *fp;
++static int WriteSetting(
++    const char *key,
++    const char *path,
++    const char *value,
++    const bool ignore_failure) {
++
++    int rc = EXIT_SUCCESS;
++    FILE *fp;
+ 	struct stat ts;
+ 
+-	if (!name)
+-		/* probably don't want to display this err */
+-		return 0;
+-
+-	equals = strchr(setting, '=');
+-
+-	if (!equals) {
+-		xwarnx(_("\"%s\" must be of the form name=value"),
+-		       setting);
+-		return -1;
+-	}
+-
+-	/* point to the value in name=value */
+-	value = equals + 1;
+-
+-	if (!*name || name == equals) {
+-		xwarnx(_("malformed setting \"%s\""), setting);
+-		return -2;
+-	}
+-
+-	ignore_failure = name[0] == '-';
+-	if (ignore_failure)
+-	    name++;
+-
+-	/* used to open the file */
+-	tmpname = xmalloc(equals - name + 1 + strlen(PROC_PATH));
+-	strcpy(tmpname, PROC_PATH);
+-	strncat(tmpname, name, (int) (equals - name));
+-	tmpname[equals - name + strlen(PROC_PATH)] = 0;
+-	/* change . to / */
+-	slashdot(tmpname + strlen(PROC_PATH), '.', '/');
++    if (!key || !path)
++        return rc;
+ 
+-	/* used to display the output */
+-	outname = xmalloc(equals - name + 1);
+-	strncpy(outname, name, (int) (equals - name));
+-	outname[equals - name] = 0;
+-	/* change / to . */
+-	slashdot(outname, '/', '.');
+-	last_dot = strrchr(outname, '.');
+-	if (last_dot != NULL && is_deprecated(last_dot + 1)) {
+-		xwarnx(_("%s is deprecated, value not set"), outname);
+-		goto out;
+-        }
+-
+-	if (stat(tmpname, &ts) < 0) {
++	if (stat(path, &ts) < 0) {
+ 		if (!IgnoreError) {
+-			xwarn(_("cannot stat %s"), tmpname);
+-			rc = -1;
++			xwarn(_("cannot stat %s"), path);
++			rc = EXIT_FAILURE;
+ 		}
+-		goto out;
++        return rc;
+ 	}
+ 
+ 	if ((ts.st_mode & S_IWUSR) == 0) {
+-		xwarn(_("setting key \"%s\""), outname);
+-		goto out;
++		xwarn(_("setting key \"%s\""), key);
++        return rc;
+ 	}
+ 
+ 	if (S_ISDIR(ts.st_mode)) {
+-		xwarn(_("setting key \"%s\""), outname);
+-		goto out;
++		xwarn(_("setting key \"%s\""), key);
++        return rc;
+ 	}
+ 
+-	fp = fprocopen(tmpname, "w");
+-
+-	if (!fp) {
+-		switch (errno) {
+-		case ENOENT:
+-			if (!IgnoreError) {
+-				xwarnx(_("\"%s\" is an unknown key%s"), outname, (ignore_failure?_(", ignoring"):""));
++    if (!DryRun) {
++        if ((fp = fprocopen(path, "w")) == NULL) {
++            switch (errno) {
++            case ENOENT:
++                if (!IgnoreError) {
++                    xwarnx(_("\"%s\" is an unknown key%s"),
++                           key, (ignore_failure?_(", ignoring"):""));
+ 				if (!ignore_failure)
+-				    rc = -1;
++				    rc = EXIT_FAILURE;
+ 			}
+ 			break;
+-		case EPERM:
+-		case EROFS:
+-		case EACCES:
+-			xwarnx(_("permission denied on key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
+-			break;
+-		default:
+-			xwarn(_("setting key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
+-			break;
+-		}
+-		if (!ignore_failure && errno != ENOENT)
+-		    rc = -1;
+-	} else {
+-		rc = fprintf(fp, "%s\n", value);
+-		if (0 < rc)
+-			rc = 0;
+-		if (close_stream(fp) != 0)
+-			xwarn(_("setting key \"%s\""), outname);
+-		else if (rc == 0 && !Quiet) {
+-			if (NameOnly) {
+-				fprintf(stdout, "%s\n", outname);
+-			} else {
+-				if (PrintName) {
+-					fprintf(stdout, "%s = %s\n",
+-						outname, value);
+-				} else {
+-					if (PrintNewline)
+-						fprintf(stdout, "%s\n", value);
+-					else
+-						fprintf(stdout, "%s", value);
+-				}
+-			}
+-		}
+-	}
+-      out:
+-	free(tmpname);
+-	free(outname);
+-	return rc;
++            case EPERM:
++            case EROFS:
++            case EACCES:
++                xwarnx(_("permission denied on key \"%s\"%s"),
++                       key, (ignore_failure?_(", ignoring"):""));
++                break;
++            default:
++                xwarn(_("setting key \"%s\"%s"),
++                      key, (ignore_failure?_(", ignoring"):""));
++                break;
++            }
++            if (!ignore_failure && errno != ENOENT)
++		    rc = EXIT_FAILURE;
++        } else {
++	    if (0 < fprintf(fp, "%s\n", value))
++		rc = EXIT_SUCCESS;
++            if (close_stream(fp) != 0) {
++                xwarn(_("setting key \"%s\""), path);
++                return rc;
++            }
++        }
++    }
++    if ((rc == EXIT_SUCCESS && !Quiet) || DryRun) {
++        if (NameOnly) {
++            printf("%s\n", value);
++        } else {
++            if (PrintName) {
++                printf("%s = %s\n", path, value);
++            } else {
++                if (PrintNewline)
++                    printf("%s\n", value);
++                else
++                    printf("%s", value);
++            }
++        }
++    }
++    return rc;
++}
++
++/*
++ * parse each configuration line, there are multiple ways of specifying
++ * a key/value here:
++ *
++ * key = value                               simple setting
++ * -key = value                              ignore errors
++ * key.pattern.*.with.glob = value           set keys that match glob
++ * -key.pattern.exclude.with.glob            dont set this value
++ * key.pattern.override.with.glob = value    set this glob match to value
++ *
++ */
++
++static SysctlSetting *parse_setting_line(
++    const char *path,
++    const int linenum,
++    char *line)
++{
++    SysctlSetting *s;
++    char *key;
++    char *value;
++    bool glob_exclude = FALSE;
++    bool ignore_failure = FALSE;
++
++    key = lstrip(line);
++    if (strlen(key) < 2)
++        return NULL;
++
++    /* skip over comments */
++    if (key[0] == '#' || key[0] == ';')
++        return NULL;
++
++    if (pattern && !pattern_match(key, pattern))
++        return NULL;
++
++    value = strchr(key, '=');
++    if (value == NULL) {
++        if (key[0] == '-') {
++            glob_exclude = TRUE;
++            key++;
++            value = NULL;
++            rstrip(key);
++        } else {
++            xwarnx(_("%s(%d): invalid syntax, continuing..."),
++                   path, linenum);
++            return NULL;
++        }
++    } else {
++        value[0]='\0';
++        if (key[0] == '-') {
++            ignore_failure = TRUE;
++            key++;
++        }
++        value++; // skip over =
++        value=lstrip(value);
++        rstrip(value);
++        rstrip(key);
++    }
++    return setting_new(key, value, ignore_failure, glob_exclude);
++}
++
++/* Go through the setting list, expand and sort out
++ * setting globs and actually write the settings out
++ */
++static int write_setting_list(const SettingList *sl)
++{
++    SysctlSetting *node;
++    int rc = EXIT_SUCCESS;
++
++    for (node=sl->head; node != NULL; node=node->next) {
++        if (node->glob_exclude)
++            continue;
++
++        if (string_is_glob(node->path)) {
++            char *gl_path;
++            glob_t globbuf;
++            int i;
++
++            if (glob(node->path, 0, NULL, &globbuf) != 0)
++                continue;
++
++            for(i=0; i < globbuf.gl_pathc; i++) {
++                if (settinglist_findpath(sl, globbuf.gl_pathv[i]))
++                    continue; // override or exclude
++
++                rc |= WriteSetting(node->key, globbuf.gl_pathv[i], node->value,
++                                   node->ignore_failure);
++            }
++        } else {
++            rc |= WriteSetting(node->key, node->path, node->value,
++                               node->ignore_failure);
++        }
++
++
++    }
++
++    return rc;
+ }
+ 
+ static int pattern_match(const char *string, const char *pat)
+@@ -513,12 +699,12 @@ static int pattern_match(const char *string, const char *pat)
+  * Preload the sysctl's from the conf file.  We parse the file and then
+  * reform it (strip out whitespace).
+  */
+-static int Preload(const char *restrict const filename)
++static int Preload(SettingList *setlist, const char *restrict const filename)
+ {
+ 	FILE *fp;
+ 	char *t;
+ 	int n = 0;
+-	int rc = 0;
++	int rc = EXIT_SUCCESS;
+ 	ssize_t rlen;
+ 	char *name, *value;
+ 	glob_t globbuf;
+@@ -547,62 +733,26 @@ static int Preload(const char *restrict const filename)
+ 		    ? stdin : fopen(globbuf.gl_pathv[j], "r");
+ 		if (!fp) {
+ 			xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
+-			rc = -1;
+-			goto out;
++            return EXIT_FAILURE;
+ 		}
+ 
+ 		while ((rlen =  getline(&iobuf, &iolen, fp)) > 0) {
+ 			size_t offset;
++            SysctlSetting *setting;
+ 
+ 			n++;
+ 
+ 			if (rlen < 2)
+ 				continue;
+ 
+-			t = StripLeadingAndTrailingSpaces(iobuf);
+-			if (strlen(t) < 2)
+-				continue;
+-
+-			if (*t == '#' || *t == ';')
+-				continue;
+-
+-			name = strtok(t, "=");
+-			if (!name || !*name) {
+-				xwarnx(_("%s(%d): invalid syntax, continuing..."),
+-				       globbuf.gl_pathv[j], n);
+-				continue;
+-			}
+-
+-			StripLeadingAndTrailingSpaces(name);
+-
+-			if (pattern && !pattern_match(name, pattern))
+-				continue;
+-
+-			offset = strlen(name);
+-			memmove(&iobuf[0], name, offset);
+-			iobuf[offset++] = '=';
+-
+-			value = strtok(NULL, "\n\r");
+-			if (!value || !*value) {
+-				xwarnx(_("%s(%d): invalid syntax, continuing..."),
+-				       globbuf.gl_pathv[j], n);
+-				continue;
+-			}
+-
+-			while ((*value == ' ' || *value == '\t') && *value != 0)
+-				value++;
+-
+-			/* should NameOnly affect this? */
+-			memmove(&iobuf[offset], value, strlen(value));
+-			offset += strlen(value);
+-			iobuf[offset] = '\0';
+-
+-			rc |= WriteSetting(iobuf);
++            if ( (setting = parse_setting_line(globbuf.gl_pathv[j], n, iobuf))
++                 == NULL)
++                continue;
++            settinglist_add(setlist, setting);
+ 		}
+ 
+ 		fclose(fp);
+ 	}
+-out:
+ 	return rc;
+ }
+ 
+@@ -618,7 +768,7 @@ static int sortpairs(const void *A, const void *B)
+ 	return strcmp(a->name, b->name);
+ }
+ 
+-static int PreloadSystem(void)
++static int PreloadSystem(SettingList *setlist)
+ {
+ 	unsigned di, i;
+ 	const char *dirs[] = {
+@@ -630,7 +780,7 @@ static int PreloadSystem(void)
+ 	};
+ 	struct pair **cfgs = NULL;
+ 	unsigned ncfgs = 0;
+-	int rc = 0;
++	int rc = EXIT_SUCCESS;
+ 	struct stat ts;
+ 	enum { nprealloc = 16 };
+ 
+@@ -688,14 +838,14 @@ static int PreloadSystem(void)
+ 	for (i = 0; i < ncfgs; ++i) {
+ 		if (!Quiet)
+ 			printf(_("* Applying %s ...\n"), cfgs[i]->value);
+-		rc |= Preload(cfgs[i]->value);
++		rc |= Preload(setlist, cfgs[i]->value);
+ 	}
+ 
+ 
+ 	if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
+ 		if (!Quiet)
+ 			printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
+-		rc |= Preload(DEFAULT_PRELOAD);
++		rc |= Preload(setlist, DEFAULT_PRELOAD);
+ 	}
+ 
+ 	/* cleaning */
+@@ -717,15 +867,19 @@ int main(int argc, char *argv[])
+ 	bool preloadfileOpt = false;
+ 	int ReturnCode = 0;
+ 	int c;
++    int rc;
+ 	const char *preloadfile = NULL;
++    SettingList *setlist;
+ 
+ 	enum {
+ 		DEPRECATED_OPTION = CHAR_MAX + 1,
+-		SYSTEM_OPTION
++		SYSTEM_OPTION,
++        DRYRUN_OPTION
+ 	};
+ 	static const struct option longopts[] = {
+ 		{"all", no_argument, NULL, 'a'},
+ 		{"deprecated", no_argument, NULL, DEPRECATED_OPTION},
++		{"dry-run", no_argument, NULL, DRYRUN_OPTION},
+ 		{"binary", no_argument, NULL, 'b'},
+ 		{"ignore", no_argument, NULL, 'e'},
+ 		{"names", no_argument, NULL, 'N'},
+@@ -753,6 +907,10 @@ int main(int argc, char *argv[])
+ 	IgnoreError = false;
+ 	Quiet = false;
+ 	IgnoreDeprecated = true;
++    DryRun = false;
++    setlist = xmalloc(sizeof(SettingList));
++    setlist->head = NULL;
++    setlist->tail = NULL;
+ 
+ 	if (argc < 2)
+ 		Usage(stderr);
+@@ -805,7 +963,12 @@ int main(int argc, char *argv[])
+ 			break;
+ 		case SYSTEM_OPTION:
+ 			IgnoreError = true;
+-			return PreloadSystem();
++			rc |= PreloadSystem(setlist);
++            rc |= write_setting_list(setlist);
++            return rc;
++        case DRYRUN_OPTION:
++            DryRun = true;
++            break;
+ 		case 'r':
+ 			pattern = xstrdup(optarg);
+ 			break;
+@@ -833,15 +996,16 @@ int main(int argc, char *argv[])
+ 		int ret = EXIT_SUCCESS, i;
+ 		if (!preloadfile) {
+ 			if (!argc) {
+-				ret |= Preload(DEFAULT_PRELOAD);
++				ret |= Preload(setlist, DEFAULT_PRELOAD);
+ 			}
+ 		} else {
+ 			/* This happens when -pfile option is
+ 			 * used without space. */
+-			ret |= Preload(preloadfile);
++			ret |= Preload(setlist, preloadfile);
+ 		}
+ 		for (i = 0; i < argc; i++)
+-			ret |= Preload(argv[i]);
++			ret |= Preload(setlist, argv[i]);
++        ret |= write_setting_list(setlist);
+ 		return ret;
+ 	}
+ 
+@@ -855,9 +1019,14 @@ int main(int argc, char *argv[])
+ 		      program_invocation_short_name);
+ 
+ 	for ( ; *argv; argv++) {
+-		if (WriteMode || strchr(*argv, '='))
+-			ReturnCode += WriteSetting(*argv);
+-		else
++		if (WriteMode || strchr(*argv, '=')) {
++            SysctlSetting *s;
++            if ( (s = parse_setting_line("command line", 0, *argv)) != NULL)
++                ReturnCode |= WriteSetting(s->key, s->path, s->value,
++                                           s->ignore_failure);
++            else
++                ReturnCode |= EXIT_FAILURE;
++        } else
+ 			ReturnCode += ReadSetting(*argv);
+ 	}
+ 	return ReturnCode;
+diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp
+index 4156c3b..ecdc0bf 100644
+--- a/testsuite/config/unix.exp
++++ b/testsuite/config/unix.exp
+@@ -136,6 +136,15 @@ proc expect_table_dsc { test match_header match_item } {
+     #}
+ }
+ 
++proc expect_spawn_retval { test retval } {
++    foreach {pid spawnid os_error_flag value} [wait] break
++
++    if {$value == $retval} {
++      return
++    }
++    fail "$test (exit value)"
++}
++
+ proc make_pipeproc { } {
+     global pipeproc_pid pipeproc_spawnid topdir
+ 
+diff --git a/testsuite/sysctl.test/sysctl_write.exp b/testsuite/sysctl.test/sysctl_write.exp
+new file mode 100644
+index 0000000..5a74dec
+--- /dev/null
++++ b/testsuite/sysctl.test/sysctl_write.exp
+@@ -0,0 +1,29 @@
++
++set sysctl ${topdir}sysctl
++
++set test "sysctl write from command line"
++spawn $sysctl --dry-run kernel.hostname=procps-test
++expect_pass "$test" "/proc/sys/kernel/hostname = procps-test"
++
++set test "sysctl write from configuration file"
++spawn $sysctl --dry-run -f ${topdir}testsuite/sysctl_glob_test.conf
++expect_pass "$test" "/proc/sys/fs/protected_fifos = 2\\s+/proc/sys/fs/protected_symlinks = 2\\s+/proc/sys/fs/protected_hardlinks = 1"
++
++set hostname_file "/proc/sys/kernel/hostname"
++if {[file exists ${hostname_file}]} {
++  if {[file writable ${hostname_file}]} {
++    unsupported "sysctl write: hostname file is writable"
++  } else {
++    set test "sysctl write unwritable file"
++    spawn $sysctl -q kernel.hostname=procpstest
++    expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\"\\s*$"
++    expect_spawn_retval "$test" 1
++
++    set test "sysctl write unwritable file ignored"
++    spawn $sysctl -q -- -kernel.hostname=procpstest
++    expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\", ignoring\\s*$"
++    expect_spawn_retval "$test" 0
++  }
++} else {
++  unsupported "sysctl write: hostname file doe not exist"
++}
+diff --git a/testsuite/sysctl_glob_test.conf b/testsuite/sysctl_glob_test.conf
+new file mode 100644
+index 0000000..45ae904
+--- /dev/null
++++ b/testsuite/sysctl_glob_test.conf
+@@ -0,0 +1,6 @@
++#
++# Test configuration for for glob in sysctl
++#
++fs.protected_* = 2
++fs.protected_hardlinks = 1
++-fs.protected_regular
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/procps.git/commitdiff/ff0f153f544ea1a960232366b85c86429969ceda



More information about the pld-cvs-commit mailing list