Bump to procps-ng 3.3.16
[platform/upstream/procps-ng.git] / sysctl.c
1 /*
2  * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
3  *
4  * "Copyright 1999 George Staikos
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  *
20  * Changelog:
21  *            v1.01:
22  *                   - added -p <preload> to preload values from a file
23  *            Horms:
24  *                   - added -q to be quiet when modifying values
25  *
26  * Changes by Albert Cahalan, 2002.
27  */
28
29 #include <dirent.h>
30 #include <errno.h>
31 #include <getopt.h>
32 #include <glob.h>
33 #include <libgen.h>
34 #include <limits.h>
35 #include <regex.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43
44 #include "c.h"
45 #include "fileutils.h"
46 #include "nls.h"
47 #include "xalloc.h"
48 #include "proc/procps.h"
49 #include "proc/version.h"
50
51 extern FILE *fprocopen(const char *, const char *);
52
53 /*
54  *    Globals...
55  */
56 static const char PROC_PATH[] = "/proc/sys/";
57 static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
58 static const char *DEPRECATED[] = {
59         "base_reachable_time",
60         "retrans_time",
61         ""
62 };
63 static bool IgnoreDeprecated;
64 static bool NameOnly;
65 static bool PrintName;
66 static bool PrintNewline;
67 static bool IgnoreError;
68 static bool Quiet;
69 static char *pattern;
70
71 #define LINELEN 4096
72 static char *iobuf;
73 static size_t iolen = LINELEN;
74
75 /* Function prototypes. */
76 static int pattern_match(const char *string, const char *pat);
77 static int DisplayAll(const char *restrict const path);
78
79 static void slashdot(char *restrict p, char old, char new)
80 {
81         int warned = 1;
82         p = strpbrk(p, "/.");
83         if (!p)
84                 /* nothing -- can't be, but oh well */
85                 return;
86         if (*p == new)
87                 /* already in desired format */
88                 return;
89         while (p) {
90                 char c = *p;
91                 if ((*(p + 1) == '/' || *(p + 1) == '.') && warned) {
92                         xwarnx(_("separators should not be repeated: %s"), p);
93                         warned = 0;
94                 }
95                 if (c == old)
96                         *p = new;
97                 if (c == new)
98                         *p = old;
99                 p = strpbrk(p + 1, "/.");
100         }
101 }
102
103 /*
104  * Display the usage format
105  */
106 static void __attribute__ ((__noreturn__))
107     Usage(FILE * out)
108 {
109         fputs(USAGE_HEADER, out);
110         fprintf(out,
111               _(" %s [options] [variable[=value] ...]\n"),
112                 program_invocation_short_name);
113         fputs(USAGE_OPTIONS, out);
114         fputs(_("  -a, --all            display all variables\n"), out);
115         fputs(_("  -A                   alias of -a\n"), out);
116         fputs(_("  -X                   alias of -a\n"), out);
117         fputs(_("      --deprecated     include deprecated parameters to listing\n"), out);
118         fputs(_("  -b, --binary         print value without new line\n"), out);
119         fputs(_("  -e, --ignore         ignore unknown variables errors\n"), out);
120         fputs(_("  -N, --names          print variable names without values\n"), out);
121         fputs(_("  -n, --values         print only values of the given variable(s)\n"), out);
122         fputs(_("  -p, --load[=<file>]  read values from file\n"), out);
123         fputs(_("  -f                   alias of -p\n"), out);
124         fputs(_("      --system         read values from all system directories\n"), out);
125         fputs(_("  -r, --pattern <expression>\n"
126                 "                       select setting that match expression\n"), out);
127         fputs(_("  -q, --quiet          do not echo variable set\n"), out);
128         fputs(_("  -w, --write          enable writing a value to variable\n"), out);
129         fputs(_("  -o                   does nothing\n"), out);
130         fputs(_("  -x                   does nothing\n"), out);
131         fputs(_("  -d                   alias of -h\n"), out);
132         fputs(USAGE_SEPARATOR, out);
133         fputs(USAGE_HELP, out);
134         fputs(USAGE_VERSION, out);
135         fprintf(out, USAGE_MAN_TAIL("sysctl(8)"));
136
137         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
138 }
139
140 /*
141  * Strip the leading and trailing spaces from a string
142  */
143 static char *StripLeadingAndTrailingSpaces(char *oneline)
144 {
145         char *t;
146
147         if (!oneline || !*oneline)
148                 return oneline;
149
150         t = oneline;
151         t += strlen(oneline) - 1;
152
153         while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline)
154                 *t-- = 0;
155
156         t = oneline;
157
158         while ((*t == ' ' || *t == '\t') && *t != 0)
159                 t++;
160
161         return t;
162 }
163
164 /*
165  * Read a sysctl setting
166  */
167 static int ReadSetting(const char *restrict const name)
168 {
169         int rc = 0;
170         char *restrict tmpname;
171         char *restrict outname;
172         ssize_t rlen;
173         FILE *restrict fp;
174         struct stat ts;
175
176         if (!name || !*name) {
177                 xwarnx(_("\"%s\" is an unknown key"), name);
178                 return -1;
179         }
180
181         /* used to display the output */
182         outname = xstrdup(name);
183         /* change / to . */
184         slashdot(outname, '/', '.');
185
186         /* used to open the file */
187         tmpname = xmalloc(strlen(name) + strlen(PROC_PATH) + 2);
188         strcpy(tmpname, PROC_PATH);
189         strcat(tmpname, name);
190         /* change . to / */
191         slashdot(tmpname + strlen(PROC_PATH), '.', '/');
192
193         /* used to display the output */
194         outname = xstrdup(name);
195         /* change / to . */
196         slashdot(outname, '/', '.');
197
198         if (stat(tmpname, &ts) < 0) {
199                 if (!IgnoreError) {
200                         xwarn(_("cannot stat %s"), tmpname);
201                         rc = -1;
202                 }
203                 goto out;
204         }
205         if ((ts.st_mode & S_IRUSR) == 0)
206                 goto out;
207
208         if (S_ISDIR(ts.st_mode)) {
209                 size_t len;
210                 len = strlen(tmpname);
211                 tmpname[len] = '/';
212                 tmpname[len + 1] = '\0';
213                 rc = DisplayAll(tmpname);
214                 goto out;
215         }
216
217         if (pattern && !pattern_match(outname, pattern)) {
218                 rc = 0;
219                 goto out;
220         }
221
222         if (NameOnly) {
223                 fprintf(stdout, "%s\n", outname);
224                 goto out;
225         }
226
227         fp = fprocopen(tmpname, "r");
228
229         if (!fp) {
230                 switch (errno) {
231                 case ENOENT:
232                         if (!IgnoreError) {
233                                 xwarnx(_("\"%s\" is an unknown key"), outname);
234                                 rc = -1;
235                         }
236                         break;
237                 case EACCES:
238                         xwarnx(_("permission denied on key '%s'"), outname);
239                         rc = -1;
240                         break;
241                 case EIO:           /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
242                         rc = -1;
243                         break;
244                 default:
245                         xwarn(_("reading key \"%s\""), outname);
246                         rc = -1;
247                         break;
248                 }
249         } else {
250                 errno = 0;
251                 if ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
252                         /* this loop is required, see
253                          * /sbin/sysctl -a | egrep -6 dev.cdrom.info
254                          */
255                         do {
256                                 char *nlptr;
257                                 if (PrintName) {
258                                         fprintf(stdout, "%s = ", outname);
259                                         do {
260                                                 fprintf(stdout, "%s", iobuf);
261                                                 nlptr = &iobuf[strlen(iobuf) - 1];
262                                                 /* already has the \n in it */
263                                                 if (*nlptr == '\n')
264                                                         break;
265                                         } while ((rlen = getline(&iobuf, &iolen, fp)) > 0);
266                                         if (*nlptr != '\n')
267                                                 putchar('\n');
268                                 } else {
269                                         if (!PrintNewline) {
270                                                 nlptr = strchr(iobuf, '\n');
271                                                 if (nlptr)
272                                                         *nlptr = '\0';
273                                         }
274                                         fprintf(stdout, "%s", iobuf);
275                                 }
276                         } while ((rlen = getline(&iobuf, &iolen, fp)) > 0);
277                 } else {
278                         switch (errno) {
279                         case EACCES:
280                                 xwarnx(_("permission denied on key '%s'"),
281                                        outname);
282                                 rc = -1;
283                                 break;
284                         case EISDIR: {
285                                         size_t len;
286                                         len = strlen(tmpname);
287                                         tmpname[len] = '/';
288                                         tmpname[len + 1] = '\0';
289                                         fclose(fp);
290                                         rc = DisplayAll(tmpname);
291                                         goto out;
292                                 }
293                         case EIO:           /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
294                                 rc = -1;
295                                 break;
296                         default:
297                                 xwarnx(_("reading key \"%s\""), outname);
298                                 rc = -1;
299                         case 0:
300                                 break;
301                         }
302                 }
303                 fclose(fp);
304         }
305       out:
306         free(tmpname);
307         free(outname);
308         return rc;
309 }
310
311 static int is_deprecated(char *filename)
312 {
313         int i;
314         for (i = 0; strlen(DEPRECATED[i]); i++) {
315                 if (strcmp(DEPRECATED[i], filename) == 0)
316                         return 1;
317         }
318         return 0;
319 }
320
321 /*
322  * Display all the sysctl settings
323  */
324 static int DisplayAll(const char *restrict const path)
325 {
326         int rc = 0;
327         int rc2;
328         DIR *restrict dp;
329         struct dirent *restrict de;
330         struct stat ts;
331
332         dp = opendir(path);
333
334         if (!dp) {
335                 xwarnx(_("unable to open directory \"%s\""), path);
336                 rc = -1;
337         } else {
338                 readdir(dp);    /* skip .  */
339                 readdir(dp);    /* skip .. */
340                 while ((de = readdir(dp))) {
341                         char *restrict tmpdir;
342                         if (IgnoreDeprecated && is_deprecated(de->d_name))
343                                 continue;
344                         tmpdir =
345                             (char *restrict) xmalloc(strlen(path) +
346                                                      strlen(de->d_name) +
347                                                      2);
348                         sprintf(tmpdir, "%s%s", path, de->d_name);
349                         rc2 = stat(tmpdir, &ts);
350                         if (rc2 != 0) {
351                                 xwarn(_("cannot stat %s"), tmpdir);
352                         } else {
353                                 if (S_ISDIR(ts.st_mode)) {
354                                         strcat(tmpdir, "/");
355                                         DisplayAll(tmpdir);
356                                 } else {
357                                         rc |=
358                                             ReadSetting(tmpdir +
359                                                         strlen(PROC_PATH));
360                                 }
361                         }
362                         free(tmpdir);
363                 }
364                 closedir(dp);
365         }
366         return rc;
367 }
368
369 /*
370  * Write a sysctl setting
371  */
372 static int WriteSetting(const char *setting)
373 {
374         int rc = 0;
375         const char *name = setting;
376         const char *value;
377         const char *equals;
378         char *tmpname;
379         char *outname;
380         char *last_dot;
381         bool ignore_failure;
382
383         FILE *fp;
384         struct stat ts;
385
386         if (!name)
387                 /* probably don't want to display this err */
388                 return 0;
389
390         equals = strchr(setting, '=');
391
392         if (!equals) {
393                 xwarnx(_("\"%s\" must be of the form name=value"),
394                        setting);
395                 return -1;
396         }
397
398         /* point to the value in name=value */
399         value = equals + 1;
400
401         if (!*name || name == equals) {
402                 xwarnx(_("malformed setting \"%s\""), setting);
403                 return -2;
404         }
405
406         ignore_failure = name[0] == '-';
407         if (ignore_failure)
408             name++;
409
410         /* used to open the file */
411         tmpname = xmalloc(equals - name + 1 + strlen(PROC_PATH));
412         strcpy(tmpname, PROC_PATH);
413         strncat(tmpname, name, (int) (equals - name));
414         tmpname[equals - name + strlen(PROC_PATH)] = 0;
415         /* change . to / */
416         slashdot(tmpname + strlen(PROC_PATH), '.', '/');
417
418         /* used to display the output */
419         outname = xmalloc(equals - name + 1);
420         strncpy(outname, name, (int) (equals - name));
421         outname[equals - name] = 0;
422         /* change / to . */
423         slashdot(outname, '/', '.');
424         last_dot = strrchr(outname, '.');
425         if (last_dot != NULL && is_deprecated(last_dot + 1)) {
426                 xwarnx(_("%s is deprecated, value not set"), outname);
427                 goto out;
428         }
429
430         if (stat(tmpname, &ts) < 0) {
431                 if (!IgnoreError) {
432                         xwarn(_("cannot stat %s"), tmpname);
433                         rc = -1;
434                 }
435                 goto out;
436         }
437
438         if ((ts.st_mode & S_IWUSR) == 0) {
439                 xwarn(_("setting key \"%s\""), outname);
440                 goto out;
441         }
442
443         if (S_ISDIR(ts.st_mode)) {
444                 xwarn(_("setting key \"%s\""), outname);
445                 goto out;
446         }
447
448         fp = fprocopen(tmpname, "w");
449
450         if (!fp) {
451                 switch (errno) {
452                 case ENOENT:
453                         if (!IgnoreError) {
454                                 xwarnx(_("\"%s\" is an unknown key%s"), outname, (ignore_failure?_(", ignoring"):""));
455                                 if (!ignore_failure)
456                                     rc = -1;
457                         }
458                         break;
459                 case EPERM:
460                 case EROFS:
461                 case EACCES:
462                         xwarnx(_("permission denied on key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
463                         break;
464                 default:
465                         xwarn(_("setting key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
466                         break;
467                 }
468                 if (!ignore_failure && errno != ENOENT)
469                     rc = -1;
470         } else {
471                 rc = fprintf(fp, "%s\n", value);
472                 if (0 < rc)
473                         rc = 0;
474                 if (close_stream(fp) != 0)
475                         xwarn(_("setting key \"%s\""), outname);
476                 else if (rc == 0 && !Quiet) {
477                         if (NameOnly) {
478                                 fprintf(stdout, "%s\n", outname);
479                         } else {
480                                 if (PrintName) {
481                                         fprintf(stdout, "%s = %s\n",
482                                                 outname, value);
483                                 } else {
484                                         if (PrintNewline)
485                                                 fprintf(stdout, "%s\n", value);
486                                         else
487                                                 fprintf(stdout, "%s", value);
488                                 }
489                         }
490                 }
491         }
492       out:
493         free(tmpname);
494         free(outname);
495         return rc;
496 }
497
498 static int pattern_match(const char *string, const char *pat)
499 {
500         int status;
501         regex_t re;
502
503         if (regcomp(&re, pat, REG_EXTENDED | REG_NOSUB) != 0)
504                 return (0);
505         status = regexec(&re, string, (size_t) 0, NULL, 0);
506         regfree(&re);
507         if (status != 0)
508                 return (0);
509         return (1);
510 }
511
512 /*
513  * Preload the sysctl's from the conf file.  We parse the file and then
514  * reform it (strip out whitespace).
515  */
516 static int Preload(const char *restrict const filename)
517 {
518         FILE *fp;
519         char *t;
520         int n = 0;
521         int rc = 0;
522         ssize_t rlen;
523         char *name, *value;
524         glob_t globbuf;
525         int globerr;
526         int globflg;
527         int j;
528
529         globflg = GLOB_NOCHECK;
530 #ifdef GLOB_BRACE
531         globflg |= GLOB_BRACE;
532 #endif
533 #ifdef GLOB_TILDE
534         globflg |= GLOB_TILDE;
535 #else
536         if (filename[0] == '~')
537                 xwarnx(_("GLOB_TILDE is not supported on your platform, "
538                          "the tilde in \"%s\" won't be expanded."), filename);
539 #endif
540         globerr = glob(filename, globflg, NULL, &globbuf);
541
542         if (globerr != 0 && globerr != GLOB_NOMATCH)
543                 xerr(EXIT_FAILURE, _("glob failed"));
544
545         for (j = 0; j < globbuf.gl_pathc; j++) {
546                 fp = (globbuf.gl_pathv[j][0] == '-' && !globbuf.gl_pathv[j][1])
547                     ? stdin : fopen(globbuf.gl_pathv[j], "r");
548                 if (!fp) {
549                         xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
550                         rc = -1;
551                         goto out;
552                 }
553
554                 while ((rlen =  getline(&iobuf, &iolen, fp)) > 0) {
555                         size_t offset;
556
557                         n++;
558
559                         if (rlen < 2)
560                                 continue;
561
562                         t = StripLeadingAndTrailingSpaces(iobuf);
563                         if (strlen(t) < 2)
564                                 continue;
565
566                         if (*t == '#' || *t == ';')
567                                 continue;
568
569                         name = strtok(t, "=");
570                         if (!name || !*name) {
571                                 xwarnx(_("%s(%d): invalid syntax, continuing..."),
572                                        globbuf.gl_pathv[j], n);
573                                 continue;
574                         }
575
576                         StripLeadingAndTrailingSpaces(name);
577
578                         if (pattern && !pattern_match(name, pattern))
579                                 continue;
580
581                         offset = strlen(name);
582                         memmove(&iobuf[0], name, offset);
583                         iobuf[offset++] = '=';
584
585                         value = strtok(NULL, "\n\r");
586                         if (!value || !*value) {
587                                 xwarnx(_("%s(%d): invalid syntax, continuing..."),
588                                        globbuf.gl_pathv[j], n);
589                                 continue;
590                         }
591
592                         while ((*value == ' ' || *value == '\t') && *value != 0)
593                                 value++;
594
595                         /* should NameOnly affect this? */
596                         memmove(&iobuf[offset], value, strlen(value));
597                         offset += strlen(value);
598                         iobuf[offset] = '\0';
599
600                         rc |= WriteSetting(iobuf);
601                 }
602
603                 fclose(fp);
604         }
605 out:
606         return rc;
607 }
608
609 struct pair {
610         char *name;
611         char *value;
612 };
613
614 static int sortpairs(const void *A, const void *B)
615 {
616         const struct pair *a = *(struct pair * const *) A;
617         const struct pair *b = *(struct pair * const *) B;
618         return strcmp(a->name, b->name);
619 }
620
621 static int PreloadSystem(void)
622 {
623         unsigned di, i;
624         const char *dirs[] = {
625                 "/run/sysctl.d",
626                 "/etc/sysctl.d",
627                 "/usr/local/lib/sysctl.d",
628                 "/usr/lib/sysctl.d",
629                 "/lib/sysctl.d",
630         };
631         struct pair **cfgs = NULL;
632         unsigned ncfgs = 0;
633         int rc = 0;
634         struct stat ts;
635         enum { nprealloc = 16 };
636
637         for (di = 0; di < sizeof(dirs) / sizeof(dirs[0]); ++di) {
638                 struct dirent *de;
639                 DIR *dp = opendir(dirs[di]);
640                 if (!dp)
641                         continue;
642
643                 while ((de = readdir(dp))) {
644                         if (!strcmp(de->d_name, ".")
645                             || !strcmp(de->d_name, ".."))
646                                 continue;
647                         if (strlen(de->d_name) < 5
648                             || strcmp(de->d_name + strlen(de->d_name) - 5, ".conf"))
649                                 continue;
650                         /* check if config already known */
651                         for (i = 0; i < ncfgs; ++i) {
652                                 if (cfgs && !strcmp(cfgs[i]->name, de->d_name))
653                                         break;
654                         }
655                         if (i < ncfgs)
656                                 /* already in */
657                                 continue;
658
659                         if (ncfgs % nprealloc == 0)
660                                 cfgs =
661                                     xrealloc(cfgs,
662                                              sizeof(struct pair *) * (ncfgs +
663                                                                       nprealloc));
664
665                         if (cfgs) {
666                                 cfgs[ncfgs] =
667                                     xmalloc(sizeof(struct pair) +
668                                             strlen(de->d_name) * 2 + 2 +
669                                             strlen(dirs[di]) + 1);
670                                 cfgs[ncfgs]->name =
671                                     (char *)cfgs[ncfgs] + sizeof(struct pair);
672                                 strcpy(cfgs[ncfgs]->name, de->d_name);
673                                 cfgs[ncfgs]->value =
674                                     (char *)cfgs[ncfgs] + sizeof(struct pair) +
675                                     strlen(cfgs[ncfgs]->name) + 1;
676                                 sprintf(cfgs[ncfgs]->value, "%s/%s", dirs[di],
677                                         de->d_name);
678                                 ncfgs++;
679                         } else {
680                                 xerrx(EXIT_FAILURE, _("internal error"));
681                         }
682
683                 }
684                 closedir(dp);
685         }
686         qsort(cfgs, ncfgs, sizeof(struct cfg *), sortpairs);
687
688         for (i = 0; i < ncfgs; ++i) {
689                 if (!Quiet)
690                         printf(_("* Applying %s ...\n"), cfgs[i]->value);
691                 rc |= Preload(cfgs[i]->value);
692         }
693
694
695         if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
696                 if (!Quiet)
697                         printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
698                 rc |= Preload(DEFAULT_PRELOAD);
699         }
700
701         /* cleaning */
702         for (i = 0; i < ncfgs; ++i) {
703                 free(cfgs[i]);
704         }
705         if (cfgs) free(cfgs);
706
707         return rc;
708 }
709
710 /*
711  * Main...
712  */
713 int main(int argc, char *argv[])
714 {
715         bool WriteMode = false;
716         bool DisplayAllOpt = false;
717         bool preloadfileOpt = false;
718         int ReturnCode = 0;
719         int c;
720         const char *preloadfile = NULL;
721
722         enum {
723                 DEPRECATED_OPTION = CHAR_MAX + 1,
724                 SYSTEM_OPTION
725         };
726         static const struct option longopts[] = {
727                 {"all", no_argument, NULL, 'a'},
728                 {"deprecated", no_argument, NULL, DEPRECATED_OPTION},
729                 {"binary", no_argument, NULL, 'b'},
730                 {"ignore", no_argument, NULL, 'e'},
731                 {"names", no_argument, NULL, 'N'},
732                 {"values", no_argument, NULL, 'n'},
733                 {"load", optional_argument, NULL, 'p'},
734                 {"quiet", no_argument, NULL, 'q'},
735                 {"write", no_argument, NULL, 'w'},
736                 {"system", no_argument, NULL, SYSTEM_OPTION},
737                 {"pattern", required_argument, NULL, 'r'},
738                 {"help", no_argument, NULL, 'h'},
739                 {"version", no_argument, NULL, 'V'},
740                 {NULL, 0, NULL, 0}
741         };
742
743 #ifdef HAVE_PROGRAM_INVOCATION_NAME
744         program_invocation_name = program_invocation_short_name;
745 #endif
746         setlocale(LC_ALL, "");
747         bindtextdomain(PACKAGE, LOCALEDIR);
748         textdomain(PACKAGE);
749         atexit(close_stdout);
750
751         PrintName = true;
752         PrintNewline = true;
753         IgnoreError = false;
754         Quiet = false;
755         IgnoreDeprecated = true;
756
757         if (argc < 2)
758                 Usage(stderr);
759
760         while ((c =
761                 getopt_long(argc, argv, "bneNwfp::qoxaAXr:Vdh", longopts,
762                             NULL)) != -1) {
763                 switch (c) {
764                 case 'b':
765                         /* This is "binary" format, which means more for BSD. */
766                         PrintNewline = false;
767                         /* FALL THROUGH */
768                 case 'n':
769                         PrintName = false;
770                         break;
771                 case 'e':
772                         /*
773                          * For FreeBSD, -e means a "%s=%s\n" format.
774                          * ("%s: %s\n" default). We (and NetBSD) use
775                          * "%s = %s\n" always, and -e to ignore errors.
776                          */
777                         IgnoreError = true;
778                         break;
779                 case 'N':
780                         NameOnly = true;
781                         break;
782                 case 'w':
783                         WriteMode = true;
784                         break;
785                 case 'f':       /* the NetBSD way */
786                 case 'p':
787                         preloadfileOpt = true;
788                         if (optarg)
789                                 preloadfile = optarg;
790                         break;
791                 case 'q':
792                         Quiet = true;
793                         break;
794                 case 'o':       /* BSD: binary values too, 1st 16 bytes in hex */
795                 case 'x':       /* BSD: binary values too, whole thing in hex */
796                         /* does nothing */ ;
797                         break;
798                 case 'a':       /* string and integer values (for Linux, all of them) */
799                 case 'A':       /* same as -a -o */
800                 case 'X':       /* same as -a -x */
801                         DisplayAllOpt = true;
802                         break;
803                 case DEPRECATED_OPTION:
804                         IgnoreDeprecated = false;
805                         break;
806                 case SYSTEM_OPTION:
807                         IgnoreError = true;
808                         return PreloadSystem();
809                 case 'r':
810                         pattern = xstrdup(optarg);
811                         break;
812                 case 'V':
813                         printf(PROCPS_NG_VERSION);
814                         return EXIT_SUCCESS;
815                 case 'd':       /* BSD: print description ("vm.kvm_size: Size of KVM") */
816                 case 'h':       /* BSD: human-readable (did FreeBSD 5 make -e default?) */
817                 case '?':
818                         Usage(stdout);
819                 default:
820                         Usage(stderr);
821                 }
822         }
823
824         argc -= optind;
825         argv += optind;
826
827         iobuf = xmalloc(iolen);
828
829         if (DisplayAllOpt)
830                 return DisplayAll(PROC_PATH);
831
832         if (preloadfileOpt) {
833                 int ret = EXIT_SUCCESS, i;
834                 if (!preloadfile) {
835                         if (!argc) {
836                                 ret |= Preload(DEFAULT_PRELOAD);
837                         }
838                 } else {
839                         /* This happens when -pfile option is
840                          * used without space. */
841                         ret |= Preload(preloadfile);
842                 }
843                 for (i = 0; i < argc; i++)
844                         ret |= Preload(argv[i]);
845                 return ret;
846         }
847
848         if (argc < 1)
849                 xerrx(EXIT_FAILURE, _("no variables specified\n"
850                                       "Try `%s --help' for more information."),
851                       program_invocation_short_name);
852         if (NameOnly && Quiet)
853                 xerrx(EXIT_FAILURE, _("options -N and -q cannot coexist\n"
854                                       "Try `%s --help' for more information."),
855                       program_invocation_short_name);
856
857         for ( ; *argv; argv++) {
858                 if (WriteMode || strchr(*argv, '='))
859                         ReturnCode += WriteSetting(*argv);
860                 else
861                         ReturnCode += ReadSetting(*argv);
862         }
863         return ReturnCode;
864 }