Merge from vendor branch NTPD:
[dragonfly.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  * $FreeBSD: src/sbin/kldconfig/kldconfig.c,v 1.3.2.1 2001/08/01 05:52:36 obrien Exp $
27  * $DragonFly: src/sbin/kldconfig/kldconfig.c,v 1.2 2003/06/17 04:27:33 dillon Exp $
28  */
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, NULL) == -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, NULL) == -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)
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 }