LCOV - code coverage report
Current view: top level - arch/um/drivers - chan_user.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 7 111 6.3 %
Date: 2023-03-27 20:00:47 Functions: 2 10 20.0 %

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

Generated by: LCOV version 1.14