1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2002 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
4 */
5
6 #include <unistd.h>
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <poll.h>
10 #include <pty.h>
11 #include <sched.h>
12 #include <signal.h>
13 #include <string.h>
14 #include <sys/epoll.h>
15 #include <kern_util.h>
16 #include <init.h>
17 #include <os.h>
18 #include <sigio.h>
19 #include <um_malloc.h>
20
21 /*
22 * Protected by sigio_lock(), also used by sigio_cleanup, which is an
23 * exitcall.
24 */
25 static struct os_helper_thread *write_sigio_td;
26
27 static int epollfd = -1;
28
29 #define MAX_EPOLL_EVENTS 64
30
31 static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
32
write_sigio_thread(void * unused)33 static void *write_sigio_thread(void *unused)
34 {
35 int pid = getpid();
36 int r;
37
38 os_fix_helper_thread_signals();
39
40 while (1) {
41 r = epoll_wait(epollfd, epoll_events, MAX_EPOLL_EVENTS, -1);
42 if (r < 0) {
43 if (errno == EINTR)
44 continue;
45 printk(UM_KERN_ERR "%s: epoll_wait failed, errno = %d\n",
46 __func__, errno);
47 }
48
49 CATCH_EINTR(r = tgkill(pid, pid, SIGIO));
50 if (r < 0)
51 printk(UM_KERN_ERR "%s: tgkill failed, errno = %d\n",
52 __func__, errno);
53 }
54
55 return NULL;
56 }
57
__add_sigio_fd(int fd)58 int __add_sigio_fd(int fd)
59 {
60 struct epoll_event event = {
61 .data.fd = fd,
62 .events = EPOLLIN | EPOLLET,
63 };
64 int r;
65
66 CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event));
67 return r < 0 ? -errno : 0;
68 }
69
add_sigio_fd(int fd)70 int add_sigio_fd(int fd)
71 {
72 int err;
73
74 sigio_lock();
75 err = __add_sigio_fd(fd);
76 sigio_unlock();
77
78 return err;
79 }
80
__ignore_sigio_fd(int fd)81 int __ignore_sigio_fd(int fd)
82 {
83 struct epoll_event event;
84 int r;
85
86 CATCH_EINTR(r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event));
87 return r < 0 ? -errno : 0;
88 }
89
ignore_sigio_fd(int fd)90 int ignore_sigio_fd(int fd)
91 {
92 int err;
93
94 sigio_lock();
95 err = __ignore_sigio_fd(fd);
96 sigio_unlock();
97
98 return err;
99 }
100
write_sigio_workaround(void)101 static void write_sigio_workaround(void)
102 {
103 int err;
104
105 sigio_lock();
106 if (write_sigio_td)
107 goto out;
108
109 epollfd = epoll_create(MAX_EPOLL_EVENTS);
110 if (epollfd < 0) {
111 printk(UM_KERN_ERR "%s: epoll_create failed, errno = %d\n",
112 __func__, errno);
113 goto out;
114 }
115
116 err = os_run_helper_thread(&write_sigio_td, write_sigio_thread, NULL);
117 if (err < 0) {
118 printk(UM_KERN_ERR "%s: os_run_helper_thread failed, errno = %d\n",
119 __func__, -err);
120 close(epollfd);
121 epollfd = -1;
122 goto out;
123 }
124
125 out:
126 sigio_unlock();
127 }
128
sigio_broken(void)129 void sigio_broken(void)
130 {
131 write_sigio_workaround();
132 }
133
134 /* Changed during early boot */
135 static int pty_output_sigio;
136
maybe_sigio_broken(int fd)137 void maybe_sigio_broken(int fd)
138 {
139 if (!isatty(fd))
140 return;
141
142 if (pty_output_sigio)
143 return;
144
145 sigio_broken();
146 }
147
sigio_cleanup(void)148 static void sigio_cleanup(void)
149 {
150 if (!write_sigio_td)
151 return;
152
153 os_kill_helper_thread(write_sigio_td);
154 write_sigio_td = NULL;
155 }
156
157 __uml_exitcall(sigio_cleanup);
158
159 /* Used as a flag during SIGIO testing early in boot */
160 static int got_sigio;
161
handler(int sig)162 static void __init handler(int sig)
163 {
164 got_sigio = 1;
165 }
166
167 struct openpty_arg {
168 int master;
169 int slave;
170 int err;
171 };
172
openpty_cb(void * arg)173 static void openpty_cb(void *arg)
174 {
175 struct openpty_arg *info = arg;
176
177 info->err = 0;
178 if (openpty(&info->master, &info->slave, NULL, NULL, NULL))
179 info->err = -errno;
180 }
181
async_pty(int master,int slave)182 static int async_pty(int master, int slave)
183 {
184 int flags;
185
186 flags = fcntl(master, F_GETFL);
187 if (flags < 0)
188 return -errno;
189
190 if ((fcntl(master, F_SETFL, flags | O_NONBLOCK | O_ASYNC) < 0) ||
191 (fcntl(master, F_SETOWN, os_getpid()) < 0))
192 return -errno;
193
194 if ((fcntl(slave, F_SETFL, flags | O_NONBLOCK) < 0))
195 return -errno;
196
197 return 0;
198 }
199
check_one_sigio(void (* proc)(int,int))200 static void __init check_one_sigio(void (*proc)(int, int))
201 {
202 struct sigaction old, new;
203 struct openpty_arg pty = { .master = -1, .slave = -1 };
204 int master, slave, err;
205
206 initial_thread_cb(openpty_cb, &pty);
207 if (pty.err) {
208 printk(UM_KERN_ERR "check_one_sigio failed, errno = %d\n",
209 -pty.err);
210 return;
211 }
212
213 master = pty.master;
214 slave = pty.slave;
215
216 if ((master == -1) || (slave == -1)) {
217 printk(UM_KERN_ERR "check_one_sigio failed to allocate a "
218 "pty\n");
219 return;
220 }
221
222 /* Not now, but complain so we now where we failed. */
223 err = raw(master);
224 if (err < 0) {
225 printk(UM_KERN_ERR "check_one_sigio : raw failed, errno = %d\n",
226 -err);
227 return;
228 }
229
230 err = async_pty(master, slave);
231 if (err < 0) {
232 printk(UM_KERN_ERR "check_one_sigio : sigio_async failed, "
233 "err = %d\n", -err);
234 return;
235 }
236
237 if (sigaction(SIGIO, NULL, &old) < 0) {
238 printk(UM_KERN_ERR "check_one_sigio : sigaction 1 failed, "
239 "errno = %d\n", errno);
240 return;
241 }
242
243 new = old;
244 new.sa_handler = handler;
245 if (sigaction(SIGIO, &new, NULL) < 0) {
246 printk(UM_KERN_ERR "check_one_sigio : sigaction 2 failed, "
247 "errno = %d\n", errno);
248 return;
249 }
250
251 got_sigio = 0;
252 (*proc)(master, slave);
253
254 close(master);
255 close(slave);
256
257 if (sigaction(SIGIO, &old, NULL) < 0)
258 printk(UM_KERN_ERR "check_one_sigio : sigaction 3 failed, "
259 "errno = %d\n", errno);
260 }
261
tty_output(int master,int slave)262 static void tty_output(int master, int slave)
263 {
264 int n;
265 char buf[512];
266
267 printk(UM_KERN_INFO "Checking that host ptys support output SIGIO...");
268
269 memset(buf, 0, sizeof(buf));
270
271 while (write(master, buf, sizeof(buf)) > 0) ;
272 if (errno != EAGAIN)
273 printk(UM_KERN_ERR "tty_output : write failed, errno = %d\n",
274 errno);
275 while (((n = read(slave, buf, sizeof(buf))) > 0) &&
276 !({ barrier(); got_sigio; }))
277 ;
278
279 if (got_sigio) {
280 printk(UM_KERN_CONT "Yes\n");
281 pty_output_sigio = 1;
282 } else if (n == -EAGAIN)
283 printk(UM_KERN_CONT "No, enabling workaround\n");
284 else
285 printk(UM_KERN_CONT "tty_output : read failed, err = %d\n", n);
286 }
287
check_sigio(void)288 static void __init check_sigio(void)
289 {
290 if ((access("/dev/ptmx", R_OK) < 0) &&
291 (access("/dev/ptyp0", R_OK) < 0)) {
292 printk(UM_KERN_WARNING "No pseudo-terminals available - "
293 "skipping pty SIGIO check\n");
294 return;
295 }
296 check_one_sigio(tty_output);
297 }
298
299 /* Here because it only does the SIGIO testing for now */
os_check_bugs(void)300 void __init os_check_bugs(void)
301 {
302 check_sigio();
303 }
304