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