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