Linux 2.1.98 daemonless kmod patch

Adam J. Richter (adam@yggdrasil.com)
Fri, 24 Apr 1998 03:46:11 -0700


Since 2.1.98 is out and includes a tiny change to its kmod.c,
here is the patch for the daemonless kmod with everybody's tweaks
against pristine 2.1.98. There is are no changes here. I am just
posting this diff from 2.1.98 for everyone's convenience.

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.98/Documentation/kmod.txt Tue Mar 10 14:43:13 1998
+++ linux/Documentation/kmod.txt Fri Apr 24 01:16:07 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.98/Documentation/sysctl/kernel.txt Fri Apr 17 22:06:22 1998
+++ linux/Documentation/sysctl/kernel.txt Fri Apr 24 01:16:11 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.98/include/linux/sysctl.h Sat Apr 11 17:18:15 1998
+++ linux/include/linux/sysctl.h Fri Apr 24 01:19:08 1998
@@ -72,7 +72,6 @@
KERN_STATINODE,
KERN_DENTRY, /* dentry statistics */
KERN_MODPROBE,
- KERN_KMOD_UNLOAD_DELAY
};


--- /tmp/linux-2.1.98/init/main.c Mon Apr 13 18:11:17 1998
+++ linux/init/main.c Fri Apr 24 01:19:12 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.98/kernel/kmod.c Sat Apr 18 19:41:17 1998
+++ linux/kernel/kmod.c Fri Apr 24 01:19:13 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.98/kernel/sysctl.c Wed Apr 1 16:26:35 1998
+++ linux/kernel/sysctl.c Fri Apr 24 01:19:13 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