]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/netbsd-tests/kernel/t_pty.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / netbsd-tests / kernel / t_pty.c
1 /* $Id: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $ */
2
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.
7  *
8  * Written by Matthew Mondor
9  */
10
11 #include <sys/cdefs.h>
12 __RCSID("$NetBSD: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $");
13
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>
28
29 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32
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
40
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);
46
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;
52
53
54 static
55 void run(void)
56 {
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);
63
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);
72
73         for (i = 0; i < buffer_size; i++)
74                 dbuf[i] = i & 0xff;
75
76         if (verbose)
77                 (void)printf("parent: writing\n");
78
79         for (i = 0; i < packets; i++) {
80                 ssize_t size;
81
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;
89                 }
90                 if (verbose)
91                         (void)printf("parent: wrote %zd bytes to PTY\n", size);
92         }
93
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");
100
101         if (verbose)
102                 (void)printf("parent: closing PTY\n");
103         (void)close(pty);
104         if (verbose)
105                 (void)printf("parent: exiting\n");
106 }
107
108 static void
109 condition(int fd)
110 {
111         struct termios  tios;
112
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);
122         }
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()");
129 }
130
131 static int
132 pty_open(void)
133 {
134         int     fd;
135
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");
142
143
144         condition(fd);
145
146         if (unlockpt(fd) == -1)
147                 err(EXIT_FAILURE, "unlockpt()");
148
149         return fd;
150 }
151
152 static int
153 tty_open(const char *ttydev)
154 {
155         int             fd;
156
157         if ((fd = open(ttydev, O_RDWR, 0)) == -1)
158                 err(EXIT_FAILURE, "Couldn't open tty(4) device");
159
160 #ifdef USE_PPP_DISCIPLINE
161         {
162                 int     opt = PPPDISC;
163                 if (ioctl(fd, TIOCSETD, &opt) == -1)
164                         err(EXIT_FAILURE,
165                             "Couldn't set tty(4) discipline to PPP");
166         }
167 #endif
168
169         condition(fd);
170
171         return fd;
172 }
173
174 static void
175 fd_nonblock(int fd)
176 {
177         int     opt;
178
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()");
183 }
184
185 static pid_t
186 child_spawn(const char *ttydev)
187 {
188         pid_t           pid;
189         int             tty;
190         struct pollfd   pfd;
191         size_t          total = 0;
192
193         if ((pid = fork()) == -1)
194                 err(EXIT_FAILURE, "fork()");
195         (void)setsid();
196         if (pid != 0)
197                 return pid;
198
199         if (verbose)
200                 (void)printf("child: started; open \"%s\"\n", ttydev);
201         tty = tty_open(ttydev);
202         fd_nonblock(tty);
203
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;
212
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()");
232                                 }
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;
245                         }
246                 }
247         }
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);
257
258         exit(EXIT_SUCCESS);
259 }
260
261 #ifdef STANDALONE
262 static void
263 usage(const char *msg)
264 {
265
266         if (msg != NULL)
267                 (void) fprintf(stderr, "\n%s\n\n", msg);
268
269         (void)fprintf(stderr,
270             "Usage: %s [-v] [-q <qsize>] [-s <packetsize>] [-n <packets>]\n",
271                 getprogname());
272
273         exit(EXIT_FAILURE);
274 }
275
276 static void
277 parse_args(int argc, char **argv)
278 {
279         int     ch;
280
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;
298                 }
299         }
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");
304 }
305
306 int
307 main(int argc, char **argv)
308 {
309
310         parse_args(argc, argv);
311         run();
312         exit(EXIT_SUCCESS);
313 }
314
315 #else
316 ATF_TC(pty_no_queue);
317
318 ATF_TC_HEAD(pty_no_queue, tc)
319 {
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");
322 }
323
324 ATF_TC_BODY(pty_no_queue, tc)
325 {
326         qsize = 0;
327         run();
328 }
329
330 ATF_TC(pty_queue);
331
332 ATF_TC_HEAD(pty_queue, tc)
333 {
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");
336 }
337
338 ATF_TC_BODY(pty_queue, tc)
339 {
340         qsize = 4096;
341         run();
342 }
343
344 ATF_TP_ADD_TCS(tp)
345 {
346         ATF_TP_ADD_TC(tp, pty_no_queue);
347         ATF_TP_ADD_TC(tp, pty_queue);
348
349         return atf_no_error();
350 }
351 #endif