X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=modutils%2Fmodprobe.c;h=59c06ee1ed7bfffd4ca782a8297525b70b08ce4f;hb=e2e56c7c4129de7d20df42e8239fd304c81ef29b;hp=5d6c8f3ce55078520cf4b69e4dfc8ad6effa36c3;hpb=fcf47321d4b121cd2de73c8d29fface50e192ae2;p=oweals%2Fbusybox.git diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 5d6c8f3ce..59c06ee1e 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c @@ -4,24 +4,16 @@ * * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au + * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com * - * 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 + * Portions Copyright (c) 2005 by Yann E. MORIN, yann.morin.1998@anciens.enib.fr * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. */ #include +#include +#include #include #include #include @@ -31,24 +23,31 @@ #include #include "busybox.h" +struct mod_opt_t { /* one-way list of options to pass to a module */ + char * m_opt_val; + struct mod_opt_t * m_next; +}; +struct dep_t { /* one-way list of dependency rules */ + /* a dependency rule */ + char * m_name; /* the module name*/ + char * m_path; /* the module file path */ + struct mod_opt_t * m_options; /* the module options */ -struct dep_t { - char * m_module; - char * m_options; - - int m_isalias : 1; - int m_reserved : 15; + int m_isalias : 1; /* the module is an alias */ + int m_reserved : 15; /* stuffin' */ - int m_depcnt : 16; - char ** m_deparr; + int m_depcnt : 16; /* the number of dependable module(s) */ + char ** m_deparr; /* the list of dependable module(s) */ - struct dep_t * m_next; + struct dep_t * m_next; /* the next dependency rule */ }; -struct mod_list_t { - char * m_module; - char * m_options; +struct mod_list_t { /* two-way list of modules to process */ + /* a module description */ + char * m_name; + char * m_path; + struct mod_opt_t * m_options; struct mod_list_t * m_prev; struct mod_list_t * m_next; @@ -56,10 +55,32 @@ struct mod_list_t { 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 ) +#define main_options "acdklnqrst:vVC:" +#define INSERT_ALL 1 /* a */ +#define DUMP_CONF_EXIT 2 /* c */ +#define D_OPT_IGNORED 4 /* d */ +#define AUTOCLEAN_FLG 8 /* k */ +#define LIST_ALL 16 /* l */ +#define SHOW_ONLY 32 /* n */ +#define QUIET 64 /* q */ +#define REMOVE_OPT 128 /* r */ +#define DO_SYSLOG 256 /* s */ +#define RESTRICT_DIR 512 /* t */ +#define VERBOSE 1024 /* v */ +#define VERSION_ONLY 2048 /* V */ +#define CONFIG_FILE 4096 /* C */ + +#define autoclean (main_opts & AUTOCLEAN_FLG) +#define show_only (main_opts & SHOW_ONLY) +#define quiet (main_opts & QUIET) +#define remove_opt (main_opts & REMOVE_OPT) +#define do_syslog (main_opts & DO_SYSLOG) +#define verbose (main_opts & VERBOSE) + +static int main_opts; + +static int parse_tag_value ( char *buffer, char **ptag, char **pvalue ) { char *tag, *value; @@ -109,34 +130,268 @@ static char *reads ( int fd, char *buffer, size_t len ) return 0; } +/* + * This function appends an option to a list + */ +static struct mod_opt_t *append_option( struct mod_opt_t *opt_list, char *opt ) +{ + struct mod_opt_t *ol = opt_list; + + if( ol ) { + while( ol-> m_next ) { + ol = ol-> m_next; + } + ol-> m_next = xmalloc( sizeof( struct mod_opt_t ) ); + ol = ol-> m_next; + } else { + ol = opt_list = xmalloc( sizeof( struct mod_opt_t ) ); + } + + ol-> m_opt_val = bb_xstrdup( opt ); + ol-> m_next = NULL; + + return opt_list; +} + +#if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS +/* static char* parse_command_string( char* src, char **dst ); + * src: pointer to string containing argument + * dst: pointer to where to store the parsed argument + * return value: the pointer to the first char after the parsed argument, + * NULL if there was no argument parsed (only trailing spaces). + * Note that memory is allocated with bb_xstrdup when a new argument was + * parsed. Don't forget to free it! + */ +#define ARG_EMPTY 0x00 +#define ARG_IN_DQUOTES 0x01 +#define ARG_IN_SQUOTES 0x02 +static char *parse_command_string( char *src, char **dst ) +{ + int opt_status = ARG_EMPTY; + char* tmp_str; + + /* Dumb you, I have nothing to do... */ + if( src == NULL ) return src; + + /* Skip leading spaces */ + while( *src == ' ' ) { + src++; + } + /* Is the end of string reached? */ + if( *src == '\0' ) { + return NULL; + } + /* Reached the start of an argument + * By the way, we duplicate a little too much + * here but what is too much is freed later. */ + *dst = tmp_str = bb_xstrdup( src ); + /* Get to the end of that argument */ + while( ( *tmp_str != '\0' ) + && ( ( *tmp_str != ' ' ) + || ( opt_status & ( ARG_IN_DQUOTES | ARG_IN_SQUOTES ) ) ) ) { + switch( *tmp_str ) { + case '\'': + if( opt_status & ARG_IN_DQUOTES ) { + /* Already in double quotes, keep current char as is */ + } else { + /* shift left 1 char, until end of string: get rid of the opening/closing quotes */ + memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) ); + /* mark me: we enter or leave single quotes */ + opt_status ^= ARG_IN_SQUOTES; + /* Back one char, as we need to re-scan the new char there. */ + tmp_str--; + } + break; + case '"': + if( opt_status & ARG_IN_SQUOTES ) { + /* Already in single quotes, keep current char as is */ + } else { + /* shift left 1 char, until end of string: get rid of the opening/closing quotes */ + memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) ); + /* mark me: we enter or leave double quotes */ + opt_status ^= ARG_IN_DQUOTES; + /* Back one char, as we need to re-scan the new char there. */ + tmp_str--; + } + break; + case '\\': + if( opt_status & ARG_IN_SQUOTES ) { + /* Between single quotes: keep as is. */ + } else { + switch( *(tmp_str+1) ) { + case 'a': + case 'b': + case 't': + case 'n': + case 'v': + case 'f': + case 'r': + case '0': + /* We escaped a special character. For now, keep + * both the back-slash and the following char. */ + tmp_str++; src++; + break; + default: + /* We escaped a space or a single or double quote, + * or a back-slash, or a non-escapable char. Remove + * the '\' and keep the new current char as is. */ + memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) ); + break; + } + } + break; + /* Any other char that is special shall appear here. + * Example: $ starts a variable + case '$': + do_variable_expansion(); + break; + * */ + default: + /* any other char is kept as is. */ + break; + } + tmp_str++; /* Go to next char */ + src++; /* Go to next char to find the end of the argument. */ + } + /* End of string, but still no ending quote */ + if( opt_status & ( ARG_IN_DQUOTES | ARG_IN_SQUOTES ) ) { + bb_error_msg_and_die( "unterminated (single or double) quote in options list: %s", src ); + } + *tmp_str++ = '\0'; + *dst = xrealloc( *dst, (tmp_str - *dst ) ); + return src; +} +#else +#define parse_command_string(src, dst) (0) +#endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ + +/* + * This function reads aliases and default module options from a configuration file + * (/etc/modprobe.conf syntax). It supports includes (only files, no directories). + */ +static void include_conf ( struct dep_t **first, struct dep_t **current, char *buffer, int buflen, int fd ) +{ + int continuation_line = 0; + + // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) ! + + while ( reads ( fd, buffer, buflen)) { + int l; + char *p; + + p = strchr ( buffer, '#' ); + if ( p ) + *p = 0; + + l = 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 )) { + /* handle alias as a module dependent on the aliased module */ + 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; + + /* split the line in the module/alias name, and options */ + if ( parse_tag_value ( buffer + 8, &mod, &opt )) { + struct dep_t *dt; + + /* find the corresponding module */ + for ( dt = *first; dt; dt = dt-> m_next ) { + if ( strcmp ( dt-> m_name, mod ) == 0 ) + break; + } + if ( dt ) { + if ( ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS ) { + char* new_opt = NULL; + while( ( opt = parse_command_string( opt, &new_opt ) ) ) { + dt-> m_options = append_option( dt-> m_options, new_opt ); + } + } else { + dt-> m_options = append_option( dt-> m_options, opt ); + } + } + } + } + else if (( strncmp ( buffer, "include", 7 ) == 0 ) && isspace ( buffer [7] )) { + + int fdi; char *filename = buffer + 8; + + while ( isspace ( *filename )) + filename++; + + if (( fdi = open ( filename, O_RDONLY )) >= 0 ) { + include_conf(first, current, buffer, buflen, fdi); + close(fdi); + } + } + } + } +} + +/* + * This function builds a list of dependency rules from /lib/modules/`uname -r`\modules.dep. + * It then fills every modules and aliases with their default options, found by parsing + * modprobe.conf (or modules.conf, or conf.modules). + */ static struct dep_t *build_dep ( void ) { int fd; struct utsname un; struct dep_t *first = 0; struct dep_t *current = 0; - char buffer[256]; - char *filename = buffer; + char buffer[2048]; + char *filename; int continuation_line = 0; + int k_version; k_version = 0; if ( uname ( &un )) - return 0; + bb_error_msg_and_die("can't determine kernel version"); - // 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 ) { - + filename = bb_xasprintf("/lib/modules/%s/modules.dep", un.release ); + fd = open ( filename, O_RDONLY ); + if (ENABLE_FEATURE_CLEAN_UP) + free(filename); + if (fd < 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; @@ -144,10 +399,10 @@ static struct dep_t *build_dep ( void ) } while ( reads ( fd, buffer, sizeof( buffer ))) { - int l = bb_strlen ( buffer ); + int l = strlen ( buffer ); char *p = 0; - while ( isspace ( buffer [l-1] )) { + while ( l > 0 && isspace ( buffer [l-1] )) { buffer [l-1] = 0; l--; } @@ -157,33 +412,43 @@ static struct dep_t *build_dep ( void ) continue; } + /* Is this a new module dep description? */ if ( !continuation_line ) { + /* find the dep beginning */ char *col = strchr ( buffer, ':' ); + char *dot = col; if ( col ) { + /* This line is a dep description */ char *mods; + char *modpath; char *mod; - int ext = 0; + /* Find the beginning of the module file name */ *col = 0; mods = strrchr ( buffer, '/' ); if ( !mods ) - mods = buffer; + mods = buffer; /* no path for this module */ else - mods++; - -#if defined(CONFIG_FEATURE_2_6_MODULES) - if ((k_version > 4) && ( *(col-3) == '.' ) && - ( *(col-2) == 'k' ) && ( *(col-1) == 'o' )) - ext = 3; + mods++; /* there was a path for this module... */ + + /* find the path of the module */ + modpath = strchr ( buffer, '/' ); /* ... and this is the path */ + if ( !modpath ) + modpath = buffer; /* module with no path */ + /* find the end of the module name in the file name */ + if ( ENABLE_FEATURE_2_6_MODULES && + (k_version > 4) && ( *(col-3) == '.' ) && + ( *(col-2) == 'k' ) && ( *(col-1) == 'o' ) ) + dot = col - 3; else -#endif if (( *(col-2) == '.' ) && ( *(col-1) == 'o' )) - ext = 2; + dot = col - 2; - mod = bb_xstrndup ( buffer, col - buffer ); + mod = bb_xstrndup ( mods, dot - mods ); + /* enqueue new module */ if ( !current ) { first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); } @@ -191,25 +456,28 @@ static struct dep_t *build_dep ( void ) 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_name = mod; + current-> m_path = bb_xstrdup(modpath); + current-> m_options = NULL; current-> m_isalias = 0; current-> m_depcnt = 0; current-> m_deparr = 0; current-> m_next = 0; - //printf ( "%s:\n", mod ); p = col + 1; } else + /* this line is not a dep description */ p = 0; } else + /* It's a dep description continuation */ p = buffer; while ( p && *p && isblank(*p)) p++; + /* p points to the first dependable module; if NULL, no dependable module */ if ( p && *p ) { char *end = &buffer [l-1]; char *deps; @@ -222,6 +490,7 @@ static struct dep_t *build_dep ( void ) do { + /* search the end of the dependency */ next = strchr (p, ' ' ); if (next) { @@ -231,6 +500,7 @@ static struct dep_t *build_dep ( void ) else next = end; + /* find the beginning of the module file name */ deps = strrchr ( p, '/' ); if ( !deps || ( deps < p )) { @@ -242,12 +512,12 @@ static struct dep_t *build_dep ( void ) else deps++; -#if defined(CONFIG_FEATURE_2_6_MODULES) - if ((k_version > 4) && ( *(next-2) == '.' ) && *(next-1) == 'k' && - ( *next == 'o' )) + /* find the end of the module name in the file name */ + if ( ENABLE_FEATURE_2_6_MODULES && + (k_version > 4) && ( *(next-2) == '.' ) && + ( *(next-1) == 'k' ) && ( *next == 'o' ) ) ext = 3; else -#endif if (( *(next-1) == '.' ) && ( *next == 'o' )) ext = 2; @@ -256,16 +526,17 @@ static struct dep_t *build_dep ( void ) continue; dep = bb_xstrndup ( deps, next - deps - ext + 1 ); + /* Add the new dependable module name */ 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); } + /* is there other dependable module(s) ? */ if ( buffer [l-1] == '\\' ) continuation_line = 1; else @@ -273,118 +544,37 @@ static struct dep_t *build_dep ( void ) } 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 (!ENABLE_FEATURE_2_6_MODULES + || ( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 ) 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; + include_conf (&first, ¤t, buffer, sizeof(buffer), fd); + close(fd); - 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_module = 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_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 ); - } - } - } + filename = bb_xasprintf("/lib/modules/%s/modules.alias", un.release); + fd = open ( filename, O_RDONLY ); + if (ENABLE_FEATURE_CLEAN_UP) + free(filename); + if (fd < 0) { + /* Ok, that didn't work. Fall back to looking in /lib/modules */ + if (( fd = open ( "/lib/modules/modules.alias", O_RDONLY )) < 0 ) { + return first; } } - close ( fd ); - return first; -} + include_conf (&first, ¤t, buffer, sizeof(buffer), fd); + close(fd); -/* check if /lib/modules/bar/foo.ko belongs to module foo */ -/* return 1 = found, 0 = not found */ -static int mod_strcmp ( const char *mod_path, const char *mod_name ) -{ - /* last path component */ - const char *last_comp = strrchr (mod_path, '/'); - const char *mod_ext = ".o"; - -#if defined(CONFIG_FEATURE_2_6_MODULES) - if ( k_version > 4 ) - mod_ext = ".ko"; -#endif - - last_comp = last_comp ? last_comp + 1 : mod_path; - - return (strncmp(last_comp, - mod_name, - strlen(mod_name)) == 0 ) && - ((strcmp(last_comp + strlen (mod_name), mod_ext) == 0) || last_comp[strlen(mod_name)] == 0) && - (strcmp(mod_path + strlen(mod_path) - - strlen(mod_ext), mod_ext) == 0); + return first; } /* return 1 = loaded, 0 = not loaded, -1 = can't tell */ static int already_loaded (const char *name) { int fd; - char buffer[256]; + char buffer[4096]; fd = open ("/proc/modules", O_RDONLY); if (fd < 0) @@ -396,7 +586,10 @@ static int already_loaded (const char *name) p = strchr (buffer, ' '); if (p) { *p = 0; - if (mod_strcmp (name, buffer)) { + for( p = buffer; ENABLE_FEATURE_2_6_MODULES && *p; p++ ) { + *p = ((*p)=='-')?'_':*p; + } + if (strcmp (name, buffer) == 0) { close (fd); return 1; } @@ -409,29 +602,90 @@ static int already_loaded (const char *name) static int mod_process ( struct mod_list_t *list, int do_insert ) { - char lcmd [256]; int rc = 0; + char **argv = NULL; + struct mod_opt_t *opts; + int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */ + int argc; while ( list ) { - *lcmd = '\0'; + argc = 0; + if( ENABLE_FEATURE_CLEAN_UP ) + argc_malloc = 0; + /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory + * each time we allocate memory for argv. + * But it is (quite) small amounts of memory that leak each + * time a module is loaded, and it is reclaimed when modprobe + * exits anyway (even when standalone shell?). + * This could become a problem when loading a module with LOTS of + * dependencies, with LOTS of options for each dependencies, with + * very little memory on the target... But in that case, the module + * would not load because there is no more memory, so there's no + * problem. */ + /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */ + argv = (char**) malloc( 6 * sizeof( char* ) ); 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 : "" ); + if (already_loaded (list->m_name) != 1) { + argv[argc++] = "insmod"; + if (do_syslog) + argv[argc++] = "-s"; + if (autoclean) + argv[argc++] = "-k"; + if (quiet) + argv[argc++] = "-q"; + else if(verbose) /* verbose and quiet are mutually exclusive */ + argv[argc++] = "-v"; + argv[argc++] = list-> m_path; + if( ENABLE_FEATURE_CLEAN_UP ) + argc_malloc = argc; + opts = list-> m_options; + while( opts ) { + /* Add one more option */ + argc++; + argv = (char**) xrealloc( argv, ( argc + 1 ) * sizeof( char* ) ); + argv[argc-1] = opts-> m_opt_val; + opts = opts-> m_next; + } + } } else { - if (already_loaded (list->m_module) != 0) - snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s", - do_syslog ? "-s" : "", list-> m_module ); + /* modutils uses short name for removal */ + if (already_loaded (list->m_name) != 0) { + argv[argc++] = "rmmod"; + if (do_syslog) + argv[argc++] = "-s"; + argv[argc++] = list->m_name; + if( ENABLE_FEATURE_CLEAN_UP ) + argc_malloc = argc; + } } + argv[argc] = NULL; - if (*lcmd) { + if (argc) { if (verbose) { - printf("%s\n", lcmd); + printf("%s module %s\n", do_insert?"Loading":"Unloading", list-> m_name ); } if (!show_only) { - int rc2 = system(lcmd); + int rc2 = 0; + int status; + switch (fork()) { + case -1: + rc2 = 1; + break; + case 0: //child + execvp(argv[0], argv); + bb_perror_msg_and_die("exec of %s", argv[0]); + /* NOTREACHED */ + default: + if (wait(&status) == -1) { + rc2 = 1; + break; + } + if (WIFEXITED(status)) + rc2 = WEXITSTATUS(status); + if (WIFSIGNALED(status)) + rc2 = WTERMSIG(status); + break; + } if (do_insert) { rc = rc2; /* only last module matters */ } @@ -439,65 +693,83 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) rc = 0; /* success if remove any mod */ } } + if( ENABLE_FEATURE_CLEAN_UP ) + /* the last value in the array has index == argc, but + * it is the terminating NULL, so we must not free it. */ + while( argc_malloc < argc ) { + free( argv[argc_malloc++] ); + } + } + if( ENABLE_FEATURE_CLEAN_UP ) { + free( argv ); + argv = NULL; } list = do_insert ? list-> m_prev : list-> m_next; } return (show_only) ? 0 : rc; } +/* + * Builds the dependency list (aka stack) of a module. + * head: the highest module in the stack (last to insmod, first to rmmod) + * tail: the lowest module in the stack (first to insmod, last to rmmod) + */ 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 = bb_strlen ( mod ); - -#if defined(CONFIG_FEATURE_2_6_MODULES) - if ((k_version > 4) && ( mod [lm-3] == '.' ) && - ( mod [lm-2] == 'k' ) && ( mod [lm-1] == 'o' )) - mod [lm-3] = 0; - else -#endif - if (( mod [lm-2] == '.' ) && ( mod [lm-1] == 'o' )) - mod [lm-2] = 0; + struct mod_opt_t *opt = 0; + char *path = 0; // check dependencies for ( dt = depend; dt; dt = dt-> m_next ) { - if ( mod_strcmp ( dt-> m_module, mod )) { - mod = dt-> m_module; - opt = dt-> m_options; + if ( strcmp ( dt-> m_name, mod ) == 0) { break; } } + if( !dt ) { + bb_error_msg ("module %s not found.", mod); + return; + } + // resolve alias names - while ( dt && dt-> m_isalias ) { + while ( 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 ) + if ( strcmp ( adt-> m_name, dt-> m_deparr [0] ) == 0 ) break; } if ( adt ) { + /* This is the module we are aliased to */ + struct mod_opt_t *opts = dt-> m_options; + /* Option of the alias are appended to the options of the module */ + while( opts ) { + adt-> m_options = append_option( adt-> m_options, opts-> m_opt_val ); + opts = opts-> m_next; + } dt = adt; - mod = dt-> m_module; - if ( !opt ) - opt = dt-> m_options; } - else + else { + bb_error_msg ("module %s not found.", mod); return; + } } - else + else { + bb_error_msg ("Bad alias %s", dt-> m_name); return; + } } + mod = dt-> m_name; + path = dt-> m_path; + opt = dt-> m_options; + // search for duplicates for ( find = *head; find; find = find-> m_next ) { - if ( !strcmp ( mod, find-> m_module )) { + if ( !strcmp ( mod, find-> m_name )) { // found -> dequeue it if ( find-> m_prev ) @@ -516,7 +788,8 @@ 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_name = mod; + find-> m_path = path; find-> m_options = opt; } @@ -533,13 +806,12 @@ static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t * if ( dt ) { int i; + /* Add all dependable module for that new module */ 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; @@ -550,21 +822,11 @@ static int mod_insert ( char *mod, int argc, char **argv ) check_dep ( mod, &head, &tail ); if ( head && tail ) { - if ( argc ) { + 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, " " ); - } + head->m_options = append_option( head->m_options, argv[i] ); } // process tail ---> head @@ -579,7 +841,7 @@ static int mod_insert ( char *mod, int argc, char **argv ) static int mod_remove ( char *mod ) { int rc; - static struct mod_list_t rm_a_dummy = { "-a", 0, 0 }; + static struct mod_list_t rm_a_dummy = { "-a", NULL, NULL, NULL, NULL }; struct mod_list_t *head = 0; struct mod_list_t *tail = 0; @@ -597,77 +859,42 @@ static int mod_remove ( char *mod ) } - - -extern int modprobe_main(int argc, char** argv) +int modprobe_main(int argc, char** argv) { - int opt; - int remove_opt = 0; + int rc = EXIT_SUCCESS; + char *unused; - 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 + bb_opt_complementally = "?V-:q-v:v-q"; + main_opts = bb_getopt_ulflags(argc, argv, "acdklnqrst:vVC:", + &unused, &unused); + if((main_opts & (DUMP_CONF_EXIT | LIST_ALL))) return EXIT_SUCCESS; - break; - case 'C': // no config used - case 't': // no pattern matching + if((main_opts & (RESTRICT_DIR | CONFIG_FILE))) 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 )) { + argv [optind] : NULL )) { bb_error_msg ("failed to remove module %s", argv [optind] ); rc = EXIT_FAILURE; } } while ( ++optind < argc ); + } else { + if (optind >= argc) + bb_error_msg_and_die ( "No module or pattern provided\n" ); - return rc; + if ( mod_insert ( argv [optind], argc - optind - 1, argv + optind + 1 )) + bb_error_msg_and_die ( "failed to load module %s", argv [optind] ); } - 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] ); + /* Here would be a good place to free up memory allocated during the dependencies build. */ - return EXIT_SUCCESS; + return rc; }