]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/kldconfig/kldconfig.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / sbin / kldconfig / kldconfig.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2001 Peter Pentchev
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/queue.h>
35 #include <sys/sysctl.h>
36
37 #include <err.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 /* the default sysctl name */
46 #define PATHCTL "kern.module_path"
47
48 /* queue structure for the module path broken down into components */
49 TAILQ_HEAD(pathhead, pathentry);
50 struct pathentry {
51         char                    *path;
52         TAILQ_ENTRY(pathentry)  next;
53 };
54
55 /* the Management Information Base entries for the search path sysctl */
56 static int       mib[5];
57 static size_t    miblen;
58 /* the sysctl name, defaults to PATHCTL */
59 static char     *pathctl;
60 /* the sysctl value - the current module search path */
61 static char     *modpath;
62 /* flag whether user actions require changing the sysctl value */
63 static int       changed;
64
65 /* Top-level path management functions */
66 static void      addpath(struct pathhead *, char *, int, int);
67 static void      rempath(struct pathhead *, char *, int, int);
68 static void      showpath(struct pathhead *);
69
70 /* Low-level path management functions */
71 static char     *qstring(struct pathhead *);
72
73 /* sysctl-related functions */
74 static void      getmib(void);
75 static void      getpath(void);
76 static void      parsepath(struct pathhead *, char *, int);
77 static void      setpath(struct pathhead *);
78
79 static void      usage(void);
80
81 /* Get the MIB entry for our sysctl */
82 static void
83 getmib(void)
84 {
85
86         /* have we already fetched it? */
87         if (miblen != 0)
88                 return;
89         
90         miblen = nitems(mib);
91         if (sysctlnametomib(pathctl, mib, &miblen) != 0)
92                 err(1, "sysctlnametomib(%s)", pathctl);
93 }
94
95 /* Get the current module search path */
96 static void
97 getpath(void)
98 {
99         char *path;
100         size_t sz;
101
102         if (modpath != NULL) {
103                 free(modpath);
104                 modpath = NULL;
105         }
106
107         if (miblen == 0)
108                 getmib();
109         if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1)
110                 err(1, "getting path: sysctl(%s) - size only", pathctl);
111         if ((path = malloc(sz + 1)) == NULL) {
112                 errno = ENOMEM;
113                 err(1, "allocating %lu bytes for the path",
114                     (unsigned long)sz+1);
115         }
116         if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1)
117                 err(1, "getting path: sysctl(%s)", pathctl);
118         modpath = path;
119 }
120
121 /* Set the module search path after changing it */
122 static void
123 setpath(struct pathhead *pathq)
124 {
125         char *newpath;
126
127         if (miblen == 0)
128                 getmib();
129         if ((newpath = qstring(pathq)) == NULL) {
130                 errno = ENOMEM;
131                 err(1, "building path string");
132         }
133         if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
134                 err(1, "setting path: sysctl(%s)", pathctl);
135
136         if (modpath != NULL)
137                 free(modpath);
138         modpath = newpath;
139 }
140
141 /* Add/insert a new component to the module search path */
142 static void
143 addpath(struct pathhead *pathq, char *path, int force, int insert)
144 {
145         struct pathentry *pe, *pskip;
146         char pathbuf[MAXPATHLEN+1];
147         size_t len;
148         static unsigned added = 0;
149         unsigned i;
150
151         /*
152          * If the path exists, use it; otherwise, take the user-specified
153          * path at face value - may be a removed directory.
154          */
155         if (realpath(path, pathbuf) == NULL)
156                 strlcpy(pathbuf, path, sizeof(pathbuf));
157
158         len = strlen(pathbuf);
159         /* remove a terminating slash if present */
160         if ((len > 0) && (pathbuf[len-1] == '/'))
161                 pathbuf[--len] = '\0';
162
163         /* is it already in there? */
164         TAILQ_FOREACH(pe, pathq, next)
165                 if (!strcmp(pe->path, pathbuf))
166                         break;
167         if (pe != NULL) {
168                 if (force)
169                         return;
170                 errx(1, "already in the module search path: %s", pathbuf);
171         }
172         
173         /* OK, allocate and add it. */
174         if (((pe = malloc(sizeof(*pe))) == NULL) ||
175             ((pe->path = strdup(pathbuf)) == NULL)) {
176                 errno = ENOMEM;
177                 err(1, "allocating path component");
178         }
179         if (!insert) {
180                 TAILQ_INSERT_TAIL(pathq, pe, next);
181         } else {
182                 for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++)
183                         pskip = TAILQ_NEXT(pskip, next);
184                 if (pskip != NULL)
185                         TAILQ_INSERT_BEFORE(pskip, pe, next);
186                 else
187                         TAILQ_INSERT_TAIL(pathq, pe, next);
188                 added++;
189         }
190         changed = 1;
191 }
192
193 /* Remove a path component from the module search path */
194 static void
195 rempath(struct pathhead *pathq, char *path, int force, int insert __unused)
196 {
197         char pathbuf[MAXPATHLEN+1];
198         struct pathentry *pe;
199         size_t len;
200
201         /* same logic as in addpath() */
202         if (realpath(path, pathbuf) == NULL)
203                 strlcpy(pathbuf, path, sizeof(pathbuf));
204
205         len = strlen(pathbuf);
206         /* remove a terminating slash if present */
207         if ((len > 0) && (pathbuf[len-1] == '/'))
208                 pathbuf[--len] = '\0';
209
210         /* Is it in there? */
211         TAILQ_FOREACH(pe, pathq, next)
212                 if (!strcmp(pe->path, pathbuf))
213                         break;
214         if (pe == NULL) {
215                 if (force)
216                         return;
217                 errx(1, "not in module search path: %s", pathbuf);
218         }
219
220         /* OK, remove it now.. */
221         TAILQ_REMOVE(pathq, pe, next);
222         changed = 1;
223 }
224
225 /* Display the retrieved module search path */
226 static void
227 showpath(struct pathhead *pathq)
228 {
229         char *s;
230
231         if ((s = qstring(pathq)) == NULL) {
232                 errno = ENOMEM;
233                 err(1, "building path string");
234         }
235         printf("%s\n", s);
236         free(s);
237 }
238
239 /* Break a string down into path components, store them into a queue */
240 static void
241 parsepath(struct pathhead *pathq, char *path, int uniq)
242 {
243         char *p;
244         struct pathentry *pe;
245         
246         while ((p = strsep(&path, ";")) != NULL)
247                 if (!uniq) {
248                         if (((pe = malloc(sizeof(*pe))) == NULL) ||
249                             ((pe->path = strdup(p)) == NULL)) {
250                                 errno = ENOMEM;
251                                 err(1, "allocating path element");
252                         }
253                         TAILQ_INSERT_TAIL(pathq, pe, next);
254                 } else {
255                         addpath(pathq, p, 1, 0);
256                 }
257 }
258
259 /* Recreate a path string from a components queue */
260 static char *
261 qstring(struct pathhead *pathq)
262 {
263         char *s, *p;
264         struct pathentry *pe;
265         
266         s = strdup("");
267         TAILQ_FOREACH(pe, pathq, next) {
268                 asprintf(&p, "%s%s%s",
269                     s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
270                 free(s);
271                 if (p == NULL)
272                         return (NULL);
273                 s = p;
274         }
275
276         return (s);
277 }
278
279 /* Usage message */
280 static void
281 usage(void)
282 {
283
284         fprintf(stderr, "%s\n%s\n",
285             "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path ...]",
286             "\tkldconfig -r");
287         exit(1);
288 }
289
290 /* Main function */
291 int
292 main(int argc, char *argv[])
293 {
294         /* getopt() iterator */
295         int c;
296         /* iterator over argv[] path components */
297         int i;
298         /* Command-line flags: */
299         /* "-f" - no diagnostic messages */
300         int fflag;
301         /* "-i" - insert before the first element */
302         int iflag;
303         /* "-m" - merge into the existing path, do not replace it */
304         int mflag;
305         /* "-n" - do not actually set the new module path */
306         int nflag;
307         /* "-r" - print out the current search path */
308         int rflag;
309         /* "-U" - remove duplicate values from the path */
310         int uniqflag;
311         /* "-v" - verbose operation (currently a no-op) */
312         int vflag;
313         /* The higher-level function to call - add/remove */
314         void (*act)(struct pathhead *, char *, int, int);
315         /* The original path */
316         char *origpath;
317         /* The module search path broken down into components */
318         struct pathhead pathq;
319
320         fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0;
321         act = addpath;
322         origpath = NULL;
323         if ((pathctl = strdup(PATHCTL)) == NULL) {
324                 /* this is just too paranoid ;) */
325                 errno = ENOMEM;
326                 err(1, "initializing sysctl name %s", PATHCTL);
327         }
328
329         /* If no arguments and no options are specified, force '-m' */
330         if (argc == 1)
331                 mflag = 1;
332
333         while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1)
334                 switch (c) {
335                         case 'd':
336                                 if (iflag || mflag)
337                                         usage();
338                                 act = rempath;
339                                 break;
340                         case 'f':
341                                 fflag = 1;
342                                 break;
343                         case 'i':
344                                 if (act != addpath)
345                                         usage();
346                                 iflag = 1;
347                                 break;
348                         case 'm':
349                                 if (act != addpath)
350                                         usage();
351                                 mflag = 1;
352                                 break;
353                         case 'n':
354                                 nflag = 1;
355                                 break;
356                         case 'r':
357                                 rflag = 1;
358                                 break;
359                         case 'S':
360                                 free(pathctl);
361                                 if ((pathctl = strdup(optarg)) == NULL) {
362                                         errno = ENOMEM;
363                                         err(1, "sysctl name %s", optarg);
364                                 }
365                                 break;
366                         case 'U':
367                                 uniqflag = 1;
368                                 break;
369                         case 'v':
370                                 vflag++;
371                                 break;
372                         default:
373                                 usage();
374                 }
375
376         argc -= optind;
377         argv += optind;
378
379         /* The '-r' flag cannot be used when paths are also specified */
380         if (rflag && (argc > 0))
381                 usage();
382
383         TAILQ_INIT(&pathq);
384
385         /* Retrieve and store the path from the sysctl value */
386         getpath();
387         if ((origpath = strdup(modpath)) == NULL) {
388                 errno = ENOMEM;
389                 err(1, "saving the original search path");
390         }
391
392         /*
393          * Break down the path into the components queue if:
394          * - we are NOT adding paths, OR
395          * - the 'merge' flag is specified, OR
396          * - the 'print only' flag is specified, OR
397          * - the 'unique' flag is specified.
398          */
399         if ((act != addpath) || mflag || rflag || uniqflag)
400                 parsepath(&pathq, modpath, uniqflag);
401         else if (modpath[0] != '\0')
402                 changed = 1;
403
404         /* Process the path arguments */
405         for (i = 0; i < argc; i++)
406                 act(&pathq, argv[i], fflag, iflag);
407
408         if (changed && !nflag)
409                 setpath(&pathq);
410
411         if (rflag || (changed && vflag)) {
412                 if (changed && (vflag > 1))
413                         printf("%s -> ", origpath);
414                 showpath(&pathq);
415         }
416
417         return (0);
418 }