Add kldconfig(8), a utility to modify the kernel module search path.

Reviewed by:	-arch, -audit
This commit is contained in:
Peter Pentchev 2001-06-29 16:55:24 +00:00
parent a15906e7aa
commit 48c8fe6863
3 changed files with 587 additions and 0 deletions

34
sbin/kldconfig/Makefile Normal file
View File

@ -0,0 +1,34 @@
#
# Copyright (c) 2001 Peter Pentchev
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
PROG= kldconfig
MAN= kldconfig.8
WARNS?= 2
.include <bsd.prog.mk>

109
sbin/kldconfig/kldconfig.8 Normal file
View File

@ -0,0 +1,109 @@
.\"
.\" Copyright (c) 2001 Peter Pentchev
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd June 15, 2001
.Dt KLDCONFIG 8
.Os
.Sh NAME
.Nm kldconfig
.Nd display or modify the kernel module search path
.Sh SYNOPSIS
.Nm
.Op Fl dfimnUv
.Op Fl S Ar name
.Op Ar path ...
.Nm
.Fl r
.Sh DESCRIPTION
.Nm
displays or modifies the search path used by the kernel when loading modules
using the
.Xr kldload 8
utility or the
.Xr kldload 2
syscall.
.Pp
The following options are available:
.Bl -tag -width indent
.It Fl d
Remove the specified paths from the module search path.
.It Fl f
Do not display a diagnostic message if a path specified for adding is
already present in the search path, or if a path specified for removing
is not present in the search path.
This may be useful in startup/shutdown scripts for adding a path to
a filesystem which is still not mounted, or in shutdown scripts for
unconditionally removing a path that may have been added during startup.
.It Fl i
Add the specified paths to the beginning of the search path, not to the end.
This option can only be used when adding paths.
.It Fl m
Instead of replacing the module search path with the set of paths
specified,
.Dq merge
in the new entries.
.It Fl n
Do not actually change the module search path.
.It Fl r
Display the current search path.
This option cannot be used if any paths are also specified.
.It Fl S Ar name
Specify the sysctl name to use instead of the default
.Va kern.module_path .
.It Fl U
.Dq Unique-ify
the current search path - if any of the directories is repeated one
or more times, only the first occurrence remains.
This option implies
.Fl m .
.It Fl v
Verbose output: display the new module search path.
If the path has been changed, and the
.Fl v
flag is specified more than once, the old path is displayed as well.
.El
.Sh FILES
.Bl -tag -width indent
.It Pa /boot/kernel /boot/modules /modules
The default module search path used by the kernel.
.El
.Sh DIAGNOSTICS
The
.Nm
utility exits with a status of 0 on success
and with a nonzero status if an error occurs.
.Sh SEE ALSO
.Xr kldload 2 ,
.Xr kldload 8 ,
.Xr sysctl 8
.Sh HISTORY
The
.Nm
utility first appeared in
.Fx 5.0 .
.Sh AUTHORS
.An Peter Pentchev Aq roam@FreeBSD.org

444
sbin/kldconfig/kldconfig.c Normal file
View File

