]> git.pld-linux.org Git - packages/procps.git/commitdiff
Rel 2; support for systemd globs in sysctl config - https://gitlab.com/procps-ng... auto/th/procps-3.3.17-2
authorArkadiusz Miśkiewicz <arekm@maven.pl>
Wed, 15 Sep 2021 11:55:18 +0000 (13:55 +0200)
committerArkadiusz Miśkiewicz <arekm@maven.pl>
Wed, 15 Sep 2021 11:55:18 +0000 (13:55 +0200)
procps.spec
systemd-glob.patch [new file with mode: 0644]

index e3450596607e0864fd848d438eb2e7be9500d9df..92fdb47abac763e327b3e4c5e7017ba18a776a8d 100644 (file)
@@ -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 (file)
index 0000000..2aa6a98
--- /dev/null
@@ -0,0 +1,857 @@
+commit 474847ed35dda5fd4c33a717d8cc7c4d17b90232
+Author: Craig Small <csmall@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@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
This page took 0.179646 seconds and 4 git commands to generate.