1272343Sngie/* $Id: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $ */ 2272343Sngie 3272343Sngie/* 4272343Sngie * Allocates a pty(4) device, and sends the specified number of packets of the 5272343Sngie * specified length though it, while a child reader process reads and reports 6272343Sngie * results. 7272343Sngie * 8272343Sngie * Written by Matthew Mondor 9272343Sngie */ 10272343Sngie 11272343Sngie#include <sys/cdefs.h> 12272343Sngie__RCSID("$NetBSD: t_pty.c,v 1.1 2011/09/24 15:53:01 christos Exp $"); 13272343Sngie 14272343Sngie#include <errno.h> 15272343Sngie#include <err.h> 16272343Sngie#include <fcntl.h> 17272343Sngie#include <poll.h> 18272343Sngie#include <stdio.h> 19272343Sngie#ifdef __linux__ 20272343Sngie#define _XOPEN_SOURCE 21272343Sngie#define __USE_XOPEN 22272343Sngie#endif 23272343Sngie#include <stdint.h> 24272343Sngie#include <stdlib.h> 25272343Sngie#include <string.h> 26272343Sngie#include <termios.h> 27272343Sngie#include <unistd.h> 28272343Sngie 29272343Sngie#include <sys/ioctl.h> 30272343Sngie#include <sys/types.h> 31272343Sngie#include <sys/wait.h> 32272343Sngie 33272343Sngie#ifdef STANDALONE 34272343Sngiestatic __dead void usage(const char *); 35272343Sngiestatic void parse_args(int, char **); 36272343Sngie#else 37272343Sngie#include <atf-c.h> 38272343Sngie#include "../h_macros.h" 39272343Sngie#endif 40272343Sngie 41272343Sngiestatic int pty_open(void); 42272343Sngiestatic int tty_open(const char *); 43272343Sngiestatic void fd_nonblock(int); 44272343Sngiestatic pid_t child_spawn(const char *); 45272343Sngiestatic void run(void); 46272343Sngie 47272343Sngiestatic size_t buffer_size = 4096; 48272343Sngiestatic size_t packets = 2; 49272343Sngiestatic uint8_t *dbuf; 50272343Sngiestatic int verbose; 51272343Sngiestatic int qsize; 52272343Sngie 53272343Sngie 54272343Sngiestatic 55272343Sngievoid run(void) 56272343Sngie{ 57272343Sngie size_t i; 58272343Sngie int pty; 59272343Sngie int status; 60272343Sngie pid_t child; 61272343Sngie if ((dbuf = calloc(1, buffer_size)) == NULL) 62272343Sngie err(EXIT_FAILURE, "malloc(%zu)", buffer_size); 63272343Sngie 64272343Sngie if (verbose) 65272343Sngie (void)printf( 66272343Sngie "parent: started; opening PTY and spawning child\n"); 67272343Sngie pty = pty_open(); 68272343Sngie child = child_spawn(ptsname(pty)); 69272343Sngie if (verbose) 70272343Sngie (void)printf("parent: sleeping to make sure child is ready\n"); 71272343Sngie (void)sleep(1); 72272343Sngie 73272343Sngie for (i = 0; i < buffer_size; i++) 74272343Sngie dbuf[i] = i & 0xff; 75272343Sngie 76272343Sngie if (verbose) 77272343Sngie (void)printf("parent: writing\n"); 78272343Sngie 79272343Sngie for (i = 0; i < packets; i++) { 80272343Sngie ssize_t size; 81272343Sngie 82272343Sngie if (verbose) 83272343Sngie (void)printf( 84272343Sngie "parent: attempting to write %zu bytes to PTY\n", 85272343Sngie buffer_size); 86272343Sngie if ((size = write(pty, dbuf, buffer_size)) == -1) { 87272343Sngie err(EXIT_FAILURE, "parent: write()"); 88272343Sngie break; 89272343Sngie } 90272343Sngie if (verbose) 91272343Sngie (void)printf("parent: wrote %zd bytes to PTY\n", size); 92272343Sngie } 93272343Sngie 94272343Sngie if (verbose) 95272343Sngie (void)printf("parent: waiting for child to exit\n"); 96272343Sngie if (waitpid(child, &status, 0) == -1) 97272343Sngie err(EXIT_FAILURE, "waitpid"); 98272343Sngie if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 99272343Sngie errx(EXIT_FAILURE, "child failed"); 100272343Sngie 101272343Sngie if (verbose) 102272343Sngie (void)printf("parent: closing PTY\n"); 103272343Sngie (void)close(pty); 104272343Sngie if (verbose) 105272343Sngie (void)printf("parent: exiting\n"); 106272343Sngie} 107272343Sngie 108272343Sngiestatic void 109272343Sngiecondition(int fd) 110272343Sngie{ 111272343Sngie struct termios tios; 112272343Sngie 113272343Sngie if (qsize) { 114272343Sngie int opt = qsize; 115272343Sngie if (ioctl(fd, TIOCSQSIZE, &opt) == -1) 116272343Sngie err(EXIT_FAILURE, "Couldn't set tty(4) buffer size"); 117272343Sngie if (ioctl(fd, TIOCGQSIZE, &opt) == -1) 118272343Sngie err(EXIT_FAILURE, "Couldn't get tty(4) buffer size"); 119272343Sngie if (opt != qsize) 120272343Sngie errx(EXIT_FAILURE, "Wrong qsize %d != %d\n", 121272343Sngie qsize, opt); 122272343Sngie } 123272343Sngie if (tcgetattr(fd, &tios) == -1) 124272343Sngie err(EXIT_FAILURE, "tcgetattr()"); 125272343Sngie cfmakeraw(&tios); 126272343Sngie cfsetspeed(&tios, B921600); 127272343Sngie if (tcsetattr(fd, TCSANOW, &tios) == -1) 128272343Sngie err(EXIT_FAILURE, "tcsetattr()"); 129272343Sngie} 130272343Sngie 131272343Sngiestatic int 132272343Sngiepty_open(void) 133272343Sngie{ 134272343Sngie int fd; 135272343Sngie 136272343Sngie if ((fd = posix_openpt(O_RDWR)) == -1) 137272343Sngie err(EXIT_FAILURE, "Couldn't pty(4) device"); 138272343Sngie condition(fd); 139272343Sngie if (grantpt(fd) == -1) 140272343Sngie err(EXIT_FAILURE, 141272343Sngie "Couldn't grant permissions on tty(4) device"); 142272343Sngie 143272343Sngie 144272343Sngie condition(fd); 145272343Sngie 146272343Sngie if (unlockpt(fd) == -1) 147272343Sngie err(EXIT_FAILURE, "unlockpt()"); 148272343Sngie 149272343Sngie return fd; 150272343Sngie} 151272343Sngie 152272343Sngiestatic int 153272343Sngietty_open(const char *ttydev) 154272343Sngie{ 155272343Sngie int fd; 156272343Sngie 157272343Sngie if ((fd = open(ttydev, O_RDWR, 0)) == -1) 158272343Sngie err(EXIT_FAILURE, "Couldn't open tty(4) device"); 159272343Sngie 160272343Sngie#ifdef USE_PPP_DISCIPLINE 161272343Sngie { 162272343Sngie int opt = PPPDISC; 163272343Sngie if (ioctl(fd, TIOCSETD, &opt) == -1) 164272343Sngie err(EXIT_FAILURE, 165272343Sngie "Couldn't set tty(4) discipline to PPP"); 166272343Sngie } 167272343Sngie#endif 168272343Sngie 169272343Sngie condition(fd); 170272343Sngie 171272343Sngie return fd; 172272343Sngie} 173272343Sngie 174272343Sngiestatic void 175272343Sngiefd_nonblock(int fd) 176272343Sngie{ 177272343Sngie int opt; 178272343Sngie 179272343Sngie if ((opt = fcntl(fd, F_GETFL, NULL)) == -1) 180272343Sngie err(EXIT_FAILURE, "fcntl()"); 181272343Sngie if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) 182272343Sngie err(EXIT_FAILURE, "fcntl()"); 183272343Sngie} 184272343Sngie 185272343Sngiestatic pid_t 186272343Sngiechild_spawn(const char *ttydev) 187272343Sngie{ 188272343Sngie pid_t pid; 189272343Sngie int tty; 190272343Sngie struct pollfd pfd; 191272343Sngie size_t total = 0; 192272343Sngie 193272343Sngie if ((pid = fork()) == -1) 194272343Sngie err(EXIT_FAILURE, "fork()"); 195272343Sngie (void)setsid(); 196272343Sngie if (pid != 0) 197272343Sngie return pid; 198272343Sngie 199272343Sngie if (verbose) 200272343Sngie (void)printf("child: started; open \"%s\"\n", ttydev); 201272343Sngie tty = tty_open(ttydev); 202272343Sngie fd_nonblock(tty); 203272343Sngie 204272343Sngie if (verbose) 205272343Sngie (void)printf("child: TTY open, starting read loop\n"); 206272343Sngie pfd.fd = tty; 207272343Sngie pfd.events = POLLIN; 208272343Sngie pfd.revents = 0; 209272343Sngie for (;;) { 210272343Sngie int ret; 211272343Sngie ssize_t size; 212272343Sngie 213272343Sngie if (verbose) 214272343Sngie (void)printf("child: polling\n"); 215272343Sngie if ((ret = poll(&pfd, 1, 2000)) == -1) 216272343Sngie err(EXIT_FAILURE, "child: poll()"); 217272343Sngie if (ret == 0) 218272343Sngie break; 219272343Sngie if ((pfd.revents & POLLERR) != 0) 220272343Sngie break; 221272343Sngie if ((pfd.revents & POLLIN) != 0) { 222272343Sngie for (;;) { 223272343Sngie if (verbose) 224272343Sngie (void)printf( 225272343Sngie "child: attempting to read %zu" 226272343Sngie " bytes\n", buffer_size); 227272343Sngie if ((size = read(tty, dbuf, buffer_size)) 228272343Sngie == -1) { 229272343Sngie if (errno == EAGAIN) 230272343Sngie break; 231272343Sngie err(EXIT_FAILURE, "child: read()"); 232272343Sngie } 233272343Sngie if (qsize && size < qsize && 234272343Sngie (size_t)size < buffer_size) 235272343Sngie errx(EXIT_FAILURE, "read returned %zd " 236272343Sngie "less than the queue size %d", 237272343Sngie size, qsize); 238272343Sngie if (verbose) 239272343Sngie (void)printf( 240272343Sngie "child: read %zd bytes from TTY\n", 241272343Sngie size); 242272343Sngie if (size == 0) 243272343Sngie goto end; 244272343Sngie total += size; 245272343Sngie } 246272343Sngie } 247272343Sngie } 248272343Sngieend: 249272343Sngie if (verbose) 250272343Sngie (void)printf("child: closing TTY %zu\n", total); 251272343Sngie (void)close(tty); 252272343Sngie if (verbose) 253272343Sngie (void)printf("child: exiting\n"); 254272343Sngie if (total != buffer_size * packets) 255272343Sngie errx(EXIT_FAILURE, 256272343Sngie "Lost data %zu != %zu\n", total, buffer_size * packets); 257272343Sngie 258272343Sngie exit(EXIT_SUCCESS); 259272343Sngie} 260272343Sngie 261272343Sngie#ifdef STANDALONE 262272343Sngiestatic void 263272343Sngieusage(const char *msg) 264272343Sngie{ 265272343Sngie 266272343Sngie if (msg != NULL) 267272343Sngie (void) fprintf(stderr, "\n%s\n\n", msg); 268272343Sngie 269272343Sngie (void)fprintf(stderr, 270272343Sngie "Usage: %s [-v] [-q <qsize>] [-s <packetsize>] [-n <packets>]\n", 271272343Sngie getprogname()); 272272343Sngie 273272343Sngie exit(EXIT_FAILURE); 274272343Sngie} 275272343Sngie 276272343Sngiestatic void 277272343Sngieparse_args(int argc, char **argv) 278272343Sngie{ 279272343Sngie int ch; 280272343Sngie 281272343Sngie while ((ch = getopt(argc, argv, "n:q:s:v")) != -1) { 282272343Sngie switch (ch) { 283272343Sngie case 'n': 284272343Sngie packets = (size_t)atoi(optarg); 285272343Sngie break; 286272343Sngie case 'q': 287272343Sngie qsize = atoi(optarg); 288272343Sngie break; 289272343Sngie case 's': 290272343Sngie buffer_size = (size_t)atoi(optarg); 291272343Sngie break; 292272343Sngie case 'v': 293272343Sngie verbose++; 294272343Sngie break; 295272343Sngie default: 296272343Sngie usage(NULL); 297272343Sngie break; 298272343Sngie } 299272343Sngie } 300272343Sngie if (buffer_size < 0 || buffer_size > 65536) 301272343Sngie usage("-s must be between 0 and 65536"); 302272343Sngie if (packets < 1 || packets > 100) 303272343Sngie usage("-p must be between 1 and 100"); 304272343Sngie} 305272343Sngie 306272343Sngieint 307272343Sngiemain(int argc, char **argv) 308272343Sngie{ 309272343Sngie 310272343Sngie parse_args(argc, argv); 311272343Sngie run(); 312272343Sngie exit(EXIT_SUCCESS); 313272343Sngie} 314272343Sngie 315272343Sngie#else 316272343SngieATF_TC(pty_no_queue); 317272343Sngie 318272343SngieATF_TC_HEAD(pty_no_queue, tc) 319272343Sngie{ 320272343Sngie atf_tc_set_md_var(tc, "descr", "Checks that writing to pty " 321272343Sngie "does not lose data with the default queue size of 1024"); 322272343Sngie} 323272343Sngie 324272343SngieATF_TC_BODY(pty_no_queue, tc) 325272343Sngie{ 326272343Sngie qsize = 0; 327272343Sngie run(); 328272343Sngie} 329272343Sngie 330272343SngieATF_TC(pty_queue); 331272343Sngie 332272343SngieATF_TC_HEAD(pty_queue, tc) 333272343Sngie{ 334272343Sngie atf_tc_set_md_var(tc, "descr", "Checks that writing to pty " 335272343Sngie "does not lose data with the a queue size of 4096"); 336272343Sngie} 337272343Sngie 338272343SngieATF_TC_BODY(pty_queue, tc) 339272343Sngie{ 340272343Sngie qsize = 4096; 341272343Sngie run(); 342272343Sngie} 343272343Sngie 344272343SngieATF_TP_ADD_TCS(tp) 345272343Sngie{ 346272343Sngie ATF_TP_ADD_TC(tp, pty_no_queue); 347272343Sngie ATF_TP_ADD_TC(tp, pty_queue); 348272343Sngie 349272343Sngie return atf_no_error(); 350272343Sngie} 351272343Sngie#endif 352