Add getenv(9) boolean parsing functions

This adds the getenv_bool() function, to parse a boolean value from a
kernel environment variable or tunable. This works for traditional
boolean values like "0" and "1", and also "true" and "false"
(case-insensitive). These semantics do not yet apply to sysctls declared
using SYSCTL_BOOL with CTLFLAG_TUN (they still only parse 1 and 0).

Also added are two wrapper functions, getenv_is_true() and
getenv_is_false(). These are slightly simpler for callers wishing to
perform a single check of a configuration variable.

Reviewed by:	jhb (slightly earlier version)
Sponsored by:	NetApp, Inc.
Sponsored by:	Klara, Inc.
Differential Revision:	https://reviews.freebsd.org/D26270
This commit is contained in:
Mitchell Horne 2020-09-21 15:24:44 +00:00
parent aea49d9fed
commit cba446e2c2
5 changed files with 138 additions and 2 deletions

View File

@ -1092,6 +1092,9 @@ MLINKS+=getenv.9 freeenv.9 \
getenv.9 getenv_quad.9 \
getenv.9 getenv_uint.9 \
getenv.9 getenv_ulong.9 \
getenv.9 getenv_bool.9 \
getenv.9 getenv_is_true.9 \
getenv.9 getenv_is_false.9 \
getenv.9 kern_getenv.9 \
getenv.9 kern_setenv.9 \
getenv.9 kern_unsetenv.9 \

View File

@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 1, 2017
.Dd September 21, 2020
.Dt GETENV 9
.Os
.Sh NAME
@ -39,6 +39,9 @@
.Nm getenv_quad ,
.Nm getenv_uint ,
.Nm getenv_ulong ,
.Nm getenv_bool ,
.Nm getenv_is_true ,
.Nm getenv_is_false ,
.Nm kern_setenv ,
.Nm testenv ,
.Nm kern_unsetenv
@ -63,6 +66,12 @@
.Ft int
.Fn getenv_ulong "const char *name" "unsigned long *data"
.Ft int
.Fn getenv_bool "const char *name" "bool *data"
.Ft bool
.Fn getenv_is_true "const char *name"
.Ft bool
.Fn getenv_is_false "const char *name"
.Ft int
.Fn kern_setenv "const char *name" "const char *value"
.Ft int
.Fn testenv "const char *name"
@ -194,6 +203,28 @@ up to
characters of its value are copied to the buffer pointed to by
.Fa data
followed by a null character and a non-zero value is returned.
.Pp
The
.Fn getenv_bool
function interprets the value of the kernel environment variable
.Fa name
as a boolean value by performing a case-insensitive comparison against the
strings "1",
"0",
"true",
and "false".
If the environment variable exists and has a valid boolean value, then that
value will be copied to the variable pointed to by
.Fa data .
If the environment variable exists but is not a boolean value, then a warning
will be printed to the kernel message buffer.
The
.Fn getenv_is_true
and
.Fn getenv_is_false
functions are wrappers around
.Fn getenv_bool
that simplify testing for a desired boolean value.
.Sh RETURN VALUES
The
.Fn kern_getenv
@ -211,12 +242,25 @@ The
.Fn testenv
function returns zero if the specified environment variable does not exist and
a non-zero value if it does exist.
.Pp
The
.Fn getenv_int ,
.Fn getenv_long ,
.Fn getenv_string ,
.Fn getenv_quad ,
.Fn getenv_uint ,
.Fn getenv_ulong ,
and
.Fn getenv_ulong
.Fn getenv_bool
functions return a non-zero value on success and zero on failure.
.Pp
The
.Fn getenv_is_true
and
.Fn getenv_is_false
functions return
.Dv true
if the specified environment variable exists and its value matches the desired
boolean condition, and
.Dv false
otherwise.

View File

@ -941,6 +941,65 @@ getenv_quad(const char *name, quad_t *data)
return (0);
}
/*
* Return a boolean value from an environment variable. This can be in
* numerical or string form, i.e. "1" or "true".
*/
int
getenv_bool(const char *name, bool *data)
{
char *val;
int ret = 0;
if (name == NULL)
return (0);
val = kern_getenv(name);
if (val == NULL)
return (0);
if ((strcmp(val, "1") == 0) || (strcasecmp(val, "true") == 0)) {
*data = true;
ret = 1;
} else if ((strcmp(val, "0") == 0) || (strcasecmp(val, "false") == 0)) {
*data = false;
ret = 1;
} else {
/* Spit out a warning for malformed boolean variables. */
printf("Environment variable %s has non-boolean value \"%s\"\n",
name, val);
}
freeenv(val);
return (ret);
}
/*
* Wrapper around getenv_bool to easily check for true.
*/
bool
getenv_is_true(const char *name)
{
bool val;
if (getenv_bool(name, &val) != 0)
return (val);
return (false);
}
/*
* Wrapper around getenv_bool to easily check for false.
*/
bool
getenv_is_false(const char *name)
{
bool val;
if (getenv_bool(name, &val) != 0)
return (!val);
return (false);
}
/*
* Find the next entry after the one which (cp) falls within, return a
* pointer to its start or NULL if there are no more.
@ -1007,6 +1066,14 @@ tunable_quad_init(void *data)
TUNABLE_QUAD_FETCH(d->path, d->var);
}
void
tunable_bool_init(void *data)
{
struct tunable_bool *d = (struct tunable_bool *)data;
TUNABLE_BOOL_FETCH(d->path, d->var);
}
void
tunable_str_init(void *data)
{

View File

@ -421,6 +421,25 @@ struct tunable_quad {
#define TUNABLE_QUAD_FETCH(path, var) getenv_quad((path), (var))
/*
* bool
*/
extern void tunable_bool_init(void *);
struct tunable_bool {
const char *path;
bool *var;
};
#define TUNABLE_BOOL(path, var) \
static struct tunable_bool __CONCAT(__tunable_bool_, __LINE__) = { \
(path), \
(var), \
}; \
SYSINIT(__CONCAT(__Tunable_init_, __LINE__), \
SI_SUB_TUNABLES, SI_ORDER_MIDDLE, tunable_bool_init, \
&__CONCAT(__tunable_bool_, __LINE__))
#define TUNABLE_BOOL_FETCH(path, var) getenv_bool((path), (var))
extern void tunable_str_init(void *);
struct tunable_str {
const char *path;

View File

@ -461,6 +461,9 @@ int getenv_string(const char *name, char *data, int size);
int getenv_int64(const char *name, int64_t *data);
int getenv_uint64(const char *name, uint64_t *data);
int getenv_quad(const char *name, quad_t *data);
int getenv_bool(const char *name, bool *data);
bool getenv_is_true(const char *name);
bool getenv_is_false(const char *name);
int kern_setenv(const char *name, const char *value);
int kern_unsetenv(const char *name);
int testenv(const char *name);