modprobe-small: add depfile creation
[oweals/busybox.git] / modutils / modprobe-small.c
index 1a9d98422f25dad2c2f346b921e7f8e637fa3372..cf9be47a6a05d27bf869804e396d8320336f5f55 100644 (file)
@@ -43,6 +43,7 @@ typedef struct module_info {
 struct globals {
        module_info *modinfo;
        char *module_load_options;
+       smallint dep_bb_seen;
        int module_count;
        int module_found_idx;
        int stringbuf_idx;
@@ -51,6 +52,7 @@ struct globals {
 };
 #define G (*ptr_to_globals)
 #define modinfo             (G.modinfo            )
+#define dep_bb_seen         (G.dep_bb_seen        )
 #define module_count        (G.module_count       )
 #define module_found_idx    (G.module_found_idx   )
 #define module_load_options (G.module_load_options)
@@ -320,13 +322,28 @@ static FAST_FUNC int fileAction(const char *pathname,
        return TRUE;
 }
 
-static void load_dep_bb(void)
+static int load_dep_bb(void)
 {
        char *line;
        FILE *fp = fopen(DEPFILE_BB, "r");
 
        if (!fp)
-               return;
+               return 0;
+
+       dep_bb_seen = 1;
+       dbg1_error_msg("loading "DEPFILE_BB);
+
+       /* Why? There is a rare scenario: we did not find modprobe.dep.bb,
+        * we scanned the dir and found no module by name, then we search
+        * for alias (full scan), and we decided to generate modprobe.dep.bb.
+        * But we see modprobe.dep.bb.new! Other modprobe is at work!
+        * We wait and other modprobe renames it to modprobe.dep.bb.
+        * Now we can use it.
+        * But we already have modinfo[] filled, and "module_count = 0"
+        * makes us start anew. Yes, we leak modinfo[].xxx pointers -
+        * there is not much of data there anyway. */
+       module_count = 0;
+       memset(&modinfo[0], 0, sizeof(modinfo[0]));
 
        while ((line = xmalloc_fgetline(fp)) != NULL) {
                char* space;
@@ -355,13 +372,74 @@ static void load_dep_bb(void)
                        free(line);
                }
        }
+       return 1;
+}
+
+static int start_dep_bb_writeout(void)
+{
+       int fd;
+
+       fd = open(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+       if (fd < 0) {
+               if (errno == EEXIST) {
+                       int count = 5 * 20;
+                       dbg1_error_msg(DEPFILE_BB".new exists, waiting for "DEPFILE_BB);
+                       while (1) {
+                               usleep(1000*1000 / 20);
+                               if (load_dep_bb()) {
+                                       dbg1_error_msg(DEPFILE_BB" appeared");
+                                       return -2; /* magic number */
+                               }
+                               if (!--count)
+                                       break;
+                       }
+                       bb_error_msg("deleting stale %s", DEPFILE_BB".new");
+                       fd = open_or_warn(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC);
+               }
+       }
+       dbg1_error_msg("opened "DEPFILE_BB".new:%d", fd);
+       return fd;
+}
+
+static void write_out_dep_bb(int fd)
+{
+       int i;
+       FILE *fp;
+
+       /* We want good error reporting. fdprintf is not good enough. */
+       fp = fdopen(fd, "w");
+       if (!fp) {
+               close(fd);
+               goto err;
+       }
+       i = 0;
+       while (modinfo[i].pathname) {
+               fprintf(fp, "%s%s%s\n" "%s%s\n",
+                       modinfo[i].pathname, modinfo[i].aliases[0] ? " " : "", modinfo[i].aliases,
+                       modinfo[i].deps, modinfo[i].deps[0] ? "\n" : "");
+               i++;
+       }
+       /* Badly formatted depfile is a no-no. Be paranoid. */
+       errno = 0;
+       if (ferror(fp) | fclose(fp))
+               goto err;
+       if (rename(DEPFILE_BB".new", DEPFILE_BB) != 0) {
+ err:
+               bb_perror_msg("can't create %s", DEPFILE_BB);
+               unlink(DEPFILE_BB".new");
+       } else {
+               dbg1_error_msg("created "DEPFILE_BB);
+       }
 }
 
 static module_info* find_alias(const char *alias)
 {
        int i;
+       int dep_bb_fd;
+       module_info *result;
        dbg1_error_msg("find_alias('%s')", alias);
 
+ try_again:
        /* First try to find by name (cheaper) */
        i = 0;
        while (modinfo[i].pathname) {
@@ -376,13 +454,22 @@ static module_info* find_alias(const char *alias)
                i++;
        }
 
+       /* Ok, we definitely have to scan module bodies. This is a good
+        * moment to generate modprobe.dep.bb, if it does not exist yet */
+       dep_bb_fd = dep_bb_seen ? -1 : start_dep_bb_writeout();
+       if (dep_bb_fd == -2) /* modprobe.dep.bb appeared? */
+               goto try_again;
+
        /* Scan all module bodies, extract modinfo (it contains aliases) */
        i = 0;
+       result = NULL;
        while (modinfo[i].pathname) {
                char *desc, *s;
                if (!modinfo[i].aliases) {
                        parse_module(&modinfo[i], modinfo[i].pathname);
                }
+               if (result)
+                       continue;
                /* "alias1 symbol:sym1 alias2 symbol:sym2" */
                desc = str_2_list(modinfo[i].aliases);
                /* Does matching substring exist? */
@@ -392,17 +479,25 @@ static module_info* find_alias(const char *alias)
                         * "pci:v000010DEd000000D9sv*sd*bc*sc*i*".
                         * Plain strcmp() won't catch that */
                        if (fnmatch(s, alias, 0) == 0) {
-                               free(desc);
                                dbg1_error_msg("found alias '%s' in module '%s'",
                                                alias, modinfo[i].pathname);
-                               return &modinfo[i];
+                               result = &modinfo[i];
+                               break;
                        }
                }
                free(desc);
+               if (result && dep_bb_fd < 0)
+                       return result;
                i++;
        }
-       dbg1_error_msg("find_alias '%s' returns NULL", alias);
-       return NULL;
+
+       /* Create module.dep.bb if needed */
+       if (dep_bb_fd >= 0) {
+               write_out_dep_bb(dep_bb_fd);
+       }
+
+       dbg1_error_msg("find_alias '%s' returns %p", alias, result);
+       return result;
 }
 
 #if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
@@ -497,7 +592,7 @@ static void process_module(char *name, const char *cmdline_options)
 
        /* rmmod? unload it by name */
        if (is_rmmod) {
-               if (delete_module(name, O_NONBLOCK|O_EXCL) != 0
+               if (delete_module(name, O_NONBLOCK | O_EXCL) != 0
                 && !(option_mask32 & OPT_q)
                ) {
                        bb_perror_msg("remove '%s'", name);
@@ -511,6 +606,8 @@ static void process_module(char *name, const char *cmdline_options)
        }
 
        if (!info) { /* both dirscan and find_alias found nothing */
+               bb_error_msg("module '%s' not found", name);
+//TODO: _and_die()?
                goto ret;
        }
 
@@ -637,6 +734,10 @@ int modprobe_main(int argc UNUSED_PARAM, char **argv)
                argv[1] = NULL;
 #endif
 
+       /* Prevent ugly corner cases with no modules at all */
+       modinfo = xzalloc(sizeof(modinfo[0]));
+
+       /* Try to load modprobe.dep.bb */
        load_dep_bb();
 
        /* Load/remove modules.