diff options
-rw-r--r-- | modutils/Config.in | 19 | ||||
-rw-r--r-- | modutils/modprobe.c | 189 |
2 files changed, 186 insertions, 22 deletions
diff --git a/modutils/Config.in b/modutils/Config.in index 986b65c6a..bf19a3e67 100644 --- a/modutils/Config.in +++ b/modutils/Config.in | |||
@@ -100,6 +100,25 @@ config CONFIG_MODPROBE | |||
100 | Handle the loading of modules, and their dependancies on a high | 100 | Handle the loading of modules, and their dependancies on a high |
101 | level. | 101 | level. |
102 | 102 | ||
103 | Note that, in the state it is, modprobe can pass only one option | ||
104 | to the modules it loads. See option below. | ||
105 | |||
106 | config CONFIG_MODPROBE_MULTIPLE_OPTIONS | ||
107 | bool "Multiple options parsing" | ||
108 | default y | ||
109 | depends on CONFIG_MODPROBE | ||
110 | help | ||
111 | Allow modprobe to understand more than one option to pass to | ||
112 | modules. | ||
113 | |||
114 | This is a WIP, while waiting for a common argument parsing | ||
115 | common amongst all BB applets (shell, modprobe, etc...) and | ||
116 | adds around 600 bytes on x86, 700 bytes on ARM. The code is | ||
117 | biggish and uggly, but just works. | ||
118 | |||
119 | Saying Y here is not a bad idea if you're not that short | ||
120 | on storage capacity. | ||
121 | |||
103 | config CONFIG_RMMOD | 122 | config CONFIG_RMMOD |
104 | bool "rmmod" | 123 | bool "rmmod" |
105 | default n | 124 | default n |
diff --git a/modutils/modprobe.c b/modutils/modprobe.c index 8d739ef3c..998c8668e 100644 --- a/modutils/modprobe.c +++ b/modutils/modprobe.c | |||
@@ -6,20 +6,7 @@ | |||
6 | * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au | 6 | * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au |
7 | * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com | 7 | * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | * | ||
23 | */ | 10 | */ |
24 | 11 | ||
25 | #include <sys/utsname.h> | 12 | #include <sys/utsname.h> |
@@ -394,16 +381,139 @@ static int already_loaded (const char *name) | |||
394 | return 0; | 381 | return 0; |
395 | } | 382 | } |
396 | 383 | ||
384 | #ifdef CONFIG_MODPROBE_MULTIPLE_OPTIONS | ||
385 | /* static char* parse_command_string( char* src, char **dst ); | ||
386 | * src: pointer to string containing argument | ||
387 | * dst: pointer to where to store the parsed argument | ||
388 | * return value: the pointer to the first char after the parsed argument, | ||
389 | * NULL if there was no argument parsed (only trailing spaces). | ||
390 | * Note that memory is allocated with bb_xstrdup when a new argument was | ||
391 | * parsed. Don't forget to free it! | ||
392 | */ | ||
393 | #define ARG_EMPTY 0x00 | ||
394 | #define ARG_IN_DQUOTES 0x01 | ||
395 | #define ARG_IN_SQUOTES 0x02 | ||
396 | static char *parse_command_string( char *src, char **dst ) | ||
397 | { | ||
398 | int opt_status = ARG_EMPTY; | ||
399 | char* tmp_str; | ||
400 | |||
401 | /* Dumb you, I have nothing to do... */ | ||
402 | if( src == NULL ) return src; | ||
403 | |||
404 | /* Skip leading spaces */ | ||
405 | while( *src == ' ' ) { | ||
406 | src++; | ||
407 | } | ||
408 | /* Is the end of string reached? */ | ||
409 | if( *src == '\0' ) { | ||
410 | return NULL; | ||
411 | } | ||
412 | /* Reached the start of an argument | ||
413 | * By the way, we duplicate a little too much here :-/ but that's the easy way: | ||
414 | * cost effective wrt code, cost consumming wrt memory usage. */ | ||
415 | *dst = tmp_str = bb_xstrdup( src ); | ||
416 | /* Get to the end of that argument */ | ||
417 | while( ( *tmp_str != '\0' ) | ||
418 | && ( ( *tmp_str != ' ' ) | ||
419 | || ( opt_status & ( ARG_IN_DQUOTES | ARG_IN_SQUOTES ) ) ) ) { | ||
420 | switch( *tmp_str ) { | ||
421 | case '\'': | ||
422 | if( opt_status & ARG_IN_DQUOTES ) { | ||
423 | /* Already in double quotes, keep current char as is */ | ||
424 | } else { | ||
425 | /* shift left 1 char, until end of string: get rid of the opening/closing quotes */ | ||
426 | memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) ); | ||
427 | /* mark me: we enter or leave single quotes */ | ||
428 | opt_status ^= ARG_IN_SQUOTES; | ||
429 | /* Back one char, as we need to re-scan the new char there. */ | ||
430 | tmp_str--; | ||
431 | } | ||
432 | break; | ||
433 | case '"': | ||
434 | if( opt_status & ARG_IN_SQUOTES ) { | ||
435 | /* Already in single quotes, keep current char as is */ | ||
436 | } else { | ||
437 | /* shift left 1 char, until end of string: get rid of the opening/closing quotes */ | ||
438 | memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) ); | ||
439 | /* mark me: we enter or leave double quotes */ | ||
440 | opt_status ^= ARG_IN_DQUOTES; | ||
441 | /* Back one char, as we need to re-scan the new char there. */ | ||
442 | tmp_str--; | ||
443 | } | ||
444 | break; | ||
445 | case '\\': | ||
446 | if( opt_status & ARG_IN_SQUOTES ) { | ||
447 | /* Between single quotes: keep as is. */ | ||
448 | } else { | ||
449 | switch( *(tmp_str+1) ) { | ||
450 | case 'a': | ||
451 | case 'b': | ||
452 | case 't': | ||
453 | case 'n': | ||
454 | case 'v': | ||
455 | case 'f': | ||
456 | case 'r': | ||
457 | case '0': | ||
458 | /* We escaped a special character. For now, keep | ||
459 | * both the back-slash and the following char. */ | ||
460 | tmp_str++; src++; | ||
461 | break; | ||
462 | default: | ||
463 | /* We escaped a space or a single or double quote, | ||
464 | * or a back-slash, or a non-escapable char. Remove | ||
465 | * the '\' and keep the new current char as is. */ | ||
466 | memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) ); | ||
467 | break; | ||
468 | } | ||
469 | } | ||
470 | break; | ||
471 | /* Any other char that is special shall appear here. | ||
472 | * Example: $ starts a variable | ||
473 | case '$': | ||
474 | do_variable_expansion(); | ||
475 | break; | ||
476 | * */ | ||
477 | default: | ||
478 | /* any other char is kept as is. */ | ||
479 | break; | ||
480 | } | ||
481 | tmp_str++; /* Go to next char */ | ||
482 | src++; /* Go to next char to find the end of the argument. */ | ||
483 | } | ||
484 | /* End of string, but still no ending quote */ | ||
485 | if( opt_status & ( ARG_IN_DQUOTES | ARG_IN_SQUOTES ) ) { | ||
486 | bb_error_msg_and_die( "unterminated (single or double) quote in options list: %s", src ); | ||
487 | } | ||
488 | *tmp_str = '\0'; | ||
489 | return src; | ||
490 | } | ||
491 | #endif /* CONFIG_MODPROBE_MULTIPLE_OPTIONS */ | ||
492 | |||
397 | static int mod_process ( struct mod_list_t *list, int do_insert ) | 493 | static int mod_process ( struct mod_list_t *list, int do_insert ) |
398 | { | 494 | { |
399 | int rc = 0; | 495 | int rc = 0; |
496 | #ifdef CONFIG_MODPROBE_MULTIPLE_OPTIONS | ||
497 | char **argv = NULL; | ||
498 | char *opts; | ||
499 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
500 | int argc_malloc; | ||
501 | #endif | ||
502 | #else /* CONFIG_MODPROBE_MULTIPLE_OPTIONS */ | ||
400 | char *argv[10]; | 503 | char *argv[10]; |
504 | #endif | ||
401 | int argc; | 505 | int argc; |
402 | 506 | ||
403 | while ( list ) { | 507 | while ( list ) { |
404 | argc = 0; | 508 | argc = 0; |
509 | #ifdef CONFIG_MODPROBE_MULTIPLE_OPTIONS | ||
510 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
511 | argc_malloc = 0; | ||
512 | #endif | ||
513 | argv = (char**) malloc( 6 * sizeof( char* ) ); /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */ | ||
514 | #endif | ||
405 | if ( do_insert ) { | 515 | if ( do_insert ) { |
406 | if (already_loaded (list->m_name) != 1) { | 516 | if (already_loaded (list->m_name) != 1) { |
407 | argv[argc++] = "insmod"; | 517 | argv[argc++] = "insmod"; |
408 | if (do_syslog) | 518 | if (do_syslog) |
409 | argv[argc++] = "-s"; | 519 | argv[argc++] = "-s"; |
@@ -411,17 +521,34 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) | |||
411 | argv[argc++] = "-k"; | 521 | argv[argc++] = "-k"; |
412 | if (quiet) | 522 | if (quiet) |
413 | argv[argc++] = "-q"; | 523 | argv[argc++] = "-q"; |
524 | else if(verbose) /* verbose and quiet are mutually exclusive */ | ||
525 | argv[argc++] = "-v"; | ||
414 | argv[argc++] = list-> m_path; | 526 | argv[argc++] = list-> m_path; |
527 | #ifdef CONFIG_MODPROBE_MULTIPLE_OPTIONS | ||
528 | #ifdef CONFIG_FEATURE_CLEAN_UP | ||
529 | argc_malloc = argc; | ||
530 | #endif | ||
531 | opts = list-> m_options; | ||
532 | while( ( opts = parse_command_string( opts, &(argv[argc]) ) ) != NULL ) { | ||
533 | /* Increase the argv array by 1 */ | ||
534 | argc++; | ||
535 | argv = (char**) xrealloc( argv, ( argc + 1 ) * sizeof( char* ) ); | ||
536 | } | ||
537 | #else /* CONFIG_MODPROBE_MULTIPLE_OPTIONS */ | ||
415 | if (list-> m_options) | 538 | if (list-> m_options) |
416 | argv[argc++] = list-> m_options; | 539 | argv[argc++] = list-> m_options; |
540 | #endif /* CONFIG_MODPROBE_MULTIPLE_OPTIONS */ | ||
417 | } | 541 | } |
418 | } else { | 542 | } else { |
419 | /* modutils uses short name for removal */ | 543 | /* modutils uses short name for removal */ |
420 | if (already_loaded (list->m_name) != 0) { | 544 | if (already_loaded (list->m_name) != 0) { |
421 | argv[argc++] = "rmmod"; | 545 | argv[argc++] = "rmmod"; |
422 | if (do_syslog) | 546 | if (do_syslog) |
423 | argv[argc++] = "-s"; | 547 | argv[argc++] = "-s"; |
424 | argv[argc++] = list->m_name; | 548 | argv[argc++] = list->m_name; |
549 | #if ( defined CONFIG_MODPROBE_MULTIPLE_OPTIONS ) && ( defined CONFIG_FEATURE_CLEAN_UP ) | ||
550 | argc_malloc = argc; | ||
551 | #endif | ||
425 | } | 552 | } |
426 | } | 553 | } |
427 | argv[argc] = NULL; | 554 | argv[argc] = NULL; |
@@ -429,9 +556,9 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) | |||
429 | if (argc) { | 556 | if (argc) { |
430 | if (verbose) { | 557 | if (verbose) { |
431 | int i; | 558 | int i; |
559 | printf("argc=%d\n", argc ); | ||
432 | for (i=0; i<argc; i++) | 560 | for (i=0; i<argc; i++) |
433 | printf("%s ", argv[i]); | 561 | printf("argv[%02d]=\"%s\"\n", i, argv[i]); |
434 | printf("\n"); | ||
435 | } | 562 | } |
436 | if (!show_only) { | 563 | if (!show_only) { |
437 | int rc2 = 0; | 564 | int rc2 = 0; |
@@ -462,7 +589,27 @@ static int mod_process ( struct mod_list_t *list, int do_insert ) | |||
462 | rc = 0; /* success if remove any mod */ | 589 | rc = 0; /* success if remove any mod */ |
463 | } | 590 | } |
464 | } | 591 | } |
592 | #if ( defined CONFIG_MODPROBE_MULTIPLE_OPTIONS ) && ( defined CONFIG_FEATURE_CLEAN_UP ) | ||
593 | /* the last value in the array has index == argc, but | ||
594 | * it is the terminatign NULL, so we must not free it. */ | ||
595 | while( argc_malloc < argc ) { | ||
596 | free( argv[argc_malloc++] ); | ||
597 | } | ||
465 | } | 598 | } |
599 | free( argv ); | ||
600 | /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory | ||
601 | * here. But it is (quite) small amounts of memory that leak each | ||
602 | * time a module is loaded, and it is reclaimed when modprobe | ||
603 | * exits anyway. | ||
604 | * This could become a problem when loading a module with LOTS of | ||
605 | * dependencies, with LOTS of options for each dependencies, with | ||
606 | * very little memory on the target... But in that case, the module | ||
607 | * would not load because there is no more memory, so there's no | ||
608 | * problem. Hmm, wait... Is this true, whatever the allocation policy? */ | ||
609 | argv = NULL; | ||
610 | #else /* CONFIG_MODPROBE_MULTIPLE_OPTIONS && CONFIG_FEATURE_CLEAN_UP */ | ||
611 | } | ||
612 | #endif | ||
466 | list = do_insert ? list-> m_prev : list-> m_next; | 613 | list = do_insert ? list-> m_prev : list-> m_next; |
467 | } | 614 | } |
468 | return (show_only) ? 0 : rc; | 615 | return (show_only) ? 0 : rc; |
@@ -557,8 +704,6 @@ static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t * | |||
557 | } | 704 | } |
558 | } | 705 | } |
559 | 706 | ||
560 | |||
561 | |||
562 | static int mod_insert ( char *mod, int argc, char **argv ) | 707 | static int mod_insert ( char *mod, int argc, char **argv ) |
563 | { | 708 | { |
564 | struct mod_list_t *tail = 0; | 709 | struct mod_list_t *tail = 0; |
@@ -645,7 +790,7 @@ extern int modprobe_main(int argc, char** argv) | |||
645 | show_only++; | 790 | show_only++; |
646 | break; | 791 | break; |
647 | case 'q': | 792 | case 'q': |
648 | quiet++; | 793 | quiet++; verbose=0; |
649 | break; | 794 | break; |
650 | case 'r': | 795 | case 'r': |
651 | remove_opt++; | 796 | remove_opt++; |
@@ -654,7 +799,7 @@ extern int modprobe_main(int argc, char** argv) | |||
654 | do_syslog++; | 799 | do_syslog++; |
655 | break; | 800 | break; |
656 | case 'v': | 801 | case 'v': |
657 | verbose++; | 802 | verbose++; quiet=0; |
658 | break; | 803 | break; |
659 | case 'V': | 804 | case 'V': |
660 | default: | 805 | default: |