linux/arch/um/drivers/chan_user.c
<<
>>
Prefs
   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
  19void generic_close(int fd, void *unused)
  20{
  21        close(fd);
  22}
  23
  24int 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
  40int 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
  54int 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
  71void generic_free(void *data)
  72{
  73        kfree(data);
  74}
  75
  76int 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;
 113error:
 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
 137static void winch_handler(int sig)
 138{
 139}
 140
 141struct winch_data {
 142        int pty_fd;
 143        int pipe_fd;
 144};
 145
 146static 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
 221static 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
 276void 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