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