160324Sbde/* $Id: t_pty.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ 260324Sbde 360324Sbde/* 443412Snewton * Allocates a pty(4) device, and sends the specified number of packets of the 543412Snewton * specified length though it, while a child reader process reads and reports 643412Snewton * results. 7123783Speter * 843412Snewton * Written by Matthew Mondor 9146806Srwatson */ 10146806Srwatson 11146806Srwatson#include <sys/cdefs.h> 12146806Srwatson__RCSID("$NetBSD: t_pty.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); 13146806Srwatson 1443412Snewton#include <errno.h> 1543412Snewton#include <err.h> 1643412Snewton#include <fcntl.h> 1743412Snewton#include <poll.h> 1843412Snewton#include <stdio.h> 1943412Snewton#ifdef __linux__ 2043412Snewton#define _XOPEN_SOURCE 2143412Snewton#define __USE_XOPEN 2243412Snewton#endif 2343412Snewton#include <stdint.h> 2443412Snewton#include <stdlib.h> 2543412Snewton#include <string.h> 2643412Snewton#include <termios.h> 2760324Sbde#include <unistd.h> 2860324Sbde 2960324Sbde#include <sys/ioctl.h> 3043412Snewton#include <sys/types.h> 3143412Snewton#include <sys/wait.h> 3243412Snewton 3343412Snewton#ifdef STANDALONE 3443412Snewtonstatic __dead void usage(const char *); 3565302Sobrienstatic void parse_args(int, char **); 3665302Sobrien#else 3765302Sobrien#include <atf-c.h> 3865302Sobrien#include "h_macros.h" 3943412Snewton#endif 40146806Srwatson 41160798Sjhbstatic int pty_open(void); 42146806Srwatsonstatic int tty_open(const char *); 43160798Sjhbstatic void fd_nonblock(int); 44160798Sjhbstatic pid_t child_spawn(const char *); 45160798Sjhbstatic void run(void); 46160798Sjhb 47146806Srwatsonstatic size_t buffer_size = 4096; 48160798Sjhbstatic size_t packets = 2; 49160798Sjhbstatic uint8_t *dbuf; 50160798Sjhbstatic int verbose; 51160798Sjhbstatic int qsize; 52160798Sjhb 53160798Sjhb 54160798Sjhbstatic 55160798Sjhbvoid run(void) 56160798Sjhb{ 57160798Sjhb size_t i; 58160798Sjhb int pty; 59160798Sjhb int status; 60160798Sjhb pid_t child; 61146806Srwatson if ((dbuf = calloc(1, buffer_size)) == NULL) 62160798Sjhb err(EXIT_FAILURE, "malloc(%zu)", buffer_size); 63146806Srwatson 64160798Sjhb if (verbose) 65146806Srwatson (void)printf( 66146806Srwatson "parent: started; opening PTY and spawning child\n"); 67160798Sjhb pty = pty_open(); 68160798Sjhb child = child_spawn(ptsname(pty)); 69146806Srwatson if (verbose) 70146806Srwatson (void)printf("parent: sleeping to make sure child is ready\n"); 71160798Sjhb (void)sleep(1); 72160798Sjhb 73146806Srwatson for (i = 0; i < buffer_size; i++) 74160798Sjhb dbuf[i] = i & 0xff; 75160798Sjhb 76146806Srwatson if (verbose) 77146806Srwatson (void)printf("parent: writing\n"); 78146806Srwatson 79160798Sjhb for (i = 0; i < packets; i++) { 80227691Sed ssize_t size; 81160798Sjhb 82146806Srwatson if (verbose) 83160798Sjhb (void)printf( 84160798Sjhb "parent: attempting to write %zu bytes to PTY\n", 85146806Srwatson buffer_size); 86160798Sjhb if ((size = write(pty, dbuf, buffer_size)) == -1) { 87146806Srwatson err(EXIT_FAILURE, "parent: write()"); 88146806Srwatson break; 89160798Sjhb } 90160798Sjhb if (verbose) 91160798Sjhb (void)printf("parent: wrote %zd bytes to PTY\n", size); 92146806Srwatson } 93146806Srwatson 94160798Sjhb if (verbose) 95160798Sjhb (void)printf("parent: waiting for child to exit\n"); 96160798Sjhb if (waitpid(child, &status, 0) == -1) 97146806Srwatson err(EXIT_FAILURE, "waitpid"); 98160798Sjhb if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 99147965Sjhb errx(EXIT_FAILURE, "child failed"); 100160798Sjhb 101146806Srwatson if (verbose) 102160798Sjhb (void)printf("parent: closing PTY\n"); 103147965Sjhb (void)close(pty); 104160798Sjhb if (verbose) 105147965Sjhb (void)printf("parent: exiting\n"); 106160798Sjhb} 107146806Srwatson 108146806Srwatsonstatic void 109146806Srwatsoncondition(int fd) 110160798Sjhb{ 111146806Srwatson struct termios tios; 112160798Sjhb 113160798Sjhb if (qsize) { 114146806Srwatson int opt = qsize; 115160798Sjhb if (ioctl(fd, TIOCSQSIZE, &opt) == -1) 116160798Sjhb err(EXIT_FAILURE, "Couldn't set tty(4) buffer size"); 117160798Sjhb if (ioctl(fd, TIOCGQSIZE, &opt) == -1) 118146806Srwatson err(EXIT_FAILURE, "Couldn't get tty(4) buffer size"); 119160798Sjhb if (opt != qsize) 120146806Srwatson errx(EXIT_FAILURE, "Wrong qsize %d != %d\n", 121146806Srwatson qsize, opt); 122146806Srwatson } 123146806Srwatson if (tcgetattr(fd, &tios) == -1) 124146806Srwatson err(EXIT_FAILURE, "tcgetattr()"); 125146806Srwatson cfmakeraw(&tios); 126146806Srwatson cfsetspeed(&tios, B921600); 127146806Srwatson if (tcsetattr(fd, TCSANOW, &tios) == -1) 128146806Srwatson err(EXIT_FAILURE, "tcsetattr()"); 129146806Srwatson} 130146806Srwatson 131146806Srwatsonstatic int 132146806Srwatsonpty_open(void) 133146806Srwatson{ 134146806Srwatson int fd; 135146806Srwatson 136160798Sjhb if ((fd = posix_openpt(O_RDWR)) == -1) 137160798Sjhb err(EXIT_FAILURE, "Couldn't pty(4) device"); 138160798Sjhb condition(fd); 139146806Srwatson if (grantpt(fd) == -1) 140146806Srwatson err(EXIT_FAILURE, 141146806Srwatson "Couldn't grant permissions on tty(4) device"); 142146806Srwatson 143160798Sjhb 144146806Srwatson condition(fd); 145146806Srwatson 146160798Sjhb if (unlockpt(fd) == -1) 147146806Srwatson err(EXIT_FAILURE, "unlockpt()"); 148146806Srwatson 149160798Sjhb return fd; 150146806Srwatson} 151160798Sjhb 152146806Srwatsonstatic int 153160798Sjhbtty_open(const char *ttydev) 154160798Sjhb{ 155146806Srwatson int fd; 156160798Sjhb 157146806Srwatson if ((fd = open(ttydev, O_RDWR, 0)) == -1) 158160798Sjhb err(EXIT_FAILURE, "Couldn't open tty(4) device"); 159146806Srwatson 160160798Sjhb#ifdef USE_PPP_DISCIPLINE 161160798Sjhb { 162160798Sjhb int opt = PPPDISC; 163146806Srwatson if (ioctl(fd, TIOCSETD, &opt) == -1) 164146806Srwatson err(EXIT_FAILURE, 165160798Sjhb "Couldn't set tty(4) discipline to PPP"); 166146806Srwatson } 167160798Sjhb#endif 168146806Srwatson 169146806Srwatson condition(fd); 170160798Sjhb 171146806Srwatson return fd; 172146806Srwatson} 173160798Sjhb 174146806Srwatsonstatic void 175160798Sjhbfd_nonblock(int fd) 176146806Srwatson{ 177146806Srwatson int opt; 178146806Srwatson 179160798Sjhb if ((opt = fcntl(fd, F_GETFL, NULL)) == -1) 180146806Srwatson err(EXIT_FAILURE, "fcntl()"); 181160798Sjhb if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) 182146806Srwatson err(EXIT_FAILURE, "fcntl()"); 183146806Srwatson} 184146806Srwatson 185160798Sjhbstatic pid_t 186146806Srwatsonchild_spawn(const char *ttydev) 187146806Srwatson{ 188160798Sjhb pid_t pid; 189146806Srwatson int tty; 190146806Srwatson struct pollfd pfd; 191146806Srwatson size_t total = 0; 192146806Srwatson 193160798Sjhb if ((pid = fork()) == -1) 194146806Srwatson err(EXIT_FAILURE, "fork()"); 195146806Srwatson (void)setsid(); 196160798Sjhb if (pid != 0) 197146806Srwatson return pid; 198146806Srwatson 199160798Sjhb if (verbose) 200146806Srwatson (void)printf("child: started; open \"%s\"\n", ttydev); 201160798Sjhb tty = tty_open(ttydev); 202160798Sjhb fd_nonblock(tty); 203160798Sjhb 204160798Sjhb if (verbose) 205160798Sjhb (void)printf("child: TTY open, starting read loop\n"); 206146806Srwatson pfd.fd = tty; 207160798Sjhb pfd.events = POLLIN; 208146806Srwatson pfd.revents = 0; 209160798Sjhb for (;;) { 210146806Srwatson int ret; 211160798Sjhb ssize_t size; 212146806Srwatson 213160798Sjhb if (verbose) 214146806Srwatson (void)printf("child: polling\n"); 215160798Sjhb if ((ret = poll(&pfd, 1, 2000)) == -1) 216146806Srwatson err(EXIT_FAILURE, "child: poll()"); 217146806Srwatson if (ret == 0) 218160798Sjhb break; 219146806Srwatson if ((pfd.revents & POLLERR) != 0) 220160798Sjhb break; 221146806Srwatson if ((pfd.revents & POLLIN) != 0) { 222160798Sjhb for (;;) { 223146806Srwatson if (verbose) 224160798Sjhb (void)printf( 225146806Srwatson "child: attempting to read %zu" 226146806Srwatson " bytes\n", buffer_size); 227146806Srwatson if ((size = read(tty, dbuf, buffer_size)) 228146806Srwatson == -1) { 229160798Sjhb if (errno == EAGAIN) 230160798Sjhb break; 231146806Srwatson err(EXIT_FAILURE, "child: read()"); 232160798Sjhb } 233160798Sjhb if (qsize && size < qsize && 234160798Sjhb (size_t)size < buffer_size) 235146806Srwatson errx(EXIT_FAILURE, "read returned %zd " 236160798Sjhb "less than the queue size %d", 237146806Srwatson size, qsize); 238146806Srwatson if (verbose) 239160798Sjhb (void)printf( 240146806Srwatson "child: read %zd bytes from TTY\n", 24143412Snewton size); 242160798Sjhb if (size == 0) 243146806Srwatson goto end; 244146806Srwatson total += size; 245146806Srwatson } 246146806Srwatson } 247146806Srwatson } 248146806Srwatsonend: 249146806Srwatson if (verbose) 250146806Srwatson (void)printf("child: closing TTY %zu\n", total); 251146806Srwatson (void)close(tty); 252160798Sjhb if (verbose) 253160798Sjhb (void)printf("child: exiting\n"); 254146806Srwatson if (total != buffer_size * packets) 255160798Sjhb errx(EXIT_FAILURE, 256160798Sjhb "Lost data %zu != %zu\n", total, buffer_size * packets); 257146806Srwatson 258160798Sjhb exit(EXIT_SUCCESS); 259146806Srwatson} 260160798Sjhb 261146806Srwatson#ifdef STANDALONE 262146806Srwatsonstatic void 263146806Srwatsonusage(const char *msg) 264146806Srwatson{ 265146806Srwatson 266146806Srwatson if (msg != NULL) 267146806Srwatson (void) fprintf(stderr, "\n%s\n\n", msg); 268146806Srwatson 269146806Srwatson (void)fprintf(stderr, 270146806Srwatson "Usage: %s [-v] [-q <qsize>] [-s <packetsize>] [-n <packets>]\n", 271146806Srwatson getprogname()); 272146806Srwatson 273146806Srwatson exit(EXIT_FAILURE); 274146806Srwatson} 275146806Srwatson 276146806Srwatsonstatic void 277146806Srwatsonparse_args(int argc, char **argv) 278146806Srwatson{ 279146806Srwatson int ch; 280146806Srwatson 281146806Srwatson while ((ch = getopt(argc, argv, "n:q:s:v")) != -1) { 282160798Sjhb switch (ch) { 283146806Srwatson case 'n': 284146806Srwatson packets = (size_t)atoi(optarg); 285146806Srwatson break; 286146806Srwatson case 'q': 287146806Srwatson qsize = atoi(optarg); 288146806Srwatson break; 289146806Srwatson case 's': 290146806Srwatson buffer_size = (size_t)atoi(optarg); 291146806Srwatson break; 292146806Srwatson case 'v': 293160798Sjhb verbose++; 294146806Srwatson break; 295160798Sjhb default: 296146806Srwatson usage(NULL); 297146806Srwatson break; 298146806Srwatson } 299146806Srwatson } 300146806Srwatson if (buffer_size < 0 || buffer_size > 65536) 301146806Srwatson usage("-s must be between 0 and 65536"); 302146806Srwatson if (packets < 1 || packets > 100) 303146806Srwatson usage("-p must be between 1 and 100"); 304146806Srwatson} 305146806Srwatson 306146806Srwatsonint 307146806Srwatsonmain(int argc, char **argv) 308146806Srwatson{ 309160798Sjhb 310146806Srwatson parse_args(argc, argv); 311146806Srwatson run(); 312160798Sjhb exit(EXIT_SUCCESS); 313146806Srwatson} 314146806Srwatson 315160798Sjhb#else 316160798SjhbATF_TC(pty_no_queue); 317146806Srwatson 318146806SrwatsonATF_TC_HEAD(pty_no_queue, tc) 319146806Srwatson{ 320146806Srwatson atf_tc_set_md_var(tc, "descr", "Checks that writing to pty " 321146806Srwatson "does not lose data with the default queue size of 1024"); 322160798Sjhb} 323146806Srwatson 324146806SrwatsonATF_TC_BODY(pty_no_queue, tc) 325146806Srwatson{ 326146806Srwatson qsize = 0; 327160798Sjhb run(); 328146806Srwatson} 329146806Srwatson 330160798SjhbATF_TC(pty_queue); 331146806Srwatson 332146806SrwatsonATF_TC_HEAD(pty_queue, tc) 333160798Sjhb{ 334146806Srwatson atf_tc_set_md_var(tc, "descr", "Checks that writing to pty " 335160798Sjhb "does not lose data with the a queue size of 4096"); 336146806Srwatson} 337160798Sjhb 338146806SrwatsonATF_TC_BODY(pty_queue, tc) 339160798Sjhb{ 340146806Srwatson qsize = 4096; 341160798Sjhb run(); 342146806Srwatson} 343160798Sjhb 344146806SrwatsonATF_TP_ADD_TCS(tp) 345160798Sjhb{ 346146806Srwatson ATF_TP_ADD_TC(tp, pty_no_queue); 347146806Srwatson ATF_TP_ADD_TC(tp, pty_queue); 348146806Srwatson 349160798Sjhb return atf_no_error(); 350146806Srwatson} 351160798Sjhb#endif 352146806Srwatson