1138447Srwatson/*-
2138447Srwatson * Copyright (c) 2004 Robert N. M. Watson
3138447Srwatson * All rights reserved.
4138447Srwatson *
5138447Srwatson * Redistribution and use in source and binary forms, with or without
6138447Srwatson * modification, are permitted provided that the following conditions
7138447Srwatson * are met:
8138447Srwatson * 1. Redistributions of source code must retain the above copyright
9138447Srwatson *    notice, this list of conditions and the following disclaimer.
10138447Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11138447Srwatson *    notice, this list of conditions and the following disclaimer in the
12138447Srwatson *    documentation and/or other materials provided with the distribution.
13138447Srwatson *
14138447Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15138447Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16138447Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17138447Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18138447Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19138447Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20138447Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21138447Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22138447Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23138447Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24138447Srwatson * SUCH DAMAGE.
25138447Srwatson *
26138447Srwatson * $FreeBSD$
27138447Srwatson */
28138447Srwatson
29138447Srwatson/*
30138447Srwatson * Regression test to do some very basic AIO exercising on several types of
31138447Srwatson * file descriptors.  Currently, the tests consist of initializing a fixed
32138447Srwatson * size buffer with pseudo-random data, writing it to one fd using AIO, then
33138447Srwatson * reading it from a second descriptor using AIO.  For some targets, the same
34138447Srwatson * fd is used for write and read (i.e., file, md device), but for others the
35138447Srwatson * operation is performed on a peer (pty, socket, fifo, etc).  A timeout is
36138447Srwatson * initiated to detect undo blocking.  This test does not attempt to exercise
37138447Srwatson * error cases or more subtle asynchronous behavior, just make sure that the
38138447Srwatson * basic operations work on some basic object types.
39138447Srwatson */
40138447Srwatson
41138447Srwatson#include <sys/types.h>
42138447Srwatson#include <sys/socket.h>
43205224Skib#include <sys/stat.h>
44138447Srwatson#include <sys/mdioctl.h>
45138447Srwatson
46138447Srwatson#include <aio.h>
47138447Srwatson#include <err.h>
48138447Srwatson#include <errno.h>
49138447Srwatson#include <fcntl.h>
50205224Skib#include <libutil.h>
51138447Srwatson#include <limits.h>
52253526Skib#include <stdint.h>
53138447Srwatson#include <stdio.h>
54205224Skib#include <stdlib.h>
55138447Srwatson#include <string.h>
56138447Srwatson#include <termios.h>
57138447Srwatson#include <unistd.h>
58138447Srwatson
59138447Srwatson#define	PATH_TEMPLATE	"/tmp/aio.XXXXXXXXXX"
60138447Srwatson
61138447Srwatson/*
62138447Srwatson * GLOBAL_MAX sets the largest usable buffer size to be read and written, as
63138447Srwatson * it sizes ac_buffer in the aio_context structure.  It is also the default
64138447Srwatson * size for file I/O.  For other types, we use smaller blocks or we risk
65138447Srwatson * blocking (and we run in a single process/thread so that would be bad).
66138447Srwatson */
67138447Srwatson#define	GLOBAL_MAX	16384
68138447Srwatson
69138447Srwatson#define	BUFFER_MAX	GLOBAL_MAX
70138447Srwatsonstruct aio_context {
71138447Srwatson	const char	*ac_test;
72138447Srwatson	int		 ac_read_fd, ac_write_fd;
73138447Srwatson	long		 ac_seed;
74138447Srwatson	char		 ac_buffer[GLOBAL_MAX];
75138447Srwatson	int		 ac_buflen;
76138447Srwatson	int		 ac_seconds;
77138447Srwatson	void		 (*ac_cleanup)(void *arg);
78138447Srwatson	void		*ac_cleanup_arg;
79138447Srwatson};
80138447Srwatson
81138447Srwatsonstatic int	aio_timedout;
82138447Srwatsonstatic int	aio_notpresent;
83138447Srwatson
84138447Srwatson/*
85138447Srwatson * Attempt to provide a cleaner failure mode in the event AIO support is not
86138447Srwatson * present by catching and reporting SIGSYS.
87138447Srwatson */
88138447Srwatsonstatic void
89138447Srwatsonaio_sigsys(int sig)
90138447Srwatson{
91138447Srwatson
92138447Srwatson	aio_notpresent = 1;
93138447Srwatson}
94138447Srwatson
95138447Srwatsonstatic void
96138447Srwatsonaio_sigsys_setup(void)
97138447Srwatson{
98138447Srwatson
99138447Srwatson	if (signal(SIGSYS, aio_sigsys) == SIG_ERR)
100138447Srwatson		errx(-1, "FAIL: signal(SIGSYS): %s", strerror(errno));
101138447Srwatson}
102138447Srwatson
103138447Srwatson/*
104138447Srwatson * Each test run specifies a timeout in seconds.  Use the somewhat obsoleted
105138447Srwatson * signal(3) and alarm(3) APIs to set this up.
106138447Srwatson */
107138447Srwatsonstatic void
108138447Srwatsonaio_timeout_signal(int sig)
109138447Srwatson{
110138447Srwatson
111138447Srwatson	aio_timedout = 1;
112138447Srwatson}
113138447Srwatson
114138447Srwatsonstatic void
115138447Srwatsonaio_timeout_start(const char *string1, const char *string2, int seconds)
116138447Srwatson{
117138447Srwatson
118138447Srwatson	aio_timedout = 0;
119138447Srwatson	if (signal(SIGALRM, aio_timeout_signal) == SIG_ERR)
120138447Srwatson		errx(-1, "FAIL: %s: %s: aio_timeout_set: signal(SIGALRM): %s",
121138447Srwatson		    string1, string2, strerror(errno));
122138447Srwatson	alarm(seconds);
123138447Srwatson}
124138447Srwatson
125138447Srwatsonstatic void
126138447Srwatsonaio_timeout_stop(const char *string1, const char *string2)
127138447Srwatson{
128138447Srwatson
129138447Srwatson	if (signal(SIGALRM, NULL) == SIG_ERR)
130138447Srwatson		errx(-1, "FAIL: %s: %s: aio_timeout_stop: signal(NULL): %s",
131138447Srwatson		    string1, string2, strerror(errno));
132138447Srwatson	alarm(0);
133138447Srwatson}
134138447Srwatson
135138447Srwatson/*
136138447Srwatson * Fill a buffer given a seed that can be fed into srandom() to initialize
137138447Srwatson * the PRNG in a repeatable manner.
138138447Srwatson */
139138447Srwatsonstatic void
140138447Srwatsonaio_fill_buffer(char *buffer, int len, long seed)
141138447Srwatson{
142138447Srwatson	char ch;
143138447Srwatson	int i;
144138447Srwatson
145138447Srwatson	srandom(seed);
146138447Srwatson	for (i = 0; i < len; i++) {
147138447Srwatson		ch = random() & 0xff;
148138447Srwatson		buffer[i] = ch;
149138447Srwatson	}
150138447Srwatson}
151138447Srwatson
152138447Srwatson/*
153138447Srwatson * Test that a buffer matches a given seed.  See aio_fill_buffer().  Return
154138447Srwatson * (1) on a match, (0) on a mismatch.
155138447Srwatson */
156138447Srwatsonstatic int
157138447Srwatsonaio_test_buffer(char *buffer, int len, long seed)
158138447Srwatson{
159138447Srwatson	char ch;
160138447Srwatson	int i;
161138447Srwatson
162138447Srwatson	srandom(seed);
163138447Srwatson	for (i = 0; i < len; i++) {
164138447Srwatson		ch = random() & 0xff;
165138447Srwatson		if (buffer[i] != ch)
166138447Srwatson			return (0);
167138447Srwatson	}
168138447Srwatson	return (1);
169138447Srwatson}
170138447Srwatson
171138447Srwatson/*
172138447Srwatson * Initialize a testing context given the file descriptors provided by the
173138447Srwatson * test setup.
174138447Srwatson */
175138447Srwatsonstatic void
176138447Srwatsonaio_context_init(struct aio_context *ac, const char *test, int read_fd,
177138447Srwatson    int write_fd, int buflen, int seconds, void (*cleanup)(void *),
178138447Srwatson    void *cleanup_arg)
179138447Srwatson{
180138447Srwatson
181138447Srwatson	if (buflen > BUFFER_MAX)
182138447Srwatson		errx(-1, "FAIL: %s: aio_context_init: buffer too large",
183138447Srwatson		    test);
184138447Srwatson	bzero(ac, sizeof(*ac));
185138447Srwatson	ac->ac_test = test;
186138447Srwatson	ac->ac_read_fd = read_fd;
187138447Srwatson	ac->ac_write_fd = write_fd;
188138447Srwatson	ac->ac_buflen = buflen;
189138447Srwatson	srandomdev();
190138447Srwatson	ac->ac_seed = random();
191138447Srwatson	aio_fill_buffer(ac->ac_buffer, buflen, ac->ac_seed);
192138447Srwatson	if (aio_test_buffer(ac->ac_buffer, buflen, ac->ac_seed) == 0)
193138447Srwatson		errx(-1, "%s: aio_context_init: aio_test_buffer: internal "
194138447Srwatson		    "error", test);
195138447Srwatson	ac->ac_seconds = seconds;
196138447Srwatson	ac->ac_cleanup = cleanup;
197138447Srwatson	ac->ac_cleanup_arg = cleanup_arg;
198138447Srwatson}
199138447Srwatson
200138447Srwatson/*
201138447Srwatson * Each tester can register a callback to clean up in the event the test
202138447Srwatson * fails.  Preserve the value of errno so that subsequent calls to errx()
203138447Srwatson * work properly.
204138447Srwatson */
205138447Srwatsonstatic void
206138447Srwatsonaio_cleanup(struct aio_context *ac)
207138447Srwatson{
208138447Srwatson	int error;
209138447Srwatson
210138447Srwatson	if (ac->ac_cleanup == NULL)
211138447Srwatson		return;
212138447Srwatson	error = errno;
213138447Srwatson	(ac->ac_cleanup)(ac->ac_cleanup_arg);
214138447Srwatson	errno = error;
215138447Srwatson}
216138447Srwatson
217138447Srwatson/*
218138447Srwatson * Perform a simple write test of our initialized data buffer to the provided
219138447Srwatson * file descriptor.
220138447Srwatson */
221138447Srwatsonstatic void
222138447Srwatsonaio_write_test(struct aio_context *ac)
223138447Srwatson{
224138447Srwatson	struct aiocb aio, *aiop;
225138447Srwatson	ssize_t len;
226138447Srwatson	int error;
227138447Srwatson
228138447Srwatson	bzero(&aio, sizeof(aio));
229138447Srwatson	aio.aio_buf = ac->ac_buffer;
230138447Srwatson	aio.aio_nbytes = ac->ac_buflen;
231138447Srwatson	aio.aio_fildes = ac->ac_write_fd;
232138447Srwatson	aio.aio_offset = 0;
233138447Srwatson
234138447Srwatson	aio_timeout_start(ac->ac_test, "aio_write_test", ac->ac_seconds);
235138447Srwatson
236138447Srwatson	if (aio_write(&aio) < 0) {
237138447Srwatson		if (errno == EINTR) {
238138447Srwatson			if (aio_notpresent)
239138447Srwatson				errno = EOPNOTSUPP;
240138447Srwatson			if (aio_timedout) {
241138447Srwatson				aio_cleanup(ac);
242138447Srwatson				errx(-1, "FAIL: %s: aio_write_test: "
243138447Srwatson				    "aio_write: timed out", ac->ac_test);
244138447Srwatson			}
245138447Srwatson		}
246138447Srwatson		aio_cleanup(ac);
247138447Srwatson		errx(-1, "FAIL: %s: aio_write_test: aio_write: %s",
248138447Srwatson		    ac->ac_test, strerror(errno));
249138447Srwatson	}
250138447Srwatson
251138447Srwatson	len = aio_waitcomplete(&aiop, NULL);
252138447Srwatson	if (len < 0) {
253138447Srwatson		if (errno == EINTR) {
254138447Srwatson			if (aio_notpresent)
255138447Srwatson				errno = EOPNOTSUPP;
256138447Srwatson			if (aio_timedout) {
257138447Srwatson				aio_cleanup(ac);
258138447Srwatson				errx(-1, "FAIL: %s: aio_write_test: "
259138447Srwatson				    "aio_waitcomplete: timed out",
260138447Srwatson				    ac->ac_test);
261138447Srwatson			}
262138447Srwatson		}
263138447Srwatson		aio_cleanup(ac);
264138447Srwatson		errx(-1, "FAIL: %s: aio_write_test: aio_waitcomplete: %s",
265138447Srwatson		    ac->ac_test, strerror(errno));
266138447Srwatson	}
267138447Srwatson
268138447Srwatson	aio_timeout_stop(ac->ac_test, "aio_write_test");
269138447Srwatson
270138447Srwatson	if (len != ac->ac_buflen) {
271138447Srwatson		aio_cleanup(ac);
272138447Srwatson		errx(-1, "FAIL: %s: aio_write_test: aio_waitcomplete: short "
273253526Skib		    "write (%jd)", ac->ac_test, (intmax_t)len);
274138447Srwatson	}
275138447Srwatson}
276138447Srwatson
277138447Srwatson/*
278138447Srwatson * Perform a simple read test of our initialized data buffer from the
279138447Srwatson * provided file descriptor.
280138447Srwatson */
281138447Srwatsonstatic void
282138447Srwatsonaio_read_test(struct aio_context *ac)
283138447Srwatson{
284138447Srwatson	struct aiocb aio, *aiop;
285138447Srwatson	ssize_t len;
286138447Srwatson
287138447Srwatson	bzero(ac->ac_buffer, ac->ac_buflen);
288138447Srwatson	bzero(&aio, sizeof(aio));
289138447Srwatson	aio.aio_buf = ac->ac_buffer;
290138447Srwatson	aio.aio_nbytes = ac->ac_buflen;
291138447Srwatson	aio.aio_fildes = ac->ac_read_fd;
292138447Srwatson	aio.aio_offset = 0;
293138447Srwatson
294138447Srwatson	aio_timeout_start(ac->ac_test, "aio_read_test", ac->ac_seconds);
295138447Srwatson
296138447Srwatson	if (aio_read(&aio) < 0) {
297138447Srwatson		if (errno == EINTR) {
298138447Srwatson			if (aio_notpresent)
299138447Srwatson				errno = EOPNOTSUPP;
300138447Srwatson			if (aio_timedout) {
301138447Srwatson				aio_cleanup(ac);
302138447Srwatson				errx(-1, "FAIL: %s: aio_read_test: "
303138447Srwatson				    "aio_read: timed out", ac->ac_test);
304138447Srwatson			}
305138447Srwatson		}
306138447Srwatson		aio_cleanup(ac);
307138447Srwatson		errx(-1, "FAIL: %s: aio_read_test: aio_read %s", ac->ac_test,
308138447Srwatson		    strerror(errno));
309138447Srwatson	}
310138447Srwatson
311138447Srwatson	len = aio_waitcomplete(&aiop, NULL);
312138447Srwatson	if (len < 0) {
313138447Srwatson		if (errno == EINTR) {
314138447Srwatson			if (aio_notpresent)
315138447Srwatson				errno = EOPNOTSUPP;
316138447Srwatson			if (aio_timedout) {
317138447Srwatson				aio_cleanup(ac);
318138447Srwatson				errx(-1, "FAIL: %s: aio_read_test: "
319138447Srwatson				    "aio_waitcomplete: timed out",
320138447Srwatson				    ac->ac_test);
321138447Srwatson			}
322138447Srwatson		}
323138447Srwatson		aio_cleanup(ac);
324138447Srwatson		errx(-1, "FAIL: %s: aio_read_test: aio_waitcomplete: %s",
325138447Srwatson		    ac->ac_test, strerror(errno));
326138447Srwatson	}
327138447Srwatson
328138447Srwatson	aio_timeout_stop(ac->ac_test, "aio_read_test");
329138447Srwatson
330138447Srwatson	if (len != ac->ac_buflen) {
331138447Srwatson		aio_cleanup(ac);
332138447Srwatson		errx(-1, "FAIL: %s: aio_read_test: aio_waitcomplete: short "
333253526Skib		    "read (%jd)", ac->ac_test, (intmax_t)len);
334138447Srwatson	}
335138447Srwatson
336138447Srwatson	if (aio_test_buffer(ac->ac_buffer, ac->ac_buflen, ac->ac_seed) == 0) {
337138447Srwatson		aio_cleanup(ac);
338138447Srwatson		errx(-1, "FAIL: %s: aio_read_test: buffer mismatch",
339138447Srwatson		    ac->ac_test);
340138447Srwatson	}
341138447Srwatson}
342138447Srwatson
343138447Srwatson/*
344138447Srwatson * Series of type-specific tests for AIO.  For now, we just make sure we can
345138447Srwatson * issue a write and then a read to each type.  We assume that once a write
346138447Srwatson * is issued, a read can follow.
347138447Srwatson */
348138447Srwatson
349138447Srwatson/*
350138447Srwatson * Test with a classic file.  Assumes we can create a moderate size temporary
351138447Srwatson * file.
352138447Srwatson */
353138447Srwatsonstruct aio_file_arg {
354138447Srwatson	int	 afa_fd;
355138447Srwatson	char	*afa_pathname;
356138447Srwatson};
357138447Srwatson
358138447Srwatsonstatic void
359138447Srwatsonaio_file_cleanup(void *arg)
360138447Srwatson{
361138447Srwatson	struct aio_file_arg *afa;
362138447Srwatson
363138447Srwatson	afa = arg;
364138447Srwatson	close(afa->afa_fd);
365138447Srwatson	unlink(afa->afa_pathname);
366138447Srwatson}
367138447Srwatson
368138447Srwatson#define	FILE_LEN	GLOBAL_MAX
369138447Srwatson#define	FILE_TIMEOUT	30
370253526Skibstatic void
371138447Srwatsonaio_file_test(void)
372138447Srwatson{
373138447Srwatson	char pathname[PATH_MAX];
374138447Srwatson	struct aio_file_arg arg;
375138447Srwatson	struct aio_context ac;
376138447Srwatson	int fd;
377138447Srwatson
378138447Srwatson	strcpy(pathname, PATH_TEMPLATE);
379138447Srwatson	fd = mkstemp(pathname);
380138447Srwatson	if (fd == -1)
381138447Srwatson		errx(-1, "FAIL: aio_file_test: mkstemp: %s",
382138447Srwatson		    strerror(errno));
383138447Srwatson
384138447Srwatson	arg.afa_fd = fd;
385138447Srwatson	arg.afa_pathname = pathname;
386138447Srwatson
387138447Srwatson	aio_context_init(&ac, "aio_file_test", fd, fd, FILE_LEN,
388138447Srwatson	    FILE_TIMEOUT, aio_file_cleanup, &arg);
389138447Srwatson	aio_write_test(&ac);
390138447Srwatson	aio_read_test(&ac);
391138447Srwatson
392138447Srwatson	aio_file_cleanup(&arg);
393138447Srwatson
394138447Srwatson	fprintf(stderr, "PASS: aio_file_test\n");
395138447Srwatson}
396138447Srwatson
397138447Srwatsonstruct aio_fifo_arg {
398138447Srwatson	int	 afa_read_fd;
399138447Srwatson	int	 afa_write_fd;
400138447Srwatson	char	*afa_pathname;
401138447Srwatson};
402138447Srwatson
403138447Srwatsonstatic void
404138447Srwatsonaio_fifo_cleanup(void *arg)
405138447Srwatson{
406138447Srwatson	struct aio_fifo_arg *afa;
407138447Srwatson
408138447Srwatson	afa = arg;
409138447Srwatson	if (afa->afa_read_fd != -1)
410138447Srwatson		close(afa->afa_read_fd);
411138447Srwatson	if (afa->afa_write_fd != -1)
412138447Srwatson		close(afa->afa_write_fd);
413138447Srwatson	unlink(afa->afa_pathname);
414138447Srwatson}
415138447Srwatson
416138447Srwatson#define	FIFO_LEN	256
417138447Srwatson#define	FIFO_TIMEOUT	30
418253526Skibstatic void
419138447Srwatsonaio_fifo_test(void)
420138447Srwatson{
421138447Srwatson	int error, read_fd = -1, write_fd = -1;
422138447Srwatson	struct aio_fifo_arg arg;
423138447Srwatson	char pathname[PATH_MAX];
424138447Srwatson	struct aio_context ac;
425138447Srwatson
426138447Srwatson	/*
427138447Srwatson	 * In theory, mktemp() can return a name that is then collided with.
428138447Srwatson	 * Because this is a regression test, we treat that as a test failure
429138447Srwatson	 * rather than retrying.
430138447Srwatson	 */
431138447Srwatson	strcpy(pathname, PATH_TEMPLATE);
432138447Srwatson	mktemp(pathname);
433138447Srwatson	if (mkfifo(pathname, 0600) == -1)
434138447Srwatson		errx(-1, "FAIL: aio_fifo_test: mkfifo: %s", strerror(errno));
435138447Srwatson	arg.afa_pathname = pathname;
436138447Srwatson	arg.afa_read_fd = -1;
437138447Srwatson	arg.afa_write_fd = -1;
438138447Srwatson
439138447Srwatson	read_fd = open(pathname, O_RDONLY | O_NONBLOCK);
440138447Srwatson	if (read_fd == -1) {
441138447Srwatson		error = errno;
442138447Srwatson		aio_fifo_cleanup(&arg);
443138447Srwatson		errno = error;
444138447Srwatson		errx(-1, "FAIL: aio_fifo_test: read_fd open: %s",
445138447Srwatson		    strerror(errno));
446138447Srwatson	}
447138447Srwatson	arg.afa_read_fd = read_fd;
448138447Srwatson
449138447Srwatson	write_fd = open(pathname, O_WRONLY);
450138447Srwatson	if (write_fd == -1) {
451138447Srwatson		error = errno;
452138447Srwatson		aio_fifo_cleanup(&arg);
453138447Srwatson		errno = error;
454138447Srwatson		errx(-1, "FAIL: aio_fifo_test: write_fd open: %s",
455138447Srwatson		    strerror(errno));
456138447Srwatson	}
457138447Srwatson	arg.afa_write_fd = write_fd;
458138447Srwatson
459138447Srwatson	aio_context_init(&ac, "aio_fifo_test", read_fd, write_fd, FIFO_LEN,
460138447Srwatson	    FIFO_TIMEOUT, aio_fifo_cleanup, &arg);
461138447Srwatson	aio_write_test(&ac);
462138447Srwatson	aio_read_test(&ac);
463138447Srwatson
464138447Srwatson	aio_fifo_cleanup(&arg);
465138447Srwatson
466138447Srwatson	fprintf(stderr, "PASS: aio_fifo_test\n");
467138447Srwatson}
468138447Srwatson
469138447Srwatsonstruct aio_unix_socketpair_arg {
470138447Srwatson	int	asa_sockets[2];
471138447Srwatson};
472138447Srwatson
473138447Srwatsonstatic void
474138447Srwatsonaio_unix_socketpair_cleanup(void *arg)
475138447Srwatson{
476138447Srwatson	struct aio_unix_socketpair_arg *asa;
477138447Srwatson
478138447Srwatson	asa = arg;
479138447Srwatson	close(asa->asa_sockets[0]);
480138447Srwatson	close(asa->asa_sockets[1]);
481138447Srwatson}
482138447Srwatson
483138447Srwatson#define	UNIX_SOCKETPAIR_LEN	256
484138447Srwatson#define	UNIX_SOCKETPAIR_TIMEOUT	30
485253526Skibstatic void
486138447Srwatsonaio_unix_socketpair_test(void)
487138447Srwatson{
488138447Srwatson	struct aio_unix_socketpair_arg arg;
489138447Srwatson	struct aio_context ac;
490138447Srwatson	int sockets[2];
491138447Srwatson
492138447Srwatson	if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockets) < 0)
493138447Srwatson		errx(-1, "FAIL: aio_socketpair_test: socketpair: %s",
494138447Srwatson		    strerror(errno));
495138447Srwatson
496138447Srwatson	arg.asa_sockets[0] = sockets[0];
497138447Srwatson	arg.asa_sockets[1] = sockets[1];
498138447Srwatson	aio_context_init(&ac, "aio_unix_socketpair_test", sockets[0],
499138447Srwatson	    sockets[1], UNIX_SOCKETPAIR_LEN, UNIX_SOCKETPAIR_TIMEOUT,
500138447Srwatson	    aio_unix_socketpair_cleanup, &arg);
501138447Srwatson	aio_write_test(&ac);
502138447Srwatson	aio_read_test(&ac);
503138447Srwatson
504138447Srwatson	aio_unix_socketpair_cleanup(&arg);
505138447Srwatson
506138447Srwatson	fprintf(stderr, "PASS: aio_unix_socketpair_test\n");
507138447Srwatson}
508138447Srwatson
509138447Srwatsonstruct aio_pty_arg {
510138447Srwatson	int	apa_read_fd;
511138447Srwatson	int	apa_write_fd;
512138447Srwatson};
513138447Srwatson
514138447Srwatsonstatic void
515138447Srwatsonaio_pty_cleanup(void *arg)
516138447Srwatson{
517138447Srwatson	struct aio_pty_arg *apa;
518138447Srwatson
519253526Skib	apa = arg;
520138447Srwatson	close(apa->apa_read_fd);
521138447Srwatson	close(apa->apa_write_fd);
522138447Srwatson};
523138447Srwatson
524138447Srwatson#define	PTY_LEN		256
525138447Srwatson#define	PTY_TIMEOUT	30
526253526Skibstatic void
527138447Srwatsonaio_pty_test(void)
528138447Srwatson{
529138447Srwatson	struct aio_pty_arg arg;
530138447Srwatson	struct aio_context ac;
531138447Srwatson	int read_fd, write_fd;
532138447Srwatson	struct termios ts;
533138447Srwatson	int error;
534138447Srwatson
535138447Srwatson	if (openpty(&read_fd, &write_fd, NULL, NULL, NULL) < 0)
536138447Srwatson		errx(-1, "FAIL: aio_pty_test: openpty: %s", strerror(errno));
537138447Srwatson
538138447Srwatson	arg.apa_read_fd = read_fd;
539138447Srwatson	arg.apa_write_fd = write_fd;
540138447Srwatson
541138447Srwatson	if (tcgetattr(write_fd, &ts) < 0) {
542138447Srwatson		error = errno;
543138447Srwatson		aio_pty_cleanup(&arg);
544138447Srwatson		errno = error;
545138447Srwatson		errx(-1, "FAIL: aio_pty_test: tcgetattr: %s",
546138447Srwatson		    strerror(errno));
547138447Srwatson	}
548138447Srwatson	cfmakeraw(&ts);
549138447Srwatson	if (tcsetattr(write_fd, TCSANOW, &ts) < 0) {
550138447Srwatson		error = errno;
551138447Srwatson		aio_pty_cleanup(&arg);
552138447Srwatson		errno = error;
553138447Srwatson		errx(-1, "FAIL: aio_pty_test: tcsetattr: %s",
554138447Srwatson		    strerror(errno));
555138447Srwatson	}
556138447Srwatson
557138447Srwatson	aio_context_init(&ac, "aio_pty_test", read_fd, write_fd, PTY_LEN,
558138447Srwatson	    PTY_TIMEOUT, aio_pty_cleanup, &arg);
559138447Srwatson	aio_write_test(&ac);
560138447Srwatson	aio_read_test(&ac);
561138447Srwatson
562138447Srwatson	aio_pty_cleanup(&arg);
563138447Srwatson
564138447Srwatson	fprintf(stderr, "PASS: aio_pty_test\n");
565138447Srwatson}
566138447Srwatson
567138447Srwatsonstatic void
568138447Srwatsonaio_pipe_cleanup(void *arg)
569138447Srwatson{
570138447Srwatson	int *pipes = arg;
571138447Srwatson
572138447Srwatson	close(pipes[0]);
573138447Srwatson	close(pipes[1]);
574138447Srwatson}
575138447Srwatson
576138447Srwatson#define	PIPE_LEN	256
577138447Srwatson#define	PIPE_TIMEOUT	30
578253526Skibstatic void
579138447Srwatsonaio_pipe_test(void)
580138447Srwatson{
581138447Srwatson	struct aio_context ac;
582138447Srwatson	int pipes[2];
583138447Srwatson
584138447Srwatson	if (pipe(pipes) < 0)
585138447Srwatson		errx(-1, "FAIL: aio_pipe_test: pipe: %s", strerror(errno));
586138447Srwatson
587138447Srwatson	aio_context_init(&ac, "aio_file_test", pipes[0], pipes[1], PIPE_LEN,
588138447Srwatson	    PIPE_TIMEOUT, aio_pipe_cleanup, pipes);
589138447Srwatson	aio_write_test(&ac);
590138447Srwatson	aio_read_test(&ac);
591138447Srwatson
592138447Srwatson	aio_pipe_cleanup(pipes);
593138447Srwatson
594138447Srwatson	fprintf(stderr, "PASS: aio_pipe_test\n");
595138447Srwatson}
596138447Srwatson
597138447Srwatsonstruct aio_md_arg {
598138447Srwatson	int	ama_mdctl_fd;
599138447Srwatson	int	ama_unit;
600138447Srwatson	int	ama_fd;
601138447Srwatson};
602138447Srwatson
603138447Srwatsonstatic void
604138447Srwatsonaio_md_cleanup(void *arg)
605138447Srwatson{
606138447Srwatson	struct aio_md_arg *ama;
607138447Srwatson	struct md_ioctl mdio;
608138447Srwatson	int error;
609138447Srwatson
610138447Srwatson	ama = arg;
611138447Srwatson
612138447Srwatson	if (ama->ama_fd != -1)
613138447Srwatson		close(ama->ama_fd);
614138447Srwatson
615138447Srwatson	if (ama->ama_unit != -1) {
616138447Srwatson		bzero(&mdio, sizeof(mdio));
617138447Srwatson		mdio.md_version = MDIOVERSION;
618138447Srwatson		mdio.md_unit = ama->ama_unit;
619138447Srwatson		if (ioctl(ama->ama_mdctl_fd, MDIOCDETACH, &mdio) < 0) {
620138447Srwatson			error = errno;
621138447Srwatson			close(ama->ama_mdctl_fd);
622138447Srwatson			errno = error;
623138447Srwatson			warnx("FAIL: aio_md_test: MDIOCDETACH: %s",
624138447Srwatson			    strerror(errno));
625138447Srwatson		}
626138447Srwatson	}
627138447Srwatson
628138447Srwatson	close(ama->ama_mdctl_fd);
629138447Srwatson}
630138447Srwatson
631138447Srwatson#define	MD_LEN		GLOBAL_MAX
632138447Srwatson#define	MD_TIMEOUT	30
633253526Skibstatic void
634138447Srwatsonaio_md_test(void)
635138447Srwatson{
636138447Srwatson	int error, fd, i, mdctl_fd, unit;
637138447Srwatson	char pathname[PATH_MAX];
638138447Srwatson	struct aio_md_arg arg;
639138447Srwatson	struct aio_context ac;
640138447Srwatson	struct md_ioctl mdio;
641138447Srwatson
642138447Srwatson	mdctl_fd = open("/dev/" MDCTL_NAME, O_RDWR, 0);
643138447Srwatson	if (mdctl_fd < 0)
644138447Srwatson		errx(-1, "FAIL: aio_md_test: open(/dev/%s): %s", MDCTL_NAME,
645138447Srwatson		    strerror(errno));
646138447Srwatson
647138447Srwatson	bzero(&mdio, sizeof(mdio));
648138447Srwatson	mdio.md_version = MDIOVERSION;
649138447Srwatson	mdio.md_type = MD_MALLOC;
650138447Srwatson	mdio.md_options = MD_AUTOUNIT | MD_COMPRESS;
651138447Srwatson	mdio.md_mediasize = GLOBAL_MAX;
652138447Srwatson	mdio.md_sectorsize = 512;
653138447Srwatson
654138447Srwatson	arg.ama_mdctl_fd = mdctl_fd;
655138447Srwatson	arg.ama_unit = -1;
656138447Srwatson	arg.ama_fd = -1;
657138447Srwatson	if (ioctl(mdctl_fd, MDIOCATTACH, &mdio) < 0) {
658138447Srwatson		error = errno;
659138447Srwatson		aio_md_cleanup(&arg);
660138447Srwatson		errno = error;
661138447Srwatson		errx(-1, "FAIL: aio_md_test: MDIOCATTACH: %s",
662138447Srwatson		    strerror(errno));
663138447Srwatson	}
664138447Srwatson
665138447Srwatson	arg.ama_unit = unit = mdio.md_unit;
666138447Srwatson	snprintf(pathname, PATH_MAX, "/dev/md%d", unit);
667138447Srwatson	fd = open(pathname, O_RDWR);
668138447Srwatson	if (fd < 0) {
669138447Srwatson		error = errno;
670138447Srwatson		aio_md_cleanup(&arg);
671138447Srwatson		errno = error;
672138447Srwatson		errx(-1, "FAIL: aio_md_test: open(%s): %s", pathname,
673138447Srwatson		    strerror(errno));
674138447Srwatson	}
675138447Srwatson	arg.ama_fd = fd;
676138447Srwatson
677138447Srwatson	aio_context_init(&ac, "aio_md_test", fd, fd, MD_LEN, MD_TIMEOUT,
678138447Srwatson	    aio_md_cleanup, &arg);
679138447Srwatson	aio_write_test(&ac);
680138447Srwatson	aio_read_test(&ac);
681138447Srwatson
682138447Srwatson	aio_md_cleanup(&arg);
683138447Srwatson
684138447Srwatson	fprintf(stderr, "PASS: aio_md_test\n");
685138447Srwatson}
686138447Srwatson
687138447Srwatsonint
688138447Srwatsonmain(int argc, char *argv[])
689138447Srwatson{
690138447Srwatson
691138447Srwatson	aio_sigsys_setup();
692138447Srwatson	aio_file_test();
693138447Srwatson	aio_fifo_test();
694138447Srwatson	aio_unix_socketpair_test();
695138447Srwatson	aio_pty_test();
696138447Srwatson	aio_pipe_test();
697138449Srwatson	if (geteuid() == 0)
698138449Srwatson		aio_md_test();
699138449Srwatson	else
700138449Srwatson		fprintf(stderr, "WARNING: aio_md_test: skipped as euid "
701138449Srwatson		    "!= 0\n");
702138447Srwatson}
703