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