procps/sysctl.c

512 lines
13 KiB
C
Raw Normal View History

2002-02-02 06:47:29 +08:00
/*
* Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
*
*
* "Copyright 1999 George Staikos
* This file may be used subject to the terms and conditions of the
* GNU General Public License Version 2, or any later version
* at your option, as published by the Free Software Foundation.
* 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."
*
* Changelog:
* v1.01:
* - added -p <preload> to preload values from a file
2004-07-15 12:44:42 +08:00
* Horms:
* - added -q to be quiet when modifying values
*
* Changes by Albert Cahalan, 2002.
2002-02-02 06:47:29 +08:00
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
2002-11-27 16:21:30 +08:00
#include "proc/procps.h"
2002-12-12 06:55:42 +08:00
#include "proc/version.h"
2002-02-02 06:47:29 +08:00
2002-12-12 06:55:42 +08:00
// Proof that C++ causes brain damage:
typedef int bool;
2002-02-02 06:47:29 +08:00
static bool true = 1;
static bool false = 0;
/*
* Globals...
*/
2002-10-10 05:32:33 +08:00
static const char PROC_PATH[] = "/proc/sys/";
static const char DEFAULT_PRELOAD[] = "/etc/sysctl.conf";
2004-07-15 23:22:23 +08:00
static bool NameOnly;
2002-02-02 06:47:29 +08:00
static bool PrintName;
static bool PrintNewline;
2002-12-12 06:55:42 +08:00
static bool IgnoreError;
2004-07-15 12:44:42 +08:00
static bool Quiet;
2002-02-02 06:47:29 +08:00
/* error messages */
2005-01-06 05:21:58 +08:00
static const char ERR_UNKNOWN_PARAMETER[] = "error: Unknown parameter \"%s\"\n";
static const char ERR_MALFORMED_SETTING[] = "error: Malformed setting \"%s\"\n";
static const char ERR_NO_EQUALS[] = "error: \"%s\" must be of the form name=value\n";
static const char ERR_INVALID_KEY[] = "error: \"%s\" is an unknown key\n";
static const char ERR_UNKNOWN_WRITING[] = "error: \"%s\" setting key \"%s\"\n";
static const char ERR_UNKNOWN_READING[] = "error: \"%s\" reading key \"%s\"\n";
2002-10-10 05:32:33 +08:00
static const char ERR_PERMISSION_DENIED[] = "error: permission denied on key '%s'\n";
2005-01-06 05:21:58 +08:00
static const char ERR_OPENING_DIR[] = "error: unable to open directory \"%s\"\n";
static const char ERR_PRELOAD_FILE[] = "error: unable to open preload file \"%s\"\n";
2002-10-10 05:32:33 +08:00
static const char WARN_BAD_LINE[] = "warning: %s(%d): invalid syntax, continuing...\n";
2002-02-02 06:47:29 +08:00
2002-11-26 07:24:40 +08:00
static void slashdot(char *restrict p, char old, char new){
2002-10-09 14:23:58 +08:00
p = strpbrk(p,"/.");
if(!p) return; /* nothing -- can't be, but oh well */
if(*p==new) return; /* already in desired format */
while(p){
char c = *p;
if(c==old) *p=new;
if(c==new) *p=old;
p = strpbrk(p+1,"/.");
}
}
2002-02-02 06:47:29 +08:00
/*
* Display the usage format
*
*/
2002-11-26 07:24:40 +08:00
static int Usage(const char *restrict const name) {
2002-12-12 06:55:42 +08:00
printf("usage: %s [-n] [-e] variable ... \n"
2004-07-15 12:44:42 +08:00
" %s [-n] [-e] [-q] -w variable=value ... \n"
2002-12-12 06:55:42 +08:00
" %s [-n] [-e] -a \n"
2004-07-15 12:44:42 +08:00
" %s [-n] [-e] [-q] -p <file> (default /etc/sysctl.conf) \n"
2002-12-12 06:55:42 +08:00
" %s [-n] [-e] -A\n", name, name, name, name, name);
2002-10-10 05:32:33 +08:00
return -1;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
/*
* Strip the leading and trailing spaces from a string
*
*/
2002-10-10 05:32:33 +08:00
static char *StripLeadingAndTrailingSpaces(char *oneline) {
char *t;
2002-02-02 06:47:29 +08:00
2002-10-10 05:32:33 +08:00
if (!oneline || !*oneline)
return oneline;
2002-02-02 06:47:29 +08:00
2002-10-10 05:32:33 +08:00
t = oneline;
t += strlen(oneline)-1;
2002-02-02 06:47:29 +08:00
2002-10-10 05:32:33 +08:00
while ((*t==' ' || *t=='\t' || *t=='\n' || *t=='\r') && t!=oneline)
*t-- = 0;
2002-02-02 06:47:29 +08:00
2002-10-10 05:32:33 +08:00
t = oneline;
2002-02-02 06:47:29 +08:00
2002-10-10 05:32:33 +08:00
while ((*t==' ' || *t=='\t') && *t!=0)
t++;
2002-02-02 06:47:29 +08:00
2002-10-10 05:32:33 +08:00
return t;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
2004-07-15 23:22:23 +08:00
static int DisplayAll(const char *restrict const path);
2002-02-02 06:47:29 +08:00
/*
2002-11-26 05:42:17 +08:00
* Read a sysctl setting
2002-02-02 06:47:29 +08:00
*
*/
2002-11-26 07:24:40 +08:00
static int ReadSetting(const char *restrict const name) {
2002-11-26 05:42:17 +08:00
int rc = 0;
2002-11-26 07:24:40 +08:00
char *restrict tmpname;
char *restrict outname;
2002-11-26 05:42:17 +08:00
char inbuf[1025];
2002-11-26 07:24:40 +08:00
FILE *restrict fp;
2002-02-02 06:47:29 +08:00
2002-11-26 07:24:40 +08:00
if (!name || !*name) {
fprintf(stderr, ERR_INVALID_KEY, name);
return -1;
}
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
/* used to open the file */
tmpname = malloc(strlen(name)+strlen(PROC_PATH)+1);
strcpy(tmpname, PROC_PATH);
strcat(tmpname, name);
slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
/* used to display the output */
outname = strdup(name);
slashdot(outname,'/','.'); /* change / to . */
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
fp = fopen(tmpname, "r");
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
if (!fp) {
switch(errno) {
case ENOENT:
2002-12-12 06:55:42 +08:00
if (!IgnoreError) {
fprintf(stderr, ERR_INVALID_KEY, outname);
rc = -1;
}
break;
2002-11-26 05:42:17 +08:00
case EACCES:
fprintf(stderr, ERR_PERMISSION_DENIED, outname);
2002-12-12 06:55:42 +08:00
rc = -1;
break;
2002-11-26 05:42:17 +08:00
default:
2005-01-06 05:21:58 +08:00
fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
2002-12-12 06:55:42 +08:00
rc = -1;
break;
}
2002-11-26 05:42:17 +08:00
} else {
2004-07-15 23:22:23 +08:00
if(fgets(inbuf, sizeof inbuf - 1, fp)) {
// this loop is required, see
// /sbin/sysctl -a | egrep -6 dev.cdrom.info
do {
if (NameOnly) {
fprintf(stdout, "%s\n", outname);
} else {
/* already has the \n in it */
if (PrintName) {
fprintf(stdout, "%s = %s", outname, inbuf);
} else {
if (!PrintNewline) {
char *nlptr = strchr(inbuf,'\n');
if(nlptr) *nlptr='\0';
}
fprintf(stdout, "%s", inbuf);
}
2002-11-26 05:42:17 +08:00
}
2004-07-15 23:22:23 +08:00
} while(fgets(inbuf, sizeof inbuf - 1, fp));
} else {
switch(errno) {
case EACCES:
fprintf(stderr, ERR_PERMISSION_DENIED, outname);
rc = -1;
break;
case EISDIR:{
size_t len;
len = strlen(tmpname);
tmpname[len] = '/';
tmpname[len+1] = '\0';
rc = DisplayAll(tmpname);
break;
}
default:
2005-01-06 05:21:58 +08:00
fprintf(stderr, ERR_UNKNOWN_READING, strerror(errno), outname);
2004-07-15 23:22:23 +08:00
rc = -1;
break;
2002-11-26 05:42:17 +08:00
}
2002-12-12 06:55:42 +08:00
}
2002-11-26 05:42:17 +08:00
fclose(fp);
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
free(tmpname);
free(outname);
return rc;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
/*
* Display all the sysctl settings
*
*/
2004-07-15 23:22:23 +08:00
static int DisplayAll(const char *restrict const path) {
2002-11-26 05:42:17 +08:00
int rc = 0;
int rc2;
2002-11-26 07:24:40 +08:00
DIR *restrict dp;
struct dirent *restrict de;
2002-11-26 05:42:17 +08:00
struct stat ts;
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
dp = opendir(path);
if (!dp) {
fprintf(stderr, ERR_OPENING_DIR, path);
rc = -1;
} else {
2004-07-15 23:22:23 +08:00
readdir(dp); // skip .
readdir(dp); // skip ..
2002-11-26 05:42:17 +08:00
while (( de = readdir(dp) )) {
2004-07-15 23:22:23 +08:00
char *restrict tmpdir;
2002-11-26 07:24:40 +08:00
tmpdir = (char *restrict)malloc(strlen(path)+strlen(de->d_name)+2);
2002-11-26 05:42:17 +08:00
sprintf(tmpdir, "%s%s", path, de->d_name);
2004-07-15 23:22:23 +08:00
rc2 = stat(tmpdir, &ts);
2002-11-26 05:42:17 +08:00
if (rc2 != 0) {
perror(tmpdir);
} else {
if (S_ISDIR(ts.st_mode)) {
strcat(tmpdir, "/");
2004-07-15 23:22:23 +08:00
DisplayAll(tmpdir);
2002-11-26 05:42:17 +08:00
} else {
rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
2002-11-26 07:24:40 +08:00
}
}
2002-11-26 05:42:17 +08:00
free(tmpdir);
2002-11-26 07:24:40 +08:00
}
2002-11-26 05:42:17 +08:00
closedir(dp);
2002-11-26 07:24:40 +08:00
}
2002-11-26 05:42:17 +08:00
return rc;
2002-11-26 07:24:40 +08:00
}
2002-02-02 06:47:29 +08:00
/*
* Write a sysctl setting
*
*/
2002-10-10 05:32:33 +08:00
static int WriteSetting(const char *setting) {
int rc = 0;
const char *name = setting;
const char *value;
const char *equals;
char *tmpname;
FILE *fp;
char *outname;
2002-02-02 06:47:29 +08:00
2002-10-09 14:23:58 +08:00
if (!name) { /* probably don't want to display this err */
2002-02-02 06:47:29 +08:00
return 0;
} /* end if */
equals = index(setting, '=');
if (!equals) {
fprintf(stderr, ERR_NO_EQUALS, setting);
return -1;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
2002-10-09 14:23:58 +08:00
value = equals + 1; /* point to the value in name=value */
2002-02-02 06:47:29 +08:00
if (!*name || !*value || name == equals) {
fprintf(stderr, ERR_MALFORMED_SETTING, setting);
return -2;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
2002-10-09 14:23:58 +08:00
/* used to open the file */
tmpname = malloc(equals-name+1+strlen(PROC_PATH));
2002-02-02 06:47:29 +08:00
strcpy(tmpname, PROC_PATH);
strncat(tmpname, name, (int)(equals-name));
tmpname[equals-name+strlen(PROC_PATH)] = 0;
2002-10-09 14:23:58 +08:00
slashdot(tmpname+strlen(PROC_PATH),'.','/'); /* change . to / */
/* used to display the output */
outname = malloc(equals-name+1);
2002-02-02 06:47:29 +08:00
strncpy(outname, name, (int)(equals-name));
outname[equals-name] = 0;
2002-10-09 14:23:58 +08:00
slashdot(outname,'/','.'); /* change / to . */
2002-02-02 06:47:29 +08:00
fp = fopen(tmpname, "w");
if (!fp) {
switch(errno) {
case ENOENT:
2002-12-12 06:55:42 +08:00
if (!IgnoreError) {
fprintf(stderr, ERR_INVALID_KEY, outname);
rc = -1;
}
break;
2002-02-02 06:47:29 +08:00
case EACCES:
fprintf(stderr, ERR_PERMISSION_DENIED, outname);
2002-12-12 06:55:42 +08:00
rc = -1;
break;
2002-02-02 06:47:29 +08:00
default:
2005-01-06 05:21:58 +08:00
fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
2002-12-12 06:55:42 +08:00
rc = -1;
break;
}
2002-02-02 06:47:29 +08:00
} else {
2004-07-15 12:44:42 +08:00
rc = fprintf(fp, "%s\n", value);
if (rc < 0) {
2005-01-06 05:21:58 +08:00
fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
2004-07-15 12:44:42 +08:00
fclose(fp);
2002-02-02 06:47:29 +08:00
} else {
2004-07-15 12:44:42 +08:00
rc=fclose(fp);
if (rc != 0)
2005-01-06 05:21:58 +08:00
fprintf(stderr, ERR_UNKNOWN_WRITING, strerror(errno), outname);
2004-07-15 12:44:42 +08:00
}
if (rc==0 && !Quiet) {
2004-07-15 23:22:23 +08:00
if (NameOnly) {
fprintf(stdout, "%s\n", outname);
2004-07-15 12:44:42 +08:00
} else {
2004-07-15 23:22:23 +08:00
if (PrintName) {
fprintf(stdout, "%s = %s\n", outname, value);
} else {
if (PrintNewline)
fprintf(stdout, "%s\n", value);
else
fprintf(stdout, "%s", value);
}
2004-07-15 12:44:42 +08:00
}
2002-02-02 06:47:29 +08:00
}
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
free(tmpname);
free(outname);
2002-10-10 05:32:33 +08:00
return rc;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
/*
2002-11-26 05:42:17 +08:00
* Preload the sysctl's from the conf file
* - we parse the file and then reform it (strip out whitespace)
2002-02-02 06:47:29 +08:00
*
*/
2002-12-12 06:55:42 +08:00
static int Preload(const char *restrict const filename) {
2002-12-29 07:57:10 +08:00
char oneline[256];
char buffer[256];
2002-10-10 05:32:33 +08:00
FILE *fp;
2002-11-26 05:42:17 +08:00
char *t;
int n = 0;
2002-12-12 06:55:42 +08:00
int rc = 0;
2002-11-26 05:42:17 +08:00
char *name, *value;
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
fprintf(stderr, ERR_PRELOAD_FILE, filename);
2002-12-12 06:55:42 +08:00
return -1;
}
2002-02-02 06:47:29 +08:00
2002-12-29 07:57:10 +08:00
while (fgets(oneline, sizeof oneline, fp)) {
2002-11-26 05:42:17 +08:00
n++;
t = StripLeadingAndTrailingSpaces(oneline);
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
if (strlen(t) < 2)
continue;
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
if (*t == '#' || *t == ';')
continue;
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
name = strtok(t, "=");
if (!name || !*name) {
fprintf(stderr, WARN_BAD_LINE, filename, n);
continue;
2002-12-12 06:55:42 +08:00
}
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
StripLeadingAndTrailingSpaces(name);
value = strtok(NULL, "\n\r");
if (!value || !*value) {
fprintf(stderr, WARN_BAD_LINE, filename, n);
continue;
2002-12-12 06:55:42 +08:00
}
2002-11-26 05:42:17 +08:00
while ((*value == ' ' || *value == '\t') && *value != 0)
value++;
2004-07-15 23:22:23 +08:00
// should NameOnly affect this?
2002-11-26 05:42:17 +08:00
sprintf(buffer, "%s=%s", name, value);
2002-12-12 06:55:42 +08:00
rc |= WriteSetting(buffer);
}
2002-11-26 05:42:17 +08:00
fclose(fp);
2002-12-12 06:55:42 +08:00
return rc;
}
2002-02-02 06:47:29 +08:00
/*
2002-11-26 05:42:17 +08:00
* Main...
2002-02-02 06:47:29 +08:00
*
*/
2004-07-17 23:25:45 +08:00
int main(int argc, char *argv[]) {
2002-11-26 05:42:17 +08:00
const char *me = (const char *)basename(argv[0]);
bool SwitchesAllowed = true;
bool WriteMode = false;
int ReturnCode = 0;
const char *preloadfile = DEFAULT_PRELOAD;
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
PrintName = true;
PrintNewline = true;
2002-12-12 06:55:42 +08:00
IgnoreError = false;
2004-07-15 12:44:42 +08:00
Quiet = false;
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
if (argc < 2) {
return Usage(me);
2002-12-12 12:49:39 +08:00
}
2002-02-02 06:47:29 +08:00
2002-11-26 05:42:17 +08:00
argv++;
for (; argv && *argv && **argv; argv++) {
if (SwitchesAllowed && **argv == '-') { /* we have a switch */
2002-12-12 12:49:39 +08:00
if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew"
if (!strcmp("--help",*argv)) {
Usage(me);
exit(0);
}
if (!strcmp("--version",*argv)) {
fprintf(stdout, "sysctl (%s)\n",procps_version);
exit(0);
}
fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
return Usage(me);
}
2002-11-26 05:42:17 +08:00
switch((*argv)[1]) {
case 'b':
/* This is "binary" format, which means more for BSD. */
PrintNewline = false;
/* FALL THROUGH */
case 'n':
PrintName = false;
break;
2002-12-12 06:55:42 +08:00
case 'e':
2004-07-17 23:25:45 +08:00
// 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.
2002-12-12 06:55:42 +08:00
IgnoreError = true;
break;
2004-07-15 23:22:23 +08:00
case 'N':
NameOnly = true;
break;
2002-11-26 05:42:17 +08:00
case 'w':
SwitchesAllowed = false;
WriteMode = true;
break;
2004-07-17 23:25:45 +08:00
case 'f': // the NetBSD way
2002-11-26 05:42:17 +08:00
case 'p':
argv++;
if (argv && *argv && **argv) {
preloadfile = *argv;
2002-12-12 06:55:42 +08:00
}
return Preload(preloadfile);
2004-07-15 12:44:42 +08:00
case 'q':
Quiet = true;
break;
2004-07-17 23:25:45 +08:00
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
2002-11-26 05:42:17 +08:00
SwitchesAllowed = false;
2004-07-15 23:22:23 +08:00
return DisplayAll(PROC_PATH);
2002-12-12 06:55:42 +08:00
case 'V':
fprintf(stdout, "sysctl (%s)\n",procps_version);
exit(0);
2004-07-17 23:25:45 +08:00
case 'd': // BSD: print description ("vm.kvm_size: Size of KVM")
case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?)
2002-11-26 05:42:17 +08:00
case '?':
return Usage(me);
default:
fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
return Usage(me);
2002-12-12 06:55:42 +08:00
}
2002-11-26 05:42:17 +08:00
} else {
2004-07-15 23:22:23 +08:00
if (NameOnly && Quiet) // nonsense
return Usage(me);
2002-11-26 05:42:17 +08:00
SwitchesAllowed = false;
2004-07-17 23:25:45 +08:00
if (WriteMode || index(*argv, '='))
2002-11-26 05:42:17 +08:00
ReturnCode = WriteSetting(*argv);
2002-12-12 06:55:42 +08:00
else
ReturnCode = ReadSetting(*argv);
}
}
2002-11-26 05:42:17 +08:00
2002-12-12 06:55:42 +08:00
return ReturnCode;
}
2002-11-26 05:42:17 +08:00
2002-02-02 06:47:29 +08:00