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