/* * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters * * "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 to preload values from a file * Horms: * - added -q to be quiet when modifying values * * Changes by Albert Cahalan, 2002. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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[=] 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 \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 display the output */ outname = xstrdup(name); /* change / to . */ slashdot(outname, '/', '.'); /* 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) { xwarn(_("setting key \"%s\""), dotted_key); free(dotted_key); return rc; } if (S_ISDIR(ts.st_mode)) { 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", value); } 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; }