aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/kmod.h51
-rw-r--r--kernel/kmod.c51
-rw-r--r--kernel/sys.c6
3 files changed, 73 insertions, 35 deletions
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index facb27fe7de0..f9edf63e90ea 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -23,6 +23,7 @@
23#include <linux/stddef.h> 23#include <linux/stddef.h>
24#include <linux/errno.h> 24#include <linux/errno.h>
25#include <linux/compiler.h> 25#include <linux/compiler.h>
26#include <linux/workqueue.h>
26 27
27#define KMOD_PATH_LEN 256 28#define KMOD_PATH_LEN 256
28 29
@@ -45,7 +46,27 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS;
45 46
46struct key; 47struct key;
47struct file; 48struct file;
48struct subprocess_info; 49
50enum umh_wait {
51 UMH_NO_WAIT = -1, /* don't wait at all */
52 UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
53 UMH_WAIT_PROC = 1, /* wait for the process to complete */
54};
55
56struct subprocess_info {
57 struct work_struct work;
58 struct completion *complete;
59 struct cred *cred;
60 char *path;
61 char **argv;
62 char **envp;
63 enum umh_wait wait;
64 int retval;
65 struct file *stdin;
66 int (*init)(struct subprocess_info *info);
67 void (*cleanup)(struct subprocess_info *info);
68 void *data;
69};
49 70
50/* Allocate a subprocess_info structure */ 71/* Allocate a subprocess_info structure */
51struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, 72struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
@@ -56,14 +77,10 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
56 struct key *session_keyring); 77 struct key *session_keyring);
57int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, 78int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
58 struct file **filp); 79 struct file **filp);
59void call_usermodehelper_setcleanup(struct subprocess_info *info, 80void call_usermodehelper_setfns(struct subprocess_info *info,
60 void (*cleanup)(char **argv, char **envp)); 81 int (*init)(struct subprocess_info *info),
61 82 void (*cleanup)(struct subprocess_info *info),
62enum umh_wait { 83 void *data);
63 UMH_NO_WAIT = -1, /* don't wait at all */
64 UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
65 UMH_WAIT_PROC = 1, /* wait for the process to complete */
66};
67 84
68/* Actually execute the sub-process */ 85/* Actually execute the sub-process */
69int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait); 86int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
@@ -73,18 +90,32 @@ int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
73void call_usermodehelper_freeinfo(struct subprocess_info *info); 90void call_usermodehelper_freeinfo(struct subprocess_info *info);
74 91
75static inline int 92static inline int
76call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait) 93call_usermodehelper_fns(char *path, char **argv, char **envp,
94 enum umh_wait wait,
95 int (*init)(struct subprocess_info *info),
96 void (*cleanup)(struct subprocess_info *), void *data)
77{ 97{
78 struct subprocess_info *info; 98 struct subprocess_info *info;
79 gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; 99 gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
80 100
81 info = call_usermodehelper_setup(path, argv, envp, gfp_mask); 101 info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
102
82 if (info == NULL) 103 if (info == NULL)
83 return -ENOMEM; 104 return -ENOMEM;
105
106 call_usermodehelper_setfns(info, init, cleanup, data);
107
84 return call_usermodehelper_exec(info, wait); 108 return call_usermodehelper_exec(info, wait);
85} 109}
86 110
87static inline int 111static inline int
112call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
113{
114 return call_usermodehelper_fns(path, argv, envp, wait,
115 NULL, NULL, NULL);
116}
117
118static inline int
88call_usermodehelper_keys(char *path, char **argv, char **envp, 119call_usermodehelper_keys(char *path, char **argv, char **envp,
89 struct key *session_keyring, enum umh_wait wait) 120 struct key *session_keyring, enum umh_wait wait)
90{ 121{
diff --git a/kernel/kmod.c b/kernel/kmod.c
index bf0e231d9702..531ef62cf536 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -116,27 +116,16 @@ int __request_module(bool wait, const char *fmt, ...)
116 116
117 trace_module_request(module_name, wait, _RET_IP_); 117 trace_module_request(module_name, wait, _RET_IP_);
118 118
119 ret = call_usermodehelper(modprobe_path, argv, envp, 119 ret = call_usermodehelper_fns(modprobe_path, argv, envp,
120 wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC); 120 wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC,
121 NULL, NULL, NULL);
122
121 atomic_dec(&kmod_concurrent); 123 atomic_dec(&kmod_concurrent);
122 return ret; 124 return ret;
123} 125}
124EXPORT_SYMBOL(__request_module); 126EXPORT_SYMBOL(__request_module);
125#endif /* CONFIG_MODULES */ 127#endif /* CONFIG_MODULES */
126 128
127struct subprocess_info {
128 struct work_struct work;
129 struct completion *complete;
130 struct cred *cred;
131 char *path;
132 char **argv;
133 char **envp;
134 enum umh_wait wait;
135 int retval;
136 struct file *stdin;
137 void (*cleanup)(char **argv, char **envp);
138};
139
140/* 129/*
141 * This is the task which runs the usermode application 130 * This is the task which runs the usermode application
142 */ 131 */
@@ -184,9 +173,16 @@ static int ____call_usermodehelper(void *data)
184 */ 173 */
185 set_user_nice(current, 0); 174 set_user_nice(current, 0);
186 175
176 if (sub_info->init) {
177 retval = sub_info->init(sub_info);
178 if (retval)
179 goto fail;
180 }
181
187 retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp); 182 retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
188 183
189 /* Exec failed? */ 184 /* Exec failed? */
185fail:
190 sub_info->retval = retval; 186 sub_info->retval = retval;
191 do_exit(0); 187 do_exit(0);
192} 188}
@@ -194,7 +190,7 @@ static int ____call_usermodehelper(void *data)
194void call_usermodehelper_freeinfo(struct subprocess_info *info) 190void call_usermodehelper_freeinfo(struct subprocess_info *info)
195{ 191{
196 if (info->cleanup) 192 if (info->cleanup)
197 (*info->cleanup)(info->argv, info->envp); 193 (*info->cleanup)(info);
198 if (info->cred) 194 if (info->cred)
199 put_cred(info->cred); 195 put_cred(info->cred);
200 kfree(info); 196 kfree(info);
@@ -406,21 +402,31 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
406EXPORT_SYMBOL(call_usermodehelper_setkeys); 402EXPORT_SYMBOL(call_usermodehelper_setkeys);
407 403
408/** 404/**
409 * call_usermodehelper_setcleanup - set a cleanup function 405 * call_usermodehelper_setfns - set a cleanup/init function
410 * @info: a subprocess_info returned by call_usermodehelper_setup 406 * @info: a subprocess_info returned by call_usermodehelper_setup
411 * @cleanup: a cleanup function 407 * @cleanup: a cleanup function
408 * @init: an init function
409 * @data: arbitrary context sensitive data
410 *
411 * The init function is used to customize the helper process prior to
412 * exec. A non-zero return code causes the process to error out, exit,
413 * and return the failure to the calling process
412 * 414 *
413 * The cleanup function is just befor ethe subprocess_info is about to 415 * The cleanup function is just before ethe subprocess_info is about to
414 * be freed. This can be used for freeing the argv and envp. The 416 * be freed. This can be used for freeing the argv and envp. The
415 * Function must be runnable in either a process context or the 417 * Function must be runnable in either a process context or the
416 * context in which call_usermodehelper_exec is called. 418 * context in which call_usermodehelper_exec is called.
417 */ 419 */
418void call_usermodehelper_setcleanup(struct subprocess_info *info, 420void call_usermodehelper_setfns(struct subprocess_info *info,
419 void (*cleanup)(char **argv, char **envp)) 421 int (*init)(struct subprocess_info *info),
422 void (*cleanup)(struct subprocess_info *info),
423 void *data)
420{ 424{
421 info->cleanup = cleanup; 425 info->cleanup = cleanup;
426 info->init = init;
427 info->data = data;
422} 428}
423EXPORT_SYMBOL(call_usermodehelper_setcleanup); 429EXPORT_SYMBOL(call_usermodehelper_setfns);
424 430
425/** 431/**
426 * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin 432 * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
@@ -515,7 +521,8 @@ int call_usermodehelper_pipe(char *path, char **argv, char **envp,
515 struct subprocess_info *sub_info; 521 struct subprocess_info *sub_info;
516 int ret; 522 int ret;
517 523
518 sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL); 524 sub_info = call_usermodehelper_setup(path, argv, envp,
525 GFP_KERNEL);
519 if (sub_info == NULL) 526 if (sub_info == NULL)
520 return -ENOMEM; 527 return -ENOMEM;
521 528
diff --git a/kernel/sys.c b/kernel/sys.c
index 0d36d889c74d..e83ddbbaf89d 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1632,9 +1632,9 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
1632 1632
1633char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; 1633char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
1634 1634
1635static void argv_cleanup(char **argv, char **envp) 1635static void argv_cleanup(struct subprocess_info *info)
1636{ 1636{
1637 argv_free(argv); 1637 argv_free(info->argv);
1638} 1638}
1639 1639
1640/** 1640/**
@@ -1668,7 +1668,7 @@ int orderly_poweroff(bool force)
1668 goto out; 1668 goto out;
1669 } 1669 }
1670 1670
1671 call_usermodehelper_setcleanup(info, argv_cleanup); 1671 call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
1672 1672
1673 ret = call_usermodehelper_exec(info, UMH_NO_WAIT); 1673 ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
1674 1674