X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=modutils%2Fmodprobe.c;h=07cbb6fc78996b28125ebf6013fdf3c16abaae7b;hb=9c91e4142d5bbc74a0c4453055537931c1274757;hp=a510f69e8a1eda9a9583ab7b27f333fbfb250b18;hpb=1d4ef2a9e9c8d8b448bcce7dea36220e220e137b;p=oweals%2Fbusybox.git diff --git a/modutils/modprobe.c b/modutils/modprobe.c index a510f69e8..07cbb6fc7 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -1,27 +1,41 @@ /* vi: set sw=4 ts=4: */ /* - * really dumb modprobe implementation for busybox - * Copyright (C) 2001 Lineo, davidm@lineo.com + * Modprobe written from scratch for BusyBox * - * dependency specific stuff completly rewritten and - * copyright (c) 2002 by Robert Griebl, griebl@gmx.de + * 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 -#include #include #include #include #include #include #include +#include #include "busybox.h" struct dep_t { char * m_module; + char * m_options; int m_isalias : 1; int m_reserved : 15; @@ -34,6 +48,7 @@ struct dep_t { struct mod_list_t { char * m_module; + char * m_options; struct mod_list_t * m_prev; struct mod_list_t * m_next; @@ -43,14 +58,61 @@ struct mod_list_t { static struct dep_t *depend; static int autoclean, show_only, quiet, do_syslog, verbose; +int parse_tag_value ( char *buffer, char **ptag, char **pvalue ) +{ + char *tag, *value; + + while ( isspace ( *buffer )) + buffer++; + tag = value = buffer; + while ( !isspace ( *value )) + value++; + *value++ = 0; + while ( isspace ( *value )) + value++; + + *ptag = tag; + *pvalue = value; + + return bb_strlen( tag ) && bb_strlen( value ); +} + +/* 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; - FILE *f; struct dep_t *first = 0; struct dep_t *current = 0; - char buffer [4096]; + char buffer[256]; char *filename = buffer; int continuation_line = 0; @@ -58,19 +120,24 @@ static struct dep_t *build_dep ( void ) return 0; // check for buffer overflow in following code - if ( xstrlen ( un. release ) > ( sizeof( buffer ) - 64 )) + if ( bb_strlen ( un.release ) > ( sizeof( buffer ) - 64 )) { return 0; + } strcpy ( filename, "/lib/modules/" ); - strcat ( filename, un. release ); + strcat ( filename, un.release ); strcat ( filename, "/modules.dep" ); - f = fopen ( filename, "r" ); - if ( !f ) - return 0; - - while ( fgets ( buffer, sizeof( buffer), f )) { - int l = xstrlen ( buffer ); + 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] )) { @@ -102,16 +169,17 @@ static struct dep_t *build_dep ( void ) if (( *(col-2) == '.' ) && ( *(col-1) == 'o' )) ext = 2; - mod = xstrndup ( mods, col - mods - ext ); + mod = bb_xstrndup ( mods, col - mods - ext ); if ( !current ) { - first = current = (struct dep_t *) malloc ( sizeof ( struct dep_t )); + first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); } else { - current-> m_next = (struct dep_t *) malloc ( sizeof ( struct dep_t )); + current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); current = current-> m_next; } current-> m_module = mod; + current-> m_options = 0; current-> m_isalias = 0; current-> m_depcnt = 0; current-> m_deparr = 0; @@ -149,8 +217,12 @@ static struct dep_t *build_dep ( void ) if (( *(end-1) == '.' ) && ( *end == 'o' )) ext = 2; + + /* Cope with blank lines */ + if ((end-deps-ext+1) <= 0) + continue; - dep = xstrndup ( deps, end - deps - ext + 1 ); + dep = bb_xstrndup ( deps, end - deps - ext + 1 ); current-> m_depcnt++; current-> m_deparr = (char **) xrealloc ( current-> m_deparr, sizeof ( char *) * current-> m_depcnt ); @@ -164,62 +236,50 @@ static struct dep_t *build_dep ( void ) else continuation_line = 0; } - fclose ( f ); + close ( fd ); // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) ! - f = fopen ( "/etc/modules.conf", "r" ); - if ( !f ) - f = fopen ( "/etc/conf.modules", "r" ); - if ( f ) { - continuation_line = 0; + if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 ) + if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 ) + return first; - while ( fgets ( buffer, sizeof( buffer), f )) { - int l; - char *p; - - p = strchr ( buffer, '#' ); - if ( p ) - *p = 0; - - l = xstrlen ( buffer ); + continuation_line = 0; + while ( reads ( fd, buffer, sizeof( buffer ))) { + int l; + char *p; - while ( l && isspace ( buffer [l-1] )) { - buffer [l-1] = 0; - l--; - } + p = strchr ( buffer, '#' ); + if ( p ) + *p = 0; - if ( l == 0 ) { - continuation_line = 0; - continue; - } - - if ( !continuation_line ) { - if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) { - char *alias, *mod; + 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; - alias = buffer + 6; - - while ( isspace ( *alias )) - alias++; - mod = alias; - while ( !isspace ( *mod )) - mod++; - *mod = 0; - mod++; - while ( isspace ( *mod )) - mod++; - -// fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", 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 *) malloc ( sizeof ( struct dep_t )); + first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); } else { - current-> m_next = (struct dep_t *) malloc ( sizeof ( struct dep_t )); + current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); current = current-> m_next; } - current-> m_module = xstrdup ( alias ); + current-> m_module = bb_xstrdup ( alias ); current-> m_isalias = 1; if (( strcmp ( alias, "off" ) == 0 ) || ( strcmp ( alias, "null" ) == 0 )) { @@ -229,67 +289,130 @@ static struct dep_t *build_dep ( void ) else { current-> m_depcnt = 1; current-> m_deparr = xmalloc ( 1 * sizeof( char * )); - current-> m_deparr[0] = xstrdup ( mod ); + 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_module, 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_module, dt-> m_options ); + } + } } } - fclose ( f ); } + 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[256]; + + 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 [256]; - int rc = 0; - - if ( !list ) - return 1; + int rc = 1; while ( list ) { - if ( do_insert ) - snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s 2>/dev/null", do_syslog ? "-s" : "", autoclean ? "-k" : "", quiet ? "-q" : "", list-> m_module ); - else - snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s 2>/dev/null", do_syslog ? "-s" : "", list-> m_module ); + if ( do_insert ) { + if (already_loaded (list->m_module) != 1) + snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s", do_syslog ? "-s" : "", autoclean ? "-k" : "", quiet ? "-q" : "", list-> m_module, list-> m_options ? list-> m_options : "" ); + } else { + if (already_loaded (list->m_module) != 0) + snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s", do_syslog ? "-s" : "", list-> m_module ); + } if ( verbose ) printf ( "%s\n", lcmd ); - if ( !show_only ) - rc |= system ( 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 rc; + 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; int lm; // remove .o extension - lm = xstrlen ( mod ); + lm = bb_strlen ( mod ); if (( mod [lm-2] == '.' ) && ( mod [lm-1] == 'o' )) mod [lm-2] = 0; // check dependencies for ( dt = depend; dt; dt = dt-> m_next ) { - if ( strcmp ( dt-> m_module, mod ) == 0 ) + if ( strcmp ( dt-> m_module, mod ) == 0 ) { + opt = dt-> m_options; break; + } } + // resolve alias names - if ( dt && dt-> m_isalias ) { - if ( dt-> m_depcnt == 1 ) - check_dep ( dt-> m_deparr [0], head, tail ); - printf ( "Got alias: %s -> %s\n", mod, dt-> m_deparr [0] ); - - return; + 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_module, dt-> m_deparr [0] ) == 0 ) + break; + } + if ( adt ) { + dt = adt; + mod = dt-> m_module; + if ( !opt ) + opt = dt-> m_options; + } + else + return; + } + else + return; } // search for duplicates @@ -314,6 +437,7 @@ static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t * if ( !find ) { // did not find a duplicate find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t)); find-> m_module = mod; + find-> m_options = opt; } // enqueue at tail @@ -340,29 +464,31 @@ static int mod_insert ( char *mod, int argc, char **argv ) { struct mod_list_t *tail = 0; struct mod_list_t *head = 0; - int rc = 0; + int rc; // get dep list for module mod check_dep ( mod, &head, &tail ); if ( head && tail ) { - int i; - int l = 0; + if ( argc ) { + int i; + int l = 0; - // append module args - l = xstrlen ( head-> m_module ); - for ( i = 0; i < argc; i++ ) - l += ( xstrlen ( argv [i] ) + 1 ); + // append module args + for ( i = 0; i < argc; i++ ) + l += ( bb_strlen ( argv [i] ) + 1 ); - head-> m_module = realloc ( head-> m_module, l + 1 ); + head-> m_options = xrealloc ( head-> m_options, l + 1 ); + head-> m_options [0] = 0; - for ( i = 0; i < argc; i++ ) { - strcat ( head-> m_module, " " ); - strcat ( head-> m_module, argv [i] ); + for ( i = 0; i < argc; i++ ) { + strcat ( head-> m_options, argv [i] ); + strcat ( head-> m_options, " " ); + } } - + // process tail ---> head - rc |= mod_process ( tail, 1 ); + rc = mod_process ( tail, 1 ); } else rc = 1; @@ -370,8 +496,9 @@ static int mod_insert ( char *mod, int argc, char **argv ) return rc; } -static void mod_remove ( char *mod ) +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; @@ -383,7 +510,11 @@ static void mod_remove ( char *mod ) head = tail = &rm_a_dummy; if ( head && tail ) - mod_process ( head, 0 ); // process head ---> tail + rc = mod_process ( head, 0 ); // process head ---> tail + else + rc = 1; + return rc; + } @@ -403,7 +534,7 @@ extern int modprobe_main(int argc, char** argv) break; case 'C': // no config used case 't': // no pattern matching - error_msg_and_die("-t and -C not supported"); + bb_error_msg_and_die("-t and -C not supported"); case 'a': // ignore case 'd': // ignore @@ -428,7 +559,7 @@ extern int modprobe_main(int argc, char** argv) break; case 'V': default: - show_usage(); + bb_show_usage(); break; } } @@ -436,21 +567,29 @@ extern int modprobe_main(int argc, char** argv) depend = build_dep ( ); if ( !depend ) - error_msg_and_die ( "could not parse modules.dep\n" ); + bb_error_msg_and_die ( "could not parse modules.dep\n" ); if (remove_opt) { + int rc = EXIT_SUCCESS; do { - mod_remove ( optind < argc ? argv [optind] : 0 ); + 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 EXIT_SUCCESS; + return rc; } if (optind >= argc) - error_msg_and_die ( "No module or pattern provided\n" ); + 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 mod_insert ( argv [optind], argc - optind - 1, argv + optind + 1 ) ? \ - EXIT_FAILURE : EXIT_SUCCESS; + return EXIT_SUCCESS; }