123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- /* 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
- * 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
- *
- */
- #include <sys/utsname.h>
- #include <sys/types.h>
- #include <sys/wait.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 )
- {
- int rc = 0;
- char *argv[10];
- int argc;
- while ( list ) {
- argc = 0;
- if ( do_insert ) {
- 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";
- argv[argc++] = list-> m_path;
- if (list-> m_options)
- argv[argc++] = list-> m_options;
- }
- } else {
- /* 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;
- }
- }
- argv[argc] = NULL;
- if (argc) {
- if (verbose) {
- int i;
- for (i=0; i<argc; i++)
- printf("%s ", argv[i]);
- printf("\n");
- }
- if (!show_only) {
- 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 */
- }
- 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;
- }
|