1/*	$OpenBSD: sigio.c,v 1.5 2020/01/08 16:27:40 visa Exp $	*/
2
3/*
4 * Copyright (c) 2018 Anton Lindqvist <anton@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/ioctl.h>
20#include <sys/time.h>
21#include <sys/wait.h>
22
23#include <dev/wscons/wsconsio.h>
24
25#include <err.h>
26#include <fcntl.h>
27#include <signal.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "util.h"
33
34static int test_getown_fcntl(int);
35static int test_getown_ioctl(int);
36static int test_gpgrp(int);
37static int test_setown_fcntl(int);
38static int test_setown_ioctl(int);
39static int test_sigio(int);
40static int test_spgrp(int);
41
42static int test_common_getown(int, int);
43static int test_common_setown(int, int);
44
45static void sigio(int);
46static void syncrecv(int, int);
47static void syncsend(int, int);
48
49static volatile sig_atomic_t nsigio;
50
51static int
52test_getown_fcntl(int fd)
53{
54	return test_common_getown(fd, 1);
55}
56
57static int
58test_getown_ioctl(int fd)
59{
60	return test_common_getown(fd, 0);
61}
62
63static int
64test_gpgrp(int fd)
65{
66	int arg, pgrp;
67
68	if (ioctl(fd, TIOCGPGRP, &pgrp) == -1)
69		err(1, "ioctl: TIOCGPGRP");
70	if (pgrp != 0)
71		errx(1, "ioctl: TIOCGPGRP: expected 0, got %d", pgrp);
72
73	arg = getpgrp();
74	if (ioctl(fd, TIOCSPGRP, &arg) == -1)
75		err(1, "ioctl: TIOCSPGRP");
76	if (ioctl(fd, TIOCGPGRP, &pgrp) == -1)
77		err(1, "ioctl: TIOCGPGRP");
78	if (pgrp != getpgrp())
79		errx(1, "ioctl: TIOCGPGRP: expected %d, got %d", getpgrp(), pgrp);
80
81	return 0;
82}
83
84static int
85test_setown_fcntl(int fd)
86{
87	return test_common_setown(fd, 1);
88}
89
90static int
91test_setown_ioctl(int fd)
92{
93	return test_common_setown(fd, 0);
94}
95
96static int
97test_sigio(int fd)
98{
99	struct wscons_event ev;
100	int cfd[2], pfd[2];
101	ssize_t n;
102	pid_t pid;
103	int arg, len, status;
104
105	if (pipe(cfd) == -1)
106		err(1, "pipe");
107	if (pipe(pfd) == -1)
108		err(1, "pipe");
109
110	arg = getpid();
111	if (ioctl(fd, FIOSETOWN, &arg) == -1)
112		err(1, "ioctl: FIOSETOWN");
113
114	/* Enable async IO. */
115	arg = 1;
116	if (ioctl(fd, FIOASYNC, &arg) == -1)
117		err(1, "ioctl: FIOASYNC");
118
119	pid = fork();
120	if (pid == -1)
121		err(1, "fork");
122	if (pid == 0) {
123		close(cfd[1]);
124		close(pfd[0]);
125
126		syncsend(pfd[1], 1);
127		syncrecv(cfd[0], 2);
128
129		memset(&ev, 0, sizeof(ev));
130		if (ioctl(fd, WSMUXIO_INJECTEVENT, &ev) == -1)
131			err(1, "ioctl: WSMUXIO_INJECTEVENT");
132
133		close(cfd[0]);
134		close(pfd[1]);
135		_exit(0);
136	}
137	close(cfd[0]);
138	close(pfd[1]);
139
140	syncrecv(pfd[0], 1);
141
142	if (signal(SIGIO, sigio) == SIG_ERR)
143		err(1, "signal");
144
145	syncsend(cfd[1], 2);
146
147	if (waitpid(pid, &status, 0) == -1)
148		err(1, "waitpid");
149	if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
150		errx(1, "child exited %d", WEXITSTATUS(status));
151	if (WIFSIGNALED(status))
152                errx(1, "child killed by signal %d", WTERMSIG(status));
153
154	if (nsigio != 1)
155                errx(1, "expected SIGIO to be received once, got %d", nsigio);
156
157	len = sizeof(ev);
158	n = read(fd, &ev, len);
159	if (n == -1)
160		err(1, "read");
161	if (n != len)
162		errx(1, "read: expected %d bytes, got %ld", len, n);
163
164	/* Disable async IO. */
165	arg = 0;
166	if (ioctl(fd, FIOASYNC, &arg) == -1)
167		err(1, "ioctl: FIOASYNC");
168
169	return 0;
170}
171
172static int
173test_spgrp(int fd)
174{
175	int arg;
176
177	/* The process group must be able to receive SIGIO. */
178	arg = getpgrp();
179	if (ioctl(fd, TIOCSPGRP, &arg) == -1)
180		errx(1, "ioctl: TIOCSPGRP");
181
182	/* Bogus process groups must be rejected. */
183	arg = -getpgrp();
184	if (ioctl(fd, TIOCSPGRP, &arg) != -1)
185		errx(1, "ioctl: TIOCSPGRP: %d accepted", arg);
186	arg = 1000000;
187	if (ioctl(fd, TIOCSPGRP, &arg) != -1)
188		errx(1, "ioctl: TIOCSPGRP: %d accepted", arg);
189
190	return 0;
191}
192
193static int
194test_common_getown(int fd, int dofcntl)
195{
196	int arg, pgrp;
197
198	if (dofcntl) {
199		pgrp = fcntl(fd, F_GETOWN);
200		if (pgrp == -1)
201			err(1, "fcntl: F_GETOWN");
202		if (pgrp != 0)
203			errx(1, "fcntl: F_GETOWN: expected 0, got %d", pgrp);
204	} else {
205		if (ioctl(fd, FIOGETOWN, &pgrp) == -1)
206			err(1, "ioctl: FIOGETOWN");
207		if (pgrp != 0)
208			errx(1, "ioctl: FIOGETOWN: expected 0, got %d", pgrp);
209	}
210
211	arg = -getpgrp();
212	if (ioctl(fd, FIOSETOWN, &arg) == -1)
213		err(1, "ioctl: FIOSETOWN");
214	if (dofcntl) {
215		pgrp = fcntl(fd, F_GETOWN);
216		if (pgrp == -1)
217			err(1, "fcntl: F_GETOWN");
218		if (pgrp != -getpgrp())
219			errx(1, "fcntl: F_GETOWN: expected %d, got %d",
220			    -getpgrp(), pgrp);
221	} else {
222		if (ioctl(fd, FIOGETOWN, &pgrp) == -1)
223			err(1, "ioctl: FIOGETOWN");
224		if (pgrp != -getpgrp())
225			errx(1, "ioctl: FIOGETOWN: expected %d, got %d",
226			    -getpgrp(), pgrp);
227	}
228
229	return 0;
230}
231
232static int
233test_common_setown(int fd, int dofcntl)
234{
235	int arg;
236
237        /* The process must be able to receive SIGIO. */
238	arg = getpid();
239	if (dofcntl) {
240		if (fcntl(fd, F_SETOWN, arg) == -1)
241			errx(1, "fcntl: F_SETOWN: process rejected");
242	} else {
243		if (ioctl(fd, FIOSETOWN, &arg) == -1)
244			errx(1, "ioctl: FIOSETOWN: process rejected");
245	}
246
247	/* The process group must be able to receive SIGIO. */
248	arg = -getpgrp();
249	if (dofcntl) {
250		if (fcntl(fd, F_SETOWN, arg) == -1)
251			errx(1, "fcntl: F_SETOWN: process group rejected");
252	} else {
253		if (ioctl(fd, FIOSETOWN, &arg) == -1)
254			errx(1, "ioctl: FIOSETOWN: process group rejected");
255	}
256
257	/* A bogus process must be rejected. */
258	arg = 1000000;
259	if (dofcntl) {
260		if (fcntl(fd, F_SETOWN, arg) != -1)
261			errx(1, "fcntl: F_SETOWN: bogus process accepted");
262	} else {
263		if (ioctl(fd, FIOSETOWN, &arg) != -1)
264			errx(1, "ioctl: FIOSETOWN: bogus process accepted");
265	}
266
267	/* A bogus process group must be rejected. */
268	arg = -1000000;
269	if (dofcntl) {
270		if (fcntl(fd, F_SETOWN, arg) != -1)
271			errx(1, "fcntl: F_SETOWN: bogus process group accepted");
272	} else {
273		if (ioctl(fd, FIOSETOWN, &arg) != -1)
274			errx(1, "ioctl: FIOSETOWN: bogus process group accepted");
275	}
276
277	return 0;
278}
279
280static void
281sigio(int signo)
282{
283	nsigio++;
284}
285
286static void
287syncrecv(int fd, int id)
288{
289	int r;
290
291	if (read(fd, &r, sizeof(r)) == -1)
292		err(1, "%s: read", __func__);
293	if (r != id)
294		errx(1, "%s: expected %d, got %d", __func__, id, r);
295}
296
297static void
298syncsend(int fd, int id)
299{
300	if (write(fd, &id, sizeof(id)) == -1)
301		err(1, "%s: write", __func__);
302}
303
304int
305main(int argc, char *argv[])
306{
307	struct test tests[] = {
308		{ "getown-fcntl",	test_getown_fcntl },
309		{ "getown-ioctl",	test_getown_ioctl },
310		{ "gpgrp",		test_gpgrp },
311		{ "setown-fcntl",	test_setown_fcntl },
312		{ "setown-ioctl",	test_setown_ioctl },
313		{ "sigio",		test_sigio },
314		{ "spgrp",		test_spgrp },
315		{ NULL,			NULL },
316	};
317
318	return dotest(argc, argv, tests);
319}
320