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 } 419