t_pty.c revision 272345
133965Sjdp/* $Id: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $ */ 233965Sjdp 333965Sjdp/* 433965Sjdp * Allocates a pty(4) device, and sends the specified number of packets of the 533965Sjdp * specified length though it, while a child reader process reads and reports 633965Sjdp * results. 733965Sjdp * 833965Sjdp * Written by Matthew Mondor 933965Sjdp */ 1033965Sjdp 1133965Sjdp#include <sys/cdefs.h> 1233965Sjdp__RCSID("$NetBSD: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $"); 1333965Sjdp 1433965Sjdp#include <errno.h> 1533965Sjdp#include <err.h> 1633965Sjdp#include <fcntl.h> 1733965Sjdp#include <poll.h> 1833965Sjdp#include <stdio.h> 1933965Sjdp#ifdef __linux__ 2033965Sjdp#define _XOPEN_SOURCE 2133965Sjdp#define __USE_XOPEN 2233965Sjdp#endif 2333965Sjdp#include <stdint.h> 2433965Sjdp#include <stdlib.h> 2589857Sobrien#include <string.h> 2689857Sobrien#include <termios.h> 2789857Sobrien#include <unistd.h> 2889857Sobrien 2989857Sobrien#include <sys/ioctl.h> 3089857Sobrien#include <sys/types.h> 3189857Sobrien#include <sys/wait.h> 3289857Sobrien 3389857Sobrien#ifdef STANDALONE 3489857Sobrienstatic __dead void usage(const char *); 3589857Sobrienstatic void parse_args(int, char **); 3689857Sobrien#else 3789857Sobrien#include <atf-c.h> 3889857Sobrien#include "../h_macros.h" 3989857Sobrien#endif 4077298Sobrien 4177298Sobrienstatic int pty_open(void); 4233965Sjdpstatic int tty_open(const char *); 4333965Sjdpstatic void fd_nonblock(int); 4433965Sjdpstatic pid_t child_spawn(const char *); 4533965Sjdpstatic void run(void); 4633965Sjdp 4733965Sjdpstatic size_t buffer_size = 4096; 4833965Sjdpstatic size_t packets = 2; 4933965Sjdpstatic uint8_t *dbuf; 5033965Sjdpstatic int verbose; 5133965Sjdpstatic int qsize; 5233965Sjdp 5333965Sjdp 5433965Sjdpstatic 5533965Sjdpvoid run(void) 5633965Sjdp{ 5733965Sjdp size_t i; 5833965Sjdp int pty; 5933965Sjdp int status; 6033965Sjdp pid_t child; 6177298Sobrien if ((dbuf = calloc(1, buffer_size)) == NULL) 6233965Sjdp err(EXIT_FAILURE, "malloc(%zu)", buffer_size); 6333965Sjdp 6433965Sjdp if (verbose) 6533965Sjdp (void)printf( 6633965Sjdp "parent: started; opening PTY and spawning child\n"); 6733965Sjdp pty = pty_open(); 6833965Sjdp child = child_spawn(ptsname(pty)); 6933965Sjdp if (verbose) 7033965Sjdp (void)printf("parent: sleeping to make sure child is ready\n"); 7133965Sjdp (void)sleep(1); 7233965Sjdp 7333965Sjdp for (i = 0; i < buffer_size; i++) 7433965Sjdp dbuf[i] = i & 0xff; 7533965Sjdp 7633965Sjdp if (verbose) 7733965Sjdp (void)printf("parent: writing\n"); 7833965Sjdp 7933965Sjdp for (i = 0; i < packets; i++) { 8033965Sjdp ssize_t size; 8133965Sjdp 8233965Sjdp if (verbose) 8333965Sjdp (void)printf( 8433965Sjdp "parent: attempting to write %zu bytes to PTY\n", 8533965Sjdp buffer_size); 8633965Sjdp if ((size = write(pty, dbuf, buffer_size)) == -1) { 8733965Sjdp err(EXIT_FAILURE, "parent: write()"); 8833965Sjdp break; 8933965Sjdp } 9033965Sjdp if (verbose) 9133965Sjdp (void)printf("parent: wrote %zd bytes to PTY\n", size); 9233965Sjdp } 9333965Sjdp 9433965Sjdp if (verbose) 9533965Sjdp (void)printf("parent: waiting for child to exit\n"); 9633965Sjdp if (waitpid(child, &status, 0) == -1) 9733965Sjdp err(EXIT_FAILURE, "waitpid"); 9833965Sjdp if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 9933965Sjdp errx(EXIT_FAILURE, "child failed"); 10033965Sjdp 10133965Sjdp if (verbose) 10233965Sjdp (void)printf("parent: closing PTY\n"); 10333965Sjdp (void)close(pty); 10433965Sjdp if (verbose) 10533965Sjdp (void)printf("parent: exiting\n"); 10633965Sjdp} 10777298Sobrien 10833965Sjdpstatic void 10933965Sjdpcondition(int fd) 11077298Sobrien{ 11133965Sjdp struct termios tios; 11233965Sjdp 11333965Sjdp if (qsize) { 11433965Sjdp int opt = qsize; 11533965Sjdp if (ioctl(fd, TIOCSQSIZE, &opt) == -1) 11633965Sjdp err(EXIT_FAILURE, "Couldn't set tty(4) buffer size"); 11733965Sjdp if (ioctl(fd, TIOCGQSIZE, &opt) == -1) 11833965Sjdp err(EXIT_FAILURE, "Couldn't get tty(4) buffer size"); 11933965Sjdp if (opt != qsize) 12033965Sjdp errx(EXIT_FAILURE, "Wrong qsize %d != %d\n", 12133965Sjdp qsize, opt); 12233965Sjdp } 12333965Sjdp if (tcgetattr(fd, &tios) == -1) 12477298Sobrien err(EXIT_FAILURE, "tcgetattr()"); 12533965Sjdp cfmakeraw(&tios); 12677298Sobrien cfsetspeed(&tios, B921600); 12733965Sjdp if (tcsetattr(fd, TCSANOW, &tios) == -1) 12833965Sjdp err(EXIT_FAILURE, "tcsetattr()"); 12933965Sjdp} 13033965Sjdp 13133965Sjdpstatic int 13233965Sjdppty_open(void) 13333965Sjdp{ 13433965Sjdp int fd; 13533965Sjdp 13633965Sjdp if ((fd = posix_openpt(O_RDWR)) == -1) 13733965Sjdp err(EXIT_FAILURE, "Couldn't pty(4) device"); 13833965Sjdp 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 152static int 153tty_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 174static void 175fd_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 185static pid_t 186child_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 } 248end: 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 262static void 263usage(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 276static void 277parse_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 306int 307main(int argc, char **argv) 308{ 309 310 parse_args(argc, argv); 311 run(); 312 exit(EXIT_SUCCESS); 313} 314 315#else 316ATF_TC(pty_no_queue); 317 318ATF_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 324ATF_TC_BODY(pty_no_queue, tc) 325{ 326 qsize = 0; 327 run(); 328} 329 330ATF_TC(pty_queue); 331 332ATF_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 338ATF_TC_BODY(pty_queue, tc) 339{ 340 qsize = 4096; 341 run(); 342} 343 344ATF_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 352