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
run(void)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
condition(int fd)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
pty_open(void)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
tty_open(const char * ttydev)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
fd_nonblock(int fd)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
child_spawn(const char * ttydev)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
usage(const char * msg)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
parse_args(int argc,char ** argv)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
main(int argc,char ** argv)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
ATF_TC_HEAD(pty_no_queue,tc)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
ATF_TC_BODY(pty_no_queue,tc)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
ATF_TC_HEAD(pty_queue,tc)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
ATF_TC_BODY(pty_queue,tc)33857718be8SEnji Cooper ATF_TC_BODY(pty_queue, tc)
33957718be8SEnji Cooper {
34057718be8SEnji Cooper qsize = 4096;
34157718be8SEnji Cooper run();
34257718be8SEnji Cooper }
34357718be8SEnji Cooper
ATF_TP_ADD_TCS(tp)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