a61f334fd2
This patch converts calls in the os layer to os_{read,write}_file to calls directly to libc read() and write() where it is clear that the I/O buffer is in the kernel. We can do that here instead of calling os_{read,write}_file_k since we are in libc code and can call libc directly. With the change in the calls, error handling needs to be changed to refer to errno directly rather than the return value of the call. CATCH_EINTR wrappers were also added where needed. Signed-off-by: Jeff Dike <jdike@linux.intel.com> Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
167 lines
3.8 KiB
C
167 lines
3.8 KiB
C
/*
|
|
* Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
|
|
* Licensed under the GPL
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sched.h>
|
|
#include <limits.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/wait.h>
|
|
#include "user.h"
|
|
#include "kern_util.h"
|
|
#include "os.h"
|
|
#include "um_malloc.h"
|
|
#include "kern_constants.h"
|
|
|
|
struct helper_data {
|
|
void (*pre_exec)(void*);
|
|
void *pre_data;
|
|
char **argv;
|
|
int fd;
|
|
char *buf;
|
|
};
|
|
|
|
static int helper_child(void *arg)
|
|
{
|
|
struct helper_data *data = arg;
|
|
char **argv = data->argv;
|
|
int errval;
|
|
|
|
if (data->pre_exec != NULL)
|
|
(*data->pre_exec)(data->pre_data);
|
|
errval = execvp_noalloc(data->buf, argv[0], argv);
|
|
printk("helper_child - execvp of '%s' failed - errno = %d\n", argv[0],
|
|
-errval);
|
|
write(data->fd, &errval, sizeof(errval));
|
|
kill(os_getpid(), SIGKILL);
|
|
return 0;
|
|
}
|
|
|
|
/* Returns either the pid of the child process we run or -E* on failure.
|
|
* XXX The alloc_stack here breaks if this is called in the tracing thread, so
|
|
* we need to receive a preallocated stack (a local buffer is ok). */
|
|
int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
|
|
unsigned long *stack_out)
|
|
{
|
|
struct helper_data data;
|
|
unsigned long stack, sp;
|
|
int pid, fds[2], ret, n;
|
|
|
|
if ((stack_out != NULL) && (*stack_out != 0))
|
|
stack = *stack_out;
|
|
else
|
|
stack = alloc_stack(0, __cant_sleep());
|
|
if (stack == 0)
|
|
return -ENOMEM;
|
|
|
|
ret = os_pipe(fds, 1, 0);
|
|
if (ret < 0) {
|
|
printk("run_helper : pipe failed, ret = %d\n", -ret);
|
|
goto out_free;
|
|
}
|
|
|
|
ret = os_set_exec_close(fds[1], 1);
|
|
if (ret < 0) {
|
|
printk("run_helper : setting FD_CLOEXEC failed, ret = %d\n",
|
|
-ret);
|
|
goto out_close;
|
|
}
|
|
|
|
sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
|
|
data.pre_exec = pre_exec;
|
|
data.pre_data = pre_data;
|
|
data.argv = argv;
|
|
data.fd = fds[1];
|
|
data.buf = __cant_sleep() ? um_kmalloc_atomic(PATH_MAX) :
|
|
um_kmalloc(PATH_MAX);
|
|
pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data);
|
|
if (pid < 0) {
|
|
ret = -errno;
|
|
printk("run_helper : clone failed, errno = %d\n", errno);
|
|
goto out_free2;
|
|
}
|
|
|
|
close(fds[1]);
|
|
fds[1] = -1;
|
|
|
|
/*
|
|
* Read the errno value from the child, if the exec failed, or get 0 if
|
|
* the exec succeeded because the pipe fd was set as close-on-exec.
|
|
*/
|
|
n = read(fds[0], &ret, sizeof(ret));
|
|
if (n == 0) {
|
|
ret = pid;
|
|
} else {
|
|
if (n < 0) {
|
|
n = -errno;
|
|
printk("run_helper : read on pipe failed, ret = %d\n",
|
|
-n);
|
|
ret = n;
|
|
kill(pid, SIGKILL);
|
|
}
|
|
CATCH_EINTR(waitpid(pid, NULL, 0));
|
|
}
|
|
|
|
out_free2:
|
|
kfree(data.buf);
|
|
out_close:
|
|
if (fds[1] != -1)
|
|
close(fds[1]);
|
|
close(fds[0]);
|
|
out_free:
|
|
if ((stack_out == NULL) || (*stack_out == 0))
|
|
free_stack(stack, 0);
|
|
return ret;
|
|
}
|
|
|
|
int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
|
|
unsigned long *stack_out, int stack_order)
|
|
{
|
|
unsigned long stack, sp;
|
|
int pid, status, err;
|
|
|
|
stack = alloc_stack(stack_order, __cant_sleep());
|
|
if (stack == 0)
|
|
return -ENOMEM;
|
|
|
|
sp = stack + (UM_KERN_PAGE_SIZE << stack_order) - sizeof(void *);
|
|
pid = clone(proc, (void *) sp, flags | SIGCHLD, arg);
|
|
if (pid < 0) {
|
|
err = -errno;
|
|
printk("run_helper_thread : clone failed, errno = %d\n",
|
|
errno);
|
|
return err;
|
|
}
|
|
if (stack_out == NULL) {
|
|
CATCH_EINTR(pid = waitpid(pid, &status, 0));
|
|
if (pid < 0) {
|
|
err = -errno;
|
|
printk("run_helper_thread - wait failed, errno = %d\n",
|
|
errno);
|
|
pid = err;
|
|
}
|
|
if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
|
|
printk("run_helper_thread - thread returned status "
|
|
"0x%x\n", status);
|
|
free_stack(stack, stack_order);
|
|
} else
|
|
*stack_out = stack;
|
|
return pid;
|
|
}
|
|
|
|
int helper_wait(int pid)
|
|
{
|
|
int ret;
|
|
|
|
CATCH_EINTR(ret = waitpid(pid, NULL, WNOHANG));
|
|
if (ret < 0) {
|
|
ret = -errno;
|
|
printk("helper_wait : waitpid failed, errno = %d\n", errno);
|
|
}
|
|
return ret;
|
|
}
|