Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 15 Jun 2001 15:06:39 +0300
From:      Peter Pentchev <roam@orbitel.bg>
To:        audit@FreeBSD.org
Cc:        arch@FreeBSD.org
Subject:   new kldpath(8): display/modify the module search path
Message-ID:  <20010615150639.D94445@ringworld.oblivion.bg>

next in thread | raw e-mail | index | archive | help
Hi,

Attached is a shar of a new kld-family utility, which parses and modifies
the kern.module_path sysctl in a script-friendly way.  It might be useful
in startup/shutdown scripts for programs using more than one module,
or just to allow startup scripts to specify additional module directories
(e.g. /usr/local/libexec/modules, or /usr/local/lib/au88x0).

Sample use:

[root@edge:p0 ~]# mkdir -p /usr/local/libexec/modules
[root@edge:p0 ~]# mv /boot/kernel/accf_data.ko /usr/local/libexec/modules
[root@edge:p0 ~]# mv /boot/kernel/accf_http.ko /usr/local/libexec/modules
[root@edge:p0 ~]# kldpath -p
Module path: /boot/kernel;/boot/modules;/modules
[root@edge:p0 ~]# kldload accf_http.ko
kldload: can't load accf_http.ko: No such file or directory
[root@edge:p0 ~]# kldpath /usr/local/libexec/modules/
Module path: /boot/kernel;/boot/modules;/modules;/usr/local/libexec/modules
[root@edge:p0 ~]# kldload accf_http.ko
[root@edge:p0 ~]# kldpath -r /usr/local/libexec/modules
Module path: /boot/kernel;/boot/modules;/modules
[root@edge:p0 ~]# kldload accf_data.ko
kldload: can't load accf_data.ko: No such file or directory
[root@edge:p0 ~]#

As shown, kldpath strips the trailing slash in the directory name,
as this is the format the kernel linker wants the path in.  There is
an automatic __FreeBSD_version-based detection if the kernel linker
might like the trailing slash present, and in RELENG_4 it is added.
(OK, so 500000 might not be the best signpost for the point at which
the kernel linker became able to deal with paths without kernel slashes,
but I think this would be the slightest nit in anyone's comments/objections
to this new utility :P )

Comments?  Objections?  Flames?  Overwhelming reasons this should *not*
go into base, other than 'it goes against the POLA of making the user
tweak obscure, undocumented sysctl's, instead of using command-line
utilities'? :)  (not that this applies to FreeBSD in particular;
I've found more than enough command-line utilities in FreeBSD to control
all kinds of system behavior.)

(And yes, kern.module_path is indeed undocumented..)

G'luck,
Peter

