busybox/modutils/modprobe.c
Glenn L McGrath bdf6319d3a Patch from Egor Duda
Attached patch prevents modprobe from trying to call 'insmod (null)'
whenever nonexistent module is either passed to modprobe via command
line or mentioned in modules.dep

this replaces cryptic error
sh: Syntax error: word unexpected (expecting ")")
with
modprobe: module some-module not found.

egor.
2004-09-24 09:18:55 +00:00

655 lines
14 KiB
C

/* vi: set sw=4 ts=4: */
/*
* Modprobe written from scratch for BusyBox
*
* Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
* Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <sys/utsname.h>
#include <getopt.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include "busybox.h"
struct dep_t {
char * m_name;
char * m_path;
char * m_options;
int m_isalias : 1;
int m_reserved : 15;
int m_depcnt : 16;
char ** m_deparr;
struct dep_t * m_next;
};
struct mod_list_t {
char * m_name;
char * m_path;
char * m_options;
struct mod_list_t * m_prev;
struct mod_list_t * m_next;
};
static struct dep_t *depend;
static int autoclean, show_only, quiet, do_syslog, verbose;
static int k_version;
int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
{
char *tag, *value;
while ( isspace ( *buffer ))
buffer++;
tag = value = buffer;
while ( !isspace ( *value ))
if (!*value) return 0;
else value++;
*value++ = 0;
while ( isspace ( *value ))
value++;
if (!*value) return 0;
*ptag = tag;
*pvalue = value;
return 1;
}
/* Jump through hoops to simulate how fgets() grabs just one line at a
* time... Don't use any stdio since modprobe gets called from a kernel
* thread and stdio junk can overflow the limited stack...
*/
static char *reads ( int fd, char *buffer, size_t len )
{
int n = read ( fd, buffer, len );
if ( n > 0 ) {
char *p;
buffer [len-1] = 0;
p = strchr ( buffer, '\n' );
if ( p ) {
off_t offset;
offset = lseek ( fd, 0L, SEEK_CUR ); // Get the current file descriptor offset
lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
p[1] = 0;
}
return buffer;
}
else
return 0;
}
static struct dep_t *build_dep ( void )
{
int fd;
struct utsname un;
struct dep_t *first = 0;
struct dep_t *current = 0;
char buffer[2048];
char *filename = buffer;
int continuation_line = 0;
k_version = 0;
if ( uname ( &un ))
return 0;
// check for buffer overflow in following code
if ( bb_strlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
return 0;
}
if (un.release[0] == '2') {
k_version = un.release[2] - '0';
}
strcpy ( filename, "/lib/modules/" );
strcat ( filename, un.release );
strcat ( filename, "/modules.dep" );
if (( fd = open ( filename, O_RDONLY )) < 0 ) {
/* Ok, that didn't work. Fall back to looking in /lib/modules */
if (( fd = open ( "/lib/modules/modules.dep", O_RDONLY )) < 0 ) {
return 0;
}
}
while ( reads ( fd, buffer, sizeof( buffer ))) {
int l = bb_strlen ( buffer );
char *p = 0;
while ( isspace ( buffer [l-1] )) {
buffer [l-1] = 0;
l--;
}
if ( l == 0 ) {
continuation_line = 0;
continue;
}
if ( !continuation_line ) {
char *col = strchr ( buffer, ':' );
char *dot = col;
if ( col ) {
char *mods;
char *modpath;
char *mod;
*col = 0;
mods = strrchr ( buffer, '/' );
if ( !mods )
mods = buffer;
else
mods++;
modpath = strchr ( buffer, '/' );
if ( !modpath )
modpath = buffer;
#if defined(CONFIG_FEATURE_2_6_MODULES)
if ((k_version > 4) && ( *(col-3) == '.' ) &&
( *(col-2) == 'k' ) && ( *(col-1) == 'o' ))
dot = col - 3;
else
#endif
if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
dot = col - 2;
mod = bb_xstrndup ( mods, dot - mods );
if ( !current ) {
first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
}
else {
current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
current = current-> m_next;
}
current-> m_name = mod;
current-> m_path = bb_xstrdup(modpath);
current-> m_options = 0;
current-> m_isalias = 0;
current-> m_depcnt = 0;
current-> m_deparr = 0;
current-> m_next = 0;
//printf ( "%s:\n", mod );
p = col + 1;
}
else
p = 0;
}
else
p = buffer;
while ( p && *p && isblank(*p))
p++;
if ( p && *p ) {
char *end = &buffer [l-1];
char *deps;
char *dep;
char *next;
int ext = 0;
while ( isblank ( *end ) || ( *end == '\\' ))
end--;
do
{
next = strchr (p, ' ' );
if (next)
{
*next = 0;
next--;
}
else
next = end;
deps = strrchr ( p, '/' );
if ( !deps || ( deps < p )) {
deps = p;
while ( isblank ( *deps ))
deps++;
}
else
deps++;
#if defined(CONFIG_FEATURE_2_6_MODULES)
if ((k_version > 4) && ( *(next-2) == '.' ) && *(next-1) == 'k' &&
( *next == 'o' ))
ext = 3;
else
#endif
if (( *(next-1) == '.' ) && ( *next == 'o' ))
ext = 2;
/* Cope with blank lines */
if ((next-deps-ext+1) <= 0)
continue;
dep = bb_xstrndup ( deps, next - deps - ext + 1 );
current-> m_depcnt++;
current-> m_deparr = (char **) xrealloc ( current-> m_deparr,
sizeof ( char *) * current-> m_depcnt );
current-> m_deparr [current-> m_depcnt - 1] = dep;
//printf ( " %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
p = next + 2;
} while (next < end);
}
if ( buffer [l-1] == '\\' )
continuation_line = 1;
else
continuation_line = 0;
}
close ( fd );
// alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
#if defined(CONFIG_FEATURE_2_6_MODULES)
if (( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 )
#endif
if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
return first;
continuation_line = 0;
while ( reads ( fd, buffer, sizeof( buffer ))) {
int l;
char *p;
p = strchr ( buffer, '#' );
if ( p )
*p = 0;
l = bb_strlen ( buffer );
while ( l && isspace ( buffer [l-1] )) {
buffer [l-1] = 0;
l--;
}
if ( l == 0 ) {
continuation_line = 0;
continue;
}
if ( !continuation_line ) {
if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
char *alias, *mod;
if ( parse_tag_value ( buffer + 6, &alias, &mod )) {
// fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
if ( !current ) {
first = current = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
}
else {
current-> m_next = (struct dep_t *) xcalloc ( 1, sizeof ( struct dep_t ));
current = current-> m_next;
}
current-> m_name = bb_xstrdup ( alias );
current-> m_isalias = 1;
if (( strcmp ( mod, "off" ) == 0 ) || ( strcmp ( mod, "null" ) == 0 )) {
current-> m_depcnt = 0;
current-> m_deparr = 0;
}
else {
current-> m_depcnt = 1;
current-> m_deparr = xmalloc ( 1 * sizeof( char * ));
current-> m_deparr[0] = bb_xstrdup ( mod );
}
current-> m_next = 0;
}
}
else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) {
char *mod, *opt;
if ( parse_tag_value ( buffer + 8, &mod, &opt )) {
struct dep_t *dt;
for ( dt = first; dt; dt = dt-> m_next ) {
if ( strcmp ( dt-> m_name, mod ) == 0 )
break;
}
if ( dt ) {
dt-> m_options = xrealloc ( dt-> m_options, bb_strlen( opt ) + 1 );
strcpy ( dt-> m_options, opt );
// fprintf ( stderr, "OPTION: '%s' -> '%s'\n", dt-> m_name, dt-> m_options );
}
}
}
}
}
close ( fd );
return first;
}
/* return 1 = loaded, 0 = not loaded, -1 = can't tell */
static int already_loaded (const char *name)
{
int fd;
char buffer[4096];
fd = open ("/proc/modules", O_RDONLY);
if (fd < 0)
return -1;
while ( reads ( fd, buffer, sizeof( buffer ))) {
char *p;
p = strchr (buffer, ' ');
if (p) {
*p = 0;
if (strcmp (name, buffer) == 0) {
close (fd);
return 1;
}
}
}
close (fd);
return 0;
}
static int mod_process ( struct mod_list_t *list, int do_insert )
{
char lcmd [4096];
int rc = 0;
while ( list ) {
*lcmd = '\0';
if ( do_insert ) {
if (already_loaded (list->m_name) != 1)
snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s",
do_syslog ? "-s" : "", autoclean ? "-k" : "",
quiet ? "-q" : "", list-> m_path, list-> m_options ?
list-> m_options : "" );
} else {
/* modutils uses short name for removal */
if (already_loaded (list->m_name) != 0)
snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s",
do_syslog ? "-s" : "", list-> m_name );
}
if (*lcmd) {
if (verbose) {
printf("%s\n", lcmd);
}
if (!show_only) {
int rc2 = system(lcmd);
if (do_insert) {
rc = rc2; /* only last module matters */
}
else if (!rc2) {
rc = 0; /* success if remove any mod */
}
}
}
list = do_insert ? list-> m_prev : list-> m_next;
}
return (show_only) ? 0 : rc;
}
static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
{
struct mod_list_t *find;
struct dep_t *dt;
char *opt = 0;
char *path = 0;
// check dependencies
for ( dt = depend; dt; dt = dt-> m_next ) {
if ( strcmp ( dt-> m_name, mod ) == 0) {
mod = dt-> m_name;
path = dt-> m_path;
opt = dt-> m_options;
break;
}
}
// resolve alias names
while ( dt && dt-> m_isalias ) {
if ( dt-> m_depcnt == 1 ) {
struct dep_t *adt;
for ( adt = depend; adt; adt = adt-> m_next ) {
if ( strcmp ( adt-> m_name, dt-> m_deparr [0] ) == 0 )
break;
}
if ( adt ) {
dt = adt;
mod = dt-> m_name;
path = dt-> m_path;
if ( !opt )
opt = dt-> m_options;
}
else
return;
}
else
return;
}
if ( !path ) {
bb_error_msg ("module %s not found.", mod);
return;
}
// search for duplicates
for ( find = *head; find; find = find-> m_next ) {
if ( !strcmp ( mod, find-> m_name )) {
// found -> dequeue it
if ( find-> m_prev )
find-> m_prev-> m_next = find-> m_next;
else
*head = find-> m_next;
if ( find-> m_next )
find-> m_next-> m_prev = find-> m_prev;
else
*tail = find-> m_prev;
break; // there can be only one duplicate
}
}
if ( !find ) { // did not find a duplicate
find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));
find-> m_name = mod;
find-> m_path = path;
find-> m_options = opt;
}
// enqueue at tail
if ( *tail )
(*tail)-> m_next = find;
find-> m_prev = *tail;
find-> m_next = 0;
if ( !*head )
*head = find;
*tail = find;
if ( dt ) {
int i;
for ( i = 0; i < dt-> m_depcnt; i++ )
check_dep ( dt-> m_deparr [i], head, tail );
}
}
static int mod_insert ( char *mod, int argc, char **argv )
{
struct mod_list_t *tail = 0;
struct mod_list_t *head = 0;
int rc;
// get dep list for module mod
check_dep ( mod, &head, &tail );
if ( head && tail ) {
#if defined(CONFIG_FEATURE_2_6_MODULES)
if ( argc ) {
int i;
int l = 0;
// append module args
for ( i = 0; i < argc; i++ )
l += ( bb_strlen ( argv [i] ) + 1 );
head-> m_options = xrealloc ( head-> m_options, l + 1 );
head-> m_options [0] = 0;
for ( i = 0; i < argc; i++ ) {
strcat ( head-> m_options, argv [i] );
strcat ( head-> m_options, " " );
}
}
#endif
// process tail ---> head
rc = mod_process ( tail, 1 );
}
else
rc = 1;
return rc;
}
static int mod_remove ( char *mod )
{
int rc;
static struct mod_list_t rm_a_dummy = { "-a", 0, 0 };
struct mod_list_t *head = 0;
struct mod_list_t *tail = 0;
if ( mod )
check_dep ( mod, &head, &tail );
else // autoclean
head = tail = &rm_a_dummy;
if ( head && tail )
rc = mod_process ( head, 0 ); // process head ---> tail
else
rc = 1;
return rc;
}
extern int modprobe_main(int argc, char** argv)
{
int opt;
int remove_opt = 0;
autoclean = show_only = quiet = do_syslog = verbose = 0;
while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
switch(opt) {
case 'c': // no config used
case 'l': // no pattern matching
return EXIT_SUCCESS;
break;
case 'C': // no config used
case 't': // no pattern matching
bb_error_msg_and_die("-t and -C not supported");
case 'a': // ignore
case 'd': // ignore
break;
case 'k':
autoclean++;
break;
case 'n':
show_only++;
break;
case 'q':
quiet++;
break;
case 'r':
remove_opt++;
break;
case 's':
do_syslog++;
break;
case 'v':
verbose++;
break;
case 'V':
default:
bb_show_usage();
break;
}
}
depend = build_dep ( );
if ( !depend )
bb_error_msg_and_die ( "could not parse modules.dep\n" );
if (remove_opt) {
int rc = EXIT_SUCCESS;
do {
if (mod_remove ( optind < argc ?
bb_xstrdup (argv [optind]) : NULL )) {
bb_error_msg ("failed to remove module %s",
argv [optind] );
rc = EXIT_FAILURE;
}
} while ( ++optind < argc );
return rc;
}
if (optind >= argc)
bb_error_msg_and_die ( "No module or pattern provided\n" );
if ( mod_insert ( bb_xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 ))
bb_error_msg_and_die ( "failed to load module %s", argv [optind] );
return EXIT_SUCCESS;
}