HAMMER Utilities: Update for HAMMER changes.
[dragonfly.git] / sbin / kldconfig / kldconfig.c
blob4d6494be5bded155b73ece1d66ace3790bcdfdeb
1 /*
2 * Copyright (c) 2001 Peter Pentchev
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
26 * $FreeBSD: src/sbin/kldconfig/kldconfig.c,v 1.3.2.1 2001/08/01 05:52:36 obrien Exp $
27 * $DragonFly: src/sbin/kldconfig/kldconfig.c,v 1.4 2007/11/25 18:10:07 swildner Exp $
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/queue.h>
33 #include <sys/sysctl.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
43 #if defined(__FreeBSD_version)
44 #if __FreeBSD_version < 500000
45 #define NEED_SLASHTERM
46 #endif /* < 500000 */
47 #else /* defined(__FreeBSD_version) */
48 /* just in case.. */
49 #define NEED_SLASHTERM
50 #endif /* defined(__FreeBSD_version) */
52 /* the default sysctl name */
53 #define PATHCTL "kern.module_path"
55 /* queue structure for the module path broken down into components */
56 TAILQ_HEAD(pathhead, pathentry);
57 struct pathentry {
58 char *path;
59 TAILQ_ENTRY(pathentry) next;
62 /* the Management Information Base entries for the search path sysctl */
63 static int mib[5];
64 static size_t miblen;
65 /* the sysctl name, defaults to PATHCTL */
66 static char *pathctl;
67 /* the sysctl value - the current module search path */
68 static char *modpath;
69 /* flag whether user actions require changing the sysctl value */
70 static int changed;
72 /* Top-level path management functions */
73 static void addpath(struct pathhead *, char *, int, int);
74 static void rempath(struct pathhead *, char *, int, int);
75 static void showpath(struct pathhead *);
77 /* Low-level path management functions */
78 static char *qstring(struct pathhead *);
80 /* sysctl-related functions */
81 static void getmib(void);
82 static void getpath(void);
83 static void parsepath(struct pathhead *, char *, int);
84 static void setpath(struct pathhead *);
86 static void usage(void);
88 /* Get the MIB entry for our sysctl */
89 static void
90 getmib(void)
93 /* have we already fetched it? */
94 if (miblen != 0)
95 return;
97 miblen = sizeof(mib) / sizeof(mib[0]);
98 if (sysctlnametomib(pathctl, mib, &miblen) != 0)
99 err(1, "sysctlnametomib(%s)", pathctl);
102 /* Get the current module search path */
103 static void
104 getpath(void)
106 char *path;
107 size_t sz;
109 if (modpath != NULL) {
110 free(modpath);
111 modpath = NULL;
114 if (miblen == 0)
115 getmib();
116 if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1)
117 err(1, "getting path: sysctl(%s) - size only", pathctl);
118 if ((path = malloc(sz + 1)) == NULL) {
119 errno = ENOMEM;
120 err(1, "allocating %lu bytes for the path",
121 (unsigned long)sz+1);
123 if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
124 err(1, "getting path: sysctl(%s)", pathctl);
125 modpath = path;
128 /* Set the module search path after changing it */
129 static void
130 setpath(struct pathhead *pathq)
132 char *newpath;
134 if (miblen == 0)
135 getmib();
136 if ((newpath = qstring(pathq)) == NULL) {
137 errno = ENOMEM;
138 err(1, "building path string");
140 if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
141 err(1, "setting path: sysctl(%s)", pathctl);
143 if (modpath)
144 free(modpath);
145 modpath = newpath;
148 /* Add/insert a new component to the module search path */
149 static void
150 addpath(struct pathhead *pathq, char *path, int force, int insert)
152 struct pathentry *pe, *pskip;
153 char pathbuf[MAXPATHLEN+1];
154 size_t len;
155 static unsigned added = 0;
156 unsigned i;
159 * If the path exists, use it; otherwise, take the user-specified
160 * path at face value - may be a removed directory.
162 if (realpath(path, pathbuf) == NULL)
163 strlcpy(pathbuf, path, sizeof(pathbuf));
165 len = strlen(pathbuf);
166 #ifdef NEED_SLASHTERM
167 /* slash-terminate, because the kernel linker said so. */
168 if ((len == 0) || (pathbuf[len-1] != '/')) {
169 if (len == sizeof(pathbuf) - 1)
170 errx(1, "path too long: %s", pathbuf);
171 pathbuf[len] = '/';
173 #else /* NEED_SLASHTERM */
174 /* remove a terminating slash if present */
175 if ((len > 0) && (pathbuf[len-1] == '/'))
176 pathbuf[--len] = '\0';
177 #endif /* NEED_SLASHTERM */
179 /* is it already in there? */
180 TAILQ_FOREACH(pe, pathq, next)
181 if (!strcmp(pe->path, pathbuf))
182 break;
183 if (pe != NULL) {
184 if (force)
185 return;
186 errx(1, "already in the module search path: %s", pathbuf);
189 /* OK, allocate and add it. */
190 if (((pe = malloc(sizeof(*pe))) == NULL) ||
191 ((pe->path = strdup(pathbuf)) == NULL)) {
192 errno = ENOMEM;
193 err(1, "allocating path component");
195 if (!insert) {
196 TAILQ_INSERT_TAIL(pathq, pe, next);
197 } else {
198 for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++)
199 pskip = TAILQ_NEXT(pskip, next);
200 if (pskip != NULL)
201 TAILQ_INSERT_BEFORE(pskip, pe, next);
202 else
203 TAILQ_INSERT_TAIL(pathq, pe, next);
204 added++;
206 changed = 1;
209 /* Remove a path component from the module search path */
210 static void
211 rempath(struct pathhead *pathq, char *path, int force, int insert __unused)
213 char pathbuf[MAXPATHLEN+1];
214 struct pathentry *pe;
215 size_t len;
217 /* same logic as in addpath() */
218 if (realpath(path, pathbuf) == NULL)
219 strlcpy(pathbuf, path, sizeof(pathbuf));
221 len = strlen(pathbuf);
222 #ifdef NEED_SLASHTERM
223 /* slash-terminate, because the kernel linker said so. */
224 if ((len == 0) || (pathbuf[len-1] != '/')) {
225 if (len == sizeof(pathbuf) - 1)
226 errx(1, "path too long: %s", pathbuf);
227 pathbuf[len] = '/';
229 #else /* NEED_SLASHTERM */
230 /* remove a terminating slash if present */
231 if ((len > 0) && (pathbuf[len-1] == '/'))
232 pathbuf[--len] = '\0';
233 #endif /* NEED_SLASHTERM */
235 /* Is it in there? */
236 TAILQ_FOREACH(pe, pathq, next)
237 if (!strcmp(pe->path, pathbuf))
238 break;
239 if (pe == NULL) {
240 if (force)
241 return;
242 errx(1, "not in module search path: %s", pathbuf);
245 /* OK, remove it now.. */
246 TAILQ_REMOVE(pathq, pe, next);
247 changed = 1;
250 /* Display the retrieved module search path */
251 static void
252 showpath(struct pathhead *pathq)
254 char *s;
256 if ((s = qstring(pathq)) == NULL) {
257 errno = ENOMEM;
258 err(1, "building path string");
260 printf("%s\n", s);
261 free(s);
264 /* Break a string down into path components, store them into a queue */
265 static void
266 parsepath(struct pathhead *pathq, char *path, int uniq)
268 char *p;
269 struct pathentry *pe;
271 while ((p = strsep(&path, ";")) != NULL)
272 if (!uniq) {
273 if (((pe = malloc(sizeof(*pe))) == NULL) ||
274 ((pe->path = strdup(p)) == NULL)) {
275 errno = ENOMEM;
276 err(1, "allocating path element");
278 TAILQ_INSERT_TAIL(pathq, pe, next);
279 } else {
280 addpath(pathq, p, 1, 0);
284 /* Recreate a path string from a components queue */
285 static char *
286 qstring(struct pathhead *pathq)
288 char *s, *p;
289 struct pathentry *pe;
291 s = strdup("");
292 TAILQ_FOREACH(pe, pathq, next) {
293 asprintf(&p, "%s%s%s",
294 s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
295 free(s);
296 if (p == NULL)
297 return (NULL);
298 s = p;
301 return (s);
304 /* Usage message */
305 static void
306 usage(void)
309 fprintf(stderr, "%s\n%s\n",
310 "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path..]",
311 "\tkldconfig -r");
312 exit(1);
315 /* Main function */
317 main(int argc, char *argv[])
319 /* getopt() iterator */
320 int c;
321 /* iterator over argv[] path components */
322 int i;
323 /* Command-line flags: */
324 /* "-f" - no diagnostic messages */
325 int fflag;
326 /* "-i" - insert before the first element */
327 int iflag;
328 /* "-m" - merge into the existing path, do not replace it */
329 int mflag;
330 /* "-n" - do not actually set the new module path */
331 int nflag;
332 /* "-r" - print out the current search path */
333 int rflag;
334 /* "-U" - remove duplicate values from the path */
335 int uniqflag;
336 /* "-v" - verbose operation (currently a no-op) */
337 int vflag;
338 /* The higher-level function to call - add/remove */
339 void (*act)(struct pathhead *, char *, int, int);
340 /* The original path */
341 char *origpath;
342 /* The module search path broken down into components */
343 struct pathhead pathq;
345 fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0;
346 act = addpath;
347 origpath = NULL;
348 if ((pathctl = strdup(PATHCTL)) == NULL) {
349 /* this is just too paranoid ;) */
350 errno = ENOMEM;
351 err(1, "initializing sysctl name %s", PATHCTL);
354 /* If no arguments and no options are specified, force '-m' */
355 if (argc == 1)
356 mflag = 1;
358 while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1)
359 switch (c) {
360 case 'd':
361 if (iflag || mflag)
362 usage();
363 act = rempath;
364 break;
365 case 'f':
366 fflag = 1;
367 break;
368 case 'i':
369 if (act != addpath)
370 usage();
371 iflag = 1;
372 break;
373 case 'm':
374 if (act != addpath)
375 usage();
376 mflag = 1;
377 break;
378 case 'n':
379 nflag = 1;
380 break;
381 case 'r':
382 rflag = 1;
383 break;
384 case 'S':
385 free(pathctl);
386 if ((pathctl = strdup(optarg)) == NULL) {
387 errno = ENOMEM;
388 err(1, "sysctl name %s", optarg);
390 break;
391 case 'U':
392 uniqflag = 1;
393 break;
394 case 'v':
395 vflag++;
396 break;
397 default:
398 usage();
401 argc -= optind;
402 argv += optind;
404 /* The '-r' flag cannot be used when paths are also specified */
405 if (rflag && (argc > 0))
406 usage();
408 TAILQ_INIT(&pathq);
410 /* Retrieve and store the path from the sysctl value */
411 getpath();
412 if ((origpath = strdup(modpath)) == NULL) {
413 errno = ENOMEM;
414 err(1, "saving the original search path");
418 * Break down the path into the components queue if:
419 * - we are NOT adding paths, OR
420 * - the 'merge' flag is specified, OR
421 * - the 'print only' flag is specified, OR
422 * - the 'unique' flag is specified.
424 if ((act != addpath) || mflag || rflag || uniqflag)
425 parsepath(&pathq, modpath, uniqflag);
426 else if (modpath[0] != '\0')
427 changed = 1;
429 /* Process the path arguments */
430 for (i = 0; i < argc; i++)
431 act(&pathq, argv[i], fflag, iflag);
433 if (changed && !nflag)
434 setpath(&pathq);
436 if (rflag || (changed && vflag)) {
437 if (changed && (vflag > 1))
438 printf("%s -> ", origpath);
439 showpath(&pathq);
442 return (0);