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