2 * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
4 * "Copyright 1999 George Staikos
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.
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.
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.
22 * - added -p <preload> to preload values from a file
24 * - added -q to be quiet when modifying values
26 * Changes by Albert Cahalan, 2002.
41 #include <sys/types.h>
45 #include "fileutils.h"
48 #include "proc/procps.h"
49 #include "proc/version.h"
51 extern FILE *fprocopen(const char *, const char *);
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",
63 static bool IgnoreDeprecated;
65 static bool PrintName;
66 static bool PrintNewline;
67 static bool IgnoreError;
73 static size_t iolen = LINELEN;
75 /* Function prototypes. */
76 static int pattern_match(const char *string, const char *pat);
77 static int DisplayAll(const char *restrict const path);
79 static void slashdot(char *restrict p, char old, char new)
84 /* nothing -- can't be, but oh well */
87 /* already in desired format */
91 if ((*(p + 1) == '/' || *(p + 1) == '.') && warned) {
92 xwarnx(_("separators should not be repeated: %s"), p);
99 p = strpbrk(p + 1, "/.");
104 * Display the usage format
106 static void __attribute__ ((__noreturn__))
109 fputs(USAGE_HEADER, 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)"));
137 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
141 * Strip the leading and trailing spaces from a string
143 static char *StripLeadingAndTrailingSpaces(char *oneline)
147 if (!oneline || !*oneline)
151 t += strlen(oneline) - 1;
153 while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline)
158 while ((*t == ' ' || *t == '\t') && *t != 0)
165 * Read a sysctl setting
167 static int ReadSetting(const char *restrict const name)
170 char *restrict tmpname;
171 char *restrict outname;
176 if (!name || !*name) {
177 xwarnx(_("\"%s\" is an unknown key"), name);
181 /* used to display the output */
182 outname = xstrdup(name);
184 slashdot(outname, '/', '.');
186 /* used to open the file */
187 tmpname = xmalloc(strlen(name) + strlen(PROC_PATH) + 2);
188 strcpy(tmpname, PROC_PATH);
189 strcat(tmpname, name);
191 slashdot(tmpname + strlen(PROC_PATH), '.', '/');
193 /* used to display the output */
194 outname = xstrdup(name);
196 slashdot(outname, '/', '.');
198 if (stat(tmpname, &ts) < 0) {
200 xwarn(_("cannot stat %s"), tmpname);
205 if ((ts.st_mode & S_IRUSR) == 0)
208 if (S_ISDIR(ts.st_mode)) {
210 len = strlen(tmpname);
212 tmpname[len + 1] = '\0';
213 rc = DisplayAll(tmpname);
217 if (pattern && !pattern_match(outname, pattern)) {
223 fprintf(stdout, "%s\n", outname);
227 fp = fprocopen(tmpname, "r");
233 xwarnx(_("\"%s\" is an unknown key"), outname);
238 xwarnx(_("permission denied on key '%s'"), outname);
241 case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
245 xwarn(_("reading key \"%s\""), outname);
251 if ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
252 /* this loop is required, see
253 * /sbin/sysctl -a | egrep -6 dev.cdrom.info
258 fprintf(stdout, "%s = ", outname);
260 fprintf(stdout, "%s", iobuf);
261 nlptr = &iobuf[strlen(iobuf) - 1];
262 /* already has the \n in it */
265 } while ((rlen = getline(&iobuf, &iolen, fp)) > 0);
270 nlptr = strchr(iobuf, '\n');
274 fprintf(stdout, "%s", iobuf);
276 } while ((rlen = getline(&iobuf, &iolen, fp)) > 0);
280 xwarnx(_("permission denied on key '%s'"),
286 len = strlen(tmpname);
288 tmpname[len + 1] = '\0';
290 rc = DisplayAll(tmpname);
293 case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
297 xwarnx(_("reading key \"%s\""), outname);
311 static int is_deprecated(char *filename)
314 for (i = 0; strlen(DEPRECATED[i]); i++) {
315 if (strcmp(DEPRECATED[i], filename) == 0)
322 * Display all the sysctl settings
324 static int DisplayAll(const char *restrict const path)
329 struct dirent *restrict de;
335 xwarnx(_("unable to open directory \"%s\""), path);
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))
345 (char *restrict) xmalloc(strlen(path) +
348 sprintf(tmpdir, "%s%s", path, de->d_name);
349 rc2 = stat(tmpdir, &ts);
351 xwarn(_("cannot stat %s"), tmpdir);
353 if (S_ISDIR(ts.st_mode)) {
370 * Write a sysctl setting
372 static int WriteSetting(const char *setting)
375 const char *name = setting;
387 /* probably don't want to display this err */
390 equals = strchr(setting, '=');
393 xwarnx(_("\"%s\" must be of the form name=value"),
398 /* point to the value in name=value */
401 if (!*name || name == equals) {
402 xwarnx(_("malformed setting \"%s\""), setting);
406 ignore_failure = name[0] == '-';
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;
416 slashdot(tmpname + strlen(PROC_PATH), '.', '/');
418 /* used to display the output */
419 outname = xmalloc(equals - name + 1);
420 strncpy(outname, name, (int) (equals - name));
421 outname[equals - name] = 0;
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);
430 if (stat(tmpname, &ts) < 0) {
432 xwarn(_("cannot stat %s"), tmpname);
438 if ((ts.st_mode & S_IWUSR) == 0) {
439 xwarn(_("setting key \"%s\""), outname);
443 if (S_ISDIR(ts.st_mode)) {
444 xwarn(_("setting key \"%s\""), outname);
448 fp = fprocopen(tmpname, "w");
454 xwarnx(_("\"%s\" is an unknown key%s"), outname, (ignore_failure?_(", ignoring"):""));
462 xwarnx(_("permission denied on key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
465 xwarn(_("setting key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
468 if (!ignore_failure && errno != ENOENT)
471 rc = fprintf(fp, "%s\n", value);
474 if (close_stream(fp) != 0)
475 xwarn(_("setting key \"%s\""), outname);
476 else if (rc == 0 && !Quiet) {
478 fprintf(stdout, "%s\n", outname);
481 fprintf(stdout, "%s = %s\n",
485 fprintf(stdout, "%s\n", value);
487 fprintf(stdout, "%s", value);
498 static int pattern_match(const char *string, const char *pat)
503 if (regcomp(&re, pat, REG_EXTENDED | REG_NOSUB) != 0)
505 status = regexec(&re, string, (size_t) 0, NULL, 0);
513 * Preload the sysctl's from the conf file. We parse the file and then
514 * reform it (strip out whitespace).
516 static int Preload(const char *restrict const filename)
529 globflg = GLOB_NOCHECK;
531 globflg |= GLOB_BRACE;
534 globflg |= GLOB_TILDE;
536 if (filename[0] == '~')
537 xwarnx(_("GLOB_TILDE is not supported on your platform, "
538 "the tilde in \"%s\" won't be expanded."), filename);
540 globerr = glob(filename, globflg, NULL, &globbuf);
542 if (globerr != 0 && globerr != GLOB_NOMATCH)
543 xerr(EXIT_FAILURE, _("glob failed"));
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");
549 xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
554 while ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
562 t = StripLeadingAndTrailingSpaces(iobuf);
566 if (*t == '#' || *t == ';')
569 name = strtok(t, "=");
570 if (!name || !*name) {
571 xwarnx(_("%s(%d): invalid syntax, continuing..."),
572 globbuf.gl_pathv[j], n);
576 StripLeadingAndTrailingSpaces(name);
578 if (pattern && !pattern_match(name, pattern))
581 offset = strlen(name);
582 memmove(&iobuf[0], name, offset);
583 iobuf[offset++] = '=';
585 value = strtok(NULL, "\n\r");
586 if (!value || !*value) {
587 xwarnx(_("%s(%d): invalid syntax, continuing..."),
588 globbuf.gl_pathv[j], n);
592 while ((*value == ' ' || *value == '\t') && *value != 0)
595 /* should NameOnly affect this? */
596 memmove(&iobuf[offset], value, strlen(value));
597 offset += strlen(value);
598 iobuf[offset] = '\0';
600 rc |= WriteSetting(iobuf);
614 static int sortpairs(const void *A, const void *B)
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);
621 static int PreloadSystem(void)
624 const char *dirs[] = {
627 "/usr/local/lib/sysctl.d",
631 struct pair **cfgs = NULL;
635 enum { nprealloc = 16 };
637 for (di = 0; di < sizeof(dirs) / sizeof(dirs[0]); ++di) {
639 DIR *dp = opendir(dirs[di]);
643 while ((de = readdir(dp))) {
644 if (!strcmp(de->d_name, ".")
645 || !strcmp(de->d_name, ".."))
647 if (strlen(de->d_name) < 5
648 || strcmp(de->d_name + strlen(de->d_name) - 5, ".conf"))
650 /* check if config already known */
651 for (i = 0; i < ncfgs; ++i) {
652 if (cfgs && !strcmp(cfgs[i]->name, de->d_name))
659 if (ncfgs % nprealloc == 0)
662 sizeof(struct pair *) * (ncfgs +
667 xmalloc(sizeof(struct pair) +
668 strlen(de->d_name) * 2 + 2 +
669 strlen(dirs[di]) + 1);
671 (char *)cfgs[ncfgs] + sizeof(struct pair);
672 strcpy(cfgs[ncfgs]->name, de->d_name);
674 (char *)cfgs[ncfgs] + sizeof(struct pair) +
675 strlen(cfgs[ncfgs]->name) + 1;
676 sprintf(cfgs[ncfgs]->value, "%s/%s", dirs[di],
680 xerrx(EXIT_FAILURE, _("internal error"));
686 qsort(cfgs, ncfgs, sizeof(struct cfg *), sortpairs);
688 for (i = 0; i < ncfgs; ++i) {
690 printf(_("* Applying %s ...\n"), cfgs[i]->value);
691 rc |= Preload(cfgs[i]->value);
695 if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
697 printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
698 rc |= Preload(DEFAULT_PRELOAD);
702 for (i = 0; i < ncfgs; ++i) {
705 if (cfgs) free(cfgs);
713 int main(int argc, char *argv[])
715 bool WriteMode = false;
716 bool DisplayAllOpt = false;
717 bool preloadfileOpt = false;
720 const char *preloadfile = NULL;
723 DEPRECATED_OPTION = CHAR_MAX + 1,
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'},
743 #ifdef HAVE_PROGRAM_INVOCATION_NAME
744 program_invocation_name = program_invocation_short_name;
746 setlocale(LC_ALL, "");
747 bindtextdomain(PACKAGE, LOCALEDIR);
749 atexit(close_stdout);
755 IgnoreDeprecated = true;
761 getopt_long(argc, argv, "bneNwfp::qoxaAXr:Vdh", longopts,
765 /* This is "binary" format, which means more for BSD. */
766 PrintNewline = false;
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.
785 case 'f': /* the NetBSD way */
787 preloadfileOpt = true;
789 preloadfile = optarg;
794 case 'o': /* BSD: binary values too, 1st 16 bytes in hex */
795 case 'x': /* BSD: binary values too, whole thing in hex */
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;
803 case DEPRECATED_OPTION:
804 IgnoreDeprecated = false;
808 return PreloadSystem();
810 pattern = xstrdup(optarg);
813 printf(PROCPS_NG_VERSION);
815 case 'd': /* BSD: print description ("vm.kvm_size: Size of KVM") */
816 case 'h': /* BSD: human-readable (did FreeBSD 5 make -e default?) */
827 iobuf = xmalloc(iolen);
830 return DisplayAll(PROC_PATH);
832 if (preloadfileOpt) {
833 int ret = EXIT_SUCCESS, i;
836 ret |= Preload(DEFAULT_PRELOAD);
839 /* This happens when -pfile option is
840 * used without space. */
841 ret |= Preload(preloadfile);
843 for (i = 0; i < argc; i++)
844 ret |= Preload(argv[i]);
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);
857 for ( ; *argv; argv++) {
858 if (WriteMode || strchr(*argv, '='))
859 ReturnCode += WriteSetting(*argv);
861 ReturnCode += ReadSetting(*argv);