kldconfig.c revision 285830
175115Sfenner/* 275115Sfenner * Copyright (c) 2001 Peter Pentchev 375115Sfenner * All rights reserved. 475115Sfenner * 575115Sfenner * Redistribution and use in source and binary forms, with or without 675115Sfenner * modification, are permitted provided that the following conditions 775115Sfenner * are met: 875115Sfenner * 1. Redistributions of source code must retain the above copyright 975115Sfenner * notice, this list of conditions and the following disclaimer. 1075115Sfenner * 2. Redistributions in binary form must reproduce the above copyright 1175115Sfenner * notice, this list of conditions and the following disclaimer in the 1275115Sfenner * documentation and/or other materials provided with the distribution. 1375115Sfenner * 1475115Sfenner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1575115Sfenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1675115Sfenner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1775115Sfenner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1875115Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1975115Sfenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2075115Sfenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2175115Sfenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2275115Sfenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2375115Sfenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2475115Sfenner * SUCH DAMAGE. 2575115Sfenner */ 2675115Sfenner 2775115Sfenner#include <sys/cdefs.h> 2875115Sfenner__FBSDID("$FreeBSD: releng/10.2/sbin/kldconfig/kldconfig.c 152169 2005-11-07 19:22:20Z ru $"); 2975115Sfenner 3075115Sfenner#include <sys/param.h> 3175115Sfenner#include <sys/types.h> 3275115Sfenner#include <sys/queue.h> 3375115Sfenner#include <sys/sysctl.h> 3475115Sfenner 3575115Sfenner#include <err.h> 3675115Sfenner#include <errno.h> 3775115Sfenner#include <limits.h> 3875115Sfenner#include <stdio.h> 3975115Sfenner#include <stdlib.h> 4075115Sfenner#include <string.h> 4175115Sfenner#include <unistd.h> 4275115Sfenner 4375115Sfenner#if defined(__FreeBSD_version) 4475115Sfenner#if __FreeBSD_version < 500000 4575115Sfenner#define NEED_SLASHTERM 4675115Sfenner#endif /* < 500000 */ 4775115Sfenner#else /* defined(__FreeBSD_version) */ 4875115Sfenner/* just in case.. */ 4975115Sfenner#define NEED_SLASHTERM 5075115Sfenner#endif /* defined(__FreeBSD_version) */ 5175115Sfenner 5275115Sfenner/* the default sysctl name */ 5375115Sfenner#define PATHCTL "kern.module_path" 5475115Sfenner 5575115Sfenner/* queue structure for the module path broken down into components */ 5675115SfennerTAILQ_HEAD(pathhead, pathentry); 5775115Sfennerstruct pathentry { 5875115Sfenner char *path; 5975115Sfenner TAILQ_ENTRY(pathentry) next; 6075115Sfenner}; 6175115Sfenner 6275115Sfenner/* the Management Information Base entries for the search path sysctl */ 6375115Sfennerstatic int mib[5]; 6475115Sfennerstatic size_t miblen; 6575115Sfenner/* the sysctl name, defaults to PATHCTL */ 6675115Sfennerstatic char *pathctl; 6775115Sfenner/* the sysctl value - the current module search path */ 6875115Sfennerstatic char *modpath; 6975115Sfenner/* flag whether user actions require changing the sysctl value */ 7075115Sfennerstatic int changed; 7175115Sfenner 7275115Sfenner/* Top-level path management functions */ 7375115Sfennerstatic void addpath(struct pathhead *, char *, int, int); 7475115Sfennerstatic void rempath(struct pathhead *, char *, int, int); 7575115Sfennerstatic void showpath(struct pathhead *); 7675115Sfenner 7775115Sfenner/* Low-level path management functions */ 7875115Sfennerstatic char *qstring(struct pathhead *); 7975115Sfenner 8075115Sfenner/* sysctl-related functions */ 8175115Sfennerstatic void getmib(void); 8275115Sfennerstatic void getpath(void); 8375115Sfennerstatic void parsepath(struct pathhead *, char *, int); 8475115Sfennerstatic void setpath(struct pathhead *); 8575115Sfenner 8675115Sfennerstatic void usage(void); 8775115Sfenner 8875115Sfenner/* Get the MIB entry for our sysctl */ 8975115Sfennerstatic void 9075115Sfennergetmib(void) 9175115Sfenner{ 9275115Sfenner 9375115Sfenner /* have we already fetched it? */ 9475115Sfenner if (miblen != 0) 9575115Sfenner return; 9675115Sfenner 9775115Sfenner miblen = sizeof(mib) / sizeof(mib[0]); 9875115Sfenner if (sysctlnametomib(pathctl, mib, &miblen) != 0) 9975115Sfenner err(1, "sysctlnametomib(%s)", pathctl); 10075115Sfenner} 10175115Sfenner 10275115Sfenner/* Get the current module search path */ 10375115Sfennerstatic void 10475115Sfennergetpath(void) 10575115Sfenner{ 10675115Sfenner char *path; 10775115Sfenner size_t sz; 10875115Sfenner 10975115Sfenner if (modpath != NULL) { 11075115Sfenner free(modpath); 11175115Sfenner modpath = NULL; 11275115Sfenner } 11375115Sfenner 11475115Sfenner if (miblen == 0) 11575115Sfenner getmib(); 11675115Sfenner if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1) 11775115Sfenner err(1, "getting path: sysctl(%s) - size only", pathctl); 11875115Sfenner if ((path = malloc(sz + 1)) == NULL) { 11975115Sfenner errno = ENOMEM; 12075115Sfenner err(1, "allocating %lu bytes for the path", 12175115Sfenner (unsigned long)sz+1); 12275115Sfenner } 12375115Sfenner if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1) 12475115Sfenner err(1, "getting path: sysctl(%s)", pathctl); 12575115Sfenner modpath = path; 12675115Sfenner} 12775115Sfenner 12875115Sfenner/* Set the module search path after changing it */ 12975115Sfennerstatic void 13075115Sfennersetpath(struct pathhead *pathq) 13175115Sfenner{ 13275115Sfenner char *newpath; 13375115Sfenner 13475115Sfenner if (miblen == 0) 13575115Sfenner getmib(); 13675115Sfenner if ((newpath = qstring(pathq)) == NULL) { 13775115Sfenner errno = ENOMEM; 13875115Sfenner err(1, "building path string"); 13975115Sfenner } 14075115Sfenner if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1) 14175115Sfenner err(1, "setting path: sysctl(%s)", pathctl); 14275115Sfenner 14375115Sfenner if (modpath != NULL) 14475115Sfenner free(modpath); 14575115Sfenner modpath = newpath; 14675115Sfenner} 14775115Sfenner 14875115Sfenner/* Add/insert a new component to the module search path */ 14975115Sfennerstatic void 15075115Sfenneraddpath(struct pathhead *pathq, char *path, int force, int insert) 15175115Sfenner{ 15275115Sfenner struct pathentry *pe, *pskip; 15375115Sfenner char pathbuf[MAXPATHLEN+1]; 15475115Sfenner size_t len; 15575115Sfenner static unsigned added = 0; 15675115Sfenner unsigned i; 15775115Sfenner 15875115Sfenner /* 15975115Sfenner * If the path exists, use it; otherwise, take the user-specified 16075115Sfenner * path at face value - may be a removed directory. 16175115Sfenner */ 16275115Sfenner if (realpath(path, pathbuf) == NULL) 16375115Sfenner strlcpy(pathbuf, path, sizeof(pathbuf)); 16475115Sfenner 16575115Sfenner len = strlen(pathbuf); 16675115Sfenner#ifdef NEED_SLASHTERM 16775115Sfenner /* slash-terminate, because the kernel linker said so. */ 16875115Sfenner if ((len == 0) || (pathbuf[len-1] != '/')) { 16975115Sfenner if (len == sizeof(pathbuf) - 1) 17075115Sfenner errx(1, "path too long: %s", pathbuf); 17175115Sfenner pathbuf[len] = '/'; 17275115Sfenner } 17375115Sfenner#else /* NEED_SLASHTERM */ 17475115Sfenner /* remove a terminating slash if present */ 17575115Sfenner if ((len > 0) && (pathbuf[len-1] == '/')) 17675115Sfenner pathbuf[--len] = '\0'; 17775115Sfenner#endif /* NEED_SLASHTERM */ 17875115Sfenner 17975115Sfenner /* is it already in there? */ 18075115Sfenner TAILQ_FOREACH(pe, pathq, next) 18175115Sfenner if (!strcmp(pe->path, pathbuf)) 18275115Sfenner break; 18375115Sfenner if (pe != NULL) { 18475115Sfenner if (force) 18575115Sfenner return; 18675115Sfenner errx(1, "already in the module search path: %s", pathbuf); 18775115Sfenner } 18875115Sfenner 18975115Sfenner /* OK, allocate and add it. */ 19075115Sfenner if (((pe = malloc(sizeof(*pe))) == NULL) || 19175115Sfenner ((pe->path = strdup(pathbuf)) == NULL)) { 19275115Sfenner errno = ENOMEM; 19375115Sfenner err(1, "allocating path component"); 19475115Sfenner } 19575115Sfenner if (!insert) { 196 TAILQ_INSERT_TAIL(pathq, pe, next); 197 } else { 198 for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++) 199 pskip = TAILQ_NEXT(pskip, next); 200 if (pskip != NULL) 201 TAILQ_INSERT_BEFORE(pskip, pe, next); 202 else 203 TAILQ_INSERT_TAIL(pathq, pe, next); 204 added++; 205 } 206 changed = 1; 207} 208 209/* Remove a path component from the module search path */ 210static void 211rempath(struct pathhead *pathq, char *path, int force, int insert __unused) 212{ 213 char pathbuf[MAXPATHLEN+1]; 214 struct pathentry *pe; 215 size_t len; 216 217 /* same logic as in addpath() */ 218 if (realpath(path, pathbuf) == NULL) 219 strlcpy(pathbuf, path, sizeof(pathbuf)); 220 221 len = strlen(pathbuf); 222#ifdef NEED_SLASHTERM 223 /* slash-terminate, because the kernel linker said so. */ 224 if ((len == 0) || (pathbuf[len-1] != '/')) { 225 if (len == sizeof(pathbuf) - 1) 226 errx(1, "path too long: %s", pathbuf); 227 pathbuf[len] = '/'; 228 } 229#else /* NEED_SLASHTERM */ 230 /* remove a terminating slash if present */ 231 if ((len > 0) && (pathbuf[len-1] == '/')) 232 pathbuf[--len] = '\0'; 233#endif /* NEED_SLASHTERM */ 234 235 /* Is it in there? */ 236 TAILQ_FOREACH(pe, pathq, next) 237 if (!strcmp(pe->path, pathbuf)) 238 break; 239 if (pe == NULL) { 240 if (force) 241 return; 242 errx(1, "not in module search path: %s", pathbuf); 243 } 244 245 /* OK, remove it now.. */ 246 TAILQ_REMOVE(pathq, pe, next); 247 changed = 1; 248} 249 250/* Display the retrieved module search path */ 251static void 252showpath(struct pathhead *pathq) 253{ 254 char *s; 255 256 if ((s = qstring(pathq)) == NULL) { 257 errno = ENOMEM; 258 err(1, "building path string"); 259 } 260 printf("%s\n", s); 261 free(s); 262} 263 264/* Break a string down into path components, store them into a queue */ 265static void 266parsepath(struct pathhead *pathq, char *path, int uniq) 267{ 268 char *p; 269 struct pathentry *pe; 270 271 while ((p = strsep(&path, ";")) != NULL) 272 if (!uniq) { 273 if (((pe = malloc(sizeof(*pe))) == NULL) || 274 ((pe->path = strdup(p)) == NULL)) { 275 errno = ENOMEM; 276 err(1, "allocating path element"); 277 } 278 TAILQ_INSERT_TAIL(pathq, pe, next); 279 } else { 280 addpath(pathq, p, 1, 0); 281 } 282} 283 284/* Recreate a path string from a components queue */ 285static char * 286qstring(struct pathhead *pathq) 287{ 288 char *s, *p; 289 struct pathentry *pe; 290 291 s = strdup(""); 292 TAILQ_FOREACH(pe, pathq, next) { 293 asprintf(&p, "%s%s%s", 294 s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": "")); 295 free(s); 296 if (p == NULL) 297 return (NULL); 298 s = p; 299 } 300 301 return (s); 302} 303 304/* Usage message */ 305static void 306usage(void) 307{ 308 309 fprintf(stderr, "%s\n%s\n", 310 "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path ...]", 311 "\tkldconfig -r"); 312 exit(1); 313} 314 315/* Main function */ 316int 317main(int argc, char *argv[]) 318{ 319 /* getopt() iterator */ 320 int c; 321 /* iterator over argv[] path components */ 322 int i; 323 /* Command-line flags: */ 324 /* "-f" - no diagnostic messages */ 325 int fflag; 326 /* "-i" - insert before the first element */ 327 int iflag; 328 /* "-m" - merge into the existing path, do not replace it */ 329 int mflag; 330 /* "-n" - do not actually set the new module path */ 331 int nflag; 332 /* "-r" - print out the current search path */ 333 int rflag; 334 /* "-U" - remove duplicate values from the path */ 335 int uniqflag; 336 /* "-v" - verbose operation (currently a no-op) */ 337 int vflag; 338 /* The higher-level function to call - add/remove */ 339 void (*act)(struct pathhead *, char *, int, int); 340 /* The original path */ 341 char *origpath; 342 /* The module search path broken down into components */ 343 struct pathhead pathq; 344 345 fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0; 346 act = addpath; 347 origpath = NULL; 348 if ((pathctl = strdup(PATHCTL)) == NULL) { 349 /* this is just too paranoid ;) */ 350 errno = ENOMEM; 351 err(1, "initializing sysctl name %s", PATHCTL); 352 } 353 354 /* If no arguments and no options are specified, force '-m' */ 355 if (argc == 1) 356 mflag = 1; 357 358 while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1) 359 switch (c) { 360 case 'd': 361 if (iflag || mflag) 362 usage(); 363 act = rempath; 364 break; 365 case 'f': 366 fflag = 1; 367 break; 368 case 'i': 369 if (act != addpath) 370 usage(); 371 iflag = 1; 372 break; 373 case 'm': 374 if (act != addpath) 375 usage(); 376 mflag = 1; 377 break; 378 case 'n': 379 nflag = 1; 380 break; 381 case 'r': 382 rflag = 1; 383 break; 384 case 'S': 385 free(pathctl); 386 if ((pathctl = strdup(optarg)) == NULL) { 387 errno = ENOMEM; 388 err(1, "sysctl name %s", optarg); 389 } 390 break; 391 case 'U': 392 uniqflag = 1; 393 break; 394 case 'v': 395 vflag++; 396 break; 397 default: 398 usage(); 399 } 400 401 argc -= optind; 402 argv += optind; 403 404 /* The '-r' flag cannot be used when paths are also specified */ 405 if (rflag && (argc > 0)) 406 usage(); 407 408 TAILQ_INIT(&pathq); 409 410 /* Retrieve and store the path from the sysctl value */ 411 getpath(); 412 if ((origpath = strdup(modpath)) == NULL) { 413 errno = ENOMEM; 414 err(1, "saving the original search path"); 415 } 416 417 /* 418 * Break down the path into the components queue if: 419 * - we are NOT adding paths, OR 420 * - the 'merge' flag is specified, OR 421 * - the 'print only' flag is specified, OR 422 * - the 'unique' flag is specified. 423 */ 424 if ((act != addpath) || mflag || rflag || uniqflag) 425 parsepath(&pathq, modpath, uniqflag); 426 else if (modpath[0] != '\0') 427 changed = 1; 428 429 /* Process the path arguments */ 430 for (i = 0; i < argc; i++) 431 act(&pathq, argv[i], fflag, iflag); 432 433 if (changed && !nflag) 434 setpath(&pathq); 435 436 if (rflag || (changed && vflag)) { 437 if (changed && (vflag > 1)) 438 printf("%s -> ", origpath); 439 showpath(&pathq); 440 } 441 442 return (0); 443} 444