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