sysctl: Support systemd glob patterns
systemd-sysctl handles glob patterns along with overrides and exceptions. Now the procps sysctl does it too. The return value for sysctl is consistently either 0 or 1. Added tests to check sysctl functions. References: procps-ng/procps#191 Signed-off-by: Craig Small <csmall@dropbear.xyz>
This commit is contained in:
parent
8c23dfdcd4
commit
6389deca5b
1
NEWS
1
NEWS
@ -11,6 +11,7 @@ procps-ng-NEXT
|
|||||||
* ps: Add PSS and USS fields issue #112
|
* ps: Add PSS and USS fields issue #112
|
||||||
* ps: Add two new autogroup fields
|
* ps: Add two new autogroup fields
|
||||||
* slabtop: Don't combine d and o options issue #160
|
* slabtop: Don't combine d and o options issue #160
|
||||||
|
* sysctl: Add support for systemd glob patterns issue #191
|
||||||
* sysctl: Check resolved path to be under /proc/sys issue #179
|
* sysctl: Check resolved path to be under /proc/sys issue #179
|
||||||
* top: added LOGID similar to 3.3.13 ps LUID
|
* top: added LOGID similar to 3.3.13 ps LUID
|
||||||
* top: added EXE identical to 3.3.17 ps EXE
|
* top: added EXE identical to 3.3.17 ps EXE
|
||||||
|
519
sysctl.c
519
sysctl.c
@ -17,6 +17,7 @@
|
|||||||
* along with this program; if not, write to the Free Software
|
* along with this program; if not, write to the Free Software
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*
|
*
|
||||||
|
* Part of this code comes from systemd, especially sysctl.c
|
||||||
* Changelog:
|
* Changelog:
|
||||||
* v1.01:
|
* v1.01:
|
||||||
* - added -p <preload> to preload values from a file
|
* - added -p <preload> to preload values from a file
|
||||||
@ -40,6 +41,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "c.h"
|
#include "c.h"
|
||||||
#include "fileutils.h"
|
#include "fileutils.h"
|
||||||
@ -63,12 +65,34 @@ static bool PrintName;
|
|||||||
static bool PrintNewline;
|
static bool PrintNewline;
|
||||||
static bool IgnoreError;
|
static bool IgnoreError;
|
||||||
static bool Quiet;
|
static bool Quiet;
|
||||||
|
static bool DryRun;
|
||||||
static char *pattern;
|
static char *pattern;
|
||||||
|
|
||||||
#define LINELEN 4096
|
#define LINELEN 4096
|
||||||
static char *iobuf;
|
static char *iobuf;
|
||||||
static size_t iolen = LINELEN;
|
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. */
|
/* Function prototypes. */
|
||||||
static int pattern_match(const char *string, const char *pat);
|
static int pattern_match(const char *string, const char *pat);
|
||||||
static int DisplayAll(const char *restrict const path);
|
static int DisplayAll(const char *restrict const path);
|
||||||
@ -115,6 +139,81 @@ static void slashdot(char *restrict p, char old, char new)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setting_free(SysctlSetting *s) {
|
||||||
|
if (!s)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(s->key);
|
||||||
|
free(s->path);
|
||||||
|
free(s->value);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 / */
|
||||||
|
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
|
* Display the usage format
|
||||||
*/
|
*/
|
||||||
@ -130,6 +229,7 @@ static void __attribute__ ((__noreturn__))
|
|||||||
fputs(_(" -A alias of -a\n"), out);
|
fputs(_(" -A alias of -a\n"), out);
|
||||||
fputs(_(" -X alias of -a\n"), out);
|
fputs(_(" -X alias of -a\n"), out);
|
||||||
fputs(_(" --deprecated include deprecated parameters to listing\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(_(" -b, --binary print value without new line\n"), out);
|
||||||
fputs(_(" -e, --ignore ignore unknown variables errors\n"), out);
|
fputs(_(" -e, --ignore ignore unknown variables errors\n"), out);
|
||||||
fputs(_(" -N, --names print variable names without values\n"), out);
|
fputs(_(" -N, --names print variable names without values\n"), out);
|
||||||
@ -152,6 +252,39 @@ static void __attribute__ ((__noreturn__))
|
|||||||
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Strip the leading and trailing spaces from a string
|
* Strip the leading and trailing spaces from a string
|
||||||
*/
|
*/
|
||||||
@ -181,7 +314,7 @@ static char *StripLeadingAndTrailingSpaces(char *oneline)
|
|||||||
*/
|
*/
|
||||||
static int ReadSetting(const char *restrict const name)
|
static int ReadSetting(const char *restrict const name)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = EXIT_SUCCESS;
|
||||||
char *restrict tmpname;
|
char *restrict tmpname;
|
||||||
char *restrict outname;
|
char *restrict outname;
|
||||||
ssize_t rlen;
|
ssize_t rlen;
|
||||||
@ -213,7 +346,7 @@ static int ReadSetting(const char *restrict const name)
|
|||||||
if (stat(tmpname, &ts) < 0) {
|
if (stat(tmpname, &ts) < 0) {
|
||||||
if (!IgnoreError) {
|
if (!IgnoreError) {
|
||||||
xwarn(_("cannot stat %s"), tmpname);
|
xwarn(_("cannot stat %s"), tmpname);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -235,7 +368,7 @@ static int ReadSetting(const char *restrict const name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pattern && !pattern_match(outname, pattern)) {
|
if (pattern && !pattern_match(outname, pattern)) {
|
||||||
rc = 0;
|
rc = EXIT_SUCCESS;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,19 +384,19 @@ static int ReadSetting(const char *restrict const name)
|
|||||||
case ENOENT:
|
case ENOENT:
|
||||||
if (!IgnoreError) {
|
if (!IgnoreError) {
|
||||||
xwarnx(_("\"%s\" is an unknown key"), outname);
|
xwarnx(_("\"%s\" is an unknown key"), outname);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EACCES:
|
case EACCES:
|
||||||
xwarnx(_("permission denied on key '%s'"), outname);
|
xwarnx(_("permission denied on key '%s'"), outname);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
|
case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
xwarn(_("reading key \"%s\""), outname);
|
xwarn(_("reading key \"%s\""), outname);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -299,7 +432,7 @@ static int ReadSetting(const char *restrict const name)
|
|||||||
case EACCES:
|
case EACCES:
|
||||||
xwarnx(_("permission denied on key '%s'"),
|
xwarnx(_("permission denied on key '%s'"),
|
||||||
outname);
|
outname);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
case EISDIR: {
|
case EISDIR: {
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -311,11 +444,11 @@ static int ReadSetting(const char *restrict const name)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
|
case EIO: /* Ignore stable_secret below /proc/sys/net/ipv6/conf */
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
xwarnx(_("reading key \"%s\""), outname);
|
xwarnx(_("reading key \"%s\""), outname);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -343,7 +476,7 @@ static int is_deprecated(char *filename)
|
|||||||
*/
|
*/
|
||||||
static int DisplayAll(const char *restrict const path)
|
static int DisplayAll(const char *restrict const path)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = EXIT_SUCCESS;
|
||||||
int rc2;
|
int rc2;
|
||||||
DIR *restrict dp;
|
DIR *restrict dp;
|
||||||
struct dirent *restrict de;
|
struct dirent *restrict de;
|
||||||
@ -353,7 +486,7 @@ static int DisplayAll(const char *restrict const path)
|
|||||||
|
|
||||||
if (!dp) {
|
if (!dp) {
|
||||||
xwarnx(_("unable to open directory \"%s\""), path);
|
xwarnx(_("unable to open directory \"%s\""), path);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
} else {
|
} else {
|
||||||
readdir(dp); /* skip . */
|
readdir(dp); /* skip . */
|
||||||
readdir(dp); /* skip .. */
|
readdir(dp); /* skip .. */
|
||||||
@ -389,70 +522,25 @@ static int DisplayAll(const char *restrict const path)
|
|||||||
/*
|
/*
|
||||||
* Write a sysctl setting
|
* Write a sysctl setting
|
||||||
*/
|
*/
|
||||||
static int WriteSetting(const char *setting)
|
static int WriteSetting(
|
||||||
{
|
const char *key,
|
||||||
int rc = 0;
|
const char *path,
|
||||||
const char *name = setting;
|
const char *value,
|
||||||
const char *value;
|
const bool ignore_failure) {
|
||||||
const char *equals;
|
|
||||||
char *tmpname;
|
|
||||||
char *outname;
|
|
||||||
char *last_dot;
|
|
||||||
bool ignore_failure;
|
|
||||||
|
|
||||||
FILE *fp;
|
int rc = EXIT_SUCCESS;
|
||||||
|
FILE *fp;
|
||||||
struct stat ts;
|
struct stat ts;
|
||||||
|
|
||||||
if (!name)
|
if (!key || !path)
|
||||||
/* probably don't want to display this err */
|
return rc;
|
||||||
return 0;
|
|
||||||
|
|
||||||
equals = strchr(setting, '=');
|
if (stat(path, &ts) < 0) {
|
||||||
|
|
||||||
if (!equals) {
|
|
||||||
xwarnx(_("\"%s\" must be of the form name=value"),
|
|
||||||
setting);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* point to the value in name=value */
|
|
||||||
value = equals + 1;
|
|
||||||
|
|
||||||
if (!*name || name == equals) {
|
|
||||||
xwarnx(_("malformed setting \"%s\""), setting);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
ignore_failure = name[0] == '-';
|
|
||||||
if (ignore_failure)
|
|
||||||
name++;
|
|
||||||
|
|
||||||
/* used to open the file */
|
|
||||||
tmpname = xmalloc(equals - name + 1 + strlen(PROC_PATH));
|
|
||||||
strcpy(tmpname, PROC_PATH);
|
|
||||||
strncat(tmpname, name, (int) (equals - name));
|
|
||||||
tmpname[equals - name + strlen(PROC_PATH)] = 0;
|
|
||||||
/* change . to / */
|
|
||||||
slashdot(tmpname + strlen(PROC_PATH), '.', '/');
|
|
||||||
|
|
||||||
/* used to display the output */
|
|
||||||
outname = xmalloc(equals - name + 1);
|
|
||||||
strncpy(outname, name, (int) (equals - name));
|
|
||||||
outname[equals - name] = 0;
|
|
||||||
/* change / to . */
|
|
||||||
slashdot(outname, '/', '.');
|
|
||||||
last_dot = strrchr(outname, '.');
|
|
||||||
if (last_dot != NULL && is_deprecated(last_dot + 1)) {
|
|
||||||
xwarnx(_("%s is deprecated, value not set"), outname);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stat(tmpname, &ts) < 0) {
|
|
||||||
if (!IgnoreError) {
|
if (!IgnoreError) {
|
||||||
xwarn(_("cannot stat %s"), tmpname);
|
xwarn(_("cannot stat %s"), path);
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
goto out;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_proc_path(tmpname)) {
|
if (!is_proc_path(tmpname)) {
|
||||||
@ -461,63 +549,161 @@ static int WriteSetting(const char *setting)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((ts.st_mode & S_IWUSR) == 0) {
|
if ((ts.st_mode & S_IWUSR) == 0) {
|
||||||
xwarn(_("setting key \"%s\""), outname);
|
xwarn(_("setting key \"%s\""), key);
|
||||||
goto out;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (S_ISDIR(ts.st_mode)) {
|
if (S_ISDIR(ts.st_mode)) {
|
||||||
xwarn(_("setting key \"%s\""), outname);
|
xwarn(_("setting key \"%s\""), key);
|
||||||
goto out;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = fprocopen(tmpname, "w");
|
if (!DryRun) {
|
||||||
|
if ((fp = fprocopen(path, "w")) == NULL) {
|
||||||
if (!fp) {
|
switch (errno) {
|
||||||
switch (errno) {
|
case ENOENT:
|
||||||
case ENOENT:
|
if (!IgnoreError) {
|
||||||
if (!IgnoreError) {
|
xwarnx(_("\"%s\" is an unknown key%s"),
|
||||||
xwarnx(_("\"%s\" is an unknown key%s"), outname, (ignore_failure?_(", ignoring"):""));
|
key, (ignore_failure?_(", ignoring"):""));
|
||||||
if (!ignore_failure)
|
if (!ignore_failure)
|
||||||
rc = -1;
|
rc = EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EPERM:
|
case EPERM:
|
||||||
case EROFS:
|
case EROFS:
|
||||||
case EACCES:
|
case EACCES:
|
||||||
xwarnx(_("permission denied on key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
|
xwarnx(_("permission denied on key \"%s\"%s"),
|
||||||
break;
|
key, (ignore_failure?_(", ignoring"):""));
|
||||||
default:
|
break;
|
||||||
xwarn(_("setting key \"%s\"%s"), outname, (ignore_failure?_(", ignoring"):""));
|
default:
|
||||||
break;
|
xwarn(_("setting key \"%s\"%s"),
|
||||||
}
|
key, (ignore_failure?_(", ignoring"):""));
|
||||||
if (!ignore_failure && errno != ENOENT)
|
break;
|
||||||
rc = -1;
|
}
|
||||||
} else {
|
if (!ignore_failure && errno != ENOENT)
|
||||||
rc = fprintf(fp, "%s\n", value);
|
rc = EXIT_FAILURE;
|
||||||
if (0 < rc)
|
} else {
|
||||||
rc = 0;
|
if (0 < fprintf(fp, "%s\n", value))
|
||||||
if (close_stream(fp) != 0)
|
rc = EXIT_SUCCESS;
|
||||||
xwarn(_("setting key \"%s\""), outname);
|
if (close_stream(fp) != 0) {
|
||||||
else if (rc == 0 && !Quiet) {
|
xwarn(_("setting key \"%s\""), path);
|
||||||
if (NameOnly) {
|
return rc;
|
||||||
fprintf(stdout, "%s\n", outname);
|
}
|
||||||
} else {
|
}
|
||||||
if (PrintName) {
|
}
|
||||||
fprintf(stdout, "%s = %s\n",
|
if ((rc == EXIT_SUCCESS && !Quiet) || DryRun) {
|
||||||
outname, value);
|
if (NameOnly) {
|
||||||
} else {
|
printf("%s\n", value);
|
||||||
if (PrintNewline)
|
} else {
|
||||||
fprintf(stdout, "%s\n", value);
|
if (PrintName) {
|
||||||
else
|
printf("%s = %s\n", path, value);
|
||||||
fprintf(stdout, "%s", value);
|
} else {
|
||||||
}
|
if (PrintNewline)
|
||||||
}
|
printf("%s\n", value);
|
||||||
}
|
else
|
||||||
}
|
printf("%s", value);
|
||||||
out:
|
}
|
||||||
free(tmpname);
|
}
|
||||||
free(outname);
|
}
|
||||||
return rc;
|
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)
|
||||||
|
{
|
||||||
|
SysctlSetting *s;
|
||||||
|
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)) {
|
||||||
|
char *gl_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)
|
static int pattern_match(const char *string, const char *pat)
|
||||||
@ -538,12 +724,12 @@ static int pattern_match(const char *string, const char *pat)
|
|||||||
* Preload the sysctl's from the conf file. We parse the file and then
|
* Preload the sysctl's from the conf file. We parse the file and then
|
||||||
* reform it (strip out whitespace).
|
* reform it (strip out whitespace).
|
||||||
*/
|
*/
|
||||||
static int Preload(const char *restrict const filename)
|
static int Preload(SettingList *setlist, const char *restrict const filename)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char *t;
|
char *t;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
int rc = 0;
|
int rc = EXIT_SUCCESS;
|
||||||
ssize_t rlen;
|
ssize_t rlen;
|
||||||
char *name, *value;
|
char *name, *value;
|
||||||
glob_t globbuf;
|
glob_t globbuf;
|
||||||
@ -572,62 +758,26 @@ static int Preload(const char *restrict const filename)
|
|||||||
? stdin : fopen(globbuf.gl_pathv[j], "r");
|
? stdin : fopen(globbuf.gl_pathv[j], "r");
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
|
xwarn(_("cannot open \"%s\""), globbuf.gl_pathv[j]);
|
||||||
rc = -1;
|
return EXIT_FAILURE;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
|
while ((rlen = getline(&iobuf, &iolen, fp)) > 0) {
|
||||||
size_t offset;
|
size_t offset;
|
||||||
|
SysctlSetting *setting;
|
||||||
|
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
if (rlen < 2)
|
if (rlen < 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
t = StripLeadingAndTrailingSpaces(iobuf);
|
if ( (setting = parse_setting_line(globbuf.gl_pathv[j], n, iobuf))
|
||||||
if (strlen(t) < 2)
|
== NULL)
|
||||||
continue;
|
continue;
|
||||||
|
settinglist_add(setlist, setting);
|
||||||
if (*t == '#' || *t == ';')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
name = strtok(t, "=");
|
|
||||||
if (!name || !*name) {
|
|
||||||
xwarnx(_("%s(%d): invalid syntax, continuing..."),
|
|
||||||
globbuf.gl_pathv[j], n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
StripLeadingAndTrailingSpaces(name);
|
|
||||||
|
|
||||||
if (pattern && !pattern_match(name, pattern))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
offset = strlen(name);
|
|
||||||
memmove(&iobuf[0], name, offset);
|
|
||||||
iobuf[offset++] = '=';
|
|
||||||
|
|
||||||
value = strtok(NULL, "\n\r");
|
|
||||||
if (!value || !*value) {
|
|
||||||
xwarnx(_("%s(%d): invalid syntax, continuing..."),
|
|
||||||
globbuf.gl_pathv[j], n);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((*value == ' ' || *value == '\t') && *value != 0)
|
|
||||||
value++;
|
|
||||||
|
|
||||||
/* should NameOnly affect this? */
|
|
||||||
memmove(&iobuf[offset], value, strlen(value));
|
|
||||||
offset += strlen(value);
|
|
||||||
iobuf[offset] = '\0';
|
|
||||||
|
|
||||||
rc |= WriteSetting(iobuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,7 +793,7 @@ static int sortpairs(const void *A, const void *B)
|
|||||||
return strcmp(a->name, b->name);
|
return strcmp(a->name, b->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int PreloadSystem(void)
|
static int PreloadSystem(SettingList *setlist)
|
||||||
{
|
{
|
||||||
unsigned di, i;
|
unsigned di, i;
|
||||||
const char *dirs[] = {
|
const char *dirs[] = {
|
||||||
@ -655,7 +805,7 @@ static int PreloadSystem(void)
|
|||||||
};
|
};
|
||||||
struct pair **cfgs = NULL;
|
struct pair **cfgs = NULL;
|
||||||
unsigned ncfgs = 0;
|
unsigned ncfgs = 0;
|
||||||
int rc = 0;
|
int rc = EXIT_SUCCESS;
|
||||||
struct stat ts;
|
struct stat ts;
|
||||||
enum { nprealloc = 16 };
|
enum { nprealloc = 16 };
|
||||||
|
|
||||||
@ -713,14 +863,14 @@ static int PreloadSystem(void)
|
|||||||
for (i = 0; i < ncfgs; ++i) {
|
for (i = 0; i < ncfgs; ++i) {
|
||||||
if (!Quiet)
|
if (!Quiet)
|
||||||
printf(_("* Applying %s ...\n"), cfgs[i]->value);
|
printf(_("* Applying %s ...\n"), cfgs[i]->value);
|
||||||
rc |= Preload(cfgs[i]->value);
|
rc |= Preload(setlist, cfgs[i]->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
|
if (stat(DEFAULT_PRELOAD, &ts) == 0 && S_ISREG(ts.st_mode)) {
|
||||||
if (!Quiet)
|
if (!Quiet)
|
||||||
printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
|
printf(_("* Applying %s ...\n"), DEFAULT_PRELOAD);
|
||||||
rc |= Preload(DEFAULT_PRELOAD);
|
rc |= Preload(setlist, DEFAULT_PRELOAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cleaning */
|
/* cleaning */
|
||||||
@ -742,15 +892,19 @@ int main(int argc, char *argv[])
|
|||||||
bool preloadfileOpt = false;
|
bool preloadfileOpt = false;
|
||||||
int ReturnCode = 0;
|
int ReturnCode = 0;
|
||||||
int c;
|
int c;
|
||||||
|
int rc;
|
||||||
const char *preloadfile = NULL;
|
const char *preloadfile = NULL;
|
||||||
|
SettingList *setlist;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
DEPRECATED_OPTION = CHAR_MAX + 1,
|
DEPRECATED_OPTION = CHAR_MAX + 1,
|
||||||
SYSTEM_OPTION
|
SYSTEM_OPTION,
|
||||||
|
DRYRUN_OPTION
|
||||||
};
|
};
|
||||||
static const struct option longopts[] = {
|
static const struct option longopts[] = {
|
||||||
{"all", no_argument, NULL, 'a'},
|
{"all", no_argument, NULL, 'a'},
|
||||||
{"deprecated", no_argument, NULL, DEPRECATED_OPTION},
|
{"deprecated", no_argument, NULL, DEPRECATED_OPTION},
|
||||||
|
{"dry-run", no_argument, NULL, DRYRUN_OPTION},
|
||||||
{"binary", no_argument, NULL, 'b'},
|
{"binary", no_argument, NULL, 'b'},
|
||||||
{"ignore", no_argument, NULL, 'e'},
|
{"ignore", no_argument, NULL, 'e'},
|
||||||
{"names", no_argument, NULL, 'N'},
|
{"names", no_argument, NULL, 'N'},
|
||||||
@ -778,6 +932,10 @@ int main(int argc, char *argv[])
|
|||||||
IgnoreError = false;
|
IgnoreError = false;
|
||||||
Quiet = false;
|
Quiet = false;
|
||||||
IgnoreDeprecated = true;
|
IgnoreDeprecated = true;
|
||||||
|
DryRun = false;
|
||||||
|
setlist = xmalloc(sizeof(SettingList));
|
||||||
|
setlist->head = NULL;
|
||||||
|
setlist->tail = NULL;
|
||||||
|
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
Usage(stderr);
|
Usage(stderr);
|
||||||
@ -830,7 +988,12 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case SYSTEM_OPTION:
|
case SYSTEM_OPTION:
|
||||||
IgnoreError = true;
|
IgnoreError = true;
|
||||||
return PreloadSystem();
|
rc |= PreloadSystem(setlist);
|
||||||
|
rc |= write_setting_list(setlist);
|
||||||
|
return rc;
|
||||||
|
case DRYRUN_OPTION:
|
||||||
|
DryRun = true;
|
||||||
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
pattern = xstrdup(optarg);
|
pattern = xstrdup(optarg);
|
||||||
break;
|
break;
|
||||||
@ -858,15 +1021,16 @@ int main(int argc, char *argv[])
|
|||||||
int ret = EXIT_SUCCESS, i;
|
int ret = EXIT_SUCCESS, i;
|
||||||
if (!preloadfile) {
|
if (!preloadfile) {
|
||||||
if (!argc) {
|
if (!argc) {
|
||||||
ret |= Preload(DEFAULT_PRELOAD);
|
ret |= Preload(setlist, DEFAULT_PRELOAD);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* This happens when -pfile option is
|
/* This happens when -pfile option is
|
||||||
* used without space. */
|
* used without space. */
|
||||||
ret |= Preload(preloadfile);
|
ret |= Preload(setlist, preloadfile);
|
||||||
}
|
}
|
||||||
for (i = 0; i < argc; i++)
|
for (i = 0; i < argc; i++)
|
||||||
ret |= Preload(argv[i]);
|
ret |= Preload(setlist, argv[i]);
|
||||||
|
ret |= write_setting_list(setlist);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,9 +1044,14 @@ int main(int argc, char *argv[])
|
|||||||
program_invocation_short_name);
|
program_invocation_short_name);
|
||||||
|
|
||||||
for ( ; *argv; argv++) {
|
for ( ; *argv; argv++) {
|
||||||
if (WriteMode || strchr(*argv, '='))
|
if (WriteMode || strchr(*argv, '=')) {
|
||||||
ReturnCode += WriteSetting(*argv);
|
SysctlSetting *s;
|
||||||
else
|
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);
|
ReturnCode += ReadSetting(*argv);
|
||||||
}
|
}
|
||||||
return ReturnCode;
|
return ReturnCode;
|
||||||
|
@ -136,6 +136,15 @@ proc expect_table_dsc { test match_header match_item } {
|
|||||||
#}
|
#}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc expect_spawn_retval { test retval } {
|
||||||
|
foreach {pid spawnid os_error_flag value} [wait] break
|
||||||
|
|
||||||
|
if {$value == $retval} {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fail "$test (exit value)"
|
||||||
|
}
|
||||||
|
|
||||||
proc make_pipeproc { } {
|
proc make_pipeproc { } {
|
||||||
global pipeproc_pid pipeproc_spawnid topdir
|
global pipeproc_pid pipeproc_spawnid topdir
|
||||||
|
|
||||||
|
29
testsuite/sysctl.test/sysctl_write.exp
Normal file
29
testsuite/sysctl.test/sysctl_write.exp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
set sysctl ${topdir}sysctl
|
||||||
|
|
||||||
|
set test "sysctl write from command line"
|
||||||
|
spawn $sysctl --dry-run kernel.hostname=procps-test
|
||||||
|
expect_pass "$test" "/proc/sys/kernel/hostname = procps-test"
|
||||||
|
|
||||||
|
set test "sysctl write from configuration file"
|
||||||
|
spawn $sysctl --dry-run -f ${topdir}testsuite/sysctl_glob_test.conf
|
||||||
|
expect_pass "$test" "/proc/sys/fs/protected_fifos = 2\\s+/proc/sys/fs/protected_symlinks = 2\\s+/proc/sys/fs/protected_hardlinks = 1"
|
||||||
|
|
||||||
|
set hostname_file "/proc/sys/kernel/hostname"
|
||||||
|
if {[file exists ${hostname_file}]} {
|
||||||
|
if {[file writable ${hostname_file}]} {
|
||||||
|
unsupported "sysctl write: hostname file is writable"
|
||||||
|
} else {
|
||||||
|
set test "sysctl write unwritable file"
|
||||||
|
spawn $sysctl -q kernel.hostname=procpstest
|
||||||
|
expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\"\\s*$"
|
||||||
|
expect_spawn_retval "$test" 1
|
||||||
|
|
||||||
|
set test "sysctl write unwritable file ignored"
|
||||||
|
spawn $sysctl -q -- -kernel.hostname=procpstest
|
||||||
|
expect_pass "$test" "sysctl: permission denied on key \"kernel.hostname\", ignoring\\s*$"
|
||||||
|
expect_spawn_retval "$test" 0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsupported "sysctl write: hostname file doe not exist"
|
||||||
|
}
|
6
testsuite/sysctl_glob_test.conf
Normal file
6
testsuite/sysctl_glob_test.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#
|
||||||
|
# Test configuration for for glob in sysctl
|
||||||
|
#
|
||||||
|
fs.protected_* = 2
|
||||||
|
fs.protected_hardlinks = 1
|
||||||
|
-fs.protected_regular
|
Loading…
Reference in New Issue
Block a user