summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modutils/modprobe-small.c115
1 files changed, 108 insertions, 7 deletions
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 1a9d984..cf9be47 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -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.