kldconfig.c revision 285830
175115Sfenner/*
275115Sfenner * Copyright (c) 2001 Peter Pentchev
375115Sfenner * All rights reserved.
475115Sfenner *
575115Sfenner * Redistribution and use in source and binary forms, with or without
675115Sfenner * modification, are permitted provided that the following conditions
775115Sfenner * are met:
875115Sfenner * 1. Redistributions of source code must retain the above copyright
975115Sfenner *    notice, this list of conditions and the following disclaimer.
1075115Sfenner * 2. Redistributions in binary form must reproduce the above copyright
1175115Sfenner *    notice, this list of conditions and the following disclaimer in the
1275115Sfenner *    documentation and/or other materials provided with the distribution.
1375115Sfenner *
1475115Sfenner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575115Sfenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675115Sfenner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775115Sfenner * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875115Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975115Sfenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075115Sfenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175115Sfenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2275115Sfenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375115Sfenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475115Sfenner * SUCH DAMAGE.
2575115Sfenner */
2675115Sfenner
2775115Sfenner#include <sys/cdefs.h>
2875115Sfenner__FBSDID("$FreeBSD: releng/10.2/sbin/kldconfig/kldconfig.c 152169 2005-11-07 19:22:20Z ru $");
2975115Sfenner
3075115Sfenner#include <sys/param.h>
3175115Sfenner#include <sys/types.h>
3275115Sfenner#include <sys/queue.h>
3375115Sfenner#include <sys/sysctl.h>
3475115Sfenner
3575115Sfenner#include <err.h>
3675115Sfenner#include <errno.h>
3775115Sfenner#include <limits.h>
3875115Sfenner#include <stdio.h>
3975115Sfenner#include <stdlib.h>
4075115Sfenner#include <string.h>
4175115Sfenner#include <unistd.h>
4275115Sfenner
4375115Sfenner#if defined(__FreeBSD_version)
4475115Sfenner#if __FreeBSD_version < 500000
4575115Sfenner#define NEED_SLASHTERM
4675115Sfenner#endif /* < 500000 */
4775115Sfenner#else  /* defined(__FreeBSD_version) */
4875115Sfenner/* just in case.. */
4975115Sfenner#define NEED_SLASHTERM
5075115Sfenner#endif /* defined(__FreeBSD_version) */
5175115Sfenner
5275115Sfenner/* the default sysctl name */
5375115Sfenner#define PATHCTL	"kern.module_path"
5475115Sfenner
5575115Sfenner/* queue structure for the module path broken down into components */
5675115SfennerTAILQ_HEAD(pathhead, pathentry);
5775115Sfennerstruct pathentry {
5875115Sfenner	char			*path;
5975115Sfenner	TAILQ_ENTRY(pathentry)	next;
6075115Sfenner};
6175115Sfenner
6275115Sfenner/* the Management Information Base entries for the search path sysctl */
6375115Sfennerstatic int	 mib[5];
6475115Sfennerstatic size_t	 miblen;
6575115Sfenner/* the sysctl name, defaults to PATHCTL */
6675115Sfennerstatic char	*pathctl;
6775115Sfenner/* the sysctl value - the current module search path */
6875115Sfennerstatic char	*modpath;
6975115Sfenner/* flag whether user actions require changing the sysctl value */
7075115Sfennerstatic int	 changed;
7175115Sfenner
7275115Sfenner/* Top-level path management functions */
7375115Sfennerstatic void	 addpath(struct pathhead *, char *, int, int);
7475115Sfennerstatic void	 rempath(struct pathhead *, char *, int, int);
7575115Sfennerstatic void	 showpath(struct pathhead *);
7675115Sfenner
7775115Sfenner/* Low-level path management functions */
7875115Sfennerstatic char	*qstring(struct pathhead *);
7975115Sfenner
8075115Sfenner/* sysctl-related functions */
8175115Sfennerstatic void	 getmib(void);
8275115Sfennerstatic void	 getpath(void);
8375115Sfennerstatic void	 parsepath(struct pathhead *, char *, int);
8475115Sfennerstatic void	 setpath(struct pathhead *);
8575115Sfenner
8675115Sfennerstatic void	 usage(void);
8775115Sfenner
8875115Sfenner/* Get the MIB entry for our sysctl */
8975115Sfennerstatic void
9075115Sfennergetmib(void)
9175115Sfenner{
9275115Sfenner
9375115Sfenner	/* have we already fetched it? */
9475115Sfenner	if (miblen != 0)
9575115Sfenner		return;
9675115Sfenner
9775115Sfenner	miblen = sizeof(mib) / sizeof(mib[0]);
9875115Sfenner	if (sysctlnametomib(pathctl, mib, &miblen) != 0)
9975115Sfenner		err(1, "sysctlnametomib(%s)", pathctl);
10075115Sfenner}
10175115Sfenner
10275115Sfenner/* Get the current module search path */
10375115Sfennerstatic void
10475115Sfennergetpath(void)
10575115Sfenner{
10675115Sfenner	char *path;
10775115Sfenner	size_t sz;
10875115Sfenner
10975115Sfenner	if (modpath != NULL) {
11075115Sfenner		free(modpath);
11175115Sfenner		modpath = NULL;
11275115Sfenner	}
11375115Sfenner
11475115Sfenner	if (miblen == 0)
11575115Sfenner		getmib();
11675115Sfenner	if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1)
11775115Sfenner		err(1, "getting path: sysctl(%s) - size only", pathctl);
11875115Sfenner	if ((path = malloc(sz + 1)) == NULL) {
11975115Sfenner		errno = ENOMEM;
12075115Sfenner		err(1, "allocating %lu bytes for the path",
12175115Sfenner		    (unsigned long)sz+1);
12275115Sfenner	}
12375115Sfenner	if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
12475115Sfenner		err(1, "getting path: sysctl(%s)", pathctl);
12575115Sfenner	modpath = path;
12675115Sfenner}
12775115Sfenner
12875115Sfenner/* Set the module search path after changing it */
12975115Sfennerstatic void
13075115Sfennersetpath(struct pathhead *pathq)
13175115Sfenner{
13275115Sfenner	char *newpath;
13375115Sfenner
13475115Sfenner	if (miblen == 0)
13575115Sfenner		getmib();
13675115Sfenner	if ((newpath = qstring(pathq)) == NULL) {
13775115Sfenner		errno = ENOMEM;
13875115Sfenner		err(1, "building path string");
13975115Sfenner	}
14075115Sfenner	if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
14175115Sfenner		err(1, "setting path: sysctl(%s)", pathctl);
14275115Sfenner
14375115Sfenner	if (modpath != NULL)
14475115Sfenner		free(modpath);
14575115Sfenner	modpath = newpath;
14675115Sfenner}
14775115Sfenner
14875115Sfenner/* Add/insert a new component to the module search path */
14975115Sfennerstatic void
15075115Sfenneraddpath(struct pathhead *pathq, char *path, int force, int insert)
15175115Sfenner{
15275115Sfenner	struct pathentry *pe, *pskip;
15375115Sfenner	char pathbuf[MAXPATHLEN+1];
15475115Sfenner	size_t len;
15575115Sfenner	static unsigned added = 0;
15675115Sfenner	unsigned i;
15775115Sfenner
15875115Sfenner	/*
15975115Sfenner	 * If the path exists, use it; otherwise, take the user-specified
16075115Sfenner	 * path at face value - may be a removed directory.
16175115Sfenner	 */
16275115Sfenner	if (realpath(path, pathbuf) == NULL)
16375115Sfenner		strlcpy(pathbuf, path, sizeof(pathbuf));
16475115Sfenner
16575115Sfenner	len = strlen(pathbuf);
16675115Sfenner#ifdef NEED_SLASHTERM
16775115Sfenner	/* slash-terminate, because the kernel linker said so. */
16875115Sfenner	if ((len == 0) || (pathbuf[len-1] != '/')) {
16975115Sfenner		if (len == sizeof(pathbuf) - 1)
17075115Sfenner			errx(1, "path too long: %s", pathbuf);
17175115Sfenner		pathbuf[len] = '/';
17275115Sfenner	}
17375115Sfenner#else  /* NEED_SLASHTERM */
17475115Sfenner	/* remove a terminating slash if present */
17575115Sfenner	if ((len > 0) && (pathbuf[len-1] == '/'))
17675115Sfenner		pathbuf[--len] = '\0';
17775115Sfenner#endif /* NEED_SLASHTERM */
17875115Sfenner
17975115Sfenner	/* is it already in there? */
18075115Sfenner	TAILQ_FOREACH(pe, pathq, next)
18175115Sfenner		if (!strcmp(pe->path, pathbuf))
18275115Sfenner			break;
18375115Sfenner	if (pe != NULL) {
18475115Sfenner		if (force)
18575115Sfenner			return;
18675115Sfenner		errx(1, "already in the module search path: %s", pathbuf);
18775115Sfenner	}
18875115Sfenner
18975115Sfenner	/* OK, allocate and add it. */
19075115Sfenner	if (((pe = malloc(sizeof(*pe))) == NULL) ||
19175115Sfenner	    ((pe->path = strdup(pathbuf)) == NULL)) {
19275115Sfenner		errno = ENOMEM;
19375115Sfenner		err(1, "allocating path component");
19475115Sfenner	}
19575115Sfenner	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 */
210static void
211rempath(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 */
251static void
252showpath(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 */
265static void
266parsepath(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 */
285static char *
286qstring(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 */
305static void
306usage(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 */
316int
317main(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