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