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