xref: /freebsd-src/contrib/netbsd-tests/kernel/t_pty.c (revision 57718be8fa0bd5edc11ab9a72e68cc71982939a6)
1*57718be8SEnji Cooper /* $Id: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $ */
2*57718be8SEnji Cooper 
3*57718be8SEnji Cooper /*
4*57718be8SEnji Cooper  * Allocates a pty(4) device, and sends the specified number of packets of the
5*57718be8SEnji Cooper  * specified length though it, while a child reader process reads and reports
6*57718be8SEnji Cooper  * results.
7*57718be8SEnji Cooper  *
8*57718be8SEnji Cooper  * Written by Matthew Mondor
9*57718be8SEnji Cooper  */
10*57718be8SEnji Cooper 
11*57718be8SEnji Cooper #include <sys/cdefs.h>
12*57718be8SEnji Cooper __RCSID("$NetBSD: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $");
13*57718be8SEnji Cooper 
14*57718be8SEnji Cooper #include <errno.h>
15*57718be8SEnji Cooper #include <err.h>
16*57718be8SEnji Cooper #include <fcntl.h>
17*57718be8SEnji Cooper #include <poll.h>
18*57718be8SEnji Cooper #include <stdio.h>
19*57718be8SEnji Cooper #ifdef __linux__
20*57718be8SEnji Cooper #define _XOPEN_SOURCE
21*57718be8SEnji Cooper #define __USE_XOPEN
22*57718be8SEnji Cooper #endif
23*57718be8SEnji Cooper #include <stdint.h>
24*57718be8SEnji Cooper #include <stdlib.h>
25*57718be8SEnji Cooper #include <string.h>
26*57718be8SEnji Cooper #include <termios.h>
27*57718be8SEnji Cooper #include <unistd.h>
28*57718be8SEnji Cooper 
29*57718be8SEnji Cooper #include <sys/ioctl.h>
30*57718be8SEnji Cooper #include <sys/types.h>
31*57718be8SEnji Cooper #include <sys/wait.h>
32*57718be8SEnji Cooper 
33*57718be8SEnji Cooper #ifdef STANDALONE
34*57718be8SEnji Cooper static __dead void	usage(const char *);
35*57718be8SEnji Cooper static void		parse_args(int, char **);
36*57718be8SEnji Cooper #else
37*57718be8SEnji Cooper #include <atf-c.h>
38*57718be8SEnji Cooper #include "../h_macros.h"
39*57718be8SEnji Cooper #endif
40*57718be8SEnji Cooper 
41*57718be8SEnji Cooper static int		pty_open(void);
42*57718be8SEnji Cooper static int		tty_open(const char *);
43*57718be8SEnji Cooper static void		fd_nonblock(int);
44*57718be8SEnji Cooper static pid_t		child_spawn(const char *);
45*57718be8SEnji Cooper static void		run(void);
46*57718be8SEnji Cooper 
47*57718be8SEnji Cooper static size_t		buffer_size = 4096;
48*57718be8SEnji Cooper static size_t		packets = 2;
49*57718be8SEnji Cooper static uint8_t		*dbuf;
50*57718be8SEnji Cooper static int		verbose;
51*57718be8SEnji Cooper static int		qsize;
52*57718be8SEnji Cooper 
53*57718be8SEnji Cooper 
54*57718be8SEnji Cooper static
55*57718be8SEnji Cooper void run(void)
56*57718be8SEnji Cooper {
57*57718be8SEnji Cooper 	size_t i;
58*57718be8SEnji Cooper 	int pty;
59*57718be8SEnji Cooper 	int status;
60*57718be8SEnji Cooper 	pid_t child;
61*57718be8SEnji Cooper 	if ((dbuf = calloc(1, buffer_size)) == NULL)
62*57718be8SEnji Cooper 		err(EXIT_FAILURE, "malloc(%zu)", buffer_size);
63*57718be8SEnji Cooper 
64*57718be8SEnji Cooper 	if (verbose)
65*57718be8SEnji Cooper 		(void)printf(
66*57718be8SEnji Cooper 		    "parent: started; opening PTY and spawning child\n");
67*57718be8SEnji Cooper 	pty = pty_open();
68*57718be8SEnji Cooper 	child = child_spawn(ptsname(pty));
69*57718be8SEnji Cooper 	if (verbose)
70*57718be8SEnji Cooper 		(void)printf("parent: sleeping to make sure child is ready\n");
71*57718be8SEnji Cooper 	(void)sleep(1);
72*57718be8SEnji Cooper 
73*57718be8SEnji Cooper 	for (i = 0; i < buffer_size; i++)
74*57718be8SEnji Cooper 		dbuf[i] = i & 0xff;
75*57718be8SEnji Cooper 
76*57718be8SEnji Cooper 	if (verbose)
77*57718be8SEnji Cooper 		(void)printf("parent: writing\n");
78*57718be8SEnji Cooper 
79*57718be8SEnji Cooper 	for (i = 0; i < packets; i++) {
80*57718be8SEnji Cooper 		ssize_t	size;
81*57718be8SEnji Cooper 
82*57718be8SEnji Cooper 		if (verbose)
83*57718be8SEnji Cooper 			(void)printf(
84*57718be8SEnji Cooper 			    "parent: attempting to write %zu bytes to PTY\n",
85*57718be8SEnji Cooper 			    buffer_size);
86*57718be8SEnji Cooper 		if ((size = write(pty, dbuf, buffer_size)) == -1) {
87*57718be8SEnji Cooper 			err(EXIT_FAILURE, "parent: write()");
88*57718be8SEnji Cooper 			break;
89*57718be8SEnji Cooper 		}
90*57718be8SEnji Cooper 		if (verbose)
91*57718be8SEnji Cooper 			(void)printf("parent: wrote %zd bytes to PTY\n", size);
92*57718be8SEnji Cooper 	}
93*57718be8SEnji Cooper 
94*57718be8SEnji Cooper 	if (verbose)
95*57718be8SEnji Cooper 		(void)printf("parent: waiting for child to exit\n");
96*57718be8SEnji Cooper 	if (waitpid(child, &status, 0) == -1)
97*57718be8SEnji Cooper 		err(EXIT_FAILURE, "waitpid");
98*57718be8SEnji Cooper 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
99*57718be8SEnji Cooper 		errx(EXIT_FAILURE, "child failed");
100*57718be8SEnji Cooper 
101*57718be8SEnji Cooper 	if (verbose)
102*57718be8SEnji Cooper 		(void)printf("parent: closing PTY\n");
103*57718be8SEnji Cooper 	(void)close(pty);
104*57718be8SEnji Cooper 	if (verbose)
105*57718be8SEnji Cooper 		(void)printf("parent: exiting\n");
106*57718be8SEnji Cooper }
107*57718be8SEnji Cooper 
108*57718be8SEnji Cooper static void
109*57718be8SEnji Cooper condition(int fd)
110*57718be8SEnji Cooper {
111*57718be8SEnji Cooper 	struct termios	tios;
112*57718be8SEnji Cooper 
113*57718be8SEnji Cooper 	if (qsize) {
114*57718be8SEnji Cooper 		int opt = qsize;
115*57718be8SEnji Cooper 		if (ioctl(fd, TIOCSQSIZE, &opt) == -1)
116*57718be8SEnji Cooper 			err(EXIT_FAILURE, "Couldn't set tty(4) buffer size");
117*57718be8SEnji Cooper 		if (ioctl(fd, TIOCGQSIZE, &opt) == -1)
118*57718be8SEnji Cooper 			err(EXIT_FAILURE, "Couldn't get tty(4) buffer size");
119*57718be8SEnji Cooper 		if (opt != qsize)
120*57718be8SEnji Cooper 			errx(EXIT_FAILURE, "Wrong qsize %d != %d\n",
121*57718be8SEnji Cooper 			    qsize, opt);
122*57718be8SEnji Cooper 	}
123*57718be8SEnji Cooper 	if (tcgetattr(fd, &tios) == -1)
124*57718be8SEnji Cooper 		err(EXIT_FAILURE, "tcgetattr()");
125*57718be8SEnji Cooper 	cfmakeraw(&tios);
126*57718be8SEnji Cooper 	cfsetspeed(&tios, B921600);
127*57718be8SEnji Cooper 	if (tcsetattr(fd, TCSANOW, &tios) == -1)
128*57718be8SEnji Cooper 		err(EXIT_FAILURE, "tcsetattr()");
129*57718be8SEnji Cooper }
130*57718be8SEnji Cooper 
131*57718be8SEnji Cooper static int
132*57718be8SEnji Cooper pty_open(void)
133*57718be8SEnji Cooper {
134*57718be8SEnji Cooper 	int	fd;
135*57718be8SEnji Cooper 
136*57718be8SEnji Cooper 	if ((fd = posix_openpt(O_RDWR)) == -1)
137*57718be8SEnji Cooper 		err(EXIT_FAILURE, "Couldn't pty(4) device");
138*57718be8SEnji Cooper 	condition(fd);
139*57718be8SEnji Cooper 	if (grantpt(fd) == -1)
140*57718be8SEnji Cooper 		err(EXIT_FAILURE,
141*57718be8SEnji Cooper 		    "Couldn't grant permissions on tty(4) device");
142*57718be8SEnji Cooper 
143*57718be8SEnji Cooper 
144*57718be8SEnji Cooper 	condition(fd);
145*57718be8SEnji Cooper 
146*57718be8SEnji Cooper 	if (unlockpt(fd) == -1)
147*57718be8SEnji Cooper 		err(EXIT_FAILURE, "unlockpt()");
148*57718be8SEnji Cooper 
149*57718be8SEnji Cooper 	return fd;
150*57718be8SEnji Cooper }
151*57718be8SEnji Cooper 
152*57718be8SEnji Cooper static int
153*57718be8SEnji Cooper tty_open(const char *ttydev)
154*57718be8SEnji Cooper {
155*57718be8SEnji Cooper 	int		fd;
156*57718be8SEnji Cooper 
157*57718be8SEnji Cooper 	if ((fd = open(ttydev, O_RDWR, 0)) == -1)
158*57718be8SEnji Cooper 		err(EXIT_FAILURE, "Couldn't open tty(4) device");
159*57718be8SEnji Cooper 
160*57718be8SEnji Cooper #ifdef USE_PPP_DISCIPLINE
161*57718be8SEnji Cooper 	{
162*57718be8SEnji Cooper 		int	opt = PPPDISC;
163*57718be8SEnji Cooper 		if (ioctl(fd, TIOCSETD, &opt) == -1)
164*57718be8SEnji Cooper 			err(EXIT_FAILURE,
165*57718be8SEnji Cooper 			    "Couldn't set tty(4) discipline to PPP");
166*57718be8SEnji Cooper 	}
167*57718be8SEnji Cooper #endif
168*57718be8SEnji Cooper 
169*57718be8SEnji Cooper 	condition(fd);
170*57718be8SEnji Cooper 
171*57718be8SEnji Cooper 	return fd;
172*57718be8SEnji Cooper }
173*57718be8SEnji Cooper 
174*57718be8SEnji Cooper static void
175*57718be8SEnji Cooper fd_nonblock(int fd)
176*57718be8SEnji Cooper {
177*57718be8SEnji Cooper 	int	opt;
178*57718be8SEnji Cooper 
179*57718be8SEnji Cooper 	if ((opt = fcntl(fd, F_GETFL, NULL)) == -1)
180*57718be8SEnji Cooper 		err(EXIT_FAILURE, "fcntl()");
181*57718be8SEnji Cooper 	if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1)
182*57718be8SEnji Cooper 		err(EXIT_FAILURE, "fcntl()");
183*57718be8SEnji Cooper }
184*57718be8SEnji Cooper 
185*57718be8SEnji Cooper static pid_t
186*57718be8SEnji Cooper child_spawn(const char *ttydev)
187*57718be8SEnji Cooper {
188*57718be8SEnji Cooper 	pid_t		pid;
189*57718be8SEnji Cooper 	int		tty;
190*57718be8SEnji Cooper 	struct pollfd	pfd;
191*57718be8SEnji Cooper 	size_t		total = 0;
192*57718be8SEnji Cooper 
193*57718be8SEnji Cooper 	if ((pid = fork()) == -1)
194*57718be8SEnji Cooper 		err(EXIT_FAILURE, "fork()");
195*57718be8SEnji Cooper 	(void)setsid();
196*57718be8SEnji Cooper 	if (pid != 0)
197*57718be8SEnji Cooper 		return pid;
198*57718be8SEnji Cooper 
199*57718be8SEnji Cooper 	if (verbose)
200*57718be8SEnji Cooper 		(void)printf("child: started; open \"%s\"\n", ttydev);
201*57718be8SEnji Cooper 	tty = tty_open(ttydev);
202*57718be8SEnji Cooper 	fd_nonblock(tty);
203*57718be8SEnji Cooper 
204*57718be8SEnji Cooper 	if (verbose)
205*57718be8SEnji Cooper 		(void)printf("child: TTY open, starting read loop\n");
206*57718be8SEnji Cooper 	pfd.fd = tty;
207*57718be8SEnji Cooper 	pfd.events = POLLIN;
208*57718be8SEnji Cooper 	pfd.revents = 0;
209*57718be8SEnji Cooper 	for (;;) {
210*57718be8SEnji Cooper 		int	ret;
211*57718be8SEnji Cooper 		ssize_t	size;
212*57718be8SEnji Cooper 
213*57718be8SEnji Cooper 		if (verbose)
214*57718be8SEnji Cooper 			(void)printf("child: polling\n");
215*57718be8SEnji Cooper 		if ((ret = poll(&pfd, 1, 2000)) == -1)
216*57718be8SEnji Cooper 			err(EXIT_FAILURE, "child: poll()");
217*57718be8SEnji Cooper 		if (ret == 0)
218*57718be8SEnji Cooper 			break;
219*57718be8SEnji Cooper 		if ((pfd.revents & POLLERR) != 0)
220*57718be8SEnji Cooper 			break;
221*57718be8SEnji Cooper 		if ((pfd.revents & POLLIN) != 0) {
222*57718be8SEnji Cooper 			for (;;) {
223*57718be8SEnji Cooper 				if (verbose)
224*57718be8SEnji Cooper 					(void)printf(
225*57718be8SEnji Cooper 					    "child: attempting to read %zu"
226*57718be8SEnji Cooper 					    " bytes\n", buffer_size);
227*57718be8SEnji Cooper 				if ((size = read(tty, dbuf, buffer_size))
228*57718be8SEnji Cooper 				    == -1) {
229*57718be8SEnji Cooper 					if (errno == EAGAIN)
230*57718be8SEnji Cooper 						break;
231*57718be8SEnji Cooper 					err(EXIT_FAILURE, "child: read()");
232*57718be8SEnji Cooper 				}
233*57718be8SEnji Cooper 				if (qsize && size < qsize &&
234*57718be8SEnji Cooper 				    (size_t)size < buffer_size)
235*57718be8SEnji Cooper 					errx(EXIT_FAILURE, "read returned %zd "
236*57718be8SEnji Cooper 					    "less than the queue size %d",
237*57718be8SEnji Cooper 					    size, qsize);
238*57718be8SEnji Cooper 				if (verbose)
239*57718be8SEnji Cooper 					(void)printf(
240*57718be8SEnji Cooper 					    "child: read %zd bytes from TTY\n",
241*57718be8SEnji Cooper 					    size);
242*57718be8SEnji Cooper 				if (size == 0)
243*57718be8SEnji Cooper 					goto end;
244*57718be8SEnji Cooper 				total += size;
245*57718be8SEnji Cooper 			}
246*57718be8SEnji Cooper 		}
247*57718be8SEnji Cooper 	}
248*57718be8SEnji Cooper end:
249*57718be8SEnji Cooper 	if (verbose)
250*57718be8SEnji Cooper 		(void)printf("child: closing TTY %zu\n", total);
251*57718be8SEnji Cooper 	(void)close(tty);
252*57718be8SEnji Cooper 	if (verbose)
253*57718be8SEnji Cooper 		(void)printf("child: exiting\n");
254*57718be8SEnji Cooper 	if (total != buffer_size * packets)
255*57718be8SEnji Cooper 		errx(EXIT_FAILURE,
256*57718be8SEnji Cooper 		    "Lost data %zu != %zu\n", total, buffer_size * packets);
257*57718be8SEnji Cooper 
258*57718be8SEnji Cooper 	exit(EXIT_SUCCESS);
259*57718be8SEnji Cooper }
260*57718be8SEnji Cooper 
261*57718be8SEnji Cooper #ifdef STANDALONE
262*57718be8SEnji Cooper static void
263*57718be8SEnji Cooper usage(const char *msg)
264*57718be8SEnji Cooper {
265*57718be8SEnji Cooper 
266*57718be8SEnji Cooper 	if (msg != NULL)
267*57718be8SEnji Cooper 		(void) fprintf(stderr, "\n%s\n\n", msg);
268*57718be8SEnji Cooper 
269*57718be8SEnji Cooper 	(void)fprintf(stderr,
270*57718be8SEnji Cooper 	    "Usage: %s [-v] [-q <qsize>] [-s <packetsize>] [-n <packets>]\n",
271*57718be8SEnji Cooper 		getprogname());
272*57718be8SEnji Cooper 
273*57718be8SEnji Cooper 	exit(EXIT_FAILURE);
274*57718be8SEnji Cooper }
275*57718be8SEnji Cooper 
276*57718be8SEnji Cooper static void
277*57718be8SEnji Cooper parse_args(int argc, char **argv)
278*57718be8SEnji Cooper {
279*57718be8SEnji Cooper 	int	ch;
280*57718be8SEnji Cooper 
281*57718be8SEnji Cooper 	while ((ch = getopt(argc, argv, "n:q:s:v")) != -1) {
282*57718be8SEnji Cooper 		switch (ch) {
283*57718be8SEnji Cooper 		case 'n':
284*57718be8SEnji Cooper 			packets = (size_t)atoi(optarg);
285*57718be8SEnji Cooper 			break;
286*57718be8SEnji Cooper 		case 'q':
287*57718be8SEnji Cooper 			qsize = atoi(optarg);
288*57718be8SEnji Cooper 			break;
289*57718be8SEnji Cooper 		case 's':
290*57718be8SEnji Cooper 			buffer_size = (size_t)atoi(optarg);
291*57718be8SEnji Cooper 			break;
292*57718be8SEnji Cooper 		case 'v':
293*57718be8SEnji Cooper 			verbose++;
294*57718be8SEnji Cooper 			break;
295*57718be8SEnji Cooper 		default:
296*57718be8SEnji Cooper 			usage(NULL);
297*57718be8SEnji Cooper 			break;
298*57718be8SEnji Cooper 		}
299*57718be8SEnji Cooper 	}
300*57718be8SEnji Cooper 	if (buffer_size < 0 || buffer_size > 65536)
301*57718be8SEnji Cooper 		usage("-s must be between 0 and 65536");
302*57718be8SEnji Cooper 	if (packets < 1 || packets > 100)
303*57718be8SEnji Cooper 		usage("-p must be between 1 and 100");
304*57718be8SEnji Cooper }
305*57718be8SEnji Cooper 
306*57718be8SEnji Cooper int
307*57718be8SEnji Cooper main(int argc, char **argv)
308*57718be8SEnji Cooper {
309*57718be8SEnji Cooper 
310*57718be8SEnji Cooper 	parse_args(argc, argv);
311*57718be8SEnji Cooper 	run();
312*57718be8SEnji Cooper 	exit(EXIT_SUCCESS);
313*57718be8SEnji Cooper }
314*57718be8SEnji Cooper 
315*57718be8SEnji Cooper #else
316*57718be8SEnji Cooper ATF_TC(pty_no_queue);
317*57718be8SEnji Cooper 
318*57718be8SEnji Cooper ATF_TC_HEAD(pty_no_queue, tc)
319*57718be8SEnji Cooper {
320*57718be8SEnji Cooper         atf_tc_set_md_var(tc, "descr", "Checks that writing to pty "
321*57718be8SEnji Cooper 	    "does not lose data with the default queue size of 1024");
322*57718be8SEnji Cooper }
323*57718be8SEnji Cooper 
324*57718be8SEnji Cooper ATF_TC_BODY(pty_no_queue, tc)
325*57718be8SEnji Cooper {
326*57718be8SEnji Cooper 	qsize = 0;
327*57718be8SEnji Cooper 	run();
328*57718be8SEnji Cooper }
329*57718be8SEnji Cooper 
330*57718be8SEnji Cooper ATF_TC(pty_queue);
331*57718be8SEnji Cooper 
332*57718be8SEnji Cooper ATF_TC_HEAD(pty_queue, tc)
333*57718be8SEnji Cooper {
334*57718be8SEnji Cooper         atf_tc_set_md_var(tc, "descr", "Checks that writing to pty "
335*57718be8SEnji Cooper 	    "does not lose data with the a queue size of 4096");
336*57718be8SEnji Cooper }
337*57718be8SEnji Cooper 
338*57718be8SEnji Cooper ATF_TC_BODY(pty_queue, tc)
339*57718be8SEnji Cooper {
340*57718be8SEnji Cooper 	qsize = 4096;
341*57718be8SEnji Cooper 	run();
342*57718be8SEnji Cooper }
343*57718be8SEnji Cooper 
344*57718be8SEnji Cooper ATF_TP_ADD_TCS(tp)
345*57718be8SEnji Cooper {
346*57718be8SEnji Cooper         ATF_TP_ADD_TC(tp, pty_no_queue);
347*57718be8SEnji Cooper         ATF_TP_ADD_TC(tp, pty_queue);
348*57718be8SEnji Cooper 
349*57718be8SEnji Cooper         return atf_no_error();
350*57718be8SEnji Cooper }
351*57718be8SEnji Cooper #endif
352