kldconfig.c revision 79002
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 28static const char rcsid[] = 29 "$FreeBSD: head/sbin/kldconfig/kldconfig.c 79002 2001-06-29 22:31:17Z roam $"; 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 */ 58TAILQ_HEAD(pathhead, pathentry); 59struct pathentry { 60 char *path; 61 TAILQ_ENTRY(pathentry) next; 62}; 63 64/* the Management Information Base entries for the search path sysctl */ 65static int mib[5]; 66static size_t miblen; 67/* the sysctl name, defaults to PATHCTL */ 68static char *pathctl; 69/* the sysctl value - the current module search path */ 70static char *modpath; 71/* flag whether user actions require changing the sysctl value */ 72static int changed; 73 74/* Top-level path management functions */ 75static void addpath(struct pathhead *, char *, int, int); 76static void rempath(struct pathhead *, char *, int, int); 77static void showpath(struct pathhead *); 78 79/* Low-level path management functions */ 80static char *qstring(struct pathhead *); 81 82/* sysctl-related functions */ 83static void getmib(void); 84static void getpath(void); 85static void parsepath(struct pathhead *, char *, int); 86static void setpath(struct pathhead *); 87 88static void usage(void); 89 90/* Get the MIB entry for our sysctl */ 91static void 92getmib(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 */ 105static void 106getpath(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 */ 131static void 132setpath(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 */ 151static void 152addpath(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 */ 212static void 213rempath(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 */ 253static void 254showpath(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 */ 267static void 268parsepath(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 */ 287static char * 288qstring(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 */ 307static void 308usage(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 */ 318int 319main(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