@ -0,0 +1,444 @@
/*
* Copyright (c) 2001 Peter Pentchev
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if defined(__FreeBSD_version)
#if __FreeBSD_version < 500000
#define NEED_SLASHTERM
#endif /* < 500000 */
#else /* defined(__FreeBSD_version) */
/* just in case.. */
#define NEED_SLASHTERM
#endif /* defined(__FreeBSD_version) */
/* the default sysctl name */
#define PATHCTL "kern.module_path"
/* queue structure for the module path broken down into components */
TAILQ_HEAD(pathhead, pathentry);
struct pathentry {
char *path;
TAILQ_ENTRY(pathentry) next;
};
/* the Management Information Base entries for the search path sysctl */
static int mib[5];
static size_t miblen;
/* the sysctl name, defaults to PATHCTL */
static char *pathctl;
/* the sysctl value - the current module search path */
static char *modpath;
/* flag whether user actions require changing the sysctl value */
static int changed;
/* Top-level path management functions */
static void addpath(struct pathhead *, char *, int, int);
static void rempath(struct pathhead *, char *, int, int);
static void showpath(struct pathhead *);
/* Low-level path management functions */
static char *qstring(struct pathhead *);
/* sysctl-related functions */
static void getmib(void);
static void getpath(void);
static void parsepath(struct pathhead *, char *, int);
static void setpath(struct pathhead *);
static void usage(void);
/* Get the MIB entry for our sysctl */
static void
getmib(void)
{
/* have we already fetched it? */
if (miblen != 0)
return;
miblen = sizeof(mib) / sizeof(mib[0]);
if (sysctlnametomib(pathctl, mib, &miblen) != 0)
err(1, "sysctlnametomib(%s)", pathctl);
}
/* Get the current module search path */
static void
getpath(void)
{
char *path;
size_t sz;
if (modpath != NULL) {
free(modpath);
modpath = NULL;
}
if (miblen == 0)
getmib();
if (sysctl(mib, miblen, NULL, &sz, NULL, NULL) == -1)
err(1, "getting path: sysctl(%s) - size only", pathctl);
if ((path = malloc(sz + 1)) == NULL) {
errno = ENOMEM;
err(1, "allocating %u bytes for the path", sz+1);
}
if (sysctl(mib, miblen, path, &sz, NULL, NULL) == -1)
err(1, "getting path: sysctl(%s)", pathctl);
modpath = path;
}
/* Set the module search path after changing it */
static void
setpath(struct pathhead *pathq)
{
char *newpath;
if (miblen == 0)
getmib();
if ((newpath = qstring(pathq)) == NULL) {
errno = ENOMEM;
err(1, "building path string");
}
if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
err(1, "setting path: sysctl(%s)", pathctl);
if (modpath)
free(modpath);
modpath = newpath;
}
/* Add/insert a new component to the module search path */
static void
addpath(struct pathhead *pathq, char *path, int force, int insert)
{
struct pathentry *pe, *pskip;
char pathbuf[MAXPATHLEN+1];
size_t len;
static unsigned added = 0;
unsigned i;
/*
* If the path exists, use it; otherwise, take the user-specified
* path at face value - may be a removed directory.
*/
if (realpath(path, pathbuf) == NULL)
strlcpy(pathbuf, path, sizeof(pathbuf));
len = strlen(pathbuf);
#ifdef NEED_SLASHTERM
/* slash-terminate, because the kernel linker said so. */
if ((len == 0) || (pathbuf[len-1] != '/')) {
if (len == sizeof(pathbuf) - 1)
errx(1, "path too long: %s", pathbuf);
pathbuf[len] = '/';
}
#else /* NEED_SLASHTERM */
/* remove a terminating slash if present */
if ((len > 0) && (pathbuf[len-1] == '/'))
pathbuf[--len] = '\0';
#endif /* NEED_SLASHTERM */
/* is it already in there? */
TAILQ_FOREACH(pe, pathq, next)
if (!strcmp(pe->path, pathbuf))
break;
if (pe != NULL) {
if (force)
return;
errx(1, "already in the module search path: %s", pathbuf);
}
/* OK, allocate and add it. */
if (((pe = malloc(sizeof(*pe))) == NULL) ||
((pe->path = strdup(pathbuf)) == NULL)) {
errno = ENOMEM;
err(1, "allocating path component");
}
if (!insert) {
TAILQ_INSERT_TAIL(pathq, pe, next);
} else {
for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++)
pskip = TAILQ_NEXT(pskip, next);
if (pskip != NULL)
TAILQ_INSERT_BEFORE(pskip, pe, next);
else
TAILQ_INSERT_TAIL(pathq, pe, next);
added++;
}
changed = 1;
}
/* Remove a path component from the module search path */
static void
rempath(struct pathhead *pathq, char *path, int force, int insert __unused)
{
char pathbuf[MAXPATHLEN+1];
struct pathentry *pe;
size_t len;
/* same logic as in addpath() */
if (realpath(path, pathbuf) == NULL)
strlcpy(pathbuf, path, sizeof(pathbuf));
len = strlen(pathbuf);
#ifdef NEED_SLASHTERM
/* slash-terminate, because the kernel linker said so. */
if ((len == 0) || (pathbuf[len-1] != '/')) {
if (len == sizeof(pathbuf) - 1)
errx(1, "path too long: %s", pathbuf);
pathbuf[len] = '/';
}
#else /* NEED_SLASHTERM */
/* remove a terminating slash if present */
if ((len > 0) && (pathbuf[len-1] == '/'))
pathbuf[--len] = '\0';
#endif /* NEED_SLASHTERM */
/* Is it in there? */
TAILQ_FOREACH(pe, pathq, next)
if (!strcmp(pe->path, pathbuf))
break;
if (pe == NULL) {
if (force)
return;
errx(1, "not in module search path: %s", pathbuf);
}
/* OK, remove it now.. */
TAILQ_REMOVE(pathq, pe, next);
changed = 1;
}
/* Display the retrieved module search path */
static void
showpath(struct pathhead *pathq)
{
char *s;
if ((s = qstring(pathq)) == NULL) {
errno = ENOMEM;
err(1, "building path string");
}
printf("%s\n", s);
free(s);
}
/* Break a string down into path components, store them into a queue */
static void
parsepath(struct pathhead *pathq, char *path, int uniq)
{
char *p;
struct pathentry *pe;
while ((p = strsep(&path, ";")) != NULL)
if (!uniq) {
if (((pe = malloc(sizeof(pe))) == NULL) ||
((pe->path = strdup(p)) == NULL)) {
errno = ENOMEM;
err(1, "allocating path element");
}
TAILQ_INSERT_TAIL(pathq, pe, next);
} else {
addpath(pathq, p, 1, 0);
}
}
/* Recreate a path string from a components queue */
static char *
qstring(struct pathhead *pathq)
{
char *s, *p;
struct pathentry *pe;
s = strdup("");
TAILQ_FOREACH(pe, pathq, next) {
asprintf(&p, "%s%s%s",
s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
free(s);
if (p == NULL)
return (NULL);
s = p;
}
return (s);
}
/* Usage message */
static void
usage(void)
{
fprintf(stderr, "%s\n%s\n",
"usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path..]",
"\tkldconfig -r");
exit(1);
}
/* Main function */
int
main(int argc, char *argv[])
{
/* getopt() iterator */
int c;
/* iterator over argv[] path components */
int i;
/* Command-line flags: */
/* "-f" - no diagnostic messages */
int fflag;
/* "-i" - insert before the first element */
int iflag;
/* "-m" - merge into the existing path, do not replace it */
int mflag;
/* "-n" - do not actually set the new module path */
int nflag;
/* "-r" - print out the current search path */
int rflag;
/* "-U" - remove duplicate values from the path */
int uniqflag;
/* "-v" - verbose operation (currently a no-op) */
int vflag;
/* The higher-level function to call - add/remove */
void (*act)(struct pathhead *, char *, int, int);
/* The original path */
char *origpath;
/* The module search path broken down into components */
struct pathhead pathq;
fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0;
act = addpath;
origpath = NULL;
if ((pathctl = strdup(PATHCTL)) == NULL) {
/* this is just too paranoid ;) */
errno = ENOMEM;
err(1, "initializing sysctl name %s", PATHCTL);
}
/* If no arguments and no options are specified, force '-m' */
if (argc == 1)
mflag = 1;
while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1)
switch (c) {
case 'd':
if (iflag || mflag)
usage();
act = rempath;
break;
case 'f':
fflag = 1;
break;
case 'i':
if (act != addpath)
usage();
iflag = 1;
break;
case 'm':
if (act != addpath)
usage();
mflag = 1;
break;
case 'n':
nflag = 1;
break;
case 'r':
rflag = 1;
break;
case 'S':
free(pathctl);
if ((pathctl = strdup(optarg)) == NULL) {
errno = ENOMEM;
err(1, "sysctl name %s", optarg);
}
break;
case 'U':
uniqflag = 1;
break;
case 'v':
vflag++;
break;
default:
usage();
}
argc -= optind;
argv += optind;
/* The '-r' flag cannot be used when paths are also specified */
if (rflag && (argc > 0))
usage();
TAILQ_INIT(&pathq);
/* Retrieve and store the path from the sysctl value */
getpath();
if ((origpath = strdup(modpath)) == NULL) {
errno = ENOMEM;
err(1, "saving the original search path");
}
/*
* Break down the path into the components queue if:
* - we are NOT adding paths, OR
* - the 'merge' flag is specified, OR
* - the 'print only' flag is specified, OR
* - the 'unique' flag is specified.
*/
if ((act != addpath) || mflag || rflag || uniqflag)
parsepath(&pathq, modpath, uniqflag);
else if (modpath[0] != '\0')
changed = 1;
/* Process the path arguments */
for (i = 0; i < argc; i++)
act(&pathq, argv[i], fflag, iflag);
if (changed && !nflag)
setpath(&pathq);
if (rflag || (changed && vflag)) {
if (changed && (vflag > 1))
printf("%s -> ", origpath);
showpath(&pathq);
}
return (0);
}