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
|
.Nd list or modify the kernel environment
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
|
.Op Fl l | s
|
||||||
.Op Fl hNq
|
.Op Fl hNq
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl qv
|
.Op Fl qv
|
||||||
@ -45,6 +46,23 @@ The
|
|||||||
.Nm
|
.Nm
|
||||||
utility will list all variables in the kernel environment if
|
utility will list all variables in the kernel environment if
|
||||||
invoked without arguments.
|
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
|
If the
|
||||||
.Fl h
|
.Fl h
|
||||||
option is specified, it will limit the report to kernel probe hints.
|
option is specified, it will limit the report to kernel probe hints.
|
||||||
|
@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <kenv.h>
|
#include <kenv.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -36,14 +37,16 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static void usage(void);
|
static void usage(void);
|
||||||
static int kdumpenv(void);
|
static int kdumpenv(int dump_type);
|
||||||
static int kgetenv(const char *);
|
static int kgetenv(const char *);
|
||||||
static int ksetenv(const char *, char *);
|
static int ksetenv(const char *, char *);
|
||||||
static int kunsetenv(const char *);
|
static int kunsetenv(const char *);
|
||||||
|
|
||||||
static int hflag = 0;
|
static int hflag = 0;
|
||||||
|
static int lflag = 0;
|
||||||
static int Nflag = 0;
|
static int Nflag = 0;
|
||||||
static int qflag = 0;
|
static int qflag = 0;
|
||||||
|
static int sflag = 0;
|
||||||
static int uflag = 0;
|
static int uflag = 0;
|
||||||
static int vflag = 0;
|
static int vflag = 0;
|
||||||
|
|
||||||
@ -51,7 +54,7 @@ static void
|
|||||||
usage(void)
|
usage(void)
|
||||||
{
|
{
|
||||||
(void)fprintf(stderr, "%s\n%s\n%s\n",
|
(void)fprintf(stderr, "%s\n%s\n%s\n",
|
||||||
"usage: kenv [-hNq]",
|
"usage: kenv [-l|-s] [-hNq]",
|
||||||
" kenv [-qv] variable[=value]",
|
" kenv [-qv] variable[=value]",
|
||||||
" kenv [-q] -u variable");
|
" kenv [-q] -u variable");
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -65,17 +68,23 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
val = NULL;
|
val = NULL;
|
||||||
env = NULL;
|
env = NULL;
|
||||||
while ((ch = getopt(argc, argv, "hNquv")) != -1) {
|
while ((ch = getopt(argc, argv, "hlNqsuv")) != -1) {
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'h':
|
case 'h':
|
||||||
hflag++;
|
hflag++;
|
||||||
break;
|
break;
|
||||||
|
case 'l':
|
||||||
|
lflag++;
|
||||||
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
Nflag++;
|
Nflag++;
|
||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
qflag++;
|
qflag++;
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
sflag++;
|
||||||
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
uflag++;
|
uflag++;
|
||||||
break;
|
break;
|
||||||
@ -100,12 +109,23 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
if ((hflag || Nflag) && env != NULL)
|
if ((hflag || Nflag) && env != NULL)
|
||||||
usage();
|
usage();
|
||||||
|
if (lflag && sflag)
|
||||||
|
usage();
|
||||||
if (argc > 0 || ((uflag || vflag) && env == NULL))
|
if (argc > 0 || ((uflag || vflag) && env == NULL))
|
||||||
usage();
|
usage();
|
||||||
if (env == NULL) {
|
if (env == NULL) {
|
||||||
error = kdumpenv();
|
if (lflag)
|
||||||
if (error && !qflag)
|
error = kdumpenv(KENV_DUMP_LOADER);
|
||||||
warn("kdumpenv");
|
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) {
|
} else if (val == NULL) {
|
||||||
if (uflag) {
|
if (uflag) {
|
||||||
error = kunsetenv(env);
|
error = kunsetenv(env);
|
||||||
@ -125,12 +145,12 @@ main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
kdumpenv(void)
|
kdumpenv(int dump_type)
|
||||||
{
|
{
|
||||||
char *buf, *bp, *cp;
|
char *buf, *bp, *cp;
|
||||||
int buflen, envlen;
|
int buflen, envlen;
|
||||||
|
|
||||||
envlen = kenv(KENV_DUMP, NULL, NULL, 0);
|
envlen = kenv(dump_type, NULL, NULL, 0);
|
||||||
if (envlen < 0)
|
if (envlen < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -138,7 +158,7 @@ kdumpenv(void)
|
|||||||
buf = calloc(1, buflen + 1);
|
buf = calloc(1, buflen + 1);
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
envlen = kenv(KENV_DUMP, NULL, buf, buflen);
|
envlen = kenv(dump_type, NULL, buf, buflen);
|
||||||
if (envlen < 0) {
|
if (envlen < 0) {
|
||||||
free(buf);
|
free(buf);
|
||||||
return (-1);
|
return (-1);
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 20, 2017
|
.Dd June 20, 2021
|
||||||
.Dt KENV 2
|
.Dt KENV 2
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -49,7 +49,7 @@ the kernel environment.
|
|||||||
The
|
The
|
||||||
.Fa action
|
.Fa action
|
||||||
argument can be one of the following:
|
argument can be one of the following:
|
||||||
.Bl -tag -width ".Dv KENV_UNSET"
|
.Bl -tag -width ".Dv KENV_DUMP_LOADER"
|
||||||
.It Dv KENV_GET
|
.It Dv KENV_GET
|
||||||
Get the
|
Get the
|
||||||
.Fa value
|
.Fa value
|
||||||
@ -90,7 +90,7 @@ and
|
|||||||
arguments are ignored.
|
arguments are ignored.
|
||||||
This option is only available to the superuser.
|
This option is only available to the superuser.
|
||||||
.It Dv KENV_DUMP
|
.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 ,
|
.Fa value ,
|
||||||
whose size is given in
|
whose size is given in
|
||||||
.Fa len .
|
.Fa len .
|
||||||
@ -103,6 +103,18 @@ will return the number of bytes required to copy out the entire environment.
|
|||||||
The
|
The
|
||||||
.Fa name
|
.Fa name
|
||||||
is ignored.
|
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
|
.El
|
||||||
.Sh RETURN VALUES
|
.Sh RETURN VALUES
|
||||||
The
|
The
|
||||||
@ -142,6 +154,12 @@ for a
|
|||||||
.Dv KENV_GET
|
.Dv KENV_GET
|
||||||
or
|
or
|
||||||
.Dv KENV_UNSET .
|
.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
|
.It Bq Er EPERM
|
||||||
A user other than the superuser attempted to set or unset a kernel
|
A user other than the superuser attempted to set or unset a kernel
|
||||||
environment variable.
|
environment variable.
|
||||||
|
@ -92,60 +92,103 @@ bool dynamic_kenv;
|
|||||||
#define KENV_CHECK if (!dynamic_kenv) \
|
#define KENV_CHECK if (!dynamic_kenv) \
|
||||||
panic("%s: called before SI_SUB_KMEM", __func__)
|
panic("%s: called before SI_SUB_KMEM", __func__)
|
||||||
|
|
||||||
int
|
static int
|
||||||
sys_kenv(td, uap)
|
kenv_dump(struct thread *td, char **envp, int what, char *value, int len)
|
||||||
struct thread *td;
|
|
||||||
struct kenv_args /* {
|
|
||||||
int what;
|
|
||||||
const char *name;
|
|
||||||
char *value;
|
|
||||||
int len;
|
|
||||||
} */ *uap;
|
|
||||||
{
|
{
|
||||||
char *name, *value, *buffer = NULL;
|
char *buffer, *senv;
|
||||||
size_t len, done, needed, buflen;
|
size_t done, needed, buflen;
|
||||||
int error, i;
|
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"));
|
KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = false"));
|
||||||
|
|
||||||
error = 0;
|
error = 0;
|
||||||
if (uap->what == KENV_DUMP) {
|
|
||||||
|
switch (uap->what) {
|
||||||
|
case KENV_DUMP:
|
||||||
#ifdef MAC
|
#ifdef MAC
|
||||||
error = mac_kenv_check_dump(td->td_ucred);
|
error = mac_kenv_check_dump(td->td_ucred);
|
||||||
if (error)
|
if (error)
|
||||||
return (error);
|
return (error);
|
||||||
#endif
|
#endif
|
||||||
done = needed = 0;
|
return (kenv_dump(td, kenvp, uap->what, uap->value, uap->len));
|
||||||
buflen = uap->len;
|
case KENV_DUMP_LOADER:
|
||||||
if (buflen > KENV_SIZE * (KENV_MNAMELEN + kenv_mvallen + 2))
|
case KENV_DUMP_STATIC:
|
||||||
buflen = KENV_SIZE * (KENV_MNAMELEN +
|
#ifdef MAC
|
||||||
kenv_mvallen + 2);
|
error = mac_kenv_check_dump(td->td_ucred);
|
||||||
if (uap->len > 0 && uap->value != NULL)
|
if (error)
|
||||||
buffer = malloc(buflen, M_TEMP, M_WAITOK|M_ZERO);
|
return (error);
|
||||||
mtx_lock(&kenv_lock);
|
#endif
|
||||||
for (i = 0; kenvp[i] != NULL; i++) {
|
#ifdef PRESERVE_EARLY_KENV
|
||||||
len = strlen(kenvp[i]) + 1;
|
return (kenv_dump(td,
|
||||||
needed += len;
|
uap->what == KENV_DUMP_LOADER ? (char **)md_envp :
|
||||||
len = min(len, buflen - done);
|
(char **)kern_envp, uap->what, uap->value, uap->len));
|
||||||
/*
|
#else
|
||||||
* If called with a NULL or insufficiently large
|
return (ENOENT);
|
||||||
* buffer, just keep computing the required size.
|
#endif
|
||||||
*/
|
|
||||||
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) {
|
|
||||||
case KENV_SET:
|
case KENV_SET:
|
||||||
error = priv_check(td, PRIV_KENV_SET);
|
error = priv_check(td, PRIV_KENV_SET);
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -34,10 +34,12 @@
|
|||||||
/*
|
/*
|
||||||
* Constants for the kenv(2) syscall
|
* Constants for the kenv(2) syscall
|
||||||
*/
|
*/
|
||||||
#define KENV_GET 0
|
#define KENV_GET 0
|
||||||
#define KENV_SET 1
|
#define KENV_SET 1
|
||||||
#define KENV_UNSET 2
|
#define KENV_UNSET 2
|
||||||
#define KENV_DUMP 3
|
#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_MNAMELEN 128 /* Maximum name length (for the syscall) */
|
||||||
#define KENV_MVALLEN 128 /* Maximum value length (for the syscall) */
|
#define KENV_MVALLEN 128 /* Maximum value length (for the syscall) */
|
||||||
|
Loading…
Reference in New Issue
Block a user