sigio.c revision 1.3
1/*	$OpenBSD: sigio.c,v 1.3 2018/11/20 19:37:10 anton 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 <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32
33static int test_getown_fcntl(int);
34static int test_getown_ioctl(int);
35static int test_gpgrp(int);
36static int test_setown_fcntl(int);
37static int test_setown_ioctl(int);
38static int test_sigio(int);
39static int test_spgrp(int);
40
41static int test_common_getown(int, int);
42static int test_common_setown(int, int);
43
44static void sigio(int);
45static void syncrecv(int, int);
46static void syncsend(int, int);
47static __dead void usage(void);
48
49static volatile sig_atomic_t nsigio;
50
51static struct {
52	const char *name;
53	int (*fn)(int);
54} tests[] = {
55	{ "getown-fcntl",	test_getown_fcntl },
56	{ "getown-ioctl",	test_getown_ioctl },
57	{ "gpgrp",		test_gpgrp },
58	{ "setown-fcntl",	test_setown_fcntl },
59	{ "setown-ioctl",	test_setown_ioctl },
60	{ "sigio",		test_sigio },
61	{ "spgrp",		test_spgrp },
62	{ NULL,	NULL },
63};
64
65int
66main(int argc, char *argv[])
67{
68	int (*fn)(int) = NULL;
69	const char *dev = NULL;
70	int c, fd, i;
71	int prereq = 0;
72
73	while ((c = getopt(argc, argv, "d:p")) != -1)
74		switch (c) {
75		case 'd':
76			dev = optarg;
77			break;
78		case 'p':
79			prereq = 1;
80			break;
81		default:
82			usage();
83		}
84	argc -= optind;
85	argv += optind;
86	if (dev == NULL || argc != 1)
87		usage();
88
89	fd = open(dev, O_RDWR);
90	if (fd == -1)
91		err(1, "open: %s", dev);
92	if (prereq)
93		return 0;
94
95	for (i = 0; tests[i].name != NULL; i++) {
96		if (strcmp(argv[0], tests[i].name))
97			continue;
98
99		fn = tests[i].fn;
100		break;
101	}
102	if (fn == NULL)
103		errx(1, "%s: no such test", argv[0]);
104
105	return fn(fd);
106}
107
108static int
109test_getown_fcntl(int fd)
110{
111	return test_common_getown(fd, 1);
112}
113
114static int
115test_getown_ioctl(int fd)
116{
117	return test_common_getown(fd, 0);
118}
119
120static int
121test_gpgrp(int fd)
122{
123	int arg, pgrp;
124
125	if (ioctl(fd, TIOCGPGRP, &pgrp) == -1)
126		err(1, "ioctl: TIOCGPGRP");
127	if (pgrp != 0)
128		errx(1, "ioctl: TIOCGPGRP: expected 0, got %d", pgrp);
129
130	arg = getpgrp();
131	if (ioctl(fd, TIOCSPGRP, &arg) == -1)
132		err(1, "ioctl: TIOCSPGRP");
133	if (ioctl(fd, TIOCGPGRP, &pgrp) == -1)
134		err(1, "ioctl: TIOCGPGRP");
135	if (pgrp != getpgrp())
136		errx(1, "ioctl: TIOCGPGRP: expected %d, got %d", getpgrp(), pgrp);
137
138	return 0;
139}
140
141static int
142test_setown_fcntl(int fd)
143{
144	return test_common_setown(fd, 1);
145}
146
147static int
148test_setown_ioctl(int fd)
149{
150	return test_common_setown(fd, 0);
151}
152
153static int
154test_sigio(int fd)
155{
156	struct wscons_event ev;
157	int cfd[2], pfd[2];
158	ssize_t n;
159	pid_t pid;
160	int arg, len, status;
161
162	if (pipe(cfd) == -1)
163		err(1, "pipe");
164	if (pipe(pfd) == -1)
165		err(1, "pipe");
166
167	arg = getpid();
168	if (ioctl(fd, FIOSETOWN, &arg) == -1)
169		err(1, "ioctl: FIOSETOWN");
170
171	/* Enable async IO. */
172	arg = 1;
173	if (ioctl(fd, FIOASYNC, &arg) == -1)
174		err(1, "ioctl: FIOASYNC");
175
176	pid = fork();
177	if (pid == -1)
178		err(1, "fork");
179	if (pid == 0) {
180		close(cfd[1]);
181		close(pfd[0]);
182
183		syncsend(pfd[1], 1);
184		syncrecv(cfd[0], 2);
185
186		memset(&ev, 0, sizeof(ev));
187		if (ioctl(fd, WSMUXIO_INJECTEVENT, &ev) == -1)
188			err(1, "ioctl: WSMUXIO_INJECTEVENT");
189
190		close(cfd[0]);
191		close(pfd[1]);
192		_exit(0);
193	}
194	close(cfd[0]);
195	close(pfd[1]);
196
197	syncrecv(pfd[0], 1);
198
199	if (signal(SIGIO, sigio) == SIG_ERR)
200		err(1, "signal");
201
202	syncsend(cfd[1], 2);
203
204	if (waitpid(pid, &status, 0) == -1)
205		err(1, "waitpid");
206	if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
207		errx(1, "child exited %d", WEXITSTATUS(status));
208	if (WIFSIGNALED(status))
209                errx(1, "child killed by signal %d", WTERMSIG(status));
210
211	if (nsigio != 1)
212                errx(1, "expected SIGIO to be received once, got %d", nsigio);
213
214	len = sizeof(ev);
215	n = read(fd, &ev, len);
216	if (n == -1)
217		err(1, "read");
218	if (n != len)
219		errx(1, "read: expected %d bytes, got %ld", len, n);
220
221	/* Disable async IO. */
222	arg = 0;
223	if (ioctl(fd, FIOASYNC, &arg) == -1)
224		err(1, "ioctl: FIOASYNC");
225
226	return 0;
227}
228
229static int
230test_spgrp(int fd)
231{
232	int arg;
233
234	/* The process group must be able to receive SIGIO. */
235	arg = getpgrp();
236	if (ioctl(fd, TIOCSPGRP, &arg) == -1)
237		errx(1, "ioctl: TIOCSPGRP");
238
239	/* Bogus process groups must be rejected. */
240	arg = -getpgrp();
241	if (ioctl(fd, TIOCSPGRP, &arg) != -1)
242		errx(1, "ioctl: TIOCSPGRP: %d accepted", arg);
243	arg = 1000000;
244	if (ioctl(fd, TIOCSPGRP, &arg) != -1)
245		errx(1, "ioctl: TIOCSPGRP: %d accepted", arg);
246
247	return 0;
248}
249
250static int
251test_common_getown(int fd, int dofcntl)
252{
253	int arg, pgrp;
254
255	if (dofcntl) {
256		pgrp = fcntl(fd, F_GETOWN);
257		if (pgrp == -1)
258			err(1, "fcntl: F_GETOWN");
259		if (pgrp != 0)
260			errx(1, "fcntl: F_GETOWN: expected 0, got %d", pgrp);
261	} else {
262		if (ioctl(fd, FIOGETOWN, &pgrp) == -1)
263			err(1, "ioctl: FIOGETOWN");
264		if (pgrp != 0)
265			errx(1, "ioctl: FIOGETOWN: expected 0, got %d", pgrp);
266	}
267
268	arg = getpid();
269	if (ioctl(fd, FIOSETOWN, &arg) == -1)
270		err(1, "ioctl: FIOSETOWN");
271	if (dofcntl) {
272		pgrp = fcntl(fd, F_GETOWN);
273		if (pgrp == -1)
274			err(1, "fcntl: F_GETOWN");
275		if (pgrp != -getpgrp())
276			errx(1, "fcntl: F_GETOWN: expected %d, got %d",
277			    -getpgrp(), pgrp);
278	} else {
279		if (ioctl(fd, FIOGETOWN, &pgrp) == -1)
280			err(1, "ioctl: FIOGETOWN");
281		if (pgrp != -getpgrp())
282			errx(1, "ioctl: FIOGETOWN: expected %d, got %d",
283			    -getpgrp(), pgrp);
284	}
285
286	return 0;
287}
288
289static int
290test_common_setown(int fd, int dofcntl)
291{
292	int arg;
293
294        /* The process must be able to receive SIGIO. */
295	arg = getpid();
296	if (dofcntl) {
297		if (fcntl(fd, F_SETOWN, arg) == -1)
298			errx(1, "fcntl: F_SETOWN: process rejected");
299	} else {
300		if (ioctl(fd, FIOSETOWN, &arg) == -1)
301			errx(1, "ioctl: FIOSETOWN: process rejected");
302	}
303
304	/* The process group must be able to receive SIGIO. */
305	arg = -getpgrp();
306	if (dofcntl) {
307		if (fcntl(fd, F_SETOWN, arg) == -1)
308			errx(1, "fcntl: F_SETOWN: process group rejected");
309	} else {
310		if (ioctl(fd, FIOSETOWN, &arg) == -1)
311			errx(1, "ioctl: FIOSETOWN: process group rejected");
312	}
313
314	/* A bogus process must be rejected. */
315	arg = 1000000;
316	if (dofcntl) {
317		if (fcntl(fd, F_SETOWN, arg) != -1)
318			errx(1, "fcntl: F_SETOWN: bogus process accepted");
319	} else {
320		if (ioctl(fd, FIOSETOWN, &arg) != -1)
321			errx(1, "ioctl: FIOSETOWN: bogus process accepted");
322	}
323
324	/* A bogus process group must be rejected. */
325	arg = -1000000;
326	if (dofcntl) {
327		if (fcntl(fd, F_SETOWN, arg) != -1)
328			errx(1, "fcntl: F_SETOWN: bogus process group accepted");
329	} else {
330		if (ioctl(fd, FIOSETOWN, &arg) != -1)
331			errx(1, "ioctl: FIOSETOWN: bogus process group accepted");
332	}
333
334	return 0;
335}
336
337static void
338sigio(int signo)
339{
340	nsigio++;
341}
342
343static void
344syncrecv(int fd, int id)
345{
346	int r;
347
348	if (read(fd, &r, sizeof(r)) == -1)
349		err(1, "%s: read", __func__);
350	if (r != id)
351		errx(1, "%s: expected %d, got %d", __func__, id, r);
352}
353
354static void
355syncsend(int fd, int id)
356{
357	if (write(fd, &id, sizeof(id)) == -1)
358		err(1, "%s: write", __func__);
359}
360
361static __dead void
362usage(void)
363{
364	fprintf(stderr, "usage: sigio [-p] -d device test\n");
365	exit(1);
366}
367