sigio.c revision 1.2
1/*	$OpenBSD: sigio.c,v 1.2 2018/11/20 18:49:42 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_setown_fcntl(int);
34static int test_setown_ioctl(int);
35static int test_sigio(int);
36static int test_spgrp(int);
37
38static int test_common_setown(int, int);
39
40static void sigio(int);
41static void syncrecv(int, int);
42static void syncsend(int, int);
43static __dead void usage(void);
44
45static volatile sig_atomic_t nsigio;
46
47static struct {
48	const char *name;
49	int (*fn)(int);
50} tests[] = {
51	{ "setown-fcntl",	test_setown_fcntl },
52	{ "setown-ioctl",	test_setown_ioctl },
53	{ "sigio",		test_sigio },
54	{ "spgrp",		test_spgrp },
55	{ NULL,	NULL },
56};
57
58int
59main(int argc, char *argv[])
60{
61	int (*fn)(int) = NULL;
62	const char *dev = NULL;
63	int c, fd, i;
64	int prereq = 0;
65
66	while ((c = getopt(argc, argv, "d:p")) != -1)
67		switch (c) {
68		case 'd':
69			dev = optarg;
70			break;
71		case 'p':
72			prereq = 1;
73			break;
74		default:
75			usage();
76		}
77	argc -= optind;
78	argv += optind;
79	if (dev == NULL || argc != 1)
80		usage();
81
82	fd = open(dev, O_RDWR);
83	if (fd == -1)
84		err(1, "open: %s", dev);
85	if (prereq)
86		return 0;
87
88	for (i = 0; tests[i].name != NULL; i++) {
89		if (strcmp(argv[0], tests[i].name))
90			continue;
91
92		fn = tests[i].fn;
93		break;
94	}
95	if (fn == NULL)
96		errx(1, "%s: no such test", argv[0]);
97
98	return fn(fd);
99}
100
101static int
102test_setown_fcntl(int fd)
103{
104	return test_common_setown(fd, 1);
105}
106
107static int
108test_setown_ioctl(int fd)
109{
110	return test_common_setown(fd, 0);
111}
112
113static int
114test_sigio(int fd)
115{
116	struct wscons_event ev;
117	int cfd[2], pfd[2];
118	ssize_t n;
119	pid_t pid;
120	int arg, len, status;
121
122	if (pipe(cfd) == -1)
123		err(1, "pipe");
124	if (pipe(pfd) == -1)
125		err(1, "pipe");
126
127	arg = getpid();
128	if (ioctl(fd, FIOSETOWN, &arg) == -1)
129		err(1, "ioctl: FIOSETOWN");
130
131	/* Enable async IO. */
132	arg = 1;
133	if (ioctl(fd, FIOASYNC, &arg) == -1)
134		err(1, "ioctl: FIOASYNC");
135
136	pid = fork();
137	if (pid == -1)
138		err(1, "fork");
139	if (pid == 0) {
140		close(cfd[1]);
141		close(pfd[0]);
142
143		syncsend(pfd[1], 1);
144		syncrecv(cfd[0], 2);
145
146		memset(&ev, 0, sizeof(ev));
147		if (ioctl(fd, WSMUXIO_INJECTEVENT, &ev) == -1)
148			err(1, "ioctl: WSMUXIO_INJECTEVENT");
149
150		close(cfd[0]);
151		close(pfd[1]);
152		_exit(0);
153	}
154	close(cfd[0]);
155	close(pfd[1]);
156
157	syncrecv(pfd[0], 1);
158
159	if (signal(SIGIO, sigio) == SIG_ERR)
160		err(1, "signal");
161
162	syncsend(cfd[1], 2);
163
164	if (waitpid(pid, &status, 0) == -1)
165		err(1, "waitpid");
166	if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
167		errx(1, "child exited %d", WEXITSTATUS(status));
168	if (WIFSIGNALED(status))
169                errx(1, "child killed by signal %d", WTERMSIG(status));
170
171	if (nsigio != 1)
172                errx(1, "expected SIGIO to be received once, got %d", nsigio);
173
174	len = sizeof(ev);
175	n = read(fd, &ev, len);
176	if (n == -1)
177		err(1, "read");
178	if (n != len)
179		errx(1, "read: expected %d bytes, got %ld", len, n);
180
181	/* Disable async IO. */
182	arg = 0;
183	if (ioctl(fd, FIOASYNC, &arg) == -1)
184		err(1, "ioctl: FIOASYNC");
185
186	return 0;
187}
188
189static int
190test_spgrp(int fd)
191{
192	int arg;
193
194	/* The process group must be able to receive SIGIO. */
195	arg = getpgrp();
196	if (ioctl(fd, TIOCSPGRP, &arg) == -1)
197		errx(1, "ioctl: TIOCSPGRP");
198
199	/* Bogus process groups must be rejected. */
200	arg = -getpgrp();
201	if (ioctl(fd, TIOCSPGRP, &arg) != -1)
202		errx(1, "ioctl: TIOCSPGRP: %d accepted", arg);
203	arg = 1000000;
204	if (ioctl(fd, TIOCSPGRP, &arg) != -1)
205		errx(1, "ioctl: TIOCSPGRP: %d accepted", arg);
206
207	return 0;
208}
209
210static int
211test_common_setown(int fd, int dofcntl)
212{
213	int arg;
214
215        /* The process must be able to receive SIGIO. */
216	arg = getpid();
217	if (dofcntl) {
218		if (fcntl(fd, F_SETOWN, arg) == -1)
219			errx(1, "fcntl: F_SETOWN: process rejected");
220	} else {
221		if (ioctl(fd, FIOSETOWN, &arg) == -1)
222			errx(1, "ioctl: FIOSETOWN: process rejected");
223	}
224
225	/* The process group must be able to receive SIGIO. */
226	arg = -getpgrp();
227	if (dofcntl) {
228		if (fcntl(fd, F_SETOWN, arg) == -1)
229			errx(1, "fcntl: F_SETOWN: process group rejected");
230	} else {
231		if (ioctl(fd, FIOSETOWN, &arg) == -1)
232			errx(1, "ioctl: FIOSETOWN: process group rejected");
233	}
234
235	/* A bogus process must be rejected. */
236	arg = 1000000;
237	if (dofcntl) {
238		if (fcntl(fd, F_SETOWN, arg) != -1)
239			errx(1, "fcntl: F_SETOWN: bogus process accepted");
240	} else {
241		if (ioctl(fd, FIOSETOWN, &arg) != -1)
242			errx(1, "ioctl: FIOSETOWN: bogus process accepted");
243	}
244
245	/* A bogus process group must be rejected. */
246	arg = -1000000;
247	if (dofcntl) {
248		if (fcntl(fd, F_SETOWN, arg) != -1)
249			errx(1, "fcntl: F_SETOWN: bogus process group accepted");
250	} else {
251		if (ioctl(fd, FIOSETOWN, &arg) != -1)
252			errx(1, "ioctl: FIOSETOWN: bogus process group accepted");
253	}
254
255	return 0;
256}
257
258static void
259sigio(int signo)
260{
261	nsigio++;
262}
263
264static void
265syncrecv(int fd, int id)
266{
267	int r;
268
269	if (read(fd, &r, sizeof(r)) == -1)
270		err(1, "%s: read", __func__);
271	if (r != id)
272		errx(1, "%s: expected %d, got %d", __func__, id, r);
273}
274
275static void
276syncsend(int fd, int id)
277{
278	if (write(fd, &id, sizeof(id)) == -1)
279		err(1, "%s: write", __func__);
280}
281
282static __dead void
283usage(void)
284{
285	fprintf(stderr, "usage: sigio [-p] -d device test\n");
286	exit(1);
287}
288