aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorPeter Pentchev <roam@FreeBSD.org>2001-06-29 16:55:24 +0000
committerPeter Pentchev <roam@FreeBSD.org>2001-06-29 16:55:24 +0000
commit48c8fe686321be4297ceab64de06479b256bc006 (patch)
treec8b8ed200bc3d04ae1f6feadbfba585a563d4601 /sbin
parenta15906e7aa3474d45ce8890358008ed0799e2f40 (diff)
downloadsrc-48c8fe686321be4297ceab64de06479b256bc006.tar.gz
src-48c8fe686321be4297ceab64de06479b256bc006.zip
Add kldconfig(8), a utility to modify the kernel module search path.
Reviewed by: -arch, -audit
Notes
Notes: svn path=/head/; revision=78977
Diffstat (limited to 'sbin')
-rw-r--r--sbin/kldconfig/Makefile34
-rw-r--r--sbin/kldconfig/kldconfig.8109
-rw-r--r--sbin/kldconfig/kldconfig.c444
3 files changed, 587 insertions, 0 deletions
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 <bsd.prog.mk>
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 <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);
+}