Skip to contentSkip to conversation

call_usermodehelper]

PATCH New Andrew Morton 1 participant 1 message Dec 17, 2000

Subsystems (7)
Networking Network drivers USB PCI +3 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 call_usermodehelper]

I missed a certain mailing list on this one...

-------- Original Message --------
Subject: [patch] call_usermodehelper
Date: Sun, 17 Dec 2000 23:01:14 +1100
From: Andrew Morton <[email protected]>
To: Linus Torvalds <[email protected]>
CC: David Brownell <[email protected]>,Thomas Sailer <[email protected]>,Keith Owens <[email protected]>,"[email protected]" <[email protected]>

Final installment...

This function has three modes:

* Synchronous. The caller is blocked until error or termination of the subprocess. Needed by baycomm_epp.c and, in the future, by request_module().

* Asynchronous with completion. The completion is called when an error occurs or when the subprocess exits. To be used by USB.

* Asynchronous with no completion (the "fingers crossed" mode). Used by netdevices.

I've been very careful to ensure that the caller always knows whether or not the subprocess ran, and whether or not it succeeded (exit code). See the inline kernel-docs for details.

- baycom_epp.c has been changed to use call_usermodehelper() (Thomas has tested this - thanks!) and exec_usermodehelper() has been removed from the kernel API. It still exists, statically, for request_module(). I'll kill it completely post-2.4

- In the three places where call_usermodehelper() is called, the initialisation of the PATH and HOME environment variables has been removed. It is the responsibility of the usermode process to initialise these. This change was not made in baycom_epp.c - that might break an existing application.

If someone wants to set up a system which has world-writable /usr/sbin, the kernel shouldn't prevent this by offering users subtle rootsploits. I think.

Keith, I think we should do this to modprobe post-2.4 as well:
it won't have a $PATH or $HOME when it is launched.

- Asynchronous usage of call_usermodehelper() from interrupt context is permitted.

- Asynchronous mode is now fully async wrt vfork(), which fixes the lingering concerns over vfork() doing something which requires hotplugging and causing keventd deadlock.

- Synchronous use of call_usermodehelper() from within keventd deadlocks keventd. Not recommended. Use a completion or a standalone thread.

- When there is a completion outstanding we manage the caller's module refcount for them. But the caller must still do things like not shut the hardware down or free resources if a completion is outstanding. If call_usermodehelper() returned non-negative, the completion _will_ be called. Promise.

- Created lib/misc.c as a placeholder for reusable boilerplate.

- ObBloatStatement - grows the image by 1.1k. Sorry.

TODO:

- Infinite recursion protection in synchronous mode. I think this can wait until request_module() is using call_usermodehelper().

include/linux/string.h +4 -1
@@ -13,7 +13,10 @@
 extern char * strtok(char *,const char *);
 extern char * strsep(char **,const char *);
 extern __kernel_size_t strspn(const char *,const char *);
-
+extern void *kmallocz(size_t size, int gfp_flags);
+extern char *kstrdup(const char *orig, int gfp_flags);
+extern char **kstrdup_vec(char **orig, int gfp_flags);
+extern void kstrfree_vec(char **vec);
 
 /*
  * Include machine specific inline routines
include/linux/kmod.h +19 -2
@@ -22,14 +22,31 @@
 #include <linux/config.h>
 #include <linux/errno.h>
 
+struct module;
 #ifdef CONFIG_KMOD
 extern int request_module(const char * name);
 #else
 static inline int request_module(const char * name) { return -ENOSYS; }
 #endif
 
-extern int exec_usermodehelper(char *program_path, char *argv[], char *envp[]);
-extern int call_usermodehelper(char *path, char *argv[], char *envp[]);
+struct umh_control {
+	int synchronous;					/* Flag: make call_usermodehelper() block */
+	void (*completion)(void *completion_arg, int exit_code);/* Called when subprocess exits if non-NULL */
+	void *completion_arg;					/* Private to caller */
+	struct module *owner;
+};
+
+#define INIT_UMH_CONTROL(_synchronous, _completion, _completion_arg) {	\
+		synchronous:	(_synchronous),				\
+		completion:	(_completion),				\
+		completion_arg:	(_completion_arg),			\
+		owner:		THIS_MODULE,				\
+	}
+
+#define DEFINE_UMH_CONTROL(name, synchronous, completion, completion_arg)	\
+	struct umh_control name = INIT_UMH_CONTROL(synchronous, completion, completion_arg)
+
+extern int call_usermodehelper(char *path, char *argv[], char *envp[], struct umh_control *umh_c);
 
 #ifdef CONFIG_HOTPLUG
 extern char hotplug_path [];
