csplit(1): Staticize.
[dragonfly.git] / sbin / kldconfig / kldconfig.c
blob4d3286122682a14f6ad77916df859156d01550d5
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 $
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/queue.h>
32 #include <sys/sysctl.h>
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>
42 #define NEED_SLASHTERM
44 /* the default sysctl name */
45 #define PATHCTL "kern.module_path"
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;
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;
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 *);
69 /* Low-level path management functions */
70 static char *qstring(struct pathhead *);
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 *);
78 static void usage(void);
80 /* Get the MIB entry for our sysctl */
81 static void
82 getmib(void)
85 /* have we already fetched it? */
86 if (miblen != 0)
87 return;
89 miblen = sizeof(mib) / sizeof(mib[0]);
90 if (sysctlnametomib(pathctl, mib, &miblen) != 0)
91 err(1, "sysctlnametomib(%s)", pathctl);
94 /* Get the current module search path */
95 static void
96 getpath(void)
98 char *path;
99 size_t sz;
101 if (modpath != NULL) {
102 free(modpath);
103 modpath = NULL;
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);
115 if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
116 err(1, "getting path: sysctl(%s)", pathctl);
117 modpath = path;
120 /* Set the module search path after changing it */
121 static void
122 setpath(struct pathhead *pathq)
124 char *newpath;
126 if (miblen == 0)
127 getmib();
128 if ((newpath = qstring(pathq)) == NULL) {
129 errno = ENOMEM;
130 err(1, "building path string");
132 if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
133 err(1, "setting path: sysctl(%s)", pathctl);
135 if (modpath)
136 free(modpath);
137 modpath = newpath;
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)
144 struct pathentry *pe, *pskip;
145 char pathbuf[MAXPATHLEN+1];
146 size_t len;
147 static unsigned added = 0;
148 unsigned i;
151 * If the path exists, use it; otherwise, take the user-specified
152 * path at face value - may be a removed directory.
154 if (realpath(path, pathbuf) == NULL)
155 strlcpy(pathbuf, path, sizeof(pathbuf));
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] = '/';
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 */
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);
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");
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++;
198 changed = 1;
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)
205 char pathbuf[MAXPATHLEN+1];
206 struct pathentry *pe;
207 size_t len;
209 /* same logic as in addpath() */
210 if (realpath(path, pathbuf) == NULL)
211 strlcpy(pathbuf, path, sizeof(pathbuf));
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] = '/';
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 */
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);
237 /* OK, remove it now.. */
238 TAILQ_REMOVE(pathq, pe, next);
239 changed = 1;
242 /* Display the retrieved module search path */
243 static void
244 showpath(struct pathhead *pathq)
246 char *s;
248 if ((s = qstring(pathq)) == NULL) {
249 errno = ENOMEM;
250 err(1, "building path string");
252 printf("%s\n", s);
253 free(s);
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)
260 char *p;
261 struct pathentry *pe;
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");
270 TAILQ_INSERT_TAIL(pathq, pe, next);
271 } else {
272 addpath(pathq, p, 1, 0);
276 /* Recreate a path string from a components queue */
277 static char *
278 qstring(struct pathhead *pathq)
280 char *s, *p;
281 struct pathentry *pe;
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;
293 return (s);
296 /* Usage message */
297 static void
298 usage(void)
301 fprintf(stderr, "%s\n%s\n",
302 "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path..]",
303 "\tkldconfig -r");
304 exit(1);
307 /* Main function */
309 main(int argc, char *argv[])
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;
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);
346 /* If no arguments and no options are specified, force '-m' */
347 if (argc == 1)
348 mflag = 1;
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);
382 break;
383 case 'U':
384 uniqflag = 1;
385 break;
386 case 'v':
387 vflag++;
388 break;
389 default:
390 usage();
393 argc -= optind;
394 argv += optind;
396 /* The '-r' flag cannot be used when paths are also specified */
397 if (rflag && (argc > 0))
398 usage();
400 TAILQ_INIT(&pathq);
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");
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.
416 if ((act != addpath) || mflag || rflag || uniqflag)
417 parsepath(&pathq, modpath, uniqflag);
418 else if (modpath[0] != '\0')
419 changed = 1;
421 /* Process the path arguments */
422 for (i = 0; i < argc; i++)
423 act(&pathq, argv[i], fflag, iflag);
425 if (changed && !nflag)
426 setpath(&pathq);
428 if (rflag || (changed && vflag)) {
429 if (changed && (vflag > 1))
430 printf("%s -> ", origpath);
431 showpath(&pathq);
434 return (0);