diff options
Diffstat (limited to 'mdk-stage1/modules.c')
-rw-r--r-- | mdk-stage1/modules.c | 498 |
1 files changed, 261 insertions, 237 deletions
diff --git a/mdk-stage1/modules.c b/mdk-stage1/modules.c index 1fe1cd42a..76177302e 100644 --- a/mdk-stage1/modules.c +++ b/mdk-stage1/modules.c @@ -1,7 +1,7 @@ /* - * Guillaume Cottenceau (gc@mandrakesoft.com) + * Guillaume Cottenceau (gc) * - * Copyright 2000 MandrakeSoft + * Copyright 2000 Mandriva * * This software may be freely redistributed under the terms of the GNU * public license. @@ -18,108 +18,130 @@ * (3) insmod them */ +#include "stage1.h" + #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mount.h> #include <fcntl.h> +#include <libgen.h> #include <unistd.h> #include <string.h> #include <stdio.h> -#include "insmod.h" -#include "stage1.h" +#include <errno.h> +#include <time.h> +#include <sys/utsname.h> +#include <libkmod.h> #include "log.h" -#include "mar/mar-extract-only.h" +#include "utils.h" #include "frontend.h" #include "mount.h" -#include "modules_descr.h" +#include "zlibsupport.h" #include "modules.h" -static struct module_deps_elem * modules_deps = NULL; - -static char * archive_name = "/modules/modules.mar"; -int disable_modules = 0; +static char modules_directory[100]; +static struct module_descr_elem * modules_descr = NULL; +extern long init_module(void *, unsigned long, const char *); -/* unarchive and insmod given module - * WARNING: module must not contain the trailing ".o" - */ -static enum insmod_return insmod_archived_file(const char * mod_name, char * options) +static const char *moderror(int err) { - char module_name[50]; - char final_name[50] = "/tmp/"; - int i, rc; - - strncpy(module_name, mod_name, sizeof(module_name)); - strcat(module_name, ".o"); - i = mar_extract_file(archive_name, module_name, "/tmp/"); - if (i == 1) { - log_message("file-not-found-in-archive %s (maybe you can try another boot floppy such as 'other.img' for seldom used SCSI modules)", module_name); - return INSMOD_FAILED_FILE_NOT_FOUND; + switch (err) { + case ENOEXEC: + return "Invalid module format"; + case ENOENT: + return "Unknown symbol in module"; + case ESRCH: + return "Module has wrong symbol version"; + case EINVAL: + return "Invalid parameters"; + default: + return strerror(err); } - if (i != 0) - return INSMOD_FAILED; +} - strcat(final_name, mod_name); - strcat(final_name, ".o"); +int insmod_local_file(char * path, char * options) +{ + void *file; + unsigned long len; + int rc; + + if (IS_TESTING) + return 0; - rc = insmod_call(final_name, options); - unlink(final_name); /* sucking no space left on device */ - if (rc) { - log_message("\tfailed"); - return INSMOD_FAILED; + file = grab_file(path, &len); + + if (!file) { + log_perror(asprintf_("\terror reading %s", path)); + return -1; } - return INSMOD_OK; + + rc = init_module(file, len, options ? options : ""); + if (rc) + log_message("\terror: %s", moderror(errno)); + return rc; } - - -static int load_modules_dependencies(void) +static char *kernel_module_extension(void) { - char * deps_file = "/modules/modules.dep"; - char * buf, * ptr, * start, * end; - struct stat s; - int fd, line, i; + return ".ko.xz"; +} - log_message("loading modules dependencies"); - if (IS_TESTING) - return 0; +static char *filename2modname(char * filename) { + char *modname, *p; - fd = open(deps_file, O_RDONLY); - if (fd == -1) { - log_perror(deps_file); - return -1; + modname = strdup(basename(filename)); + if (strstr(modname, kernel_module_extension())) { + modname[strlen(modname)-strlen(kernel_module_extension())] = '\0'; /* remove trailing .ko.gz */ } - - fstat(fd, &s); - buf = alloca(s.st_size + 1); - if (read(fd, buf, s.st_size) != (ssize_t)s.st_size) { - log_perror(deps_file); - return -1; + + p = modname; + while (p && *p) { + if (*p == '-') + *p = '_'; + p++; } - buf[s.st_size] = '\0'; - close(fd); - ptr = buf; - line = 0; - while (ptr) { - line++; - ptr = strchr(ptr + 1, '\n'); + return modname; +} + +static void find_modules_directory(void) +{ + struct utsname kernel_uname; + char * prefix = "/lib/modules"; + char * release; + if (uname(&kernel_uname)) { + fatal_error("uname failed"); } + release = kernel_uname.release; + sprintf(modules_directory , "%s/%s", prefix, release); +} + +static int load_modules_descriptions(void) +{ + char * descr_file = asprintf_("%s/%s", modules_directory, "modules.description"); + char * buf, * ptr, * start, * end; + struct stat s; + int line; - modules_deps = malloc(sizeof(*modules_deps) * (line+1)); + log_message("loading modules descriptions"); + + buf = cat_file(descr_file, &s); + if (!buf) + return -1; + line = line_counts(buf); + modules_descr = malloc(sizeof(*modules_descr) * (line+1)); start = buf; line = 0; while (start < (buf+s.st_size) && *start) { - char * tmp_deps[50]; - end = strchr(start, '\n'); *end = '\0'; - ptr = strchr(start, ':'); + ptr = strchr(start, '\t'); if (!ptr) { start = end + 1; continue; @@ -127,46 +149,24 @@ static int load_modules_dependencies(void) *ptr = '\0'; ptr++; - while (*ptr && (*ptr == ' ')) ptr++; - if (!*ptr) { - start = end + 1; - continue; - } - - /* sort of a good line */ - modules_deps[line].name = strdup(start); - - start = ptr; - i = 0; - while (start && *start) { - ptr = strchr(start, ' '); - if (ptr) *ptr = '\0'; - tmp_deps[i++] = strdup(start); - if (ptr) - start = ptr + 1; - else - start = NULL; - while (start && *start && *start == ' ') - start++; - } - tmp_deps[i++] = NULL; - - modules_deps[line].deps = memdup(tmp_deps, sizeof(char *) * i); + modules_descr[line].modname = filename2modname(start); + modules_descr[line].description = strndup(ptr, 50); line++; start = end + 1; } - modules_deps[line].name = NULL; + modules_descr[line].modname = NULL; + + free(buf); return 0; } - void init_modules_insmoding(void) { - if (load_modules_dependencies()) { - log_message("warning, error initing modules stuff, modules loading disabled"); - disable_modules = 1; + find_modules_directory(); + if (load_modules_descriptions()) { + log_message("warning, error initing modules stuff"); } } @@ -174,7 +174,7 @@ void init_modules_insmoding(void) static void add_modules_conf(char * str) { static char data[5000] = ""; - char * target = "/etc/modules.conf"; + char * target = "/tmp/modules.conf"; int fd; if (strlen(data) + strlen(str) >= sizeof(data)) @@ -197,52 +197,125 @@ static void add_modules_conf(char * str) } -static int module_already_present(const char * name) +int module_already_present(const char * name) { FILE * f; + struct stat sb; + char *path; int answ = 0; - f = fopen("/proc/modules", "rb"); - while (1) { - char buf[500]; - if (!fgets(buf, sizeof(buf), f)) break; - if (!strncmp(name, buf, strlen(name)) && buf[strlen(name)] == ' ') + + if ((f = fopen("/proc/modules", "rb"))) { + while (1) { + char buf[500]; + if (!fgets(buf, sizeof(buf), f)) break; + if (!strncmp(name, buf, strlen(name)) && buf[strlen(name)] == ' ') + answ = 1; + } + fclose(f); + } + + /* built-in module case. try to find them through sysfs */ + if (!answ) { + asprintf(&path, "/sys/module/%s", name); + if (!stat(path, &sb)) answ = 1; + free(path); + } + if (!answ) { + asprintf(&path, "/sys/fs/%s", name); + if (!stat(path, &sb)) + answ = 1; + free(path); } - fclose(f); return answ; } -static enum insmod_return insmod_with_deps(const char * mod_name, char * options) -{ - struct module_deps_elem * dep; - - dep = modules_deps; - while (dep && dep->name && strcmp(dep->name, mod_name)) dep++; - - if (dep && dep->name && dep->deps) { - char ** one_dep; - one_dep = dep->deps; - while (*one_dep) { - /* here, we can fail but we don't care, if the error is - * important, the desired module will fail also */ - insmod_with_deps(*one_dep, NULL); - one_dep++; +#ifndef ENABLE_NETWORK_STANDALONE +static enum insmod_return insmod_with_deps(const char * alias, char * options) { + struct kmod_ctx *ctx; + struct kmod_list *l, *list = NULL; + int err = 0, flags = 0; + + if (!*modules_directory) + find_modules_directory(); + + ctx = kmod_new(modules_directory, NULL); + if (!ctx) { + fputs("Error: kmod_new() failed!\n", stderr); + goto exit; + } + kmod_load_resources(ctx); + + err = kmod_module_new_from_lookup(ctx, alias, &list); + if (err < 0) + goto exit; + + // No module found... + if (list == NULL) + goto exit; + + // filter through blacklist + struct kmod_list *filtered = NULL; + err = kmod_module_apply_filter(ctx, KMOD_FILTER_BLACKLIST, list, &filtered); + kmod_module_unref_list(list); + if (err < 0) + goto exit; + list = filtered; + + kmod_list_foreach(l, list) { + struct kmod_module *mod = kmod_module_get_module(l); + err = kmod_module_probe_insert_module(mod, flags, + options, NULL, NULL, NULL); + + if (err >= 0) + /* ignore flag return values such as a mod being blacklisted */ + err = 0; + else { + switch (err) { + case -EEXIST: + fprintf(stderr, "could not insert '%s': Module already in kernel\n", + kmod_module_get_name(mod)); + break; + case -ENOENT: + fprintf(stderr, "could not insert '%s': Unknown symbol in module, " + "or unknown parameter (see dmesg)\n", + kmod_module_get_name(mod)); + break; + default: + fprintf(stderr, "could not insert '%s': %s\n", + kmod_module_get_name(mod), + strerror(-err)); + break; + } } + + kmod_module_unref(mod); + if (err < 0) + break; } - if (module_already_present(mod_name)) - return INSMOD_OK; + kmod_module_unref_list(list); - log_message("needs %s", mod_name); - return insmod_archived_file(mod_name, options); +exit: + kmod_unref(ctx); + + switch (err){ + case 0: + return INSMOD_OK; + case -ENOENT: + return INSMOD_FAILED_FILE_NOT_FOUND; + default: + return INSMOD_FAILED; + } } +#endif #ifndef DISABLE_NETWORK -enum insmod_return my_insmod(const char * mod_name, enum driver_type type, char * options) +enum insmod_return my_modprobe(const char * mod_name, enum driver_type type, char * options) #else -enum insmod_return my_insmod(const char * mod_name, enum driver_type type __attribute__ ((unused)), char * options) +enum insmod_return my_modprobe(const char * mod_name, enum driver_type type __attribute__ ((unused)), char * options) #endif { int i; @@ -250,22 +323,26 @@ enum insmod_return my_insmod(const char * mod_name, enum driver_type type __attr char ** net_devices = NULL; /* fucking compiler */ #endif - log_message("have to insmod %s", mod_name); - - if (disable_modules) { - log_message("\tdisabled"); + if (module_already_present(mod_name)) return INSMOD_OK; - } + + log_message("have to insmod %s", mod_name); #ifndef DISABLE_NETWORK if (type == NETWORK_DEVICES) net_devices = get_net_devices(); #endif - if (IS_TESTING) - return INSMOD_OK; - +#ifdef ENABLE_NETWORK_STANDALONE + { + char *cmd = options ? asprintf_("/sbin/modprobe %s %s", mod_name, options) : + asprintf_("/sbin/modprobe %s", mod_name); + log_message("running %s", cmd); + i = system(cmd); + } +#else i = insmod_with_deps(mod_name, options); +#endif if (i == 0) { log_message("\tsucceeded %s", mod_name); #ifndef DISABLE_NETWORK @@ -282,7 +359,6 @@ enum insmod_return my_insmod(const char * mod_name, enum driver_type type __attr sprintf(alias, "alias %s %s", *new_net_devices, mod_name); add_modules_conf(alias); log_message("NET: %s", alias); - net_discovered_interface(*new_net_devices); already_present: new_net_devices++; @@ -309,9 +385,9 @@ static enum return_type insmod_with_options(char * mod, enum driver_type type) strcat(options, mod); strcat(options, " "); - strcat(options, answers[0]); // because my_insmod will eventually modify the string + strcat(options, answers[0]); // because my_modprobe will eventually modify the string - if (my_insmod(mod, type, answers[0]) != INSMOD_OK) { + if (my_modprobe(mod, type, answers[0]) != INSMOD_OK) { stg1_error_message("Insmod failed."); return RETURN_ERROR; } @@ -321,117 +397,65 @@ static enum return_type insmod_with_options(char * mod, enum driver_type type) return RETURN_OK; } +static int strsortfunc(const void *a, const void *b) +{ + return strcmp(* (char * const *) a, * (char * const *) b); +} + enum return_type ask_insmod(enum driver_type type) { - char * mytype; - char msg[200]; enum return_type results; char * choice; + char ** dlist = list_directory(modules_directory); + char ** modules = alloca(sizeof(char *) * (string_array_length(dlist) + 1)); + char ** descrs = alloca(sizeof(char *) * (string_array_length(dlist) + 1)); + char ** p_dlist = dlist; + char ** p_modules = modules; + char ** p_descrs = descrs; - unset_param(MODE_AUTOMATIC); /* we are in a fallback mode */ - - if (type == SCSI_ADAPTERS) - mytype = "SCSI"; - else if (type == NETWORK_DEVICES) - mytype = "NET"; - else - return RETURN_ERROR; + qsort(dlist, string_array_length(dlist), sizeof(char *), strsortfunc); - if (disable_modules) - return RETURN_BACK; + unset_automatic(); /* we are in a fallback mode */ - snprintf(msg, sizeof(msg), "Which driver should I try to gain %s access?", mytype); - - { - char ** drivers = mar_list_contents(archive_name); - char ** descrs = malloc(sizeof(char *) * string_array_length(drivers)); - char ** p_drivers = drivers; - char ** p_descrs = descrs; - while (p_drivers && *p_drivers) { - int i; - *p_descrs = NULL; - for (i = 0 ; i < modules_descriptions_num ; i++) { - if (!strncmp(*p_drivers, modules_descriptions[i].module, strlen(modules_descriptions[i].module)) - && (*p_drivers)[strlen(modules_descriptions[i].module)] == '.') /* one contains '.o' not the other */ - *p_descrs = modules_descriptions[i].descr; - } - p_drivers++; - p_descrs++; + while (p_dlist && *p_dlist) { + struct module_descr_elem * descr; + if (!strstr(*p_dlist, kernel_module_extension())) { + p_dlist++; + continue; } - results = ask_from_list_comments(msg, drivers, descrs, &choice); + *p_modules = *p_dlist; + *p_descrs = NULL; + (*p_modules)[strlen(*p_modules)-strlen(kernel_module_extension())] = '\0'; /* remove trailing .ko.gz */ + + descr = modules_descr; + while (descr && descr->modname && strcmp(descr->modname, *p_modules)) descr++; + if (descr) + *p_descrs = descr->description; + + p_dlist++; + p_modules++; + p_descrs++; } - - if (results == RETURN_OK) { - choice[strlen(choice)-2] = '\0'; /* remove trailing .o */ - return insmod_with_options(choice, type); - } else - return results; -} - - -void update_modules(void) -{ - FILE * f; - char ** disk_contents; - char final_name[500]; - char floppy_mount_location[] = "/tmp/floppy"; - - stg1_info_message("Please insert the Update Modules floppy.");; - - my_insmod("floppy", ANY_DRIVER_TYPE, NULL); - - if (my_mount("/dev/fd0", floppy_mount_location, "ext2", 0) == -1) { - enum return_type results = ask_yes_no("I can't find a Linux ext2 floppy in first floppy drive.\n" - "Retry?"); + *p_modules = NULL; + *p_descrs = NULL; + + if (modules && *modules) { + char * mytype; + char msg[200]; + if (type == MEDIA_ADAPTERS) + mytype = "MEDIA"; + else if (type == NETWORK_DEVICES) + mytype = "NET"; + else + return RETURN_ERROR; + + snprintf(msg, sizeof(msg), "Which driver should I try to gain %s access?", mytype); + results = ask_from_list_comments(msg, modules, descrs, &choice); if (results == RETURN_OK) - return update_modules(); - return; - } - - disk_contents = list_directory(floppy_mount_location); - - if (!(f = fopen("/tmp/floppy/to_load", "rb"))) { - stg1_error_message("I can't find \"to_load\" file."); - umount(floppy_mount_location); - return update_modules(); - } - while (1) { - char module[500]; - char * options; - char ** entry = disk_contents; - - if (!fgets(module, sizeof(module), f)) break; - if (module[0] == '#' || strlen(module) == 0) - continue; - - while (module[strlen(module)-1] == '\n') - module[strlen(module)-1] = '\0'; - options = strchr(module, ' '); - if (options) { - options[0] = '\0'; - options++; - } - - log_message("updatemodules: (%s) (%s)", module, options); - while (entry && *entry) { - if (!strncmp(*entry, module, strlen(module)) && (*entry)[strlen(module)] == '.') { - sprintf(final_name, "%s/%s", floppy_mount_location, *entry); - if (insmod_call(final_name, options)) { - log_message("\t%s (floppy): failed", *entry); - stg1_error_message("Insmod %s (floppy) failed.", *entry); - } - break; - } - entry++; - } - if (!entry || !*entry) { - enum insmod_return ret = my_insmod(module, ANY_DRIVER_TYPE, options); - if (ret != INSMOD_OK) { - log_message("\t%s (marfile): failed", module); - stg1_error_message("Insmod %s (marfile) failed.", module); - } - } + return insmod_with_options(choice, type); + else + return results; + } else { + return RETURN_BACK; } - fclose(f); - umount(floppy_mount_location); } |