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