178977Sroam/* 278977Sroam * Copyright (c) 2001 Peter Pentchev 378977Sroam * All rights reserved. 478977Sroam * 578977Sroam * Redistribution and use in source and binary forms, with or without 678977Sroam * modification, are permitted provided that the following conditions 778977Sroam * are met: 878977Sroam * 1. Redistributions of source code must retain the above copyright 978977Sroam * notice, this list of conditions and the following disclaimer. 1078977Sroam * 2. Redistributions in binary form must reproduce the above copyright 1178977Sroam * notice, this list of conditions and the following disclaimer in the 1278977Sroam * documentation and/or other materials provided with the distribution. 1378977Sroam * 1478977Sroam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1578977Sroam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1678977Sroam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1778977Sroam * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1878977Sroam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1978977Sroam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2078977Sroam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2178977Sroam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2278977Sroam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2378977Sroam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2478977Sroam * SUCH DAMAGE. 2578977Sroam */ 2678977Sroam 27114589Sobrien#include <sys/cdefs.h> 28114589Sobrien__FBSDID("$FreeBSD$"); 2978977Sroam 3078977Sroam#include <sys/param.h> 3178977Sroam#include <sys/types.h> 3278977Sroam#include <sys/queue.h> 3378977Sroam#include <sys/sysctl.h> 3478977Sroam 3578977Sroam#include <err.h> 3678977Sroam#include <errno.h> 3778977Sroam#include <limits.h> 3878977Sroam#include <stdio.h> 3978977Sroam#include <stdlib.h> 4078977Sroam#include <string.h> 4178977Sroam#include <unistd.h> 4278977Sroam 4378977Sroam#if defined(__FreeBSD_version) 4478977Sroam#if __FreeBSD_version < 500000 4578977Sroam#define NEED_SLASHTERM 4678977Sroam#endif /* < 500000 */ 4778977Sroam#else /* defined(__FreeBSD_version) */ 4878977Sroam/* just in case.. */ 4978977Sroam#define NEED_SLASHTERM 5078977Sroam#endif /* defined(__FreeBSD_version) */ 5178977Sroam 5278977Sroam/* the default sysctl name */ 5378977Sroam#define PATHCTL "kern.module_path" 5478977Sroam 5578977Sroam/* queue structure for the module path broken down into components */ 5678977SroamTAILQ_HEAD(pathhead, pathentry); 5778977Sroamstruct pathentry { 5878977Sroam char *path; 5978977Sroam TAILQ_ENTRY(pathentry) next; 6078977Sroam}; 6178977Sroam 6278977Sroam/* the Management Information Base entries for the search path sysctl */ 6378977Sroamstatic int mib[5]; 6478977Sroamstatic size_t miblen; 6578977Sroam/* the sysctl name, defaults to PATHCTL */ 6678977Sroamstatic char *pathctl; 6778977Sroam/* the sysctl value - the current module search path */ 6878977Sroamstatic char *modpath; 6978977Sroam/* flag whether user actions require changing the sysctl value */ 7078977Sroamstatic int changed; 7178977Sroam 7278977Sroam/* Top-level path management functions */ 7378977Sroamstatic void addpath(struct pathhead *, char *, int, int); 7478977Sroamstatic void rempath(struct pathhead *, char *, int, int); 7578977Sroamstatic void showpath(struct pathhead *); 7678977Sroam 7778977Sroam/* Low-level path management functions */ 7878977Sroamstatic char *qstring(struct pathhead *); 7978977Sroam 8078977Sroam/* sysctl-related functions */ 8178977Sroamstatic void getmib(void); 8278977Sroamstatic void getpath(void); 8378977Sroamstatic void parsepath(struct pathhead *, char *, int); 8478977Sroamstatic void setpath(struct pathhead *); 8578977Sroam 8678977Sroamstatic void usage(void); 8778977Sroam 8878977Sroam/* Get the MIB entry for our sysctl */ 8978977Sroamstatic void 9078977Sroamgetmib(void) 9178977Sroam{ 9278977Sroam 9378977Sroam /* have we already fetched it? */ 9478977Sroam if (miblen != 0) 9578977Sroam return; 9678977Sroam 9778977Sroam miblen = sizeof(mib) / sizeof(mib[0]); 9878977Sroam if (sysctlnametomib(pathctl, mib, &miblen) != 0) 9978977Sroam err(1, "sysctlnametomib(%s)", pathctl); 10078977Sroam} 10178977Sroam 10278977Sroam/* Get the current module search path */ 10378977Sroamstatic void 10478977Sroamgetpath(void) 10578977Sroam{ 10678977Sroam char *path; 10778977Sroam size_t sz; 10878977Sroam 10978977Sroam if (modpath != NULL) { 11078977Sroam free(modpath); 11178977Sroam modpath = NULL; 11278977Sroam } 11378977Sroam 11478977Sroam if (miblen == 0) 11578977Sroam getmib(); 116126643Smarkm if (sysctl(mib, miblen, NULL, &sz, NULL, 0) == -1) 11778977Sroam err(1, "getting path: sysctl(%s) - size only", pathctl); 11878977Sroam if ((path = malloc(sz + 1)) == NULL) { 11978977Sroam errno = ENOMEM; 12079002Sroam err(1, "allocating %lu bytes for the path", 12179002Sroam (unsigned long)sz+1); 12278977Sroam } 123126643Smarkm if (sysctl(mib, miblen, path, &sz, NULL, 0) == -1) 12478977Sroam err(1, "getting path: sysctl(%s)", pathctl); 12578977Sroam modpath = path; 12678977Sroam} 12778977Sroam 12878977Sroam/* Set the module search path after changing it */ 12978977Sroamstatic void 13078977Sroamsetpath(struct pathhead *pathq) 13178977Sroam{ 13278977Sroam char *newpath; 13378977Sroam 13478977Sroam if (miblen == 0) 13578977Sroam getmib(); 13678977Sroam if ((newpath = qstring(pathq)) == NULL) { 13778977Sroam errno = ENOMEM; 13878977Sroam err(1, "building path string"); 13978977Sroam } 14078977Sroam if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1) 14178977Sroam err(1, "setting path: sysctl(%s)", pathctl); 14278977Sroam 143113936Sjohan if (modpath != NULL) 14478977Sroam free(modpath); 14578977Sroam modpath = newpath; 14678977Sroam} 14778977Sroam 14878977Sroam/* Add/insert a new component to the module search path */ 14978977Sroamstatic void 15078977Sroamaddpath(struct pathhead *pathq, char *path, int force, int insert) 15178977Sroam{ 15278977Sroam struct pathentry *pe, *pskip; 15378977Sroam char pathbuf[MAXPATHLEN+1]; 15478977Sroam size_t len; 15578977Sroam static unsigned added = 0; 15678977Sroam unsigned i; 15778977Sroam 15878977Sroam /* 15978977Sroam * If the path exists, use it; otherwise, take the user-specified 16078977Sroam * path at face value - may be a removed directory. 16178977Sroam */ 16278977Sroam if (realpath(path, pathbuf) == NULL) 16378977Sroam strlcpy(pathbuf, path, sizeof(pathbuf)); 16478977Sroam 16578977Sroam len = strlen(pathbuf); 16678977Sroam#ifdef NEED_SLASHTERM 16778977Sroam /* slash-terminate, because the kernel linker said so. */ 16878977Sroam if ((len == 0) || (pathbuf[len-1] != '/')) { 16978977Sroam if (len == sizeof(pathbuf) - 1) 17078977Sroam errx(1, "path too long: %s", pathbuf); 17178977Sroam pathbuf[len] = '/'; 17278977Sroam } 17378977Sroam#else /* NEED_SLASHTERM */ 17478977Sroam /* remove a terminating slash if present */ 17578977Sroam if ((len > 0) && (pathbuf[len-1] == '/')) 17678977Sroam pathbuf[--len] = '\0'; 17778977Sroam#endif /* NEED_SLASHTERM */ 17878977Sroam 17978977Sroam /* is it already in there? */ 18078977Sroam TAILQ_FOREACH(pe, pathq, next) 18178977Sroam if (!strcmp(pe->path, pathbuf)) 18278977Sroam break; 18378977Sroam if (pe != NULL) { 18478977Sroam if (force) 18578977Sroam return; 18678977Sroam errx(1, "already in the module search path: %s", pathbuf); 18778977Sroam } 18878977Sroam 18978977Sroam /* OK, allocate and add it. */ 19078977Sroam if (((pe = malloc(sizeof(*pe))) == NULL) || 19178977Sroam ((pe->path = strdup(pathbuf)) == NULL)) { 19278977Sroam errno = ENOMEM; 19378977Sroam err(1, "allocating path component"); 19478977Sroam } 19578977Sroam if (!insert) { 19678977Sroam TAILQ_INSERT_TAIL(pathq, pe, next); 19778977Sroam } else { 19878977Sroam for (i = 0, pskip = TAILQ_FIRST(pathq); i < added; i++) 19978977Sroam pskip = TAILQ_NEXT(pskip, next); 20078977Sroam if (pskip != NULL) 20178977Sroam TAILQ_INSERT_BEFORE(pskip, pe, next); 20278977Sroam else 20378977Sroam TAILQ_INSERT_TAIL(pathq, pe, next); 20478977Sroam added++; 20578977Sroam } 20678977Sroam changed = 1; 20778977Sroam} 20878977Sroam 20978977Sroam/* Remove a path component from the module search path */ 21078977Sroamstatic void 21178977Sroamrempath(struct pathhead *pathq, char *path, int force, int insert __unused) 21278977Sroam{ 21378977Sroam char pathbuf[MAXPATHLEN+1]; 21478977Sroam struct pathentry *pe; 21578977Sroam size_t len; 21678977Sroam 21778977Sroam /* same logic as in addpath() */ 21878977Sroam if (realpath(path, pathbuf) == NULL) 21978977Sroam strlcpy(pathbuf, path, sizeof(pathbuf)); 22078977Sroam 22178977Sroam len = strlen(pathbuf); 22278977Sroam#ifdef NEED_SLASHTERM 22378977Sroam /* slash-terminate, because the kernel linker said so. */ 22478977Sroam if ((len == 0) || (pathbuf[len-1] != '/')) { 22578977Sroam if (len == sizeof(pathbuf) - 1) 22678977Sroam errx(1, "path too long: %s", pathbuf); 22778977Sroam pathbuf[len] = '/'; 22878977Sroam } 22978977Sroam#else /* NEED_SLASHTERM */ 23078977Sroam /* remove a terminating slash if present */ 23178977Sroam if ((len > 0) && (pathbuf[len-1] == '/')) 23278977Sroam pathbuf[--len] = '\0'; 23378977Sroam#endif /* NEED_SLASHTERM */ 23478977Sroam 23578977Sroam /* Is it in there? */ 23678977Sroam TAILQ_FOREACH(pe, pathq, next) 23778977Sroam if (!strcmp(pe->path, pathbuf)) 23878977Sroam break; 23978977Sroam if (pe == NULL) { 24078977Sroam if (force) 24178977Sroam return; 24278977Sroam errx(1, "not in module search path: %s", pathbuf); 24378977Sroam } 24478977Sroam 24578977Sroam /* OK, remove it now.. */ 24678977Sroam TAILQ_REMOVE(pathq, pe, next); 24778977Sroam changed = 1; 24878977Sroam} 24978977Sroam 25078977Sroam/* Display the retrieved module search path */ 25178977Sroamstatic void 25278977Sroamshowpath(struct pathhead *pathq) 25378977Sroam{ 25478977Sroam char *s; 25578977Sroam 25678977Sroam if ((s = qstring(pathq)) == NULL) { 25778977Sroam errno = ENOMEM; 25878977Sroam err(1, "building path string"); 25978977Sroam } 26078977Sroam printf("%s\n", s); 26178977Sroam free(s); 26278977Sroam} 26378977Sroam 26478977Sroam/* Break a string down into path components, store them into a queue */ 26578977Sroamstatic void 26678977Sroamparsepath(struct pathhead *pathq, char *path, int uniq) 26778977Sroam{ 26878977Sroam char *p; 26978977Sroam struct pathentry *pe; 27078977Sroam 27178977Sroam while ((p = strsep(&path, ";")) != NULL) 27278977Sroam if (!uniq) { 273152169Sru if (((pe = malloc(sizeof(*pe))) == NULL) || 27478977Sroam ((pe->path = strdup(p)) == NULL)) { 27578977Sroam errno = ENOMEM; 27678977Sroam err(1, "allocating path element"); 27778977Sroam } 27878977Sroam TAILQ_INSERT_TAIL(pathq, pe, next); 27978977Sroam } else { 28078977Sroam addpath(pathq, p, 1, 0); 28178977Sroam } 28278977Sroam} 28378977Sroam 28478977Sroam/* Recreate a path string from a components queue */ 28578977Sroamstatic char * 28678977Sroamqstring(struct pathhead *pathq) 28778977Sroam{ 28878977Sroam char *s, *p; 28978977Sroam struct pathentry *pe; 29078977Sroam 29178977Sroam s = strdup(""); 29278977Sroam TAILQ_FOREACH(pe, pathq, next) { 29378977Sroam asprintf(&p, "%s%s%s", 29478977Sroam s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": "")); 29578977Sroam free(s); 29678977Sroam if (p == NULL) 29778977Sroam return (NULL); 29878977Sroam s = p; 29978977Sroam } 30078977Sroam 30178977Sroam return (s); 30278977Sroam} 30378977Sroam 30478977Sroam/* Usage message */ 30578977Sroamstatic void 30678977Sroamusage(void) 30778977Sroam{ 30878977Sroam 30978977Sroam fprintf(stderr, "%s\n%s\n", 310141611Sru "usage:\tkldconfig [-dfimnUv] [-S sysctlname] [path ...]", 31178977Sroam "\tkldconfig -r"); 31278977Sroam exit(1); 31378977Sroam} 31478977Sroam 31578977Sroam/* Main function */ 31678977Sroamint 31778977Sroammain(int argc, char *argv[]) 31878977Sroam{ 31978977Sroam /* getopt() iterator */ 32078977Sroam int c; 32178977Sroam /* iterator over argv[] path components */ 32278977Sroam int i; 32378977Sroam /* Command-line flags: */ 32478977Sroam /* "-f" - no diagnostic messages */ 32578977Sroam int fflag; 32678977Sroam /* "-i" - insert before the first element */ 32778977Sroam int iflag; 32878977Sroam /* "-m" - merge into the existing path, do not replace it */ 32978977Sroam int mflag; 33078977Sroam /* "-n" - do not actually set the new module path */ 33178977Sroam int nflag; 33278977Sroam /* "-r" - print out the current search path */ 33378977Sroam int rflag; 33478977Sroam /* "-U" - remove duplicate values from the path */ 33578977Sroam int uniqflag; 33678977Sroam /* "-v" - verbose operation (currently a no-op) */ 33778977Sroam int vflag; 33878977Sroam /* The higher-level function to call - add/remove */ 33978977Sroam void (*act)(struct pathhead *, char *, int, int); 34078977Sroam /* The original path */ 34178977Sroam char *origpath; 34278977Sroam /* The module search path broken down into components */ 34378977Sroam struct pathhead pathq; 34478977Sroam 34578977Sroam fflag = iflag = mflag = nflag = rflag = uniqflag = vflag = 0; 34678977Sroam act = addpath; 34778977Sroam origpath = NULL; 34878977Sroam if ((pathctl = strdup(PATHCTL)) == NULL) { 34978977Sroam /* this is just too paranoid ;) */ 35078977Sroam errno = ENOMEM; 35178977Sroam err(1, "initializing sysctl name %s", PATHCTL); 35278977Sroam } 35378977Sroam 35478977Sroam /* If no arguments and no options are specified, force '-m' */ 35578977Sroam if (argc == 1) 35678977Sroam mflag = 1; 35778977Sroam 35878977Sroam while ((c = getopt(argc, argv, "dfimnrS:Uv")) != -1) 35978977Sroam switch (c) { 36078977Sroam case 'd': 36178977Sroam if (iflag || mflag) 36278977Sroam usage(); 36378977Sroam act = rempath; 36478977Sroam break; 36578977Sroam case 'f': 36678977Sroam fflag = 1; 36778977Sroam break; 36878977Sroam case 'i': 36978977Sroam if (act != addpath) 37078977Sroam usage(); 37178977Sroam iflag = 1; 37278977Sroam break; 37378977Sroam case 'm': 37478977Sroam if (act != addpath) 37578977Sroam usage(); 37678977Sroam mflag = 1; 37778977Sroam break; 37878977Sroam case 'n': 37978977Sroam nflag = 1; 38078977Sroam break; 38178977Sroam case 'r': 38278977Sroam rflag = 1; 38378977Sroam break; 38478977Sroam case 'S': 38578977Sroam free(pathctl); 38678977Sroam if ((pathctl = strdup(optarg)) == NULL) { 38778977Sroam errno = ENOMEM; 38878977Sroam err(1, "sysctl name %s", optarg); 38978977Sroam } 39078977Sroam break; 39178977Sroam case 'U': 39278977Sroam uniqflag = 1; 39378977Sroam break; 39478977Sroam case 'v': 39578977Sroam vflag++; 39678977Sroam break; 39778977Sroam default: 39878977Sroam usage(); 39978977Sroam } 40078977Sroam 40178977Sroam argc -= optind; 40278977Sroam argv += optind; 40378977Sroam 40478977Sroam /* The '-r' flag cannot be used when paths are also specified */ 40578977Sroam if (rflag && (argc > 0)) 40678977Sroam usage(); 40778977Sroam 40878977Sroam TAILQ_INIT(&pathq); 40978977Sroam 41078977Sroam /* Retrieve and store the path from the sysctl value */ 41178977Sroam getpath(); 41278977Sroam if ((origpath = strdup(modpath)) == NULL) { 41378977Sroam errno = ENOMEM; 41478977Sroam err(1, "saving the original search path"); 41578977Sroam } 41678977Sroam 41778977Sroam /* 41878977Sroam * Break down the path into the components queue if: 41978977Sroam * - we are NOT adding paths, OR 42078977Sroam * - the 'merge' flag is specified, OR 42178977Sroam * - the 'print only' flag is specified, OR 42278977Sroam * - the 'unique' flag is specified. 42378977Sroam */ 42478977Sroam if ((act != addpath) || mflag || rflag || uniqflag) 42578977Sroam parsepath(&pathq, modpath, uniqflag); 42678977Sroam else if (modpath[0] != '\0') 42778977Sroam changed = 1; 42878977Sroam 42978977Sroam /* Process the path arguments */ 43078977Sroam for (i = 0; i < argc; i++) 43178977Sroam act(&pathq, argv[i], fflag, iflag); 43278977Sroam 43378977Sroam if (changed && !nflag) 43478977Sroam setpath(&pathq); 43578977Sroam 43678977Sroam if (rflag || (changed && vflag)) { 43778977Sroam if (changed && (vflag > 1)) 43878977Sroam printf("%s -> ", origpath); 43978977Sroam showpath(&pathq); 44078977Sroam } 44178977Sroam 44278977Sroam return (0); 44378977Sroam} 444