blob: b05158ac02e06a892ece3e42a97213a5fd8a3044 [file] [log] [blame]
Eric Andersen0139ca92001-07-22 23:01:03 +00001/* vi: set sw=4 ts=4: */
2/*
3 * really dumb modprobe implementation for busybox
4 * Copyright (C) 2001 Lineo, davidm@lineo.com
Eric Andersen60e56f52002-04-26 06:04:01 +00005 *
Robert Griebl52e8d062002-05-14 23:42:08 +00006 * dependency specific stuff completly rewritten and
7 * copyright (c) 2002 by Robert Griebl, griebl@gmx.de
Eric Andersen60e56f52002-04-26 06:04:01 +00008 *
Eric Andersen0139ca92001-07-22 23:01:03 +00009 */
10
Robert Griebl52e8d062002-05-14 23:42:08 +000011#include <sys/utsname.h>
Eric Andersen0139ca92001-07-22 23:01:03 +000012#include <getopt.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <string.h>
Eric Andersen60e56f52002-04-26 06:04:01 +000017#include <ctype.h>
Eric Andersenc06391b2002-06-04 13:28:43 +000018#include <fcntl.h>
Eric Andersen0139ca92001-07-22 23:01:03 +000019#include "busybox.h"
20
Eric Andersen0139ca92001-07-22 23:01:03 +000021
Eric Andersen60e56f52002-04-26 06:04:01 +000022
23struct dep_t {
24 char * m_module;
Robert Griebl52e8d062002-05-14 23:42:08 +000025
Robert Griebl1d4ef2a2002-05-28 21:32:10 +000026 int m_isalias : 1;
27 int m_reserved : 15;
28
29 int m_depcnt : 16;
Eric Andersen60e56f52002-04-26 06:04:01 +000030 char ** m_deparr;
31
32 struct dep_t * m_next;
33};
34
Robert Griebl52e8d062002-05-14 23:42:08 +000035struct 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
Robert Griebl236abbf2002-05-22 23:34:35 +000043static struct dep_t *depend;
44static int autoclean, show_only, quiet, do_syslog, verbose;
Robert Griebl52e8d062002-05-14 23:42:08 +000045
Eric Andersen60e56f52002-04-26 06:04:01 +000046
Robert Grieblbc28f7a2002-06-04 19:33:58 +000047/* 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 */
51static 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
Eric Andersen864b7972002-05-03 15:48:26 +000076static struct dep_t *build_dep ( void )
Eric Andersen60e56f52002-04-26 06:04:01 +000077{
Robert Grieblbc28f7a2002-06-04 19:33:58 +000078 int fd;
Eric Andersen60e56f52002-04-26 06:04:01 +000079 struct utsname un;
Eric Andersen60e56f52002-04-26 06:04:01 +000080 struct dep_t *first = 0;
81 struct dep_t *current = 0;
Eric Andersenc06391b2002-06-04 13:28:43 +000082 char buffer[256];
Eric Andersen60e56f52002-04-26 06:04:01 +000083 char *filename = buffer;
84 int continuation_line = 0;
85
86 if ( uname ( &un ))
87 return 0;
Robert Griebl236abbf2002-05-22 23:34:35 +000088
89 // check for buffer overflow in following code
Eric Andersenc06391b2002-06-04 13:28:43 +000090 if ( xstrlen ( un.release ) > ( sizeof( buffer ) - 64 )) {
Robert Griebl236abbf2002-05-22 23:34:35 +000091 return 0;
Eric Andersenc06391b2002-06-04 13:28:43 +000092 }
Robert Griebl236abbf2002-05-22 23:34:35 +000093
Eric Andersen60e56f52002-04-26 06:04:01 +000094 strcpy ( filename, "/lib/modules/" );
Eric Andersenc06391b2002-06-04 13:28:43 +000095 strcat ( filename, un.release );
Eric Andersen60e56f52002-04-26 06:04:01 +000096 strcat ( filename, "/modules.dep" );
97
Robert Grieblbc28f7a2002-06-04 19:33:58 +000098 if (( fd = open ( filename, O_RDONLY )) < 0 )
Eric Andersen60e56f52002-04-26 06:04:01 +000099 return 0;
Eric Andersenc06391b2002-06-04 13:28:43 +0000100
Robert Grieblbc28f7a2002-06-04 19:33:58 +0000101 while ( reads ( fd, buffer, sizeof( buffer ))) {
102 int l = xstrlen ( buffer );
Eric Andersen60e56f52002-04-26 06:04:01 +0000103 char *p = 0;
Robert Grieblbc28f7a2002-06-04 19:33:58 +0000104
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000105 while ( isspace ( buffer [l-1] )) {
Eric Andersen60e56f52002-04-26 06:04:01 +0000106 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;
Eric Andersen864b7972002-05-03 15:48:26 +0000121 int ext = 0;
Eric Andersen60e56f52002-04-26 06:04:01 +0000122
123 *col = 0;
124 mods = strrchr ( buffer, '/' );
125
126 if ( !mods )
127 mods = buffer;
128 else
129 mods++;
130
Eric Andersen864b7972002-05-03 15:48:26 +0000131 if (( *(col-2) == '.' ) && ( *(col-1) == 'o' ))
132 ext = 2;
133
134 mod = xstrndup ( mods, col - mods - ext );
135
Eric Andersen60e56f52002-04-26 06:04:01 +0000136 if ( !current ) {
Eric Andersenc06391b2002-06-04 13:28:43 +0000137 first = current = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
Eric Andersen60e56f52002-04-26 06:04:01 +0000138 }
139 else {
Eric Andersenc06391b2002-06-04 13:28:43 +0000140 current-> m_next = (struct dep_t *) xmalloc ( sizeof ( struct dep_t ));
Eric Andersen60e56f52002-04-26 06:04:01 +0000141 current = current-> m_next;
142 }
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000143 current-> m_module = mod;
144 current-> m_isalias = 0;
145 current-> m_depcnt = 0;
146 current-> m_deparr = 0;
147 current-> m_next = 0;
Eric Andersen60e56f52002-04-26 06:04:01 +0000148
Eric Andersen864b7972002-05-03 15:48:26 +0000149 //printf ( "%s:\n", mod );
Eric Andersen60e56f52002-04-26 06:04:01 +0000150
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;
Eric Andersen864b7972002-05-03 15:48:26 +0000163 int ext = 0;
Eric Andersen60e56f52002-04-26 06:04:01 +0000164
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
Eric Andersen864b7972002-05-03 15:48:26 +0000179 if (( *(end-1) == '.' ) && ( *end == 'o' ))
180 ext = 2;
Eric Andersenb493dec2002-07-02 19:14:23 +0000181
182 /* Cope with blank lines */
183 if ((end-deps-ext+1) <= 0)
184 continue;
Eric Andersen864b7972002-05-03 15:48:26 +0000185
186 dep = xstrndup ( deps, end - deps - ext + 1 );
Eric Andersen60e56f52002-04-26 06:04:01 +0000187
188 current-> m_depcnt++;
Eric Andersen864b7972002-05-03 15:48:26 +0000189 current-> m_deparr = (char **) xrealloc ( current-> m_deparr, sizeof ( char *) * current-> m_depcnt );
Eric Andersen60e56f52002-04-26 06:04:01 +0000190 current-> m_deparr [current-> m_depcnt - 1] = dep;
191
Eric Andersen864b7972002-05-03 15:48:26 +0000192 //printf ( " %d) %s\n", current-> m_depcnt, current-> m_deparr [current-> m_depcnt -1] );
Eric Andersen60e56f52002-04-26 06:04:01 +0000193 }
194
195 if ( buffer [l-1] == '\\' )
196 continuation_line = 1;
197 else
198 continuation_line = 0;
199 }
Eric Andersenc06391b2002-06-04 13:28:43 +0000200 close ( fd );
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000201
202 // alias parsing is not 100% correct (no correct handling of continuation lines within an alias) !
203
Robert Grieblbc28f7a2002-06-04 19:33:58 +0000204 if (( fd = open ( "/etc/modules.conf", O_RDONLY )) < 0 )
205 if (( fd = open ( "/etc/conf.modules", O_RDONLY )) < 0 )
Eric Andersenc06391b2002-06-04 13:28:43 +0000206 return first;
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000207
Eric Andersenc06391b2002-06-04 13:28:43 +0000208 continuation_line = 0;
Robert Grieblbc28f7a2002-06-04 19:33:58 +0000209 while ( reads ( fd, buffer, sizeof( buffer ))) {
Eric Andersenc06391b2002-06-04 13:28:43 +0000210 int l;
211 char *p;
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000212
Robert Grieblbc28f7a2002-06-04 19:33:58 +0000213 p = strchr ( buffer, '#' );
Eric Andersenc06391b2002-06-04 13:28:43 +0000214 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 }
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000269 }
Eric Andersenc06391b2002-06-04 13:28:43 +0000270 close ( fd );
Eric Andersen60e56f52002-04-26 06:04:01 +0000271
272 return first;
273}
274
275
Robert Griebl236abbf2002-05-22 23:34:35 +0000276static int mod_process ( struct mod_list_t *list, int do_insert )
Eric Andersen60e56f52002-04-26 06:04:01 +0000277{
Robert Griebl52e8d062002-05-14 23:42:08 +0000278 char lcmd [256];
279 int rc = 0;
Eric Andersen864b7972002-05-03 15:48:26 +0000280
Robert Griebl52e8d062002-05-14 23:42:08 +0000281 if ( !list )
282 return 1;
Eric Andersen864b7972002-05-03 15:48:26 +0000283
Robert Griebl52e8d062002-05-14 23:42:08 +0000284 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;
Eric Andersen60e56f52002-04-26 06:04:01 +0000296 }
Robert Griebl52e8d062002-05-14 23:42:08 +0000297 return rc;
Eric Andersen60e56f52002-04-26 06:04:01 +0000298}
299
Robert Griebl52e8d062002-05-14 23:42:08 +0000300static void check_dep ( char *mod, struct mod_list_t **head, struct mod_list_t **tail )
Eric Andersen60e56f52002-04-26 06:04:01 +0000301{
Robert Griebl52e8d062002-05-14 23:42:08 +0000302 struct mod_list_t *find;
Eric Andersen60e56f52002-04-26 06:04:01 +0000303 struct dep_t *dt;
Eric Andersen60e56f52002-04-26 06:04:01 +0000304
Robert Griebl52e8d062002-05-14 23:42:08 +0000305 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
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000312 // 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
Robert Griebl52e8d062002-05-14 23:42:08 +0000326 // 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
Robert Griebl52e8d062002-05-14 23:42:08 +0000341 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;
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000355
Robert Griebl52e8d062002-05-14 23:42:08 +0000356 if ( !*head )
357 *head = find;
358 *tail = find;
Eric Andersen60e56f52002-04-26 06:04:01 +0000359
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000360 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 }
Robert Griebl52e8d062002-05-14 23:42:08 +0000366}
367
368
369
Robert Griebl236abbf2002-05-22 23:34:35 +0000370static int mod_insert ( char *mod, int argc, char **argv )
Robert Griebl52e8d062002-05-14 23:42:08 +0000371{
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;
Robert Griebl236abbf2002-05-22 23:34:35 +0000381 int l = 0;
Robert Griebl52e8d062002-05-14 23:42:08 +0000382
383 // append module args
Robert Griebl1d4ef2a2002-05-28 21:32:10 +0000384 l = xstrlen ( head-> m_module );
Robert Griebl236abbf2002-05-22 23:34:35 +0000385 for ( i = 0; i < argc; i++ )
386 l += ( xstrlen ( argv [i] ) + 1 );
387
Robert Griebl3b793702002-06-02 09:36:12 +0000388 head-> m_module = xrealloc ( head-> m_module, l + 1 );
Robert Griebl52e8d062002-05-14 23:42:08 +0000389
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
Robert Griebl236abbf2002-05-22 23:34:35 +0000396 rc |= mod_process ( tail, 1 );
Robert Griebl52e8d062002-05-14 23:42:08 +0000397 }
398 else
399 rc = 1;
400
401 return rc;
402}
403
Robert Griebl236abbf2002-05-22 23:34:35 +0000404static void mod_remove ( char *mod )
Robert Griebl52e8d062002-05-14 23:42:08 +0000405{
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
Robert Griebl236abbf2002-05-22 23:34:35 +0000416 if ( head && tail )
417 mod_process ( head, 0 ); // process head ---> tail
Robert Griebl52e8d062002-05-14 23:42:08 +0000418}
419
420
Eric Andersen60e56f52002-04-26 06:04:01 +0000421
Eric Andersen0139ca92001-07-22 23:01:03 +0000422extern int modprobe_main(int argc, char** argv)
423{
Robert Griebl236abbf2002-05-22 23:34:35 +0000424 int opt;
425 int remove_opt = 0;
Eric Andersen0139ca92001-07-22 23:01:03 +0000426
Robert Griebl236abbf2002-05-22 23:34:35 +0000427 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;
Eric Andersen0139ca92001-07-22 23:01:03 +0000434 break;
Robert Griebl236abbf2002-05-22 23:34:35 +0000435 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
Eric Andersen0139ca92001-07-22 23:01:03 +0000441 break;
442 case 'k':
443 autoclean++;
444 break;
Eric Andersen0139ca92001-07-22 23:01:03 +0000445 case 'n':
446 show_only++;
447 break;
448 case 'q':
449 quiet++;
450 break;
451 case 'r':
Eric Andersen1b064192001-07-25 07:23:38 +0000452 remove_opt++;
Eric Andersen0139ca92001-07-22 23:01:03 +0000453 break;
454 case 's':
455 do_syslog++;
456 break;
Eric Andersen0139ca92001-07-22 23:01:03 +0000457 case 'v':
458 verbose++;
459 break;
Eric Andersen0139ca92001-07-22 23:01:03 +0000460 case 'V':
461 default:
462 show_usage();
463 break;
464 }
Eric Andersen0139ca92001-07-22 23:01:03 +0000465 }
Eric Andersen0139ca92001-07-22 23:01:03 +0000466
Robert Griebl52e8d062002-05-14 23:42:08 +0000467 depend = build_dep ( );
468
Robert Griebl236abbf2002-05-22 23:34:35 +0000469 if ( !depend )
470 error_msg_and_die ( "could not parse modules.dep\n" );
Robert Griebl52e8d062002-05-14 23:42:08 +0000471
Eric Andersen1b064192001-07-25 07:23:38 +0000472 if (remove_opt) {
Eric Andersen0139ca92001-07-22 23:01:03 +0000473 do {
Robert Griebl3b793702002-06-02 09:36:12 +0000474 mod_remove ( optind < argc ? xstrdup ( argv [optind] ) : 0 );
Robert Griebl52e8d062002-05-14 23:42:08 +0000475 } while ( ++optind < argc );
476
Robert Griebl236abbf2002-05-22 23:34:35 +0000477 return EXIT_SUCCESS;
Eric Andersen0139ca92001-07-22 23:01:03 +0000478 }
479
Robert Griebl236abbf2002-05-22 23:34:35 +0000480 if (optind >= argc)
481 error_msg_and_die ( "No module or pattern provided\n" );
Robert Griebl52e8d062002-05-14 23:42:08 +0000482
Robert Griebl3b793702002-06-02 09:36:12 +0000483 return mod_insert ( xstrdup ( argv [optind] ), argc - optind - 1, argv + optind + 1 ) ? \
Robert Griebl236abbf2002-05-22 23:34:35 +0000484 EXIT_FAILURE : EXIT_SUCCESS;
Eric Andersen0139ca92001-07-22 23:01:03 +0000485}
486
487