2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2001 Peter Pentchev
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/sysctl.h>
45 /* the default sysctl name */
46 #define PATHCTL "kern.module_path"
48 /* queue structure for the module path broken down into components */
49 TAILQ_HEAD(pathhead, pathentry);
52 TAILQ_ENTRY(pathentry) next;
55 /* the Management Information Base entries for the search path sysctl */
58 /* the sysctl name, defaults to PATHCTL */
60 /* the sysctl value - the current module search path */
62 /* flag whether user actions require changing the sysctl value */
65 /* Top-level path management functions */
66 static void addpath(struct pathhead *, char *, int, int);
67 static void rempath(struct pathhead *, char *, int, int);
68 static void showpath(struct pathhead *);
70 /* Low-level path management functions */
71 static char *qstring(struct pathhead *);
73 /* sysctl-related functions */
74 static void getmib(void);
75 static void getpath(void);
76 static void parsepath(struct pathhead *, char *, int);
77 static void setpath(struct pathhead *);
79 static void usage(void);
81 /* Get the MIB entry for our sysctl */
86 /* have we already fetched it? */
91 if (sysctlnametomib(pathctl, mib, &miblen) != 0)
92 err(1, "sysctlnametomib(%s)", pathctl);
95 /* Get the current module search path */
102 if (modpath != NULL) {
109 if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1)
110 err(1, "getting path: sysctl(%s) - size only", pathctl);
111 if ((path = malloc(sz + 1)) == NULL) {
113 err(1, "allocating %lu bytes for the path",
114 (unsigned long)sz+1);
116 if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
117 err(1, "getting path: sysctl(%s)", pathctl);
121 /* Set the module search path after changing it */
123 setpath(struct pathhead *pathq)
129 if ((newpath = qstring(pathq)) == NULL) {
131 err(1, "building path string");
133 if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
134 err(1, "setting path: sysctl(%s)", pathctl);
141 /* Add/insert a new component to the module search path */
143 addpath(struct pathhead *pathq, char *path, int force, int insert)
145 struct pathentry *pe, *pskip;
146 char pathbuf[MAXPATHLEN+1];
148 static unsigned added = 0;
152 * If the path exists, use it; otherwise, take the user-specified
153 * path at face value - may be a removed directory.
155 if (realpath(path, pathbuf) == NULL)
156 strlcpy(pathbuf, path, sizeof(pathbuf));
158 len = strlen(pathbuf);
159 /* remove a terminating slash if present */
160 if ((len > 0) && (pathbuf[len-1] == '/'))
161 pathbuf[--len] = '\0';
163 /* is it already in there? */
164 TAILQ_FOREACH(pe, pathq, next)
165 if (!strcmp(pe->path, pathbuf))
170 errx(1, "already in the module search path: %s", pathbuf);
173 /* OK, allocate and add it. */
174 if (((pe = malloc(sizeof(*pe))) == NULL) ||
175 ((pe->path = strdup(pathbuf)) == NULL)) {
177 err(1, "allocating path component");
180 TAILQ_INSERT_TAIL(pathq, pe, next);
182 for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++)
183 pskip = TAILQ_NEXT(pskip, next);
185 TAILQ_INSERT_BEFORE(pskip, pe, next);
187 TAILQ_INSERT_TAIL(pathq, pe, next);
193 /* Remove a path component from the module search path */
195 rempath(struct pathhead *pathq, char *path, int force, int insert __unused)
197 char pathbuf[MAXPATHLEN+1];
198 struct pathentry *pe;
201 /* same logic as in addpath() */
202 if (realpath(path, pathbuf) == NULL)
203 strlcpy(pathbuf, path, sizeof(pathbuf));
205 len = strlen(pathbuf);
206 /* remove a terminating slash if present */
207 if ((len > 0) && (pathbuf[len-1] == '/'))
208 pathbuf[--len] = '\0';
210 /* Is it in there? */
211 TAILQ_FOREACH(pe, pathq, next)
212 if (!strcmp(pe->path, pathbuf))
217 errx(1, "not in module search path: %s", pathbuf);
220 /* OK, remove it now.. */
221 TAILQ_REMOVE(pathq, pe, next);
225 /* Display the retrieved module search path */
227 showpath(struct pathhead *pathq)
231 if ((s = qstring(pathq)) == NULL) {
233 err(1, "building path string");
239 /* Break a string down into path components, store them into a queue */
241 parsepath(struct pathhead *pathq, char *path, int uniq)
244 struct pathentry *pe;
246 while ((p = strsep(&path, ";")) != NULL)
248 if (((pe = malloc(sizeof(*pe))) == NULL) ||
249 ((pe->path = strdup(p)) == NULL)) {
251 err(1, "allocating path element");
253 TAILQ_INSERT_TAIL(pathq, pe, next);
255 addpath(pathq, p, 1, 0);
259 /* Recreate a path string from a components queue */
261 qstring(struct pathhead *pathq)
264 struct pathentry *pe;
267 TAILQ_FOREACH(pe, pathq, next) {
268 asprintf(&p, "%s%s%s",
269 s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
284 fprintf(stderr, "%s\n%s\n",
285 "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path ...]",
292 main(int argc, char *argv[])
294 /* getopt() iterator */
296 /* iterator over argv[] path components */
298 /* Command-line flags: */
299 /* "-f" - no diagnostic messages */
301 /* "-i" - insert before the first element */
303 /* "-m" - merge into the existing path, do not replace it */
305 /* "-n" - do not actually set the new module path */
307 /* "-r" - print out the current search path */
309 /* "-U" - remove duplicate values from the path */
311 /* "-v" - verbose operation (currently a no-op) */
313 /* The higher-level function to call - add/remove */
314 void (*act)(struct pathhead *, char *, int, int);
315 /* The original path */
317 /* The module search path broken down into components */
318 struct pathhead pathq;
320 fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0;
323 if ((pathctl = strdup(PATHCTL)) == NULL) {
324 /* this is just too paranoid ;) */
326 err(1, "initializing sysctl name %s", PATHCTL);
329 /* If no arguments and no options are specified, force '-m' */
333 while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1)
361 if ((pathctl = strdup(optarg)) == NULL) {
363 err(1, "sysctl name %s", optarg);
379 /* The '-r' flag cannot be used when paths are also specified */
380 if (rflag && (argc > 0))
385 /* Retrieve and store the path from the sysctl value */
387 if ((origpath = strdup(modpath)) == NULL) {
389 err(1, "saving the original search path");
393 * Break down the path into the components queue if:
394 * - we are NOT adding paths, OR
395 * - the 'merge' flag is specified, OR
396 * - the 'print only' flag is specified, OR
397 * - the 'unique' flag is specified.
399 if ((act != addpath) || mflag || rflag || uniqflag)
400 parsepath(&pathq, modpath, uniqflag);
401 else if (modpath[0] != '\0')
404 /* Process the path arguments */
405 for (i = 0; i < argc; i++)
406 act(&pathq, argv[i], fflag, iflag);
408 if (changed && !nflag)
411 if (rflag || (changed && vflag)) {
412 if (changed && (vflag > 1))
413 printf("%s -> ", origpath);