1 /* 2 * Copyright (c) 2001 Peter Pentchev 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #ifndef lint 28 static const char rcsid[] = 29 "$FreeBSD$"; 30 #endif /* not lint */ 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 #if defined(__FreeBSD_version) 46 #if __FreeBSD_version < 500000 47 #define NEED_SLASHTERM 48 #endif /* < 500000 */ 49 #else /* defined(__FreeBSD_version) */ 50 /* just in case.. */ 51 #define NEED_SLASHTERM 52 #endif /* defined(__FreeBSD_version) */ 53 54 /* the default sysctl name */ 55 #define PATHCTL "kern.module_path" 56 57 /* queue structure for the module path broken down into components */ 58 TAILQ_HEAD(pathhead, pathentry); 59 struct pathentry { 60 char *path; 61 TAILQ_ENTRY(pathentry) next; 62 }; 63 64 /* the Management Information Base entries for the search path sysctl */ 65 static int mib[5]; 66 static size_t miblen; 67 /* the sysctl name, defaults to PATHCTL */ 68 static char *pathctl; 69 /* the sysctl value - the current module search path */ 70 static char *modpath; 71 /* flag whether user actions require changing the sysctl value */ 72 static int changed; 73 74 /* Top-level path management functions */ 75 static void addpath(struct pathhead *, char *, int, int); 76 static void rempath(struct pathhead *, char *, int, int); 77 static void showpath(struct pathhead *); 78 79 /* Low-level path management functions */ 80 static char *qstring(struct pathhead *); 81 82 /* sysctl-related functions */ 83 static void getmib(void); 84 static void getpath(void); 85 static void parsepath(struct pathhead *, char *, int); 86 static void setpath(struct pathhead *); 87 88 static void usage(void); 89 90 /* Get the MIB entry for our sysctl */ 91 static void 92 getmib(void) 93 { 94 95 /* have we already fetched it? */ 96 if (miblen != 0) 97 return; 98 99 miblen = sizeof(mib) / sizeof(mib[0]); 100 if (sysctlnametomib(pathctl, mib, &miblen) != 0) 101 err(1, "sysctlnametomib(%s)", pathctl); 102 } 103 104 /* Get the current module search path */ 105 static void 106 getpath(void) 107 { 108 char *path; 109 size_t sz; 110 111 if (modpath != NULL) { 112 free(modpath); 113 modpath = NULL; 114 } 115 116 if (miblen == 0) 117 getmib(); 118 if (sysctl(mib, miblen, NULL, &sz, NULL, NULL) == -1) 119 err(1, "getting path: sysctl(%s) - size only", pathctl); 120 if ((path = malloc(sz + 1)) == NULL) { 121 errno = ENOMEM; 122 err(1, "allocating %lu bytes for the path", 123 (unsigned long)sz+1); 124 } 125 if (sysctl(mib, miblen, path, &sz, NULL, NULL) == -1) 126 err(1, "getting path: sysctl(%s)", pathctl); 127 modpath = path; 128 } 129 130 /* Set the module search path after changing it */ 131 static void 132 setpath(struct pathhead *pathq) 133 { 134 char *newpath; 135 136 if (miblen == 0) 137 getmib(); 138 if ((newpath = qstring(pathq)) == NULL) { 139 errno = ENOMEM; 140 err(1, "building path string"); 141 } 142 if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1) 143 err(1, "setting path: sysctl(%s)", pathctl); 144 145 if (modpath) 146 free(modpath); 147 modpath = newpath; 148 } 149 150 /* Add/insert a new component to the module search path */ 151 static void 152 addpath(struct pathhead *pathq, char *path, int force, int insert) 153 { 154 struct pathentry *pe, *pskip; 155 char pathbuf[MAXPATHLEN+1]; 156 size_t len; 157 static unsigned added = 0; 158 unsigned i; 159 160 /* 161 * If the path exists, use it; otherwise, take the user-specified 162 * path at face value - may be a removed directory. 163 */ 164 if (realpath(path, pathbuf) == NULL) 165 strlcpy(pathbuf, path, sizeof(pathbuf)); 166 167 len = strlen(pathbuf); 168 #ifdef NEED_SLASHTERM 169 /* slash-terminate, because the kernel linker said so. */ 170 if ((len == 0) || (pathbuf[len-1] != '/')) { 171 if (len == sizeof(pathbuf) - 1) 172 errx(1, "path too long: %s", pathbuf); 173 pathbuf[len] = '/'; 174 } 175 #else /* NEED_SLASHTERM */ 176 /* remove a terminating slash if present */ 177 if ((len > 0) && (pathbuf[len-1] == '/')) 178 pathbuf[--len] = '\0'; 179 #endif /* NEED_SLASHTERM */ 180 181 /* is it already in there? */ 182 TAILQ_FOREACH(pe, pathq, next) 183 if (!strcmp(pe->path, pathbuf)) 184 break; 185 if (pe != NULL) { 186 if (force) 187 return; 188 errx(1, "already in the module search path: %s", pathbuf); 189 } 190 191 /* OK, allocate and add it. */ 192 if (((pe = malloc(sizeof(*pe))) == NULL) || 193 ((pe->path = strdup(pathbuf)) == NULL)) { 194 errno = ENOMEM; 195 err(1, "allocating path component"); 196 } 197 if (!insert) { 198 TAILQ_INSERT_TAIL(pathq, pe, next); 199 } else { 200 for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++) 201 pskip = TAILQ_NEXT(pskip, next); 202 if (pskip != NULL) 203 TAILQ_INSERT_BEFORE(pskip, pe, next); 204 else 205 TAILQ_INSERT_TAIL(pathq, pe, next); 206 added++; 207 } 208 changed = 1; 209 } 210 211 /* Remove a path component from the module search path */ 212 static void 213 rempath(struct pathhead *pathq, char *path, int force, int insert __unused) 214 { 215 char pathbuf[MAXPATHLEN+1]; 216 struct pathentry *pe; 217 size_t len; 218 219 /* same logic as in addpath() */ 220 if (realpath(path, pathbuf) == NULL) 221 strlcpy(pathbuf, path, sizeof(pathbuf)); 222 223 len = strlen(pathbuf); 224 #ifdef NEED_SLASHTERM 225 /* slash-terminate, because the kernel linker said so. */ 226 if ((len == 0) || (pathbuf[len-1] != '/')) { 227 if (len == sizeof(pathbuf) - 1) 228 errx(1, "path too long: %s", pathbuf); 229 pathbuf[len] = '/'; 230 } 231 #else /* NEED_SLASHTERM */ 232 /* remove a terminating slash if present */ 233 if ((len > 0) && (pathbuf[len-1] == '/')) 234 pathbuf[--len] = '\0'; 235 #endif /* NEED_SLASHTERM */ 236 237 /* Is it in there? */ 238 TAILQ_FOREACH(pe, pathq, next) 239 if (!strcmp(pe->path, pathbuf)) 240 break; 241 if (pe == NULL) { 242 if (force) 243 return; 244 errx(1, "not in module search path: %s", pathbuf); 245 } 246 247 /* OK, remove it now.. */ 248 TAILQ_REMOVE(pathq, pe, next); 249 changed = 1; 250 } 251 252 /* Display the retrieved module search path */ 253 static void 254 showpath(struct pathhead *pathq) 255 { 256 char *s; 257 258 if ((s = qstring(pathq)) == NULL) { 259 errno = ENOMEM; 260 err(1, "building path string"); 261 } 262 printf("%s\n", s); 263 free(s); 264 } 265 266 /* Break a string down into path components, store them into a queue */ 267 static void 268 parsepath(struct pathhead *pathq, char *path, int uniq) 269 { 270 char *p; 271 struct pathentry *pe; 272 273 while ((p = strsep(&path, ";")) != NULL) 274 if (!uniq) { 275 if (((pe = malloc(sizeof(pe))) == NULL) || 276 ((pe->path = strdup(p)) == NULL)) { 277 errno = ENOMEM; 278 err(1, "allocating path element"); 279 } 280 TAILQ_INSERT_TAIL(pathq, pe, next); 281 } else { 282 addpath(pathq, p, 1, 0); 283 } 284 } 285 286 /* Recreate a path string from a components queue */ 287 static char * 288 qstring(struct pathhead *pathq) 289 { 290 char *s, *p; 291 struct pathentry *pe; 292 293 s = strdup(""); 294 TAILQ_FOREACH(pe, pathq, next) { 295 asprintf(&p, "%s%s%s", 296 s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": "")); 297 free(s); 298 if (p == NULL) 299 return (NULL); 300 s = p; 301 } 302 303 return (s); 304 } 305 306 /* Usage message */ 307 static void 308 usage(void) 309 { 310 311 fprintf(stderr, "%s\n%s\n", 312 "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path..]", 313 "\tkldconfig -r"); 314 exit(1); 315 } 316 317 /* Main function */ 318 int 319 main(int argc, char *argv[]) 320 { 321 /* getopt() iterator */ 322 int c; 323 /* iterator over argv[] path components */ 324 int i; 325 /* Command-line flags: */ 326 /* "-f" - no diagnostic messages */ 327 int fflag; 328 /* "-i" - insert before the first element */ 329 int iflag; 330 /* "-m" - merge into the existing path, do not replace it */ 331 int mflag; 332 /* "-n" - do not actually set the new module path */ 333 int nflag; 334 /* "-r" - print out the current search path */ 335 int rflag; 336 /* "-U" - remove duplicate values from the path */ 337 int uniqflag; 338 /* "-v" - verbose operation (currently a no-op) */ 339 int vflag; 340 /* The higher-level function to call - add/remove */ 341 void (*act)(struct pathhead *, char *, int, int); 342 /* The original path */ 343 char *origpath; 344 /* The module search path broken down into components */ 345 struct pathhead pathq; 346 347 fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0; 348 act = addpath; 349 origpath = NULL; 350 if ((pathctl = strdup(PATHCTL)) == NULL) { 351 /* this is just too paranoid ;) */ 352 errno = ENOMEM; 353 err(1, "initializing sysctl name %s", PATHCTL); 354 } 355 356 /* If no arguments and no options are specified, force '-m' */ 357 if (argc == 1) 358 mflag = 1; 359 360 while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1) 361 switch (c) { 362 case 'd': 363 if (iflag || mflag) 364 usage(); 365 act = rempath; 366 break; 367 case 'f': 368 fflag = 1; 369 break; 370 case 'i': 371 if (act != addpath) 372 usage(); 373 iflag = 1; 374 break; 375 case 'm': 376 if (act != addpath) 377 usage(); 378 mflag = 1; 379 break; 380 case 'n': 381 nflag = 1; 382 break; 383 case 'r': 384 rflag = 1; 385 break; 386 case 'S': 387 free(pathctl); 388 if ((pathctl = strdup(optarg)) == NULL) { 389 errno = ENOMEM; 390 err(1, "sysctl name %s", optarg); 391 } 392 break; 393 case 'U': 394 uniqflag = 1; 395 break; 396 case 'v': 397 vflag++; 398 break; 399 default: 400 usage(); 401 } 402 403 argc -= optind; 404 argv += optind; 405 406 /* The '-r' flag cannot be used when paths are also specified */ 407 if (rflag && (argc > 0)) 408 usage(); 409 410 TAILQ_INIT(&pathq); 411 412 /* Retrieve and store the path from the sysctl value */ 413 getpath(); 414 if ((origpath = strdup(modpath)) == NULL) { 415 errno = ENOMEM; 416 err(1, "saving the original search path"); 417 } 418 419 /* 420 * Break down the path into the components queue if: 421 * - we are NOT adding paths, OR 422 * - the 'merge' flag is specified, OR 423 * - the 'print only' flag is specified, OR 424 * - the 'unique' flag is specified. 425 */ 426 if ((act != addpath) || mflag || rflag || uniqflag) 427 parsepath(&pathq, modpath, uniqflag); 428 else if (modpath[0] != '\0') 429 changed = 1; 430 431 /* Process the path arguments */ 432 for (i = 0; i < argc; i++) 433 act(&pathq, argv[i], fflag, iflag); 434 435 if (changed && !nflag) 436 setpath(&pathq); 437 438 if (rflag || (changed && vflag)) { 439 if (changed && (vflag > 1)) 440 printf("%s -> ", origpath); 441 showpath(&pathq); 442 } 443 444 return (0); 445 } 446