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