master tmtm.c
  1#include <stdarg.h>
  2#include <stdio.h>
  3#include <stdlib.h>
  4#include <string.h>
  5#include <sys/ioctl.h>
  6#include <sys/poll.h>
  7#include <termios.h>
  8#include <unistd.h>
  9
 10#ifdef __linux__
 11#include <pty.h>
 12#else /* BSD and macOS */
 13#include <util.h>
 14#endif
 15
 16#define TMTM_VER "26.6"
 17
 18static void die(int ln, int ec, const char* fmt, ...);
 19static void getwinsize(void);
 20static void rawon(void);
 21static void rawoff(void);
 22static void run(void);
 23static void spawnpty(void);
 24
 25static struct termios orig_termios;
 26static struct winsize ws;
 27static int masterfd = -1;
 28
 29static void die(int ln, int ec, const char* fmt, ...)
 30{
 31	va_list args;
 32	va_start(args, fmt);
 33
 34	fprintf(stderr, "ln:%d ", ln);
 35	vfprintf(stderr, fmt, args);
 36	fprintf(stderr, "\n");
 37
 38	va_end(args);
 39	exit(ec);
 40}
 41
 42static void rawon(void)
 43{
 44	if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
 45		die(__LINE__, EXIT_FAILURE, "tcgetattr failed");
 46	if (atexit(rawoff) != 0)
 47		die(__LINE__, EXIT_FAILURE, "cannot register disable_raw_mode");
 48
 49	struct termios raw_state = orig_termios;
 50	raw_state.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
 51	raw_state.c_iflag &= ~(IXON | ICRNL);
 52	raw_state.c_oflag &= ~(OPOST);
 53	raw_state.c_cc[VMIN] = 1;
 54	raw_state.c_cc[VTIME] = 0;
 55
 56	if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_state) == -1)
 57		die(__LINE__, EXIT_FAILURE, "tcsetattr failed");
 58}
 59
 60static void rawoff(void)
 61{
 62	tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
 63}
 64
 65static void getwinsize(void)
 66{
 67	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0)
 68		die(__LINE__, EXIT_FAILURE, "failed to get window size");
 69}
 70
 71static void run(void)
 72{
 73	spawnpty();
 74	struct pollfd fds[2]; /* stdin, shell */
 75	fds[0].fd = STDIN_FILENO;
 76	fds[0].events = POLLIN;
 77	fds[1].fd = masterfd;
 78	fds[1].events = POLLIN;
 79	char buf[256];
 80
 81	printf("\r\nin pty\r\n");
 82	for (;;) {
 83		if (poll(fds, 2, -1) < 0)
 84			break;
 85
 86		/* input->shell */
 87		if (fds[0].revents & POLLIN) {
 88			int n = read(STDIN_FILENO, buf, sizeof(buf));
 89			if (n <= 0)
 90				break;
 91			if (n == 1 && buf[0] == 0x0E) /* TODO ctrl-n */
 92				break;
 93			if (write(masterfd, buf, n) < 0)
 94				break;
 95		}
 96
 97		/* shell->screen */
 98		if (fds[1].revents & POLLIN) {
 99			int n = read(masterfd, buf, sizeof(buf));
100			if (n <= 0)
101				break;
102			if (write(STDOUT_FILENO, buf, n) < 0)
103				break;
104		}
105
106		/* either descriptor closes */
107		if ((fds[0].revents | fds[1].revents) & (POLLHUP | POLLERR))
108			break;
109	}
110
111	printf("\r\nexited pty\r\n");
112
113}
114
115static void spawnpty(void)
116{
117	pid_t pid = forkpty(&masterfd, NULL, NULL, &ws);
118	if (pid < 0)
119		die(__LINE__, EXIT_FAILURE, "forkpty failed");
120	if (pid == 0) {
121		char* shell = getenv("SHELL");
122		if (!shell)
123			shell = "sh";
124		execlp(shell, shell, NULL);
125		fprintf(stderr, "ln:%d execlp failed", __LINE__);
126		_exit(EXIT_FAILURE);
127	}
128}
129
130int main(int argc, char* argv[])
131{
132	if (argc > 1) {
133		if (strcmp("-v", argv[1]) == 0) {
134			printf("tmtm v%s", TMTM_VER);
135			return EXIT_SUCCESS;
136		}
137	}
138
139	rawon();
140	getwinsize();
141	run();
142
143	return EXIT_SUCCESS;
144}
145