18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.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 <signal.h>
118c2ecf20Sopenharmony_ci#include <termios.h>
128c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
138c2ecf20Sopenharmony_ci#include "chan_user.h"
148c2ecf20Sopenharmony_ci#include <os.h>
158c2ecf20Sopenharmony_ci#include <um_malloc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_civoid generic_close(int fd, void *unused)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	close(fd);
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ciint generic_read(int fd, char *c_out, void *unused)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	int n;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	n = read(fd, c_out, sizeof(*c_out));
278c2ecf20Sopenharmony_ci	if (n > 0)
288c2ecf20Sopenharmony_ci		return n;
298c2ecf20Sopenharmony_ci	else if (n == 0)
308c2ecf20Sopenharmony_ci		return -EIO;
318c2ecf20Sopenharmony_ci	else if (errno == EAGAIN)
328c2ecf20Sopenharmony_ci		return 0;
338c2ecf20Sopenharmony_ci	return -errno;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* XXX Trivial wrapper around write */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciint generic_write(int fd, const char *buf, int n, void *unused)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int err;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	err = write(fd, buf, n);
438c2ecf20Sopenharmony_ci	if (err > 0)
448c2ecf20Sopenharmony_ci		return err;
458c2ecf20Sopenharmony_ci	else if (errno == EAGAIN)
468c2ecf20Sopenharmony_ci		return 0;
478c2ecf20Sopenharmony_ci	else if (err == 0)
488c2ecf20Sopenharmony_ci		return -EIO;
498c2ecf20Sopenharmony_ci	return -errno;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ciint generic_window_size(int fd, void *unused, unsigned short *rows_out,
538c2ecf20Sopenharmony_ci			unsigned short *cols_out)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	struct winsize size;
568c2ecf20Sopenharmony_ci	int ret;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (ioctl(fd, TIOCGWINSZ, &size) < 0)
598c2ecf20Sopenharmony_ci		return -errno;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col));
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	*rows_out = size.ws_row;
648c2ecf20Sopenharmony_ci	*cols_out = size.ws_col;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return ret;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_civoid generic_free(void *data)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	kfree(data);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciint generic_console_write(int fd, const char *buf, int n)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	sigset_t old, no_sigio;
778c2ecf20Sopenharmony_ci	struct termios save, new;
788c2ecf20Sopenharmony_ci	int err;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (isatty(fd)) {
818c2ecf20Sopenharmony_ci		sigemptyset(&no_sigio);
828c2ecf20Sopenharmony_ci		sigaddset(&no_sigio, SIGIO);
838c2ecf20Sopenharmony_ci		if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
848c2ecf20Sopenharmony_ci			goto error;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		CATCH_EINTR(err = tcgetattr(fd, &save));
878c2ecf20Sopenharmony_ci		if (err)
888c2ecf20Sopenharmony_ci			goto error;
898c2ecf20Sopenharmony_ci		new = save;
908c2ecf20Sopenharmony_ci		/*
918c2ecf20Sopenharmony_ci		 * The terminal becomes a bit less raw, to handle \n also as
928c2ecf20Sopenharmony_ci		 * "Carriage Return", not only as "New Line". Otherwise, the new
938c2ecf20Sopenharmony_ci		 * line won't start at the first column.
948c2ecf20Sopenharmony_ci		 */
958c2ecf20Sopenharmony_ci		new.c_oflag |= OPOST;
968c2ecf20Sopenharmony_ci		CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new));
978c2ecf20Sopenharmony_ci		if (err)
988c2ecf20Sopenharmony_ci			goto error;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	err = generic_write(fd, buf, n, NULL);
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * Restore raw mode, in any case; we *must* ignore any error apart
1038c2ecf20Sopenharmony_ci	 * EINTR, except for debug.
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	if (isatty(fd)) {
1068c2ecf20Sopenharmony_ci		CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
1078c2ecf20Sopenharmony_ci		sigprocmask(SIG_SETMASK, &old, NULL);
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return err;
1118c2ecf20Sopenharmony_cierror:
1128c2ecf20Sopenharmony_ci	return -errno;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * UML SIGWINCH handling
1178c2ecf20Sopenharmony_ci *
1188c2ecf20Sopenharmony_ci * The point of this is to handle SIGWINCH on consoles which have host
1198c2ecf20Sopenharmony_ci * ttys and relay them inside UML to whatever might be running on the
1208c2ecf20Sopenharmony_ci * console and cares about the window size (since SIGWINCH notifies
1218c2ecf20Sopenharmony_ci * about terminal size changes).
1228c2ecf20Sopenharmony_ci *
1238c2ecf20Sopenharmony_ci * So, we have a separate thread for each host tty attached to a UML
1248c2ecf20Sopenharmony_ci * device (side-issue - I'm annoyed that one thread can't have
1258c2ecf20Sopenharmony_ci * multiple controlling ttys for the purpose of handling SIGWINCH, but
1268c2ecf20Sopenharmony_ci * I imagine there are other reasons that doesn't make any sense).
1278c2ecf20Sopenharmony_ci *
1288c2ecf20Sopenharmony_ci * SIGWINCH can't be received synchronously, so you have to set up to
1298c2ecf20Sopenharmony_ci * receive it as a signal.  That being the case, if you are going to
1308c2ecf20Sopenharmony_ci * wait for it, it is convenient to sit in sigsuspend() and wait for
1318c2ecf20Sopenharmony_ci * the signal to bounce you out of it (see below for how we make sure
1328c2ecf20Sopenharmony_ci * to exit only on SIGWINCH).
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic void winch_handler(int sig)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistruct winch_data {
1408c2ecf20Sopenharmony_ci	int pty_fd;
1418c2ecf20Sopenharmony_ci	int pipe_fd;
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int winch_thread(void *arg)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct winch_data *data = arg;
1478c2ecf20Sopenharmony_ci	sigset_t sigs;
1488c2ecf20Sopenharmony_ci	int pty_fd, pipe_fd;
1498c2ecf20Sopenharmony_ci	int count;
1508c2ecf20Sopenharmony_ci	char c = 1;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	pty_fd = data->pty_fd;
1538c2ecf20Sopenharmony_ci	pipe_fd = data->pipe_fd;
1548c2ecf20Sopenharmony_ci	count = write(pipe_fd, &c, sizeof(c));
1558c2ecf20Sopenharmony_ci	if (count != sizeof(c))
1568c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : failed to write "
1578c2ecf20Sopenharmony_ci		       "synchronization byte, err = %d\n", -count);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/*
1608c2ecf20Sopenharmony_ci	 * We are not using SIG_IGN on purpose, so don't fix it as I thought to
1618c2ecf20Sopenharmony_ci	 * do! If using SIG_IGN, the sigsuspend() call below would not stop on
1628c2ecf20Sopenharmony_ci	 * SIGWINCH.
1638c2ecf20Sopenharmony_ci	 */
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	signal(SIGWINCH, winch_handler);
1668c2ecf20Sopenharmony_ci	sigfillset(&sigs);
1678c2ecf20Sopenharmony_ci	/* Block all signals possible. */
1688c2ecf20Sopenharmony_ci	if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
1698c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
1708c2ecf20Sopenharmony_ci		       "errno = %d\n", errno);
1718c2ecf20Sopenharmony_ci		exit(1);
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	/* In sigsuspend(), block anything else than SIGWINCH. */
1748c2ecf20Sopenharmony_ci	sigdelset(&sigs, SIGWINCH);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (setsid() < 0) {
1778c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d\n",
1788c2ecf20Sopenharmony_ci		       errno);
1798c2ecf20Sopenharmony_ci		exit(1);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
1838c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
1848c2ecf20Sopenharmony_ci		       "fd %d err = %d\n", pty_fd, errno);
1858c2ecf20Sopenharmony_ci		exit(1);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
1898c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
1908c2ecf20Sopenharmony_ci		       "fd %d err = %d\n", pty_fd, errno);
1918c2ecf20Sopenharmony_ci		exit(1);
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/*
1958c2ecf20Sopenharmony_ci	 * These are synchronization calls between various UML threads on the
1968c2ecf20Sopenharmony_ci	 * host - since they are not different kernel threads, we cannot use
1978c2ecf20Sopenharmony_ci	 * kernel semaphores. We don't use SysV semaphores because they are
1988c2ecf20Sopenharmony_ci	 * persistent.
1998c2ecf20Sopenharmony_ci	 */
2008c2ecf20Sopenharmony_ci	count = read(pipe_fd, &c, sizeof(c));
2018c2ecf20Sopenharmony_ci	if (count != sizeof(c))
2028c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : failed to read "
2038c2ecf20Sopenharmony_ci		       "synchronization byte, err = %d\n", errno);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	while(1) {
2068c2ecf20Sopenharmony_ci		/*
2078c2ecf20Sopenharmony_ci		 * This will be interrupted by SIGWINCH only, since
2088c2ecf20Sopenharmony_ci		 * other signals are blocked.
2098c2ecf20Sopenharmony_ci		 */
2108c2ecf20Sopenharmony_ci		sigsuspend(&sigs);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		count = write(pipe_fd, &c, sizeof(c));
2138c2ecf20Sopenharmony_ci		if (count != sizeof(c))
2148c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "winch_thread : write failed, "
2158c2ecf20Sopenharmony_ci			       "err = %d\n", errno);
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int winch_tramp(int fd, struct tty_port *port, int *fd_out,
2208c2ecf20Sopenharmony_ci		       unsigned long *stack_out)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct winch_data data;
2238c2ecf20Sopenharmony_ci	int fds[2], n, err, pid;
2248c2ecf20Sopenharmony_ci	char c;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	err = os_pipe(fds, 1, 1);
2278c2ecf20Sopenharmony_ci	if (err < 0) {
2288c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n",
2298c2ecf20Sopenharmony_ci		       -err);
2308c2ecf20Sopenharmony_ci		goto out;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	data = ((struct winch_data) { .pty_fd 		= fd,
2348c2ecf20Sopenharmony_ci				      .pipe_fd 		= fds[1] } );
2358c2ecf20Sopenharmony_ci	/*
2368c2ecf20Sopenharmony_ci	 * CLONE_FILES so this thread doesn't hold open files which are open
2378c2ecf20Sopenharmony_ci	 * now, but later closed in a different thread.  This is a
2388c2ecf20Sopenharmony_ci	 * problem with /dev/net/tun, which if held open by this
2398c2ecf20Sopenharmony_ci	 * thread, prevents the TUN/TAP device from being reused.
2408c2ecf20Sopenharmony_ci	 */
2418c2ecf20Sopenharmony_ci	pid = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out);
2428c2ecf20Sopenharmony_ci	if (pid < 0) {
2438c2ecf20Sopenharmony_ci		err = pid;
2448c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n",
2458c2ecf20Sopenharmony_ci		       -err);
2468c2ecf20Sopenharmony_ci		goto out_close;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	*fd_out = fds[0];
2508c2ecf20Sopenharmony_ci	n = read(fds[0], &c, sizeof(c));
2518c2ecf20Sopenharmony_ci	if (n != sizeof(c)) {
2528c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_tramp : failed to read "
2538c2ecf20Sopenharmony_ci		       "synchronization byte\n");
2548c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "read failed, err = %d\n", errno);
2558c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd);
2568c2ecf20Sopenharmony_ci		err = -EINVAL;
2578c2ecf20Sopenharmony_ci		goto out_close;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	err = os_set_fd_block(*fd_out, 0);
2618c2ecf20Sopenharmony_ci	if (err) {
2628c2ecf20Sopenharmony_ci		printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd "
2638c2ecf20Sopenharmony_ci		       "non-blocking.\n");
2648c2ecf20Sopenharmony_ci		goto out_close;
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	return pid;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci out_close:
2708c2ecf20Sopenharmony_ci	close(fds[1]);
2718c2ecf20Sopenharmony_ci	close(fds[0]);
2728c2ecf20Sopenharmony_ci out:
2738c2ecf20Sopenharmony_ci	return err;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_civoid register_winch(int fd, struct tty_port *port)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	unsigned long stack;
2798c2ecf20Sopenharmony_ci	int pid, thread, count, thread_fd = -1;
2808c2ecf20Sopenharmony_ci	char c = 1;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (!isatty(fd))
2838c2ecf20Sopenharmony_ci		return;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	pid = tcgetpgrp(fd);
2868c2ecf20Sopenharmony_ci	if (is_skas_winch(pid, fd, port)) {
2878c2ecf20Sopenharmony_ci		register_winch_irq(-1, fd, -1, port, 0);
2888c2ecf20Sopenharmony_ci		return;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (pid == -1) {
2928c2ecf20Sopenharmony_ci		thread = winch_tramp(fd, port, &thread_fd, &stack);
2938c2ecf20Sopenharmony_ci		if (thread < 0)
2948c2ecf20Sopenharmony_ci			return;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		register_winch_irq(thread_fd, fd, thread, port, stack);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		count = write(thread_fd, &c, sizeof(c));
2998c2ecf20Sopenharmony_ci		if (count != sizeof(c))
3008c2ecf20Sopenharmony_ci			printk(UM_KERN_ERR "register_winch : failed to write "
3018c2ecf20Sopenharmony_ci			       "synchronization byte, err = %d\n", errno);
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci}
304