Remove xcalloc() and convert its callers to xzalloc(). About half of them
[oweals/busybox.git] / modutils / modprobe.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Modprobe written from scratch for BusyBox
4  *
5  * Copyright (c) 2002 by Robert Griebl, griebl@gmx.de
6  * Copyright (c) 2003 by Andrew Dennison, andrew.dennison@motec.com.au
7  * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com
8  *
9  * Portions Copyright (c) 2005 by Yann E. MORIN, yann.morin.1998@anciens.enib.fr
10  *
11  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
12 */
13
14 #include "busybox.h"
15 #include <sys/utsname.h>
16 #include <fnmatch.h>
17
18 struct mod_opt_t {      /* one-way list of options to pass to a module */
19         char *  m_opt_val;
20         struct mod_opt_t * m_next;
21 };
22
23 struct dep_t {  /* one-way list of dependency rules */
24         /* a dependency rule */
25         char *  m_name;                         /* the module name*/
26         char *  m_path;                         /* the module file path */
27         struct mod_opt_t *  m_options;  /* the module options */
28
29         int     m_isalias  : 1;                 /* the module is an alias */
30         int     m_reserved : 15;                /* stuffin' */
31
32         int     m_depcnt   : 16;                /* the number of dependable module(s) */
33         char ** m_deparr;                       /* the list of dependable module(s) */
34
35         struct dep_t * m_next;                  /* the next dependency rule */
36 };
37
38 struct mod_list_t {     /* two-way list of modules to process */
39         /* a module description */
40         char *  m_name;
41         char *  m_path;
42         struct mod_opt_t *  m_options;
43
44         struct mod_list_t * m_prev;
45         struct mod_list_t * m_next;
46 };
47
48
49 static struct dep_t *depend;
50
51 #define main_options "acdklnqrst:vVC:"
52 #define INSERT_ALL     1        /* a */
53 #define DUMP_CONF_EXIT 2        /* c */
54 #define D_OPT_IGNORED  4        /* d */
55 #define AUTOCLEAN_FLG  8        /* k */
56 #define LIST_ALL       16       /* l */
57 #define SHOW_ONLY      32       /* n */
58 #define QUIET          64       /* q */
59 #define REMOVE_OPT     128      /* r */
60 #define DO_SYSLOG      256      /* s */
61 #define RESTRICT_DIR   512      /* t */
62 #define VERBOSE        1024     /* v */
63 #define VERSION_ONLY   2048     /* V */
64 #define CONFIG_FILE    4096     /* C */
65
66 #define autoclean       (main_opts & AUTOCLEAN_FLG)
67 #define show_only       (main_opts & SHOW_ONLY)
68 #define quiet           (main_opts & QUIET)
69 #define remove_opt      (main_opts & REMOVE_OPT)
70 #define do_syslog       (main_opts & DO_SYSLOG)
71 #define verbose         (main_opts & VERBOSE)
72
73 static int main_opts;
74
75 static int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
76 {
77         char *tag, *value;
78
79         while ( isspace ( *buffer ))
80                 buffer++;
81         tag = value = buffer;
82         while ( !isspace ( *value ))
83                 if (!*value) return 0;
84                 else value++;
85         *value++ = 0;
86         while ( isspace ( *value ))
87                 value++;
88         if (!*value) return 0;
89
90         *ptag = tag;
91         *pvalue = value;
92
93         return 1;
94 }
95
96 /* Jump through hoops to simulate how fgets() grabs just one line at a
97  * time... Don't use any stdio since modprobe gets called from a kernel
98  * thread and stdio junk can overflow the limited stack...
99  */
100 static char *reads ( int fd, char *buffer, size_t len )
101 {
102         int n = read ( fd, buffer, len );
103
104         if ( n > 0 ) {
105                 char *p;
106
107                 buffer [len-1] = 0;
108                 p = strchr ( buffer, '\n' );
109
110                 if ( p ) {
111                         off_t offset;
112
113                         offset = lseek ( fd, 0L, SEEK_CUR );               // Get the current file descriptor offset
114                         lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
115
116                         p[1] = 0;
117                 }
118                 return buffer;
119         }
120
121         else
122                 return 0;
123 }
124
125 /*
126  * This function appends an option to a list
127  */
128 static struct mod_opt_t *append_option( struct mod_opt_t *opt_list, char *opt )
129 {
130         struct mod_opt_t *ol = opt_list;
131
132         if( ol ) {
133                 while( ol-> m_next ) {
134                         ol = ol-> m_next;
135                 }
136                 ol-> m_next = xmalloc( sizeof( struct mod_opt_t ) );
137                 ol = ol-> m_next;
138         } else {
139                 ol = opt_list = xmalloc( sizeof( struct mod_opt_t ) );
140         }
141
142         ol-> m_opt_val = xstrdup( opt );
143         ol-> m_next = NULL;
144
145         return opt_list;
146 }
147
148 #if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS
149 /* static char* parse_command_string( char* src, char **dst );
150  *   src: pointer to string containing argument
151  *   dst: pointer to where to store the parsed argument
152  *   return value: the pointer to the first char after the parsed argument,
153  *                 NULL if there was no argument parsed (only trailing spaces).
154  *   Note that memory is allocated with xstrdup when a new argument was
155  *   parsed. Don't forget to free it!
156  */
157 #define ARG_EMPTY      0x00
158 #define ARG_IN_DQUOTES 0x01
159 #define ARG_IN_SQUOTES 0x02
160 static char *parse_command_string( char *src, char **dst )
161 {
162         int opt_status = ARG_EMPTY;
163         char* tmp_str;
164
165         /* Dumb you, I have nothing to do... */
166         if( src == NULL ) return src;
167
168         /* Skip leading spaces */
169         while( *src == ' ' ) {
170                 src++;
171         }
172         /* Is the end of string reached? */
173         if( *src == '\0' ) {
174                 return NULL;
175         }
176         /* Reached the start of an argument
177          * By the way, we duplicate a little too much
178          * here but what is too much is freed later. */
179         *dst = tmp_str = xstrdup( src );
180         /* Get to the end of that argument */
181         while(    ( *tmp_str != '\0' )
182                && (    ( *tmp_str != ' ' )
183                     || ( opt_status & ( ARG_IN_DQUOTES | ARG_IN_SQUOTES ) ) ) ) {
184                 switch( *tmp_str ) {
185                         case '\'':
186                                 if( opt_status & ARG_IN_DQUOTES ) {
187                                         /* Already in double quotes, keep current char as is */
188                                 } else {
189                                         /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
190                                         memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) );
191                                         /* mark me: we enter or leave single quotes */
192                                         opt_status ^= ARG_IN_SQUOTES;
193                                         /* Back one char, as we need to re-scan the new char there. */
194                                         tmp_str--;
195                                 }
196                         break;
197                         case '"':
198                                 if( opt_status & ARG_IN_SQUOTES ) {
199                                         /* Already in single quotes, keep current char as is */
200                                 } else {
201                                         /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
202                                         memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) );
203                                         /* mark me: we enter or leave double quotes */
204                                         opt_status ^= ARG_IN_DQUOTES;
205                                         /* Back one char, as we need to re-scan the new char there. */
206                                         tmp_str--;
207                                 }
208                         break;
209                         case '\\':
210                                 if( opt_status & ARG_IN_SQUOTES ) {
211                                         /* Between single quotes: keep as is. */
212                                 } else {
213                                         switch( *(tmp_str+1) ) {
214                                                 case 'a':
215                                                 case 'b':
216                                                 case 't':
217                                                 case 'n':
218                                                 case 'v':
219                                                 case 'f':
220                                                 case 'r':
221                                                 case '0':
222                                                         /* We escaped a special character. For now, keep
223                                                          * both the back-slash and the following char. */
224                                                         tmp_str++; src++;
225                                                         break;
226                                                 default:
227                                                         /* We escaped a space or a single or double quote,
228                                                          * or a back-slash, or a non-escapable char. Remove
229                                                          * the '\' and keep the new current char as is. */
230                                                         memmove( tmp_str, tmp_str + 1, strlen( tmp_str ) );
231                                                         break;
232                                         }
233                                 }
234                         break;
235                         /* Any other char that is special shall appear here.
236                          * Example: $ starts a variable
237                         case '$':
238                                 do_variable_expansion();
239                                 break;
240                          * */
241                         default:
242                                 /* any other char is kept as is. */
243                                 break;
244                 }
245                 tmp_str++; /* Go to next char */
246                 src++; /* Go to next char to find the end of the argument. */
247         }
248         /* End of string, but still no ending quote */
249         if( opt_status & ( ARG_IN_DQUOTES | ARG_IN_SQUOTES ) ) {
250                 bb_error_msg_and_die( "unterminated (single or double) quote in options list: %s", src );
251         }
252         *tmp_str++ = '\0';
253         *dst = xrealloc( *dst, (tmp_str - *dst ) );
254         return src;
255 }
256 #else
257 #define parse_command_string(src, dst)  (0)
258 #endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */
259
260 /*
261  * This function reads aliases and default module options from a configuration file
262  * (/etc/modprobe.conf syntax). It supports includes (only files, no directories).
263  */
264 static void include_conf ( struct dep_t **first, struct dep_t **current, char *buffer, int buflen, int fd )
265 {
266         int continuation_line = 0;
267
268         // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
269
270         while ( reads ( fd, buffer, buflen)) {
271                 int l;
272                 char *p;
273
274                 p = strchr ( buffer, '#' );
275                 if ( p )
276                         *p = 0;
277
278                 l = strlen ( buffer );
279
280                 while ( l && isspace ( buffer [l-1] )) {
281                         buffer [l-1] = 0;
282                         l--;
283                 }
284
285                 if ( l == 0 ) {
286                         continuation_line = 0;
287                         continue;
288                 }
289
290                 if ( !continuation_line ) {
291                         if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
292                                 char *alias, *mod;
293
294                                 if ( parse_tag_value ( buffer + 6, &alias, &mod )) {
295                                         /* handle alias as a module dependent on the aliased module */
296                                         if ( !*current ) {
297                                                 (*first) = (*current) = (struct dep_t *) xzalloc (sizeof ( struct dep_t ));
298                                         }
299                                         else {
300                                                 (*current)-> m_next = (struct dep_t *) xzalloc (sizeof ( struct dep_t ));
301                                                 (*current) = (*current)-> m_next;
302                                         }
303                                         (*current)-> m_name  = xstrdup ( alias );
304                                         (*current)-> m_isalias = 1;
305
306                                         if (( strcmp ( mod, "off" ) == 0 ) || ( strcmp ( mod, "null" ) == 0 )) {
307                                                 (*current)-> m_depcnt = 0;
308                                                 (*current)-> m_deparr = 0;
309                                         }
310                                         else {
311                                                 (*current)-> m_depcnt  = 1;
312                                                 (*current)-> m_deparr  = xmalloc ( 1 * sizeof( char * ));
313                                                 (*current)-> m_deparr[0] = xstrdup ( mod );
314                                         }
315                                         (*current)-> m_next    = 0;
316                                 }
317                         }
318                         else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) {
319                                 char *mod, *opt;
320
321                                 /* split the line in the module/alias name, and options */
322                                 if ( parse_tag_value ( buffer + 8, &mod, &opt )) {
323                                         struct dep_t *dt;
324
325                                         /* find the corresponding module */
326                                         for ( dt = *first; dt; dt = dt-> m_next ) {
327                                                 if ( strcmp ( dt-> m_name, mod ) == 0 )
328                                                         break;
329                                         }
330                                         if ( dt ) {
331                                                 if ( ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS ) {
332                                                         char* new_opt = NULL;
333                                                         while( ( opt = parse_command_string( opt, &new_opt ) ) ) {
334                                                                 dt-> m_options = append_option( dt-> m_options, new_opt );
335                                                         }
336                                                 } else {
337                                                         dt-> m_options = append_option( dt-> m_options, opt );
338                                                 }
339                                         }
340                                 }
341                         }
342                         else if (( strncmp ( buffer, "include", 7 ) == 0 ) && isspace ( buffer [7] )) {
343
344                                 int fdi; char *filename = buffer + 8;
345
346                                 while ( isspace ( *filename ))
347                                         filename++;
348
349                                 if (( fdi = open ( filename, O_RDONLY )) >= 0 ) {
350                                         include_conf(first, current, buffer, buflen, fdi);
351                                         close(fdi);
352                                 }
353                         }
354                 }
355         }
356 }
357
358 /*
359  * This function builds a list of dependency rules from /lib/modules/`uname -r`\modules.dep.
360  * It then fills every modules and aliases with their default options, found by parsing
361  * modprobe.conf (or modules.conf, or conf.modules).
362  */
363 static struct dep_t *build_dep ( void )
364 {
365         int fd;
366         struct utsname un;
367         struct dep_t *first = 0;
368         struct dep_t *current = 0;
369         char buffer[2048];
370         char *filename;
371         int continuation_line = 0;
372         int k_version;
373
374         k_version = 0;
375         if ( uname ( &un ))
376                 bb_error_msg_and_die("can't determine kernel version");
377
378         if (un.release[0] == '2') {
379                 k_version = un.release[2] - '0';
380         }
381
382         filename = xasprintf("/lib/modules/%s/modules.dep", un.release );
383         fd = open ( filename, O_RDONLY );
384         if (ENABLE_FEATURE_CLEAN_UP)
385                 free(filename);
386         if (fd < 0) {
387                 /* Ok, that didn't work.  Fall back to looking in /lib/modules */
388                 if (( fd = open ( "/lib/modules/modules.dep", O_RDONLY )) < 0 ) {
389                         return 0;
390                 }
391         }
392
393         while ( reads ( fd, buffer, sizeof( buffer ))) {
394                 int l = strlen ( buffer );
395                 char *p = 0;
396
397                 while ( l > 0 && isspace ( buffer [l-1] )) {
398                         buffer [l-1] = 0;
399                         l--;
400                 }
401
402                 if ( l == 0 ) {
403                         continuation_line = 0;
404                         continue;
405                 }
406
407                 /* Is this a new module dep description? */
408                 if ( !continuation_line ) {
409                         /* find the dep beginning */
410                         char *col = strchr ( buffer, ':' );
411                         char *dot = col;
412
413                         if ( col ) {
414                                 /* This line is a dep description */
415                                 char *mods;
416                                 char *modpath;
417                                 char *mod;
418
419                                 /* Find the beginning of the module file name */
420                                 *col = 0;
421                                 mods = strrchr ( buffer, '/' );
422
423                                 if ( !mods )
424                                         mods = buffer; /* no path for this module */
425                                 else
426                                         mods++; /* there was a path for this module... */
427
428                                 /* find the path of the module */
429                                 modpath = strchr ( buffer, '/' ); /* ... and this is the path */
430                                 if ( !modpath )
431                                         modpath = buffer; /* module with no path */
432                                 /* find the end of the module name in the file name */
433                                 if ( ENABLE_FEATURE_2_6_MODULES &&
434                                      (k_version > 4) && ( *(col-3) == '.' ) &&
435                                      ( *(col-2) == 'k' ) && ( *(col-1) == 'o' ) )
436                                         dot = col - 3;
437                                 else
438                                         if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
439                                                 dot = col - 2;
440
441                                 mod = xstrndup ( mods, dot - mods );
442
443                                 /* enqueue new module */
444                                 if ( !current ) {
445                                         first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
446                                 }
447                                 else {
448                                         current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
449                                         current = current-> m_next;
450                                 }
451                                 current-> m_name  = mod;
452                                 current-> m_path  = xstrdup(modpath);
453                                 current-> m_options = NULL;
454                                 current-> m_isalias = 0;
455                                 current-> m_depcnt  = 0;
456                                 current-> m_deparr  = 0;
457                                 current-> m_next    = 0;
458
459                                 p = col + 1;
460                         }
461                         else
462                                 /* this line is not a dep description */
463                                 p = 0;
464                 }
465                 else
466                         /* It's a dep description continuation */
467                         p = buffer;
468
469                 while ( p && *p && isblank(*p))
470                         p++;
471
472                 /* p points to the first dependable module; if NULL, no dependable module */
473                 if ( p && *p ) {
474                         char *end = &buffer [l-1];
475                         char *deps;
476                         char *dep;
477                         char *next;
478                         int ext = 0;
479
480                         while ( isblank ( *end ) || ( *end == '\\' ))
481                                 end--;
482
483                         do
484                         {
485                                 /* search the end of the dependency */
486                                 next = strchr (p, ' ' );
487                                 if (next)
488                                 {
489                                         *next = 0;
490                                         next--;
491                                 }
492                                 else
493                                         next = end;
494
495                                 /* find the beginning of the module file name */
496                                 deps = strrchr ( p, '/' );
497
498                                 if ( !deps || ( deps < p )) {
499                                         deps = p;
500
501                                         while ( isblank ( *deps ))
502                                                 deps++;
503                                 }
504                                 else
505                                         deps++;
506
507                                 /* find the end of the module name in the file name */
508                                 if ( ENABLE_FEATURE_2_6_MODULES &&
509                                      (k_version > 4) && ( *(next-2) == '.' ) &&
510                                      ( *(next-1) == 'k' )  && ( *next == 'o' ) )
511                                         ext = 3;
512                                 else
513                                         if (( *(next-1) == '.' ) && ( *next == 'o' ))
514                                                 ext = 2;
515
516                                 /* Cope with blank lines */
517                                 if ((next-deps-ext+1) <= 0)
518                                         continue;
519                                 dep = xstrndup ( deps, next - deps - ext + 1 );
520
521                                 /* Add the new dependable module name */
522                                 current-> m_depcnt++;
523                                 current-> m_deparr = (char **) xrealloc ( current-> m_deparr,
524                                                 sizeof ( char *) * current-> m_depcnt );
525                                 current-> m_deparr [current-> m_depcnt - 1] = dep;
526
527                                 p = next + 2;
528                         } while (next < end);
529                 }
530
531                 /* is there other dependable module(s) ? */
532                 if ( buffer [l-1] == '\\' )
533                         continuation_line = 1;
534                 else
535                         continuation_line = 0;
536         }
537         close ( fd );
538
539         /*
540          * First parse system-specific options and aliases
541          * as they take precedence over the kernel ones.
542          */
543         if (!ENABLE_FEATURE_2_6_MODULES
544                         || ( fd = open ( "/etc/modprobe.conf", O_RDONLY )) < 0 )
545                 if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
546                         fd = open ( "/etc/conf.modules", O_RDONLY );
547
548         if (fd >= 0) {
549                 include_conf (&first, &current, buffer, sizeof(buffer), fd);
550                 close(fd);
551         }
552
553         /* Only 2.6 has a modules.alias file */
554         if (ENABLE_FEATURE_2_6_MODULES) {
555                 /* Parse kernel-declared aliases */
556                 filename = xasprintf("/lib/modules/%s/modules.alias", un.release);
557                 if ((fd = open ( filename, O_RDONLY )) < 0) {
558                         /* Ok, that didn't work.  Fall back to looking in /lib/modules */
559                         fd = open ( "/lib/modules/modules.alias", O_RDONLY );
560                 }
561                 if (ENABLE_FEATURE_CLEAN_UP)
562                         free(filename);
563
564                 if (fd >= 0) {
565                         include_conf (&first, &current, buffer, sizeof(buffer), fd);
566                         close(fd);
567                 }
568         }
569
570         return first;
571 }
572
573 /* return 1 = loaded, 0 = not loaded, -1 = can't tell */
574 static int already_loaded (const char *name)
575 {
576         int fd, ret = 0;
577         char buffer[4096];
578
579         fd = open ("/proc/modules", O_RDONLY);
580         if (fd < 0)
581                 return -1;
582
583         while ( reads ( fd, buffer, sizeof( buffer ))) {
584                 char *p;
585
586                 p = strchr (buffer, ' ');
587                 if (p) {
588                         const char *n;
589
590                         // Truncate buffer at first space and check for matches, with
591                         // the idiosyncrasy that _ and - are interchangeable because the
592                         // 2.6 kernel does weird things.
593
594                         *p = 0;
595                         for (p = buffer, n = name; ; p++, n++) {
596                                 if (*p != *n) {
597                                         if ((*p == '_' || *p == '-') && (*n == '_' || *n == '-'))
598                                                 continue;
599                                         break;
600                                 }
601                                 // If we made it to the end, that's a match.
602                                 if (!*p) {
603                                         ret = 1;
604                                         goto done;
605                                 }
606                         }
607                 }
608         }
609 done:
610         close (fd);
611         return ret;
612 }
613
614 static int mod_process ( struct mod_list_t *list, int do_insert )
615 {
616         int rc = 0;
617         char **argv = NULL;
618         struct mod_opt_t *opts;
619         int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */
620         int argc;
621
622         while ( list ) {
623                 argc = 0;
624                 if( ENABLE_FEATURE_CLEAN_UP )
625                         argc_malloc = 0;
626                 /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory
627                  * each time we allocate memory for argv.
628                  * But it is (quite) small amounts of memory that leak each
629                  * time a module is loaded,  and it is reclaimed when modprobe
630                  * exits anyway (even when standalone shell?).
631                  * This could become a problem when loading a module with LOTS of
632                  * dependencies, with LOTS of options for each dependencies, with
633                  * very little memory on the target... But in that case, the module
634                  * would not load because there is no more memory, so there's no
635                  * problem. */
636                 /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */
637                 argv = (char**) malloc( 6 * sizeof( char* ) );
638                 if ( do_insert ) {
639                         if (already_loaded (list->m_name) != 1) {
640                                 argv[argc++] = "insmod";
641                                 if (ENABLE_FEATURE_2_4_MODULES) {
642                                         if (do_syslog)
643                                                 argv[argc++] = "-s";
644                                         if (autoclean)
645                                                 argv[argc++] = "-k";
646                                         if (quiet)
647                                                 argv[argc++] = "-q";
648                                         else if(verbose) /* verbose and quiet are mutually exclusive */
649                                                 argv[argc++] = "-v";
650                                 }
651                                 argv[argc++] = list-> m_path;
652                                 if( ENABLE_FEATURE_CLEAN_UP )
653                                         argc_malloc = argc;
654                                 opts = list-> m_options;
655                                 while( opts ) {
656                                         /* Add one more option */
657                                         argc++;
658                                         argv = (char**) xrealloc( argv, ( argc + 1 ) * sizeof( char* ) );
659                                         argv[argc-1] = opts-> m_opt_val;
660                                         opts = opts-> m_next;
661                                 }
662                         }
663                 } else {
664                         /* modutils uses short name for removal */
665                         if (already_loaded (list->m_name) != 0) {
666                                 argv[argc++] = "rmmod";
667                                 if (do_syslog)
668                                         argv[argc++] = "-s";
669                                 argv[argc++] = list->m_name;
670                                 if( ENABLE_FEATURE_CLEAN_UP )
671                                         argc_malloc = argc;
672                         }
673                 }
674                 argv[argc] = NULL;
675
676                 if (argc) {
677                         if (verbose) {
678                                 printf("%s module %s\n", do_insert?"Loading":"Unloading", list-> m_name );
679                         }
680                         if (!show_only) {
681                                 int rc2 = wait4pid(spawn(argv));
682                                 
683                                 if (do_insert) {
684                                         rc = rc2; /* only last module matters */
685                                 }
686                                 else if (!rc2) {
687                                         rc = 0; /* success if remove any mod */
688                                 }
689                         }
690                         if( ENABLE_FEATURE_CLEAN_UP )
691                                 /* the last value in the array has index == argc, but
692                                  * it is the terminating NULL, so we must not free it. */
693                                 while( argc_malloc < argc ) {
694                                         free( argv[argc_malloc++] );
695                         }
696                 }
697                 if( ENABLE_FEATURE_CLEAN_UP ) {
698                         free( argv );
699                         argv = NULL;
700                 }
701                 list = do_insert ? list-> m_prev : list-> m_next;
702         }
703         return (show_only) ? 0 : rc;
704 }
705
706 /*
707  * Check the matching between a pattern and a module name.
708  * We need this as *_* is equivalent to *-*, even in pattern matching.
709  */
710 static int check_pattern( const char* pat_src, const char* mod_src ) {
711         int ret;
712
713         if (ENABLE_FEATURE_MODPROBE_FANCY_ALIAS) {
714                 char* pat;
715                 char* mod;
716                 char* p;
717
718                 pat = xstrdup (pat_src);
719                 mod = xstrdup (mod_src);
720
721                 for (p = pat; (p = strchr(p, '-')); *p++ = '_' );
722                 for (p = mod; (p = strchr(p, '-')); *p++ = '_' );
723
724                 ret = fnmatch ( pat, mod, 0 );
725
726                 if (ENABLE_FEATURE_CLEAN_UP) {
727                         free (pat);
728                         free (mod);
729                 }
730
731                 return ret;
732         } else {
733                 return fnmatch ( pat_src, mod_src, 0 );
734         }
735 }
736
737 /*
738  * Builds the dependency list (aka stack) of a module.
739  * head: the highest module in the stack (last to insmod, first to rmmod)
740  * tail: the lowest module in the stack (first to insmod, last to rmmod)
741  */
742 static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
743 {
744         struct mod_list_t *find;
745         struct dep_t *dt;
746         struct mod_opt_t *opt = 0;
747         char *path = 0;
748
749         /* Search for the given module name amongst all dependency rules.
750          * The module name in a dependency rule can be a shell pattern,
751          * so try to match the given module name against such a pattern.
752          * Of course if the name in the dependency rule is a plain string,
753          * then we consider it a pattern, and matching will still work. */
754         for ( dt = depend; dt; dt = dt-> m_next ) {
755                 if ( check_pattern ( dt-> m_name, mod ) == 0) {
756                         break;
757                 }
758         }
759
760         if( !dt ) {
761                 bb_error_msg ("module %s not found.", mod);
762                 return;
763         }
764
765         // resolve alias names
766         while ( dt-> m_isalias ) {
767                 if ( dt-> m_depcnt == 1 ) {
768                         struct dep_t *adt;
769
770                         for ( adt = depend; adt; adt = adt-> m_next ) {
771                                 if ( check_pattern ( adt-> m_name, dt-> m_deparr [0] ) == 0 )
772                                         break;
773                         }
774                         if ( adt ) {
775                                 /* This is the module we are aliased to */
776                                 struct mod_opt_t *opts = dt-> m_options;
777                                 /* Option of the alias are appended to the options of the module */
778                                 while( opts ) {
779                                         adt-> m_options = append_option( adt-> m_options, opts-> m_opt_val );
780                                         opts = opts-> m_next;
781                                 }
782                                 dt = adt;
783                         }
784                         else {
785                                 bb_error_msg ("module %s not found.", mod);
786                                 return;
787                         }
788                 }
789                 else {
790                         bb_error_msg ("Bad alias %s", dt-> m_name);
791                         return;
792                 }
793         }
794
795         mod = dt-> m_name;
796         path = dt-> m_path;
797         opt = dt-> m_options;
798
799         // search for duplicates
800         for ( find = *head; find; find = find-> m_next ) {
801                 if ( !strcmp ( mod, find-> m_name )) {
802                         // found -> dequeue it
803
804                         if ( find-> m_prev )
805                                 find-> m_prev-> m_next = find-> m_next;
806                         else
807                                 *head = find-> m_next;
808
809                         if ( find-> m_next )
810                                 find-> m_next-> m_prev = find-> m_prev;
811                         else
812                                 *tail = find-> m_prev;
813
814                         break; // there can be only one duplicate
815                 }
816         }
817
818         if ( !find ) { // did not find a duplicate
819                 find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));
820                 find-> m_name = mod;
821                 find-> m_path = path;
822                 find-> m_options = opt;
823         }
824
825         // enqueue at tail
826         if ( *tail )
827                 (*tail)-> m_next = find;
828         find-> m_prev   = *tail;
829         find-> m_next   = 0;
830
831         if ( !*head )
832                 *head = find;
833         *tail = find;
834
835         if ( dt ) {
836                 int i;
837
838                 /* Add all dependable module for that new module */
839                 for ( i = 0; i < dt-> m_depcnt; i++ )
840                         check_dep ( dt-> m_deparr [i], head, tail );
841         }
842 }
843
844 static int mod_insert ( char *mod, int argc, char **argv )
845 {
846         struct mod_list_t *tail = 0;
847         struct mod_list_t *head = 0;
848         int rc;
849
850         // get dep list for module mod
851         check_dep ( mod, &head, &tail );
852
853         if ( head && tail ) {
854                 if( argc ) {
855                         int i;
856                         // append module args
857                         for ( i = 0; i < argc; i++ )
858                                 head->m_options = append_option( head->m_options, argv[i] );
859                 }
860
861                 // process tail ---> head
862                 rc = mod_process ( tail, 1 );
863         }
864         else
865                 rc = 1;
866
867         return rc;
868 }
869
870 static int mod_remove ( char *mod )
871 {
872         int rc;
873         static struct mod_list_t rm_a_dummy = { "-a", NULL, NULL, NULL, NULL };
874
875         struct mod_list_t *head = 0;
876         struct mod_list_t *tail = 0;
877
878         if ( mod )
879                 check_dep ( mod, &head, &tail );
880         else  // autoclean
881                 head = tail = &rm_a_dummy;
882
883         if ( head && tail )
884                 rc = mod_process ( head, 0 );  // process head ---> tail
885         else
886                 rc = 1;
887         return rc;
888
889 }
890
891 int modprobe_main(int argc, char** argv)
892 {
893         int rc = EXIT_SUCCESS;
894         char *unused;
895
896         bb_opt_complementally = "?V-:q-v:v-q";
897         main_opts = bb_getopt_ulflags(argc, argv, "acdklnqrst:vVC:",
898                                                         &unused, &unused);
899         if((main_opts & (DUMP_CONF_EXIT | LIST_ALL)))
900                                 return EXIT_SUCCESS;
901         if((main_opts & (RESTRICT_DIR | CONFIG_FILE)))
902                                 bb_error_msg_and_die("-t and -C not supported");
903
904         depend = build_dep ( );
905
906         if ( !depend )
907                 bb_error_msg_and_die ( "could not parse modules.dep" );
908
909         if (remove_opt) {
910                 do {
911                         if (mod_remove ( optind < argc ?
912                                                 argv [optind] : NULL )) {
913                                 bb_error_msg ("failed to remove module %s",
914                                                 argv [optind] );
915                                 rc = EXIT_FAILURE;
916                         }
917                 } while ( ++optind < argc );
918         } else {
919                 if (optind >= argc)
920                         bb_error_msg_and_die ( "No module or pattern provided" );
921
922                 if ( mod_insert ( argv [optind], argc - optind - 1, argv + optind + 1 ))
923                         bb_error_msg_and_die ( "failed to load module %s", argv [optind] );
924         }
925
926         /* Here would be a good place to free up memory allocated during the dependencies build. */
927
928         return rc;
929 }