diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-05-07 02:55:41 +0000 |
commit | dcec8a13bf565e47942a1751a9cec21bec5648fe (patch) | |
tree | 548b69625b18cc2e88c3e68d0923be546c9ebb03 /kernel/kmod.c | |
parent | 2e0f55e79c49509b7ff70ff1a10e1e9e90a3dfd4 (diff) |
o Merge with Linux 2.1.99.
o Fix ancient bug in the ELF loader making ldd crash.
o Fix ancient bug in the keyboard code for SGI, SNI and Jazz.
Diffstat (limited to 'kernel/kmod.c')
-rw-r--r-- | kernel/kmod.c | 178 |
1 files changed, 66 insertions, 112 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 379c27695..7468e4382 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -1,6 +1,12 @@ /* kmod, the new module loader (replaces kerneld) Kirk Petersen + + Reorganized not to be a daemon by Adam Richter, with guidance + from Greg Zornetzer. + + Modified to avoid chroot and file sharing problems. + Mikael Pettersson */ #define __KERNEL_SYSCALLS__ @@ -8,140 +14,88 @@ #include <linux/sched.h> #include <linux/types.h> #include <linux/unistd.h> +#include <asm/smp_lock.h> +#include <asm/uaccess.h> /* - kmod_unload_delay and modprobe_path are set via /proc/sys. + modprobe_path is set via /proc/sys. */ -int kmod_unload_delay = 60; char modprobe_path[256] = "/sbin/modprobe"; -static char module_name[64] = ""; -static char * argv[] = { "modprobe", "-k", module_name, NULL, }; -static char * envp[] = { "HOME=/", "TERM=linux", NULL, }; - -/* - kmod_queue synchronizes the kmod thread and the rest of the system - kmod_unload_timer is what we use to unload modules - after kmod_unload_delay seconds -*/ -static struct wait_queue * kmod_queue = NULL; -static struct timer_list kmod_unload_timer; +static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL }; /* - kmod_thread is the thread that does most of the work. kmod_unload and - request_module tell it to wake up and do work. + exec_modprobe is spawned from a kernel-mode user process, + then changes its state to behave _as_if_ it was spawned + from the kernel's init process + (ppid and {e,}gid are not adjusted, but that shouldn't + be a problem since we trust modprobe) */ -int kmod_thread(void * data) -{ - int pid; +#define task_init task[smp_num_cpus] - /* - Initialize basic thread information - */ - current->session = 1; - current->pgrp = 1; - sprintf(current->comm, "kmod"); - sigfillset(¤t->blocked); - - /* - This is the main kmod_thread loop. It first sleeps, then - handles requests from request_module or kmod_unload. - */ +static inline void +use_init_file_context(void) { + lock_kernel(); - while (1) { - interruptible_sleep_on(&kmod_queue); - - /* - If request_module woke us up, we should try to - load module_name. If not, kmod_unload woke us up, - do call delete_module. - (if somehow both want us to do something, ignore the - delete_module request) - */ - if (module_name[0] == '\0') { - delete_module(NULL); - } else { - pid = fork(); - if (pid > 0) { - waitpid(pid, NULL, 0); - module_name[0] = '\0'; - wake_up(&kmod_queue); - } else - if (pid == 0) { - - /* - Call modprobe with module_name. If execve returns, - print out an error. - */ - execve(modprobe_path, argv, envp); - - printk("kmod: failed to load module %s\n", module_name); - _exit(0); - } else { - printk("error, fork failed in kmod\n"); - } - } - } + /* don't use the user's root, use init's root instead */ + exit_fs(current); /* current->fs->count--; */ + current->fs = task_init->fs; + current->fs->count++; - return 0; /* Never reached. */ + unlock_kernel(); } -/* - kmod_unload is the function that the kernel calls when - the kmod_unload_timer expires -*/ -void kmod_unload(unsigned long x) +static int exec_modprobe(void * module_name) { - /* - wake up the kmod thread, which does the work - (we can't call delete_module, as it locks the kernel and - we are in the bottom half of the kernel (right?)) - once it is awake, reset the timer - */ - wake_up(&kmod_queue); - kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ); - add_timer(&kmod_unload_timer); -} + char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL}; + int i; -int kmod_init(void) -{ - printk ("Starting kmod\n"); + use_init_file_context(); - kernel_thread(kmod_thread, NULL, 0); + /* Prevent parent user process from sending signals to child. + Otherwise, if the modprobe program does not exist, it might + be possible to get a user defined signal handler to execute + as the super user right after the execve fails if you time + the signal just right. + */ + spin_lock_irq(¤t->sigmask_lock); + flush_signals(current); + flush_signal_handlers(current); + spin_unlock_irq(¤t->sigmask_lock); - kmod_unload_timer.next = NULL; - kmod_unload_timer.prev = NULL; - kmod_unload_timer.expires = jiffies + (5 * 60 * HZ); - kmod_unload_timer.data = 0L; - kmod_unload_timer.function = kmod_unload; - add_timer(&kmod_unload_timer); + for (i = 0; i < current->files->max_fds; i++ ) { + if (current->files->fd[i]) close(i); + } + set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */ + current->uid = current->euid = current->fsuid = 0; + if (execve(modprobe_path, argv, envp) < 0) { + printk(KERN_ERR + "kmod: failed to exec %s -s -k %s, errno = %d\n", + modprobe_path, (char*) module_name, errno); + return -errno; + } return 0; } /* - request_module, the function that everyone calls when they need a - module to be loaded + request_module: the function that everyone calls when they need + a module. */ -int request_module(const char * name) +int request_module(const char * module_name) { - /* first, copy the name of the module into module_name */ - /* then wake_up() the kmod daemon */ - /* wait for the kmod daemon to finish (it will wake us up) */ - - /* - kmod_thread is sleeping, so start by copying the name of - the module into module_name. Once that is done, wake up - kmod_thread. - */ - strncpy(module_name, name, sizeof(module_name)); - module_name[sizeof(module_name)-1] = '\0'; - wake_up(&kmod_queue); - - /* - Now that we have told kmod_thread what to do, we want to - go to sleep and let it do its work. It will wake us up, - at which point we will be done (the module will be loaded). - */ - interruptible_sleep_on(&kmod_queue); + int pid; + int waitpid_result; + + pid = kernel_thread(exec_modprobe, (void*) module_name, + CLONE_FS | SIGCHLD); + if (pid < 0) { + printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid); + return pid; + } + waitpid_result = waitpid(pid, NULL, 0); + if (waitpid_result != pid) { + printk (KERN_ERR "kmod: waitpid(%d,NULL,0) failed, returning %d.\n", + pid, waitpid_result); + } return 0; } |