diff options
Diffstat (limited to 'kernel/kmod.c')
| -rw-r--r-- | kernel/kmod.c | 193 | 
1 files changed, 38 insertions, 155 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index bf0e231d9702..6e9b19667a8d 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -116,27 +116,16 @@ int __request_module(bool wait, const char *fmt, ...)  	trace_module_request(module_name, wait, _RET_IP_); -	ret = call_usermodehelper(modprobe_path, argv, envp, -			wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC); +	ret = call_usermodehelper_fns(modprobe_path, argv, envp, +			wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC, +			NULL, NULL, NULL); +  	atomic_dec(&kmod_concurrent);  	return ret;  }  EXPORT_SYMBOL(__request_module);  #endif /* CONFIG_MODULES */ -struct subprocess_info { -	struct work_struct work; -	struct completion *complete; -	struct cred *cred; -	char *path; -	char **argv; -	char **envp; -	enum umh_wait wait; -	int retval; -	struct file *stdin; -	void (*cleanup)(char **argv, char **envp); -}; -  /*   * This is the task which runs the usermode application   */ @@ -145,36 +134,10 @@ static int ____call_usermodehelper(void *data)  	struct subprocess_info *sub_info = data;  	int retval; -	BUG_ON(atomic_read(&sub_info->cred->usage) != 1); - -	/* Unblock all signals */  	spin_lock_irq(¤t->sighand->siglock);  	flush_signal_handlers(current, 1); -	sigemptyset(¤t->blocked); -	recalc_sigpending();  	spin_unlock_irq(¤t->sighand->siglock); -	/* Install the credentials */ -	commit_creds(sub_info->cred); -	sub_info->cred = NULL; - -	/* Install input pipe when needed */ -	if (sub_info->stdin) { -		struct files_struct *f = current->files; -		struct fdtable *fdt; -		/* no races because files should be private here */ -		sys_close(0); -		fd_install(0, sub_info->stdin); -		spin_lock(&f->file_lock); -		fdt = files_fdtable(f); -		FD_SET(0, fdt->open_fds); -		FD_CLR(0, fdt->close_on_exec); -		spin_unlock(&f->file_lock); - -		/* and disallow core files too */ -		current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0}; -	} -  	/* We can run anywhere, unlike our parent keventd(). */  	set_cpus_allowed_ptr(current, cpu_all_mask); @@ -184,9 +147,16 @@ static int ____call_usermodehelper(void *data)  	 */  	set_user_nice(current, 0); +	if (sub_info->init) { +		retval = sub_info->init(sub_info); +		if (retval) +			goto fail; +	} +  	retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);  	/* Exec failed? */ +fail:  	sub_info->retval = retval;  	do_exit(0);  } @@ -194,9 +164,7 @@ static int ____call_usermodehelper(void *data)  void call_usermodehelper_freeinfo(struct subprocess_info *info)  {  	if (info->cleanup) -		(*info->cleanup)(info->argv, info->envp); -	if (info->cred) -		put_cred(info->cred); +		(*info->cleanup)(info);  	kfree(info);  }  EXPORT_SYMBOL(call_usermodehelper_freeinfo); @@ -207,16 +175,16 @@ static int wait_for_helper(void *data)  	struct subprocess_info *sub_info = data;  	pid_t pid; -	/* Install a handler: if SIGCLD isn't handled sys_wait4 won't -	 * populate the status, but will return -ECHILD. */ -	allow_signal(SIGCHLD); +	/* If SIGCLD is ignored sys_wait4 won't populate the status. */ +	spin_lock_irq(¤t->sighand->siglock); +	current->sighand->action[SIGCHLD-1].sa.sa_handler = SIG_DFL; +	spin_unlock_irq(¤t->sighand->siglock);  	pid = kernel_thread(____call_usermodehelper, sub_info, SIGCHLD);  	if (pid < 0) {  		sub_info->retval = pid;  	} else { -		int ret; - +		int ret = -ECHILD;  		/*  		 * Normally it is bogus to call wait4() from in-kernel because  		 * wait4() wants to write the exit code to a userspace address. @@ -237,10 +205,7 @@ static int wait_for_helper(void *data)  			sub_info->retval = ret;  	} -	if (sub_info->wait == UMH_NO_WAIT) -		call_usermodehelper_freeinfo(sub_info); -	else -		complete(sub_info->complete); +	complete(sub_info->complete);  	return 0;  } @@ -249,15 +214,13 @@ static void __call_usermodehelper(struct work_struct *work)  {  	struct subprocess_info *sub_info =  		container_of(work, struct subprocess_info, work); -	pid_t pid;  	enum umh_wait wait = sub_info->wait; - -	BUG_ON(atomic_read(&sub_info->cred->usage) != 1); +	pid_t pid;  	/* CLONE_VFORK: wait until the usermode helper has execve'd  	 * successfully We need the data structures to stay around  	 * until that is done.  */ -	if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT) +	if (wait == UMH_WAIT_PROC)  		pid = kernel_thread(wait_for_helper, sub_info,  				    CLONE_FS | CLONE_FILES | SIGCHLD);  	else @@ -266,15 +229,16 @@ static void __call_usermodehelper(struct work_struct *work)  	switch (wait) {  	case UMH_NO_WAIT: +		call_usermodehelper_freeinfo(sub_info);  		break;  	case UMH_WAIT_PROC:  		if (pid > 0)  			break; -		sub_info->retval = pid;  		/* FALLTHROUGH */ -  	case UMH_WAIT_EXEC: +		if (pid < 0) +			sub_info->retval = pid;  		complete(sub_info->complete);  	}  } @@ -376,80 +340,37 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,  	sub_info->path = path;  	sub_info->argv = argv;  	sub_info->envp = envp; -	sub_info->cred = prepare_usermodehelper_creds(); -	if (!sub_info->cred) { -		kfree(sub_info); -		return NULL; -	} -    out:  	return sub_info;  }  EXPORT_SYMBOL(call_usermodehelper_setup);  /** - * call_usermodehelper_setkeys - set the session keys for usermode helper - * @info: a subprocess_info returned by call_usermodehelper_setup - * @session_keyring: the session keyring for the process - */ -void call_usermodehelper_setkeys(struct subprocess_info *info, -				 struct key *session_keyring) -{ -#ifdef CONFIG_KEYS -	struct thread_group_cred *tgcred = info->cred->tgcred; -	key_put(tgcred->session_keyring); -	tgcred->session_keyring = key_get(session_keyring); -#else -	BUG(); -#endif -} -EXPORT_SYMBOL(call_usermodehelper_setkeys); - -/** - * call_usermodehelper_setcleanup - set a cleanup function + * call_usermodehelper_setfns - set a cleanup/init function   * @info: a subprocess_info returned by call_usermodehelper_setup   * @cleanup: a cleanup function + * @init: an init function + * @data: arbitrary context sensitive data   * - * The cleanup function is just befor ethe subprocess_info is about to + * The init function is used to customize the helper process prior to + * exec.  A non-zero return code causes the process to error out, exit, + * and return the failure to the calling process + * + * The cleanup function is just before ethe subprocess_info is about to   * be freed.  This can be used for freeing the argv and envp.  The   * Function must be runnable in either a process context or the   * context in which call_usermodehelper_exec is called.   */ -void call_usermodehelper_setcleanup(struct subprocess_info *info, -				    void (*cleanup)(char **argv, char **envp)) +void call_usermodehelper_setfns(struct subprocess_info *info, +		    int (*init)(struct subprocess_info *info), +		    void (*cleanup)(struct subprocess_info *info), +		    void *data)  {  	info->cleanup = cleanup; +	info->init = init; +	info->data = data;  } -EXPORT_SYMBOL(call_usermodehelper_setcleanup); - -/** - * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin - * @sub_info: a subprocess_info returned by call_usermodehelper_setup - * @filp: set to the write-end of a pipe - * - * This constructs a pipe, and sets the read end to be the stdin of the - * subprocess, and returns the write-end in *@filp. - */ -int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, -				  struct file **filp) -{ -	struct file *f; - -	f = create_write_pipe(0); -	if (IS_ERR(f)) -		return PTR_ERR(f); -	*filp = f; - -	f = create_read_pipe(f, 0); -	if (IS_ERR(f)) { -		free_write_pipe(*filp); -		return PTR_ERR(f); -	} -	sub_info->stdin = f; - -	return 0; -} -EXPORT_SYMBOL(call_usermodehelper_stdinpipe); +EXPORT_SYMBOL(call_usermodehelper_setfns);  /**   * call_usermodehelper_exec - start a usermode application @@ -469,9 +390,6 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,  	DECLARE_COMPLETION_ONSTACK(done);  	int retval = 0; -	BUG_ON(atomic_read(&sub_info->cred->usage) != 1); -	validate_creds(sub_info->cred); -  	helper_lock();  	if (sub_info->path[0] == '\0')  		goto out; @@ -498,41 +416,6 @@ unlock:  }  EXPORT_SYMBOL(call_usermodehelper_exec); -/** - * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin - * @path: path to usermode executable - * @argv: arg vector for process - * @envp: environment for process - * @filp: set to the write-end of a pipe - * - * This is a simple wrapper which executes a usermode-helper function - * with a pipe as stdin.  It is implemented entirely in terms of - * lower-level call_usermodehelper_* functions. - */ -int call_usermodehelper_pipe(char *path, char **argv, char **envp, -			     struct file **filp) -{ -	struct subprocess_info *sub_info; -	int ret; - -	sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL); -	if (sub_info == NULL) -		return -ENOMEM; - -	ret = call_usermodehelper_stdinpipe(sub_info, filp); -	if (ret < 0) { -		call_usermodehelper_freeinfo(sub_info); -		return ret; -	} - -	ret = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC); -	if (ret < 0)	/* Failed to execute helper, close pipe */ -		filp_close(*filp, NULL); - -	return ret; -} -EXPORT_SYMBOL(call_usermodehelper_pipe); -  void __init usermodehelper_init(void)  {  	khelper_wq = create_singlethread_workqueue("khelper");  |