From 48c8fe686321be4297ceab64de06479b256bc006 Mon Sep 17 00:00:00 2001 From: Peter Pentchev Date: Fri, 29 Jun 2001 16:55:24 +0000 Subject: [PATCH] Add kldconfig(8), a utility to modify the kernel module search path. Reviewed by: -arch, -audit --- sbin/kldconfig/Makefile | 34 +++ sbin/kldconfig/kldconfig.8 | 109 +++++++++ sbin/kldconfig/kldconfig.c | 444 +++++++++++++++++++++++++++++++++++++ 3 files changed, 587 insertions(+) create mode 100644 sbin/kldconfig/Makefile create mode 100644 sbin/kldconfig/kldconfig.8 create mode 100644 sbin/kldconfig/kldconfig.c diff --git a/sbin/kldconfig/Makefile b/sbin/kldconfig/Makefile new file mode 100644 index 000000000000..7455af9d318c --- /dev/null +++ b/sbin/kldconfig/Makefile @@ -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 diff --git a/sbin/kldconfig/kldconfig.8 b/sbin/kldconfig/kldconfig.8 new file mode 100644 index 000000000000..51ef4a13eea3 --- /dev/null +++ b/sbin/kldconfig/kldconfig.8 @@ -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 diff --git a/sbin/kldconfig/kldconfig.c b/sbin/kldconfig/kldconfig.c new file mode 100644 index 000000000000..979ff2e66f97 --- /dev/null +++ b/sbin/kldconfig/kldconfig.c @@ -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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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); +}