xref: /dragonfly/sbin/kldconfig/kldconfig.c (revision 7ff0fc30)
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  * $FreeBSD: src/sbin/kldconfig/kldconfig.c,v 1.3.2.1 2001/08/01 05:52:36 obrien Exp $
27  */
28 
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/queue.h>
32 #include <sys/sysctl.h>
33 
34 #include <err.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #define NEED_SLASHTERM
43 
44 /* the default sysctl name */
45 #define PATHCTL	"kern.module_path"
46 
47 /* queue structure for the module path broken down into components */
48 TAILQ_HEAD(pathhead, pathentry);
49 struct pathentry {
50 	char			*path;
51 	TAILQ_ENTRY(pathentry)	next;
52 };
53 
54 /* the Management Information Base entries for the search path sysctl */
55 static int	 mib[5];
56 static size_t	 miblen;
57 /* the sysctl name, defaults to PATHCTL */
58 static char	*pathctl;
59 /* the sysctl value - the current module search path */
60 static char	*modpath;
61 /* flag whether user actions require changing the sysctl value */
62 static int	 changed;
63 
64 /* Top-level path management functions */
65 static void	 addpath(struct pathhead *, char *, int, int);
66 static void	 rempath(struct pathhead *, char *, int, int);
67 static void	 showpath(struct pathhead *);
68 
69 /* Low-level path management functions */
70 static char	*qstring(struct pathhead *);
71 
72 /* sysctl-related functions */
73 static void	 getmib(void);
74 static void	 getpath(void);
75 static void	 parsepath(struct pathhead *, char *, int);
76 static void	 setpath(struct pathhead *);
77 
78 static void	 usage(void);
79 
80 /* Get the MIB entry for our sysctl */
81 static void
82 getmib(void)
83 {
84 
85 	/* have we already fetched it? */
86 	if (miblen != 0)
87 		return;
88 
89 	miblen = NELEM(mib);
90 	if (sysctlnametomib(pathctl, mib, &miblen) != 0)
91 		err(1, "sysctlnametomib(%s)", pathctl);
92 }
93 
94 /* Get the current module search path */
95 static void
96 getpath(void)
97 {
98 	char *path;
99 	size_t sz;
100 
101 	if (modpath != NULL) {
102 		free(modpath);
103 		modpath = NULL;
104 	}
105 
106 	if (miblen == 0)
107 		getmib();
108 	if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1)
109 		err(1, "getting path: sysctl(%s) - size only", pathctl);
110 	if ((path = malloc(sz + 1)) == NULL) {
111 		errno = ENOMEM;
112 		err(1, "allocating %lu bytes for the path",
113 		    (unsigned long)sz+1);
114 	}
115 	if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
116 		err(1, "getting path: sysctl(%s)", pathctl);
117 	modpath = path;
118 }
119 
120 /* Set the module search path after changing it */
121 static void
122 setpath(struct pathhead *pathq)
123 {
124 	char *newpath;
125 
126 	if (miblen == 0)
127 		getmib();
128 	if ((newpath = qstring(pathq)) == NULL) {
129 		errno = ENOMEM;
130 		err(1, "building path string");
131 	}
132 	if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
133 		err(1, "setting path: sysctl(%s)", pathctl);
134 
135 	if (modpath)
136 		free(modpath);
137 	modpath = newpath;
138 }
139 
140 /* Add/insert a new component to the module search path */
141 static void
142 addpath(struct pathhead *pathq, char *path, int force, int insert)
143 {
144 	struct pathentry *pe, *pskip;
145 	char pathbuf[MAXPATHLEN+1];
146 	size_t len;
147 	static unsigned added = 0;
148 	unsigned i;
149 
150 	/*
151 	 * If the path exists, use it; otherwise, take the user-specified
152 	 * path at face value - may be a removed directory.
153 	 */
154 	if (realpath(path, pathbuf) == NULL)
155 		strlcpy(pathbuf, path, sizeof(pathbuf));
156 
157 	len = strlen(pathbuf);
158 #ifdef NEED_SLASHTERM
159 	/* slash-terminate, because the kernel linker said so. */
160 	if ((len == 0) || (pathbuf[len-1] != '/')) {
161 		if (len == sizeof(pathbuf) - 1)
162 			errx(1, "path too long: %s", pathbuf);
163 		pathbuf[len] = '/';
164 	}
165 #else  /* NEED_SLASHTERM */
166 	/* remove a terminating slash if present */
167 	if ((len > 0) && (pathbuf[len-1] == '/'))
168 		pathbuf[--len] = '\0';
169 #endif /* NEED_SLASHTERM */
170 
171 	/* is it already in there? */
172 	TAILQ_FOREACH(pe, pathq, next)
173 		if (!strcmp(pe->path, pathbuf))
174 			break;
175 	if (pe != NULL) {
176 		if (force)
177 			return;
178 		errx(1, "already in the module search path: %s", pathbuf);
179 	}
180 
181 	/* OK, allocate and add it. */
182 	if (((pe = malloc(sizeof(*pe))) == NULL) ||
183 	    ((pe->path = strdup(pathbuf)) == NULL)) {
184 		errno = ENOMEM;
185 		err(1, "allocating path component");
186 	}
187 	if (!insert) {
188 		TAILQ_INSERT_TAIL(pathq, pe, next);
189 	} else {
190 		for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++)
191 			pskip = TAILQ_NEXT(pskip, next);
192 		if (pskip != NULL)
193 			TAILQ_INSERT_BEFORE(pskip, pe, next);
194 		else
195 			TAILQ_INSERT_TAIL(pathq, pe, next);
196 		added++;
197 	}
198 	changed = 1;
199 }
200 
201 /* Remove a path component from the module search path */
202 static void
203 rempath(struct pathhead *pathq, char *path, int force, int insert __unused)
204 {
205 	char pathbuf[MAXPATHLEN+1];
206 	struct pathentry *pe;
207 	size_t len;
208 
209 	/* same logic as in addpath() */
210 	if (realpath(path, pathbuf) == NULL)
211 		strlcpy(pathbuf, path, sizeof(pathbuf));
212 
213 	len = strlen(pathbuf);
214 #ifdef NEED_SLASHTERM
215 	/* slash-terminate, because the kernel linker said so. */
216 	if ((len == 0) || (pathbuf[len-1] != '/')) {
217 		if (len == sizeof(pathbuf) - 1)
218 			errx(1, "path too long: %s", pathbuf);
219 		pathbuf[len] = '/';
220 	}
221 #else  /* NEED_SLASHTERM */
222 	/* remove a terminating slash if present */
223 	if ((len > 0) && (pathbuf[len-1] == '/'))
224 		pathbuf[--len] = '\0';
225 #endif /* NEED_SLASHTERM */
226 
227 	/* Is it in there? */
228 	TAILQ_FOREACH(pe, pathq, next)
229 		if (!strcmp(pe->path, pathbuf))
230 			break;
231 	if (pe == NULL) {
232 		if (force)
233 			return;
234 		errx(1, "not in module search path: %s", pathbuf);
235 	}
236 
237 	/* OK, remove it now.. */
238 	TAILQ_REMOVE(pathq, pe, next);
239 	changed = 1;
240 }
241 
242 /* Display the retrieved module search path */
243 static void
244 showpath(struct pathhead *pathq)
245 {
246 	char *s;
247 
248 	if ((s = qstring(pathq)) == NULL) {
249 		errno = ENOMEM;
250 		err(1, "building path string");
251 	}
252 	printf("%s\n", s);
253 	free(s);
254 }
255 
256 /* Break a string down into path components, store them into a queue */
257 static void
258 parsepath(struct pathhead *pathq, char *path, int uniq)
259 {
260 	char *p;
261 	struct pathentry *pe;
262 
263 	while ((p = strsep(&path, ";")) != NULL)
264 		if (!uniq) {
265 			if (((pe = malloc(sizeof(*pe))) == NULL) ||
266 			    ((pe->path = strdup(p)) == NULL)) {
267 				errno = ENOMEM;
268 				err(1, "allocating path element");
269 			}
270 			TAILQ_INSERT_TAIL(pathq, pe, next);
271 		} else {
272 			addpath(pathq, p, 1, 0);
273 		}
274 }
275 
276 /* Recreate a path string from a components queue */
277 static char *
278 qstring(struct pathhead *pathq)
279 {
280 	char *s, *p;
281 	struct pathentry *pe;
282 
283 	s = strdup("");
284 	TAILQ_FOREACH(pe, pathq, next) {
285 		asprintf(&p, "%s%s%s",
286 		    s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
287 		free(s);
288 		if (p == NULL)
289 			return (NULL);
290 		s = p;
291 	}
292 
293 	return (s);
294 }
295 
296 /* Usage message */
297 static void
298 usage(void)
299 {
300 
301 	fprintf(stderr, "%s\n%s\n",
302 	    "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path..]",
303 	    "\tkldconfig -r");
304 	exit(1);
305 }
306 
307 /* Main function */
308 int
309 main(int argc, char *argv[])
310 {
311 	/* getopt() iterator */
312 	int c;
313 	/* iterator over argv[] path components */
314 	int i;
315 	/* Command-line flags: */
316 	/* "-f" - no diagnostic messages */
317 	int fflag;
318 	/* "-i" - insert before the first element */
319 	int iflag;
320 	/* "-m" - merge into the existing path, do not replace it */
321 	int mflag;
322 	/* "-n" - do not actually set the new module path */
323 	int nflag;
324 	/* "-r" - print out the current search path */
325 	int rflag;
326 	/* "-U" - remove duplicate values from the path */
327 	int uniqflag;
328 	/* "-v" - verbose operation (currently a no-op) */
329 	int vflag;
330 	/* The higher-level function to call - add/remove */
331 	void (*act)(struct pathhead *, char *, int, int);
332 	/* The original path */
333 	char *origpath;
334 	/* The module search path broken down into components */
335 	struct pathhead pathq;
336 
337 	fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0;
338 	act = addpath;
339 	origpath = NULL;
340 	if ((pathctl = strdup(PATHCTL)) == NULL) {
341 		/* this is just too paranoid ;) */
342 		errno = ENOMEM;
343 		err(1, "initializing sysctl name %s", PATHCTL);
344 	}
345 
346 	/* If no arguments and no options are specified, force '-m' */
347 	if (argc == 1)
348 		mflag = 1;
349 
350 	while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1)
351 		switch (c) {
352 			case 'd':
353 				if (iflag || mflag)
354 					usage();
355 				act = rempath;
356 				break;
357 			case 'f':
358 				fflag = 1;
359 				break;
360 			case 'i':
361 				if (act != addpath)
362 					usage();
363 				iflag = 1;
364 				break;
365 			case 'm':
366 				if (act != addpath)
367 					usage();
368 				mflag = 1;
369 				break;
370 			case 'n':
371 				nflag = 1;
372 				break;
373 			case 'r':
374 				rflag = 1;
375 				break;
376 			case 'S':
377 				free(pathctl);
378 				if ((pathctl = strdup(optarg)) == NULL) {
379 					errno = ENOMEM;
380 					err(1, "sysctl name %s", optarg);
381 				}
382 				break;
383 			case 'U':
384 				uniqflag = 1;
385 				break;
386 			case 'v':
387 				vflag++;
388 				break;
389 			default:
390 				usage();
391 		}
392 
393 	argc -= optind;
394 	argv += optind;
395 
396 	/* The '-r' flag cannot be used when paths are also specified */
397 	if (rflag && (argc > 0))
398 		usage();
399 
400 	TAILQ_INIT(&pathq);
401 
402 	/* Retrieve and store the path from the sysctl value */
403 	getpath();
404 	if ((origpath = strdup(modpath)) == NULL) {
405 		errno = ENOMEM;
406 		err(1, "saving the original search path");
407 	}
408 
409 	/*
410 	 * Break down the path into the components queue if:
411 	 * - we are NOT adding paths, OR
412 	 * - the 'merge' flag is specified, OR
413 	 * - the 'print only' flag is specified, OR
414 	 * - the 'unique' flag is specified.
415 	 */
416 	if ((act != addpath) || mflag || rflag || uniqflag)
417 		parsepath(&pathq, modpath, uniqflag);
418 	else if (modpath[0] != '\0')
419 		changed = 1;
420 
421 	/* Process the path arguments */
422 	for (i = 0; i < argc; i++)
423 		act(&pathq, argv[i], fflag, iflag);
424 
425 	if (changed && !nflag)
426 		setpath(&pathq);
427 
428 	if (rflag || (changed && vflag)) {
429 		if (changed && (vflag > 1))
430 			printf("%s -> ", origpath);
431 		showpath(&pathq);
432 	}
433 
434 	return (0);
435 }
436