kenv: allow listing of static kernel environments
The early environment is typically cleared, so these new options need the PRESERVE_EARLY_KENV kernel config(8) option. These environments are reported as missing by kenv(1) if the option is not present in the running kernel. Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D30835
This commit is contained in:
parent
7a129c973b
commit
db0f264393
@ -32,6 +32,7 @@
|
||||
.Nd list or modify the kernel environment
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl l | s
|
||||
.Op Fl hNq
|
||||
.Nm
|
||||
.Op Fl qv
|
||||
@ -45,6 +46,23 @@ The
|
||||
.Nm
|
||||
utility will list all variables in the kernel environment if
|
||||
invoked without arguments.
|
||||
.Pp
|
||||
If the
|
||||
.Fl l
|
||||
option is specified, then the static environment provided by
|
||||
.Xr loader 8
|
||||
will be listed instead.
|
||||
Similarly, the
|
||||
.Fl s
|
||||
option will list the static environment defined by the kernel config.
|
||||
Both of the
|
||||
.Fl l
|
||||
and
|
||||
.Fl s
|
||||
options are dependent on the kernel being configured to preserve early kernel
|
||||
environments.
|
||||
The default kernel configuration does not preserve these environments.
|
||||
.Pp
|
||||
If the
|
||||
.Fl h
|
||||
option is specified, it will limit the report to kernel probe hints.
|
||||
|
@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <kenv.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -36,14 +37,16 @@ __FBSDID("$FreeBSD$");
|
||||
#include <unistd.h>
|
||||
|
||||
static void usage(void);
|
||||
static int kdumpenv(void);
|
||||
static int kdumpenv(int dump_type);
|
||||
static int kgetenv(const char *);
|
||||
static int ksetenv(const char *, char *);
|
||||
static int kunsetenv(const char *);
|
||||
|
||||
static int hflag = 0;
|
||||
static int lflag = 0;
|
||||
static int Nflag = 0;
|
||||
static int qflag = 0;
|
||||
static int sflag = 0;
|
||||
static int uflag = 0;
|
||||
static int vflag = 0;
|
||||
|
||||
@ -51,7 +54,7 @@ static void
|
||||
usage(void)
|
||||
{
|
||||
(void)fprintf(stderr, "%s\n%s\n%s\n",
|
||||
"usage: kenv [-hNq]",
|
||||
"usage: kenv [-l|-s] [-hNq]",
|
||||
" kenv [-qv] variable[=value]",
|
||||
" kenv [-q] -u variable");
|
||||
exit(1);
|
||||
@ -65,17 +68,23 @@ main(int argc, char **argv)
|
||||
|
||||
val = NULL;
|
||||
env = NULL;
|
||||
while ((ch = getopt(argc, argv, "hNquv")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "hlNqsuv")) != -1) {
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
hflag++;
|
||||
break;
|
||||
case 'l':
|
||||
lflag++;
|
||||
break;
|
||||
case 'N':
|
||||
Nflag++;
|
||||
break;
|
||||
case 'q':
|
||||
qflag++;
|
||||
break;
|
||||
case 's':
|
||||
sflag++;
|
||||
break;
|
||||
case 'u':
|
||||
uflag++;
|
||||
break;
|
||||
@ -100,12 +109,23 @@ main(int argc, char **argv)
|
||||
}
|
||||
if ((hflag || Nflag) && env != NULL)
|
||||
usage();
|
||||
if (lflag && sflag)
|
||||
usage();
|
||||
if (argc > 0 || ((uflag || vflag) && env == NULL))
|
||||
usage();
|
||||
if (env == NULL) {
|
||||
error = kdumpenv();
|
||||
if (error && !qflag)
|
||||
warn("kdumpenv");
|
||||
if (lflag)
|
||||
error = kdumpenv(KENV_DUMP_LOADER);
|
||||
else if (sflag)
|
||||
error = kdumpenv(KENV_DUMP_STATIC);
|
||||
else
|
||||
error = kdumpenv(KENV_DUMP);
|
||||
if (error && !qflag) {
|
||||
if (errno == ENOENT)
|
||||
warnx("requested environment is unavailable");
|
||||
else
|
||||
warn("kdumpenv");
|
||||
}
|
||||
} else if (val == NULL) {
|
||||
if (uflag) {
|
||||
error = kunsetenv(env);
|
||||
@ -125,12 +145,12 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
static int
|
||||
kdumpenv(void)
|
||||
kdumpenv(int dump_type)
|
||||
{
|
||||
char *buf, *bp, *cp;
|
||||
int buflen, envlen;
|
||||
|
||||
envlen = kenv(KENV_DUMP, NULL, NULL, 0);
|
||||
envlen = kenv(dump_type, NULL, NULL, 0);
|
||||
if (envlen < 0)
|
||||
return (-1);
|
||||
for (;;) {
|
||||
@ -138,7 +158,7 @@ kdumpenv(void)
|
||||
buf = calloc(1, buflen + 1);
|
||||
if (buf == NULL)
|
||||
return (-1);
|
||||
envlen = kenv(KENV_DUMP, NULL, buf, buflen);
|
||||
envlen = kenv(dump_type, NULL, buf, buflen);
|
||||
if (envlen < 0) {
|
||||
free(buf);
|
||||
return (-1);
|
||||
|
@ -26,7 +26,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd February 20, 2017
|
||||
.Dd June 20, 2021
|
||||
.Dt KENV 2
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -49,7 +49,7 @@ the kernel environment.
|
||||
The
|
||||
.Fa action
|
||||
argument can be one of the following:
|
||||
.Bl -tag -width ".Dv KENV_UNSET"
|
||||
.Bl -tag -width ".Dv KENV_DUMP_LOADER"
|
||||
.It Dv KENV_GET
|
||||
Get the
|
||||
.Fa value
|
||||
@ -90,7 +90,7 @@ and
|
||||
arguments are ignored.
|
||||
This option is only available to the superuser.
|
||||
.It Dv KENV_DUMP
|
||||
Dump as much of the kernel environment as will fit in
|
||||
Dump as much of the dynamic kernel environment as will fit in
|
||||
.Fa value ,
|
||||
whose size is given in
|
||||
.Fa len .
|
||||
@ -103,6 +103,18 @@ will return the number of bytes required to copy out the entire environment.
|
||||
The
|
||||
.Fa name
|
||||
is ignored.
|
||||
.It Dv KENV_DUMP_LOADER
|
||||
Dump the static environment provided by
|
||||
.Xr loader 8 ,
|
||||
with semantics identical to
|
||||
.Dv KENV_DUMP .
|
||||
Duplicate and malformed variables originally present in this environment are
|
||||
discarded by the kernel and will not appear in the output.
|
||||
.It Dv KENV_DUMP_STATIC
|
||||
Dump the static environment defined by the kernel
|
||||
.Xr config 5 .
|
||||
The semantics are identical to
|
||||
.Dv KENV_DUMP_LOADER .
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
@ -142,6 +154,12 @@ for a
|
||||
.Dv KENV_GET
|
||||
or
|
||||
.Dv KENV_UNSET .
|
||||
.It Bq Er ENOENT
|
||||
The requested environment is not available for a
|
||||
.Dv KENV_DUMP_LOADER
|
||||
or
|
||||
.Dv KENV_DUMP_STATIC .
|
||||
The kernel is configured to destroy these environments by default.
|
||||
.It Bq Er EPERM
|
||||
A user other than the superuser attempted to set or unset a kernel
|
||||
environment variable.
|
||||
|
@ -92,60 +92,103 @@ bool dynamic_kenv;
|
||||
#define KENV_CHECK if (!dynamic_kenv) \
|
||||
panic("%s: called before SI_SUB_KMEM", __func__)
|
||||
|
||||
int
|
||||
sys_kenv(td, uap)
|
||||
struct thread *td;
|
||||
struct kenv_args /* {
|
||||
int what;
|
||||
const char *name;
|
||||
char *value;
|
||||
int len;
|
||||
} */ *uap;
|
||||
static int
|
||||
kenv_dump(struct thread *td, char **envp, int what, char *value, int len)
|
||||
{
|
||||
char *name, *value, *buffer = NULL;
|
||||
size_t len, done, needed, buflen;
|
||||
int error, i;
|
||||
char *buffer, *senv;
|
||||
size_t done, needed, buflen;
|
||||
int error;
|
||||
|
||||
error = 0;
|
||||
buffer = NULL;
|
||||
done = needed = 0;
|
||||
|
||||
MPASS(what == KENV_DUMP || what == KENV_DUMP_LOADER ||
|
||||
what == KENV_DUMP_STATIC);
|
||||
|
||||
/*
|
||||
* For non-dynamic kernel environment, we pass in either md_envp or
|
||||
* kern_envp and we must traverse with kernenv_next(). This shuffling
|
||||
* of pointers simplifies the below loop by only differing in how envp
|
||||
* is modified.
|
||||
*/
|
||||
if (what != KENV_DUMP) {
|
||||
senv = (char *)envp;
|
||||
envp = &senv;
|
||||
}
|
||||
|
||||
buflen = len;
|
||||
if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
|
||||
buflen = KENV_SIZE * (KENV_MNAMELEN +
|
||||
kenv_mvallen + 2);
|
||||
if (len > 0 && value != NULL)
|
||||
buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
|
||||
|
||||
/* Only take the lock for the dynamic kenv. */
|
||||
if (what == KENV_DUMP)
|
||||
mtx_lock(&kenv_lock);
|
||||
while (*envp != NULL) {
|
||||
len = strlen(*envp) + 1;
|
||||
needed += len;
|
||||
len = min(len, buflen - done);
|
||||
/*
|
||||
* If called with a NULL or insufficiently large
|
||||
* buffer, just keep computing the required size.
|
||||
*/
|
||||
if (value != NULL && buffer != NULL && len > 0) {
|
||||
bcopy(*envp, buffer + done, len);
|
||||
done += len;
|
||||
}
|
||||
|
||||
/* Advance the pointer depending on the kenv format. */
|
||||
if (what == KENV_DUMP)
|
||||
envp++;
|
||||
else
|
||||
senv = kernenv_next(senv);
|
||||
}
|
||||
if (what == KENV_DUMP)
|
||||
mtx_unlock(&kenv_lock);
|
||||
if (buffer != NULL) {
|
||||
error = copyout(buffer, value, done);
|
||||
free(buffer, M_TEMP);
|
||||
}
|
||||
td->td_retval[0] = ((done == needed) ? 0 : needed);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
sys_kenv(struct thread *td, struct kenv_args *uap)
|
||||
{
|
||||
char *name, *value;
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false"));
|
||||
|
||||
error = 0;
|
||||
if (uap->what == KENV_DUMP) {
|
||||
|
||||
switch (uap->what) {
|
||||
case KENV_DUMP:
|
||||
#ifdef MAC
|
||||
error = mac_kenv_check_dump(td->td_ucred);
|
||||
if (error)
|
||||
return (error);
|
||||
#endif
|
||||
done = needed = 0;
|
||||
buflen = uap->len;
|
||||
if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
|
||||
buflen = KENV_SIZE * (KENV_MNAMELEN +
|
||||
kenv_mvallen + 2);
|
||||
if (uap->len > 0 && uap->value != NULL)
|
||||
buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
|
||||
mtx_lock(&kenv_lock);
|
||||
for (i = 0; kenvp[i] != NULL; i++) {
|
||||
len = strlen(kenvp[i]) + 1;
|
||||
needed += len;
|
||||
len = min(len, buflen - done);
|
||||
/*
|
||||
* If called with a NULL or insufficiently large
|
||||
* buffer, just keep computing the required size.
|
||||
*/
|
||||
if (uap->value != NULL && buffer != NULL && len > 0) {
|
||||
bcopy(kenvp[i], buffer + done, len);
|
||||
done += len;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&kenv_lock);
|
||||
if (buffer != NULL) {
|
||||
error = copyout(buffer, uap->value, done);
|
||||
free(buffer, M_TEMP);
|
||||
}
|
||||
td->td_retval[0] = ((done == needed) ? 0 : needed);
|
||||
return (error);
|
||||
}
|
||||
|
||||
switch (uap->what) {
|
||||
return (kenv_dump(td, kenvp, uap->what, uap->value, uap->len));
|
||||
case KENV_DUMP_LOADER:
|
||||
case KENV_DUMP_STATIC:
|
||||
#ifdef MAC
|
||||
error = mac_kenv_check_dump(td->td_ucred);
|
||||
if (error)
|
||||
return (error);
|
||||
#endif
|
||||
#ifdef PRESERVE_EARLY_KENV
|
||||
return (kenv_dump(td,
|
||||
uap->what == KENV_DUMP_LOADER ? (char **)md_envp :
|
||||
(char **)kern_envp, uap->what, uap->value, uap->len));
|
||||
#else
|
||||
return (ENOENT);
|
||||
#endif
|
||||
case KENV_SET:
|
||||
error = priv_check(td, PRIV_KENV_SET);
|
||||
if (error)
|
||||
|
@ -34,10 +34,12 @@
|
||||
/*
|
||||
* Constants for the kenv(2) syscall
|
||||
*/
|
||||
#define KENV_GET 0
|
||||
#define KENV_SET 1
|
||||
#define KENV_UNSET 2
|
||||
#define KENV_DUMP 3
|
||||
#define KENV_GET 0
|
||||
#define KENV_SET 1
|
||||
#define KENV_UNSET 2
|
||||
#define KENV_DUMP 3
|
||||
#define KENV_DUMP_LOADER 4
|
||||
#define KENV_DUMP_STATIC 5
|
||||
|
||||
#define KENV_MNAMELEN 128 /* Maximum name length (for the syscall) */
|
||||
#define KENV_MVALLEN 128 /* Maximum value length (for the syscall) */
|
||||
|
Loading…
Reference in New Issue
Block a user