xref: /openbmc/linux/arch/um/os-Linux/helper.c (revision 87832e937c808a7ebc41254b408362e3255c87c9)
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