Patch from Yann E. Morin, something to do with bugs 276 and 272.
This commit was genuinely broken at the time (not related to our build system): scripts/config/mkconfigs.c (2005-08-01 - 2006-10-05 arasi) uretilen bbconfigopts.h icine .config satirlarini kacissiz, ham newline'larla gömüyordu - gecerli C degildi, busybox'un kendi bb_mkdep araci bile parse edemiyordu (7d219aab7 ile duzeltildi, mkconfigs shell scripte cevrildi)
No binary (build failed)
—
(log yok)
| @@ -5,6 +5,8 @@ | |
| * Copyright (c) 2002 by Robert Griebl, [email protected] | |
| * Copyright (c) 2003 by Andrew Dennison, [email protected] | |
| * Copyright (c) 2005 by Jim Bauer, [email protected] | |
| + | * |
| + | * Portions Copyright (c) 2005 by Yann E. MORIN, [email protected] |
| * | |
| * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | |
| */ | |
| @@ -21,26 +23,31 @@ | |
| #include <fcntl.h> | |
| #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 */ |
| + | |
| + | int m_isalias : 1; /* the module is an alias */ |
| + | int m_reserved : 15; /* stuffin' */ |
| - | 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; |
| + | 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 { |
| + | struct mod_list_t { /* two-way list of modules to process */ |
| + | /* a module description */ |
| char * m_name; | |
| char * m_path; | |
| - | char * m_options; |
| + | struct mod_opt_t * m_options; |
| struct mod_list_t * m_prev; | |
| struct mod_list_t * m_next; | |
| @@ -49,7 +56,6 @@ struct mod_list_t { | |
| static struct dep_t *depend; | |
| static int autoclean, show_only, quiet, do_syslog, verbose; | |
| - | static int k_version; |
| static int parse_tag_value ( char *buffer, char **ptag, char **pvalue ) | |
| { | |
| @@ -101,6 +107,144 @@ static char *reads ( int fd, char *buffer, size_t len ) | |
| return 0; | |
| } | |
| + | /* |
| + | * This function appends an option to a list |
| + | */ |
| + | 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 (defined CONFIG_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, strlen( *dst ) ); |
| + | return src; |
| + | } |
| + | #endif /* CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ |
| + | |
| + | /* |
| + | * 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; | |
| @@ -110,10 +254,11 @@ static struct dep_t *build_dep ( void ) | |
| char buffer[2048]; | |
| char *filename = buffer; | |
| 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 )) { | |
| @@ -149,37 +294,43 @@ static struct dep_t *build_dep ( void ) | |
| continue; | |
| } | |
| + | /* Is this a new module dep description? */ |
| if ( !continuation_line ) { | |
| + | /* find the dep begining */ |
| char *col = strchr ( buffer, ':' ); | |
| char *dot = col; | |
| if ( col ) { | |
| + | /* This line is a dep description */ |
| char *mods; | |
| char *modpath; | |
| char *mod; | |
| + | /* 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++; |
| + | mods++; /* there was a path for this module... */ |
| - | modpath = strchr ( buffer, '/' ); |
| + | /* find the path of the module */ |
| + | modpath = strchr ( buffer, '/' ); /* ... and this is the path */ |
| if ( !modpath ) | |
| - | modpath = buffer; |
| - | #if defined(CONFIG_FEATURE_2_6_MODULES) |
| - | if ((k_version > 4) && ( *(col-3) == '.' ) && |
| - | ( *(col-2) == 'k' ) && ( *(col-1) == 'o' )) |
| + | 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' )) | |
| dot = col - 2; | |
| mod = bb_xstrndup ( mods, dot - mods ); | |
| + | /* enqueue new module */ |
| if ( !current ) { | |
| first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t )); | |
| } | |
| @@ -189,24 +340,26 @@ static struct dep_t *build_dep ( void ) | |
| } | |
| current-> m_name = mod; | |
| current-> m_path = bb_xstrdup(modpath); | |
| - | current-> m_options = 0; |
| + | 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; | |
| @@ -219,6 +372,7 @@ static struct dep_t *build_dep ( void ) | |
| do | |
| { | |
| + | /* search the end of the dependency */ |
| next = strchr (p, ' ' ); | |
| if (next) | |
| { | |
| @@ -228,6 +382,7 @@ static struct dep_t *build_dep ( void ) | |
| else | |
| next = end; | |
| + | /* find the begining of the module file name */ |
| deps = strrchr ( p, '/' ); | |
| if ( !deps || ( deps < p )) { | |
| @@ -239,12 +394,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; | |
| @@ -253,16 +408,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 | |
| @@ -305,8 +461,7 @@ static struct dep_t *build_dep ( void ) | |
| char *alias, *mod; | |
| if ( parse_tag_value ( buffer + 6, &alias, &mod )) { | |
| - | // fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", 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 )); | |
| } | |
| @@ -332,18 +487,24 @@ static struct dep_t *build_dep ( void ) | |
| 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 ) { | |
| - | 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 ); |
| + | if ( CONFIG_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 ); |
| + | } |
| } | |
| } | |
| } | |
| @@ -370,6 +531,9 @@ static int already_loaded (const char *name) | |
| p = strchr (buffer, ' '); | |
| if (p) { | |
| *p = 0; | |
| + | for( p = buffer; ENABLE_FEATURE_2_6_MODULES && *p; p++ ) { |
| + | *p = ((*p)=='-')?'_':*p; |
| + | } |
| if (strcmp (name, buffer) == 0) { | |
| close (fd); | |
| return 1; | |
| @@ -381,137 +545,30 @@ static int already_loaded (const char *name) | |
| return 0; | |
| } | |
| - | #ifdef CONFIG_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 that's the easy way: |
| - | * cost effective wrt code, cost consumming wrt memory usage. */ |
| - | *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'; |
| - | return src; |
| - | } |
| - | #endif /* CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ |
| - | |
| static int mod_process ( struct mod_list_t *list, int do_insert ) | |
| { | |
| int rc = 0; | |
| - | #ifdef CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS |
| char **argv = NULL; | |
| - | char *opts; |
| - | #ifdef CONFIG_FEATURE_CLEAN_UP |
| - | int argc_malloc; |
| - | #endif |
| - | #else /* CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ |
| - | char *argv[10]; |
| - | #endif |
| + | struct mod_opt_t *opts; |
| + | int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */ |
| int argc; | |
| while ( list ) { | |
| argc = 0; | |
| - | #ifdef CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS |
| - | #ifdef CONFIG_FEATURE_CLEAN_UP |
| - | argc_malloc = 0; |
| - | #endif |
| - | argv = (char**) malloc( 6 * sizeof( char* ) ); /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */ |
| - | #endif |
| + | 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_name) != 1) { | |
| argv[argc++] = "insmod"; | |
| @@ -524,20 +581,16 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) | |
| else if(verbose) /* verbose and quiet are mutually exclusive */ | |
| argv[argc++] = "-v"; | |
| argv[argc++] = list-> m_path; | |
| - | #ifdef CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS |
| - | #ifdef CONFIG_FEATURE_CLEAN_UP |
| - | argc_malloc = argc; |
| - | #endif |
| + | if( ENABLE_FEATURE_CLEAN_UP ) |
| + | argc_malloc = argc; |
| opts = list-> m_options; | |
| - | while( ( opts = parse_command_string( opts, &(argv[argc]) ) ) != NULL ) { |
| - | /* Increase the argv array by 1 */ |
| + | 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 /* CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ |
| - | if (list-> m_options) |
| - | argv[argc++] = list-> m_options; |
| - | #endif /* CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS */ |
| } | |
| } else { | |
| /* modutils uses short name for removal */ | |
| @@ -546,19 +599,15 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) | |
| if (do_syslog) | |
| argv[argc++] = "-s"; | |
| argv[argc++] = list->m_name; | |
| - | #if ( defined CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS ) && ( defined CONFIG_FEATURE_CLEAN_UP ) |
| - | argc_malloc = argc; |
| - | #endif |
| + | if( ENABLE_FEATURE_CLEAN_UP ) |
| + | argc_malloc = argc; |
| } | |
| } | |
| argv[argc] = NULL; | |
| if (argc) { | |
| if (verbose) { | |
| - | int i; |
| - | printf("argc=%d\n", argc ); |
| - | for (i=0; i<argc; i++) |
| - | printf("argv[%02d]=\"%s\"\n", i, argv[i]); |
| + | printf("%s module %s\n", do_insert?"Loading":"Unloading", list-> m_name ); |
| } | |
| if (!show_only) { | |
| int rc2 = 0; | |
| @@ -589,51 +638,48 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) | |
| rc = 0; /* success if remove any mod */ | |
| } | |
| } | |
| - | #if ( defined CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS ) && ( defined CONFIG_FEATURE_CLEAN_UP ) |
| - | /* the last value in the array has index == argc, but |
| - | * it is the terminatign NULL, so we must not free it. */ |
| - | while( argc_malloc < argc ) { |
| - | free( argv[argc_malloc++] ); |
| + | 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++] ); |
| } | |
| } | |
| - | free( argv ); |
| - | /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory |
| - | * here. But it is (quite) small amounts of memory that leak each |
| - | * time a module is loaded, and it is reclaimed when modprobe |
| - | * exits anyway. |
| - | * 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. Hmm, wait... Is this true, whatever the allocation policy? */ |
| - | argv = NULL; |
| - | #else /* CONFIG_FEATURE_MODPROBE_MULTIPLE_OPTIONS && CONFIG_FEATURE_CLEAN_UP */ |
| + | if( ENABLE_FEATURE_CLEAN_UP ) { |
| + | free( argv ); |
| + | argv = NULL; |
| } | |
| - | #endif |
| 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; |
| + | struct mod_opt_t *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; | |
| } | |
| } | |
| + | 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; | |
| @@ -642,24 +688,30 @@ static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t * | |
| 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_name; |
| - | path = dt-> m_path; |
| - | 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; | |
| + | } |
| } | |
| - | if ( !path ) { |
| - | bb_error_msg ("module %s not found.", mod); |
| - | 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_name )) { | |
| @@ -699,6 +751,7 @@ 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 ); | |
| } | |
| @@ -714,24 +767,12 @@ static int mod_insert ( char *mod, int argc, char **argv ) | |
| check_dep ( mod, &head, &tail ); | |
| if ( head && tail ) { | |
| - | #if defined(CONFIG_FEATURE_2_6_MODULES) |
| - | 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] ); |
| } | |
| - | #endif |
| // process tail ---> head | |
| rc = mod_process ( tail, 1 ); | |
| @@ -766,6 +807,7 @@ static int mod_remove ( char *mod ) | |
| extern int modprobe_main(int argc, char** argv) | |
| { | |
| int opt; | |
| + | int rc = EXIT_SUCCESS; |
| int remove_opt = 0; | |
| autoclean = show_only = quiet = do_syslog = verbose = 0; | |
| @@ -814,7 +856,6 @@ extern int modprobe_main(int argc, char** argv) | |
| 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 )) { | |
| @@ -823,15 +864,15 @@ extern int modprobe_main(int argc, char** argv) | |
| 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 ( bb_xstrdup ( 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" ); |
| + | /* Here would be a good place to free up memory allocated during the dependencies build. */ |
| - | 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; |
| + | return rc; |
| } | |