xref: /netbsd-src/tests/kernel/t_pty.c (revision f01e01eb6a56a2bd3224e5c5dcadc7c74b8d2cbf)
1*f01e01ebSrin /* $Id: t_pty.c,v 1.5 2020/06/24 07:02:57 rin Exp $ */
26f52d919Schristos 
36f52d919Schristos /*
46f52d919Schristos  * Allocates a pty(4) device, and sends the specified number of packets of the
56f52d919Schristos  * specified length though it, while a child reader process reads and reports
66f52d919Schristos  * results.
76f52d919Schristos  *
86f52d919Schristos  * Written by Matthew Mondor
96f52d919Schristos  */
106f52d919Schristos 
116f52d919Schristos #include <sys/cdefs.h>
12*f01e01ebSrin __RCSID("$NetBSD: t_pty.c,v 1.5 2020/06/24 07:02:57 rin Exp $");
136f52d919Schristos 
146f52d919Schristos #include <errno.h>
156f52d919Schristos #include <err.h>
166f52d919Schristos #include <fcntl.h>
176f52d919Schristos #include <poll.h>
186f52d919Schristos #include <stdio.h>
196f52d919Schristos #ifdef __linux__
206f52d919Schristos #define _XOPEN_SOURCE
216f52d919Schristos #define __USE_XOPEN
226f52d919Schristos #endif
236f52d919Schristos #include <stdint.h>
246f52d919Schristos #include <stdlib.h>
256f52d919Schristos #include <string.h>
266f52d919Schristos #include <termios.h>
276f52d919Schristos #include <unistd.h>
286f52d919Schristos 
296f52d919Schristos #include <sys/ioctl.h>
306f52d919Schristos #include <sys/types.h>
316f52d919Schristos #include <sys/wait.h>
326f52d919Schristos 
336f52d919Schristos #ifdef STANDALONE
345e62095eSrin #define	atf_tc_fail_errno(fmt, ...)	err(EXIT_FAILURE, fmt, ## __VA_ARGS__)
355e62095eSrin #define	atf_tc_fail(fmt, ...)		errx(EXIT_FAILURE, fmt, ## __VA_ARGS__)
366f52d919Schristos static __dead void	usage(const char *);
376f52d919Schristos static void		parse_args(int, char **);
386f52d919Schristos #else
396f52d919Schristos #include <atf-c.h>
40c54cb811Schristos #include "h_macros.h"
416f52d919Schristos #endif
426f52d919Schristos 
436f52d919Schristos static int		pty_open(void);
446f52d919Schristos static int		tty_open(const char *);
456f52d919Schristos static void		fd_nonblock(int);
466f52d919Schristos static pid_t		child_spawn(const char *);
476f52d919Schristos static void		run(void);
486f52d919Schristos 
496f52d919Schristos static size_t		buffer_size = 4096;
506f52d919Schristos static size_t		packets = 2;
516f52d919Schristos static uint8_t		*dbuf;
526f52d919Schristos static int		verbose;
536f52d919Schristos static int		qsize;
546f52d919Schristos 
556f52d919Schristos 
566f52d919Schristos static
run(void)576f52d919Schristos void run(void)
586f52d919Schristos {
596f52d919Schristos 	size_t i;
606f52d919Schristos 	int pty;
616f52d919Schristos 	int status;
626f52d919Schristos 	pid_t child;
636f52d919Schristos 	if ((dbuf = calloc(1, buffer_size)) == NULL)
645e62095eSrin 		atf_tc_fail_errno("malloc(%zu)", buffer_size);
656f52d919Schristos 
666f52d919Schristos 	if (verbose)
676f52d919Schristos 		(void)printf(
686f52d919Schristos 		    "parent: started; opening PTY and spawning child\n");
696f52d919Schristos 	pty = pty_open();
706f52d919Schristos 	child = child_spawn(ptsname(pty));
716f52d919Schristos 	if (verbose)
726f52d919Schristos 		(void)printf("parent: sleeping to make sure child is ready\n");
736f52d919Schristos 	(void)sleep(1);
746f52d919Schristos 
756f52d919Schristos 	for (i = 0; i < buffer_size; i++)
766f52d919Schristos 		dbuf[i] = i & 0xff;
776f52d919Schristos 
786f52d919Schristos 	if (verbose)
796f52d919Schristos 		(void)printf("parent: writing\n");
806f52d919Schristos 
816f52d919Schristos 	for (i = 0; i < packets; i++) {
826f52d919Schristos 		ssize_t	size;
836f52d919Schristos 
846f52d919Schristos 		if (verbose)
856f52d919Schristos 			(void)printf(
866f52d919Schristos 			    "parent: attempting to write %zu bytes to PTY\n",
876f52d919Schristos 			    buffer_size);
886f52d919Schristos 		if ((size = write(pty, dbuf, buffer_size)) == -1) {
895e62095eSrin 			atf_tc_fail_errno("parent: write()");
906f52d919Schristos 			break;
916f52d919Schristos 		}
926f52d919Schristos 		if (verbose)
936f52d919Schristos 			(void)printf("parent: wrote %zd bytes to PTY\n", size);
946f52d919Schristos 	}
956f52d919Schristos 
966f52d919Schristos 	if (verbose)
976f52d919Schristos 		(void)printf("parent: waiting for child to exit\n");
986f52d919Schristos 	if (waitpid(child, &status, 0) == -1)
995e62095eSrin 		atf_tc_fail_errno("waitpid");
1006f52d919Schristos 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
1015e62095eSrin 		atf_tc_fail("child failed");
1026f52d919Schristos 
1036f52d919Schristos 	if (verbose)
1046f52d919Schristos 		(void)printf("parent: closing PTY\n");
1056f52d919Schristos 	(void)close(pty);
1066f52d919Schristos 	if (verbose)
1076f52d919Schristos 		(void)printf("parent: exiting\n");
1086f52d919Schristos }
1096f52d919Schristos 
1106f52d919Schristos static void
condition(int fd)1116f52d919Schristos condition(int fd)
1126f52d919Schristos {
1136f52d919Schristos 	struct termios	tios;
1146f52d919Schristos 
1156f52d919Schristos 	if (qsize) {
1166f52d919Schristos 		int opt = qsize;
1176f52d919Schristos 		if (ioctl(fd, TIOCSQSIZE, &opt) == -1)
1185e62095eSrin 			atf_tc_fail_errno("Couldn't set tty(4) buffer size");
1196f52d919Schristos 		if (ioctl(fd, TIOCGQSIZE, &opt) == -1)
1205e62095eSrin 			atf_tc_fail_errno("Couldn't get tty(4) buffer size");
1216f52d919Schristos 		if (opt != qsize)
122*f01e01ebSrin 			atf_tc_fail("Wrong qsize %d != %d\n", qsize, opt);
1236f52d919Schristos 	}
1246f52d919Schristos 	if (tcgetattr(fd, &tios) == -1)
1255e62095eSrin 		atf_tc_fail_errno("tcgetattr()");
1266f52d919Schristos 	cfmakeraw(&tios);
1276f52d919Schristos 	cfsetspeed(&tios, B921600);
1286f52d919Schristos 	if (tcsetattr(fd, TCSANOW, &tios) == -1)
1295e62095eSrin 		atf_tc_fail_errno("tcsetattr()");
1306f52d919Schristos }
1316f52d919Schristos 
1326f52d919Schristos static int
pty_open(void)1336f52d919Schristos pty_open(void)
1346f52d919Schristos {
1356f52d919Schristos 	int	fd;
1366f52d919Schristos 
1376f52d919Schristos 	if ((fd = posix_openpt(O_RDWR)) == -1)
1385e62095eSrin 		atf_tc_fail_errno("Couldn't pty(4) device");
1396f52d919Schristos 	condition(fd);
1406f52d919Schristos 	if (grantpt(fd) == -1)
1415e62095eSrin 		atf_tc_fail_errno(
1426f52d919Schristos 		    "Couldn't grant permissions on tty(4) device");
1436f52d919Schristos 
1446f52d919Schristos 
1456f52d919Schristos 	condition(fd);
1466f52d919Schristos 
1476f52d919Schristos 	if (unlockpt(fd) == -1)
1485e62095eSrin 		atf_tc_fail_errno("unlockpt()");
1496f52d919Schristos 
1506f52d919Schristos 	return fd;
1516f52d919Schristos }
1526f52d919Schristos 
1536f52d919Schristos static int
tty_open(const char * ttydev)1546f52d919Schristos tty_open(const char *ttydev)
1556f52d919Schristos {
1566f52d919Schristos 	int		fd;
1576f52d919Schristos 
1586f52d919Schristos 	if ((fd = open(ttydev, O_RDWR, 0)) == -1)
1595e62095eSrin 		atf_tc_fail_errno("Couldn't open tty(4) device");
1606f52d919Schristos 
1616f52d919Schristos #ifdef USE_PPP_DISCIPLINE
1626f52d919Schristos 	{
1636f52d919Schristos 		int	opt = PPPDISC;
1646f52d919Schristos 		if (ioctl(fd, TIOCSETD, &opt) == -1)
1655e62095eSrin 			atf_tc_fail_errno(
1666f52d919Schristos 			    "Couldn't set tty(4) discipline to PPP");
1676f52d919Schristos 	}
1686f52d919Schristos #endif
1696f52d919Schristos 
1706f52d919Schristos 	condition(fd);
1716f52d919Schristos 
1726f52d919Schristos 	return fd;
1736f52d919Schristos }
1746f52d919Schristos 
1756f52d919Schristos static void
fd_nonblock(int fd)1766f52d919Schristos fd_nonblock(int fd)
1776f52d919Schristos {
1786f52d919Schristos 	int	opt;
1796f52d919Schristos 
1806f52d919Schristos 	if ((opt = fcntl(fd, F_GETFL, NULL)) == -1)
1815e62095eSrin 		atf_tc_fail_errno("fcntl()");
1826f52d919Schristos 	if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1)
1835e62095eSrin 		atf_tc_fail_errno("fcntl()");
1846f52d919Schristos }
1856f52d919Schristos 
1866f52d919Schristos static pid_t
child_spawn(const char * ttydev)1876f52d919Schristos child_spawn(const char *ttydev)
1886f52d919Schristos {
1896f52d919Schristos 	pid_t		pid;
1906f52d919Schristos 	int		tty;
1916f52d919Schristos 	struct pollfd	pfd;
1926f52d919Schristos 	size_t		total = 0;
1936f52d919Schristos 
1946f52d919Schristos 	if ((pid = fork()) == -1)
1955e62095eSrin 		atf_tc_fail_errno("fork()");
1966f52d919Schristos 	(void)setsid();
1976f52d919Schristos 	if (pid != 0)
1986f52d919Schristos 		return pid;
1996f52d919Schristos 
2006f52d919Schristos 	if (verbose)
2016f52d919Schristos 		(void)printf("child: started; open \"%s\"\n", ttydev);
2026f52d919Schristos 	tty = tty_open(ttydev);
2036f52d919Schristos 	fd_nonblock(tty);
2046f52d919Schristos 
2056f52d919Schristos 	if (verbose)
2066f52d919Schristos 		(void)printf("child: TTY open, starting read loop\n");
2076f52d919Schristos 	pfd.fd = tty;
2086f52d919Schristos 	pfd.events = POLLIN;
2096f52d919Schristos 	pfd.revents = 0;
2106f52d919Schristos 	for (;;) {
2116f52d919Schristos 		int	ret;
2126f52d919Schristos 		ssize_t	size;
2136f52d919Schristos 
2146f52d919Schristos 		if (verbose)
2156f52d919Schristos 			(void)printf("child: polling\n");
2166f52d919Schristos 		if ((ret = poll(&pfd, 1, 2000)) == -1)
2176f52d919Schristos 			err(EXIT_FAILURE, "child: poll()");
2186f52d919Schristos 		if (ret == 0)
2196f52d919Schristos 			break;
2206f52d919Schristos 		if ((pfd.revents & POLLERR) != 0)
2216f52d919Schristos 			break;
2226f52d919Schristos 		if ((pfd.revents & POLLIN) != 0) {
2236f52d919Schristos 			for (;;) {
2246f52d919Schristos 				if (verbose)
2256f52d919Schristos 					(void)printf(
2266f52d919Schristos 					    "child: attempting to read %zu"
2276f52d919Schristos 					    " bytes\n", buffer_size);
2286f52d919Schristos 				if ((size = read(tty, dbuf, buffer_size))
2296f52d919Schristos 				    == -1) {
2306f52d919Schristos 					if (errno == EAGAIN)
2316f52d919Schristos 						break;
2326f52d919Schristos 					err(EXIT_FAILURE, "child: read()");
2336f52d919Schristos 				}
2346f52d919Schristos 				if (verbose)
2356f52d919Schristos 					(void)printf(
2366f52d919Schristos 					    "child: read %zd bytes from TTY\n",
2376f52d919Schristos 					    size);
2386f52d919Schristos 				if (size == 0)
2396f52d919Schristos 					goto end;
2406f52d919Schristos 				total += size;
2416f52d919Schristos 			}
2426f52d919Schristos 		}
2436f52d919Schristos 	}
2446f52d919Schristos end:
2456f52d919Schristos 	if (verbose)
2466f52d919Schristos 		(void)printf("child: closing TTY %zu\n", total);
2476f52d919Schristos 	(void)close(tty);
2486f52d919Schristos 	if (verbose)
2496f52d919Schristos 		(void)printf("child: exiting\n");
2506f52d919Schristos 	if (total != buffer_size * packets)
2516f52d919Schristos 		errx(EXIT_FAILURE,
2526f52d919Schristos 		    "Lost data %zu != %zu\n", total, buffer_size * packets);
2536f52d919Schristos 
2546f52d919Schristos 	exit(EXIT_SUCCESS);
2556f52d919Schristos }
2566f52d919Schristos 
2576f52d919Schristos #ifdef STANDALONE
2586f52d919Schristos static void
usage(const char * msg)2596f52d919Schristos usage(const char *msg)
2606f52d919Schristos {
2616f52d919Schristos 
2626f52d919Schristos 	if (msg != NULL)
2636f52d919Schristos 		(void) fprintf(stderr, "\n%s\n\n", msg);
2646f52d919Schristos 
2656f52d919Schristos 	(void)fprintf(stderr,
2666f52d919Schristos 	    "Usage: %s [-v] [-q <qsize>] [-s <packetsize>] [-n <packets>]\n",
2676f52d919Schristos 		getprogname());
2686f52d919Schristos 
2696f52d919Schristos 	exit(EXIT_FAILURE);
2706f52d919Schristos }
2716f52d919Schristos 
2726f52d919Schristos static void
parse_args(int argc,char ** argv)2736f52d919Schristos parse_args(int argc, char **argv)
2746f52d919Schristos {
2756f52d919Schristos 	int	ch;
2766f52d919Schristos 
2776f52d919Schristos 	while ((ch = getopt(argc, argv, "n:q:s:v")) != -1) {
2786f52d919Schristos 		switch (ch) {
2796f52d919Schristos 		case 'n':
2806f52d919Schristos 			packets = (size_t)atoi(optarg);
2816f52d919Schristos 			break;
2826f52d919Schristos 		case 'q':
2836f52d919Schristos 			qsize = atoi(optarg);
2846f52d919Schristos 			break;
2856f52d919Schristos 		case 's':
2866f52d919Schristos 			buffer_size = (size_t)atoi(optarg);
2876f52d919Schristos 			break;
2886f52d919Schristos 		case 'v':
2896f52d919Schristos 			verbose++;
2906f52d919Schristos 			break;
2916f52d919Schristos 		default:
2926f52d919Schristos 			usage(NULL);
2936f52d919Schristos 			break;
2946f52d919Schristos 		}
2956f52d919Schristos 	}
2966f52d919Schristos 	if (buffer_size < 0 || buffer_size > 65536)
2976f52d919Schristos 		usage("-s must be between 0 and 65536");
2986f52d919Schristos 	if (packets < 1 || packets > 100)
2996f52d919Schristos 		usage("-p must be between 1 and 100");
3006f52d919Schristos }
3016f52d919Schristos 
3026f52d919Schristos int
main(int argc,char ** argv)3036f52d919Schristos main(int argc, char **argv)
3046f52d919Schristos {
3056f52d919Schristos 
3066f52d919Schristos 	parse_args(argc, argv);
3076f52d919Schristos 	run();
3086f52d919Schristos 	exit(EXIT_SUCCESS);
3096f52d919Schristos }
3106f52d919Schristos 
3116f52d919Schristos #else
3126f52d919Schristos ATF_TC(pty_no_queue);
3136f52d919Schristos 
ATF_TC_HEAD(pty_no_queue,tc)3146f52d919Schristos ATF_TC_HEAD(pty_no_queue, tc)
3156f52d919Schristos {
3166f52d919Schristos         atf_tc_set_md_var(tc, "descr", "Checks that writing to pty "
3176f52d919Schristos 	    "does not lose data with the default queue size of 1024");
3186f52d919Schristos }
3196f52d919Schristos 
ATF_TC_BODY(pty_no_queue,tc)3206f52d919Schristos ATF_TC_BODY(pty_no_queue, tc)
3216f52d919Schristos {
3226f52d919Schristos 	qsize = 0;
3236f52d919Schristos 	run();
3246f52d919Schristos }
3256f52d919Schristos 
3266f52d919Schristos ATF_TC(pty_queue);
3276f52d919Schristos 
ATF_TC_HEAD(pty_queue,tc)3286f52d919Schristos ATF_TC_HEAD(pty_queue, tc)
3296f52d919Schristos {
3306f52d919Schristos         atf_tc_set_md_var(tc, "descr", "Checks that writing to pty "
3316f52d919Schristos 	    "does not lose data with the a queue size of 4096");
3326f52d919Schristos }
3336f52d919Schristos 
ATF_TC_BODY(pty_queue,tc)3346f52d919Schristos ATF_TC_BODY(pty_queue, tc)
3356f52d919Schristos {
3366f52d919Schristos 	qsize = 4096;
3376f52d919Schristos 	run();
3386f52d919Schristos }
3396f52d919Schristos 
ATF_TP_ADD_TCS(tp)3406f52d919Schristos ATF_TP_ADD_TCS(tp)
3416f52d919Schristos {
3426f52d919Schristos         ATF_TP_ADD_TC(tp, pty_no_queue);
3436f52d919Schristos         ATF_TP_ADD_TC(tp, pty_queue);
3446f52d919Schristos 
3456f52d919Schristos         return atf_no_error();
3466f52d919Schristos }
3476f52d919Schristos #endif
348