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