465 lines
10 KiB
C
465 lines
10 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mount.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#ifdef CONFIG_USE_ZLIB
|
|
#include <zlib.h>
|
|
#endif
|
|
|
|
#include "stage1.h"
|
|
#include "log.h"
|
|
#include "frontend.h"
|
|
#include "mount.h"
|
|
#include "modules_descr.h"
|
|
#include "modules.h"
|
|
|
|
extern int mar_extract_inplace(char *, const char *, void **, int *);
|
|
extern char ** mar_list_contents(char *);
|
|
|
|
static struct module_deps_elem * modules_deps = NULL;
|
|
|
|
static char * archive_name = "/modules/modules.mar";
|
|
int disable_modules = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
int insmod_call(char *pathname, char *params)
|
|
{
|
|
void *file;
|
|
unsigned long len;
|
|
int rc = -1;
|
|
|
|
file = grab_file(pathname, &len);
|
|
if (!file) {
|
|
log_message("insmod: can't read '%s': %s", pathname, strerror(errno));
|
|
return rc;
|
|
}
|
|
|
|
rc = init_module(file, len, params == NULL ? "" : params);
|
|
free(file);
|
|
if (rc != 0) {
|
|
log_message("init_module: '%s': %s", pathname, moderror(errno));
|
|
return rc;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static enum insmod_return insmod_archived_file(const char * mod_name, char * options)
|
|
{
|
|
void *file;
|
|
char module_name[50];
|
|
int len, i, rc;
|
|
|
|
strncpy(module_name, mod_name, sizeof(module_name));
|
|
strcat(module_name, ".ko");
|
|
i = mar_extract_inplace(archive_name, module_name, &file, &len);
|
|
if (i == 1) {
|
|
log_message("file-not-found-in-archive %s", module_name);
|
|
return INSMOD_FAILED_FILE_NOT_FOUND;
|
|
}
|
|
if (i != 0)
|
|
return INSMOD_FAILED;
|
|
|
|
rc = init_module(file, len, options == NULL ? "" : options);
|
|
free(file);
|
|
if (rc != 0) {
|
|
log_message("init_module: '%s': %s", module_name, moderror(errno));
|
|
return INSMOD_FAILED;
|
|
}
|
|
return INSMOD_OK;
|
|
}
|
|
|
|
static int load_modules_dependencies(void)
|
|
{
|
|
char * deps_file = "/modules/modules.dep";
|
|
char * buf, * ptr, * start, * end;
|
|
struct stat s;
|
|
int fd, line, i;
|
|
|
|
log_message("loading modules dependencies");
|
|
|
|
if (IS_TESTING)
|
|
return 0;
|
|
|
|
fd = open(deps_file, O_RDONLY);
|
|
if (fd == -1) {
|
|
log_perror(deps_file);
|
|
return -1;
|
|
}
|
|
|
|
fstat(fd, &s);
|
|
buf = alloca(s.st_size + 1);
|
|
if (read(fd, buf, s.st_size) != s.st_size) {
|
|
log_perror(deps_file);
|
|
return -1;
|
|
}
|
|
buf[s.st_size] = '\0';
|
|
close(fd);
|
|
|
|
ptr = buf;
|
|
line = 0;
|
|
while (ptr) {
|
|
line++;
|
|
ptr = strchr(ptr + 1, '\n');
|
|
}
|
|
|
|
modules_deps = malloc(sizeof(*modules_deps) * (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, ':');
|
|
if (!ptr) {
|
|
start = end + 1;
|
|
continue;
|
|
}
|
|
*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);
|
|
|
|
line++;
|
|
start = end + 1;
|
|
}
|
|
modules_deps[line].name = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void init_modules_insmoding(void)
|
|
{
|
|
if (load_modules_dependencies()) {
|
|
log_message("warning, error initing modules stuff, modules loading disabled");
|
|
disable_modules = 1;
|
|
}
|
|
}
|
|
|
|
static int module_already_present(const char * name)
|
|
{
|
|
FILE * f;
|
|
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)] == ' ')
|
|
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);
|
|
}
|
|
|
|
|
|
enum insmod_return my_insmod(const char * mod_name, enum driver_type type, char * options)
|
|
{
|
|
char alias[500];
|
|
int i;
|
|
#ifndef DISABLE_NETWORK
|
|
char ** net_devices = NULL;
|
|
#endif
|
|
|
|
log_message("have to insmod %s", mod_name);
|
|
|
|
if (disable_modules) {
|
|
log_message("\tdisabled");
|
|
return INSMOD_OK;
|
|
}
|
|
|
|
#ifndef DISABLE_NETWORK
|
|
if (type == NETWORK_DEVICES || type == BRIDGE_OTHER)
|
|
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_MEDIAS
|
|
if (type == MEDIA_ADAPTERS) {
|
|
log_message("MEDIA: %s", alias);
|
|
}
|
|
#endif
|
|
#ifndef DISABLE_NETWORK
|
|
if (type == NETWORK_DEVICES || type == BRIDGE_OTHER) {
|
|
char ** new_net_devices = get_net_devices();
|
|
while (new_net_devices && *new_net_devices) {
|
|
char ** ptr = net_devices;
|
|
while (ptr && *ptr) {
|
|
if (!strcmp(*new_net_devices, *ptr))
|
|
goto already_present;
|
|
ptr++;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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 == MEDIA_ADAPTERS)
|
|
mytype = "IDE or 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 ** 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++;
|
|
}
|
|
results = ask_from_list_comments(msg, drivers, descrs, &choice);
|
|
}
|
|
|
|
if (results == RETURN_OK) {
|
|
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);
|
|
}
|