source: branches/stable/mindi-busybox/modutils/modprobe.c @ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 13 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod? silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian? keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René? Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 23.0 KB
Line 
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 * Copyright (c) 2005 by Jim Bauer, jfbauer@nfr.com
8 *
9 * Portions Copyright (c) 2005 by Yann E. MORIN, yann.morin.1998@anciens.enib.fr
10 *
11 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
12*/
13
14#include "libbb.h"
15#include <sys/utsname.h>
16#include <fnmatch.h>
17
18struct mod_opt_t {  /* one-way list of options to pass to a module */
19    char *  m_opt_val;
20    struct mod_opt_t * m_next;
21};
22
23struct dep_t {  /* one-way list of dependency rules */
24    /* a dependency rule */
25    char *  m_name;                         /* the module name*/
26    char *  m_path;                         /* the module file path */
27    struct mod_opt_t *  m_options;          /* the module options */
28
29    int     m_isalias  : 1;                 /* the module is an alias */
30    int     m_reserved : 15;                /* stuffin' */
31
32    int     m_depcnt   : 16;                /* the number of dependable module(s) */
33    char ** m_deparr;                       /* the list of dependable module(s) */
34
35    struct dep_t * m_next;                  /* the next dependency rule */
36};
37
38struct mod_list_t { /* two-way list of modules to process */
39    /* a module description */
40    const char * m_name;
41    char * m_path;
42    struct mod_opt_t * m_options;
43
44    struct mod_list_t * m_prev;
45    struct mod_list_t * m_next;
46};
47
48
49static struct dep_t *depend;
50
51#define main_options "acdklnqrst:vVC:"
52#define INSERT_ALL     1        /* a */
53#define DUMP_CONF_EXIT 2        /* c */
54#define D_OPT_IGNORED  4        /* d */
55#define AUTOCLEAN_FLG  8        /* k */
56#define LIST_ALL       16       /* l */
57#define SHOW_ONLY      32       /* n */
58#define QUIET          64       /* q */
59#define REMOVE_OPT     128      /* r */
60#define DO_SYSLOG      256      /* s */
61#define RESTRICT_DIR   512      /* t */
62#define VERBOSE        1024     /* v */
63#define VERSION_ONLY   2048     /* V */
64#define CONFIG_FILE    4096     /* C */
65
66#define autoclean       (main_opts & AUTOCLEAN_FLG)
67#define show_only       (main_opts & SHOW_ONLY)
68#define quiet           (main_opts & QUIET)
69#define remove_opt      (main_opts & REMOVE_OPT)
70#define do_syslog       (main_opts & DO_SYSLOG)
71#define verbose         (main_opts & VERBOSE)
72
73static int main_opts;
74
75static int parse_tag_value(char *buffer, char **ptag, char **pvalue)
76{
77    char *tag, *value;
78
79    buffer = skip_whitespace(buffer);
80    tag = value = buffer;
81    while (!isspace(*value))
82        if (!*value) return 0;
83        else value++;
84    *value++ = 0;
85    value = skip_whitespace(value);
86    if (!*value) return 0;
87
88    *ptag = tag;
89    *pvalue = value;
90
91    return 1;
92}
93
94/*
95 * This function appends an option to a list
96 */
97static struct mod_opt_t *append_option(struct mod_opt_t *opt_list, char *opt)
98{
99    struct mod_opt_t *ol = opt_list;
100
101    if (ol) {
102        while (ol->m_next) {
103            ol = ol->m_next;
104        }
105        ol->m_next = xmalloc(sizeof(struct mod_opt_t));
106        ol = ol->m_next;
107    } else {
108        ol = opt_list = xmalloc(sizeof(struct mod_opt_t));
109    }
110
111    ol->m_opt_val = xstrdup(opt);
112    ol->m_next = NULL;
113
114    return opt_list;
115}
116
117#if ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS
118/* static char* parse_command_string(char* src, char **dst);
119 *   src: pointer to string containing argument
120 *   dst: pointer to where to store the parsed argument
121 *   return value: the pointer to the first char after the parsed argument,
122 *                 NULL if there was no argument parsed (only trailing spaces).
123 *   Note that memory is allocated with xstrdup when a new argument was
124 *   parsed. Don't forget to free it!
125 */
126#define ARG_EMPTY      0x00
127#define ARG_IN_DQUOTES 0x01
128#define ARG_IN_SQUOTES 0x02
129static char *parse_command_string(char *src, char **dst)
130{
131    int opt_status = ARG_EMPTY;
132    char* tmp_str;
133
134    /* Dumb you, I have nothing to do... */
135    if (src == NULL) return src;
136
137    /* Skip leading spaces */
138    while (*src == ' ') {
139        src++;
140    }
141    /* Is the end of string reached? */
142    if (*src == '\0') {
143        return NULL;
144    }
145    /* Reached the start of an argument
146     * By the way, we duplicate a little too much
147     * here but what is too much is freed later. */
148    *dst = tmp_str = xstrdup(src);
149    /* Get to the end of that argument */
150    while (*tmp_str != '\0'
151     && (*tmp_str != ' ' || (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)))
152    ) {
153        switch (*tmp_str) {
154        case '\'':
155            if (opt_status & ARG_IN_DQUOTES) {
156                /* Already in double quotes, keep current char as is */
157            } else {
158                /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
159                memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
160                /* mark me: we enter or leave single quotes */
161                opt_status ^= ARG_IN_SQUOTES;
162                /* Back one char, as we need to re-scan the new char there. */
163                tmp_str--;
164            }
165            break;
166        case '"':
167            if (opt_status & ARG_IN_SQUOTES) {
168                /* Already in single quotes, keep current char as is */
169            } else {
170                /* shift left 1 char, until end of string: get rid of the opening/closing quotes */
171                memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
172                /* mark me: we enter or leave double quotes */
173                opt_status ^= ARG_IN_DQUOTES;
174                /* Back one char, as we need to re-scan the new char there. */
175                tmp_str--;
176            }
177            break;
178        case '\\':
179            if (opt_status & ARG_IN_SQUOTES) {
180                /* Between single quotes: keep as is. */
181            } else {
182                switch (*(tmp_str+1)) {
183                case 'a':
184                case 'b':
185                case 't':
186                case 'n':
187                case 'v':
188                case 'f':
189                case 'r':
190                case '0':
191                    /* We escaped a special character. For now, keep
192                     * both the back-slash and the following char. */
193                    tmp_str++; src++;
194                    break;
195                default:
196                    /* We escaped a space or a single or double quote,
197                     * or a back-slash, or a non-escapable char. Remove
198                     * the '\' and keep the new current char as is. */
199                    memmove(tmp_str, tmp_str + 1, strlen(tmp_str));
200                    break;
201                }
202            }
203            break;
204        /* Any other char that is special shall appear here.
205         * Example: $ starts a variable
206        case '$':
207            do_variable_expansion();
208            break;
209         * */
210        default:
211            /* any other char is kept as is. */
212            break;
213        }
214        tmp_str++; /* Go to next char */
215        src++; /* Go to next char to find the end of the argument. */
216    }
217    /* End of string, but still no ending quote */
218    if (opt_status & (ARG_IN_DQUOTES | ARG_IN_SQUOTES)) {
219        bb_error_msg_and_die("unterminated (single or double) quote in options list: %s", src);
220    }
221    *tmp_str++ = '\0';
222    *dst = xrealloc(*dst, (tmp_str - *dst));
223    return src;
224}
225#else
226#define parse_command_string(src, dst)  (0)
227#endif /* ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS */
228
229/*
230 * This function reads aliases and default module options from a configuration file
231 * (/etc/modprobe.conf syntax). It supports includes (only files, no directories).
232 */
233static void include_conf(struct dep_t **first, struct dep_t **current, char *buffer, int buflen, int fd)
234{
235    int continuation_line = 0;
236
237    // alias parsing is not 100% correct (no correct handling of continuation lines within an alias)!
238
239    while (reads(fd, buffer, buflen)) {
240        int l;
241        char *p;
242
243        p = strchr(buffer, '#');
244        if (p)
245            *p = 0;
246
247        l = strlen(buffer);
248
249        while (l && isspace(buffer[l-1])) {
250            buffer[l-1] = 0;
251            l--;
252        }
253
254        if (l == 0) {
255            continuation_line = 0;
256            continue;
257        }
258
259        if (!continuation_line) {
260            if ((strncmp(buffer, "alias", 5) == 0) && isspace(buffer[5])) {
261                char *alias, *mod;
262
263                if (parse_tag_value(buffer + 6, &alias, &mod)) {
264                    /* handle alias as a module dependent on the aliased module */
265                    if (!*current) {
266                        (*first) = (*current) = xzalloc(sizeof(struct dep_t));
267                    } else {
268                        (*current)->m_next = xzalloc(sizeof(struct dep_t));
269                        (*current) = (*current)->m_next;
270                    }
271                    (*current)->m_name  = xstrdup(alias);
272                    (*current)->m_isalias = 1;
273
274                    if ((strcmp(mod, "off") == 0) || (strcmp(mod, "null") == 0)) {
275                        (*current)->m_depcnt = 0;
276                        (*current)->m_deparr = 0;
277                    } else {
278                        (*current)->m_depcnt  = 1;
279                        (*current)->m_deparr  = xmalloc(1 * sizeof(char *));
280                        (*current)->m_deparr[0] = xstrdup(mod);
281                    }
282                    (*current)->m_next    = 0;
283                }
284            } else if ((strncmp(buffer, "options", 7) == 0) && isspace(buffer[7])) {
285                char *mod, *opt;
286
287                /* split the line in the module/alias name, and options */
288                if (parse_tag_value(buffer + 8, &mod, &opt)) {
289                    struct dep_t *dt;
290
291                    /* find the corresponding module */
292                    for (dt = *first; dt; dt = dt->m_next) {
293                        if (strcmp(dt->m_name, mod) == 0)
294                            break;
295                    }
296                    if (dt) {
297                        if (ENABLE_FEATURE_MODPROBE_MULTIPLE_OPTIONS) {
298                            char* new_opt = NULL;
299                            while ((opt = parse_command_string(opt, &new_opt))) {
300                                dt->m_options = append_option(dt->m_options, new_opt);
301                            }
302                        } else {
303                            dt->m_options = append_option(dt->m_options, opt);
304                        }
305                    }
306                }
307            } else if ((strncmp(buffer, "include", 7) == 0) && isspace(buffer[7])) {
308                int fdi; char *filename;
309
310                filename = skip_whitespace(buffer + 8);
311
312                if ((fdi = open(filename, O_RDONLY)) >= 0) {
313                    include_conf(first, current, buffer, buflen, fdi);
314                    close(fdi);
315                }
316            }
317        }
318    }
319}
320
321/*
322 * This function builds a list of dependency rules from /lib/modules/`uname -r`/modules.dep.
323 * It then fills every modules and aliases with their default options, found by parsing
324 * modprobe.conf (or modules.conf, or conf.modules).
325 */
326static struct dep_t *build_dep(void)
327{
328    int fd;
329    struct utsname un;
330    struct dep_t *first = 0;
331    struct dep_t *current = 0;
332    char buffer[2048];
333    char *filename;
334    int continuation_line = 0;
335    int k_version;
336
337    if (uname(&un))
338        bb_error_msg_and_die("can't determine kernel version");
339
340    k_version = 0;
341    if (un.release[0] == '2') {
342        k_version = un.release[2] - '0';
343    }
344
345    filename = xasprintf("/lib/modules/%s/modules.dep", un.release);
346    fd = open(filename, O_RDONLY);
347    if (ENABLE_FEATURE_CLEAN_UP)
348        free(filename);
349    if (fd < 0) {
350        /* Ok, that didn't work.  Fall back to looking in /lib/modules */
351        fd = open("/lib/modules/modules.dep", O_RDONLY);
352        if (fd < 0) {
353            return 0;
354        }
355    }
356
357    while (reads(fd, buffer, sizeof(buffer))) {
358        int l = strlen(buffer);
359        char *p = 0;
360
361        while (l > 0 && isspace(buffer[l-1])) {
362            buffer[l-1] = 0;
363            l--;
364        }
365
366        if (l == 0) {
367            continuation_line = 0;
368            continue;
369        }
370
371        /* Is this a new module dep description? */
372        if (!continuation_line) {
373            /* find the dep beginning */
374            char *col = strchr(buffer, ':');
375            char *dot = col;
376
377            if (col) {
378                /* This line is a dep description */
379                const char *mods;
380                char *modpath;
381                char *mod;
382
383                /* Find the beginning of the module file name */
384                *col = 0;
385                mods = bb_basename(buffer);
386
387                /* find the path of the module */
388                modpath = strchr(buffer, '/'); /* ... and this is the path */
389                if (!modpath)
390                    modpath = buffer; /* module with no path */
391                /* find the end of the module name in the file name */
392                if (ENABLE_FEATURE_2_6_MODULES &&
393                     (k_version > 4) && (*(col-3) == '.') &&
394                    (*(col-2) == 'k') && (*(col-1) == 'o'))
395                    dot = col - 3;
396                else
397                    if ((*(col-2) == '.') && (*(col-1) == 'o'))
398                        dot = col - 2;
399
400                mod = xstrndup(mods, dot - mods);
401
402                /* enqueue new module */
403                if (!current) {
404                    first = current = xmalloc(sizeof(struct dep_t));
405                } else {
406                    current->m_next = xmalloc(sizeof(struct dep_t));
407                    current = current->m_next;
408                }
409                current->m_name    = mod;
410                current->m_path    = xstrdup(modpath);
411                current->m_options = NULL;
412                current->m_isalias = 0;
413                current->m_depcnt  = 0;
414                current->m_deparr  = 0;
415                current->m_next    = 0;
416
417                p = col + 1;
418            } else
419                /* this line is not a dep description */
420                p = 0;
421        } else
422            /* It's a dep description continuation */
423            p = buffer;
424
425        while (p && *p && isblank(*p))
426            p++;
427
428        /* p points to the first dependable module; if NULL, no dependable module */
429        if (p && *p) {
430            char *end = &buffer[l-1];
431            const char *deps;
432            char *dep;
433            char *next;
434            int ext = 0;
435
436            while (isblank(*end) || (*end == '\\'))
437                end--;
438
439            do {
440                /* search the end of the dependency */
441                next = strchr(p, ' ');
442                if (next) {
443                    *next = 0;
444                    next--;
445                } else
446                    next = end;
447
448                /* find the beginning of the module file name */
449                deps = bb_basename(p);
450                if (deps == p) {
451                    while (isblank(*deps))
452                        deps++;
453                }
454
455                /* find the end of the module name in the file name */
456                if (ENABLE_FEATURE_2_6_MODULES
457                 && (k_version > 4) && (*(next-2) == '.')
458                 && (*(next-1) == 'k') && (*next == 'o'))
459                    ext = 3;
460                else
461                    if ((*(next-1) == '.') && (*next == 'o'))
462                        ext = 2;
463
464                /* Cope with blank lines */
465                if ((next-deps-ext+1) <= 0)
466                    continue;
467                dep = xstrndup(deps, next - deps - ext + 1);
468
469                /* Add the new dependable module name */
470                current->m_depcnt++;
471                current->m_deparr = xrealloc(current->m_deparr,
472                        sizeof(char *) * current->m_depcnt);
473                current->m_deparr[current->m_depcnt - 1] = dep;
474
475                p = next + 2;
476            } while (next < end);
477        }
478
479        /* is there other dependable module(s) ? */
480        if (buffer[l-1] == '\\')
481            continuation_line = 1;
482        else
483            continuation_line = 0;
484    }
485    close(fd);
486
487    /*
488     * First parse system-specific options and aliases
489     * as they take precedence over the kernel ones.
490     * >=2.6: we only care about modprobe.conf
491     * <=2.4: we care about modules.conf and conf.modules
492     */
493    if (ENABLE_FEATURE_2_6_MODULES
494     && (fd = open("/etc/modprobe.conf", O_RDONLY)) < 0)
495        if (ENABLE_FEATURE_2_4_MODULES
496         && (fd = open("/etc/modules.conf", O_RDONLY)) < 0)
497            if (ENABLE_FEATURE_2_4_MODULES)
498                fd = open("/etc/conf.modules", O_RDONLY);
499
500    if (fd >= 0) {
501        include_conf(&first, &current, buffer, sizeof(buffer), fd);
502        close(fd);
503    }
504
505    /* Only 2.6 has a modules.alias file */
506    if (ENABLE_FEATURE_2_6_MODULES) {
507        /* Parse kernel-declared module aliases */
508        filename = xasprintf("/lib/modules/%s/modules.alias", un.release);
509        fd = open(filename, O_RDONLY);
510        if (fd < 0) {
511            /* Ok, that didn't work.  Fall back to looking in /lib/modules */
512            fd = open("/lib/modules/modules.alias", O_RDONLY);
513        }
514        if (ENABLE_FEATURE_CLEAN_UP)
515            free(filename);
516
517        if (fd >= 0) {
518            include_conf(&first, &current, buffer, sizeof(buffer), fd);
519            close(fd);
520        }
521
522        /* Parse kernel-declared symbol aliases */
523        filename = xasprintf("/lib/modules/%s/modules.symbols", un.release);
524        fd = open(filename, O_RDONLY);
525        if (fd < 0) {
526            /* Ok, that didn't work.  Fall back to looking in /lib/modules */
527            fd = open("/lib/modules/modules.symbols", O_RDONLY);
528        }
529        if (ENABLE_FEATURE_CLEAN_UP)
530            free(filename);
531
532        if (fd >= 0) {
533            include_conf(&first, &current, buffer, sizeof(buffer), fd);
534            close(fd);
535        }
536    }
537
538    return first;
539}
540
541/* return 1 = loaded, 0 = not loaded, -1 = can't tell */
542static int already_loaded(const char *name)
543{
544    int fd, ret = 0;
545    char buffer[4096];
546
547    fd = open("/proc/modules", O_RDONLY);
548    if (fd < 0)
549        return -1;
550
551    while (reads(fd, buffer, sizeof(buffer))) {
552        char *p;
553
554        p = strchr (buffer, ' ');
555        if (p) {
556            const char *n;
557
558            // Truncate buffer at first space and check for matches, with
559            // the idiosyncrasy that _ and - are interchangeable because the
560            // 2.6 kernel does weird things.
561
562            *p = 0;
563            for (p = buffer, n = name; ; p++, n++) {
564                if (*p != *n) {
565                    if ((*p == '_' || *p == '-') && (*n == '_' || *n == '-'))
566                        continue;
567                    break;
568                }
569                // If we made it to the end, that's a match.
570                if (!*p) {
571                    ret = 1;
572                    goto done;
573                }
574            }
575        }
576    }
577done:
578    close (fd);
579    return ret;
580}
581
582static int mod_process(const struct mod_list_t *list, int do_insert)
583{
584    int rc = 0;
585    char **argv = NULL;
586    struct mod_opt_t *opts;
587    int argc_malloc; /* never used when CONFIG_FEATURE_CLEAN_UP not defined */
588    int argc;
589
590    while (list) {
591        argc = 0;
592        if (ENABLE_FEATURE_CLEAN_UP)
593            argc_malloc = 0;
594        /* If CONFIG_FEATURE_CLEAN_UP is not defined, then we leak memory
595         * each time we allocate memory for argv.
596         * But it is (quite) small amounts of memory that leak each
597         * time a module is loaded,  and it is reclaimed when modprobe
598         * exits anyway (even when standalone shell?).
599         * This could become a problem when loading a module with LOTS of
600         * dependencies, with LOTS of options for each dependencies, with
601         * very little memory on the target... But in that case, the module
602         * would not load because there is no more memory, so there's no
603         * problem. */
604        /* enough for minimal insmod (5 args + NULL) or rmmod (3 args + NULL) */
605        argv = xmalloc(6 * sizeof(char*));
606        if (do_insert) {
607            if (already_loaded(list->m_name) != 1) {
608                argv[argc++] = (char*)"insmod";
609                if (ENABLE_FEATURE_2_4_MODULES) {
610                    if (do_syslog)
611                        argv[argc++] = (char*)"-s";
612                    if (autoclean)
613                        argv[argc++] = (char*)"-k";
614                    if (quiet)
615                        argv[argc++] = (char*)"-q";
616                    else if (verbose) /* verbose and quiet are mutually exclusive */
617                        argv[argc++] = (char*)"-v";
618                }
619                argv[argc++] = list->m_path;
620                if (ENABLE_FEATURE_CLEAN_UP)
621                    argc_malloc = argc;
622                opts = list->m_options;
623                while (opts) {
624                    /* Add one more option */
625                    argc++;
626                    argv = xrealloc(argv,(argc + 1)* sizeof(char*));
627                    argv[argc-1] = opts->m_opt_val;
628                    opts = opts->m_next;
629                }
630            }
631        } else {
632            /* modutils uses short name for removal */
633            if (already_loaded(list->m_name) != 0) {
634                argv[argc++] = (char*)"rmmod";
635                if (do_syslog)
636                    argv[argc++] = (char*)"-s";
637                argv[argc++] = (char*)list->m_name;
638                if (ENABLE_FEATURE_CLEAN_UP)
639                    argc_malloc = argc;
640            }
641        }
642        argv[argc] = NULL;
643
644        if (argc) {
645            if (verbose) {
646                printf("%s module %s\n", do_insert?"Loading":"Unloading", list->m_name);
647            }
648            if (!show_only) {
649                int rc2 = wait4pid(spawn(argv));
650
651                if (do_insert) {
652                    rc = rc2; /* only last module matters */
653                } else if (!rc2) {
654                    rc = 0; /* success if remove any mod */
655                }
656            }
657            if (ENABLE_FEATURE_CLEAN_UP) {
658                /* the last value in the array has index == argc, but
659                 * it is the terminating NULL, so we must not free it. */
660                while (argc_malloc < argc) {
661                    free(argv[argc_malloc++]);
662                }
663            }
664        }
665        if (ENABLE_FEATURE_CLEAN_UP) {
666            free(argv);
667            argv = NULL;
668        }
669        list = do_insert ? list->m_prev : list->m_next;
670    }
671    return (show_only) ? 0 : rc;
672}
673
674/*
675 * Check the matching between a pattern and a module name.
676 * We need this as *_* is equivalent to *-*, even in pattern matching.
677 */
678static int check_pattern(const char* pat_src, const char* mod_src)
679{
680    int ret;
681
682    if (ENABLE_FEATURE_MODPROBE_FANCY_ALIAS) {
683        char* pat;
684        char* mod;
685        char* p;
686
687        pat = xstrdup(pat_src);
688        mod = xstrdup(mod_src);
689
690        for (p = pat; (p = strchr(p, '-')); *p++ = '_');
691        for (p = mod; (p = strchr(p, '-')); *p++ = '_');
692
693        ret = fnmatch(pat, mod, 0);
694
695        if (ENABLE_FEATURE_CLEAN_UP) {
696            free(pat);
697            free(mod);
698        }
699
700        return ret;
701    } else {
702        return fnmatch(pat_src, mod_src, 0);
703    }
704}
705
706/*
707 * Builds the dependency list (aka stack) of a module.
708 * head: the highest module in the stack (last to insmod, first to rmmod)
709 * tail: the lowest module in the stack (first to insmod, last to rmmod)
710 */
711static void check_dep(char *mod, struct mod_list_t **head, struct mod_list_t **tail)
712{
713    struct mod_list_t *find;
714    struct dep_t *dt;
715    struct mod_opt_t *opt = 0;
716    char *path = 0;
717
718    /* Search for the given module name amongst all dependency rules.
719     * The module name in a dependency rule can be a shell pattern,
720     * so try to match the given module name against such a pattern.
721     * Of course if the name in the dependency rule is a plain string,
722     * then we consider it a pattern, and matching will still work. */
723    for (dt = depend; dt; dt = dt->m_next) {
724        if (check_pattern(dt->m_name, mod) == 0) {
725            break;
726        }
727    }
728
729    if (!dt) {
730        bb_error_msg("module %s not found", mod);
731        return;
732    }
733
734    // resolve alias names
735    while (dt->m_isalias) {
736        if (dt->m_depcnt == 1) {
737            struct dep_t *adt;
738
739            for (adt = depend; adt; adt = adt->m_next) {
740                if (check_pattern(adt->m_name, dt->m_deparr[0]) == 0)
741                    break;
742            }
743            if (adt) {
744                /* This is the module we are aliased to */
745                struct mod_opt_t *opts = dt->m_options;
746                /* Option of the alias are appended to the options of the module */
747                while (opts) {
748                    adt->m_options = append_option(adt->m_options, opts->m_opt_val);
749                    opts = opts->m_next;
750                }
751                dt = adt;
752            } else {
753                bb_error_msg("module %s not found", mod);
754                return;
755            }
756        } else {
757            bb_error_msg("bad alias %s", dt->m_name);
758            return;
759        }
760    }
761
762    mod = dt->m_name;
763    path = dt->m_path;
764    opt = dt->m_options;
765
766    // search for duplicates
767    for (find = *head; find; find = find->m_next) {
768        if (!strcmp(mod, find->m_name)) {
769            // found ->dequeue it
770
771            if (find->m_prev)
772                find->m_prev->m_next = find->m_next;
773            else
774                *head = find->m_next;
775
776            if (find->m_next)
777                find->m_next->m_prev = find->m_prev;
778            else
779                *tail = find->m_prev;
780
781            break; // there can be only one duplicate
782        }
783    }
784
785    if (!find) { // did not find a duplicate
786        find = xmalloc(sizeof(struct mod_list_t));
787        find->m_name = mod;
788        find->m_path = path;
789        find->m_options = opt;
790    }
791
792    // enqueue at tail
793    if (*tail)
794        (*tail)->m_next = find;
795    find->m_prev = *tail;
796    find->m_next = 0;
797
798    if (!*head)
799        *head = find;
800    *tail = find;
801
802    if (dt) {
803        int i;
804
805        /* Add all dependable module for that new module */
806        for (i = 0; i < dt->m_depcnt; i++)
807            check_dep(dt->m_deparr[i], head, tail);
808    }
809}
810
811static int mod_insert(char *mod, int argc, char **argv)
812{
813    struct mod_list_t *tail = NULL;
814    struct mod_list_t *head = NULL;
815    int rc;
816
817    // get dep list for module mod
818    check_dep(mod, &head, &tail);
819
820    rc = 1;
821    if (head && tail) {
822        if (argc) {
823            int i;
824            // append module args
825            for (i = 0; i < argc; i++)
826                head->m_options = append_option(head->m_options, argv[i]);
827        }
828
829        // process tail ---> head
830        rc = mod_process(tail, 1);
831        if (rc) {
832            /*
833             * In case of using udev, multiple instances of modprobe can be
834             * spawned to load the same module (think of two same usb devices,
835             * for example; or cold-plugging at boot time). Thus we shouldn't
836             * fail if the module was loaded, and not by us.
837             */
838            if (already_loaded(mod))
839                rc = 0;
840        }
841    }
842    return rc;
843}
844
845static int mod_remove(char *mod)
846{
847    int rc;
848    static const struct mod_list_t rm_a_dummy = { "-a", NULL, NULL, NULL, NULL };
849
850    struct mod_list_t *head = NULL;
851    struct mod_list_t *tail = NULL;
852
853    if (mod)
854        check_dep(mod, &head, &tail);
855    else  // autoclean
856        head = tail = (struct mod_list_t*) &rm_a_dummy;
857
858    rc = 1;
859    if (head && tail)
860        rc = mod_process(head, 0);  // process head ---> tail
861    return rc;
862}
863
864int modprobe_main(int argc, char** argv);
865int modprobe_main(int argc, char** argv)
866{
867    int rc = EXIT_SUCCESS;
868    char *unused;
869
870    opt_complementary = "?V-:q-v:v-q";
871    main_opts = getopt32(argv, "acdklnqrst:vVC:",
872                            &unused, &unused);
873    if (main_opts & (DUMP_CONF_EXIT | LIST_ALL))
874        return EXIT_SUCCESS;
875    if (main_opts & (RESTRICT_DIR | CONFIG_FILE))
876        bb_error_msg_and_die("-t and -C not supported");
877
878    depend = build_dep();
879
880    if (!depend)
881        bb_error_msg_and_die("cannot parse modules.dep");
882
883    if (remove_opt) {
884        do {
885            if (mod_remove(optind < argc ?
886                        argv[optind] : NULL)) {
887                bb_error_msg("failed to remove module %s",
888                        argv[optind]);
889                rc = EXIT_FAILURE;
890            }
891        } while (++optind < argc);
892    } else {
893        if (optind >= argc)
894            bb_error_msg_and_die("no module or pattern provided");
895
896        if (mod_insert(argv[optind], argc - optind - 1, argv + optind + 1))
897            bb_error_msg_and_die("failed to load module %s", argv[optind]);
898    }
899
900    /* Here would be a good place to free up memory allocated during the dependencies build. */
901
902    return rc;
903}
Note: See TracBrowser for help on using the repository browser.