b05158ac02e06a892ece3e42a97213a5fd8a3044
[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 <getopt.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <syslog.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <fcntl.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 /* Jump through hoops to simulate how fgets() grabs just one line at a
48  * time... Don't use any stdio since modprobe gets called from a kernel
49  * thread and stdio junk can overflow the limited stack... 
50  */
51 static char *reads ( int fd, char *buffer, size_t len )
52 {
53         int n = read ( fd, buffer, len );
54         
55         if ( n > 0 ) {
56                 char *p;
57         
58                 buffer [len-1] = 0;
59                 p = strchr ( buffer, '\n' );
60                 
61                 if ( p ) {
62                         off_t offset;
63                         
64                         offset = lseek ( fd, 0L, SEEK_CUR );               // Get the current file descriptor offset 
65                         lseek ( fd, offset-n + (p-buffer) + 1, SEEK_SET ); // Set the file descriptor offset to right after the \n
66
67                         p[1] = 0;
68                 }
69                 return buffer;
70         }
71         
72         else
73                 return 0;
74 }
75
76 static struct dep_t *build_dep ( void )
77 {
78         int fd;
79         struct utsname un;
80         struct dep_t *first = 0;
81         struct dep_t *current = 0;
82         char buffer[256];
83         char *filename = buffer;
84         int continuation_line = 0;
85         
86         if ( uname ( &un ))
87                 return 0;
88                 
89         // check for buffer overflow in following code
90         if ( xstrlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
91                 return 0;
92         }
93                                 
94         strcpy ( filename, "/lib/modules/" );
95         strcat ( filename, un.release );
96         strcat ( filename, "/modules.dep" );
97
98         if (( fd = open ( filename, O_RDONLY )) < 0 )
99                 return 0;
100
101         while ( reads ( fd, buffer, sizeof( buffer ))) {
102                 int l = xstrlen ( buffer );
103                 char *p = 0;
104                 
105                 while ( isspace ( buffer [l-1] )) {
106                         buffer [l-1] = 0;
107                         l--;
108                 }
109                 
110                 if ( l == 0 ) {
111                         continuation_line = 0;
112                         continue;
113                 }
114                 
115                 if ( !continuation_line ) {             
116                         char *col = strchr ( buffer, ':' );
117                 
118                         if ( col ) {
119                                 char *mods;
120                                 char *mod;
121                                 int ext = 0;
122                                 
123                                 *col = 0;
124                                 mods = strrchr ( buffer, '/' );
125                                 
126                                 if ( !mods )
127                                         mods = buffer;
128                                 else
129                                         mods++;
130                                         
131                                 if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
132                                         ext = 2;
133                                 
134                                 mod = xstrndup ( mods, col - mods - ext );
135                                         
136                                 if ( !current ) {
137                                         first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
138                                 }
139                                 else {
140                                         current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
141                                         current = current-> m_next;
142                                 }
143                                 current-> m_module  = mod;
144                                 current-> m_isalias = 0;
145                                 current-> m_depcnt  = 0;
146                                 current-> m_deparr  = 0;
147                                 current-> m_next    = 0;
148                                                 
149                                 //printf ( "%s:\n", mod );
150                                                 
151                                 p = col + 1;            
152                         }
153                         else
154                                 p = 0;
155                 }
156                 else
157                         p = buffer;
158                         
159                 if ( p && *p ) {
160                         char *end = &buffer [l-1];
161                         char *deps = strrchr ( end, '/' );
162                         char *dep;
163                         int ext = 0;
164                         
165                         while ( isblank ( *end ) || ( *end == '\\' ))
166                                 end--;
167                                 
168                         deps = strrchr ( p, '/' );
169                         
170                         if ( !deps || ( deps < p )) {
171                                 deps = p;
172                 
173                                 while ( isblank ( *deps ))
174                                         deps++;
175                         }
176                         else
177                                 deps++;
178                         
179                         if (( *(end-1) == '.' ) && ( *end == 'o' ))
180                                 ext = 2;
181
182                         /* Cope with blank lines */
183                         if ((end-deps-ext+1) <= 0)
184                                 continue;
185                         
186                         dep = xstrndup ( deps, end - deps - ext + 1 );
187                         
188                         current-> m_depcnt++;
189                         current-> m_deparr = (char **) xrealloc ( current-> m_deparr, sizeof ( char *) * current-> m_depcnt );
190                         current-> m_deparr [current-> m_depcnt - 1] = dep;              
191                         
192                         //printf ( "    %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
193                 }
194         
195                 if ( buffer [l-1] == '\\' )
196                         continuation_line = 1;
197                 else
198                         continuation_line = 0;
199         }
200         close ( fd );
201
202         // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
203
204         if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
205                 if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
206                         return first;
207         
208         continuation_line = 0;
209         while ( reads ( fd, buffer, sizeof( buffer ))) {
210                 int l;
211                 char *p;
212                 
213                 p = strchr ( buffer, '#' );     
214                 if ( p )
215                         *p = 0;
216                         
217                 l = xstrlen ( buffer );
218         
219                 while ( l && isspace ( buffer [l-1] )) {
220                         buffer [l-1] = 0;
221                         l--;
222                 }
223                 
224                 if ( l == 0 ) {
225                         continuation_line = 0;
226                         continue;
227                 }
228                 
229                 if ( !continuation_line ) {             
230                         if (( strncmp ( buffer, "alias", 5 ) == 0 ) && isspace ( buffer [5] )) {
231                                 char *alias, *mod;
232
233                                 alias = buffer + 6;
234                                 
235                                 while ( isspace ( *alias ))
236                                         alias++;                        
237                                 mod = alias;                                    
238                                 while ( !isspace ( *mod ))
239                                         mod++;
240                                 *mod = 0;
241                                 mod++;
242                                 while ( isspace ( *mod ))
243                                         mod++;
244                                                                                 
245 //                                      fprintf ( stderr, "ALIAS: '%s' -> '%s'\n", alias, mod );
246                                 
247                                 if ( !current ) {
248                                         first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
249                                 }
250                                 else {
251                                         current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
252                                         current = current-> m_next;
253                                 }
254                                 current-> m_module  = xstrdup ( alias );
255                                 current-> m_isalias = 1;
256                                 
257                                 if (( strcmp ( alias, "off" ) == 0 ) || ( strcmp ( alias, "null" ) == 0 )) {
258                                         current-> m_depcnt = 0;
259                                         current-> m_deparr = 0;
260                                 }
261                                 else {
262                                         current-> m_depcnt  = 1;
263                                         current-> m_deparr  = xmalloc ( 1 * sizeof( char * ));
264                                         current-> m_deparr[0] = xstrdup ( mod );
265                                 }
266                                 current-> m_next    = 0;                                        
267                         }                               
268                 }
269         }
270         close ( fd );
271         
272         return first;
273 }
274
275
276 static int mod_process ( struct mod_list_t *list, int do_insert )
277 {
278         char lcmd [256];
279         int rc = 0;
280
281         if ( !list )
282                 return 1;
283
284         while ( list ) {
285                 if ( do_insert )
286                         snprintf ( lcmd, sizeof( lcmd ) - 1, "insmod %s %s %s %s 2>/dev/null", do_syslog ? "-s" : "", autoclean ? "-k" : "", quiet ? "-q" : "", list-> m_module );
287                 else
288                         snprintf ( lcmd, sizeof( lcmd ) - 1, "rmmod %s %s 2>/dev/null", do_syslog ? "-s" : "", list-> m_module );
289                 
290                 if ( verbose )
291                         printf ( "%s\n", lcmd );
292                 if ( !show_only )
293                         rc |= system ( lcmd );
294                         
295                 list = do_insert ? list-> m_prev : list-> m_next;
296         }
297         return rc;
298 }
299
300 static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
301 {
302         struct mod_list_t *find;
303         struct dep_t *dt;
304
305         int lm;
306
307         // remove .o extension
308         lm = xstrlen ( mod );
309         if (( mod [lm-2] == '.' ) && ( mod [lm-1] == 'o' ))
310                 mod [lm-2] = 0;
311
312         // check dependencies
313         for ( dt = depend; dt; dt = dt-> m_next ) {
314                 if ( strcmp ( dt-> m_module, mod ) == 0 ) 
315                         break;
316         }
317         // resolve alias names
318         if ( dt && dt-> m_isalias ) {
319                 if ( dt-> m_depcnt == 1 )
320                         check_dep ( dt-> m_deparr [0], head, tail );
321                 printf ( "Got alias: %s -> %s\n", mod, dt-> m_deparr [0] );
322                         
323                 return;
324         }
325         
326         // search for duplicates
327         for ( find = *head; find; find = find-> m_next ) {
328                 if ( !strcmp ( mod, find-> m_module )) {
329                         // found -> dequeue it
330
331                         if ( find-> m_prev )
332                                 find-> m_prev-> m_next = find-> m_next;
333                         else
334                                 *head = find-> m_next;
335                                         
336                         if ( find-> m_next )
337                                 find-> m_next-> m_prev = find-> m_prev;
338                         else
339                                 *tail = find-> m_prev;
340                                         
341                         break; // there can be only one duplicate
342                 }                               
343         }
344
345         if ( !find ) { // did not find a duplicate
346                 find = (struct mod_list_t *) xmalloc ( sizeof(struct mod_list_t));              
347                 find-> m_module = mod;
348         }
349
350         // enqueue at tail      
351         if ( *tail )
352                 (*tail)-> m_next = find;
353         find-> m_prev   = *tail;
354         find-> m_next   = 0;
355
356         if ( !*head )
357                 *head = find;
358         *tail = find;
359                 
360         if ( dt ) {     
361                 int i;
362                 
363                 for ( i = 0; i < dt-> m_depcnt; i++ )
364                         check_dep ( dt-> m_deparr [i], head, tail );
365         }
366 }
367
368
369
370 static int mod_insert ( char *mod, int argc, char **argv )
371 {
372         struct mod_list_t *tail = 0;
373         struct mod_list_t *head = 0;    
374         int rc = 0;
375         
376         // get dep list for module mod
377         check_dep ( mod, &head, &tail );
378         
379         if ( head && tail ) {
380                 int i;
381                 int l = 0;
382         
383                 // append module args
384                 l = xstrlen ( head-> m_module );
385                 for ( i = 0; i < argc; i++ ) 
386                         l += ( xstrlen ( argv [i] ) + 1 );
387                 
388                 head-> m_module = xrealloc ( head-> m_module, l + 1 );
389                 
390                 for ( i = 0; i < argc; i++ ) {
391                         strcat ( head-> m_module, " " );
392                         strcat ( head-> m_module, argv [i] );
393                 }
394
395                 // process tail ---> head
396                 rc |= mod_process ( tail, 1 );
397         }
398         else
399                 rc = 1;
400         
401         return rc;
402 }
403
404 static void mod_remove ( char *mod )
405 {
406         static struct mod_list_t rm_a_dummy = { "-a", 0, 0 }; 
407         
408         struct mod_list_t *head = 0;
409         struct mod_list_t *tail = 0;
410         
411         if ( mod )
412                 check_dep ( mod, &head, &tail );
413         else  // autoclean
414                 head = tail = &rm_a_dummy;
415         
416         if ( head && tail )
417                 mod_process ( head, 0 );  // process head ---> tail
418 }
419
420
421
422 extern int modprobe_main(int argc, char** argv)
423 {
424         int     opt;
425         int remove_opt = 0;
426
427         autoclean = show_only = quiet = do_syslog = verbose = 0;
428
429         while ((opt = getopt(argc, argv, "acdklnqrst:vVC:")) != -1) {
430                 switch(opt) {
431                 case 'c': // no config used
432                 case 'l': // no pattern matching
433                         return EXIT_SUCCESS;
434                         break;
435                 case 'C': // no config used
436                 case 't': // no pattern matching
437                         error_msg_and_die("-t and -C not supported");
438
439                 case 'a': // ignore
440                 case 'd': // ignore
441                         break;
442                 case 'k':
443                         autoclean++;
444                         break;
445                 case 'n':
446                         show_only++;
447                         break;
448                 case 'q':
449                         quiet++;
450                         break;
451                 case 'r':
452                         remove_opt++;
453                         break;
454                 case 's':
455                         do_syslog++;
456                         break;
457                 case 'v':
458                         verbose++;
459                         break;
460                 case 'V':
461                 default:
462                         show_usage();
463                         break;
464                 }
465         }
466         
467         depend = build_dep ( ); 
468
469         if ( !depend ) 
470                 error_msg_and_die ( "could not parse modules.dep\n" );
471         
472         if (remove_opt) {
473                 do {
474                         mod_remove ( optind < argc ? xstrdup ( argv [optind] ) : 0 );
475                 } while ( ++optind < argc );
476                 
477                 return EXIT_SUCCESS;
478         }
479
480         if (optind >= argc) 
481                 error_msg_and_die ( "No module or pattern provided\n" );
482         
483         return mod_insert ( xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 ) ? \
484                EXIT_FAILURE : EXIT_SUCCESS;
485 }
486
487