2.1.97 PATCH for daemonless kmod, latest version

Adam J. Richter (adam@yggdrasil.com)
Thu, 23 Apr 1998 21:57:06 -0700


Here is the latest version of the daemonless kmod, as a patch
against pristine 2.1.97. It includes the changes to the signal
handling code from Steve Hirsch to make it work on an Alpha. I
left in the calls to spin_{un,}lock_irq(&current->sigmask_lock).
At first I thought that these lines could saftely be deleted, per
Steve's patch because we know this process does not share its
signals with any other process. However, it occurred to me that
another process could send it a signal while this code was running,
and that could result in some synchronization problems, so I have left
the lock acquisition code in. This also incluides the code to
use the root filesystem of init instead of the calling process.

Anyhow, please let me know if there are any problems.

Linus, I hope you will incorporate this into the main kernel
tree. Reliable loadable modules make the system a lot easier to
configure.

Adam J. Richter __ ______________ 4880 Stevens Creek Blvd, Suite 205
adam@yggdrasil.com \ / San Jose, California 95129-1034
+1 408 261-6630 | g g d r a s i l United States of America
fax +1 408 261-6631 "Free Software For The Rest Of Us."
--------------------------CUT HERE----------------------------------

--- /tmp/linux-2.1.97/Documentation/kmod.txt Tue Mar 10 14:43:13 1998
+++ linux/Documentation/kmod.txt Thu Apr 23 21:40:30 1998
@@ -4,19 +4,19 @@
Kmod is a simple replacement for kerneld. It consists of a
request_module() replacement and a kernel thread called kmod. When the
kernel requests a module, the kmod wakes up and execve()s modprobe,
-passing it the name that was requested. After a configurable period of
-time, kmod will have delete_module() remove any unused modules.
+passing it the name that was requested.

-Kmod is configurable through two entries in /proc/sys/kernel. You can
-set the path of modprobe (where the kernel looks for it) by doing:
+If you have the /proc filesystem mounted, you can set the path of
+modprobe (where the kernel looks for it) by doing:

echo "/sbin/modprobe" > /proc/sys/kernel/modprobe

-To tell kmod when to unload unused modules, do something like:
+To periodically unload unused modules, put something like the following
+in root's crontab entry:

- echo "120" > /proc/sys/kernel/kmod_unload_delay
+ 0-59/5 * * * * /sbin/rmmod -a

-Kmod only loads and unloads modules. Kerneld could do more (although
+Kmod only loads modules. Kerneld could do more (although
nothing in the standard kernel used its other features). If you
require features such as request_route, we suggest that you take
a similar approach. A simple request_route function could be called,
--- /tmp/linux-2.1.97/Documentation/sysctl/kernel.txt Fri Apr 17 22:06:22 1998
+++ linux/Documentation/sysctl/kernel.txt Thu Apr 23 21:40:34 1998
@@ -25,7 +25,6 @@
- inode-max
- inode-nr
- inode-state
-- kmod_unload_delay ==> Documentation/kmod.txt
- modprobe ==> Documentation/kmod.txt
- osrelease
- ostype
--- /tmp/linux-2.1.97/include/linux/sysctl.h Sat Apr 11 17:18:15 1998
+++ linux/include/linux/sysctl.h Thu Apr 23 21:43:30 1998
@@ -72,7 +72,6 @@
KERN_STATINODE,
KERN_DENTRY, /* dentry statistics */
KERN_MODPROBE,
- KERN_KMOD_UNLOAD_DELAY
};


--- /tmp/linux-2.1.97/init/main.c Mon Apr 13 18:11:17 1998
+++ linux/init/main.c Thu Apr 23 21:43:35 1998
@@ -1162,13 +1162,6 @@
smp_begin();
#endif

