1*63d1fd59SEnji Cooper /* $Id: t_pty.c,v 1.2 2017/01/13 21:30:41 christos Exp $ */ 257718be8SEnji Cooper 357718be8SEnji Cooper /* 457718be8SEnji Cooper * Allocates a pty(4) device, and sends the specified number of packets of the 557718be8SEnji Cooper * specified length though it, while a child reader process reads and reports 657718be8SEnji Cooper * results. 757718be8SEnji Cooper * 857718be8SEnji Cooper * Written by Matthew Mondor 957718be8SEnji Cooper */ 1057718be8SEnji Cooper 1157718be8SEnji Cooper #include <sys/cdefs.h> 12*63d1fd59SEnji Cooper __RCSID("$NetBSD: t_pty.c,v 1.2 2017/01/13 21:30:41 christos Exp $"); 1357718be8SEnji Cooper 1457718be8SEnji Cooper #include <errno.h> 1557718be8SEnji Cooper #include <err.h> 1657718be8SEnji Cooper #include <fcntl.h> 1757718be8SEnji Cooper #include <poll.h> 1857718be8SEnji Cooper #include <stdio.h> 1957718be8SEnji Cooper #ifdef __linux__ 2057718be8SEnji Cooper #define _XOPEN_SOURCE 2157718be8SEnji Cooper #define __USE_XOPEN 2257718be8SEnji Cooper #endif 2357718be8SEnji Cooper #include <stdint.h> 2457718be8SEnji Cooper #include <stdlib.h> 2557718be8SEnji Cooper #include <string.h> 2657718be8SEnji Cooper #include <termios.h> 2757718be8SEnji Cooper #include <unistd.h> 2857718be8SEnji Cooper 2957718be8SEnji Cooper #include <sys/ioctl.h> 3057718be8SEnji Cooper #include <sys/types.h> 3157718be8SEnji Cooper #include <sys/wait.h> 3257718be8SEnji Cooper 3357718be8SEnji Cooper #ifdef STANDALONE 3457718be8SEnji Cooper static __dead void usage(const char *); 3557718be8SEnji Cooper static void parse_args(int, char **); 3657718be8SEnji Cooper #else 3757718be8SEnji Cooper #include <atf-c.h> 38*63d1fd59SEnji Cooper #include "h_macros.h" 3957718be8SEnji Cooper #endif 4057718be8SEnji Cooper 4157718be8SEnji Cooper static int pty_open(void); 4257718be8SEnji Cooper static int tty_open(const char *); 4357718be8SEnji Cooper static void fd_nonblock(int); 4457718be8SEnji Cooper static pid_t child_spawn(const char *); 4557718be8SEnji Cooper static void run(void); 4657718be8SEnji Cooper 4757718be8SEnji Cooper static size_t buffer_size = 4096; 4857718be8SEnji Cooper static size_t packets = 2; 4957718be8SEnji Cooper static uint8_t *dbuf; 5057718be8SEnji Cooper static int verbose; 5157718be8SEnji Cooper static int qsize; 5257718be8SEnji Cooper 5357718be8SEnji Cooper 5457718be8SEnji Cooper static 5557718be8SEnji Cooper void run(void) 5657718be8SEnji Cooper { 5757718be8SEnji Cooper size_t i; 5857718be8SEnji Cooper int pty; 5957718be8SEnji Cooper int status; 6057718be8SEnji Cooper pid_t child; 6157718be8SEnji Cooper if ((dbuf = calloc(1, buffer_size)) == NULL) 6257718be8SEnji Cooper err(EXIT_FAILURE, "malloc(%zu)", buffer_size); 6357718be8SEnji Cooper 6457718be8SEnji Cooper if (verbose) 6557718be8SEnji Cooper (void)printf( 6657718be8SEnji Cooper "parent: started; opening PTY and spawning child\n"); 6757718be8SEnji Cooper pty = pty_open(); 6857718be8SEnji Cooper child = child_spawn(ptsname(pty)); 6957718be8SEnji Cooper if (verbose) 7057718be8SEnji Cooper (void)printf("parent: sleeping to make sure child is ready\n"); 7157718be8SEnji Cooper (void)sleep(1); 7257718be8SEnji Cooper 7357718be8SEnji Cooper for (i = 0; i < buffer_size; i++) 7457718be8SEnji Cooper dbuf[i] = i & 0xff; 7557718be8SEnji Cooper 7657718be8SEnji Cooper if (verbose) 7757718be8SEnji Cooper (void)printf("parent: writing\n"); 7857718be8SEnji Cooper 7957718be8SEnji Cooper for (i = 0; i < packets; i++) { 8057718be8SEnji Cooper ssize_t size; 8157718be8SEnji Cooper 8257718be8SEnji Cooper if (verbose) 8357718be8SEnji Cooper (void)printf( 8457718be8SEnji Cooper "parent: attempting to write %zu bytes to PTY\n", 8557718be8SEnji Cooper buffer_size); 8657718be8SEnji Cooper if ((size = write(pty, dbuf, buffer_size)) == -1) { 8757718be8SEnji Cooper err(EXIT_FAILURE, "parent: write()"); 8857718be8SEnji Cooper break; 8957718be8SEnji Cooper } 9057718be8SEnji Cooper if (verbose) 9157718be8SEnji Cooper (void)printf("parent: wrote %zd bytes to PTY\n", size); 9257718be8SEnji Cooper } 9357718be8SEnji Cooper 9457718be8SEnji Cooper if (verbose) 9557718be8SEnji Cooper (void)printf("parent: waiting for child to exit\n"); 9657718be8SEnji Cooper if (waitpid(child, &status, 0) == -1) 9757718be8SEnji Cooper err(EXIT_FAILURE, "waitpid"); 9857718be8SEnji Cooper if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 9957718be8SEnji Cooper errx(EXIT_FAILURE, "child failed"); 10057718be8SEnji Cooper 10157718be8SEnji Cooper if (verbose) 10257718be8SEnji Cooper (void)printf("parent: closing PTY\n"); 10357718be8SEnji Cooper (void)close(pty); 10457718be8SEnji Cooper if (verbose) 10557718be8SEnji Cooper (void)printf("parent: exiting\n"); 10657718be8SEnji Cooper } 10757718be8SEnji Cooper 10857718be8SEnji Cooper static void 10957718be8SEnji Cooper condition(int fd) 11057718be8SEnji Cooper { 11157718be8SEnji Cooper struct termios tios; 11257718be8SEnji Cooper 11357718be8SEnji Cooper if (qsize) { 11457718be8SEnji Cooper int opt = qsize; 11557718be8SEnji Cooper if (ioctl(fd, TIOCSQSIZE, &opt) == -1) 11657718be8SEnji Cooper err(EXIT_FAILURE, "Couldn't set tty(4) buffer size"); 11757718be8SEnji Cooper if (ioctl(fd, TIOCGQSIZE, &opt) == -1) 11857718be8SEnji Cooper err(EXIT_FAILURE, "Couldn't get tty(4) buffer size"); 11957718be8SEnji Cooper if (opt != qsize) 12057718be8SEnji Cooper errx(EXIT_FAILURE, "Wrong qsize %d != %d\n", 12157718be8SEnji Cooper qsize, opt); 12257718be8SEnji Cooper } 12357718be8SEnji Cooper if (tcgetattr(fd, &tios) == -1) 12457718be8SEnji Cooper err(EXIT_FAILURE, "tcgetattr()"); 12557718be8SEnji Cooper cfmakeraw(&tios); 12657718be8SEnji Cooper cfsetspeed(&tios, B921600); 12757718be8SEnji Cooper if (tcsetattr(fd, TCSANOW, &tios) == -1) 12857718be8SEnji Cooper err(EXIT_FAILURE, "tcsetattr()"); 12957718be8SEnji Cooper } 13057718be8SEnji Cooper 13157718be8SEnji Cooper static int 13257718be8SEnji Cooper pty_open(void) 13357718be8SEnji Cooper { 13457718be8SEnji Cooper int fd; 13557718be8SEnji Cooper 13657718be8SEnji Cooper if ((fd = posix_openpt(O_RDWR)) == -1) 13757718be8SEnji Cooper err(EXIT_FAILURE, "Couldn't pty(4) device"); 13857718be8SEnji Cooper condition(fd); 13957718be8SEnji Cooper if (grantpt(fd) == -1) 14057718be8SEnji Cooper err(EXIT_FAILURE, 14157718be8SEnji Cooper "Couldn't grant permissions on tty(4) device"); 14257718be8SEnji Cooper 14357718be8SEnji Cooper 14457718be8SEnji Cooper condition(fd); 14557718be8SEnji Cooper 14657718be8SEnji Cooper if (unlockpt(fd) == -1) 14757718be8SEnji Cooper err(EXIT_FAILURE, "unlockpt()"); 14857718be8SEnji Cooper 14957718be8SEnji Cooper return fd; 15057718be8SEnji Cooper } 15157718be8SEnji Cooper 15257718be8SEnji Cooper static int 15357718be8SEnji Cooper tty_open(const char *ttydev) 15457718be8SEnji Cooper { 15557718be8SEnji Cooper int fd; 15657718be8SEnji Cooper 15757718be8SEnji Cooper if ((fd = open(ttydev, O_RDWR, 0)) == -1) 15857718be8SEnji Cooper err(EXIT_FAILURE, "Couldn't open tty(4) device"); 15957718be8SEnji Cooper 16057718be8SEnji Cooper #ifdef USE_PPP_DISCIPLINE 16157718be8SEnji Cooper { 16257718be8SEnji Cooper int opt = PPPDISC; 16357718be8SEnji Cooper if (ioctl(fd, TIOCSETD, &opt) == -1) 16457718be8SEnji Cooper err(EXIT_FAILURE, 16557718be8SEnji Cooper "Couldn't set tty(4) discipline to PPP"); 16657718be8SEnji Cooper } 16757718be8SEnji Cooper #endif 16857718be8SEnji Cooper 16957718be8SEnji Cooper condition(fd); 17057718be8SEnji Cooper 17157718be8SEnji Cooper return fd; 17257718be8SEnji Cooper } 17357718be8SEnji Cooper 17457718be8SEnji Cooper static void 17557718be8SEnji Cooper fd_nonblock(int fd) 17657718be8SEnji Cooper { 17757718be8SEnji Cooper int opt; 17857718be8SEnji Cooper 17957718be8SEnji Cooper if ((opt = fcntl(fd, F_GETFL, NULL)) == -1) 18057718be8SEnji Cooper err(EXIT_FAILURE, "fcntl()"); 18157718be8SEnji Cooper if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1) 18257718be8SEnji Cooper err(EXIT_FAILURE, "fcntl()"); 18357718be8SEnji Cooper } 18457718be8SEnji Cooper 18557718be8SEnji Cooper static pid_t 18657718be8SEnji Cooper child_spawn(const char *ttydev) 18757718be8SEnji Cooper { 18857718be8SEnji Cooper pid_t pid; 18957718be8SEnji Cooper int tty; 19057718be8SEnji Cooper struct pollfd pfd; 19157718be8SEnji Cooper size_t total = 0; 19257718be8SEnji Cooper 19357718be8SEnji Cooper if ((pid = fork()) == -1) 19457718be8SEnji Cooper err(EXIT_FAILURE, "fork()"); 19557718be8SEnji Cooper (void)setsid(); 19657718be8SEnji Cooper if (pid != 0) 19757718be8SEnji Cooper return pid; 19857718be8SEnji Cooper 19957718be8SEnji Cooper if (verbose) 20057718be8SEnji Cooper (void)printf("child: started; open \"%s\"\n", ttydev); 20157718be8SEnji Cooper tty = tty_open(ttydev); 20257718be8SEnji Cooper fd_nonblock(tty); 20357718be8SEnji Cooper 20457718be8SEnji Cooper if (verbose) 20557718be8SEnji Cooper (void)printf("child: TTY open, starting read loop\n"); 20657718be8SEnji Cooper pfd.fd = tty; 20757718be8SEnji Cooper pfd.events = POLLIN; 20857718be8SEnji Cooper pfd.revents = 0; 20957718be8SEnji Cooper for (;;) { 21057718be8SEnji Cooper int ret; 21157718be8SEnji Cooper ssize_t size; 21257718be8SEnji Cooper 21357718be8SEnji Cooper if (verbose) 21457718be8SEnji Cooper (void)printf("child: polling\n"); 21557718be8SEnji Cooper if ((ret = poll(&pfd, 1, 2000)) == -1) 21657718be8SEnji Cooper err(EXIT_FAILURE, "child: poll()"); 21757718be8SEnji Cooper if (ret == 0) 21857718be8SEnji Cooper break; 21957718be8SEnji Cooper if ((pfd.revents & POLLERR) != 0) 22057718be8SEnji Cooper break; 22157718be8SEnji Cooper if ((pfd.revents & POLLIN) != 0) { 22257718be8SEnji Cooper for (;;) { 22357718be8SEnji Cooper if (verbose) 22457718be8SEnji Cooper (void)printf( 22557718be8SEnji Cooper "child: attempting to read %zu" 22657718be8SEnji Cooper " bytes\n", buffer_size); 22757718be8SEnji Cooper if ((size = read(tty, dbuf, buffer_size)) 22857718be8SEnji Cooper == -1) { 22957718be8SEnji Cooper if (errno == EAGAIN) 23057718be8SEnji Cooper break; 23157718be8SEnji Cooper err(EXIT_FAILURE, "child: read()"); 23257718be8SEnji Cooper } 23357718be8SEnji Cooper if (qsize && size < qsize && 23457718be8SEnji Cooper (size_t)size < buffer_size) 23557718be8SEnji Cooper errx(EXIT_FAILURE, "read returned %zd " 23657718be8SEnji Cooper "less than the queue size %d", 23757718be8SEnji Cooper size, qsize); 23857718be8SEnji Cooper if (verbose) 23957718be8SEnji Cooper (void)printf( 24057718be8SEnji Cooper "child: read %zd bytes from TTY\n", 24157718be8SEnji Cooper size); 24257718be8SEnji Cooper if (size == 0) 24357718be8SEnji Cooper goto end; 24457718be8SEnji Cooper total += size; 24557718be8SEnji Cooper } 24657718be8SEnji Cooper } 24757718be8SEnji Cooper } 24857718be8SEnji Cooper end: 24957718be8SEnji Cooper if (verbose) 25057718be8SEnji Cooper (void)printf("child: closing TTY %zu\n", total); 25157718be8SEnji Cooper (void)close(tty); 25257718be8SEnji Cooper if (verbose) 25357718be8SEnji Cooper (void)printf("child: exiting\n"); 25457718be8SEnji Cooper if (total != buffer_size * packets) 25557718be8SEnji Cooper errx(EXIT_FAILURE, 25657718be8SEnji Cooper "Lost data %zu != %zu\n", total, buffer_size * packets); 25757718be8SEnji Cooper 25857718be8SEnji Cooper exit(EXIT_SUCCESS); 25957718be8SEnji Cooper } 26057718be8SEnji Cooper 26157718be8SEnji Cooper #ifdef STANDALONE 26257718be8SEnji Cooper static void 26357718be8SEnji Cooper usage(const char *msg) 26457718be8SEnji Cooper { 26557718be8SEnji Cooper 26657718be8SEnji Cooper if (msg != NULL) 26757718be8SEnji Cooper (void) fprintf(stderr, "\n%s\n\n", msg); 26857718be8SEnji Cooper 26957718be8SEnji Cooper (void)fprintf(stderr, 27057718be8SEnji Cooper "Usage: %s [-v] [-q <qsize>] [-s <packetsize>] [-n <packets>]\n", 27157718be8SEnji Cooper getprogname()); 27257718be8SEnji Cooper 27357718be8SEnji Cooper exit(EXIT_FAILURE); 27457718be8SEnji Cooper } 27557718be8SEnji Cooper 27657718be8SEnji Cooper static void 27757718be8SEnji Cooper parse_args(int argc, char **argv) 27857718be8SEnji Cooper { 27957718be8SEnji Cooper int ch; 28057718be8SEnji Cooper 28157718be8SEnji Cooper while ((ch = getopt(argc, argv, "n:q:s:v")) != -1) { 28257718be8SEnji Cooper switch (ch) { 28357718be8SEnji Cooper case 'n': 28457718be8SEnji Cooper packets = (size_t)atoi(optarg); 28557718be8SEnji Cooper break; 28657718be8SEnji Cooper case 'q': 28757718be8SEnji Cooper qsize = atoi(optarg); 28857718be8SEnji Cooper break; 28957718be8SEnji Cooper case 's': 29057718be8SEnji Cooper buffer_size = (size_t)atoi(optarg); 29157718be8SEnji Cooper break; 29257718be8SEnji Cooper case 'v': 29357718be8SEnji Cooper verbose++; 29457718be8SEnji Cooper break; 29557718be8SEnji Cooper default: 29657718be8SEnji Cooper usage(NULL); 29757718be8SEnji Cooper break; 29857718be8SEnji Cooper } 29957718be8SEnji Cooper } 30057718be8SEnji Cooper if (buffer_size < 0 || buffer_size > 65536) 30157718be8SEnji Cooper usage("-s must be between 0 and 65536"); 30257718be8SEnji Cooper if (packets < 1 || packets > 100) 30357718be8SEnji Cooper usage("-p must be between 1 and 100"); 30457718be8SEnji Cooper } 30557718be8SEnji Cooper 30657718be8SEnji Cooper int 30757718be8SEnji Cooper main(int argc, char **argv) 30857718be8SEnji Cooper { 30957718be8SEnji Cooper 31057718be8SEnji Cooper parse_args(argc, argv); 31157718be8SEnji Cooper run(); 31257718be8SEnji Cooper exit(EXIT_SUCCESS); 31357718be8SEnji Cooper } 31457718be8SEnji Cooper 31557718be8SEnji Cooper #else 31657718be8SEnji Cooper ATF_TC(pty_no_queue); 31757718be8SEnji Cooper 31857718be8SEnji Cooper ATF_TC_HEAD(pty_no_queue, tc) 31957718be8SEnji Cooper { 32057718be8SEnji Cooper atf_tc_set_md_var(tc, "descr", "Checks that writing to pty " 32157718be8SEnji Cooper "does not lose data with the default queue size of 1024"); 32257718be8SEnji Cooper } 32357718be8SEnji Cooper 32457718be8SEnji Cooper ATF_TC_BODY(pty_no_queue, tc) 32557718be8SEnji Cooper { 32657718be8SEnji Cooper qsize = 0; 32757718be8SEnji Cooper run(); 32857718be8SEnji Cooper } 32957718be8SEnji Cooper 33057718be8SEnji Cooper ATF_TC(pty_queue); 33157718be8SEnji Cooper 33257718be8SEnji Cooper ATF_TC_HEAD(pty_queue, tc) 33357718be8SEnji Cooper { 33457718be8SEnji Cooper atf_tc_set_md_var(tc, "descr", "Checks that writing to pty " 33557718be8SEnji Cooper "does not lose data with the a queue size of 4096"); 33657718be8SEnji Cooper } 33757718be8SEnji Cooper 33857718be8SEnji Cooper ATF_TC_BODY(pty_queue, tc) 33957718be8SEnji Cooper { 34057718be8SEnji Cooper qsize = 4096; 34157718be8SEnji Cooper run(); 34257718be8SEnji Cooper } 34357718be8SEnji Cooper 34457718be8SEnji Cooper ATF_TP_ADD_TCS(tp) 34557718be8SEnji Cooper { 34657718be8SEnji Cooper ATF_TP_ADD_TC(tp, pty_no_queue); 34757718be8SEnji Cooper ATF_TP_ADD_TC(tp, pty_queue); 34857718be8SEnji Cooper 34957718be8SEnji Cooper return atf_no_error(); 35057718be8SEnji Cooper } 35157718be8SEnji Cooper #endif 352