Created
March 2, 2025 18:35
-
-
Save apsun/732a91eba1dcf921ba8c7f6d91ebfee2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <errno.h> | |
#include <pty.h> | |
#include <sys/ttydefaults.h> | |
#include <sys/epoll.h> | |
#include <sys/wait.h> | |
#include <sys/signalfd.h> | |
#define lengthof(x) (sizeof(x) / sizeof(*(x))) | |
ssize_t pty2stdout(int ptymasterfd) { | |
ssize_t n; | |
char buf[4096]; | |
n = read(ptymasterfd, buf, sizeof(buf)); | |
if (n < 0) { | |
if (errno == EIO) { | |
return 0; | |
} | |
perror("read(ptymasterfd)"); | |
exit(1); | |
} | |
n = write(STDOUT_FILENO, buf, n); | |
if (n < 0) { | |
perror("write(stdout)"); | |
exit(1); | |
} | |
return 0; | |
} | |
ssize_t stdin2pty(int ptymasterfd) { | |
ssize_t n; | |
char buf[4096]; | |
n = read(STDIN_FILENO, buf, sizeof(buf)); | |
if (n < 0) { | |
perror("read(stdin)"); | |
exit(1); | |
} | |
if (n == 0) { | |
char c = CEOF; | |
n = write(ptymasterfd, &c, 1); | |
} else { | |
n = write(ptymasterfd, buf, n); | |
} | |
if (n < 0) { | |
perror("write(ptymasterfd)"); | |
exit(1); | |
} | |
return n; | |
} | |
ssize_t signalfd2pty(int sfd, int ptymasterfd) { | |
ssize_t n; | |
struct signalfd_siginfo si; | |
n = read(sfd, &si, sizeof(si)); | |
if (n < 0) { | |
perror("read(signalfd)"); | |
exit(1); | |
} | |
char c; | |
if (si.ssi_signo == SIGINT) { | |
c = CINTR; | |
} else if (si.ssi_signo == SIGTSTP) { | |
c = CSUSP; | |
} else if (si.ssi_signo == SIGQUIT) { | |
c = CQUIT; | |
} else { | |
fprintf(stderr, "unknown signal\n"); | |
exit(1); | |
} | |
n = write(ptymasterfd, &c, 1); | |
if (n < 0) { | |
perror("write(ptymasterfd)"); | |
exit(1); | |
} | |
return n; | |
} | |
int master(int ptymasterfd, int childpid) { | |
int epollfd = epoll_create1(0); | |
if (epollfd < 0) { | |
perror("epoll_create1"); | |
exit(1); | |
} | |
sigset_t mask; | |
sigemptyset(&mask); | |
sigaddset(&mask, SIGINT); | |
sigaddset(&mask, SIGTSTP); | |
sigaddset(&mask, SIGQUIT); | |
if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { | |
perror("sigprocmask"); | |
exit(1); | |
} | |
int sfd = signalfd(-1, &mask, 0); | |
if (sfd < 0) { | |
perror("signalfd"); | |
exit(1); | |
} | |
struct epoll_event ev; | |
ev.events = EPOLLIN; | |
ev.data.fd = ptymasterfd; | |
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ptymasterfd, &ev) < 0) { | |
perror("epoll_ctl(ptymasterfd)"); | |
exit(1); | |
} | |
ev.events = EPOLLIN; | |
ev.data.fd = STDIN_FILENO; | |
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) < 0) { | |
perror("epoll_ctl(STDIN_FILENO)"); | |
exit(1); | |
} | |
ev.events = EPOLLIN; | |
ev.data.fd = sfd; | |
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &ev) < 0) { | |
perror("epoll_ctl(signalfd)"); | |
exit(1); | |
} | |
while (1) { | |
struct epoll_event events[3]; | |
int nfds = epoll_wait(epollfd, events, lengthof(events), -1); | |
if (nfds < 0) { | |
perror("epoll_wait"); | |
exit(1); | |
} | |
for (int i = 0; i < nfds; ++i) { | |
struct epoll_event *ev = &events[i]; | |
ssize_t n; | |
if (ev->data.fd == ptymasterfd) { | |
n = pty2stdout(ptymasterfd); | |
} else if (ev->data.fd == STDIN_FILENO) { | |
n = stdin2pty(ptymasterfd); | |
} else if (ev->data.fd == sfd) { | |
n = signalfd2pty(sfd, ptymasterfd); | |
} else { | |
fprintf(stderr, "unknown fd\n"); | |
exit(1); | |
} | |
} | |
int status; | |
int waitret = waitpid(childpid, &status, WNOHANG); | |
if (waitret == childpid) { | |
if (WIFEXITED(status)) { | |
break; | |
} | |
} | |
} | |
close(epollfd); | |
close(sfd); | |
} | |
void dumptermios(void) { | |
struct termios curr; | |
if (tcgetattr(0, &curr) < 0) { | |
perror("tcgetattr"); | |
exit(1); | |
} | |
#define X(x) printf("c_iflag[%s] = %d\n", #x, !!(curr.c_iflag & x)); | |
X(IGNBRK) | |
X(BRKINT) | |
X(IGNPAR) | |
X(PARMRK) | |
X(INPCK) | |
X(ISTRIP) | |
X(INLCR) | |
X(IGNCR) | |
X(ICRNL) | |
X(IUCLC) | |
X(IXON) | |
X(IXANY) | |
X(IXOFF) | |
X(IMAXBEL) | |
X(IUTF8) | |
#undef X | |
printf("\n"); | |
#define X(x) printf("c_oflag[%s] = %d\n", #x, !!(curr.c_oflag & x)); | |
X(OPOST) | |
X(OLCUC) | |
X(ONLCR) | |
X(OCRNL) | |
X(ONOCR) | |
X(ONLRET) | |
X(OFILL) | |
X(OFDEL) | |
X(NLDLY) | |
X(NL0) | |
X(NL1) | |
X(CRDLY) | |
X(CR0) | |
X(CR1) | |
X(CR2) | |
X(CR3) | |
X(TABDLY) | |
X(TAB0) | |
X(TAB1) | |
X(TAB2) | |
X(TAB3) | |
X(BSDLY) | |
X(BS0) | |
X(BS1) | |
X(FFDLY) | |
X(FF0) | |
X(FF1) | |
X(VTDLY) | |
X(VT0) | |
X(VT1) | |
X(XTABS) | |
#undef X | |
printf("\n"); | |
#define X(x) printf("c_lflag[%s] = %d\n", #x, !!(curr.c_lflag & x)); | |
X(ISIG) | |
X(ICANON) | |
X(XCASE) | |
X(ECHO) | |
X(ECHOE) | |
X(ECHOK) | |
X(ECHONL) | |
X(NOFLSH) | |
X(TOSTOP) | |
X(ECHOCTL) | |
X(ECHOPRT) | |
X(ECHOKE) | |
X(FLUSHO) | |
X(PENDIN) | |
X(IEXTEN) | |
X(EXTPROC) | |
#undef X | |
printf("\n"); | |
#define X(x) printf("c_cc[%s] = %d\n", #x, curr.c_cc[x]); | |
X(VINTR) | |
X(VQUIT) | |
X(VERASE) | |
X(VKILL) | |
X(VEOF) | |
X(VTIME) | |
X(VMIN) | |
X(VSWTC) | |
X(VSTART) | |
X(VSTOP) | |
X(VSUSP) | |
X(VEOL) | |
X(VREPRINT) | |
X(VDISCARD) | |
X(VWERASE) | |
X(VLNEXT) | |
X(VEOL2) | |
#undef X | |
} | |
int main(void) { | |
dumptermios(); | |
int ptymasterfd; | |
struct termios term = { | |
.c_iflag = ICRNL | /* IXON | */ IUTF8, | |
.c_oflag = OPOST | ONLCR, | |
.c_lflag = ISIG | ICANON | /* ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | */ IEXTEN, | |
.c_cc = { | |
[VINTR] = CINTR, | |
[VEOF] = CEOF, | |
[VSUSP] = CSUSP, | |
[VQUIT] = CQUIT, | |
}, | |
}; | |
struct winsize win = { | |
.ws_row = 0, | |
.ws_col = 0, | |
.ws_xpixel = 0, | |
.ws_ypixel = 0, | |
}; | |
int childpid = forkpty(&ptymasterfd, NULL, &term, &win); | |
if (childpid < 0) { | |
perror("forkpty"); | |
exit(1); | |
} else if (childpid == 0) { | |
char *prog = "/bin/sh"; | |
char *argv[] = {prog, NULL}; | |
char *envp[] = {"TERM=dumb", NULL}; | |
if (execvpe(prog, argv, envp) < 0) { | |
perror("exec"); | |
exit(1); | |
} | |
} else { | |
int ret = master(ptymasterfd, childpid); | |
close(ptymasterfd); | |
return ret; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment