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 os_set_pdeathsig(); 165 166 pty_fd = data->pty_fd; 167 pipe_fd = data->pipe_fd; 168 count = write(pipe_fd, &c, sizeof(c)); 169 if (count != sizeof(c)) 170 os_info("winch_thread : failed to write synchronization byte, err = %d\n", 171 -count); 172 173 /* 174 * We are not using SIG_IGN on purpose, so don't fix it as I thought to 175 * do! If using SIG_IGN, the sigsuspend() call below would not stop on 176 * SIGWINCH. 177 */ 178 179 signal(SIGWINCH, winch_handler); 180 sigfillset(&sigs); 181 /* Block all signals possible. */ 182 if (sigprocmask(SIG_SETMASK, &sigs, NULL) < 0) { 183 os_info("winch_thread : sigprocmask failed, errno = %d\n", 184 errno); 185 goto wait_kill; 186 } 187 /* In sigsuspend(), block anything else than SIGWINCH. */ 188 sigdelset(&sigs, SIGWINCH); 189 190 if (setsid() < 0) { 191 os_info("winch_thread : setsid failed, errno = %d\n", 192 errno); 193 goto wait_kill; 194 } 195 196 if (ioctl(pty_fd, TIOCSCTTY, 0) < 0) { 197 os_info("winch_thread : TIOCSCTTY failed on " 198 "fd %d err = %d\n", pty_fd, errno); 199 goto wait_kill; 200 } 201 202 if (tcsetpgrp(pty_fd, os_getpid()) < 0) { 203 os_info("winch_thread : tcsetpgrp failed on fd %d err = %d\n", 204 pty_fd, errno); 205 goto wait_kill; 206 } 207 208 /* 209 * These are synchronization calls between various UML threads on the 210 * host - since they are not different kernel threads, we cannot use 211 * kernel semaphores. We don't use SysV semaphores because they are 212 * persistent. 213 */ 214 count = read(pipe_fd, &c, sizeof(c)); 215 if (count != sizeof(c)) 216 os_info("winch_thread : failed to read synchronization byte, err = %d\n", 217 errno); 218 219 while(1) { 220 /* 221 * This will be interrupted by SIGWINCH only, since 222 * other signals are blocked. 223 */ 224 sigsuspend(&sigs); 225 226 count = write(pipe_fd, &c, sizeof(c)); 227 if (count != sizeof(c)) 228 os_info("winch_thread : write failed, err = %d\n", 229 errno); 230 } 231 232 wait_kill: 233 c = 2; 234 count = write(pipe_fd, &c, sizeof(c)); 235 while (1) 236 pause(); 237 } 238 239 static int winch_tramp(int fd, struct tty_port *port, int *fd_out, 240 unsigned long *stack_out) 241 { 242 struct winch_data data; 243 int fds[2], n, err, pid; 244 char c; 245 246 err = os_pipe(fds, 1, 1); 247 if (err < 0) { 248 printk(UM_KERN_ERR "winch_tramp : os_pipe failed, err = %d\n", 249 -err); 250 goto out; 251 } 252 253 data = ((struct winch_data) { .pty_fd = fd, 254 .pipe_fd = fds[1] } ); 255 /* 256 * CLONE_FILES so this thread doesn't hold open files which are open 257 * now, but later closed in a different thread. This is a 258 * problem with /dev/net/tun, which if held open by this 259 * thread, prevents the TUN/TAP device from being reused. 260 */ 261 pid = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out); 262 if (pid < 0) { 263 err = pid; 264 printk(UM_KERN_ERR "fork of winch_thread failed - errno = %d\n", 265 -err); 266 goto out_close; 267 } 268 269 *fd_out = fds[0]; 270 n = read(fds[0], &c, sizeof(c)); 271 if (n != sizeof(c)) { 272 printk(UM_KERN_ERR "winch_tramp : failed to read " 273 "synchronization byte\n"); 274 printk(UM_KERN_ERR "read failed, err = %d\n", errno); 275 printk(UM_KERN_ERR "fd %d will not support SIGWINCH\n", fd); 276 err = -EINVAL; 277 goto out_close; 278 } 279 280 err = os_set_fd_block(*fd_out, 0); 281 if (err) { 282 printk(UM_KERN_ERR "winch_tramp: failed to set thread_fd " 283 "non-blocking.\n"); 284 goto out_close; 285 } 286 287 return pid; 288 289 out_close: 290 close(fds[1]); 291 close(fds[0]); 292 out: 293 return err; 294 } 295 296 void register_winch(int fd, struct tty_port *port) 297 { 298 unsigned long stack; 299 int pid, thread, count, thread_fd = -1; 300 char c = 1; 301 302 if (!isatty(fd)) 303 return; 304 305 pid = tcgetpgrp(fd); 306 if (is_skas_winch(pid, fd, port)) { 307 register_winch_irq(-1, fd, -1, port, 0); 308 return; 309 } 310 311 if (pid == -1) { 312 thread = winch_tramp(fd, port, &thread_fd, &stack); 313 if (thread < 0) 314 return; 315 316 register_winch_irq(thread_fd, fd, thread, port, stack); 317 318 count = write(thread_fd, &c, sizeof(c)); 319 if (count != sizeof(c)) 320 printk(UM_KERN_ERR "register_winch : failed to write " 321 "synchronization byte, err = %d\n", errno); 322 } 323 } 324