]> git.pld-linux.org Git - packages/procps.git/blob - systemd-glob.patch
- versioned Obsoletes
[packages/procps.git] / systemd-glob.patch
1 commit 474847ed35dda5fd4c33a717d8cc7c4d17b90232
2 Author: Craig Small <csmall@dropbear.xyz>
3 Date:   Mon Sep 13 22:07:37 2021 +1000
4
5     sysctl: Support systemd glob patterns
6     
7     systemd-sysctl handles glob patterns along with overrides and
8     exceptions. Now the procps sysctl does it too.
9     
10     The return value for sysctl is consistently either 0 or 1.
11     
12     Added tests to check sysctl functions.
13     
14     References:
15      procps-ng/procps#191
16     
17     Signed-off-by: Craig Small <csmall@dropbear.xyz>
18
19 diff --git a/sysctl.c b/sysctl.c
20 index bbca0b9..d26cd11 100644
21 --- a/sysctl.c
22 +++ b/sysctl.c
23 @@ -17,6 +17,7 @@
24   * along with this program; if not, write to the Free Software
25   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
26   *
27 + * Part of this code comes from systemd, especially sysctl.c
28   * Changelog:
29   *            v1.01:
30   *                   - added -p <preload> to preload values from a file
31 @@ -40,6 +41,7 @@
32  #include <sys/stat.h>
33  #include <sys/types.h>
34  #include <unistd.h>
35 +#include <ctype.h>
36  
37  #include "c.h"
38  #include "fileutils.h"
39 @@ -66,12 +68,34 @@ static bool PrintName;
40  static bool PrintNewline;
41  static bool IgnoreError;
42  static bool Quiet;
43 +static bool DryRun;
44  static char *pattern;
45  
46  #define LINELEN 4096
47  static char *iobuf;
48  static size_t iolen = LINELEN;
49  
50 +typedef struct SysctlSetting {
51 +    char *key;
52 +    char *path;
53 +    char *value;
54 +    bool ignore_failure;
55 +    bool glob_exclude;
56 +    struct SysctlSetting *next;
57 +} SysctlSetting;
58 +
59 +typedef struct SettingList {
60 +    struct SysctlSetting *head;
61 +    struct SysctlSetting *tail;
62 +} SettingList;
63 +
64 +#define GLOB_CHARS "*?["
65 +static inline bool string_is_glob(const char *p)
66 +{
67 +    return !!strpbrk(p, GLOB_CHARS);
68 +}
69 +
70 +
71  /* Function prototypes. */
72  static int pattern_match(const char *string, const char *pat);
73  static int DisplayAll(const char *restrict const path);
74 @@ -100,6 +124,81 @@ static void slashdot(char *restrict p, char old, char new)
75         }
76  }
77  
78 +static void setting_free(SysctlSetting *s) {
79 +    if (!s)
80 +       return;
81 +
82 +    free(s->key);
83 +    free(s->path);
84 +    free(s->value);
85 +    free(s);
86 +}
87 +
88 +static SysctlSetting *setting_new(
89 +       const char *key,
90 +       const char *value,
91 +       bool ignore_failure,
92 +    bool glob_exclude) {
93 +
94 +    SysctlSetting *s = NULL;
95 +    char *path = NULL;
96 +    int proc_len;
97 +
98 +    proc_len = strlen(PROC_PATH);
99 +    /* used to open the file */
100 +    path = xmalloc(strlen(key) + proc_len + 2);
101 +    strcpy(path, PROC_PATH);
102 +    if (key[0] == '-')
103 +        strcat(path + proc_len, key+1);
104 +    else
105 +        strcat(path + proc_len, key);
106 +    /* change . to / */
107 +    slashdot(path + proc_len, '.', '/');
108 +
109 +    s = xmalloc(sizeof(SysctlSetting));
110 +
111 +    *s = (SysctlSetting) {
112 +        .key = strdup(key),
113 +        .path = path,
114 +        .value = value? strdup(value): NULL,
115 +        .ignore_failure = ignore_failure,
116 +        .glob_exclude = glob_exclude,
117 +        .next = NULL,
118 +    };
119 +
120 +    return s;
121 +}
122 +
123 +static void settinglist_add(SettingList *l, SysctlSetting *s) {
124 +    SysctlSetting *old_tail;
125 +
126 +    if (!l)
127 +        return;
128 +
129 +    if (l->head == NULL)
130 +        l->head = s;
131 +
132 +    if (l->tail != NULL) {
133 +        old_tail = l->tail;
134 +        old_tail->next = s;
135 +    }
136 +    l->tail = s;
137 +}
138 +
139 +static SysctlSetting *settinglist_findpath(const SettingList *l, const char *path) {
140 +    SysctlSetting *node;
141 +
142 +    for (node=l->head; node != NULL; node = node->next) {
143 +        if (strcmp(node->path, path) == 0)
144 +            return node;
145 +    }
146 +    return NULL;
147 +}
148 +
149 +/* Function prototypes. */
150 +static int pattern_match(const char *string, const char *pat);
151 +static int DisplayAll(const char *restrict const path);
152 +
153  /*
154   * Display the usage format
155   */
156 @@ -115,6 +214,7 @@ static void __attribute__ ((__noreturn__))
157         fputs(_("  -A                   alias of -a\n"), out);
158         fputs(_("  -X                   alias of -a\n"), out);
159         fputs(_("      --deprecated     include deprecated parameters to listing\n"), out);
160 +       fputs(_("      --dry-run        Print the key and values but do not write\n"), out);
161         fputs(_("  -b, --binary         print value without new line\n"), out);
162         fputs(_("  -e, --ignore         ignore unknown variables errors\n"), out);
163         fputs(_("  -N, --names          print variable names without values\n"), out);
164 @@ -137,6 +237,39 @@ static void __attribute__ ((__noreturn__))
165         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
166  }
167  
168 +/*
169 + * Strip left/leading side of a string
170 + */
171 +static char *lstrip(char *line)
172 +{
173 +    char *start;
174 +
175 +    if (!line || !*line)
176 +        return line;
177 +
178 +    start = line;
179 +    while(isspace(*start)) start++;
180 +
181 +    return start;
182 +}
183 +
184 +/*
185 + * Strip right/trailing side of a string
186 + * by placing a \0
187 + */
188 +static void rstrip(char *line)
189 +{
190 +    char *end;
191 +
192 +    if (!line || !*line)
193 +        return;
194 +
195 +    end = line + strlen(line) - 1;
196 +    while(end > line && isspace(*end)) end--;
197 +
198 +    end[1] = '\0';
199 +}
200 +
201  /*
202   * Strip the leading and trailing spaces from a string
203   */
204 @@ -166,7 +299,7 @@ static char *StripLeadingAndTrailingSpaces(char *oneline)
205   */
206  static int ReadSetting(const char *restrict const name)
207  {
208 -       int rc = 0;
209 +       int rc = EXIT_SUCCESS;
210         char *restrict tmpname;
211         char *restrict outname;
212         ssize_t rlen;
213 @@ -198,7 +331,7 @@ static int ReadSetting(const char *restrict const name)
214         if (stat(tmpname, &ts) < 0) {
215                 if (!IgnoreError) {
216                         xwarn(_("cannot stat %s"), tmpname);
217 -                       rc = -1;
218 +                       rc = EXIT_FAILURE;
219                 }
220                 goto out;
221         }
222 @@ -215,7 +348,7 @@ static int ReadSetting(const char *restrict const name)
223         }
224  
225         if (pattern && !pattern_match(outname, pattern)) {
226 -               rc = 0;
227 +               rc = EXIT_SUCCESS;
228                 goto out;
229         }
230  
231 @@ -231,19 +364,19 @@ static int ReadSetting(const char *restrict const name)
232                 case ENOENT:
233                         if (!IgnoreError) {
234                                 xwarnx(_("\"%s\" is an unknown key"), outname);
235 -                               rc = -1;
236 +                               rc = EXIT_FAILURE;
237                         }
238                         break;
239                 case EACCES:
240                         xwarnx(_("permission denied on key '%s'"), outname);
241 -                       rc = -1;
242 +                       rc = EXIT_FAILURE;
243                         break;
244                 case EIO:           /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
245 -                       rc = -1;
246 +                       rc = EXIT_FAILURE;
247                         break;
248                 default:
249                         xwarn(_("reading key \"%s\""), outname);
250 -                       rc = -1;
251 +                       rc = EXIT_FAILURE;
252                         break;
253                 }
254         } else {
255 @@ -279,7 +412,7 @@ static int ReadSetting(const char *restrict const name)
256                         case EACCES:
257                                 xwarnx(_("permission denied on key '%s'"),
258                                        outname);
259 -                               rc = -1;
260 +                               rc = EXIT_FAILURE;
261                                 break;
262                         case EISDIR: {
263                                         size_t len;
264 @@ -291,11 +424,11 @@ static int ReadSetting(const char *restrict const name)
265                                         goto out;
266                                 }
267                         case EIO:           /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
268 -                               rc = -1;
269 +                               rc = EXIT_FAILURE;
270                                 break;
271                         default:
272                                 xwarnx(_("reading key \"%s\""), outname);
273 -                               rc = -1;
274 +                               rc = EXIT_FAILURE;
275                         case 0:
276                                 break;
277                         }
278 @@ -323,7 +456,7 @@ static int is_deprecated(char *filename)
279   */
280  static int DisplayAll(const char *restrict const path)
281  {
282 -       int rc = 0;
283 +       int rc = EXIT_SUCCESS;
284         int rc2;
285         DIR *restrict dp;
286         struct dirent *restrict de;
287 @@ -333,7 +466,7 @@ static int DisplayAll(const char *restrict const path)
288  
289         if (!dp) {
290                 xwarnx(_("unable to open directory \"%s\""), path);
291 -               rc = -1;
292 +               rc = EXIT_FAILURE;
293         } else {
294                 readdir(dp);    /* skip .  */
295                 readdir(dp);    /* skip .. */
296 @@ -369,130 +502,183 @@ static int DisplayAll(const char *restrict const path)
297  /*
298   * Write a sysctl setting
299   */
300 -static int WriteSetting(const char *setting)
301 -{
302 -       int rc = 0;
303 -       const char *name = setting;
304 -       const char *value;
305 -       const char *equals;
306 -       char *tmpname;
307 -       char *outname;
308 -       char *last_dot;
309 -       bool ignore_failure;
310 -
311 -       FILE *fp;
312 +static int WriteSetting(
313 +    const char *key,
314 +    const char *path,
315 +    const char *value,
316 +    const bool ignore_failure) {
317 +
318 +    int rc = EXIT_SUCCESS;
319 +    FILE *fp;
320         struct stat ts;
321  
322 -       if (!name)
323 -               /* probably don't want to display this err */
324 -               return 0;
325 -
326 -       equals = strchr(setting, '=');
327 -
328 -       if (!equals) {
329 -               xwarnx(_("\"%s\" must be of the form name=value"),
330 -                      setting);
331 -               return -1;
332 -       }
333 -
334 -       /* point to the value in name=value */
335 -       value = equals + 1;
336 -
337 -       if (!*name || name == equals) {
338 -               xwarnx(_("malformed setting \"%s\""), setting);
339 -               return -2;
340 -       }
341 -
342 -       ignore_failure = name[0] == '-';
343 -       if (ignore_failure)
344 -           name++;
345 -
346 -       /* used to open the file */
347 -       tmpname = xmalloc(equals - name + 1 + strlen(PROC_PATH));
348 -       strcpy(tmpname, PROC_PATH);
349 -       strncat(tmpname, name, (int) (equals - name));
350 -       tmpname[equals - name + strlen(PROC_PATH)] = 0;
351 -       /* change . to / */
352 -       slashdot(tmpname + strlen(PROC_PATH), '.', '/');
353 +    if (!key || !path)
354 +        return rc;
355  
356 -       /* used to display the output */
357 -       outname = xmalloc(equals - name + 1);
358 -       strncpy(outname, name, (int) (equals - name));
359 -       outname[equals - name] = 0;
360 -       /* change / to . */
361 -       slashdot(outname, '/', '.');
362 -       last_dot = strrchr(outname, '.');
363 -       if (last_dot != NULL && is_deprecated(last_dot + 1)) {
364 -               xwarnx(_("%s is deprecated, value not set"), outname);
365 -               goto out;
366 -        }
367 -
368 -       if (stat(tmpname, &ts) < 0) {
369 +       if (stat(path, &ts) < 0) {
370                 if (!IgnoreError) {
371 -                       xwarn(_("cannot stat %s"), tmpname);
372 -                       rc = -1;
373 +                       xwarn(_("cannot stat %s"), path);
374 +                       rc = EXIT_FAILURE;
375                 }
376 -               goto out;
377 +        return rc;
378         }
379  
380         if ((ts.st_mode & S_IWUSR) == 0) {
381 -               xwarn(_("setting key \"%s\""), outname);
382 -               goto out;
383 +               xwarn(_("setting key \"%s\""), key);
384 +        return rc;
385         }
386  
387         if (S_ISDIR(ts.st_mode)) {
388 -               xwarn(_("setting key \"%s\""), outname);
389 -               goto out;
390 +               xwarn(_("setting key \"%s\""), key);
391 +        return rc;
392         }
393  
394 -       fp = fprocopen(tmpname, "w");
395 -
396 -       if (!fp) {
397 -               switch (errno) {
398 -               case ENOENT:
399 -                       if (!IgnoreError) {
400 -                               xwarnx(_("\"%s\" is an unknown key%s"), outname, (ignore_failure?_(", ignoring"):""));
401 +    if (!DryRun) {
402 +        if ((fp = fprocopen(path, "w")) == NULL) {
403 +            switch (errno) {
404 +            case ENOENT:
405 +                if (!IgnoreError) {
406 +                    xwarnx(_("\"%s\" is an unknown key%s"),
407 +                           key, (ignore_failure?_(", ignoring"):""));
408                                 if (!ignore_failure)
409 -                                   rc = -1;
410 +                                   rc = EXIT_FAILURE;
411                         }
412                         break;
413 -               case EPERM:
414 -               case EROFS:
415 -               case EACCES:
416 -                       xwarnx(_("permission denied on key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
417 -                       break;
418 -               default:
419 -                       xwarn(_("setting key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
420 -                       break;
421 -               }
422 -               if (!ignore_failure && errno != ENOENT)
423 -                   rc = -1;
424 -       } else {
425 -               rc = fprintf(fp, "%s\n", value);
426 -               if (0 < rc)
427 -                       rc = 0;
428 -               if (close_stream(fp) != 0)
429 -                       xwarn(_("setting key \"%s\""), outname);
430 -               else if (rc == 0 && !Quiet) {
431 -                       if (NameOnly) {
432 -                               fprintf(stdout, "%s\n", outname);
433 -                       } else {
434 -                               if (PrintName) {
435 -                                       fprintf(stdout, "%s = %s\n",
436 -                                               outname, value);
437 -                               } else {
438 -                                       if (PrintNewline)
439 -                                               fprintf(stdout, "%s\n", value);
440 -                                       else
441 -                                               fprintf(stdout, "%s", value);
442 -                               }
443 -                       }
444 -               }
445 -       }
446 -      out:
447 -       free(tmpname);
448 -       free(outname);
449 -       return rc;
450 +            case EPERM:
451 +            case EROFS:
452 +            case EACCES:
453 +                xwarnx(_("permission denied on key \"%s\"%s"),
454 +                       key, (ignore_failure?_(", ignoring"):""));
455 +                break;
456 +            default:
457 +                xwarn(_("setting key \"%s\"%s"),
458 +                      key, (ignore_failure?_(", ignoring"):""));
459 +                break;
460 +            }
461 +            if (!ignore_failure && errno != ENOENT)
462 +                   rc = EXIT_FAILURE;
463 +        } else {
464 +           if (0 < fprintf(fp, "%s\n", value))
465 +               rc = EXIT_SUCCESS;
466 +            if (close_stream(fp) != 0) {
467 +                xwarn(_("setting key \"%s\""), path);
468 +                return rc;
469 +            }
470 +        }
471 +    }
472 +    if ((rc == EXIT_SUCCESS && !Quiet) || DryRun) {
473 +        if (NameOnly) {
474 +            printf("%s\n", value);
475 +        } else {
476 +            if (PrintName) {
477 +                printf("%s = %s\n", path, value);
478 +            } else {
479 +                if (PrintNewline)
480 +                    printf("%s\n", value);
481 +                else
482 +                    printf("%s", value);
483 +            }
484 +        }
485 +    }
486 +    return rc;
487 +}
488 +
489 +/*
490 + * parse each configuration line, there are multiple ways of specifying
491 + * a key/value here:
492 + *
493 + * key = value                               simple setting
494 + * -key = value                              ignore errors
495 + * key.pattern.*.with.glob = value           set keys that match glob
496 + * -key.pattern.exclude.with.glob            dont set this value
497 + * key.pattern.override.with.glob = value    set this glob match to value
498 + *
499 + */
500 +
501 +static SysctlSetting *parse_setting_line(
502 +    const char *path,
503 +    const int linenum,
504 +    char *line)
505 +{
506 +    SysctlSetting *s;
507 +    char *key;
508 +    char *value;
509 +    bool glob_exclude = FALSE;
510 +    bool ignore_failure = FALSE;
511 +
512 +    key = lstrip(line);
513 +    if (strlen(key) < 2)
514 +        return NULL;
515 +
516 +    /* skip over comments */
517 +    if (key[0] == '#' || key[0] == ';')
518 +        return NULL;
519 +
520 +    if (pattern && !pattern_match(key, pattern))
521 +        return NULL;
522 +
523 +    value = strchr(key, '=');
524 +    if (value == NULL) {
525 +        if (key[0] == '-') {
526 +            glob_exclude = TRUE;
527 +            key++;
528 +            value = NULL;
529 +            rstrip(key);
530 +        } else {
531 +            xwarnx(_("%s(%d): invalid syntax, continuing..."),
532 +                   path, linenum);
533 +            return NULL;
534 +        }
535 +    } else {
536 +        value[0]='\0';
537 +        if (key[0] == '-') {
538 +            ignore_failure = TRUE;
539 +            key++;
540 +        }
541 +        value++; // skip over =
542 +        value=lstrip(value);
543 +        rstrip(value);
544 +        rstrip(key);
545 +    }
546 +    return setting_new(key, value, ignore_failure, glob_exclude);
547 +}
548 +
549 +/* Go through the setting list, expand and sort out
550 + * setting globs and actually write the settings out
551 + */
552 +static int write_setting_list(const SettingList *sl)
553 +{
554 +    SysctlSetting *node;
555 +    int rc = EXIT_SUCCESS;
556 +
557 +    for (node=sl->head; node != NULL; node=node->next) {
558 +        if (node->glob_exclude)
559 +            continue;
560 +
561 +        if (string_is_glob(node->path)) {
562 +            char *gl_path;
563 +            glob_t globbuf;
564 +            int i;
565 +
566 +            if (glob(node->path, 0, NULL, &globbuf) != 0)
567 +                continue;
568 +
569 +            for(i=0; i < globbuf.gl_pathc; i++) {
570 +                if (settinglist_findpath(sl, globbuf.gl_pathv[i]))
571 +                    continue; // override or exclude
572 +
573 +                rc |= WriteSetting(node->key, globbuf.gl_pathv[i], node->value,
574 +                                   node->ignore_failure);
575 +            }
576 +        } else {
577 +            rc |= WriteSetting(node->key, node->path, node->value,
578 +                               node->ignore_failure);
579 +        }
580 +
581 +
582 +    }
583 +
584 +    return rc;
585  }
586  
587  static int pattern_match(const char *string, const char *pat)
588 @@ -513,12 +699,12 @@ static int pattern_match(const char *string, const char *pat)
589   * Preload the sysctl's from the conf file.  We parse the file and then
590   * reform it (strip out whitespace).
591   */
592 -static int Preload(const char *restrict const filename)
593 +static int Preload(SettingList *setlist, const char *restrict const filename)
594  {
595         FILE *fp;
596         char *t;
597         int n = 0;
598 -       int rc = 0;
599 +       int rc = EXIT_SUCCESS;
600         ssize_t rlen;
601         char *name, *value;
602         glob_t globbuf;
603 @@ -547,62 +733,26 @@ static int Preload(const char *restrict const filename)
604                     ? stdin : fopen(globbuf.gl_pathv[j], "r");
605                 if (!fp) {
606                         xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
607 -                       rc = -1;
608 -                       goto out;
609 +            return EXIT_FAILURE;
610                 }
611  
612                 while ((rlen =  getline(&iobuf, &iolen, fp)) > 0) {
613                         size_t offset;
614 +            SysctlSetting *setting;
615  
616                         n++;
617  
618                         if (rlen < 2)
619                                 continue;
620  
621 -                       t = StripLeadingAndTrailingSpaces(iobuf);
622 -                       if (strlen(t) < 2)
623 -                               continue;
624 -
625 -                       if (*t == '#' || *t == ';')
626 -                               continue;
627 -
628 -                       name = strtok(t, "=");
629 -                       if (!name || !*name) {
630 -                               xwarnx(_("%s(%d): invalid syntax, continuing..."),
631 -                                      globbuf.gl_pathv[j], n);
632 -                               continue;
633 -                       }
634 -
635 -                       StripLeadingAndTrailingSpaces(name);
636 -
637 -                       if (pattern && !pattern_match(name, pattern))
638 -                               continue;
639 -
640 -                       offset = strlen(name);
641 -                       memmove(&iobuf[0], name, offset);
642 -                       iobuf[offset++] = '=';
643 -
644 -                       value = strtok(NULL, "\n\r");
645 -                       if (!value || !*value) {
646 -                               xwarnx(_("%s(%d): invalid syntax, continuing..."),
647 -                                      globbuf.gl_pathv[j], n);
648 -                               continue;
649 -                       }
650 -
651 -                       while ((*value == ' ' || *value == '\t') && *value != 0)
652 -                               value++;
653 -
654 -                       /* should NameOnly affect this? */
655 -                       memmove(&iobuf[offset], value, strlen(value));
656 -                       offset += strlen(value);
657 -                       iobuf[offset] = '\0';
658 -
659 -                       rc |= WriteSetting(iobuf);
660 +            if ( (setting = parse_setting_line(globbuf.gl_pathv[j], n, iobuf))
661 +                 == NULL)
662 +                continue;
663 +            settinglist_add(setlist, setting);
664                 }
665  
666                 fclose(fp);
667         }
668 -out:
669         return rc;
670  }
671  
672 @@ -618,7 +768,7 @@ static int sortpairs(const void *A, const void *B)
673         return strcmp(a->name, b->name);
674  }
675  
676 -static int PreloadSystem(void)
677 +static int PreloadSystem(SettingList *setlist)
678  {
679         unsigned di, i;
680         const char *dirs[] = {
681 @@ -630,7 +780,7 @@ static int PreloadSystem(void)
682         };
683         struct pair **cfgs = NULL;
684         unsigned ncfgs = 0;
685 -       int rc = 0;
686 +       int rc = EXIT_SUCCESS;
687         struct stat ts;
688         enum { nprealloc = 16 };
689  
690 @@ -688,14 +838,14 @@ static int PreloadSystem(void)
691         for (i = 0; i < ncfgs; ++i) {
692                 if (!Quiet)
693                         printf(_("* Applying %s ...\n"), cfgs[i]->value);
694 -               rc |= Preload(cfgs[i]->value);
695 +               rc |= Preload(setlist, cfgs[i]->value);
696         }
697  
698  
699         if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
700                 if (!Quiet)
701                         printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
702 -               rc |= Preload(DEFAULT_PRELOAD);
703 +               rc |= Preload(setlist, DEFAULT_PRELOAD);
704         }
705  
706         /* cleaning */
707 @@ -717,15 +867,19 @@ int main(int argc, char *argv[])
708         bool preloadfileOpt = false;
709         int ReturnCode = 0;
710         int c;
711 +    int rc;
712         const char *preloadfile = NULL;
713 +    SettingList *setlist;
714  
715         enum {
716                 DEPRECATED_OPTION = CHAR_MAX + 1,
717 -               SYSTEM_OPTION
718 +               SYSTEM_OPTION,
719 +        DRYRUN_OPTION
720         };
721         static const struct option longopts[] = {
722                 {"all", no_argument, NULL, 'a'},
723                 {"deprecated", no_argument, NULL, DEPRECATED_OPTION},
724 +               {"dry-run", no_argument, NULL, DRYRUN_OPTION},
725                 {"binary", no_argument, NULL, 'b'},
726                 {"ignore", no_argument, NULL, 'e'},
727                 {"names", no_argument, NULL, 'N'},
728 @@ -753,6 +907,10 @@ int main(int argc, char *argv[])
729         IgnoreError = false;
730         Quiet = false;
731         IgnoreDeprecated = true;
732 +    DryRun = false;
733 +    setlist = xmalloc(sizeof(SettingList));
734 +    setlist->head = NULL;
735 +    setlist->tail = NULL;
736  
737         if (argc < 2)
738                 Usage(stderr);
739 @@ -805,7 +963,12 @@ int main(int argc, char *argv[])
740                         break;
741                 case SYSTEM_OPTION:
742                         IgnoreError = true;
743 -                       return PreloadSystem();
744 +                       rc |= PreloadSystem(setlist);
745 +            rc |= write_setting_list(setlist);
746 +            return rc;
747 +        case DRYRUN_OPTION:
748 +            DryRun = true;
749 +            break;
750                 case 'r':
751                         pattern = xstrdup(optarg);
752                         break;
753 @@ -833,15 +996,16 @@ int main(int argc, char *argv[])
754                 int ret = EXIT_SUCCESS, i;
755                 if (!preloadfile) {
756                         if (!argc) {
757 -                               ret |= Preload(DEFAULT_PRELOAD);
758 +                               ret |= Preload(setlist, DEFAULT_PRELOAD);
759                         }
760                 } else {
761                         /* This happens when -pfile option is
762                          * used without space. */
763 -                       ret |= Preload(preloadfile);
764 +                       ret |= Preload(setlist, preloadfile);
765                 }
766                 for (i = 0; i < argc; i++)
767 -                       ret |= Preload(argv[i]);
768 +                       ret |= Preload(setlist, argv[i]);
769 +        ret |= write_setting_list(setlist);
770                 return ret;
771         }
772  
773 @@ -855,9 +1019,14 @@ int main(int argc, char *argv[])
774                       program_invocation_short_name);
775  
776         for ( ; *argv; argv++) {
777 -               if (WriteMode || strchr(*argv, '='))
778 -                       ReturnCode += WriteSetting(*argv);
779 -               else
780 +               if (WriteMode || strchr(*argv, '=')) {
781 +            SysctlSetting *s;
782 +            if ( (s = parse_setting_line("command line", 0, *argv)) != NULL)
783 +                ReturnCode |= WriteSetting(s->key, s->path, s->value,
784 +                                           s->ignore_failure);
785 +            else
786 +                ReturnCode |= EXIT_FAILURE;
787 +        } else
788                         ReturnCode += ReadSetting(*argv);
789         }
790         return ReturnCode;
791 diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp
792 index 4156c3b..ecdc0bf 100644
793 --- a/testsuite/config/unix.exp
794 +++ b/testsuite/config/unix.exp
795 @@ -136,6 +136,15 @@ proc expect_table_dsc { test match_header match_item } {
796      #}
797  }
798  
799 +proc expect_spawn_retval { test retval } {
800 +    foreach {pid spawnid os_error_flag value} [wait] break
801 +
802 +    if {$value == $retval} {
803 +      return
804 +    }
805 +    fail "$test (exit value)"
806 +}
807 +
808  proc make_pipeproc { } {
809      global pipeproc_pid pipeproc_spawnid topdir
810  
811 diff --git a/testsuite/sysctl.test/sysctl_write.exp b/testsuite/sysctl.test/sysctl_write.exp
812 new file mode 100644
813 index 0000000..5a74dec
814 --- /dev/null
815 +++ b/testsuite/sysctl.test/sysctl_write.exp
816 @@ -0,0 +1,29 @@
817 +
818 +set sysctl ${topdir}sysctl
819 +
820 +set test "sysctl write from command line"
821 +spawn $sysctl --dry-run kernel.hostname=procps-test
822 +expect_pass "$test" "/proc/sys/kernel/hostname = procps-test"
823 +
824 +set test "sysctl write from configuration file"
825 +spawn $sysctl --dry-run -f ${topdir}testsuite/sysctl_glob_test.conf
826 +expect_pass "$test" "/proc/sys/fs/protected_fifos = 2\\s+/proc/sys/fs/protected_symlinks = 2\\s+/proc/sys/fs/protected_hardlinks = 1"
827 +
828 +set hostname_file "/proc/sys/kernel/hostname"
829 +if {[file exists ${hostname_file}]} {
830 +  if {[file writable ${hostname_file}]} {
831 +    unsupported "sysctl write: hostname file is writable"
832 +  } else {
833 +    set test "sysctl write unwritable file"
834 +    spawn $sysctl -q kernel.hostname=procpstest
835 +    expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\"\\s*$"
836 +    expect_spawn_retval "$test" 1
837 +
838 +    set test "sysctl write unwritable file ignored"
839 +    spawn $sysctl -q -- -kernel.hostname=procpstest
840 +    expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\", ignoring\\s*$"
841 +    expect_spawn_retval "$test" 0
842 +  }
843 +} else {
844 +  unsupported "sysctl write: hostname file doe not exist"
845 +}
846 diff --git a/testsuite/sysctl_glob_test.conf b/testsuite/sysctl_glob_test.conf
847 new file mode 100644
848 index 0000000..45ae904
849 --- /dev/null
850 +++ b/testsuite/sysctl_glob_test.conf
851 @@ -0,0 +1,6 @@
852 +#
853 +# Test configuration for for glob in sysctl
854 +#
855 +fs.protected_* = 2
856 +fs.protected_hardlinks = 1
857 +-fs.protected_regular
This page took 0.24209 seconds and 3 git commands to generate.