99650ff032b290772ae7429a824ecee4a2c96050
[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  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  *
21 */
22
23 #include <sys/utsname.h>
24 #include <getopt.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <syslog.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <fcntl.h>
31 #include "busybox.h"
32
33
34
35 struct dep_t {
36         char *  m_module;
37         char *  m_options;
38         
39         int     m_isalias  : 1;
40         int     m_reserved : 15;
41         
42         int     m_depcnt   : 16;        
43         char ** m_deparr;
44         
45         struct dep_t * m_next;
46 };
47
48 struct mod_list_t {
49         char *  m_module;
50         char *  m_options;
51         
52         struct mod_list_t * m_prev;
53         struct mod_list_t * m_next;
54 };
55
56
57 static struct dep_t *depend;
58 static int autoclean, show_only, quiet, do_syslog, verbose;
59
60 int parse_tag_value ( char *buffer, char **ptag, char **pvalue )
61 {
62         char *tag, *value;
63
64         while ( isspace ( *buffer ))
65                 buffer++;                       
66         tag = value = buffer;
67         while ( !isspace ( *value ))
68                 value++;
69         *value++ = 0;
70         while ( isspace ( *value ))
71                 value++;
72
73         *ptag = tag;
74         *pvalue = value;
75         
76         return xstrlen( tag ) && xstrlen( value );
77 }
78
79 /* Jump through hoops to simulate how fgets() grabs just one line at a
80  * time... Don't use any stdio since modprobe gets called from a kernel
81  * thread and stdio junk can overflow the limited stack... 
82  */
83 static char *reads ( int fd, char *buffer, size_t len )
84 {
85         int n = read ( fd, buffer, len );
86         
87         if ( n > 0 ) {
88                 char *p;
89         
90                 buffer [len-1] = 0;
91                 p = strchr ( buffer, '\n' );
92                 
93                 if ( p ) {
94                         off_t offset;
95                         
96                         offset = lseek ( fd, 0L, SEEK_CUR );               // Get the current file descriptor offset 
97                         lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
98
99                         p[1] = 0;
100                 }
101                 return buffer;
102         }
103         
104         else
105                 return 0;
106 }
107
108 static struct dep_t *build_dep ( void )
109 {
110         int fd;
111         struct utsname un;
112         struct dep_t *first = 0;
113         struct dep_t *current = 0;
114         char buffer[256];
115         char *filename = buffer;
116         int continuation_line = 0;
117         
118         if ( uname ( &un ))
119                 return 0;
120                 
121         // check for buffer overflow in following code
122         if ( xstrlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
123                 return 0;
124         }
125                                 
126         strcpy ( filename, "/lib/modules/" );
127         strcat ( filename, un.release );
128         strcat ( filename, "/modules.dep" );
129
130         if (( fd = open ( filename, O_RDONLY )) < 0 )
131                 return 0;
132
133         while ( reads ( fd, buffer, sizeof( buffer ))) {
134                 int l = xstrlen ( buffer );
135                 char *p = 0;
136                 
137                 while ( isspace ( buffer [l-1] )) {
138                         buffer [l-1] = 0;
139                         l--;
140                 }
141                 
142                 if ( l == 0 ) {
143                         continuation_line = 0;
144                         continue;
145                 }
146                 
147                 if ( !continuation_line ) {             
148                         char *col = strchr ( buffer, ':' );
149                 
150                         if ( col ) {
151                                 char *mods;
152                                 char *mod;
153                                 int ext = 0;
154                                 
155                                 *col = 0;
156                                 mods = strrchr ( buffer, '/' );
157                                 
158                                 if ( !mods )
159                                         mods = buffer;
160                                 else
161                                         mods++;
162                                         
163                                 if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
164                                         ext = 2;
165                                 
166                                 mod = xstrndup ( mods, col - mods - ext );
167                                         
168                                 if ( !current ) {
169                                         first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
170                                 }
171                                 else {
172                                         current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
173                                         current = current-> m_next;
174                                 }
175                                 current-> m_module  = mod;
176                                 current-> m_options = 0;
177                                 current-> m_isalias = 0;
178                                 current-> m_depcnt  = 0;
179                                 current-> m_deparr  = 0;
180                                 current-> m_next    = 0;
181                                                 
182                                 //printf ( "%s:\n", mod );
183                                                 
184                                 p = col + 1;            
185                         }
186                         else
187                                 p = 0;
188                 }
189                 else
190                         p = buffer;
191                         
192                 if ( p && *p ) {
193                         char *end = &buffer [l-1];
194                         char *deps = strrchr ( end, '/' );
195                         char *dep;
196                         int ext = 0;
197                         
198                         while ( isblank ( *end ) || ( *end == '\\' ))
199                                 end--;
200                                 
201                         deps = strrchr ( p, '/' );
202                         
203                         if ( !deps || ( deps < p )) {
204                                 deps = p;
205                 
206                                 while ( isblank ( *deps ))
207                                         deps++;
208                         }
209                         else
210                                 deps++;
211                         
212                         if (( *(end-1) == '.' ) && ( *end == 'o' ))
213                                 ext = 2;
214
215                         /* Cope with blank lines */
216                         if ((end-deps-ext+1) <= 0)
217                                 continue;
218                         
219                         dep = xstrndup ( deps, end - deps - ext + 1 );
220                         
221                         current-> m_depcnt++;
222                         current-> m_deparr = (char **) xrealloc ( current-> m_deparr, sizeof ( char *) * current-> m_depcnt );
223                         current-> m_deparr [current-> m_depcnt - 1] = dep;              
224                         
225                         //printf ( "    %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
226                 }
227         
228                 if ( buffer [l-1] == '\\' )
229                         continuation_line = 1;
230                 else
231                         continuation_line = 0;
232         }
233         close ( fd );
234
235         // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
236
237         if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
238                 if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
239                         return first;
240         
241         continuation_line = 0;
242         while ( reads ( fd, buffer, sizeof( buffer ))) {
243                 int l;
244                 char *p;
245                 
246                 p = strchr ( buffer, '#' );     
247                 if ( p )
248                         *p = 0;
249                         
250                 l = xstrlen ( buffer );
251         
252                 while ( l && isspace ( buffer [l-1] )) {
253                         buffer [l-1] = 0;
254                         l--;
255                 }
256                 
257                 if ( l == 0 ) {
258                         continuation_line = 0;
259                         continue;
260                 }
261                 
262                 if ( !continuation_line ) {             
263                         if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
264                                 char *alias, *mod;
265
266                                 if ( parse_tag_value ( buffer + 6, &alias, &mod )) {
267                                         // fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
268                                 
269                                         if ( !current ) {
270                                                 first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
271                                         }
272                                         else {
273                                                 current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
274                                                 current = current-> m_next;
275                                         }
276                                         current-> m_module  = xstrdup ( alias );
277                                         current-> m_isalias = 1;
278                                         
279                                         if (( strcmp ( alias, "off" ) == 0 ) || ( strcmp ( alias, "null" ) == 0 )) {
280                                                 current-> m_depcnt = 0;
281                                                 current-> m_deparr = 0;
282                                         }
283                                         else {
284                                                 current-> m_depcnt  = 1;
285                                                 current-> m_deparr  = xmalloc ( 1 * sizeof( char * ));
286                                                 current-> m_deparr[0] = xstrdup ( mod );
287                                         }
288                                         current-> m_next    = 0;                                        
289                                 }
290                         }                               
291                         else if (( strncmp ( buffer, "options", 7 ) == 0 ) && isspace ( buffer [7] )) { 
292                                 char *mod, *opt;
293                                 
294                                 if ( parse_tag_value ( buffer + 8, &mod, &opt )) {
295                                         struct dep_t *dt;
296         
297                                         for ( dt = first; dt; dt = dt-> m_next ) {
298                                                 if ( strcmp ( dt-> m_module, mod ) == 0 ) 
299                                                         break;
300                                         }
301                                         if ( dt ) {
302                                                 dt-> m_options = xrealloc ( dt-> m_options, xstrlen( opt ) + 1 );
303                                                 strcpy ( dt-> m_options, opt );
304                                                 
305                                                 // fprintf ( stderr, "OPTION: '%s' -> '%s'\n", dt-> m_module, dt-> m_options );
306                                         }
307                                 }
308                         }
309                 }
310         }
311         close ( fd );
312         
313         return first;
314 }
315
316
317 static int mod_process ( struct mod_list_t *list, int do_insert )
318 {
319         char lcmd [256];
320         int rc = 0;
321
322         if ( !list )
323                 return 1;
324
325         while ( list ) {
326                 if ( do_insert )
327                         snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s %s 2>/dev/null", do_syslog ? "-s" : "", autoclean ? "-k" : "", quiet ? "-q" : "", list-> m_module, list-> m_options ? list-> m_options : "" );
328                 else
329                         snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s 2>/dev/null", do_syslog ? "-s" : "", list-> m_module );
330                 
331                 if ( verbose )
332                         printf ( "%s\n", lcmd );
333                 if ( !show_only )
334                         rc |= system ( lcmd );
335                         
336                 list = do_insert ? list-> m_prev : list-> m_next;
337         }
338         return rc;
339 }
340
341 static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
342 {
343         struct mod_list_t *find;
344         struct dep_t *dt;
345         char *opt = 0;
346         int lm;
347
348         // remove .o extension
349         lm = xstrlen ( mod );
350         if (( mod [lm-2] == '.' ) && ( mod [lm-1] == 'o' ))
351                 mod [lm-2] = 0;
352
353         // check dependencies
354         for ( dt = depend; dt; dt = dt-> m_next ) {
355                 if ( strcmp ( dt-> m_module, mod ) == 0 ) {
356                         opt = dt-> m_options;
357                         break;
358                 }
359         }
360         
361         // resolve alias names
362         while ( dt && dt-> m_isalias ) {
363                 if ( dt-> m_depcnt == 1 ) {
364                         struct dep_t *adt;
365                 
366                         for ( adt = depend; adt; adt = adt-> m_next ) {
367                                 if ( strcmp ( adt-> m_module, dt-> m_deparr [0] ) == 0 )
368                                         break;
369                         }
370                         if ( adt ) {
371                                 dt = adt;                       
372                                 mod = dt-> m_module;
373                                 if ( !opt )
374                                         opt = dt-> m_options;
375                         }
376                         else
377                                 return;
378                 }
379                 else
380                         return;                 
381         }
382         
383         // search for duplicates
384         for ( find = *head; find; find = find-> m_next ) {
385                 if ( !strcmp ( mod, find-> m_module )) {
386                         // found -> dequeue it
387
388                         if ( find-> m_prev )
389                                 find-> m_prev-> m_next = find-> m_next;
390                         else
391                                 *head = find-> m_next;
392                                         
393                         if ( find-> m_next )
394                                 find-> m_next-> m_prev = find-> m_prev;
395                         else
396                                 *tail = find-> m_prev;
397                                         
398                         break; // there can be only one duplicate
399                 }                               
400         }
401
402         if ( !find ) { // did not find a duplicate
403                 find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));              
404                 find-> m_module = mod;
405                 find-> m_options = opt;
406         }
407
408         // enqueue at tail      
409         if ( *tail )
410                 (*tail)-> m_next = find;
411         find-> m_prev   = *tail;
412         find-> m_next   = 0;
413
414         if ( !*head )
415                 *head = find;
416         *tail = find;
417                 
418         if ( dt ) {     
419                 int i;
420                 
421                 for ( i = 0; i < dt-> m_depcnt; i++ )
422                         check_dep ( dt-> m_deparr [i], head, tail );
423         }
424 }
425
426
427
428 static int mod_insert ( char *mod, int argc, char **argv )
429 {
430         struct mod_list_t *tail = 0;
431         struct mod_list_t *head = 0;    
432         int rc = 0;
433         
434         // get dep list for module mod
435         check_dep ( mod, &head, &tail );
436         
437         if ( head && tail ) {
438                 if ( argc ) {
439                         int i;          
440                         int l = 0;
441         
442                         // append module args
443                         for ( i = 0; i < argc; i++ ) 
444                                 l += ( xstrlen ( argv [i] ) + 1 );
445                 
446                         head-> m_options = xrealloc ( head-> m_options, l + 1 );
447                         head-> m_options [0] = 0;
448                 
449                         for ( i = 0; i < argc; i++ ) {
450                                 strcat ( head-> m_options, argv [i] );
451                                 strcat ( head-> m_options, " " );
452                         }
453                 }
454                 
455                 // process tail ---> head
456                 rc |= mod_process ( tail, 1 );
457         }
458         else
459                 rc = 1;
460         
461         return rc;
462 }
463
464 static void mod_remove ( char *mod )
465 {
466         static struct mod_list_t rm_a_dummy = { "-a", 0, 0 }; 
467         
468         struct mod_list_t *head = 0;
469         struct mod_list_t *tail = 0;
470         
471         if ( mod )
472                 check_dep ( mod, &head, &tail );
473         else  // autoclean
474                 head = tail = &rm_a_dummy;
475         
476         if ( head && tail )
477                 mod_process ( head, 0 );  // process head ---> tail
478 }
479
480
481
482 extern int modprobe_main(int argc, char** argv)
483 {
484         int     opt;
485         int remove_opt = 0;
486
487         autoclean = show_only = quiet = do_syslog = verbose = 0;
488
489         while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
490                 switch(opt) {
491                 case 'c': // no config used
492                 case 'l': // no pattern matching
493                         return EXIT_SUCCESS;
494                         break;
495                 case 'C': // no config used
496                 case 't': // no pattern matching
497                         error_msg_and_die("-t and -C not supported");
498
499                 case 'a': // ignore
500                 case 'd': // ignore
501                         break;
502                 case 'k':
503                         autoclean++;
504                         break;
505                 case 'n':
506                         show_only++;
507                         break;
508                 case 'q':
509                         quiet++;
510                         break;
511                 case 'r':
512                         remove_opt++;
513                         break;
514                 case 's':
515                         do_syslog++;
516                         break;
517                 case 'v':
518                         verbose++;
519                         break;
520                 case 'V':
521                 default:
522                         show_usage();
523                         break;
524                 }
525         }
526         
527         depend = build_dep ( ); 
528
529         if ( !depend ) 
530                 error_msg_and_die ( "could not parse modules.dep\n" );
531         
532         if (remove_opt) {
533                 do {
534                         mod_remove ( optind < argc ? xstrdup ( argv [optind] ) : NULL );
535                 } while ( ++optind < argc );
536                 
537                 return EXIT_SUCCESS;
538         }
539
540         if (optind >= argc) 
541                 error_msg_and_die ( "No module or pattern provided\n" );
542         
543         if ( mod_insert ( xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 )) 
544                 error_msg_and_die ( "failed to load module %s", argv [optind] );
545         
546         return EXIT_SUCCESS;
547 }
548
549