Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/kldconfig/kldconfig.c
39475 views
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
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
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
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
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
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
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
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 *
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
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
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
417