summaryrefslogtreecommitdiffstats
path: root/src/sysctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sysctl.c')
-rw-r--r--src/sysctl.c1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/src/sysctl.c b/src/sysctl.c
new file mode 100644
index 0000000..1cb548c
--- /dev/null
+++ b/src/sysctl.c
@@ -0,0 +1,1070 @@
+/*
+ * Sysctl - A utility to read and manipulate the sysctl parameters
+ *
+ * Copyright © 2009-2023 Craig Small <csmall@dropbear.xyz>
+ * Copyright © 2012-2023 Jim Warner <james.warner@comcast.net>
+ * Copyright © 2017-2018 Werner Fink <werner@suse.de>
+ * Copyright © 2014 Jaromir Capik <jcapik@redhat.com>
+ * Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi>
+ * Copyright © 2002-2007 Albert Cahalan
+ * Copyright © 1999 George Staikos
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Part of this code comes from systemd, especially sysctl.c
+ * Changelog:
+ * v1.01:
+ * - added -p <preload> to preload values from a file
+ * Horms:
+ * - added -q to be quiet when modifying values
+ *
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <getopt.h>
+#include <glob.h>
+#include <libgen.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "procio.h"
+
+/*
+ * Globals...
+ */
+static const char PROC_PATH[] = "/proc/sys/";
+static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
+static const char *DEPRECATED[] = {
+ "base_reachable_time",
+ "retrans_time",
+ ""
+};
+static bool IgnoreDeprecated;
+static bool NameOnly;
+static bool PrintName;
+static bool PrintNewline;
+static bool IgnoreError;
+static bool Quiet;
+static bool DryRun;
+static char *pattern;
+
+#define LINELEN 4096
+static char *iobuf;
+static size_t iolen = LINELEN;
+
+typedef struct SysctlSetting {
+ char *key;
+ char *path;
+ char *value;
+ bool ignore_failure;
+ bool glob_exclude;
+ struct SysctlSetting *next;
+} SysctlSetting;
+
+typedef struct SettingList {
+ struct SysctlSetting *head;
+ struct SysctlSetting *tail;
+} SettingList;
+
+#define GLOB_CHARS "*?["
+static inline bool string_is_glob(const char *p)
+{
+ return !!strpbrk(p, GLOB_CHARS);
+}
+
+
+/* Function prototypes. */
+static int pattern_match(const char *string, const char *pat);
+static int DisplayAll(const char *restrict const path);
+
+static inline bool is_proc_path(
+ const char *path)
+{
+ char *resolved_path;
+
+ if ( (resolved_path = realpath(path, NULL)) == NULL)
+ return false;
+
+ if (strncmp(PROC_PATH, resolved_path, strlen(PROC_PATH)) == 0) {
+ free(resolved_path);
+ return true;
+ }
+
+ xwarnx(_("Path is not under %s: %s"), PROC_PATH, path);
+ free(resolved_path);
+ return false;
+}
+
+static void slashdot(char *restrict p, char old, char new)
+{
+ int warned = 1;
+ p = strpbrk(p, "/.");
+ if (!p)
+ /* nothing -- can't be, but oh well */
+ return;
+ if (*p == new)
+ /* already in desired format */
+ return;
+ while (p) {
+ char c = *p;
+ if ((*(p + 1) == '/' || *(p + 1) == '.') && warned) {
+ xwarnx(_("separators should not be repeated: %s"), p);
+ warned = 0;
+ }
+ if (c == old)
+ *p = new;
+ if (c == new)
+ *p = old;
+ p = strpbrk(p + 1, "/.");
+ }
+}
+
+#if 0 // avoid '-Wunused-function' warning
+static void setting_free(SysctlSetting *s) {
+ if (!s)
+ return;
+
+ free(s->key);
+ free(s->path);
+ free(s->value);
+ free(s);
+}
+#endif
+
+static SysctlSetting *setting_new(
+ const char *key,
+ const char *value,
+ bool ignore_failure,
+ bool glob_exclude) {
+
+ SysctlSetting *s = NULL;
+ char *path = NULL;
+ int proc_len;
+
+ proc_len = strlen(PROC_PATH);
+ /* used to open the file */
+ path = xmalloc(strlen(key) + proc_len + 2);
+ strcpy(path, PROC_PATH);
+ if (key[0] == '-')
+ strcat(path + proc_len, key+1);
+ else
+ strcat(path + proc_len, key);
+ /* change . to / for path */
+ slashdot(path + proc_len, '.', '/');
+
+ s = xmalloc(sizeof(SysctlSetting));
+
+ *s = (SysctlSetting) {
+ .key = strdup(key),
+ .path = path,
+ .value = value? strdup(value): NULL,
+ .ignore_failure = ignore_failure,
+ .glob_exclude = glob_exclude,
+ .next = NULL,
+ };
+
+ return s;
+}
+
+static void settinglist_add(SettingList *l, SysctlSetting *s) {
+ SysctlSetting *old_tail;
+
+ if (!l)
+ return;
+
+ if (l->head == NULL)
+ l->head = s;
+
+ if (l->tail != NULL) {
+ old_tail = l->tail;
+ old_tail->next = s;
+ }
+ l->tail = s;
+}
+
+static SysctlSetting *settinglist_findpath(const SettingList *l, const char *path) {
+ SysctlSetting *node;
+
+ for (node=l->head; node != NULL; node = node->next) {
+ if (strcmp(node->path, path) == 0)
+ return node;
+ }
+ return NULL;
+}
+
+/* Function prototypes. */
+static int pattern_match(const char *string, const char *pat);
+static int DisplayAll(const char *restrict const path);
+
+/*
+ * Display the usage format
+ */
+static void __attribute__ ((__noreturn__))
+ Usage(FILE * out)
+{
+ fputs(USAGE_HEADER, out);
+ fprintf(out,
+ _(" %s [options] [variable[=value] ...]\n"),
+ program_invocation_short_name);
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all display all variables\n"), out);
+ fputs(_(" -A alias of -a\n"), out);
+ fputs(_(" -X alias of -a\n"), out);
+ fputs(_(" --deprecated include deprecated parameters to listing\n"), out);
+ fputs(_(" --dry-run Print the key and values but do not write\n"), out);
+ fputs(_(" -b, --binary print value without new line\n"), out);
+ fputs(_(" -e, --ignore ignore unknown variables errors\n"), out);
+ fputs(_(" -N, --names print variable names without values\n"), out);
+ fputs(_(" -n, --values print only values of the given variable(s)\n"), out);
+ fputs(_(" -p, --load[=<file>] read values from file\n"), out);
+ fputs(_(" -f alias of -p\n"), out);
+ fputs(_(" --system read values from all system directories\n"), out);
+ fputs(_(" -r, --pattern <expression>\n"
+ " select setting that match expression\n"), out);
+ fputs(_(" -q, --quiet do not echo variable set\n"), out);
+ fputs(_(" -w, --write enable writing a value to variable\n"), out);
+ fputs(_(" -o does nothing\n"), out);
+ fputs(_(" -x does nothing\n"), out);
+ fputs(_(" -d alias of -h\n"), out);
+ fputs(USAGE_SEPARATOR, out);
+ fputs(USAGE_HELP, out);
+ fputs(USAGE_VERSION, out);
+ fprintf(out, USAGE_MAN_TAIL("sysctl(8)"));
+
+ exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+/*
+ * Strip left/leading side of a string
+ */
+static char *lstrip(char *line)
+{
+ char *start;
+
+ if (!line || !*line)
+ return line;
+
+ start = line;
+ while(isspace(*start)) start++;
+
+ return start;
+}
+
+/*
+ * Strip right/trailing side of a string
+ * by placing a \0
+ */
+static void rstrip(char *line)
+{
+ char *end;
+
+ if (!line || !*line)
+ return;
+
+ end = line + strlen(line) - 1;
+ while(end > line && isspace(*end)) end--;
+
+ end[1] = '\0';
+}
+
+#if 0 // avoid '-Wunused-function' warning
+/*
+ * Strip the leading and trailing spaces from a string
+ */
+static char *StripLeadingAndTrailingSpaces(char *oneline)
+{
+ char *t;
+
+ if (!oneline || !*oneline)
+ return oneline;
+
+ t = oneline;
+ t += strlen(oneline) - 1;
+
+ while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline)
+ *t-- = 0;
+
+ t = oneline;
+
+ while ((*t == ' ' || *t == '\t') && *t != 0)
+ t++;
+
+ return t;
+}
+#endif
+
+/*
+ * Read a sysctl setting
+ */
+static int ReadSetting(const char *restrict const name)
+{
+ int rc = EXIT_SUCCESS;
+ char *restrict tmpname;
+ char *restrict outname;
+ ssize_t rlen;
+ FILE *restrict fp;
+ struct stat ts;
+
+ if (!name || !*name) {
+ xwarnx(_("\"%s\" is an unknown key"), name);
+ return -1;
+ }
+
+ /* used to open the file */
+ tmpname = xmalloc(strlen(name) + strlen(PROC_PATH) + 2);
+ strcpy(tmpname, PROC_PATH);
+ strcat(tmpname, name);
+ /* change . to / */
+ slashdot(tmpname + strlen(PROC_PATH), '.', '/');
+
+ /* used to display the output */
+ outname = xstrdup(name);
+ /* change / to . */
+ slashdot(outname, '/', '.');
+
+ if (stat(tmpname, &ts) < 0) {
+ if (!IgnoreError) {
+ xwarn(_("cannot stat %s"), tmpname);
+ rc = EXIT_FAILURE;
+ }
+ goto out;
+ }
+ if ((ts.st_mode & S_IRUSR) == 0)
+ goto out;
+
+ if (!is_proc_path(tmpname)) {
+ rc = -1;
+ goto out;
+ }
+
+ if (S_ISDIR(ts.st_mode)) {
+ size_t len;
+ len = strlen(tmpname);
+ tmpname[len] = '/';
+ tmpname[len + 1] = '\0';
+ rc = DisplayAll(tmpname);
+ goto out;
+ }
+
+ if (pattern && !pattern_match(outname, pattern)) {
+ rc = EXIT_SUCCESS;
+ goto out;
+ }
+
+ if (NameOnly) {
+ fprintf(stdout, "%s\n", outname);
+ goto out;
+ }
+
+ fp = fprocopen(tmpname, "r");
+
+ if (!fp) {
+ switch (errno) {
+ case ENOENT:
+ if (!IgnoreError) {
+ xwarnx(_("\"%s\" is an unknown key"), outname);
+ rc = EXIT_FAILURE;
+ }
+ break;
+ case EACCES:
+ xwarnx(_("permission denied on key '%s'"), outname);
+ rc = EXIT_FAILURE;
+ break;
+ case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
+ rc = EXIT_FAILURE;
+ break;
+ default:
+ xwarn(_("reading key \"%s\""), outname);
+ rc = EXIT_FAILURE;
+ break;
+ }
+ } else {
+ errno = 0;
+ if ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
+ /* this loop is required, see
+ * /sbin/sysctl -a | egrep -6 dev.cdrom.info
+ */
+ do {
+ char *nlptr;
+ if (PrintName) {
+ fprintf(stdout, "%s = ", outname);
+ do {
+ fprintf(stdout, "%s", iobuf);
+ nlptr = &iobuf[strlen(iobuf) - 1];
+ /* already has the \n in it */
+ if (*nlptr == '\n')
+ break;
+ } while ((rlen = getline(&iobuf, &iolen, fp)) > 0);
+ if (*nlptr != '\n')
+ putchar('\n');
+ } else {
+ if (!PrintNewline) {
+ nlptr = strchr(iobuf, '\n');
+ if (nlptr)
+ *nlptr = '\0';
+ }
+ fprintf(stdout, "%s", iobuf);
+ }
+ } while ((rlen = getline(&iobuf, &iolen, fp)) > 0);
+ } else {
+ switch (errno) {
+ case EACCES:
+ xwarnx(_("permission denied on key '%s'"),
+ outname);
+ rc = EXIT_FAILURE;
+ break;
+ case EISDIR: {
+ size_t len;
+ len = strlen(tmpname);
+ tmpname[len] = '/';
+ tmpname[len + 1] = '\0';
+ fclose(fp);
+ rc = DisplayAll(tmpname);
+ goto out;
+ }
+ case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
+ rc = EXIT_FAILURE;
+ break;
+ default:
+ xwarnx(_("reading key \"%s\""), outname);
+ rc = EXIT_FAILURE;
+ case 0:
+ break;
+ }
+ }
+ fclose(fp);
+ }
+ out:
+ free(tmpname);
+ free(outname);
+ return rc;
+}
+
+static int is_deprecated(char *filename)
+{
+ int i;
+ for (i = 0; strlen(DEPRECATED[i]); i++) {
+ if (strcmp(DEPRECATED[i], filename) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Display all the sysctl settings
+ */
+static int DisplayAll(const char *restrict const path)
+{
+ int rc = EXIT_SUCCESS;
+ int rc2;
+ DIR *restrict dp;
+ struct dirent *restrict de;
+ struct stat ts;
+
+ dp = opendir(path);
+
+ if (!dp) {
+ xwarnx(_("unable to open directory \"%s\""), path);
+ rc = EXIT_FAILURE;
+ } else {
+ readdir(dp); /* skip . */
+ readdir(dp); /* skip .. */
+ while ((de = readdir(dp))) {
+ char *restrict tmpdir;
+ if (IgnoreDeprecated && is_deprecated(de->d_name))
+ continue;
+ tmpdir =
+ (char *restrict) xmalloc(strlen(path) +
+ strlen(de->d_name) +
+ 2);
+ sprintf(tmpdir, "%s%s", path, de->d_name);
+ rc2 = stat(tmpdir, &ts);
+ if (rc2 != 0) {
+ xwarn(_("cannot stat %s"), tmpdir);
+ } else {
+ if (S_ISDIR(ts.st_mode)) {
+ strcat(tmpdir, "/");
+ DisplayAll(tmpdir);
+ } else {
+ rc |=
+ ReadSetting(tmpdir +
+ strlen(PROC_PATH));
+ }
+ }
+ free(tmpdir);
+ }
+ closedir(dp);
+ }
+ return rc;
+}
+
+/*
+ * Write a sysctl setting
+ */
+static int WriteSetting(
+ const char *key,
+ const char *path,
+ const char *value,
+ const bool ignore_failure)
+{
+ int rc = EXIT_SUCCESS;
+ FILE *fp;
+ struct stat ts;
+ char *dotted_key;
+
+ if (!key || !path)
+ return rc;
+
+ if (stat(path, &ts) < 0) {
+ if (!IgnoreError) {
+ xwarn(_("cannot stat %s"), path);
+ rc = EXIT_FAILURE;
+ }
+ return rc;
+ }
+
+ if (!is_proc_path(path)) {
+ return EXIT_FAILURE;
+ }
+
+ /* Convert the globbed path into a dotted key */
+ if ( (dotted_key = strdup(path + strlen(PROC_PATH))) == NULL) {
+ xerrx(EXIT_FAILURE, _("strdup key"));
+ return EXIT_FAILURE;
+ }
+ slashdot(dotted_key, '/', '.');
+
+ if ((ts.st_mode & S_IWUSR) == 0) {
+ errno = EPERM;
+ xwarn(_("setting key \"%s\""), dotted_key);
+ free(dotted_key);
+ return rc;
+ }
+
+ if (S_ISDIR(ts.st_mode)) {
+ errno = EISDIR;
+ xwarn(_("setting key \"%s\""), dotted_key);
+ free(dotted_key);
+ return rc;
+ }
+
+ if (!DryRun) {
+ if ((fp = fprocopen(path, "w")) == NULL) {
+ switch (errno) {
+ case ENOENT:
+ if (!IgnoreError) {
+ xwarnx(_("\"%s\" is an unknown key%s"),
+ dotted_key, (ignore_failure?_(", ignoring"):""));
+ if (!ignore_failure)
+ rc = EXIT_FAILURE;
+ }
+ break;
+ case EPERM:
+ case EROFS:
+ case EACCES:
+ xwarnx(_("permission denied on key \"%s\"%s"),
+ dotted_key, (ignore_failure?_(", ignoring"):""));
+ break;
+ default:
+ xwarn(_("setting key \"%s\"%s"),
+ dotted_key, (ignore_failure?_(", ignoring"):""));
+ break;
+ }
+ if (!ignore_failure && errno != ENOENT)
+ rc = EXIT_FAILURE;
+ } else {
+ if (0 < fprintf(fp, "%s\n", value))
+ rc = EXIT_SUCCESS;
+ if (close_stream(fp) != 0) {
+ xwarn(_("setting key \"%s\""), dotted_key);
+ free(dotted_key);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ if ((rc == EXIT_SUCCESS && !Quiet) || DryRun) {
+ if (NameOnly) {
+ printf("%s\n", dotted_key);
+ } else {
+ if (PrintName) {
+ printf("%s = %s\n", dotted_key, value);
+ } else {
+ if (PrintNewline)
+ printf("%s\n", value);
+ else
+ printf("%s", value);
+ }
+ }
+ }
+ free(dotted_key);
+ return rc;
+}
+
+/*
+ * parse each configuration line, there are multiple ways of specifying
+ * a key/value here:
+ *
+ * key = value simple setting
+ * -key = value ignore errors
+ * key.pattern.*.with.glob = value set keys that match glob
+ * -key.pattern.exclude.with.glob dont set this value
+ * key.pattern.override.with.glob = value set this glob match to value
+ *
+ */
+
+static SysctlSetting *parse_setting_line(
+ const char *path,
+ const int linenum,
+ char *line)
+{
+ char *key;
+ char *value;
+ bool glob_exclude = FALSE;
+ bool ignore_failure = FALSE;
+
+ key = lstrip(line);
+ if (strlen(key) < 2)
+ return NULL;
+
+ /* skip over comments */
+ if (key[0] == '#' || key[0] == ';')
+ return NULL;
+
+ if (pattern && !pattern_match(key, pattern))
+ return NULL;
+
+ value = strchr(key, '=');
+ if (value == NULL) {
+ if (key[0] == '-') {
+ glob_exclude = TRUE;
+ key++;
+ value = NULL;
+ rstrip(key);
+ } else {
+ xwarnx(_("%s(%d): invalid syntax, continuing..."),
+ path, linenum);
+ return NULL;
+ }
+ } else {
+ value[0]='\0';
+ if (key[0] == '-') {
+ ignore_failure = TRUE;
+ key++;
+ }
+ value++; // skip over =
+ value=lstrip(value);
+ rstrip(value);
+ rstrip(key);
+ }
+ return setting_new(key, value, ignore_failure, glob_exclude);
+}
+
+/* Go through the setting list, expand and sort out
+ * setting globs and actually write the settings out
+ */
+static int write_setting_list(const SettingList *sl)
+{
+ SysctlSetting *node;
+ int rc = EXIT_SUCCESS;
+
+ for (node=sl->head; node != NULL; node=node->next) {
+ if (node->glob_exclude)
+ continue;
+
+ if (string_is_glob(node->path)) {
+ glob_t globbuf;
+ int i;
+
+ if (glob(node->path, 0, NULL, &globbuf) != 0)
+ continue;
+
+ for(i=0; i < globbuf.gl_pathc; i++) {
+ if (settinglist_findpath(sl, globbuf.gl_pathv[i]))
+ continue; // override or exclude
+
+ rc |= WriteSetting(node->key, globbuf.gl_pathv[i], node->value,
+ node->ignore_failure);
+ }
+ } else {
+ rc |= WriteSetting(node->key, node->path, node->value,
+ node->ignore_failure);
+ }
+
+
+ }
+
+ return rc;
+}
+
+static int pattern_match(const char *string, const char *pat)
+{
+ int status;
+ regex_t re;
+
+ if (regcomp(&re, pat, REG_EXTENDED | REG_NOSUB) != 0)
+ return (0);
+ status = regexec(&re, string, (size_t) 0, NULL, 0);
+ regfree(&re);
+ if (status != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Preload the sysctl's from the conf file. We parse the file and then
+ * reform it (strip out whitespace).
+ */
+static int Preload(SettingList *setlist, const char *restrict const filename)
+{
+ FILE *fp;
+ int n = 0;
+ int rc = EXIT_SUCCESS;
+ ssize_t rlen;
+ glob_t globbuf;
+ int globerr;
+ int globflg;
+ int j;
+
+ globflg = GLOB_NOCHECK;
+#ifdef GLOB_BRACE
+ globflg |= GLOB_BRACE;
+#endif
+#ifdef GLOB_TILDE
+ globflg |= GLOB_TILDE;
+#else
+ if (filename[0] == '~')
+ xwarnx(_("GLOB_TILDE is not supported on your platform, "
+ "the tilde in \"%s\" won't be expanded."), filename);
+#endif
+ globerr = glob(filename, globflg, NULL, &globbuf);
+
+ if (globerr != 0 && globerr != GLOB_NOMATCH)
+ xerr(EXIT_FAILURE, _("glob failed"));
+
+ for (j = 0; j < globbuf.gl_pathc; j++) {
+ fp = (globbuf.gl_pathv[j][0] == '-' && !globbuf.gl_pathv[j][1])
+ ? stdin : fopen(globbuf.gl_pathv[j], "r");
+ if (!fp) {
+ xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
+ return EXIT_FAILURE;
+ }
+
+ while ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
+ SysctlSetting *setting;
+
+ n++;
+
+ if (rlen < 2)
+ continue;
+
+ if ( (setting = parse_setting_line(globbuf.gl_pathv[j], n, iobuf))
+ == NULL)
+ continue;
+ settinglist_add(setlist, setting);
+ }
+
+ fclose(fp);
+ }
+ return rc;
+}
+
+struct pair {
+ char *name;
+ char *value;
+};
+
+static int sortpairs(const void *A, const void *B)
+{
+ const struct pair *a = *(struct pair * const *) A;
+ const struct pair *b = *(struct pair * const *) B;
+ return strcmp(a->name, b->name);
+}
+
+static int PreloadSystem(SettingList *setlist)
+{
+ unsigned di, i;
+ const char *dirs[] = {
+ "/etc/sysctl.d",
+ "/run/sysctl.d",
+ "/usr/local/lib/sysctl.d",
+ "/usr/lib/sysctl.d",
+ "/lib/sysctl.d",
+ };
+ struct pair **cfgs = NULL;
+ unsigned ncfgs = 0;
+ int rc = EXIT_SUCCESS;
+ struct stat ts;
+ enum { nprealloc = 16 };
+
+ for (di = 0; di < sizeof(dirs) / sizeof(dirs[0]); ++di) {
+ struct dirent *de;
+ DIR *dp = opendir(dirs[di]);
+ if (!dp)
+ continue;
+
+ while ((de = readdir(dp))) {
+ if (!strcmp(de->d_name, ".")
+ || !strcmp(de->d_name, ".."))
+ continue;
+ if (strlen(de->d_name) < 5
+ || strcmp(de->d_name + strlen(de->d_name) - 5, ".conf"))
+ continue;
+ /* check if config already known */
+ for (i = 0; i < ncfgs; ++i) {
+ if (cfgs && !strcmp(cfgs[i]->name, de->d_name))
+ break;
+ }
+ if (i < ncfgs)
+ /* already in */
+ continue;
+
+ if (ncfgs % nprealloc == 0)
+ cfgs =
+ xrealloc(cfgs,
+ sizeof(struct pair *) * (ncfgs +
+ nprealloc));
+
+ if (cfgs) {
+ cfgs[ncfgs] =
+ xmalloc(sizeof(struct pair) +
+ strlen(de->d_name) * 2 + 2 +
+ strlen(dirs[di]) + 1);
+ cfgs[ncfgs]->name =
+ (char *)cfgs[ncfgs] + sizeof(struct pair);
+ strcpy(cfgs[ncfgs]->name, de->d_name);
+ cfgs[ncfgs]->value =
+ (char *)cfgs[ncfgs] + sizeof(struct pair) +
+ strlen(cfgs[ncfgs]->name) + 1;
+ sprintf(cfgs[ncfgs]->value, "%s/%s", dirs[di],
+ de->d_name);
+ ncfgs++;
+ } else {
+ xerrx(EXIT_FAILURE, _("internal error"));
+ }
+
+ }
+ closedir(dp);
+ }
+ qsort(cfgs, ncfgs, sizeof(struct cfg *), sortpairs);
+
+ for (i = 0; i < ncfgs; ++i) {
+ if (!Quiet)
+ printf(_("* Applying %s ...\n"), cfgs[i]->value);
+ rc |= Preload(setlist, cfgs[i]->value);
+ }
+
+
+ if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
+ if (!Quiet)
+ printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
+ rc |= Preload(setlist, DEFAULT_PRELOAD);
+ }
+
+ /* cleaning */
+ for (i = 0; i < ncfgs; ++i) {
+ free(cfgs[i]);
+ }
+ if (cfgs) free(cfgs);
+
+ return rc;
+}
+
+/*
+ * Main...
+ */
+int main(int argc, char *argv[])
+{
+ bool WriteMode = false;
+ bool DisplayAllOpt = false;
+ bool preloadfileOpt = false;
+ int ReturnCode = 0;
+ int c;
+ int rc = 0;
+ const char *preloadfile = NULL;
+ SettingList *setlist;
+
+ enum {
+ DEPRECATED_OPTION = CHAR_MAX + 1,
+ SYSTEM_OPTION,
+ DRYRUN_OPTION
+ };
+ static const struct option longopts[] = {
+ {"all", no_argument, NULL, 'a'},
+ {"deprecated", no_argument, NULL, DEPRECATED_OPTION},
+ {"dry-run", no_argument, NULL, DRYRUN_OPTION},
+ {"binary", no_argument, NULL, 'b'},
+ {"ignore", no_argument, NULL, 'e'},
+ {"names", no_argument, NULL, 'N'},
+ {"values", no_argument, NULL, 'n'},
+ {"load", optional_argument, NULL, 'p'},
+ {"quiet", no_argument, NULL, 'q'},
+ {"write", no_argument, NULL, 'w'},
+ {"system", no_argument, NULL, SYSTEM_OPTION},
+ {"pattern", required_argument, NULL, 'r'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+
+#ifdef HAVE_PROGRAM_INVOCATION_NAME
+ program_invocation_name = program_invocation_short_name;
+#endif
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ PrintName = true;
+ PrintNewline = true;
+ IgnoreError = false;
+ Quiet = false;
+ IgnoreDeprecated = true;
+ DryRun = false;
+ setlist = xmalloc(sizeof(SettingList));
+ setlist->head = NULL;
+ setlist->tail = NULL;
+
+ if (argc < 2)
+ Usage(stderr);
+
+ while ((c =
+ getopt_long(argc, argv, "bneNwfp::qoxaAXr:Vdh", longopts,
+ NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ /* This is "binary" format, which means more for BSD. */
+ PrintNewline = false;
+ /* FALL THROUGH */
+ case 'n':
+ PrintName = false;
+ break;
+ case 'e':
+ /*
+ * For FreeBSD, -e means a "%s=%s\n" format.
+ * ("%s: %s\n" default). We (and NetBSD) use
+ * "%s = %s\n" always, and -e to ignore errors.
+ */
+ IgnoreError = true;
+ break;
+ case 'N':
+ NameOnly = true;
+ break;
+ case 'w':
+ WriteMode = true;
+ break;
+ case 'f': /* the NetBSD way */
+ case 'p':
+ preloadfileOpt = true;
+ if (optarg)
+ preloadfile = optarg;
+ break;
+ case 'q':
+ Quiet = true;
+ break;
+ case 'o': /* BSD: binary values too, 1st 16 bytes in hex */
+ case 'x': /* BSD: binary values too, whole thing in hex */
+ /* does nothing */ ;
+ break;
+ case 'a': /* string and integer values (for Linux, all of them) */
+ case 'A': /* same as -a -o */
+ case 'X': /* same as -a -x */
+ DisplayAllOpt = true;
+ break;
+ case DEPRECATED_OPTION:
+ IgnoreDeprecated = false;
+ break;
+ case SYSTEM_OPTION:
+ IgnoreError = true;
+ rc |= PreloadSystem(setlist);
+ rc |= write_setting_list(setlist);
+ return rc;
+ case DRYRUN_OPTION:
+ DryRun = true;
+ break;
+ case 'r':
+ pattern = xstrdup(optarg);
+ break;
+ case 'V':
+ printf(PROCPS_NG_VERSION);
+ return EXIT_SUCCESS;
+ case 'd': /* BSD: print description ("vm.kvm_size: Size of KVM") */
+ case 'h': /* BSD: human-readable (did FreeBSD 5 make -e default?) */
+ case '?':
+ Usage(stdout);
+ default:
+ Usage(stderr);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ iobuf = xmalloc(iolen);
+
+ if (DisplayAllOpt)
+ return DisplayAll(PROC_PATH);
+
+ if (preloadfileOpt) {
+ int ret = EXIT_SUCCESS, i;
+ if (!preloadfile) {
+ if (!argc) {
+ ret |= Preload(setlist, DEFAULT_PRELOAD);
+ }
+ } else {
+ /* This happens when -pfile option is
+ * used without space. */
+ ret |= Preload(setlist, preloadfile);
+ }
+ for (i = 0; i < argc; i++)
+ ret |= Preload(setlist, argv[i]);
+ ret |= write_setting_list(setlist);
+ return ret;
+ }
+
+ if (argc < 1)
+ xerrx(EXIT_FAILURE, _("no variables specified\n"
+ "Try `%s --help' for more information."),
+ program_invocation_short_name);
+ if (NameOnly && Quiet)
+ xerrx(EXIT_FAILURE, _("options -N and -q cannot coexist\n"
+ "Try `%s --help' for more information."),
+ program_invocation_short_name);
+
+ for ( ; *argv; argv++) {
+ if (WriteMode || strchr(*argv, '=')) {
+ SysctlSetting *s;
+ if ( (s = parse_setting_line("command line", 0, *argv)) != NULL)
+ ReturnCode |= WriteSetting(s->key, s->path, s->value,
+ s->ignore_failure);
+ else
+ ReturnCode |= EXIT_FAILURE;
+ } else
+ ReturnCode += ReadSetting(*argv);
+ }
+ return ReturnCode;
+}