syslogd: Fix indent.
[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  */
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 = sizeof(mib) / sizeof(mib[0]);
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 }