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