xref: /freebsd/sbin/kldconfig/kldconfig.c (revision e4e9813eb92cd7c4d4b819a8fbed5cbd3d92f5d8)
1 /*
2  * Copyright (c) 2001 Peter Pentchev
3  * All rights reserved.
4  *
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.
13  *
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.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/queue.h>
33 #include <sys/sysctl.h>
34 
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>
42 
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) */
51 
52 /* the default sysctl name */
53 #define PATHCTL	"kern.module_path"
54 
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;
60 };
61 
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;
71 
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 *);
76 
77 /* Low-level path management functions */
78 static char	*qstring(struct pathhead *);
79 
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 *);
85 
86 static void	 usage(void);
87 
88 /* Get the MIB entry for our sysctl */
89 static void
90 getmib(void)
91 {
92 
93 	/* have we already fetched it? */
94 	if (miblen != 0)
95 		return;
96 
97 	miblen = sizeof(mib) / sizeof(mib[0]);
98 	if (sysctlnametomib(pathctl, mib, &miblen) != 0)
99 		err(1, "sysctlnametomib(%s)", pathctl);
100 }
101 
102 /* Get the current module search path */
103 static void
104 getpath(void)
105 {
106 	char *path;
107 	size_t sz;
108 
109 	if (modpath != NULL) {
110 		free(modpath);
111 		modpath = NULL;
112 	}
113 
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);
122 	}
123 	if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
124 		err(1, "getting path: sysctl(%s)", pathctl);
125 	modpath = path;
126 }
127 
128 /* Set the module search path after changing it */
129 static void
130 setpath(struct pathhead *pathq)
131 {
132 	char *newpath;
133 
134 	if (miblen == 0)
135 		getmib();
136 	if ((newpath = qstring(pathq)) == NULL) {
137 		errno = ENOMEM;
138 		err(1, "building path string");
139 	}
140 	if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
141 		err(1, "setting path: sysctl(%s)", pathctl);
142 
143 	if (modpath != NULL)
144 		free(modpath);
145 	modpath = newpath;
146 }
147 
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)
151 {
152 	struct pathentry *pe, *pskip;
153 	char pathbuf[MAXPATHLEN+1];
154 	size_t len;
155 	static unsigned added = 0;
156 	unsigned i;
157 
158 	/*
159 	 * If the path exists, use it; otherwise, take the user-specified
160 	 * path at face value - may be a removed directory.
161 	 */
162 	if (realpath(path, pathbuf) == NULL)
163 		strlcpy(pathbuf, path, sizeof(pathbuf));
164 
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] = '/';
172 	}
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 */
178 
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);
187 	}
188 
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");
194 	}
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++;
205 	}
206 	changed = 1;
207 }
208 
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)
212 {
213 	char pathbuf[MAXPATHLEN+1];
214 	struct pathentry *pe;
215 	size_t len;
216 
217 	/* same logic as in addpath() */
218 	if (realpath(path, pathbuf) == NULL)
219 		strlcpy(pathbuf, path, sizeof(pathbuf));
220 
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] = '/';
228 	}
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 */
234 
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);
243 	}
244 
245 	/* OK, remove it now.. */
246 	TAILQ_REMOVE(pathq, pe, next);
247 	changed = 1;
248 }
249 
250 /* Display the retrieved module search path */
251 static void
252 showpath(struct pathhead *pathq)
253 {
254 	char *s;
255 
256 	if ((s = qstring(pathq)) == NULL) {
257 		errno = ENOMEM;
258 		err(1, "building path string");
259 	}
260 	printf("%s\n", s);
261 	free(s);
262 }
263 
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)
267 {
268 	char *p;
269 	struct pathentry *pe;
270 
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");
277 			}
278 			TAILQ_INSERT_TAIL(pathq, pe, next);
279 		} else {
280 			addpath(pathq, p, 1, 0);
281 		}
282 }
283 
284 /* Recreate a path string from a components queue */
285 static char *
286 qstring(struct pathhead *pathq)
287 {
288 	char *s, *p;
289 	struct pathentry *pe;
290 
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;
299 	}
300 
301 	return (s);
302 }
303 
304 /* Usage message */
305 static void
306 usage(void)
307 {
308 
309 	fprintf(stderr, "%s\n%s\n",
310 	    "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path ...]",
311 	    "\tkldconfig -r");
312 	exit(1);
313 }
314 
315 /* Main function */
316 int
317 main(int argc, char *argv[])
318 {
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;
344 
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);
352 	}
353 
354 	/* If no arguments and no options are specified, force '-m' */
355 	if (argc == 1)
356 		mflag = 1;
357 
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);
389 				}
390 				break;
391 			case 'U':
392 				uniqflag = 1;
393 				break;
394 			case 'v':
395 				vflag++;
396 				break;
397 			default:
398 				usage();
399 		}
400 
401 	argc -= optind;
402 	argv += optind;
403 
404 	/* The '-r' flag cannot be used when paths are also specified */
405 	if (rflag && (argc > 0))
406 		usage();
407 
408 	TAILQ_INIT(&pathq);
409 
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");
415 	}
416 
417 	/*
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.
423 	 */
424 	if ((act != addpath) || mflag || rflag || uniqflag)
425 		parsepath(&pathq, modpath, uniqflag);
426 	else if (modpath[0] != '\0')
427 		changed = 1;
428 
429 	/* Process the path arguments */
430 	for (i = 0; i < argc; i++)
431 		act(&pathq, argv[i], fflag, iflag);
432 
433 	if (changed && !nflag)
434 		setpath(&pathq);
435 
436 	if (rflag || (changed && vflag)) {
437 		if (changed && (vflag > 1))
438 			printf("%s -> ", origpath);
439 		showpath(&pathq);
440 	}
441 
442 	return (0);
443 }
444