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