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"
54 static const char PROC_PATH[] = "/proc/sys/";
55 static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
56 static const char *DEPRECATED[] = {
57 "base_reachable_time",
61 static bool IgnoreDeprecated;
63 static bool PrintName;
64 static bool PrintNewline;
65 static bool IgnoreError;
69 /* Function prototypes. */
70 static int pattern_match(const char *string, const char *pat);
71 static int DisplayAll(const char *restrict const path);
73 static void slashdot(char *restrict p, char old, char new)
78 /* nothing -- can't be, but oh well */
81 /* already in desired format */
85 if ((*(p + 1) == '/' || *(p + 1) == '.') && warned) {
86 xwarnx(_("separators should not be repeated: %s"), p);
93 p = strpbrk(p + 1, "/.");
98 * Display the usage format
100 static void __attribute__ ((__noreturn__))
103 fputs(USAGE_HEADER, out);
105 _(" %s [options] [variable[=value] ...]\n"),
106 program_invocation_short_name);
107 fputs(USAGE_OPTIONS, out);
108 fputs(_(" -a, --all display all variables\n"), out);
109 fputs(_(" -A alias of -a\n"), out);
110 fputs(_(" -X alias of -a\n"), out);
111 fputs(_(" --deprecated include deprecated parameters to listing\n"), out);
112 fputs(_(" -b, --binary print value without new line\n"), out);
113 fputs(_(" -e, --ignore ignore unknown variables errors\n"), out);
114 fputs(_(" -N, --names print variable names without values\n"), out);
115 fputs(_(" -n, --values print only values of a variables\n"), out);
116 fputs(_(" -p, --load[=<file>] read values from file\n"), out);
117 fputs(_(" -f alias of -p\n"), out);
118 fputs(_(" --system read values from all system directories\n"), out);
119 fputs(_(" -r, --pattern <expression>\n"
120 " select setting that match expression\n"), out);
121 fputs(_(" -q, --quiet do not echo variable set\n"), out);
122 fputs(_(" -w, --write enable writing a value to variable\n"), out);
123 fputs(_(" -o does nothing\n"), out);
124 fputs(_(" -x does nothing\n"), out);
125 fputs(_(" -d alias of -h\n"), out);
126 fputs(USAGE_SEPARATOR, out);
127 fputs(USAGE_HELP, out);
128 fputs(USAGE_VERSION, out);
129 fprintf(out, USAGE_MAN_TAIL("sysctl(8)"));
131 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
135 * Strip the leading and trailing spaces from a string
137 static char *StripLeadingAndTrailingSpaces(char *oneline)
141 if (!oneline || !*oneline)
145 t += strlen(oneline) - 1;
147 while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline)
152 while ((*t == ' ' || *t == '\t') && *t != 0)
159 * Read a sysctl setting
161 static int ReadSetting(const char *restrict const name)
164 char *restrict tmpname;
165 char *restrict outname;
170 if (!name || !*name) {
171 xwarnx(_("\"%s\" is an unknown key"), name);
175 /* used to display the output */
176 outname = xstrdup(name);
178 slashdot(outname, '/', '.');
180 /* used to open the file */
181 tmpname = xmalloc(strlen(name) + strlen(PROC_PATH) + 2);
182 strcpy(tmpname, PROC_PATH);
183 strcat(tmpname, name);
185 slashdot(tmpname + strlen(PROC_PATH), '.', '/');
187 /* used to display the output */
188 outname = xstrdup(name);
190 slashdot(outname, '/', '.');
192 if (stat(tmpname, &ts) < 0) {
194 xwarn(_("cannot stat %s"), tmpname);
199 if ((ts.st_mode & S_IRUSR) == 0)
202 if (S_ISDIR(ts.st_mode)) {
204 len = strlen(tmpname);
206 tmpname[len + 1] = '\0';
207 rc = DisplayAll(tmpname);
211 if (pattern && !pattern_match(outname, pattern)) {
216 fp = fopen(tmpname, "r");
222 xwarnx(_("\"%s\" is an unknown key"), outname);
227 xwarnx(_("permission denied on key '%s'"), outname);
231 xwarn(_("reading key \"%s\""), outname);
237 if (fgets(inbuf, sizeof inbuf - 1, fp)) {
238 /* this loop is required, see
239 * /sbin/sysctl -a | egrep -6 dev.cdrom.info
243 fprintf(stdout, "%s\n", outname);
245 /* already has the \n in it */
247 fprintf(stdout, "%s = %s",
249 if (inbuf[strlen(inbuf) - 1] != '\n')
258 fprintf(stdout, "%s", inbuf);
261 } while (fgets(inbuf, sizeof inbuf - 1, fp));
265 xwarnx(_("permission denied on key '%s'"),
271 len = strlen(tmpname);
273 tmpname[len + 1] = '\0';
275 rc = DisplayAll(tmpname);
279 xwarnx(_("reading key \"%s\""), outname);
293 static int is_deprecated(char *filename)
296 for (i = 0; strlen(DEPRECATED[i]); i++) {
297 if (strcmp(DEPRECATED[i], filename) == 0)
304 * Display all the sysctl settings
306 static int DisplayAll(const char *restrict const path)
311 struct dirent *restrict de;
317 xwarnx(_("unable to open directory \"%s\""), path);
320 readdir(dp); /* skip . */
321 readdir(dp); /* skip .. */
322 while ((de = readdir(dp))) {
323 char *restrict tmpdir;
324 if (IgnoreDeprecated && is_deprecated(de->d_name))
327 (char *restrict) xmalloc(strlen(path) +
330 sprintf(tmpdir, "%s%s", path, de->d_name);
331 rc2 = stat(tmpdir, &ts);
333 xwarn(_("cannot stat %s"), tmpdir);
335 if (S_ISDIR(ts.st_mode)) {
352 * Write a sysctl setting
354 static int WriteSetting(const char *setting)
357 const char *name = setting;
368 /* probably don't want to display this err */
371 equals = strchr(setting, '=');
374 xwarnx(_("\"%s\" must be of the form name=value"),
379 /* point to the value in name=value */
382 if (!*name || !*value || name == equals) {
383 xwarnx(_("malformed setting \"%s\""), setting);
387 /* used to open the file */
388 tmpname = xmalloc(equals - name + 1 + strlen(PROC_PATH));
389 strcpy(tmpname, PROC_PATH);
390 strncat(tmpname, name, (int) (equals - name));
391 tmpname[equals - name + strlen(PROC_PATH)] = 0;
393 slashdot(tmpname + strlen(PROC_PATH), '.', '/');
395 /* used to display the output */
396 outname = xmalloc(equals - name + 1);
397 strncpy(outname, name, (int) (equals - name));
398 outname[equals - name] = 0;
400 slashdot(outname, '/', '.');
401 last_dot = strrchr(outname, '.');
402 if (last_dot != NULL && is_deprecated(last_dot + 1)) {
403 xwarnx(_("%s is deprecated, value not set"), outname);
407 if (stat(tmpname, &ts) < 0) {
409 xwarn(_("cannot stat %s"), tmpname);
415 if ((ts.st_mode & S_IWUSR) == 0) {
416 xwarn(_("setting key \"%s\""), outname);
420 if (S_ISDIR(ts.st_mode)) {
421 xwarn(_("setting key \"%s\""), outname);
425 fp = fopen(tmpname, "w");
431 xwarnx(_("\"%s\" is an unknown key"), outname);
436 xwarnx(_("permission denied on key '%s'"), outname);
440 xwarn(_("setting key \"%s\""), outname);
445 rc = fprintf(fp, "%s\n", value);
448 if (close_stream(fp) != 0)
449 xwarn(_("setting key \"%s\""), outname);
450 if (rc == 0 && !Quiet) {
452 fprintf(stdout, "%s\n", outname);
455 fprintf(stdout, "%s = %s\n",
459 fprintf(stdout, "%s\n", value);
461 fprintf(stdout, "%s", value);
472 static int pattern_match(const char *string, const char *pat)
477 if (regcomp(&re, pat, REG_EXTENDED | REG_NOSUB) != 0)
479 status = regexec(&re, string, (size_t) 0, NULL, 0);
489 * Preload the sysctl's from the conf file. We parse the file and then
490 * reform it (strip out whitespace).
492 static int Preload(const char *restrict const filename)
494 char oneline[LINELEN];
495 char buffer[LINELEN];
506 globflg = GLOB_NOCHECK;
508 globflg |= GLOB_BRACE;
511 globflg |= GLOB_TILDE;
513 if (filename[0] == '~')
514 xwarnx(_("GLOB_TILDE is not supported on your platform, "
515 "the tilde in \"%s\" won't be expanded."), filename);
517 globerr = glob(filename, globflg, NULL, &globbuf);
519 if (globerr != 0 && globerr != GLOB_NOMATCH)
520 xerr(EXIT_FAILURE, _("glob failed"));
522 for (j = 0; j < globbuf.gl_pathc; j++) {
523 fp = (globbuf.gl_pathv[j][0] == '-' && !globbuf.gl_pathv[j][1])
524 ? stdin : fopen(globbuf.gl_pathv[j], "r");
526 xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
530 while (fgets(oneline, sizeof oneline, fp)) {
532 t = StripLeadingAndTrailingSpaces(oneline);
537 if (*t == '#' || *t == ';')
540 name = strtok(t, "=");
541 if (!name || !*name) {
542 xwarnx(_("%s(%d): invalid syntax, continuing..."),
543 globbuf.gl_pathv[j], n);
547 StripLeadingAndTrailingSpaces(name);
549 if (pattern && !pattern_match(name, pattern))
552 value = strtok(NULL, "\n\r");
553 if (!value || !*value) {
554 xwarnx(_("%s(%d): invalid syntax, continuing..."),
555 globbuf.gl_pathv[j], n);
559 while ((*value == ' ' || *value == '\t') && *value != 0)
562 /* should NameOnly affect this? */
563 sprintf(buffer, "%s=%s", name, value);
564 rc |= WriteSetting(buffer);
577 static int sortpairs(const void *A, const void *B)
579 const struct pair *a = *(struct pair * const *) A;
580 const struct pair *b = *(struct pair * const *) B;
581 return strcmp(a->name, b->name);
584 static int PreloadSystem(void)
587 const char *dirs[] = {
590 "/usr/local/lib/sysctl.d",
594 struct pair **cfgs = NULL;
598 enum { nprealloc = 16 };
600 for (di = 0; di < sizeof(dirs) / sizeof(dirs[0]); ++di) {
602 DIR *dp = opendir(dirs[di]);
606 while ((de = readdir(dp))) {
607 if (!strcmp(de->d_name, ".")
608 || !strcmp(de->d_name, ".."))
610 if (strlen(de->d_name) < 5
611 || strcmp(de->d_name + strlen(de->d_name) - 5, ".conf"))
613 /* check if config already known */
614 for (i = 0; i < ncfgs; ++i) {
615 if (cfgs && !strcmp(cfgs[i]->name, de->d_name))
622 if (ncfgs % nprealloc == 0)
625 sizeof(struct pair *) * (ncfgs +
630 xmalloc(sizeof(struct pair) +
631 strlen(de->d_name) * 2 + 2 +
632 strlen(dirs[di]) + 1);
634 (char *)cfgs[ncfgs] + sizeof(struct pair);
635 strcpy(cfgs[ncfgs]->name, de->d_name);
637 (char *)cfgs[ncfgs] + sizeof(struct pair) +
638 strlen(cfgs[ncfgs]->name) + 1;
639 sprintf(cfgs[ncfgs]->value, "%s/%s", dirs[di],
643 xerrx(EXIT_FAILURE, _("internal error"));
649 qsort(cfgs, ncfgs, sizeof(struct cfg *), sortpairs);
651 for (i = 0; i < ncfgs; ++i) {
653 printf(_("* Applying %s ...\n"), cfgs[i]->value);
654 rc |= Preload(cfgs[i]->value);
658 if (stat(DEFAULT_PRELOAD, &ts) < 0 || S_ISREG(ts.st_mode)) {
660 printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
661 rc |= Preload(DEFAULT_PRELOAD);
665 for (i = 0; i < ncfgs; ++i) {
668 if (cfgs) free(cfgs);
676 int main(int argc, char *argv[])
678 bool WriteMode = false;
679 bool DisplayAllOpt = false;
680 bool preloadfileOpt = false;
683 const char *preloadfile = NULL;
686 DEPRECATED_OPTION = CHAR_MAX + 1,
689 static const struct option longopts[] = {
690 {"all", no_argument, NULL, 'a'},
691 {"deprecated", no_argument, NULL, DEPRECATED_OPTION},
692 {"binary", no_argument, NULL, 'b'},
693 {"ignore", no_argument, NULL, 'e'},
694 {"names", no_argument, NULL, 'N'},
695 {"values", no_argument, NULL, 'n'},
696 {"load", optional_argument, NULL, 'p'},
697 {"quiet", no_argument, NULL, 'q'},
698 {"write", no_argument, NULL, 'w'},
699 {"system", no_argument, NULL, SYSTEM_OPTION},
700 {"pattern", required_argument, NULL, 'r'},
701 {"help", no_argument, NULL, 'h'},
702 {"version", no_argument, NULL, 'V'},
706 #ifdef HAVE_PROGRAM_INVOCATION_NAME
707 program_invocation_name = program_invocation_short_name;
709 setlocale(LC_ALL, "");
710 bindtextdomain(PACKAGE, LOCALEDIR);
712 atexit(close_stdout);
718 IgnoreDeprecated = true;
724 getopt_long(argc, argv, "bneNwfp::qoxaAXr:Vdh", longopts,
728 /* This is "binary" format, which means more for BSD. */
729 PrintNewline = false;
736 * For FreeBSD, -e means a "%s=%s\n" format.
737 * ("%s: %s\n" default). We (and NetBSD) use
738 * "%s = %s\n" always, and -e to ignore errors.
748 case 'f': /* the NetBSD way */
750 preloadfileOpt = true;
752 preloadfile = optarg;
757 case 'o': /* BSD: binary values too, 1st 16 bytes in hex */
758 case 'x': /* BSD: binary values too, whole thing in hex */
761 case 'a': /* string and integer values (for Linux, all of them) */
762 case 'A': /* same as -a -o */
763 case 'X': /* same as -a -x */
764 DisplayAllOpt = true;
766 case DEPRECATED_OPTION:
767 IgnoreDeprecated = false;
771 return PreloadSystem();
773 pattern = xstrdup(optarg);
776 printf(PROCPS_NG_VERSION);
778 case 'd': /* BSD: print description ("vm.kvm_size: Size of KVM") */
779 case 'h': /* BSD: human-readable (did FreeBSD 5 make -e default?) */
791 return DisplayAll(PROC_PATH);
793 if (preloadfileOpt) {
794 int ret = EXIT_SUCCESS, i;
797 ret |= Preload(DEFAULT_PRELOAD);
800 /* This happens when -pfile option is
801 * used without space. */
802 ret |= Preload(preloadfile);
804 for (i = 0; i < argc; i++)
805 ret |= Preload(argv[i]);
810 xerrx(EXIT_FAILURE, _("no variables specified\n"
811 "Try `%s --help' for more information."),
812 program_invocation_short_name);
813 if (NameOnly && Quiet)
814 xerrx(EXIT_FAILURE, _("options -N and -q cannot coexist\n"
815 "Try `%s --help' for more information."),
816 program_invocation_short_name);
818 for ( ; *argv; argv++) {
819 if (WriteMode || index(*argv, '='))
820 ReturnCode += WriteSetting(*argv);
822 ReturnCode += ReadSetting(*argv);