#include <linux/syscalls.h>
#include <linux/unistd.h>
#include <linux/kmod.h>
-#include <linux/smp_lock.h>
#include <linux/slab.h>
#include <linux/mnt_namespace.h>
#include <linux/completion.h>
/* We can run anywhere, unlike our parent keventd(). */
set_cpus_allowed(current, CPU_MASK_ALL);
+ /*
+ * Our parent is keventd, which runs with elevated scheduling priority.
+ * Avoid propagating that into the userspace child.
+ */
+ set_user_nice(current, 0);
+
retval = -EPERM;
if (current->fs->root)
retval = kernel_execve(sub_info->path,
sub_info->retval = ret;
}
- complete(sub_info->complete);
+ if (sub_info->wait < 0)
+ kfree(sub_info);
+ else
+ complete(sub_info->complete);
return 0;
}
pid = kernel_thread(____call_usermodehelper, sub_info,
CLONE_VFORK | SIGCHLD);
+ if (wait < 0)
+ return;
+
if (pid < 0) {
sub_info->retval = pid;
complete(sub_info->complete);
* @envp: null-terminated environment list
* @session_keyring: session keyring for process (NULL for an empty keyring)
* @wait: wait for the application to finish and return status.
+ * when -1 don't wait at all, but you get no useful error back when
+ * the program couldn't be exec'ed. This makes it safe to call
+ * from interrupt context.
*
* Runs a user-space application. The application is started
* asynchronously if wait is not set, and runs as a child of keventd.
struct key *session_keyring, int wait)
{
DECLARE_COMPLETION_ONSTACK(done);
- struct subprocess_info sub_info = {
- .work = __WORK_INITIALIZER(sub_info.work,
- __call_usermodehelper),
- .complete = &done,
- .path = path,
- .argv = argv,
- .envp = envp,
- .ring = session_keyring,
- .wait = wait,
- .retval = 0,
- };
+ struct subprocess_info *sub_info;
+ int retval;
if (!khelper_wq)
return -EBUSY;
if (path[0] == '\0')
return 0;
- queue_work(khelper_wq, &sub_info.work);
+ sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC);
+ if (!sub_info)
+ return -ENOMEM;
+
+ INIT_WORK(&sub_info->work, __call_usermodehelper);
+ sub_info->complete = &done;
+ sub_info->path = path;
+ sub_info->argv = argv;
+ sub_info->envp = envp;
+ sub_info->ring = session_keyring;
+ sub_info->wait = wait;
+
+ queue_work(khelper_wq, &sub_info->work);
+ if (wait < 0) /* task has freed sub_info */
+ return 0;
wait_for_completion(&done);
- return sub_info.retval;
+ retval = sub_info->retval;
+ kfree(sub_info);
+ return retval;
}
EXPORT_SYMBOL(call_usermodehelper_keys);