197870c34SAlex Dewar // SPDX-License-Identifier: GPL-2.0
2ff5c6ff5SJeff Dike /*
31aa351a3SJeff Dike * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
4ff5c6ff5SJeff Dike */
5ff5c6ff5SJeff Dike
6ff5c6ff5SJeff Dike #include <stdlib.h>
782017457SGlenn Washburn #include <string.h>
8ff5c6ff5SJeff Dike #include <unistd.h>
9ff5c6ff5SJeff Dike #include <errno.h>
10ff5c6ff5SJeff Dike #include <sched.h>
11b6d8adf4SIngo Molnar #include <linux/limits.h>
12512b6fb1SJeff Dike #include <sys/socket.h>
131aa351a3SJeff Dike #include <sys/wait.h>
1437185b33SAl Viro #include <kern_util.h>
1537185b33SAl Viro #include <os.h>
1637185b33SAl Viro #include <um_malloc.h>
17ff5c6ff5SJeff Dike
18ff5c6ff5SJeff Dike struct helper_data {
19ff5c6ff5SJeff Dike void (*pre_exec)(void*);
20ff5c6ff5SJeff Dike void *pre_data;
21ff5c6ff5SJeff Dike char **argv;
22ff5c6ff5SJeff Dike int fd;
235d48545eSPaolo 'Blaisorblade' Giarrusso char *buf;
24ff5c6ff5SJeff Dike };
25ff5c6ff5SJeff Dike
helper_child(void * arg)26ff5c6ff5SJeff Dike static int helper_child(void *arg)
27ff5c6ff5SJeff Dike {
28ff5c6ff5SJeff Dike struct helper_data *data = arg;
29ff5c6ff5SJeff Dike char **argv = data->argv;
302fdf2130SVitaliy Ivanov int err, ret;
31ff5c6ff5SJeff Dike
32ff5c6ff5SJeff Dike if (data->pre_exec != NULL)
33ff5c6ff5SJeff Dike (*data->pre_exec)(data->pre_data);
341aa351a3SJeff Dike err = execvp_noalloc(data->buf, argv[0], argv);
351aa351a3SJeff Dike
361aa351a3SJeff Dike /* If the exec succeeds, we don't get here */
372fdf2130SVitaliy Ivanov CATCH_EINTR(ret = write(data->fd, &err, sizeof(err)));
381aa351a3SJeff Dike
398b028bcdSPaolo 'Blaisorblade' Giarrusso return 0;
40ff5c6ff5SJeff Dike }
41ff5c6ff5SJeff Dike
421aa351a3SJeff Dike /* Returns either the pid of the child process we run or -E* on failure. */
run_helper(void (* pre_exec)(void *),void * pre_data,char ** argv)43c4399016SJeff Dike int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
44ff5c6ff5SJeff Dike {
45ff5c6ff5SJeff Dike struct helper_data data;
46ff5c6ff5SJeff Dike unsigned long stack, sp;
47ff5c6ff5SJeff Dike int pid, fds[2], ret, n;
48ff5c6ff5SJeff Dike
49*976c46e6SAnton Ivanov stack = alloc_stack(0, __uml_cant_sleep());
50ff5c6ff5SJeff Dike if (stack == 0)
516b7aaad9SJeff Dike return -ENOMEM;
52ff5c6ff5SJeff Dike
53512b6fb1SJeff Dike ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
54ff5c6ff5SJeff Dike if (ret < 0) {
55512b6fb1SJeff Dike ret = -errno;
561aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper : pipe failed, errno = %d\n",
571aa351a3SJeff Dike errno);
58ff5c6ff5SJeff Dike goto out_free;
59ff5c6ff5SJeff Dike }
60ff5c6ff5SJeff Dike
61512b6fb1SJeff Dike ret = os_set_exec_close(fds[1]);
62ff5c6ff5SJeff Dike if (ret < 0) {
631aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper : setting FD_CLOEXEC failed, "
641aa351a3SJeff Dike "ret = %d\n", -ret);
65ff5c6ff5SJeff Dike goto out_close;
66ff5c6ff5SJeff Dike }
67ff5c6ff5SJeff Dike
68558f9b2fSYiFei Zhu sp = stack + UM_KERN_PAGE_SIZE;
69ff5c6ff5SJeff Dike data.pre_exec = pre_exec;
70ff5c6ff5SJeff Dike data.pre_data = pre_data;
71ff5c6ff5SJeff Dike data.argv = argv;
72ff5c6ff5SJeff Dike data.fd = fds[1];
73*976c46e6SAnton Ivanov data.buf = __uml_cant_sleep() ? uml_kmalloc(PATH_MAX, UM_GFP_ATOMIC) :
7443f5b308SJeff Dike uml_kmalloc(PATH_MAX, UM_GFP_KERNEL);
754dbed85aSStanislaw Gruszka pid = clone(helper_child, (void *) sp, CLONE_VM, &data);
76ff5c6ff5SJeff Dike if (pid < 0) {
77ff5c6ff5SJeff Dike ret = -errno;
781aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper : clone failed, errno = %d\n",
791aa351a3SJeff Dike errno);
805d48545eSPaolo 'Blaisorblade' Giarrusso goto out_free2;
81ff5c6ff5SJeff Dike }
82ff5c6ff5SJeff Dike
83ff5c6ff5SJeff Dike close(fds[1]);
84ff5c6ff5SJeff Dike fds[1] = -1;
85ff5c6ff5SJeff Dike
86ef0470c0SJeff Dike /*
87ef0470c0SJeff Dike * Read the errno value from the child, if the exec failed, or get 0 if
88ef0470c0SJeff Dike * the exec succeeded because the pipe fd was set as close-on-exec.
89ef0470c0SJeff Dike */
90a61f334fSJeff Dike n = read(fds[0], &ret, sizeof(ret));
918b028bcdSPaolo 'Blaisorblade' Giarrusso if (n == 0) {
926b7aaad9SJeff Dike ret = pid;
938b028bcdSPaolo 'Blaisorblade' Giarrusso } else {
94ff5c6ff5SJeff Dike if (n < 0) {
95a61f334fSJeff Dike n = -errno;
961aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper : read on pipe failed, "
971aa351a3SJeff Dike "ret = %d\n", -n);
98ff5c6ff5SJeff Dike ret = n;
996b7aaad9SJeff Dike }
1006b187337SRichard Weinberger CATCH_EINTR(waitpid(pid, NULL, __WALL));
101ff5c6ff5SJeff Dike }
102ff5c6ff5SJeff Dike
10382017457SGlenn Washburn if (ret < 0)
10482017457SGlenn Washburn printk(UM_KERN_ERR "run_helper : failed to exec %s on host: %s\n",
10582017457SGlenn Washburn argv[0], strerror(-ret));
10682017457SGlenn Washburn
1075d48545eSPaolo 'Blaisorblade' Giarrusso out_free2:
1085d48545eSPaolo 'Blaisorblade' Giarrusso kfree(data.buf);
109ff5c6ff5SJeff Dike out_close:
110ff5c6ff5SJeff Dike if (fds[1] != -1)
111ff5c6ff5SJeff Dike close(fds[1]);
112ff5c6ff5SJeff Dike close(fds[0]);
113ff5c6ff5SJeff Dike out_free:
114ff5c6ff5SJeff Dike free_stack(stack, 0);
1158b028bcdSPaolo 'Blaisorblade' Giarrusso return ret;
116ff5c6ff5SJeff Dike }
117ff5c6ff5SJeff Dike
run_helper_thread(int (* proc)(void *),void * arg,unsigned int flags,unsigned long * stack_out)118ff5c6ff5SJeff Dike int run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
119c4399016SJeff Dike unsigned long *stack_out)
120ff5c6ff5SJeff Dike {
121ff5c6ff5SJeff Dike unsigned long stack, sp;
122ff5c6ff5SJeff Dike int pid, status, err;
123ff5c6ff5SJeff Dike
124*976c46e6SAnton Ivanov stack = alloc_stack(0, __uml_cant_sleep());
1258b028bcdSPaolo 'Blaisorblade' Giarrusso if (stack == 0)
1268b028bcdSPaolo 'Blaisorblade' Giarrusso return -ENOMEM;
127ff5c6ff5SJeff Dike
128558f9b2fSYiFei Zhu sp = stack + UM_KERN_PAGE_SIZE;
1294dbed85aSStanislaw Gruszka pid = clone(proc, (void *) sp, flags, arg);
130ff5c6ff5SJeff Dike if (pid < 0) {
131ff5c6ff5SJeff Dike err = -errno;
1321aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper_thread : clone failed, "
1331aa351a3SJeff Dike "errno = %d\n", errno);
134ff5c6ff5SJeff Dike return err;
135ff5c6ff5SJeff Dike }
136ff5c6ff5SJeff Dike if (stack_out == NULL) {
1376b187337SRichard Weinberger CATCH_EINTR(pid = waitpid(pid, &status, __WALL));
138ff5c6ff5SJeff Dike if (pid < 0) {
139ff5c6ff5SJeff Dike err = -errno;
1401aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper_thread - wait failed, "
1411aa351a3SJeff Dike "errno = %d\n", errno);
142ff5c6ff5SJeff Dike pid = err;
143ff5c6ff5SJeff Dike }
144ff5c6ff5SJeff Dike if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
1451aa351a3SJeff Dike printk(UM_KERN_ERR "run_helper_thread - thread "
1461aa351a3SJeff Dike "returned status 0x%x\n", status);
147c4399016SJeff Dike free_stack(stack, 0);
1488b028bcdSPaolo 'Blaisorblade' Giarrusso } else
1498b028bcdSPaolo 'Blaisorblade' Giarrusso *stack_out = stack;
1508b028bcdSPaolo 'Blaisorblade' Giarrusso return pid;
151ff5c6ff5SJeff Dike }
152ff5c6ff5SJeff Dike
helper_wait(int pid)1531aa351a3SJeff Dike int helper_wait(int pid)
154ff5c6ff5SJeff Dike {
1554dbed85aSStanislaw Gruszka int ret, status;
1566b187337SRichard Weinberger int wflags = __WALL;
157ff5c6ff5SJeff Dike
1584dbed85aSStanislaw Gruszka CATCH_EINTR(ret = waitpid(pid, &status, wflags));
159ff5c6ff5SJeff Dike if (ret < 0) {
1601aa351a3SJeff Dike printk(UM_KERN_ERR "helper_wait : waitpid process %d failed, "
1611aa351a3SJeff Dike "errno = %d\n", pid, errno);
1624dbed85aSStanislaw Gruszka return -errno;
1634dbed85aSStanislaw Gruszka } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1641aa351a3SJeff Dike printk(UM_KERN_ERR "helper_wait : process %d exited with "
1651aa351a3SJeff Dike "status 0x%x\n", pid, status);
1664dbed85aSStanislaw Gruszka return -ECHILD;
1674dbed85aSStanislaw Gruszka } else
1684dbed85aSStanislaw Gruszka return 0;
169ff5c6ff5SJeff Dike }
170