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