162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com)
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <stdlib.h>
762306a36Sopenharmony_ci#include <unistd.h>
862306a36Sopenharmony_ci#include <errno.h>
962306a36Sopenharmony_ci#include <sched.h>
1062306a36Sopenharmony_ci#include <signal.h>
1162306a36Sopenharmony_ci#include <termios.h>
1262306a36Sopenharmony_ci#include <sys/ioctl.h>
1362306a36Sopenharmony_ci#include "chan_user.h"
1462306a36Sopenharmony_ci#include <os.h>
1562306a36Sopenharmony_ci#include <um_malloc.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_civoid generic_close(int fd, void *unused)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	close(fd);
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciint generic_read(int fd, char *c_out, void *unused)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	int n;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	n = read(fd, c_out, sizeof(*c_out));
2762306a36Sopenharmony_ci	if (n > 0)
2862306a36Sopenharmony_ci		return n;
2962306a36Sopenharmony_ci	else if (n == 0)
3062306a36Sopenharmony_ci		return -EIO;
3162306a36Sopenharmony_ci	else if (errno == EAGAIN)
3262306a36Sopenharmony_ci		return 0;
3362306a36Sopenharmony_ci	return -errno;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* XXX Trivial wrapper around write */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciint generic_write(int fd, const char *buf, int n, void *unused)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	int err;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	err = write(fd, buf, n);
4362306a36Sopenharmony_ci	if (err > 0)
4462306a36Sopenharmony_ci		return err;
4562306a36Sopenharmony_ci	else if (errno == EAGAIN)
4662306a36Sopenharmony_ci		return 0;
4762306a36Sopenharmony_ci	else if (err == 0)
4862306a36Sopenharmony_ci		return -EIO;
4962306a36Sopenharmony_ci	return -errno;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciint generic_window_size(int fd, void *unused, unsigned short *rows_out,
5362306a36Sopenharmony_ci			unsigned short *cols_out)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	struct winsize size;
5662306a36Sopenharmony_ci	int ret;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (ioctl(fd, TIOCGWINSZ, &size) < 0)
5962306a36Sopenharmony_ci		return -errno;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col));
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	*rows_out = size.ws_row;
6462306a36Sopenharmony_ci	*cols_out = size.ws_col;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid generic_free(void *data)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	kfree(data);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciint generic_console_write(int fd, const char *buf, int n)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	sigset_t old, no_sigio;
7762306a36Sopenharmony_ci	struct termios save, new;
7862306a36Sopenharmony_ci	int err;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (isatty(fd)) {
8162306a36Sopenharmony_ci		sigemptyset(&no_sigio);
8262306a36Sopenharmony_ci		sigaddset(&no_sigio, SIGIO);
8362306a36Sopenharmony_ci		if (sigprocmask(SIG_BLOCK, &no_sigio, &old))
8462306a36Sopenharmony_ci			goto error;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		CATCH_EINTR(err = tcgetattr(fd, &save));
8762306a36Sopenharmony_ci		if (err)
8862306a36Sopenharmony_ci			goto error;
8962306a36Sopenharmony_ci		new = save;
9062306a36Sopenharmony_ci		/*
9162306a36Sopenharmony_ci		 * The terminal becomes a bit less raw, to handle \n also as
9262306a36Sopenharmony_ci		 * "Carriage Return", not only as "New Line". Otherwise, the new
9362306a36Sopenharmony_ci		 * line won't start at the first column.
9462306a36Sopenharmony_ci		 */
9562306a36Sopenharmony_ci		new.c_oflag |= OPOST;
9662306a36Sopenharmony_ci		CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new));
9762306a36Sopenharmony_ci		if (err)
9862306a36Sopenharmony_ci			goto error;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci	err = generic_write(fd, buf, n, NULL);
10162306a36Sopenharmony_ci	/*
10262306a36Sopenharmony_ci	 * Restore raw mode, in any case; we *must* ignore any error apart
10362306a36Sopenharmony_ci	 * EINTR, except for debug.
10462306a36Sopenharmony_ci	 */
10562306a36Sopenharmony_ci	if (isatty(fd)) {
10662306a36Sopenharmony_ci		CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save));
10762306a36Sopenharmony_ci		sigprocmask(SIG_SETMASK, &old, NULL);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return err;
11162306a36Sopenharmony_cierror:
11262306a36Sopenharmony_ci	return -errno;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * UML SIGWINCH handling
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * The point of this is to handle SIGWINCH on consoles which have host
11962306a36Sopenharmony_ci * ttys and relay them inside UML to whatever might be running on the
12062306a36Sopenharmony_ci * console and cares about the window size (since SIGWINCH notifies
12162306a36Sopenharmony_ci * about terminal size changes).
12262306a36Sopenharmony_ci *
12362306a36Sopenharmony_ci * So, we have a separate thread for each host tty attached to a UML
12462306a36Sopenharmony_ci * device (side-issue - I'm annoyed that one thread can't have
12562306a36Sopenharmony_ci * multiple controlling ttys for the purpose of handling SIGWINCH, but
12662306a36Sopenharmony_ci * I imagine there are other reasons that doesn't make any sense).
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * SIGWINCH can't be received synchronously, so you have to set up to
12962306a36Sopenharmony_ci * receive it as a signal.  That being the case, if you are going to
13062306a36Sopenharmony_ci * wait for it, it is convenient to sit in sigsuspend() and wait for
13162306a36Sopenharmony_ci * the signal to bounce you out of it (see below for how we make sure
13262306a36Sopenharmony_ci * to exit only on SIGWINCH).
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void winch_handler(int sig)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistruct winch_data {
14062306a36Sopenharmony_ci	int pty_fd;
14162306a36Sopenharmony_ci	int pipe_fd;
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int winch_thread(void *arg)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct winch_data *data = arg;
14762306a36Sopenharmony_ci	sigset_t sigs;
14862306a36Sopenharmony_ci	int pty_fd, pipe_fd;
14962306a36Sopenharmony_ci	int count;
15062306a36Sopenharmony_ci	char c = 1;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	pty_fd = data->pty_fd;
15362306a36Sopenharmony_ci	pipe_fd = data->pipe_fd;
15462306a36Sopenharmony_ci	count = write(pipe_fd, &c, sizeof(c));
15562306a36Sopenharmony_ci	if (count != sizeof(c))
15662306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : failed to write "
15762306a36Sopenharmony_ci		       "synchronization byte, err = %d\n", -count);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/*
16062306a36Sopenharmony_ci	 * We are not using SIG_IGN on purpose, so don't fix it as I thought to
16162306a36Sopenharmony_ci	 * do! If using SIG_IGN, the sigsuspend() call below would not stop on
16262306a36Sopenharmony_ci	 * SIGWINCH.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	signal(SIGWINCH, winch_handler);
16662306a36Sopenharmony_ci	sigfillset(&sigs);
16762306a36Sopenharmony_ci	/* Block all signals possible. */
16862306a36Sopenharmony_ci	if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) {
16962306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : sigprocmask failed, "
17062306a36Sopenharmony_ci		       "errno = %d\n", errno);
17162306a36Sopenharmony_ci		exit(1);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	/* In sigsuspend(), block anything else than SIGWINCH. */
17462306a36Sopenharmony_ci	sigdelset(&sigs, SIGWINCH);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (setsid() < 0) {
17762306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : setsid failed, errno = %d\n",
17862306a36Sopenharmony_ci		       errno);
17962306a36Sopenharmony_ci		exit(1);
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) {
18362306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : TIOCSCTTY failed on "
18462306a36Sopenharmony_ci		       "fd %d err = %d\n", pty_fd, errno);
18562306a36Sopenharmony_ci		exit(1);
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (tcsetpgrp(pty_fd, os_getpid()) < 0) {
18962306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : tcsetpgrp failed on "
19062306a36Sopenharmony_ci		       "fd %d err = %d\n", pty_fd, errno);
19162306a36Sopenharmony_ci		exit(1);
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/*
19562306a36Sopenharmony_ci	 * These are synchronization calls between various UML threads on the
19662306a36Sopenharmony_ci	 * host - since they are not different kernel threads, we cannot use
19762306a36Sopenharmony_ci	 * kernel semaphores. We don't use SysV semaphores because they are
19862306a36Sopenharmony_ci	 * persistent.
19962306a36Sopenharmony_ci	 */
20062306a36Sopenharmony_ci	count = read(pipe_fd, &c, sizeof(c));
20162306a36Sopenharmony_ci	if (count != sizeof(c))
20262306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_thread : failed to read "
20362306a36Sopenharmony_ci		       "synchronization byte, err = %d\n", errno);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	while(1) {
20662306a36Sopenharmony_ci		/*
20762306a36Sopenharmony_ci		 * This will be interrupted by SIGWINCH only, since
20862306a36Sopenharmony_ci		 * other signals are blocked.
20962306a36Sopenharmony_ci		 */
21062306a36Sopenharmony_ci		sigsuspend(&sigs);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		count = write(pipe_fd, &c, sizeof(c));
21362306a36Sopenharmony_ci		if (count != sizeof(c))
21462306a36Sopenharmony_ci			printk(UM_KERN_ERR "winch_thread : write failed, "
21562306a36Sopenharmony_ci			       "err = %d\n", errno);
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int winch_tramp(int fd, struct tty_port *port, int *fd_out,
22062306a36Sopenharmony_ci		       unsigned long *stack_out)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct winch_data data;
22362306a36Sopenharmony_ci	int fds[2], n, err, pid;
22462306a36Sopenharmony_ci	char c;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	err = os_pipe(fds, 1, 1);
22762306a36Sopenharmony_ci	if (err < 0) {
22862306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n",
22962306a36Sopenharmony_ci		       -err);
23062306a36Sopenharmony_ci		goto out;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	data = ((struct winch_data) { .pty_fd 		= fd,
23462306a36Sopenharmony_ci				      .pipe_fd 		= fds[1] } );
23562306a36Sopenharmony_ci	/*
23662306a36Sopenharmony_ci	 * CLONE_FILES so this thread doesn't hold open files which are open
23762306a36Sopenharmony_ci	 * now, but later closed in a different thread.  This is a
23862306a36Sopenharmony_ci	 * problem with /dev/net/tun, which if held open by this
23962306a36Sopenharmony_ci	 * thread, prevents the TUN/TAP device from being reused.
24062306a36Sopenharmony_ci	 */
24162306a36Sopenharmony_ci	pid = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out);
24262306a36Sopenharmony_ci	if (pid < 0) {
24362306a36Sopenharmony_ci		err = pid;
24462306a36Sopenharmony_ci		printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n",
24562306a36Sopenharmony_ci		       -err);
24662306a36Sopenharmony_ci		goto out_close;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	*fd_out = fds[0];
25062306a36Sopenharmony_ci	n = read(fds[0], &c, sizeof(c));
25162306a36Sopenharmony_ci	if (n != sizeof(c)) {
25262306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_tramp : failed to read "
25362306a36Sopenharmony_ci		       "synchronization byte\n");
25462306a36Sopenharmony_ci		printk(UM_KERN_ERR "read failed, err = %d\n", errno);
25562306a36Sopenharmony_ci		printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd);
25662306a36Sopenharmony_ci		err = -EINVAL;
25762306a36Sopenharmony_ci		goto out_close;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	err = os_set_fd_block(*fd_out, 0);
26162306a36Sopenharmony_ci	if (err) {
26262306a36Sopenharmony_ci		printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd "
26362306a36Sopenharmony_ci		       "non-blocking.\n");
26462306a36Sopenharmony_ci		goto out_close;
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return pid;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci out_close:
27062306a36Sopenharmony_ci	close(fds[1]);
27162306a36Sopenharmony_ci	close(fds[0]);
27262306a36Sopenharmony_ci out:
27362306a36Sopenharmony_ci	return err;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_civoid register_winch(int fd, struct tty_port *port)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	unsigned long stack;
27962306a36Sopenharmony_ci	int pid, thread, count, thread_fd = -1;
28062306a36Sopenharmony_ci	char c = 1;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!isatty(fd))
28362306a36Sopenharmony_ci		return;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	pid = tcgetpgrp(fd);
28662306a36Sopenharmony_ci	if (is_skas_winch(pid, fd, port)) {
28762306a36Sopenharmony_ci		register_winch_irq(-1, fd, -1, port, 0);
28862306a36Sopenharmony_ci		return;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (pid == -1) {
29262306a36Sopenharmony_ci		thread = winch_tramp(fd, port, &thread_fd, &stack);
29362306a36Sopenharmony_ci		if (thread < 0)
29462306a36Sopenharmony_ci			return;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		register_winch_irq(thread_fd, fd, thread, port, stack);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		count = write(thread_fd, &c, sizeof(c));
29962306a36Sopenharmony_ci		if (count != sizeof(c))
30062306a36Sopenharmony_ci			printk(UM_KERN_ERR "register_winch : failed to write "
30162306a36Sopenharmony_ci			       "synchronization byte, err = %d\n", errno);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci}
304