diff options
-rw-r--r-- | include/linux/kmod.h | 51 | ||||
-rw-r--r-- | kernel/kmod.c | 51 | ||||
-rw-r--r-- | kernel/sys.c | 6 |
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 | ||
46 | struct key; | 47 | struct key; |
47 | struct file; | 48 | struct file; |
48 | struct subprocess_info; | 49 | |
50 | enum 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 | |||
56 | struct 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 */ |
51 | struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, | 72 | struct 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); |
57 | int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, | 78 | int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, |
58 | struct file **filp); | 79 | struct file **filp); |
59 | void call_usermodehelper_setcleanup(struct subprocess_info *info, | 80 | void 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), | |
62 | enum 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 */ |
69 | int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait); | 86 | int 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); | |||
73 | void call_usermodehelper_freeinfo(struct subprocess_info *info); | 90 | void call_usermodehelper_freeinfo(struct subprocess_info *info); |
74 | 91 | ||
75 | static inline int | 92 | static inline int |
76 | call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait) | 93 | call_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 | ||
87 | static inline int | 111 | static inline int |
112 | call_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 | |||
118 | static inline int | ||
88 | call_usermodehelper_keys(char *path, char **argv, char **envp, | 119 | call_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 | } |
124 | EXPORT_SYMBOL(__request_module); | 126 | EXPORT_SYMBOL(__request_module); |
125 | #endif /* CONFIG_MODULES */ | 127 | #endif /* CONFIG_MODULES */ |
126 | 128 | ||
127 | struct 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? */ |
185 | fail: | ||
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) | |||
194 | void call_usermodehelper_freeinfo(struct subprocess_info *info) | 190 | void 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, | |||
406 | EXPORT_SYMBOL(call_usermodehelper_setkeys); | 402 | EXPORT_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 | */ |
418 | void call_usermodehelper_setcleanup(struct subprocess_info *info, | 420 | void 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 | } |
423 | EXPORT_SYMBOL(call_usermodehelper_setcleanup); | 429 | EXPORT_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 | ||
1633 | char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; | 1633 | char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; |
1634 | 1634 | ||
1635 | static void argv_cleanup(char **argv, char **envp) | 1635 | static 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 | ||