3 * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
6 * "Copyright 1999 George Staikos
7 * This file may be used subject to the terms and conditions of the
8 * GNU General Public License Version 2, or any later version
9 * at your option, as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details."
17 * - added -p <preload> to preload values from a file
19 * - added -q to be quiet when modifying values
21 * Changes by Albert Cahalan, 2002.
29 #include <sys/types.h>
33 #include "proc/procps.h"
34 #include "proc/version.h"
37 // Proof that C++ causes brain damage:
40 static bool false = 0;
46 static const char PROC_PATH[] = "/proc/sys/";
47 static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
49 static bool PrintName;
50 static bool PrintNewline;
51 static bool IgnoreError;
55 static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter \"%s\"\n";
56 static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting \"%s\"\n";
57 static const char ERR_NO_EQUALS[] = "error: \"%s\" must be of the form name=value\n";
58 static const char ERR_INVALID_KEY[] = "error: \"%s\" is an unknown key\n";
59 static const char ERR_UNKNOWN_WRITING[] = "error: \"%s\" setting key \"%s\"\n";
60 static const char ERR_UNKNOWN_READING[] = "error: \"%s\" reading key \"%s\"\n";
61 static const char ERR_PERMISSION_DENIED[] = "error: permission denied on key '%s'\n";
62 static const char ERR_OPENING_DIR[] = "error: unable to open directory \"%s\"\n";
63 static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n";
64 static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n";
67 static void slashdot(char *restrict p, char old, char new){
69 if(!p) return; /* nothing -- can't be, but oh well */
70 if(*p==new) return; /* already in desired format */
75 p = strpbrk(p+1,"/.");
82 * Display the usage format
85 static int Usage(const char *restrict const name) {
86 printf("usage: %s [-n] [-e] variable ... \n"
87 " %s [-n] [-e] [-q] -w variable=value ... \n"
89 " %s [-n] [-e] [-q] -p <file> (default /etc/sysctl.conf) \n"
90 " %s [-n] [-e] -A\n", name, name, name, name, name);
96 * Strip the leading and trailing spaces from a string
99 static char *StripLeadingAndTrailingSpaces(char *oneline) {
102 if (!oneline || !*oneline)
106 t += strlen(oneline)-1;
108 while ((*t==' ' || *t=='\t' || *t=='\n' || *t=='\r') && t!=oneline)
113 while ((*t==' ' || *t=='\t') && *t!=0)
119 static int DisplayAll(const char *restrict const path);
122 * Read a sysctl setting
125 static int ReadSetting(const char *restrict const name) {
127 char *restrict tmpname;
128 char *restrict outname;
132 if (!name || !*name) {
133 fprintf(stderr, ERR_INVALID_KEY, name);
137 /* used to open the file */
138 tmpname = malloc(strlen(name)+strlen(PROC_PATH)+2);
139 strcpy(tmpname, PROC_PATH);
140 strcat(tmpname, name);
141 slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
143 /* used to display the output */
144 outname = strdup(name);
145 slashdot(outname,'/','.'); /* change / to . */
147 fp = fopen(tmpname, "r");
153 fprintf(stderr, ERR_INVALID_KEY, outname);
158 fprintf(stderr, ERR_PERMISSION_DENIED, outname);
162 fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
167 if(fgets(inbuf, sizeof inbuf - 1, fp)) {
168 // this loop is required, see
169 // /sbin/sysctl -a | egrep -6 dev.cdrom.info
172 fprintf(stdout, "%s\n", outname);
174 /* already has the \n in it */
176 fprintf(stdout, "%s = %s", outname, inbuf);
179 char *nlptr = strchr(inbuf,'\n');
180 if(nlptr) *nlptr='\0';
182 fprintf(stdout, "%s", inbuf);
185 } while(fgets(inbuf, sizeof inbuf - 1, fp));
189 fprintf(stderr, ERR_PERMISSION_DENIED, outname);
194 len = strlen(tmpname);
196 tmpname[len+1] = '\0';
197 rc = DisplayAll(tmpname);
201 fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
217 * Display all the sysctl settings
220 static int DisplayAll(const char *restrict const path) {
224 struct dirent *restrict de;
230 fprintf(stderr, ERR_OPENING_DIR, path);
233 readdir(dp); // skip .
234 readdir(dp); // skip ..
235 while (( de = readdir(dp) )) {
236 char *restrict tmpdir;
237 tmpdir = (char *restrict)malloc(strlen(path)+strlen(de->d_name)+2);
238 sprintf(tmpdir, "%s%s", path, de->d_name);
239 rc2 = stat(tmpdir, &ts);
243 if (S_ISDIR(ts.st_mode)) {
247 rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
259 * Write a sysctl setting
262 static int WriteSetting(const char *setting) {
264 const char *name = setting;
271 if (!name) { /* probably don't want to display this err */
275 equals = index(setting, '=');
278 fprintf(stderr, ERR_NO_EQUALS, setting);
282 value = equals + 1; /* point to the value in name=value */
284 if (!*name || !*value || name == equals) {
285 fprintf(stderr, ERR_MALFORMED_SETTING, setting);
289 /* used to open the file */
290 tmpname = malloc(equals-name+1+strlen(PROC_PATH));
291 strcpy(tmpname, PROC_PATH);
292 strncat(tmpname, name, (int)(equals-name));
293 tmpname[equals-name+strlen(PROC_PATH)] = 0;
294 slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
296 /* used to display the output */
297 outname = malloc(equals-name+1);
298 strncpy(outname, name, (int)(equals-name));
299 outname[equals-name] = 0;
300 slashdot(outname,'/','.'); /* change / to . */
302 fp = fopen(tmpname, "w");
308 fprintf(stderr, ERR_INVALID_KEY, outname);
313 fprintf(stderr, ERR_PERMISSION_DENIED, outname);
317 fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
322 rc = fprintf(fp, "%s\n", value);
324 fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
329 fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
331 if (rc==0 && !Quiet) {
333 fprintf(stdout, "%s\n", outname);
336 fprintf(stdout, "%s = %s\n", outname, value);
339 fprintf(stdout, "%s\n", value);
341 fprintf(stdout, "%s", value);
355 * Preload the sysctl's from the conf file
356 * - we parse the file and then reform it (strip out whitespace)
359 static int Preload(const char *restrict const filename) {
368 fp = (filename[0]=='-' && !filename[1])
370 : fopen(filename, "r")
374 fprintf(stderr, ERR_PRELOAD_FILE, filename);
378 while (fgets(oneline, sizeof oneline, fp)) {
380 t = StripLeadingAndTrailingSpaces(oneline);
385 if (*t == '#' || *t == ';')
388 name = strtok(t, "=");
389 if (!name || !*name) {
390 fprintf(stderr, WARN_BAD_LINE, filename, n);
394 StripLeadingAndTrailingSpaces(name);
396 value = strtok(NULL, "\n\r");
397 if (!value || !*value) {
398 fprintf(stderr, WARN_BAD_LINE, filename, n);
402 while ((*value == ' ' || *value == '\t') && *value != 0)
405 // should NameOnly affect this?
406 sprintf(buffer, "%s=%s", name, value);
407 rc |= WriteSetting(buffer);
420 int main(int argc, char *argv[]) {
421 const char *me = (const char *)basename(argv[0]);
422 bool SwitchesAllowed = true;
423 bool WriteMode = false;
425 const char *preloadfile = DEFAULT_PRELOAD;
438 for (; argv && *argv && **argv; argv++) {
439 if (SwitchesAllowed && **argv == '-') { /* we have a switch */
440 if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew"
441 if (!strcmp("--help",*argv)) {
445 if (!strcmp("--version",*argv)) {
446 fprintf(stdout, "sysctl (%s)\n",procps_version);
449 fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
454 /* This is "binary" format, which means more for BSD. */
455 PrintNewline = false;
461 // For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default)
462 // We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors.
469 SwitchesAllowed = false;
472 case 'f': // the NetBSD way
475 if (argv && *argv && **argv) {
478 return Preload(preloadfile);
482 case 'o': // BSD: binary values too, 1st 16 bytes in hex
483 case 'x': // BSD: binary values too, whole thing in hex
486 case 'a': // string and integer values (for Linux, all of them)
487 case 'A': // same as -a -o
488 case 'X': // same as -a -x
489 SwitchesAllowed = false;
490 return DisplayAll(PROC_PATH);
492 fprintf(stdout, "sysctl (%s)\n",procps_version);
494 case 'd': // BSD: print description ("vm.kvm_size: Size of KVM")
495 case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?)
499 fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
503 if (NameOnly && Quiet) // nonsense
505 SwitchesAllowed = false;
506 if (WriteMode || index(*argv, '='))
507 ReturnCode = WriteSetting(*argv);
509 ReturnCode = ReadSetting(*argv);