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