1 /* 2 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) 3 * Licensed under the GPL 4 */ 5 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <errno.h> 9 #include <sched.h> 10 #include <linux/limits.h> 11 #include <sys/socket.h> 12 #include <sys/wait.h> 13 #include "kern_constants.h" 14 #include "kern_util.h" 15 #include "os.h" 16 #include "um_malloc.h" 17 #include "user.h" 18 #include <linux/limits.h> 19 20 struct helper_data { 21 void (*pre_exec)(void*); 22 void *pre_data; 23 char **argv; 24 int fd; 25 char *buf; 26 }; 27 28 static int helper_child(void *arg) 29 { 30 struct helper_data *data = arg; 31 char **argv = data->argv; 32 int err; 33 34 if (data->pre_exec != NULL) 35 (*data->pre_exec)(data->pre_data); 36 err = execvp_noalloc(data->buf, argv[0], argv); 37 38 /* If the exec succeeds, we don't get here */ 39 write(data->fd, &err, sizeof(err)); 40 41 return 0; 42 } 43 44 /* Returns either the pid of the child process we run or -E* on failure. */ 45 int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv) 46 { 47 struct helper_data data; 48 unsigned long stack, sp; 49 int pid, fds[2], ret, n; 50 51 stack = alloc_stack(0, __cant_sleep()); 52 if (stack == 0) 53 return -ENOMEM; 54 55 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); 56 if (ret < 0) { 57 ret = -errno; 58 printk(UM_KERN_ERR "run_helper : pipe failed, errno = %d\n", 59 errno); 60 goto out_free; 61 } 62 63 ret = os_set_exec_close(fds[1]); 64 if (ret < 0) { 65 printk(UM_KERN_ERR "run_helper : setting FD_CLOEXEC failed, " 66 "ret = %d\n", -ret); 67 goto out_close; 68 } 69 70 sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *); 71 data.pre_exec = pre_exec; 72 data.pre_data = pre_data; 73 data.argv = argv; 74 data.fd = fds[1]; 75 data.buf = __cant_sleep() ? uml_kmalloc(PATH_MAX, UM_GFP_ATOMIC) : 76 uml_kmalloc(PATH_MAX, UM_GFP_KERNEL); 77 pid = clone(helper_child, (void *) sp, CLONE_VM, &data); 78 if (pid < 0) { 79 ret = -errno; 80 printk(UM_KERN_ERR "run_helper : clone failed, errno = %d\n", 81 errno); 82 goto out_free2; 83 } 84 85 close(fds[1]); 86 fds[1] = -1; 87 88 /* 89 * Read the errno value from the child, if the exec failed, or get 0 if 90 * the exec succeeded because the pipe fd was set as close-on-exec. 91 */ 92 n = read(fds[0], &ret, sizeof(ret)); 93 if (n == 0) { 94 ret = pid; 95 } else { 96 if (n < 0) { 97 n = -errno; 98 printk(UM_KERN_ERR "run_helper : read on pipe failed, " 99 "ret = %d\n", -n); 100 ret = n; 101 } 102 CATCH_EINTR(waitpid(pid, NULL, __WCLONE)); 103 } 104 105 out_free2: 106 kfree(data.buf); 107 out_close: 108 if (fds[1] != -1) 109 close(fds[1]); 110 close(fds[0]); 111 out_free: 112 free_stack(stack, 0); 113 return ret; 114 } 115 116 int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags, 117 unsigned long *stack_out) 118 { 119 unsigned long stack, sp; 120 int pid, status, err; 121 122 stack = alloc_stack(0, __cant_sleep()); 123 if (stack == 0) 124 return -ENOMEM; 125 126 sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *); 127 pid = clone(proc, (void *) sp, flags, arg); 128 if (pid < 0) { 129 err = -errno; 130 printk(UM_KERN_ERR "run_helper_thread : clone failed, " 131 "errno = %d\n", errno); 132 return err; 133 } 134 if (stack_out == NULL) { 135 CATCH_EINTR(pid = waitpid(pid, &status, __WCLONE)); 136 if (pid < 0) { 137 err = -errno; 138 printk(UM_KERN_ERR "run_helper_thread - wait failed, " 139 "errno = %d\n", errno); 140 pid = err; 141 } 142 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) 143 printk(UM_KERN_ERR "run_helper_thread - thread " 144 "returned status 0x%x\n", status); 145 free_stack(stack, 0); 146 } else 147 *stack_out = stack; 148 return pid; 149 } 150 151 int helper_wait(int pid) 152 { 153 int ret, status; 154 int wflags = __WCLONE; 155 156 CATCH_EINTR(ret = waitpid(pid, &status, wflags)); 157 if (ret < 0) { 158 printk(UM_KERN_ERR "helper_wait : waitpid process %d failed, " 159 "errno = %d\n", pid, errno); 160 return -errno; 161 } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 162 printk(UM_KERN_ERR "helper_wait : process %d exited with " 163 "status 0x%x\n", pid, status); 164 return -ECHILD; 165 } else 166 return 0; 167 } 168