sysctl: options handling & usage output

The sysctl now uses getopt_long and has help screen which be user
friendly. Rest of the modernization is left later, since this is
a command is used in scripts, and changing for instance error
printing to use warn & warnx could break stuff.

Signed-off-by: Sami Kerola <kerolasa@iki.fi>
This commit is contained in:
Sami Kerola 2011-06-05 19:46:49 +02:00
parent d50884788d
commit 81df8e2630

219
sysctl.c
View File

@ -22,20 +22,21 @@
*/ */
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <dirent.h> #include <unistd.h>
#include <string.h>
#include <errno.h>
#include <libgen.h>
#include "proc/procps.h" #include "proc/procps.h"
#include "proc/version.h" #include "proc/version.h"
// Proof that C++ causes brain damage: /* Proof that C++ causes brain damage: */
typedef int bool; typedef int bool;
static bool true = 1; static bool true = 1;
static bool false = 0; static bool false = 0;
@ -53,7 +54,6 @@ static bool IgnoreError;
static bool Quiet; static bool Quiet;
/* error messages */ /* error messages */
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_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_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_INVALID_KEY[] = "error: \"%s\" is an unknown key\n";
@ -83,15 +83,33 @@ static void slashdot(char *restrict p, char old, char new){
* Display the usage format * Display the usage format
* *
*/ */
static int Usage(const char *restrict const name) { static void __attribute__ ((__noreturn__))
printf("usage: %s [-n] [-e] variable ... \n" Usage(FILE * out)
" %s [-n] [-e] [-q] -w variable=value ... \n" {
" %s [-n] [-e] -a \n" fprintf(out,
" %s [-n] [-e] [-q] -p <file> (default /etc/sysctl.conf) \n" "\nUsage: %s [options] [variable[=value] ...]\n"
" %s [-n] [-e] -A\n", name, name, name, name, name); "\nOptions:\n", program_invocation_short_name);
return -1; fprintf(out,
} " -a, --all display all variables\n"
" -A alias of -a\n"
" -X alias of -a\n"
" -b, --binary print value without new line\n"
" -e, --ignore ignore unknown variables errors\n"
" -N, --names print variable names without values\n"
" -n, --values print only values of a variables\n"
" -p, --load[=<file>] read values from file\n"
" -f alias of -p\n"
" -q, --quiet do not echo variable set\n"
" -w, --write enable writing a value to variable\n"
" -o does nothing\n"
" -x does nothing\n"
" -h, --help display this help text\n"
" -d alias of -h\n"
" -V, --version display version information and exit\n");
fprintf(out, "\nFor more information see sysctl(8).\n");
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}
/* /*
* Strip the leading and trailing spaces from a string * Strip the leading and trailing spaces from a string
@ -460,106 +478,113 @@ static int Preload(const char *restrict const filename) {
* Main... * Main...
* *
*/ */
int main(int argc, char *argv[]) { int main(int argc, char *argv[])
const char *me = (const char *)basename(argv[0]); {
bool SwitchesAllowed = true; bool SwitchesAllowed = true;
bool WriteMode = false; bool WriteMode = false;
bool DisplayAllOpt = false; bool DisplayAllOpt = false;
bool preloadfileOpt = false;
int ReturnCode = 0; int ReturnCode = 0;
int c;
const char *preloadfile = DEFAULT_PRELOAD; const char *preloadfile = DEFAULT_PRELOAD;
static const struct option longopts[] = {
{"all", no_argument, NULL, 'a'},
{"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'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
PrintName = true; PrintName = true;
PrintNewline = true; PrintNewline = true;
IgnoreError = false; IgnoreError = false;
Quiet = false; Quiet = false;
if (argc < 2) { if (argc < 2) {
return Usage(me); Usage(stderr);
} }
argv++; while ((c =
getopt_long(argc, argv, "bneNwfpqoxaAXVdh", longopts,
for (; argv && *argv && **argv; argv++) { NULL)) != -1)
if (SwitchesAllowed && **argv == '-') { /* we have a switch */ switch (c) {
if ((*argv)[1] && (*argv)[2]){ // don't yet handle "sysctl -ew" case 'b':
if (!strcmp("--help",*argv)) { /* This is "binary" format, which means more for BSD. */
Usage(me); PrintNewline = false;
exit(0); /* FALL THROUGH */
} case 'n':
if (!strcmp("--version",*argv)) { PrintName = false;
fprintf(stdout, "sysctl (%s)\n",procps_version); break;
exit(0); case 'e':
} /*
fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv); * For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default)
return Usage(me); * We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors.
} */
switch((*argv)[1]) { IgnoreError = true;
case 'b': break;
/* This is "binary" format, which means more for BSD. */ case 'N':
PrintNewline = false; NameOnly = true;
/* FALL THROUGH */ break;
case 'n': case 'w':
PrintName = false; SwitchesAllowed = false;
WriteMode = true;
break;
case 'f': /* the NetBSD way */
case 'p':
preloadfileOpt = true;
if (optarg)
preloadfile = optarg;
break; break;
case 'e': case 'q':
// For FreeBSD, -e means a "%s=%s\n" format. ("%s: %s\n" default) Quiet = true;
// We (and NetBSD) use "%s = %s\n" always, and -e to ignore errors. break;
IgnoreError = true; 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; break;
case 'N': case 'V':
NameOnly = true; fprintf(stdout, "sysctl (%s)\n",procps_version);
break; exit(0);
case 'w': case 'd': /* BSD: print description ("vm.kvm_size: Size of KVM") */
SwitchesAllowed = false; case 'h': /* BSD: human-readable (did FreeBSD 5 make -e default?) */
WriteMode = true; case '?':
break; Usage(stdout);
case 'f': // the NetBSD way default:
case 'p': Usage(stderr);
argv++;
if (argv && *argv && **argv) {
preloadfile = *argv;
}
return Preload(preloadfile);
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 'V':
fprintf(stdout, "sysctl (%s)\n",procps_version);
exit(0);
case 'd': // BSD: print description ("vm.kvm_size: Size of KVM")
case 'h': // BSD: human-readable (did FreeBSD 5 make -e default?)
case '?':
return Usage(me);
default:
fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
return Usage(me);
}
} else {
if (NameOnly && Quiet) // nonsense
return Usage(me);
if (DisplayAllOpt) // We cannot have values with -a
return Usage(me);
SwitchesAllowed = false;
if (WriteMode || strchr(*argv, '='))
ReturnCode = WriteSetting(*argv);
else
ReturnCode = ReadSetting(*argv);
} }
} if (DisplayAllOpt)
if (DisplayAllOpt) {
if (Quiet)
return Usage(me);
return DisplayAll(PROC_PATH); return DisplayAll(PROC_PATH);
} if (preloadfileOpt)
return Preload(preloadfile);
argc -= optind;
argv += optind;
if (argc < 1) {
warnx("no variables specified");
Usage(stderr);
}
if (NameOnly && Quiet) {
warnx("options -N and -q can not coexist");
Usage(stderr);
}
if (WriteMode || index(*argv, '='))
ReturnCode = WriteSetting(*argv);
else
ReturnCode = ReadSetting(*argv);
return ReturnCode; return ReturnCode;
} }