1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 4 */ 5 6 #include <stdlib.h> 7 #include <unistd.h> 8 #include <errno.h> 9 #include <sched.h> 10 #include <signal.h> 11 #include <termios.h> 12 #include <sys/ioctl.h> 13 #include "chan_user.h" 14 #include <os.h> 15 #include <um_malloc.h> 16 17 void generic_close(int fd, void *unused) 18 { 19 close(fd); 20 } 21 22 int generic_read(int fd, __u8 *c_out, void *unused) 23 { 24 int n; 25 26 CATCH_EINTR(n = read(fd, c_out, sizeof(*c_out))); 27 if (n > 0) 28 return n; 29 else if (n == 0) 30 return -EIO; 31 else if (errno == EAGAIN) 32 return 0; 33 return -errno; 34 } 35 36 /* XXX Trivial wrapper around write */ 37 38 int generic_write(int fd, const __u8 *buf, size_t n, void *unused) 39 { 40 int written = 0; 41 int err; 42 43 /* The FD may be in blocking mode, as such, need to retry short writes, 44 * they may have been interrupted by a signal. 45 */ 46 do { 47 errno = 0; 48 err = write(fd, buf + written, n - written); 49 if (err > 0) { 50 written += err; 51 continue; 52 } 53 } while (err < 0 && errno == EINTR); 54 55 if (written > 0) 56 return written; 57 else if (errno == EAGAIN) 58 return 0; 59 else if (err == 0) 60 return -EIO; 61 return -errno; 62 } 63 64 int generic_window_size(int fd, void *unused, unsigned short *rows_out, 65 unsigned short *cols_out) 66 { 67 struct winsize size; 68 int ret; 69 70 if (ioctl(fd, TIOCGWINSZ, &size) < 0) 71 return -errno; 72 73 ret = ((*rows_out != size.ws_row) || (*cols_out != size.ws_col)); 74 75 *rows_out = size.ws_row; 76 *cols_out = size.ws_col; 77 78 return ret; 79 } 80 81 void generic_free(void *data) 82 { 83 kfree(data); 84 } 85 86 int generic_console_write(int fd, const char *buf, int n) 87 { 88 sigset_t old, no_sigio; 89 struct termios save, new; 90 int err; 91 92 if (isatty(fd)) { 93 sigemptyset(&no_sigio); 94 sigaddset(&no_sigio, SIGIO); 95 if (sigprocmask(SIG_BLOCK, &no_sigio, &old)) 96 goto error; 97 98 CATCH_EINTR(err = tcgetattr(fd, &save)); 99 if (err) 100 goto error; 101 new = save; 102 /* 103 * The terminal becomes a bit less raw, to handle \n also as 104 * "Carriage Return", not only as "New Line". Otherwise, the new 105 * line won't start at the first column. 106 */ 107 new.c_oflag |= OPOST; 108 CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new)); 109 if (err) 110 goto error; 111 } 112 err = generic_write(fd, buf, n, NULL); 113 /* 114 * Restore raw mode, in any case; we *must* ignore any error apart 115 * EINTR, except for debug. 116 */ 117 if (isatty(fd)) { 118 CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save)); 119 sigprocmask(SIG_SETMASK, &old, NULL); 120 } 121 122 return err; 123 error: 124 return -errno; 125 } 126 127 /* 128 * UML SIGWINCH handling 129 * 130 * The point of this is to handle SIGWINCH on consoles which have host 131 * ttys and relay them inside UML to whatever might be running on the 132 * console and cares about the window size (since SIGWINCH notifies 133 * about terminal size changes). 134 * 135 * So, we have a separate thread for each host tty attached to a UML 136 * device (side-issue - I'm annoyed that one thread can't have 137 * multiple controlling ttys for the purpose of handling SIGWINCH, but 138 * I imagine there are other reasons that doesn't make any sense). 139 * 140 * SIGWINCH can't be received synchronously, so you have to set up to 141 * receive it as a signal. That being the case, if you are going to 142 * wait for it, it is convenient to sit in sigsuspend() and wait for 143 * the signal to bounce you out of it (see below for how we make sure 144 * to exit only on SIGWINCH). 145 */ 146 147 static void winch_handler(int sig) 148 { 149 } 150 151 struct winch_data { 152 int pty_fd; 153 int pipe_fd; 154 }; 155 156 static __noreturn int winch_thread(void *arg) 157 { 158 struct winch_data *data = arg; 159 sigset_t sigs; 160 int pty_fd, pipe_fd; 161 int count; 162 char c = 1; 163 164 pty_fd = data->pty_fd; 165 pipe_fd = data->pipe_fd; 166 count = write(pipe_fd, &c, sizeof(c)); 167 if (count != sizeof(c)) 168 os_info("winch_thread : failed to write synchronization byte, err = %d\n", 169 -count); 170 171 /* 172 * We are not using SIG_IGN on purpose, so don't fix it as I thought to 173 * do! If using SIG_IGN, the sigsuspend() call below would not stop on 174 * SIGWINCH. 175 */ 176 177 signal(SIGWINCH, winch_handler); 178 sigfillset(&sigs); 179 /* Block all signals possible. */ 180 if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) { 181 os_info("winch_thread : sigprocmask failed, errno = %d\n", 182 errno); 183 goto wait_kill; 184 } 185 /* In sigsuspend(), block anything else than SIGWINCH. */ 186 sigdelset(&sigs, SIGWINCH); 187 188 if (setsid() < 0) { 189 os_info("winch_thread : setsid failed, errno = %d\n", 190 errno); 191 goto wait_kill; 192 } 193 194 if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) { 195 os_info("winch_thread : TIOCSCTTY failed on " 196 "fd %d err = %d\n", pty_fd, errno); 197 goto wait_kill; 198 } 199 200 if (tcsetpgrp(pty_fd, os_getpid()) < 0) { 201 os_info("winch_thread : tcsetpgrp failed on fd %d err = %d\n", 202 pty_fd, errno); 203 goto wait_kill; 204 } 205 206 /* 207 * These are synchronization calls between various UML threads on the 208 * host - since they are not different kernel threads, we cannot use 209 * kernel semaphores. We don't use SysV semaphores because they are 210 * persistent. 211 */ 212 count = read(pipe_fd, &c, sizeof(c)); 213 if (count != sizeof(c)) 214 os_info("winch_thread : failed to read synchronization byte, err = %d\n", 215 errno); 216 217 while(1) { 218 /* 219 * This will be interrupted by SIGWINCH only, since 220 * other signals are blocked. 221 */ 222 sigsuspend(&sigs); 223 224 count = write(pipe_fd, &c, sizeof(c)); 225 if (count != sizeof(c)) 226 os_info("winch_thread : write failed, err = %d\n", 227 errno); 228 } 229 230 wait_kill: 231 c = 2; 232 count = write(pipe_fd, &c, sizeof(c)); 233 while (1) 234 pause(); 235 } 236 237 static int winch_tramp(int fd, struct tty_port *port, int *fd_out, 238 unsigned long *stack_out) 239 { 240 struct winch_data data; 241 int fds[2], n, err, pid; 242 char c; 243 244 err = os_pipe(fds, 1, 1); 245 if (err < 0) { 246 printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n", 247 -err); 248 goto out; 249 } 250 251 data = ((struct winch_data) { .pty_fd = fd, 252 .pipe_fd = fds[1] } ); 253 /* 254 * CLONE_FILES so this thread doesn't hold open files which are open 255 * now, but later closed in a different thread. This is a 256 * problem with /dev/net/tun, which if held open by this 257 * thread, prevents the TUN/TAP device from being reused. 258 */ 259 pid = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out); 260 if (pid < 0) { 261 err = pid; 262 printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n", 263 -err); 264 goto out_close; 265 } 266 267 *fd_out = fds[0]; 268 n = read(fds[0], &c, sizeof(c)); 269 if (n != sizeof(c)) { 270 printk(UM_KERN_ERR "winch_tramp : failed to read " 271 "synchronization byte\n"); 272 printk(UM_KERN_ERR "read failed, err = %d\n", errno); 273 printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd); 274 err = -EINVAL; 275 goto out_close; 276 } 277 278 err = os_set_fd_block(*fd_out, 0); 279 if (err) { 280 printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd " 281 "non-blocking.\n"); 282 goto out_close; 283 } 284 285 return pid; 286 287 out_close: 288 close(fds[1]); 289 close(fds[0]); 290 out: 291 return err; 292 } 293 294 void register_winch(int fd, struct tty_port *port) 295 { 296 unsigned long stack; 297 int pid, thread, count, thread_fd = -1; 298 char c = 1; 299 300 if (!isatty(fd)) 301 return; 302 303 pid = tcgetpgrp(fd); 304 if (is_skas_winch(pid, fd, port)) { 305 register_winch_irq(-1, fd, -1, port, 0); 306 return; 307 } 308 309 if (pid == -1) { 310 thread = winch_tramp(fd, port, &thread_fd, &stack); 311 if (thread < 0) 312 return; 313 314 register_winch_irq(thread_fd, fd, thread, port, stack); 315 316 count = write(thread_fd, &c, sizeof(c)); 317 if (count != sizeof(c)) 318 printk(UM_KERN_ERR "register_winch : failed to write " 319 "synchronization byte, err = %d\n", errno); 320 } 321 } 322