-- 
This sentence no verb.

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	kldpath
#	kldpath/Makefile
#	kldpath/kldpath.8
#	kldpath/kldpath.c
#
echo c - kldpath
mkdir -p kldpath > /dev/null 2>&1
echo x - kldpath/Makefile
sed 's/^X//' >kldpath/Makefile << 'END-of-kldpath/Makefile'
X#
X# Copyright (c) 2001 Peter Pentchev
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms, with or without
X# modification, are permitted provided that the following conditions
X# are met:
X# 1. Redistributions of source code must retain the above copyright
X#    notice, this list of conditions and the following disclaimer.
X# 2. Redistributions in binary form must reproduce the above copyright
X#    notice, this list of conditions and the following disclaimer in the
X#    documentation and/or other materials provided with the distribution.
X#
X# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X# SUCH DAMAGE.
X#
X# $FreeBSD$
X#
X
XPROG=		kldpath
XMAN8=		kldpath.8
X
XWARNS?=		2
XFORMAT_AUDIT?=	1
X
X.include <bsd.prog.mk>
END-of-kldpath/Makefile
echo x - kldpath/kldpath.8
sed 's/^X//' >kldpath/kldpath.8 << 'END-of-kldpath/kldpath.8'
X.\"
X.\" Copyright (c) 2001 Peter Pentchev
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\"    notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\"    notice, this list of conditions and the following disclaimer in the
X.\"    documentation and/or other materials provided with the distribution.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\" $FreeBSD$
X.\"
X.Dd June 15, 2001
X.Dt KLDPATH 8
X.Os FreeBSD
X.Sh NAME
X.Nm kldpath
X.Nd display or modify the kernel module search path
X.Sh SYNOPSIS
X.Nm
X.Op Fl fipqrv
X.Op Fl S Ar name
X.Op Ar path ..
X.Sh DESCRIPTION
XThe
X.Nm
Xdisplays or modifies the paths used by the kernel when loading modules
Xusing the
X.Xr kldload 8
Xutility or the
X.Xr kldload 2
Xsyscall.
X.Pp
XThe following options are available:
X.Bl -tag -width indent
X.It Fl f
XDo not display a diagnostic message if a path specified for adding does not
Xpoint to an existing directory, or if a path specified for removing is not
Xreally a part of the current module search path.
XThis may be useful in startup/shutdown scripts for adding a path to
Xa filesystem which is still not mounted, or in shutdown scripts for
Xunconditionally removing a path that may have been added during startup.
X.It Fl i
XAdd a path to the beginning of the search path, not to the end.
XThis option can only be used when adding paths.
X.It Fl p
XDisplay the current search path.
XThe
X.Fl p
Xoption overrides any previous
X.Fl q
Xoptions.
X.It Fl q
XSuppress printing the result search path after adding or removing paths.
X.It Fl r
XRemove the specified path(s) from the module search path.
X.It Fl S Ar name
XSpecify the sysctl name to use instead of the default
X.Sy kern.module_path .
X.It Fl v
XBe more verbose.
X.El
X.Sh FILES
X.Bl -tag -width indent
X.It Pa /boot/kernel/ /boot/modules/ /modules/
XThe default module search path used by the kernel.
X.El
X.Sh DIAGNOSTICS
XThe
X.Nm
Xutility exits with a status of 0 on success
Xand with a nonzero status if an error occurs.
X.Sh SEE ALSO
X.Xr kldload 2 ,
X.Xr kldload 8 ,
X.Xr sysctl 8 .
X.Sh HISTORY
XThe
X.Nm
Xcommand first appeared in
X.Fx 5.0 .
X.Sh AUTHORS
X.An Peter Pentchev Aq roam@FreeBSD.org
END-of-kldpath/kldpath.8
echo x - kldpath/kldpath.c
sed 's/^X//' >kldpath/kldpath.c << 'END-of-kldpath/kldpath.c'
X/*
X * Copyright (c) 2001 Peter Pentchev
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X
X#ifndef lint
Xstatic const char rcsid[] =
X  "$FreeBSD$";
X#endif /* not lint */
X
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/queue.h>
X#include <sys/stat.h>
X#include <sys/sysctl.h>
X
X#include <err.h>
X#include <errno.h>
X#include <limits.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <unistd.h>
X
X#if defined(__FreeBSD_version)
X#if __FreeBSD_version < 500000
X#define NEED_SLASHTERM
X#endif /* < 500000 */
X#else  /* defined(__FreeBSD_version) */
X/* just in case.. */
X#define NEED_SLASHTERM
X#endif /* defined(__FreeBSD_version) */
X
X/* the default sysctl name */
X#define PATHCTL	"kern.module_path"
X
X/* queue structure for the module path broken down into components */
XTAILQ_HEAD(pathhead, pathentry);
Xstruct pathentry {
X	char			*path;
X	TAILQ_ENTRY(pathentry)	next;
X};
X
X/* Command-line options */
X/* "-f" - no diag messages */
Xstatic int	 fflag;
X/* "-i" - insert at the start of the path */
Xstatic int	 iflag;
X
X/* the Management Information Base entries for the search path sysctl */
Xstatic int	 mib[5];
Xstatic size_t	 miblen;
X/* the sysctl name, defaults to PATHCTL */
Xstatic char	*pathctl;
X/* the sysctl value - the current module search path */
Xstatic char	*modpath;
X/* flag whether user actions require changing the sysctl value */
Xstatic int	 changed;
X
X/* Top-level path management functions */
Xstatic void	 addpath(struct pathhead *pathq, char *);
Xstatic void	 rempath(struct pathhead *pathq, char *);
Xstatic void	 showpath(struct pathhead *pathq);
X
X/* Low-level path management functions */
Xstatic void	 qclean(struct pathhead *pathq);
Xstatic void	 qadd(struct pathhead *pathq, char *);
Xstatic char	*qstring(struct pathhead *pathq);
X
X/* sysctl-related functions */
Xstatic void	 getmib(void);
Xstatic void	 getpath(struct pathhead *pathq);
Xstatic void	 setpath(struct pathhead *pathq);
X
Xstatic void	 usage(void);
X
X/* Get the MIB entry for our sysctl */
Xstatic void
Xgetmib(void) {
X
X	/* have we already fetched it? */
X	if (miblen != 0)
X		return;
X	
X	miblen = sizeof(mib) / sizeof(mib[0]);
X	if (sysctlnametomib(pathctl, mib, &miblen) != 0)
X		err(1, "sysctlnametomib(%s)", pathctl);
X}
X
X/* Get the current module search path */
Xstatic void
Xgetpath(struct pathhead *pathq) {
X	char *path;
X	size_t sz;
X
X	if (modpath != NULL)
X		return;
X
X	if (miblen == 0)
X		getmib();
X	if (sysctl(mib, miblen, NULL, &sz, NULL, NULL) == -1)
X		err(1, "getting path: sysctl(%s) - size only", pathctl);
X	if ((path = malloc(sz + 1)) == NULL) {
X		errno = ENOMEM;
X		err(1, "allocating %u bytes for the path", sz+1);
X	}
X	if (sysctl(mib, miblen, path, &sz, NULL, NULL) == -1)
X		err(1, "getting path: sysctl(%s)", pathctl);
X	modpath = path;
X
X	qclean(pathq);
X	qadd(pathq, modpath);
X}
X
X/* Set the module search path after changing it */
Xstatic void
Xsetpath(struct pathhead *pathq) {
X	char *newpath;
X
X	if (miblen == 0)
X		getmib();
X	if ((newpath = qstring(pathq)) == NULL) {
X		errno = ENOMEM;
X		err(1, "building path string");
X	}
X	if (sysctl(mib, miblen, NULL, NULL, newpath, strlen(newpath)+1) == -1)
X		err(1, "setting path: sysctl(%s)", pathctl);
X
X	if (modpath)
X		free(modpath);
X	modpath = newpath;
X}
X
X/* Add/insert a new component to the module search path */
Xstatic void
Xaddpath(struct pathhead *pathq, char *path) {
X	struct pathentry *pe;
X	char pathbuf[MAXPATHLEN+1];
X	struct stat sb;
X	size_t len;
X
X	/* is there such a thing? */
X	if (realpath(path, pathbuf) == NULL) {
X		if (!fflag)
X			err(1, "resolving path %s: %s", path, pathbuf);
X		/*
X		 * if -f specified, take the user path at face value
X		 * (may be created later)
X		 */
X		strlcpy(pathbuf, path, sizeof(pathbuf));
X	}
X	if (stat(pathbuf, &sb) == -1) {
X		if (!fflag)
X			err(1, "examining path %s", pathbuf);
X	} else if (!S_ISDIR(sb.st_mode)) {
X		if (!fflag) {
X			errno = ENOTDIR;
X			err(1, "%s", pathbuf);
X		}
X	}
X
X	len = strlen(pathbuf);
X#ifdef NEED_SLASHTERM
X	/* slash-terminate, because the kernel linker said so. */
X	if ((len == 0) || (pathbuf[len-1] != '/')) {
X		if (len == sizeof(pathbuf) - 1)
X			errx(1, "path too long: %s", pathbuf);
X		pathbuf[len] = '/';
X	}
X#else  /* NEED_SLASHTERM */
X	/* remove a terminating slash if present */
X	if ((len > 0) && (pathbuf[len-1] == '/'))
X		pathbuf[--len] = '\0';
X#endif /* NEED_SLASHTERM */
X
X	/* is it already in there? */
X	TAILQ_FOREACH(pe, pathq, next)
X		if (!strcmp(pe->path, pathbuf))
X			break;
X	if (pe != NULL) {
X		if (fflag)
X			return;
X		errx(1, "already in the module search path: %s", pathbuf);
X	}
X	
X	/* OK, allocate and add it. */
X	if (((pe = malloc(sizeof(*pe))) == NULL) ||
X	    ((pe->path = strdup(pathbuf)) == NULL)) {
X		errno = ENOMEM;
X		err(1, "allocating path component");
X	}
X	if (iflag)
X		TAILQ_INSERT_HEAD(pathq, pe, next);
X	else
X		TAILQ_INSERT_TAIL(pathq, pe, next);
X	changed = 1;
X}
X
X/* Remove a path component from the module search path */
Xstatic void
Xrempath(struct pathhead *pathq, char *path) {
X	char pathbuf[MAXPATHLEN+1];
X	struct stat sb;
X	int valid;
X	struct pathentry *pe;
X	size_t len;
X
X	/*
X	 * If the path exists, use it; otherwise, take the user-specified
X	 * path at face value - may be a removed directory.
X	 */
X	valid = 0;
X	if ((realpath(path, pathbuf) != NULL) &&
X	    (stat(pathbuf, &sb) == 0) &&
X	    S_ISDIR(sb.st_mode))
X		valid = 1;
X	if (!valid)
X		strlcpy(pathbuf, path, sizeof(pathbuf));
X
X	len = strlen(pathbuf);
X#ifdef NEED_SLASHTERM
X	/* slash-terminate, because the kernel linker said so. */
X	if ((len == 0) || (pathbuf[len-1] != '/')) {
X		if (len == sizeof(pathbuf) - 1)
X			errx(1, "path too long: %s", pathbuf);
X		pathbuf[len] = '/';
X	}
X#else  /* NEED_SLASHTERM */
X	/* remove a terminating slash if present */
X	if ((len > 0) && (pathbuf[len-1] == '/'))
X		pathbuf[--len] = '\0';
X#endif /* NEED_SLASHTERM */
X
X	/* Is it in there? */
X	TAILQ_FOREACH(pe, pathq, next)
X		if (!strcmp(pe->path, pathbuf))
X			break;
X	if (pe == NULL) {
X		if (fflag)
X			return;
X		errx(1, "not in module search path: %s", pathbuf);
X	}
X
X	/* OK, remove it now.. */
X	TAILQ_REMOVE(pathq, pe, next);
X	changed = 1;
X}
X
X/* Display the retrieved module search path */
Xstatic void
Xshowpath(struct pathhead *pathq) {
X	char *s;
X
X	getpath(pathq);
X	if ((s = qstring(pathq)) == NULL) {
X		errno = ENOMEM;
X		err(1, "building path string");
X	}
X	printf("Module path: %s\n", s);
X	free(s);
X}
X
X/* Remove all queue entries */
Xstatic void
Xqclean(struct pathhead *pathq) {
X	struct pathentry *pe;
X
X	while (!TAILQ_EMPTY(pathq)) {
X		pe = TAILQ_FIRST(pathq);
X		TAILQ_REMOVE(pathq, pe, next);
X		free(pe->path);
X		free(pe);
X	}
X}
X
X/* Break a string down into path components, store them into a queue */
Xstatic void
Xqadd(struct pathhead *pathq, char *s) {
X	char *p;
X	struct pathentry *pe __unused;
X	
X	while ((p = strsep(&s, ";")) != NULL) {
X		if (((pe = malloc(sizeof(pe))) == NULL) ||
X		    ((pe->path = strdup(p)) == NULL)) {
X			errno = ENOMEM;
X			err(1, "allocating path element");
X		}
X		TAILQ_INSERT_TAIL(pathq, pe, next);
X	}
X}
X
X/* Recreate a path string from a components queue */
Xstatic char *
Xqstring(struct pathhead *pathq) {
X	char *s, *p;
X	struct pathentry *pe;
X	
X	s = strdup("");
X	TAILQ_FOREACH(pe, pathq, next) {
X		asprintf(&p, "%s%s%s",
X		    s, pe->path, (TAILQ_NEXT(pe, next) != NULL? ";": ""));
X		free(s);
X		if (p == NULL)
X			return (NULL);
X		s = p;
X	}
X
X	return (s);
X}
X
X/* Usage message */
Xstatic void
Xusage(void) {
X
X	fprintf(stderr, "%s\n",
X	    "usage: kldpath [-fipqrv] [-S sysctlname] [path..]");
X	exit(1);
X}
X
X/* Main function */
Xint
Xmain(int argc, char *argv[]) {
X	/* getopt() iterator */
X	int c;
X	/* iterator over argv[] path components */
X	int i;
X	/* Command-line flags: */
X	/* "-p" - print out the current search path */
X	int pflag;
X	/* "-q" - quiet, do not print out the new path after changing it */
X	int qflag;
X	/* "-v" - verbose operation (currently a no-op) */
X	int vflag;
X	/* The higher-level function to call - add/remove */
X	void (*act)(struct pathhead *, char *);
X	/* The module search path broken down into components */
X	struct pathhead pathq;
X
X	pflag = qflag = vflag = 0;
X	act = addpath;
X	if ((pathctl = strdup(PATHCTL)) == NULL) {
X		/* this is just too paranoid ;) */
X		errno = ENOMEM;
X		err(1, "initializing sysctl name %s", PATHCTL);
X	}
X
X	while ((c = getopt(argc, argv, "fipqrS:v")) != -1)
X		switch (c) {
X			case 'f':
X				fflag = 1;
X				break;
X			case 'i':
X				if (act != addpath)
X					usage();
X				iflag = 1;
X				break;
X			case 'p':
X				pflag = 1;
X				break;
X			case 'q':
X				qflag = 1;
X				break;
X			case 'r':
X				if (iflag)
X					usage();
X				act = rempath;
X				break;
X			case 'S':
X				free(pathctl);
X				if ((pathctl = strdup(optarg)) == NULL) {
X					errno = ENOMEM;
X					err(1, "sysctl name %s", optarg);
X				}
X				break;
X			case 'v':
X				vflag = 1;
X				break;
X			default:
X				usage();
X		}
X
X	argc -= optind;
X	argv += optind;
X
X	TAILQ_INIT(&pathq);
X
X	if (!pflag && (argc == 0))
X		usage();
X
X	getpath(&pathq);
X
X	/* Process the path arguments */
X	for (i = 0; i < argc; i++)
X		act(&pathq, argv[i]);
X
X	if (changed)
X		setpath(&pathq);
X
X	if (pflag || (changed && !qflag))
X		showpath(&pathq);
X
X	return (0);
X}
END-of-kldpath/kldpath.c
exit


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-arch" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010615150639.D94445>