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