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