diff options
author | Jeremy Fitzhardinge <jeremy@xensource.com> | 2007-07-17 21:37:02 -0400 |
---|---|---|
committer | Jeremy Fitzhardinge <jeremy@goop.org> | 2007-07-18 11:47:40 -0400 |
commit | 0ab4dc92278a0f3816e486d6350c6652a72e06c8 (patch) | |
tree | 84bc321c94ca86a3b5eafa308c8dba9af85a725c | |
parent | d84d1cc7647c7e4f77d517e2d87b4a106a0420d9 (diff) |
usermodehelper: split setup from execution
Rather than having hundreds of variations of call_usermodehelper for
various pieces of usermode state which could be set up, split the
info allocation and initialization from the actual process execution.
This means the general pattern becomes:
info = call_usermodehelper_setup(path, argv, envp); /* basic state */
call_usermodehelper_<SET EXTRA STATE>(info, stuff...); /* extra state */
call_usermodehelper_exec(info, wait); /* run process and free info */
This patch introduces wrappers for all the existing calling styles for
call_usermodehelper_*, but folds their implementations into one.
Signed-off-by: Jeremy Fitzhardinge <jeremy@xensource.com>
Cc: Andi Kleen <ak@suse.de>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: David Howells <dhowells@redhat.com>
Cc: Bj?rn Steinbrink <B.Steinbrink@gmx.de>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
-rw-r--r-- | include/linux/kmod.h | 44 | ||||
-rw-r--r-- | kernel/kmod.c | 191 |
2 files changed, 176 insertions, 59 deletions
diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 10f505c8431d..c4cbe59d9c67 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h | |||
@@ -36,13 +36,51 @@ static inline int request_module(const char * name, ...) { return -ENOSYS; } | |||
36 | #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) | 36 | #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) |
37 | 37 | ||
38 | struct key; | 38 | struct key; |
39 | extern int call_usermodehelper_keys(char *path, char *argv[], char *envp[], | 39 | struct file; |
40 | struct key *session_keyring, int wait); | 40 | struct subprocess_info; |
41 | |||
42 | /* Allocate a subprocess_info structure */ | ||
43 | struct subprocess_info *call_usermodehelper_setup(char *path, | ||
44 | char **argv, char **envp); | ||
45 | |||
46 | /* Set various pieces of state into the subprocess_info structure */ | ||
47 | void call_usermodehelper_setkeys(struct subprocess_info *info, | ||
48 | struct key *session_keyring); | ||
49 | int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, | ||
50 | struct file **filp); | ||
51 | void call_usermodehelper_setcleanup(struct subprocess_info *info, | ||
52 | void (*cleanup)(char **argv, char **envp)); | ||
53 | |||
54 | /* Actually execute the sub-process */ | ||
55 | int call_usermodehelper_exec(struct subprocess_info *info, int wait); | ||
56 | |||
57 | /* Free the subprocess_info. This is only needed if you're not going | ||
58 | to call call_usermodehelper_exec */ | ||
59 | void call_usermodehelper_freeinfo(struct subprocess_info *info); | ||
41 | 60 | ||
42 | static inline int | 61 | static inline int |
43 | call_usermodehelper(char *path, char **argv, char **envp, int wait) | 62 | call_usermodehelper(char *path, char **argv, char **envp, int wait) |
44 | { | 63 | { |
45 | return call_usermodehelper_keys(path, argv, envp, NULL, wait); | 64 | struct subprocess_info *info; |
65 | |||
66 | info = call_usermodehelper_setup(path, argv, envp); | ||
67 | if (info == NULL) | ||
68 | return -ENOMEM; | ||
69 | return call_usermodehelper_exec(info, wait); | ||
70 | } | ||
71 | |||
72 | static inline int | ||
73 | call_usermodehelper_keys(char *path, char **argv, char **envp, | ||
74 | struct key *session_keyring, int wait) | ||
75 | { | ||
76 | struct subprocess_info *info; | ||
77 | |||
78 | info = call_usermodehelper_setup(path, argv, envp); | ||
79 | if (info == NULL) | ||
80 | return -ENOMEM; | ||
81 | |||
82 | call_usermodehelper_setkeys(info, session_keyring); | ||
83 | return call_usermodehelper_exec(info, wait); | ||
46 | } | 84 | } |
47 | 85 | ||
48 | extern void usermodehelper_init(void); | 86 | extern void usermodehelper_init(void); |
diff --git a/kernel/kmod.c b/kernel/kmod.c index 4d32eb077179..d2dce71115d8 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c | |||
@@ -122,6 +122,7 @@ struct subprocess_info { | |||
122 | int wait; | 122 | int wait; |
123 | int retval; | 123 | int retval; |
124 | struct file *stdin; | 124 | struct file *stdin; |
125 | void (*cleanup)(char **argv, char **envp); | ||
125 | }; | 126 | }; |
126 | 127 | ||
127 | /* | 128 | /* |
@@ -180,6 +181,14 @@ static int ____call_usermodehelper(void *data) | |||
180 | do_exit(0); | 181 | do_exit(0); |
181 | } | 182 | } |
182 | 183 | ||
184 | void call_usermodehelper_freeinfo(struct subprocess_info *info) | ||
185 | { | ||
186 | if (info->cleanup) | ||
187 | (*info->cleanup)(info->argv, info->envp); | ||
188 | kfree(info); | ||
189 | } | ||
190 | EXPORT_SYMBOL(call_usermodehelper_freeinfo); | ||
191 | |||
183 | /* Keventd can't block, but this (a child) can. */ | 192 | /* Keventd can't block, but this (a child) can. */ |
184 | static int wait_for_helper(void *data) | 193 | static int wait_for_helper(void *data) |
185 | { | 194 | { |
@@ -217,7 +226,7 @@ static int wait_for_helper(void *data) | |||
217 | } | 226 | } |
218 | 227 | ||
219 | if (sub_info->wait < 0) | 228 | if (sub_info->wait < 0) |
220 | kfree(sub_info); | 229 | call_usermodehelper_freeinfo(sub_info); |
221 | else | 230 | else |
222 | complete(sub_info->complete); | 231 | complete(sub_info->complete); |
223 | return 0; | 232 | return 0; |
@@ -252,11 +261,94 @@ static void __call_usermodehelper(struct work_struct *work) | |||
252 | } | 261 | } |
253 | 262 | ||
254 | /** | 263 | /** |
255 | * call_usermodehelper_keys - start a usermode application | 264 | * call_usermodehelper_setup - prepare to call a usermode helper |
256 | * @path: pathname for the application | 265 | * @path - path to usermode executable |
257 | * @argv: null-terminated argument list | 266 | * @argv - arg vector for process |
258 | * @envp: null-terminated environment list | 267 | * @envp - environment for process |
259 | * @session_keyring: session keyring for process (NULL for an empty keyring) | 268 | * |
269 | * Returns either NULL on allocation failure, or a subprocess_info | ||
270 | * structure. This should be passed to call_usermodehelper_exec to | ||
271 | * exec the process and free the structure. | ||
272 | */ | ||
273 | struct subprocess_info *call_usermodehelper_setup(char *path, | ||
274 | char **argv, char **envp) | ||
275 | { | ||
276 | struct subprocess_info *sub_info; | ||
277 | sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC); | ||
278 | if (!sub_info) | ||
279 | goto out; | ||
280 | |||
281 | INIT_WORK(&sub_info->work, __call_usermodehelper); | ||
282 | sub_info->path = path; | ||
283 | sub_info->argv = argv; | ||
284 | sub_info->envp = envp; | ||
285 | |||
286 | out: | ||
287 | return sub_info; | ||
288 | } | ||
289 | EXPORT_SYMBOL(call_usermodehelper_setup); | ||
290 | |||
291 | /** | ||
292 | * call_usermodehelper_setkeys - set the session keys for usermode helper | ||
293 | * @info: a subprocess_info returned by call_usermodehelper_setup | ||
294 | * @session_keyring: the session keyring for the process | ||
295 | */ | ||
296 | void call_usermodehelper_setkeys(struct subprocess_info *info, | ||
297 | struct key *session_keyring) | ||
298 | { | ||
299 | info->ring = session_keyring; | ||
300 | } | ||
301 | EXPORT_SYMBOL(call_usermodehelper_setkeys); | ||
302 | |||
303 | /** | ||
304 | * call_usermodehelper_setcleanup - set a cleanup function | ||
305 | * @info: a subprocess_info returned by call_usermodehelper_setup | ||
306 | * @cleanup: a cleanup function | ||
307 | * | ||
308 | * The cleanup function is just befor ethe subprocess_info is about to | ||
309 | * be freed. This can be used for freeing the argv and envp. The | ||
310 | * Function must be runnable in either a process context or the | ||
311 | * context in which call_usermodehelper_exec is called. | ||
312 | */ | ||
313 | void call_usermodehelper_setcleanup(struct subprocess_info *info, | ||
314 | void (*cleanup)(char **argv, char **envp)) | ||
315 | { | ||
316 | info->cleanup = cleanup; | ||
317 | } | ||
318 | EXPORT_SYMBOL(call_usermodehelper_setcleanup); | ||
319 | |||
320 | /** | ||
321 | * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin | ||
322 | * @sub_info: a subprocess_info returned by call_usermodehelper_setup | ||
323 | * @filp: set to the write-end of a pipe | ||
324 | * | ||
325 | * This constructs a pipe, and sets the read end to be the stdin of the | ||
326 | * subprocess, and returns the write-end in *@filp. | ||
327 | */ | ||
328 | int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, | ||
329 | struct file **filp) | ||
330 | { | ||
331 | struct file *f; | ||
332 | |||
333 | f = create_write_pipe(); | ||
334 | if (IS_ERR(f)) | ||
335 | return PTR_ERR(f); | ||
336 | *filp = f; | ||
337 | |||
338 | f = create_read_pipe(f); | ||
339 | if (IS_ERR(f)) { | ||
340 | free_write_pipe(*filp); | ||
341 | return PTR_ERR(f); | ||
342 | } | ||
343 | sub_info->stdin = f; | ||
344 | |||
345 | return 0; | ||
346 | } | ||
347 | EXPORT_SYMBOL(call_usermodehelper_stdinpipe); | ||
348 | |||
349 | /** | ||
350 | * call_usermodehelper_exec - start a usermode application | ||
351 | * @sub_info: information about the subprocessa | ||
260 | * @wait: wait for the application to finish and return status. | 352 | * @wait: wait for the application to finish and return status. |
261 | * when -1 don't wait at all, but you get no useful error back when | 353 | * when -1 don't wait at all, but you get no useful error back when |
262 | * the program couldn't be exec'ed. This makes it safe to call | 354 | * the program couldn't be exec'ed. This makes it safe to call |
@@ -265,33 +357,24 @@ static void __call_usermodehelper(struct work_struct *work) | |||
265 | * Runs a user-space application. The application is started | 357 | * Runs a user-space application. The application is started |
266 | * asynchronously if wait is not set, and runs as a child of keventd. | 358 | * asynchronously if wait is not set, and runs as a child of keventd. |
267 | * (ie. it runs with full root capabilities). | 359 | * (ie. it runs with full root capabilities). |
268 | * | ||
269 | * Must be called from process context. Returns a negative error code | ||
270 | * if program was not execed successfully, or 0. | ||
271 | */ | 360 | */ |
272 | int call_usermodehelper_keys(char *path, char **argv, char **envp, | 361 | int call_usermodehelper_exec(struct subprocess_info *sub_info, |
273 | struct key *session_keyring, int wait) | 362 | int wait) |
274 | { | 363 | { |
275 | DECLARE_COMPLETION_ONSTACK(done); | 364 | DECLARE_COMPLETION_ONSTACK(done); |
276 | struct subprocess_info *sub_info; | ||
277 | int retval; | 365 | int retval; |
278 | 366 | ||
279 | if (!khelper_wq) | 367 | if (sub_info->path[0] == '\0') { |
280 | return -EBUSY; | 368 | retval = 0; |
281 | 369 | goto out; | |
282 | if (path[0] == '\0') | 370 | } |
283 | return 0; | ||
284 | 371 | ||
285 | sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC); | 372 | if (!khelper_wq) { |
286 | if (!sub_info) | 373 | retval = -EBUSY; |
287 | return -ENOMEM; | 374 | goto out; |
375 | } | ||
288 | 376 | ||
289 | INIT_WORK(&sub_info->work, __call_usermodehelper); | ||
290 | sub_info->complete = &done; | 377 | sub_info->complete = &done; |
291 | sub_info->path = path; | ||
292 | sub_info->argv = argv; | ||
293 | sub_info->envp = envp; | ||
294 | sub_info->ring = session_keyring; | ||
295 | sub_info->wait = wait; | 378 | sub_info->wait = wait; |
296 | 379 | ||
297 | queue_work(khelper_wq, &sub_info->work); | 380 | queue_work(khelper_wq, &sub_info->work); |
@@ -299,47 +382,43 @@ int call_usermodehelper_keys(char *path, char **argv, char **envp, | |||
299 | return 0; | 382 | return 0; |
300 | wait_for_completion(&done); | 383 | wait_for_completion(&done); |
301 | retval = sub_info->retval; | 384 | retval = sub_info->retval; |
302 | kfree(sub_info); | 385 | |
386 | out: | ||
387 | call_usermodehelper_freeinfo(sub_info); | ||
303 | return retval; | 388 | return retval; |
304 | } | 389 | } |
305 | EXPORT_SYMBOL(call_usermodehelper_keys); | 390 | EXPORT_SYMBOL(call_usermodehelper_exec); |
306 | 391 | ||
392 | /** | ||
393 | * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin | ||
394 | * @path: path to usermode executable | ||
395 | * @argv: arg vector for process | ||
396 | * @envp: environment for process | ||
397 | * @filp: set to the write-end of a pipe | ||
398 | * | ||
399 | * This is a simple wrapper which executes a usermode-helper function | ||
400 | * with a pipe as stdin. It is implemented entirely in terms of | ||
401 | * lower-level call_usermodehelper_* functions. | ||
402 | */ | ||
307 | int call_usermodehelper_pipe(char *path, char **argv, char **envp, | 403 | int call_usermodehelper_pipe(char *path, char **argv, char **envp, |
308 | struct file **filp) | 404 | struct file **filp) |
309 | { | 405 | { |
310 | DECLARE_COMPLETION(done); | 406 | struct subprocess_info *sub_info; |
311 | struct subprocess_info sub_info = { | 407 | int ret; |
312 | .work = __WORK_INITIALIZER(sub_info.work, | ||
313 | __call_usermodehelper), | ||
314 | .complete = &done, | ||
315 | .path = path, | ||
316 | .argv = argv, | ||
317 | .envp = envp, | ||
318 | .retval = 0, | ||
319 | }; | ||
320 | struct file *f; | ||
321 | |||
322 | if (!khelper_wq) | ||
323 | return -EBUSY; | ||
324 | 408 | ||
325 | if (path[0] == '\0') | 409 | sub_info = call_usermodehelper_setup(path, argv, envp); |
326 | return 0; | 410 | if (sub_info == NULL) |
411 | return -ENOMEM; | ||
327 | 412 | ||
328 | f = create_write_pipe(); | 413 | ret = call_usermodehelper_stdinpipe(sub_info, filp); |
329 | if (IS_ERR(f)) | 414 | if (ret < 0) |
330 | return PTR_ERR(f); | 415 | goto out; |
331 | *filp = f; | ||
332 | 416 | ||
333 | f = create_read_pipe(f); | 417 | return call_usermodehelper_exec(sub_info, 1); |
334 | if (IS_ERR(f)) { | ||
335 | free_write_pipe(*filp); | ||
336 | return PTR_ERR(f); | ||
337 | } | ||
338 | sub_info.stdin = f; | ||
339 | 418 | ||
340 | queue_work(khelper_wq, &sub_info.work); | 419 | out: |
341 | wait_for_completion(&done); | 420 | call_usermodehelper_freeinfo(sub_info); |
342 | return sub_info.retval; | 421 | return ret; |
343 | } | 422 | } |
344 | EXPORT_SYMBOL(call_usermodehelper_pipe); | 423 | EXPORT_SYMBOL(call_usermodehelper_pipe); |
345 | 424 | ||