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