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