/* * Guillaume Cottenceau (gc@mandrakesoft.com) * * Copyright 2000 MandrakeSoft * * This software may be freely redistributed under the terms of the GNU * public license. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * (1) calculate dependencies * (2) unarchive relevant modules * (3) insmod them */ #include #include #include #include #include #include #include #include #include #include "insmod.h" #include "stage1.h" #include "log.h" #include "mar/mar-extract-only.h" #include "frontend.h" #include "mount.h" #include "modules_descr.h" #include "modules.h" static struct module_deps_elem * modules_deps = NULL; static char archive_name[] = "/modules/modules.mar"; static char additional_archive_name[] = "/tmp/tmpfs/modules.mar"; int disable_modules = 0; int allow_additional_modules_floppy = 1; extern long init_module(void *, unsigned long, const char *); static const char *moderror(int err) { 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); } } static void *grab_file(const char *filename, unsigned long *size) { unsigned int max = 16384; int ret, fd; void *buffer = malloc(max); fd = open(filename, O_RDONLY, 0); if (fd < 0) return NULL; *size = 0; while ((ret = read(fd, buffer + *size, max - *size)) > 0) { *size += ret; if (*size == max) buffer = realloc(buffer, max *= 2); } if (ret < 0) { free(buffer); buffer = NULL; } close(fd); return buffer; } static enum return_type ensure_additional_modules_available(void) { struct stat statbuf; if (stat(additional_archive_name, &statbuf)) { char floppy_mount_location[] = "/tmp/floppy"; char floppy_modules_mar[] = "/tmp/floppy/modules.mar"; int ret; if (stat("/tmp/tmpfs", &statbuf)) { if (scall(mkdir("/tmp/tmpfs", 0755), "mkdir")) return RETURN_ERROR; if (scall(mount("none", "/tmp/tmpfs", "tmpfs", MS_MGC_VAL, NULL), "mount tmpfs")) return RETURN_ERROR; } retry: stg1_info_message("Please insert the Additional Drivers floppy.");; my_insmod("floppy", ANY_DRIVER_TYPE, NULL); while (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?"); if (results != RETURN_OK) { allow_additional_modules_floppy = 0; return results; } } if (stat(floppy_modules_mar, &statbuf)) { stg1_error_message("This is not an Additional Drivers floppy, as far as I can see."); umount(floppy_mount_location); goto retry; } init_progression("Copying...", file_size(floppy_modules_mar)); ret = copy_file(floppy_modules_mar, additional_archive_name, update_progression); end_progression(); return ret; } else return RETURN_OK; } static const char * get_name_kernel_26_transition(const char * name) { struct kernel_24_26_mapping { const char * name_24; const char * name_26; }; static struct kernel_24_26_mapping mappings[] = { { "usb-ohci", "ohci-hcd" }, { "usb-uhci", "uhci-hcd" }, { "uhci", "uhci-hcd" }, // { "printer", "usblp" }, { "bcm4400", "b44" }, { "3c559", "3c359" }, { "3c90x", "3c59x" }, { "dc395x_trm", "dc395x" }, // { "audigy", "snd-emu10k1" }, }; int mappings_nb = sizeof(mappings) / sizeof(struct kernel_24_26_mapping); int i; /* pcitable contains 2.4 names. this will need to change if/when it contains 2.6 names! */ if (kernel_version() > 4) for (i=0; i= sizeof(data)) return; strcat(data, str); strcat(data, "\n"); fd = open(target, O_CREAT|O_WRONLY|O_TRUNC, 00660); if (fd == -1) { log_perror(str); return; } if (write(fd, data, strlen(data) + 1) != (ssize_t) (strlen(data) + 1)) log_perror(str); close(fd); } int module_already_present(const char * name) { FILE * f; int answ = 0; 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); } 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++; } } if (module_already_present(mod_name)) return INSMOD_OK; log_message("needs %s", mod_name); return insmod_archived_file(mod_name, options); } #ifndef DISABLE_NETWORK enum insmod_return my_insmod(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) #endif { int i; #ifndef DISABLE_NETWORK char ** net_devices = NULL; /* fucking compiler */ #endif if (module_already_present(mod_name)) return INSMOD_OK; log_message("have to insmod %s", mod_name); if (disable_modules) { log_message("\tdisabled"); return INSMOD_OK; } #ifndef DISABLE_NETWORK if (type == NETWORK_DEVICES) net_devices = get_net_devices(); #endif if (IS_TESTING) return INSMOD_OK; i = insmod_with_deps(mod_name, options); if (i == 0) { log_message("\tsucceeded %s", mod_name); #ifndef DISABLE_NETWORK if (type == NETWORK_DEVICES) { char ** new_net_devices = get_net_devices(); while (new_net_devices && *new_net_devices) { char alias[500]; char ** ptr = net_devices; while (ptr && *ptr) { if (!strcmp(*new_net_devices, *ptr)) goto already_present; ptr++; } 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++; } } #endif } else log_message("warning, insmod failed (%s %s) (%d)", mod_name, options, i); return i; } static enum return_type insmod_with_options(char * mod, enum driver_type type) { char * questions[] = { "Options", NULL }; static char ** answers = NULL; enum return_type results; char options[500] = "options "; results = ask_from_entries("Please enter the parameters to give to the kernel:", questions, &answers, 24, NULL); if (results != RETURN_OK) return results; strcat(options, mod); strcat(options, " "); strcat(options, answers[0]); // because my_insmod will eventually modify the string if (my_insmod(mod, type, answers[0]) != INSMOD_OK) { stg1_error_message("Insmod failed."); return RETURN_ERROR; } add_modules_conf(options); return RETURN_OK; } enum return_type ask_insmod(enum driver_type type) { char * mytype; char msg[200]; enum return_type results; char * choice; 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; if (disable_modules) return RETURN_BACK; snprintf(msg, sizeof(msg), "Which driver should I try to gain %s access?", mytype); { char ** modules = mar_list_contents(ensure_additional_modules_available() == RETURN_OK ? additional_archive_name : archive_name); char ** descrs = malloc(sizeof(char *) * string_array_length(modules)); char ** p_modules = modules; char ** p_descrs = descrs; while (p_modules && *p_modules) { int i; *p_descrs = NULL; for (i = 0 ; i < modules_descriptions_num ; i++) { if (!strncmp(*p_modules, modules_descriptions[i].module, strlen(modules_descriptions[i].module)) && (*p_modules)[strlen(modules_descriptions[i].module)] == '.') /* one contains '.o' not the other */ *p_descrs = modules_descriptions[i].descr; } p_modules++; p_descrs++; } results = ask_from_list_comments(msg, modules, descrs, &choice); } if (results == RETURN_OK) { if (kernel_version() <= 4) choice[strlen(choice)-2] = '\0'; /* remove trailing .o */ else choice[strlen(choice)-3] = '\0'; /* remove trailing .ko */ 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?"); 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); } } } fclose(f); umount(floppy_mount_location); }