lib/Makefile +1 -1
@@ -10,7 +10,7 @@
 
 export-objs := cmdline.o
 
-obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o
+obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o misc.o
 
 ifneq ($(CONFIG_HAVE_DEC_LOCK),y) 
   obj-y += dec_and_lock.o
lib/misc.c +94 -0

94 lines not on this page · raw message

kernel/kmod.c +164 -53

277 lines not on this page · raw message

drivers/pci/pci.c +1 -5
@@ -378,10 +378,6 @@
 	argv[i] = 0;
 
 	i = 0;
-	/* minimal command environment */
-	envp[i++] = "HOME=/";
-	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-	
 	/* other stuff we want to pass to /sbin/hotplug */
 	envp[i++] = class_id;
 	envp[i++] = id;
@@ -393,7 +389,7 @@
 		envp[i++] = "ACTION=remove";
 	envp[i] = 0;
 
-	call_usermodehelper (argv [0], argv, envp);
+	call_usermodehelper (argv [0], argv, envp, 0);
 }
 
 void
drivers/usb/usb.c +1 -5
@@ -716,10 +716,6 @@
 	argv [1] = "usb";
 	argv [2] = 0;
 
-	/* minimal command environment */
-	envp [i++] = "HOME=/";
-	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-
 #ifdef	DEBUG
 	/* hint that policy agent should enter no-stdout debug mode */
 	envp [i++] = "DEBUG=kernel";
@@ -781,7 +777,7 @@
 	/* NOTE: user mode daemons can call the agents too */
 
 	dbg ("kusbd: %s %s %d", argv [0], verb, dev->devnum);
-	value = call_usermodehelper (argv [0], argv, envp);
+	value = call_usermodehelper (argv [0], argv, envp, 0);
 	kfree (buf);
 	kfree (envp);
 	if (value != 0)
drivers/net/hamradio/baycom_epp.c +3 -31
@@ -375,15 +375,13 @@
 
 static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/usr/bin:/bin", NULL };
 
-static int errno;
-
-static int exec_eppfpga(void *b)
+static int eppconfig(struct baycom_state *bc)
 {
-	struct baycom_state *bc = (struct baycom_state *)b;
 	char modearg[256];
 	char portarg[16];
         char *argv[] = { eppconfig_path, "-s", "-p", portarg, "-m", modearg, NULL};
         int i;
+	DEFINE_UMH_CONTROL(umh_c, 1, 0, 0);
 
 	/* set up arguments */
 	sprintf(modearg, "%sclk,%smodem,fclk=%d,bps=%d,divider=%d%s,extstat",
@@ -394,39 +392,13 @@
 	sprintf(portarg, "%ld", bc->pdev->port->base);
 	printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg);
 
-	i = exec_usermodehelper(eppconfig_path, argv, envp);
+	i = call_usermodehelper(eppconfig_path, argv, envp, &umh_c);
 	if (i < 0) {
                 printk(KERN_ERR "%s: failed to exec %s -s -p %s -m %s, errno = %d\n",
                        bc_drvname, eppconfig_path, portarg, modearg, i);
                 return i;
         }
         return 0;
-}
-
-
-/* eppconfig: called during ifconfig up to configure the modem */
-
-static int eppconfig(struct baycom_state *bc)
-{
-        int i, pid, r;
-	mm_segment_t fs;
-
-        pid = kernel_thread(exec_eppfpga, bc, CLONE_FS);
-        if (pid < 0) {
-                printk(KERN_ERR "%s: fork failed, errno %d\n", bc_drvname, -pid);
-                return pid;
-        }
-	fs = get_fs();
-        set_fs(KERNEL_DS);      /* Allow i to be in kernel space. */
-	r = waitpid(pid, &i, __WCLONE);
-	set_fs(fs);
-        if (r != pid) {
-                printk(KERN_ERR "%s: waitpid(%d) failed, returning %d\n",
-		       bc_drvname, pid, r);
-		return -1;
-        }
-	printk(KERN_DEBUG "%s: eppfpga returned %d\n", bc_drvname, i);
-	return i;
 }
 
 /* ---------------------------------------------------------------------- */
net/core/dev.c +1 -4
@@ -2746,13 +2746,10 @@
         argv[i] = 0;
 
 	i = 0;
-	/* minimal command environment */
-	envp [i++] = "HOME=/";
-	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
 	envp [i++] = ifname;
 	envp [i++] = action_str;
 	envp [i] = 0;
 	
-	return call_usermodehelper(argv [0], argv, envp);
+	return call_usermodehelper(argv [0], argv, envp, 0);
 }
 #endif

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
Please read the FAQ at http://www.tux.org/lkml/