18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <stdlib.h>
78c2ecf20Sopenharmony_ci#include <unistd.h>
88c2ecf20Sopenharmony_ci#include <errno.h>
98c2ecf20Sopenharmony_ci#include <sched.h>
108c2ecf20Sopenharmony_ci#include <linux/limits.h>
118c2ecf20Sopenharmony_ci#include <sys/socket.h>
128c2ecf20Sopenharmony_ci#include <sys/wait.h>
138c2ecf20Sopenharmony_ci#include <kern_util.h>
148c2ecf20Sopenharmony_ci#include <os.h>
158c2ecf20Sopenharmony_ci#include <um_malloc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistruct helper_data {
188c2ecf20Sopenharmony_ci	void (*pre_exec)(void*);
198c2ecf20Sopenharmony_ci	void *pre_data;
208c2ecf20Sopenharmony_ci	char **argv;
218c2ecf20Sopenharmony_ci	int fd;
228c2ecf20Sopenharmony_ci	char *buf;
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int helper_child(void *arg)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct helper_data *data = arg;
288c2ecf20Sopenharmony_ci	char **argv = data->argv;
298c2ecf20Sopenharmony_ci	int err, ret;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (data->pre_exec != NULL)
328c2ecf20Sopenharmony_ci		(*data->pre_exec)(data->pre_data);
338c2ecf20Sopenharmony_ci	err = execvp_noalloc(data->buf, argv[0], argv);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* If the exec succeeds, we don't get here */
368c2ecf20Sopenharmony_ci	CATCH_EINTR(ret = write(data->fd, &err, sizeof(err)));
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return 0;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* Returns either the pid of the child process we run or -E* on failure. */
428c2ecf20Sopenharmony_ciint run_helper(void (*pre_exec)(void *), void *pre_data, char **argv)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct helper_data data;
458c2ecf20Sopenharmony_ci	unsigned long stack, sp;
468c2ecf20Sopenharmony_ci	int pid, fds[2], ret, n;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	stack = alloc_stack(0, __uml_cant_sleep());
498c2ecf20Sopenharmony_ci	if (stack == 0)
508c2ecf20Sopenharmony_ci		return -ENOMEM;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
538c2ecf20Sopenharmony_ci	if (ret < 0) {
548c2ecf20Sopenharmony_ci		ret = -errno;
558c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "run_helper : pipe failed, errno = %d\n",
568c2ecf20Sopenharmony_ci		       errno);
578c2ecf20Sopenharmony_ci		goto out_free;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ret = os_set_exec_close(fds[1]);
618c2ecf20Sopenharmony_ci	if (ret < 0) {
628c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "run_helper : setting FD_CLOEXEC failed, "
638c2ecf20Sopenharmony_ci		       "ret = %d\n", -ret);
648c2ecf20Sopenharmony_ci		goto out_close;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
688c2ecf20Sopenharmony_ci	data.pre_exec = pre_exec;
698c2ecf20Sopenharmony_ci	data.pre_data = pre_data;
708c2ecf20Sopenharmony_ci	data.argv = argv;
718c2ecf20Sopenharmony_ci	data.fd = fds[1];
728c2ecf20Sopenharmony_ci	data.buf = __uml_cant_sleep() ? uml_kmalloc(PATH_MAX, UM_GFP_ATOMIC) :
738c2ecf20Sopenharmony_ci					uml_kmalloc(PATH_MAX, UM_GFP_KERNEL);
748c2ecf20Sopenharmony_ci	pid = clone(helper_child, (void *) sp, CLONE_VM, &data);
758c2ecf20Sopenharmony_ci	if (pid < 0) {
768c2ecf20Sopenharmony_ci		ret = -errno;
778c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "run_helper : clone failed, errno = %d\n",
788c2ecf20Sopenharmony_ci		       errno);
798c2ecf20Sopenharmony_ci		goto out_free2;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	close(fds[1]);
838c2ecf20Sopenharmony_ci	fds[1] = -1;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * Read the errno value from the child, if the exec failed, or get 0 if
878c2ecf20Sopenharmony_ci	 * the exec succeeded because the pipe fd was set as close-on-exec.
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	n = read(fds[0], &ret, sizeof(ret));
908c2ecf20Sopenharmony_ci	if (n == 0) {
918c2ecf20Sopenharmony_ci		ret = pid;
928c2ecf20Sopenharmony_ci	} else {
938c2ecf20Sopenharmony_ci		if (n < 0) {
948c2ecf20Sopenharmony_ci			n = -errno;
958c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "run_helper : read on pipe failed, "
968c2ecf20Sopenharmony_ci			       "ret = %d\n", -n);
978c2ecf20Sopenharmony_ci			ret = n;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci		CATCH_EINTR(waitpid(pid, NULL, __WALL));
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ciout_free2:
1038c2ecf20Sopenharmony_ci	kfree(data.buf);
1048c2ecf20Sopenharmony_ciout_close:
1058c2ecf20Sopenharmony_ci	if (fds[1] != -1)
1068c2ecf20Sopenharmony_ci		close(fds[1]);
1078c2ecf20Sopenharmony_ci	close(fds[0]);
1088c2ecf20Sopenharmony_ciout_free:
1098c2ecf20Sopenharmony_ci	free_stack(stack, 0);
1108c2ecf20Sopenharmony_ci	return ret;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciint run_helper_thread(int (*proc)(void *), void *arg, unsigned int flags,
1148c2ecf20Sopenharmony_ci		      unsigned long *stack_out)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	unsigned long stack, sp;
1178c2ecf20Sopenharmony_ci	int pid, status, err;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	stack = alloc_stack(0, __uml_cant_sleep());
1208c2ecf20Sopenharmony_ci	if (stack == 0)
1218c2ecf20Sopenharmony_ci		return -ENOMEM;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	sp = stack + UM_KERN_PAGE_SIZE - sizeof(void *);
1248c2ecf20Sopenharmony_ci	pid = clone(proc, (void *) sp, flags, arg);
1258c2ecf20Sopenharmony_ci	if (pid < 0) {
1268c2ecf20Sopenharmony_ci		err = -errno;
1278c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "run_helper_thread : clone failed, "
1288c2ecf20Sopenharmony_ci		       "errno = %d\n", errno);
1298c2ecf20Sopenharmony_ci		return err;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci	if (stack_out == NULL) {
1328c2ecf20Sopenharmony_ci		CATCH_EINTR(pid = waitpid(pid, &status, __WALL));
1338c2ecf20Sopenharmony_ci		if (pid < 0) {
1348c2ecf20Sopenharmony_ci			err = -errno;
1358c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "run_helper_thread - wait failed, "
1368c2ecf20Sopenharmony_ci			       "errno = %d\n", errno);
1378c2ecf20Sopenharmony_ci			pid = err;
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci		if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0))
1408c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "run_helper_thread - thread "
1418c2ecf20Sopenharmony_ci			       "returned status 0x%x\n", status);
1428c2ecf20Sopenharmony_ci		free_stack(stack, 0);
1438c2ecf20Sopenharmony_ci	} else
1448c2ecf20Sopenharmony_ci		*stack_out = stack;
1458c2ecf20Sopenharmony_ci	return pid;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ciint helper_wait(int pid)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int ret, status;
1518c2ecf20Sopenharmony_ci	int wflags = __WALL;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	CATCH_EINTR(ret = waitpid(pid, &status, wflags));
1548c2ecf20Sopenharmony_ci	if (ret < 0) {
1558c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "helper_wait : waitpid process %d failed, "
1568c2ecf20Sopenharmony_ci		       "errno = %d\n", pid, errno);
1578c2ecf20Sopenharmony_ci		return -errno;
1588c2ecf20Sopenharmony_ci	} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1598c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "helper_wait : process %d exited with "
1608c2ecf20Sopenharmony_ci		       "status 0x%x\n", pid, status);
1618c2ecf20Sopenharmony_ci		return -ECHILD;
1628c2ecf20Sopenharmony_ci	} else
1638c2ecf20Sopenharmony_ci		return 0;
1648c2ecf20Sopenharmony_ci}
165