-#ifdef CONFIG_KMOD
- {
- extern int kmod_init(void);
- kmod_init();
- }
-#endif
-
#ifdef CONFIG_UMSDOS_FS
{
/*
--- /tmp/linux-2.1.97/kernel/kmod.c Mon Apr 13 18:11:38 1998
+++ linux/kernel/kmod.c Thu Apr 23 21:46:31 1998
@@ -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,147 +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_path, "-s", "-k", module_name, NULL };
static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", 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;
+ 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)
+*/
+#define task_init task[smp_num_cpus]
+
+static inline void
+use_init_file_context(void) {
+ lock_kernel();
+
+ /* 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++;
+
+ /* don't use the user's files, use init's files instead */
+ exit_files(current); /* current->files->count--; */
+ current->files = task_init->files;
+ current->files->count++;

-/*
- It is not easy to implement a full fork in kernel-space on some
- systems (Alpha), and it is not necessary for us here. This is
- a new thread that does the exec.
-*/
-static int kmod_exec_modprobe(void * data)
-{
- sigemptyset(&current->blocked);
- execve(modprobe_path, argv, envp);
- printk(KERN_ERR "kmod: failed to load module %s\n", module_name);
- return 0;
+ unlock_kernel();
}

-/*
- kmod_thread is the thread that does most of the work. kmod_unload and
- request_module tell it to wake up and do work.
-*/
-static int kmod_thread(void * data)
+static int exec_modprobe(void * module_name)
{
- int pid;
-
- /*
- Initialize basic thread information
- */
- current->session = 1;
- current->pgrp = 1;
- sprintf(current->comm, "kmod");
- sigfillset(&current->blocked);
-
- /*
- This is the main kmod_thread loop. It first sleeps, then
- handles requests from request_module or kmod_unload.
- */
-
- 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 = kernel_thread(kmod_exec_modprobe, NULL, SIGCHLD);
- if (pid > 0) {
- waitpid(pid, NULL, 0);
- module_name[0] = '\0';
- wake_up(&kmod_queue);
- } else {
- printk(KERN_ERR "kmod: fork failed, errno %d\n", -pid);
- }
- }
- }
+ char *argv[] = { modprobe_path, "-s", "-k", (char*)module_name, NULL};

- return 0; /* Never reached. */
-}
+ use_init_file_context();

-/*
- kmod_unload is the function that the kernel calls when
- the kmod_unload_timer expires
-*/
-void kmod_unload(unsigned long x)
-{
- /*
- 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
+ /* 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.
*/
- wake_up(&kmod_queue);
- kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ);
- add_timer(&kmod_unload_timer);
-}
-
-int kmod_init(void)
-{
- printk("Starting kmod\n");
-
- /*
- * CLONE_FS means that our "cwd" will follow that of init.
- * CLONE_FILES just saves some space (we don't need any
- * new file descriptors). Ditto for CLONE_SIGHAND.
- */
- kernel_thread(kmod_thread, NULL, CLONE_FILES | CLONE_FS | CLONE_SIGHAND);
-
- 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);
-
+ spin_lock_irq(&current->sigmask_lock);
+ flush_signals(current);
+ flush_signal_handlers(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ set_fs(KERNEL_DS); /* Allow execve args to be in kernel space. */
+ current->uid = current->euid = 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 | CLONE_FILES | 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;
}
--- /tmp/linux-2.1.97/kernel/sysctl.c Wed Apr 1 16:26:35 1998
+++ linux/kernel/sysctl.c Thu Apr 23 21:43:36 1998
@@ -43,7 +43,6 @@
extern int sysctl_overcommit_memory;
#ifdef CONFIG_KMOD
extern char modprobe_path[];
-extern int kmod_unload_delay;
#endif
#ifdef CONFIG_CHR_DEV_SG
extern int sg_big_buff;
@@ -180,8 +179,6 @@
#ifdef CONFIG_KMOD
{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
0644, NULL, &proc_dostring, &sysctl_string },
- {KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay,
- sizeof(int), 0644, NULL, &proc_dointvec},
#endif
#ifdef CONFIG_CHR_DEV_SG
{KERN_NRFILE, "sg-big-buff", &sg_big_buff, sizeof (int),

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu