Added support for /etc/modules.conf parsing
[oweals/busybox.git] / modutils / modprobe.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * really dumb modprobe implementation for busybox
4  * Copyright (C) 2001 Lineo, davidm@lineo.com
5  *
6  * dependency specific stuff completly rewritten and
7  * copyright (c) 2002 by Robert Griebl, griebl@gmx.de
8  *
9  */
10
11 #include <sys/utsname.h>
12 #include <stdio.h>
13 #include <getopt.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <syslog.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include "busybox.h"
20
21
22
23 struct dep_t {
24         char *  m_module;
25         
26         int     m_isalias  : 1;
27         int     m_reserved : 15;
28         
29         int     m_depcnt   : 16;        
30         char ** m_deparr;
31         
32         struct dep_t * m_next;
33 };
34
35 struct mod_list_t {
36         char *  m_module;
37         
38         struct mod_list_t * m_prev;
39         struct mod_list_t * m_next;
40 };
41
42
43 static struct dep_t *depend;
44 static int autoclean, show_only, quiet, do_syslog, verbose;
45
46
47 static struct dep_t *build_dep ( void )
48 {
49         struct utsname un;
50         FILE *f;
51         struct dep_t *first = 0;
52         struct dep_t *current = 0;
53         char buffer [4096];
54         char *filename = buffer;
55         int continuation_line = 0;
56         
57         if ( uname ( &un ))
58                 return 0;
59                 
60         // check for buffer overflow in following code
61         if ( xstrlen ( un. release ) > ( sizeof( buffer ) - 64 ))
62                 return 0;
63                                 
64         strcpy ( filename, "/lib/modules/" );
65         strcat ( filename, un. release );
66         strcat ( filename, "/modules.dep" );
67
68         f = fopen ( filename, "r" );
69         if ( !f )
70                 return 0;
71         
72         while ( fgets ( buffer, sizeof( buffer), f )) {
73                 int l = xstrlen ( buffer );
74                 char *p = 0;
75                 
76                 while ( isspace ( buffer [l-1] )) {
77                         buffer [l-1] = 0;
78                         l--;
79                 }
80                 
81                 if ( l == 0 ) {
82                         continuation_line = 0;
83                         continue;
84                 }
85                 
86                 if ( !continuation_line ) {             
87                         char *col = strchr ( buffer, ':' );
88                 
89                         if ( col ) {
90                                 char *mods;
91                                 char *mod;
92                                 int ext = 0;
93                                 
94                                 *col = 0;
95                                 mods = strrchr ( buffer, '/' );
96                                 
97                                 if ( !mods )
98                                         mods = buffer;
99                                 else
100                                         mods++;
101                                         
102                                 if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
103                                         ext = 2;
104                                 
105                                 mod = xstrndup ( mods, col - mods - ext );
106                                         
107                                 if ( !current ) {
108                                         first = current = (struct dep_t *) malloc ( sizeof ( struct dep_t ));
109                                 }
110                                 else {
111                                         current-> m_next = (struct dep_t *) malloc ( sizeof ( struct dep_t ));
112                                         current = current-> m_next;
113                                 }
114                                 current-> m_module  = mod;
115                                 current-> m_isalias = 0;
116                                 current-> m_depcnt  = 0;
117                                 current-> m_deparr  = 0;
118                                 current-> m_next    = 0;
119                                                 
120                                 //printf ( "%s:\n", mod );
121                                                 
122                                 p = col + 1;            
123                         }
124                         else
125                                 p = 0;
126                 }
127                 else
128                         p = buffer;
129                         
130                 if ( p && *p ) {
131                         char *end = &buffer [l-1];
132                         char *deps = strrchr ( end, '/' );
133                         char *dep;
134                         int ext = 0;
135                         
136                         while ( isblank ( *end ) || ( *end == '\\' ))
137                                 end--;
138                                 
139                         deps = strrchr ( p, '/' );
140                         
141                         if ( !deps || ( deps < p )) {
142                                 deps = p;
143                 
144                                 while ( isblank ( *deps ))
145                                         deps++;
146                         }
147                         else
148                                 deps++;
149                         
150                         if (( *(end-1) == '.' ) && ( *end == 'o' ))
151                                 ext = 2;
152                         
153                         dep = xstrndup ( deps, end - deps - ext + 1 );
154                         
155                         current-> m_depcnt++;
156                         current-> m_deparr = (char **) xrealloc ( current-> m_deparr, sizeof ( char *) * current-> m_depcnt );
157                         current-> m_deparr [current-> m_depcnt - 1] = dep;              
158                         
159                         //printf ( "    %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
160                 }
161         
162                 if ( buffer [l-1] == '\\' )
163                         continuation_line = 1;
164                 else
165                         continuation_line = 0;
166         }
167         fclose ( f );
168
169         // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
170
171         f = fopen ( "/etc/modules.conf", "r" );
172         if ( !f )
173                 f = fopen ( "/etc/conf.modules", "r" );
174         if ( f ) {
175                 continuation_line = 0;
176         
177                 while ( fgets ( buffer, sizeof( buffer), f )) {
178                         int l;
179                         char *p;
180                         
181                         p = strchr ( buffer, '#' );
182                         if ( p )
183                                 *p = 0;
184                                 
185                         l = xstrlen ( buffer );
186                 
187                         while ( l && isspace ( buffer [l-1] )) {
188                                 buffer [l-1] = 0;
189                                 l--;
190                         }
191                         
192                         if ( l == 0 ) {
193                                 continuation_line = 0;
194                                 continue;
195                         }
196                         
197                         if ( !continuation_line ) {             
198                                 if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
199                                         char *alias, *mod;
200
201                                         alias = buffer + 6;
202                                         
203                                         while ( isspace ( *alias ))
204                                                 alias++;                        
205                                         mod = alias;                                    
206                                         while ( !isspace ( *mod ))
207                                                 mod++;
208                                         *mod = 0;
209                                         mod++;
210                                         while ( isspace ( *mod ))
211                                                 mod++;
212                                                                                         
213 //                                      fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
214                                         
215                                         if ( !current ) {
216                                                 first = current = (struct dep_t *) malloc ( sizeof ( struct dep_t ));
217                                         }
218                                         else {
219                                                 current-> m_next = (struct dep_t *) malloc ( sizeof ( struct dep_t ));
220                                                 current = current-> m_next;
221                                         }
222                                         current-> m_module  = xstrdup ( alias );
223                                         current-> m_isalias = 1;
224                                         
225                                         if (( strcmp ( alias, "off" ) == 0 ) || ( strcmp ( alias, "null" ) == 0 )) {
226                                                 current-> m_depcnt = 0;
227                                                 current-> m_deparr = 0;
228                                         }
229                                         else {
230                                                 current-> m_depcnt  = 1;
231                                                 current-> m_deparr  = xmalloc ( 1 * sizeof( char * ));
232                                                 current-> m_deparr[0] = xstrdup ( mod );
233                                         }
234                                         current-> m_next    = 0;                                        
235                                 }                               
236                         }
237                 }
238                 fclose ( f );
239         }
240         
241         return first;
242 }
243
244
245 static int mod_process ( struct mod_list_t *list, int do_insert )
246 {
247         char lcmd [256];
248         int rc = 0;
249
250         if ( !list )
251                 return 1;
252
253         while ( list ) {
254                 if ( do_insert )
255                         snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s 2>/dev/null", do_syslog ? "-s" : "", autoclean ? "-k" : "", quiet ? "-q" : "", list-> m_module );
256                 else
257                         snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s 2>/dev/null", do_syslog ? "-s" : "", list-> m_module );
258                 
259                 if ( verbose )
260                         printf ( "%s\n", lcmd );
261                 if ( !show_only )
262                         rc |= system ( lcmd );
263                         
264                 list = do_insert ? list-> m_prev : list-> m_next;
265         }
266         return rc;
267 }
268
269 static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
270 {
271         struct mod_list_t *find;
272         struct dep_t *dt;
273
274         int lm;
275
276         // remove .o extension
277         lm = xstrlen ( mod );
278         if (( mod [lm-2] == '.' ) && ( mod [lm-1] == 'o' ))
279                 mod [lm-2] = 0;
280
281         // check dependencies
282         for ( dt = depend; dt; dt = dt-> m_next ) {
283                 if ( strcmp ( dt-> m_module, mod ) == 0 ) 
284                         break;
285         }
286         // resolve alias names
287         if ( dt && dt-> m_isalias ) {
288                 if ( dt-> m_depcnt == 1 )
289                         check_dep ( dt-> m_deparr [0], head, tail );
290                 printf ( "Got alias: %s -> %s\n", mod, dt-> m_deparr [0] );
291                         
292                 return;
293         }
294         
295         // search for duplicates
296         for ( find = *head; find; find = find-> m_next ) {
297                 if ( !strcmp ( mod, find-> m_module )) {
298                         // found -> dequeue it
299
300                         if ( find-> m_prev )
301                                 find-> m_prev-> m_next = find-> m_next;
302                         else
303                                 *head = find-> m_next;
304                                         
305                         if ( find-> m_next )
306                                 find-> m_next-> m_prev = find-> m_prev;
307                         else
308                                 *tail = find-> m_prev;
309                                         
310                         break; // there can be only one duplicate
311                 }                               
312         }
313
314         if ( !find ) { // did not find a duplicate
315                 find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));              
316                 find-> m_module = mod;
317         }
318
319         // enqueue at tail      
320         if ( *tail )
321                 (*tail)-> m_next = find;
322         find-> m_prev   = *tail;
323         find-> m_next   = 0;
324
325         if ( !*head )
326                 *head = find;
327         *tail = find;
328                 
329         if ( dt ) {     
330                 int i;
331                 
332                 for ( i = 0; i < dt-> m_depcnt; i++ )
333                         check_dep ( dt-> m_deparr [i], head, tail );
334         }
335 }
336
337
338
339 static int mod_insert ( char *mod, int argc, char **argv )
340 {
341         struct mod_list_t *tail = 0;
342         struct mod_list_t *head = 0;    
343         int rc = 0;
344         
345         // get dep list for module mod
346         check_dep ( mod, &head, &tail );
347         
348         if ( head && tail ) {
349                 int i;
350                 int l = 0;
351         
352                 // append module args
353                 l = xstrlen ( head-> m_module );
354                 for ( i = 0; i < argc; i++ ) 
355                         l += ( xstrlen ( argv [i] ) + 1 );
356                 
357                 head-> m_module = realloc ( head-> m_module, l + 1 );
358                 
359                 for ( i = 0; i < argc; i++ ) {
360                         strcat ( head-> m_module, " " );
361                         strcat ( head-> m_module, argv [i] );
362                 }
363
364                 // process tail ---> head
365                 rc |= mod_process ( tail, 1 );
366         }
367         else
368                 rc = 1;
369         
370         return rc;
371 }
372
373 static void mod_remove ( char *mod )
374 {
375         static struct mod_list_t rm_a_dummy = { "-a", 0, 0 }; 
376         
377         struct mod_list_t *head = 0;
378         struct mod_list_t *tail = 0;
379         
380         if ( mod )
381                 check_dep ( mod, &head, &tail );
382         else  // autoclean
383                 head = tail = &rm_a_dummy;
384         
385         if ( head && tail )
386                 mod_process ( head, 0 );  // process head ---> tail
387 }
388
389
390
391 extern int modprobe_main(int argc, char** argv)
392 {
393         int     opt;
394         int remove_opt = 0;
395
396         autoclean = show_only = quiet = do_syslog = verbose = 0;
397
398         while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
399                 switch(opt) {
400                 case 'c': // no config used
401                 case 'l': // no pattern matching
402                         return EXIT_SUCCESS;
403                         break;
404                 case 'C': // no config used
405                 case 't': // no pattern matching
406                         error_msg_and_die("-t and -C not supported");
407
408                 case 'a': // ignore
409                 case 'd': // ignore
410                         break;
411                 case 'k':
412                         autoclean++;
413                         break;
414                 case 'n':
415                         show_only++;
416                         break;
417                 case 'q':
418                         quiet++;
419                         break;
420                 case 'r':
421                         remove_opt++;
422                         break;
423                 case 's':
424                         do_syslog++;
425                         break;
426                 case 'v':
427                         verbose++;
428                         break;
429                 case 'V':
430                 default:
431                         show_usage();
432                         break;
433                 }
434         }
435         
436         depend = build_dep ( ); 
437
438         if ( !depend ) 
439                 error_msg_and_die ( "could not parse modules.dep\n" );
440         
441         if (remove_opt) {
442                 do {
443                         mod_remove ( optind < argc ? argv [optind] : 0 );
444                 } while ( ++optind < argc );
445                 
446                 return EXIT_SUCCESS;
447         }
448
449         if (optind >= argc) 
450                 error_msg_and_die ( "No module or pattern provided\n" );
451         
452         return mod_insert ( argv [optind], argc - optind - 1, argv + optind + 1 ) ? \
453                EXIT_FAILURE : EXIT_SUCCESS;
454 }
455
456