1 /* 2 * Copyright (C) 2000 - 2003 Jeff Dike (jdike@addtoit.com) 3 * Licensed under the GPL 4 */ 5 6 #include <unistd.h> 7 #include <stdlib.h> 8 #include <errno.h> 9 #include <termios.h> 10 #include <string.h> 11 #include <signal.h> 12 #include <sched.h> 13 #include <sys/stat.h> 14 #include <sys/ioctl.h> 15 #include <sys/socket.h> 16 #include "kern_util.h" 17 #include "chan_user.h" 18 #include "user.h" 19 #include "os.h" 20 #include "choose-mode.h" 21 #include "mode.h" 22 23 int generic_console_write(int fd, const char *buf, int n) 24 { 25 struct termios save, new; 26 int err; 27 28 if(isatty(fd)){ 29 CATCH_EINTR(err = tcgetattr(fd, &save)); 30 if (err) 31 goto error; 32 new = save; 33 /* The terminal becomes a bit less raw, to handle \n also as 34 * "Carriage Return", not only as "New Line". Otherwise, the new 35 * line won't start at the first column.*/ 36 new.c_oflag |= OPOST; 37 CATCH_EINTR(err = tcsetattr(fd, TCSAFLUSH, &new)); 38 if (err) 39 goto error; 40 } 41 err = generic_write(fd, buf, n, NULL); 42 /* Restore raw mode, in any case; we *must* ignore any error apart 43 * EINTR, except for debug.*/ 44 if(isatty(fd)) 45 CATCH_EINTR(tcsetattr(fd, TCSAFLUSH, &save)); 46 return(err); 47 error: 48 return(-errno); 49 } 50 51 /* 52 * UML SIGWINCH handling 53 * 54 * The point of this is to handle SIGWINCH on consoles which have host 55 * ttys and relay them inside UML to whatever might be running on the 56 * console and cares about the window size (since SIGWINCH notifies 57 * about terminal size changes). 58 * 59 * So, we have a separate thread for each host tty attached to a UML 60 * device (side-issue - I'm annoyed that one thread can't have 61 * multiple controlling ttys for the purpose of handling SIGWINCH, but 62 * I imagine there are other reasons that doesn't make any sense). 63 * 64 * SIGWINCH can't be received synchronously, so you have to set up to 65 * receive it as a signal. That being the case, if you are going to 66 * wait for it, it is convenient to sit in sigsuspend() and wait for 67 * the signal to bounce you out of it (see below for how we make sure 68 * to exit only on SIGWINCH). 69 */ 70 71 static void winch_handler(int sig) 72 { 73 } 74 75 struct winch_data { 76 int pty_fd; 77 int pipe_fd; 78 }; 79 80 static int winch_thread(void *arg) 81 { 82 struct winch_data *data = arg; 83 sigset_t sigs; 84 int pty_fd, pipe_fd; 85 int count, err; 86 char c = 1; 87 88 pty_fd = data->pty_fd; 89 pipe_fd = data->pipe_fd; 90 count = os_write_file(pipe_fd, &c, sizeof(c)); 91 if(count != sizeof(c)) 92 printk("winch_thread : failed to write synchronization " 93 "byte, err = %d\n", -count); 94 95 /* We are not using SIG_IGN on purpose, so don't fix it as I thought to 96 * do! If using SIG_IGN, the sigsuspend() call below would not stop on 97 * SIGWINCH. */ 98 99 signal(SIGWINCH, winch_handler); 100 sigfillset(&sigs); 101 /* Block all signals possible. */ 102 if(sigprocmask(SIG_SETMASK, &sigs, NULL) < 0){ 103 printk("winch_thread : sigprocmask failed, errno = %d\n", 104 errno); 105 exit(1); 106 } 107 /* In sigsuspend(), block anything else than SIGWINCH. */ 108 sigdelset(&sigs, SIGWINCH); 109 110 if(setsid() < 0){ 111 printk("winch_thread : setsid failed, errno = %d\n", errno); 112 exit(1); 113 } 114 115 err = os_new_tty_pgrp(pty_fd, os_getpid()); 116 if(err < 0){ 117 printk("winch_thread : new_tty_pgrp failed on fd %d, " 118 "err = %d\n", pty_fd, -err); 119 exit(1); 120 } 121 122 /* These are synchronization calls between various UML threads on the 123 * host - since they are not different kernel threads, we cannot use 124 * kernel semaphores. We don't use SysV semaphores because they are 125 * persistent. */ 126 count = os_read_file(pipe_fd, &c, sizeof(c)); 127 if(count != sizeof(c)) 128 printk("winch_thread : failed to read synchronization byte, " 129 "err = %d\n", -count); 130 131 while(1){ 132 /* This will be interrupted by SIGWINCH only, since 133 * other signals are blocked. 134 */ 135 sigsuspend(&sigs); 136 137 count = os_write_file(pipe_fd, &c, sizeof(c)); 138 if(count != sizeof(c)) 139 printk("winch_thread : write failed, err = %d\n", 140 -count); 141 } 142 } 143 144 static int winch_tramp(int fd, struct tty_struct *tty, int *fd_out, 145 unsigned long *stack_out) 146 { 147 struct winch_data data; 148 int fds[2], n, err; 149 char c; 150 151 err = os_pipe(fds, 1, 1); 152 if(err < 0){ 153 printk("winch_tramp : os_pipe failed, err = %d\n", -err); 154 goto out; 155 } 156 157 data = ((struct winch_data) { .pty_fd = fd, 158 .pipe_fd = fds[1] } ); 159 /* CLONE_FILES so this thread doesn't hold open files which are open 160 * now, but later closed in a different thread. This is a 161 * problem with /dev/net/tun, which if held open by this 162 * thread, prevents the TUN/TAP device from being reused. 163 */ 164 err = run_helper_thread(winch_thread, &data, CLONE_FILES, stack_out); 165 if(err < 0){ 166 printk("fork of winch_thread failed - errno = %d\n", -err); 167 goto out_close; 168 } 169 170 *fd_out = fds[0]; 171 n = os_read_file(fds[0], &c, sizeof(c)); 172 if(n != sizeof(c)){ 173 printk("winch_tramp : failed to read synchronization byte\n"); 174 printk("read failed, err = %d\n", -n); 175 printk("fd %d will not support SIGWINCH\n", fd); 176 err = -EINVAL; 177 goto out_close; 178 } 179 180 if (os_set_fd_block(*fd_out, 0)) { 181 printk("winch_tramp: failed to set thread_fd non-blocking.\n"); 182 goto out_close; 183 } 184 185 return err; 186 187 out_close: 188 os_close_file(fds[1]); 189 os_close_file(fds[0]); 190 out: 191 return err; 192 } 193 194 void register_winch(int fd, struct tty_struct *tty) 195 { 196 unsigned long stack; 197 int pid, thread, count, thread_fd = -1; 198 char c = 1; 199 200 if(!isatty(fd)) 201 return; 202 203 pid = tcgetpgrp(fd); 204 if (!CHOOSE_MODE_PROC(is_tracer_winch, is_skas_winch, pid, fd, tty) && 205 (pid == -1)) { 206 thread = winch_tramp(fd, tty, &thread_fd, &stack); 207 if (thread < 0) 208 return; 209 210 register_winch_irq(thread_fd, fd, thread, tty, stack); 211 212 count = os_write_file(thread_fd, &c, sizeof(c)); 213 if(count != sizeof(c)) 214 printk("register_winch : failed to write " 215 "synchronization byte, err = %d\n", -count); 216 } 217 } 218