forked from Minki/linux
6b7aaad9ba
There were some bugs in handling failures to exec helper programs. errno was passed back from the child with the wrong sign. It was also ignored. In the case where it mattered, the errno from the (successful) read in the parent was used instead. Signed-off-by: Jeff Dike <jdike@addtoit.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
166 lines
3.6 KiB
C
166 lines
3.6 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 <sys/signal.h>
|
|
#include <sys/wait.h>
|
|
#include "user.h"
|
|
#include "kern_util.h"
|
|
#include "user_util.h"
|
|
#include "os.h"
|
|
|
|
struct helper_data {
|
|
void (*pre_exec)(void*);
|
|
void *pre_data;
|
|
char **argv;
|
|
int fd;
|
|
};
|
|
|
|
/* Debugging aid, changed only from gdb */
|
|
int helper_pause = 0;
|
|
|
|
static void helper_hup(int sig)
|
|
{
|
|
}
|
|
|
|
static int helper_child(void *arg)
|
|
{
|
|
struct helper_data *data = arg;
|
|
char **argv = data->argv;
|
|
int errval;
|
|
|
|
if(helper_pause){
|
|
signal(SIGHUP, helper_hup);
|
|
pause();
|
|
}
|
|
if(data->pre_exec != NULL)
|
|
(*data->pre_exec)(data->pre_data);
|
|
execvp(argv[0], argv);
|
|
errval = -errno;
|
|
printk("helper_child - execve of '%s' failed - errno = %d\n", argv[0], errno);
|
|
os_write_file(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 */
|
|
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 + page_size() - sizeof(void *);
|
|
data.pre_exec = pre_exec;
|
|
data.pre_data = pre_data;
|
|
data.argv = argv;
|
|
data.fd = fds[1];
|
|
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_close;
|
|
}
|
|
|
|
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 = os_read_file(fds[0], &ret, sizeof(ret));
|
|
if(n == 0)
|
|
ret = pid;
|
|
else {
|
|
if(n < 0){
|
|
printk("run_helper : read on pipe failed, ret = %d\n",
|
|
-n);
|
|
ret = n;
|
|
kill(pid, SIGKILL);
|
|
}
|
|
CATCH_EINTR(waitpid(pid, NULL, 0));
|
|
}
|
|
|
|
out_close:
|
|
if (fds[1] != -1)
|
|
close(fds[1]);
|
|
close(fds[0]);
|
|
out_free:
|
|
if(stack_out == NULL)
|
|
free_stack(stack, 0);
|
|
else *stack_out = stack;
|
|
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 + (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);
|
|
}
|