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