www.pudn.com > module-init-tools-3.0.rar > depmod.c
/* New simplified depmod without backwards compat stuff and not requiring ksyms. (C) 2002 Rusty Russell IBM Corporation */ #include#include #include #include #include #include #include #include #include #include #include #include #include #include #include "zlibsupport.h" #include "depmod.h" #include "moduleops.h" #include "tables.h" #include "testing.h" /* I hate strcmp. */ #define streq(a,b) (strcmp((a),(b)) == 0) #ifndef MODULE_DIR #define MODULE_DIR "/lib/modules/" #endif static int verbose; static unsigned int skipchars; void fatal(const char *fmt, ...) { va_list arglist; fprintf(stderr, "FATAL: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist); exit(1); } void warn(const char *fmt, ...) { va_list arglist; fprintf(stderr, "WARNING: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist); } void *do_nofail(void *ptr, const char *file, int line, const char *expr) { if (!ptr) { fatal("Memory allocation failure %s line %d: %s.\n", file, line, expr); } return ptr; } #define SYMBOL_HASH_SIZE 1024 struct symbol { struct symbol *next; struct module *owner; char name[0]; }; static struct symbol *symbolhash[SYMBOL_HASH_SIZE]; /* This is based on the hash agorithm from gdbm, via tdb */ static inline unsigned int tdb_hash(const char *name) { unsigned value; /* Used to compute the hash value. */ unsigned i; /* Used to cycle through random values. */ /* Set the initial value from the key size. */ for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++) value = (value + (((unsigned char *)name)[i] << (i*5 % 24))); return (1103515243 * value + 12345); } void add_symbol(const char *name, struct module *owner) { unsigned int hash; struct symbol *new = NOFAIL(malloc(sizeof *new + strlen(name) + 1)); new->owner = owner; strcpy(new->name, name); hash = tdb_hash(name) % SYMBOL_HASH_SIZE; new->next = symbolhash[hash]; symbolhash[hash] = new; } static int print_unknown; struct module *find_symbol(const char *name, const char *modname, int weak) { struct symbol *s; /* For our purposes, .foo matches foo. PPC64 needs this. */ if (name[0] == '.') name++; for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s=s->next) { if (streq(s->name, name)) return s->owner; } if (print_unknown && !weak) warn("%s needs unknown symbol %s\n", modname, name); return NULL; } void add_dep(struct module *mod, struct module *depends_on) { unsigned int i; for (i = 0; i < mod->num_deps; i++) if (mod->deps[i] == depends_on) return; mod->deps = realloc(mod->deps, sizeof(mod->deps[0])*(mod->num_deps+1)); mod->deps[mod->num_deps++] = depends_on; } static void load_system_map(const char *filename) { FILE *system_map; char line[10240]; system_map = fopen(filename, "r"); if (!system_map) fatal("Could not open '%s': %s\n", filename, strerror(errno)); /* eg. c0294200 R __ksymtab_devfs_alloc_devnum */ while (fgets(line, sizeof(line)-1, system_map)) { char *ptr; /* Snip \n */ ptr = strchr(line, '\n'); *ptr = '\0'; ptr = strchr(line, ' '); if (!ptr || !(ptr = strchr(ptr + 1, ' '))) continue; /* Covers gpl-only and normal symbols. */ if (strncmp(ptr+1, "__ksymtab_", strlen("__ksymtab_")) == 0) add_symbol(ptr+1+strlen("__ksymtab_"), NULL); } /* __this_module is magic inserted by kernel loader. */ add_symbol("__this_module", NULL); /* On S390, this is faked up too */ add_symbol("_GLOBAL_OFFSET_TABLE_", NULL); } static struct option options[] = { { "all", 0, NULL, 'a' }, { "quick", 0, NULL, 'A' }, { "basedir", 1, NULL, 'b' }, { "errsyms", 0, NULL, 'e' }, { "filesyms", 1, NULL, 'F' }, { "help", 0, NULL, 'h' }, { "show", 0, NULL, 'n' }, { "dry-run", 0, NULL, 'n' }, { "quiet", 0, NULL, 'q' }, { "root", 0, NULL, 'r' }, { "unresolved-error", 0, NULL, 'u' }, { "verbose", 0, NULL, 'v' }, { "version", 0, NULL, 'V' }, /* Obsolete, but we need to parse it. */ { "config", 1, NULL, 'C' }, { NULL, 0, NULL, 0 } }; /* Version number or module name? Don't assume extension. */ static int is_version_number(const char *version) { unsigned int dummy; return (sscanf(version, "%u.%u.%u", &dummy, &dummy, &dummy) == 3); } static int old_module_version(const char *version) { /* Expect three part version. */ unsigned int major, sub, minor; sscanf(version, "%u.%u.%u", &major, &sub, &minor); if (major > 2) return 0; if (major < 2) return 1; /* 2.x */ if (sub > 5) return 0; if (sub < 5) return 1; /* 2.5.x */ if (minor >= 48) return 0; return 1; } static void exec_old_depmod(char *argv[]) { char *sep; char pathname[strlen(argv[0])+1]; char oldname[strlen("depmod") + strlen(argv[0]) + sizeof(".old")]; memset(pathname, 0, strlen(argv[0])+1); sep = strrchr(argv[0], '/'); if (sep) memcpy(pathname, argv[0], sep - argv[0]+1); sprintf(oldname, "%s%s.old", pathname, "depmod"); /* Recursion detection: we need an env var since we can't change argv[0] (as older modutils uses it to determine behavior). */ if (getenv("MODULE_RECURSE")) return; setenv("MODULE_RECURSE", "y", 0); execvp(oldname, argv); fprintf(stderr, "Version requires old depmod, but couldn't run %s: %s\n", oldname, strerror(errno)); exit(2); } static void print_usage(const char *name) { fprintf(stderr, "%s " VERSION " -- part of " PACKAGE "\n" "%s -[aA] [-n -e -v -q -V -r -u]\n" " [-b basedirectory] [forced_version]\n" "depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.o module2.o ...\n" "If no arguments (except options) are given, \"depmod -a\" is assumed\n" "\n" "depmod will output a dependancy list suitable for the modprobe utility.\n" "\n" "\n" "Options:\n" "\t-a, --all Probe all modules\n" "\t-n, --show Write the dependency file on stdout only\n" "\t-V, --version Print the release version\n" "\t-h, --help Print this usage message\n" "\n" "The following options are useful for people managing distributions:\n" "\t-b basedirectory\n" "\t --basedir basedirectory Use an image of a module tree.\n" "\t-F kernelsyms\n" "\t --filesyms kernelsyms Use the file instead of the\n" "\t current kernel symbols.\n", "depmod", "depmod"); } static int ends_in(const char *name, const char *ext) { unsigned int namelen, extlen; /* Grab lengths */ namelen = strlen(name); extlen = strlen(ext); if (namelen < extlen) return 0; if (streq(name + namelen - extlen, ext)) return 1; return 0; } static struct module *grab_module(const char *dirname, const char *filename, struct module *next) { struct module *new; new = NOFAIL(malloc(sizeof(*new) + strlen(dirname) + 1 + strlen(filename) + 1)); sprintf(new->pathname, "%s/%s", dirname, filename); INIT_LIST_HEAD(&new->dep_list); new->data = grab_file(new->pathname, &new->len); if (!new->data) { warn("Can't read module %s: %s\n", new->pathname, strerror(errno)); goto fail_data; } /* "\177ELF" where byte = 001 for 32-bit, 002 for 64 */ if (memcmp(new->data, ELFMAG, SELFMAG) != 0) { warn("Module %s is not an elf object\n", new->pathname); goto fail; } switch (((char *)new->data)[EI_CLASS]) { case ELFCLASS32: new->ops = &mod_ops32; break; case ELFCLASS64: new->ops = &mod_ops64; break; default: warn("Module %s has elf unknown identifier %i\n", new->pathname, ((char *)new->data)[EI_CLASS]); goto fail; } new->ops->load_symbols(new); new->ops->fetch_tables(new); new->next = next; return new; fail: release_file(new->data, new->len); fail_data: free(new); return next; } struct module_traverse { struct module_traverse *prev; struct module *mod; }; static int in_loop(struct module *mod, const struct module_traverse *traverse) { const struct module_traverse *i; for (i = traverse; i; i = i->prev) { if (i->mod == mod) return 1; } return 0; } static char *basename(const char *name) { const char *base = strrchr(name, '/'); if (base) return (char *)base + 1; return (char *)name; } /* Assume we are doing all the modules, so only report each loop once. */ static void report_loop(const struct module *mod, const struct module_traverse *traverse) { const struct module_traverse *i; /* Check that start is least alphabetically. eg. a depends on b depends on a will get reported for a, not b. */ for (i = traverse->prev; i->prev; i = i->prev) { if (strcmp(mod->pathname, i->mod->pathname) > 0) return; } /* Is start in the loop? If not, don't report now. eg. a depends on b which depends on c which depends on b. Don't report when generating depends for a. */ if (mod != i->mod) return; warn("Loop detected: %s ", mod->pathname); for (i = traverse->prev; i->prev; i = i->prev) fprintf(stderr, "needs %s ", basename(i->mod->pathname)); fprintf(stderr, "which needs %s again!\n", basename(mod->pathname)); } /* This is damn slow, but loops actually happen, and we don't want to just exit() and leave the user without any modules. */ static int has_dep_loop(struct module *module, struct module_traverse *prev) { unsigned int i; struct module_traverse traverse = { .prev = prev, .mod = module }; if (in_loop(module, prev)) { report_loop(module, &traverse); return 1; } for (i = 0; i < module->num_deps; i++) if (has_dep_loop(module->deps[i], &traverse)) return 1; return 0; } /* Uniquifies and orders a dependency list. */ static void order_dep_list(struct module *start, struct module *mod) { unsigned int i; for (i = 0; i < mod->num_deps; i++) { /* If it was previously depended on, move it to the tail. ie. if a needs b and c, and c needs b, we must order b after c. */ list_del(&mod->deps[i]->dep_list); list_add_tail(&mod->deps[i]->dep_list, &start->dep_list); order_dep_list(start, mod->deps[i]); } } static void del_module(struct module **modules, struct module *delme) { struct module **i; /* Find pointer to it. */ for (i = modules; *i != delme; i = &(*i)->next); *i = delme->next; } static void output_deps(struct module *modules, FILE *out) { struct module *i; for (i = modules; i; i = i->next) i->ops->calculate_deps(i, verbose); /* Strip out loops. */ again: for (i = modules; i; i = i->next) { if (has_dep_loop(i, NULL)) { warn("Module %s ignored, due to loop\n", i->pathname + skipchars); del_module(&modules, i); goto again; } } /* Now dump them out. */ for (i = modules; i; i = i->next) { struct list_head *j, *tmp; order_dep_list(i, i); fprintf(out, "%s:", i->pathname + skipchars); list_for_each_safe(j, tmp, &i->dep_list) { struct module *dep = list_entry(j, struct module, dep_list); fprintf(out, " %s", dep->pathname + skipchars); list_del_init(j); } fprintf(out, "\n"); } } static int smells_like_module(const char *name) { return ends_in(name,".ko") || ends_in(name, ".ko.gz"); } static struct module *grab_dir(const char *dirname, struct module *next) { DIR *dir; struct dirent *dirent; dir = opendir(dirname); if (!dir) { warn("Couldn't open directory %s: %s\n", dirname, strerror(errno)); return next; } while ((dirent = readdir(dir)) != NULL) { if (smells_like_module(dirent->d_name)) next = grab_module(dirname, dirent->d_name, next); else if (!streq(dirent->d_name, ".") && !streq(dirent->d_name, "..")) { struct stat st; char subdir[strlen(dirname) + 1 + strlen(dirent->d_name) + 1]; sprintf(subdir, "%s/%s", dirname, dirent->d_name); if (lstat(subdir, &st) != 0) warn("Couldn't stat %s: %s\n", subdir, strerror(errno)); else if (S_ISDIR(st.st_mode)) next = grab_dir(subdir, next); } } closedir(dir); return next; } /* Convert filename to the module name. Works if filename == modname, too. */ static void filename2modname(char *modname, const char *filename) { const char *afterslash; unsigned int i; afterslash = strrchr(filename, '/'); if (!afterslash) afterslash = filename; else afterslash++; /* Convert to underscores, stop at first . */ for (i = 0; afterslash[i] && afterslash[i] != '.'; i++) { if (afterslash[i] == '-') modname[i] = '_'; else modname[i] = afterslash[i]; } modname[i] = '\0'; } /* Simply dump hash table. */ static void output_symbols(struct module *unused, FILE *out) { unsigned int i; fprintf(out, "# Aliases for symbols, used by symbol_request().\n"); for (i = 0; i < SYMBOL_HASH_SIZE; i++) { struct symbol *s; for (s = symbolhash[i]; s; s = s->next) { if (s->owner) { char modname[strlen(s->owner->pathname)+1]; filename2modname(modname, s->owner->pathname); fprintf(out, "alias symbol:%s %s\n", s->name, modname); } } } } static const char *next_string(const char *string, unsigned long *secsize) { /* Skip non-zero chars */ while (string[0]) { string++; if ((*secsize)-- <= 1) return NULL; } /* Skip any zero padding. */ while (!string[0]) { string++; if ((*secsize)-- <= 1) return NULL; } return string; } static void output_aliases(struct module *modules, FILE *out) { struct module *i; const char *p; long size; fprintf(out, "# Aliases extracted from modules themselves.\n"); for (i = modules; i; i = i->next) { char modname[strlen(i->pathname)+1]; filename2modname(modname, i->pathname); /* Grab from old-style .modalias section. */ for (p = i->ops->get_aliases(i, &size); p; p = next_string(p, &size)) fprintf(out, "alias %s %s\n", p, modname); /* Grab form new-style .modinfo section. */ for (p = i->ops->get_modinfo(i, &size); p; p = next_string(p, &size)) { if (strncmp(p, "alias=", strlen("alias=")) == 0) fprintf(out, "alias %s %s\n", p + strlen("alias="), modname); } } } struct depfile { char *name; void (*func)(struct module *, FILE *); }; static struct depfile depfiles[] = { { "modules.dep", output_deps }, /* This is what we check for '-A'. */ { "modules.pcimap", output_pci_table }, { "modules.usbmap", output_usb_table }, { "modules.ccwmap", output_ccw_table }, { "modules.ieee1394map", output_ieee1394_table }, { "modules.isapnpmap", output_isapnp_table }, { "modules.inputmap", output_input_table }, { "modules.alias", output_aliases }, { "modules.symbols", output_symbols }, }; /* If we can't figure it out, it's safe to say "true". */ static int any_modules_newer(const char *dirname, time_t mtime) { DIR *dir; struct dirent *dirent; dir = opendir(dirname); if (!dir) return 1; while ((dirent = readdir(dir)) != NULL) { struct stat st; char file[strlen(dirname) + 1 + strlen(dirent->d_name) + 1]; if (streq(dirent->d_name, ".") || streq(dirent->d_name, "..")) continue; sprintf(file, "%s/%s", dirname, dirent->d_name); if (lstat(file, &st) != 0) return 1; if (smells_like_module(dirent->d_name)) { if (st.st_mtime > mtime) return 1; } else if (S_ISDIR(st.st_mode)) { if (any_modules_newer(file, mtime)) return 1; } } closedir(dir); return 0; } static int depfile_out_of_date(const char *dirname) { struct stat st; char depfile[strlen(dirname) + 1 + strlen(depfiles[0].name) + 1]; sprintf(depfile, "%s/%s", dirname, depfiles[0].name); if (stat(depfile, &st) != 0) return 1; return any_modules_newer(dirname, st.st_mtime); } int main(int argc, char *argv[]) { int opt, all = 0, maybe_all = 0, doing_stdout = 0; char *basedir = "", *dirname, *version, *badopt = NULL, *system_map = NULL; struct module *list = NULL; int i; /* Don't print out any errors just yet, we might want to exec backwards compat version. */ opterr = 0; while ((opt = getopt_long(argc, argv, "ab:ArehnqruvVF:C:", options, NULL)) != -1) { switch (opt) { case 'a': all = 1; break; case 'b': basedir = optarg; skipchars = strlen(basedir); break; case 'A': maybe_all = 1; break; case 'F': system_map = optarg; break; case 'e': print_unknown = 1; break; case 'v': verbose = 1; break; case 'u': case 'q': case 'r': case 'C': /* Ignored. */ break; case 'h': print_usage(argv[0]); break; case 'n': doing_stdout = 1; break; case 'V': printf("%s %s\n", PACKAGE, VERSION); exit(0); default: badopt = argv[optind-1]; } } /* We can't print unknowns without a System.map */ if (!system_map) print_unknown = 0; else load_system_map(system_map); /* They can specify the version naked on the command line */ if (optind < argc && is_version_number(argv[optind])) { version = strdup(argv[optind]); optind++; } else { struct utsname buf; uname(&buf); version = strdup(buf.release); } /* Run old version if required. */ if (old_module_version(version)) exec_old_depmod(argv); if (badopt) { fprintf(stderr, "%s: malformed/unrecognized option '%s'\n", argv[0], badopt); print_usage(argv[0]); exit(1); } /* Depmod -a by default if no names. */ if (optind == argc) all = 1; dirname = malloc(strlen(basedir) + strlen(MODULE_DIR) + strlen(version) + 1); sprintf(dirname, "%s%s%s", basedir, MODULE_DIR, version); if (maybe_all) { if (!doing_stdout && !depfile_out_of_date(dirname)) exit(0); all = 1; } if (!all) { /* Do command line args. */ for (opt = optind + 1; opt < argc; opt++) list = grab_module("", argv[opt], list); } else { list = grab_dir(dirname, list); } for (i = 0; i < sizeof(depfiles)/sizeof(depfiles[0]); i++) { FILE *out; struct depfile *d = &depfiles[i]; char depname[strlen(dirname) + 1 + strlen(d->name) + 1]; char tmpname[strlen(dirname) + 1 + strlen(d->name) + strlen(".temp") + 1]; sprintf(depname, "%s/%s", dirname, d->name); sprintf(tmpname, "%s/%s.temp", dirname, d->name); if (!doing_stdout) { out = fopen(tmpname, "w"); if (!out) fatal("Could not open %s for writing: %s\n", tmpname, strerror(errno)); } else out = stdout; d->func(list, out); if (!doing_stdout) { fclose(out); if (rename(tmpname, depname) < 0) fatal("Could not rename %s into %s: %s\n", tmpname, depname, strerror(errno)); } } free(dirname); free(version); return 0; }