208 lines
5.2 KiB
C
208 lines
5.2 KiB
C
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <pty.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
|
|
static sig_atomic_t died;
|
|
static void sigchld(int sig __attribute__((__unused__)))
|
|
{
|
|
const int old_errno = errno;
|
|
int status;
|
|
pid_t pid;
|
|
while ((pid = waitpid(-1, &status, WNOHANG|WUNTRACED)) != 0) {
|
|
if (errno == ECHILD)
|
|
break;
|
|
if (pid < 0)
|
|
continue;
|
|
died = 1;
|
|
}
|
|
errno = old_errno;
|
|
}
|
|
|
|
static pid_t pid = -1;
|
|
static void sigother(int sig)
|
|
{
|
|
printf("%s\n", strsignal(sig));
|
|
if (sig == SIGINT)
|
|
sig = SIGTERM;
|
|
if (pid > 0) kill(pid, sig);
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
int ptm, pts;
|
|
ssize_t len;
|
|
static struct termios o;
|
|
static struct winsize w;
|
|
char devname[NAME_MAX+1];
|
|
char buffer[65536];
|
|
sigset_t set;
|
|
struct sigaction sa;
|
|
|
|
if (ioctl(0, TIOCGWINSZ, &w) < 0) {
|
|
w.ws_row = 24;
|
|
w.ws_col = 160;
|
|
errno = 0;
|
|
}
|
|
if (tcgetattr(0, &o) < 0) {
|
|
cfsetispeed(&o, B38400);
|
|
cfsetospeed(&o, B38400);
|
|
}
|
|
|
|
o.c_iflag = TTYDEF_IFLAG;
|
|
o.c_oflag = TTYDEF_OFLAG;
|
|
o.c_lflag = TTYDEF_LFLAG;
|
|
# ifdef CBAUD
|
|
o.c_lflag &= ~CBAUD;
|
|
# endif
|
|
o.c_cflag = B38400;
|
|
o.c_cflag |= TTYDEF_CFLAG;
|
|
|
|
/* Sane setting, allow eight bit characters, no carriage return delay
|
|
* the same result as `stty sane cr0 pass8'
|
|
*/
|
|
o.c_iflag |= (BRKINT | ICRNL | IMAXBEL);
|
|
o.c_iflag &= ~(IGNBRK | INLCR | IGNCR | IXOFF | IUCLC | IXANY | INPCK | ISTRIP);
|
|
o.c_oflag |= (OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0);
|
|
o.c_oflag &= ~(OLCUC | OCRNL | ONOCR | ONLRET | OFILL | OFDEL |\
|
|
NLDLY|CRDLY|TABDLY|BSDLY|VTDLY|FFDLY);
|
|
o.c_lflag |= (ISIG | ICANON | IEXTEN | ECHO|ECHOE|ECHOK|ECHOKE);
|
|
o.c_lflag &= ~(ECHONL|ECHOCTL|ECHOPRT | NOFLSH | XCASE | TOSTOP);
|
|
o.c_cflag |= (CREAD | CS8 | HUPCL);
|
|
o.c_cflag &= ~(PARODD | PARENB);
|
|
/*
|
|
* VTIME and VMIN can overlap with VEOF and VEOL since they are
|
|
* only used for non-canonical mode. We just set the at the
|
|
* beginning, so nothing bad should happen.
|
|
*/
|
|
o.c_cc[VTIME] = 0;
|
|
o.c_cc[VMIN] = CMIN;
|
|
o.c_cc[VINTR] = CINTR;
|
|
o.c_cc[VQUIT] = CQUIT;
|
|
o.c_cc[VERASE] = CERASE; /* ASCII DEL (0177) */
|
|
o.c_cc[VKILL] = CKILL;
|
|
o.c_cc[VEOF] = CEOF;
|
|
# ifdef VSWTC
|
|
o.c_cc[VSWTC] = _POSIX_VDISABLE;
|
|
# else
|
|
o.c_cc[VSWTCH] = _POSIX_VDISABLE;
|
|
# endif
|
|
o.c_cc[VSTART] = CSTART;
|
|
o.c_cc[VSTOP] = CSTOP;
|
|
o.c_cc[VSUSP] = CSUSP;
|
|
o.c_cc[VEOL] = _POSIX_VDISABLE;
|
|
o.c_cc[VREPRINT] = CREPRINT;
|
|
o.c_cc[VDISCARD] = CDISCARD;
|
|
o.c_cc[VWERASE] = CWERASE;
|
|
o.c_cc[VLNEXT] = CLNEXT;
|
|
o.c_cc[VEOL2] = _POSIX_VDISABLE;
|
|
|
|
if (openpty(&ptm, &pts, devname, &o, &w) < 0)
|
|
perror("pty: can not open pty/tty pair");
|
|
|
|
(void)sigemptyset(&set);
|
|
(void)sigaddset(&set, SIGCHLD);
|
|
sigprocmask(SIG_UNBLOCK, &set, (sigset_t*)0);
|
|
|
|
sa.sa_flags = SA_RESTART;
|
|
sa.sa_handler = sigchld;
|
|
sigemptyset (&sa.sa_mask);
|
|
sigaction(SIGCHLD, &sa, (struct sigaction*)0);
|
|
|
|
(void)sigemptyset(&set);
|
|
(void)sigaddset(&set, SIGTERM);
|
|
sigprocmask(SIG_UNBLOCK, &set, (sigset_t*)0);
|
|
|
|
sa.sa_flags = SA_RESTART;
|
|
sa.sa_handler = sigother;
|
|
sigemptyset (&sa.sa_mask);
|
|
sigaction(SIGTERM, &sa, (struct sigaction*)0);
|
|
|
|
(void)sigemptyset(&set);
|
|
(void)sigaddset(&set, SIGHUP);
|
|
sigprocmask(SIG_UNBLOCK, &set, (sigset_t*)0);
|
|
|
|
sa.sa_flags = SA_RESTART;
|
|
sa.sa_handler = sigother;
|
|
sigemptyset (&sa.sa_mask);
|
|
sigaction(SIGHUP, &sa, (struct sigaction*)0);
|
|
|
|
switch ((pid = fork())) {
|
|
case 0:
|
|
ioctl(1, TIOCNOTTY);
|
|
if (setsid() < 0)
|
|
perror("pty: can not get controlling tty");
|
|
dup2(pts, 0);
|
|
dup2(pts, 1);
|
|
dup2(pts, 2);
|
|
close(pts);
|
|
close(ptm);
|
|
if (ioctl (0, TIOCSCTTY, 1) < 0)
|
|
perror("pty: can not get controlling tty");
|
|
break;
|
|
case -1:
|
|
close(pts);
|
|
close(ptm);
|
|
perror("pty: can not fork");
|
|
exit(1);
|
|
default:
|
|
dup2(ptm, 0);
|
|
close(pts);
|
|
close(ptm);
|
|
while ((len = read(0, buffer, sizeof(buffer)))) {
|
|
ssize_t p = 0;
|
|
const char* ptr = buffer;
|
|
while (len > 0) {
|
|
p = write(1, ptr, len);
|
|
if (p < 0) {
|
|
if (errno == EPIPE)
|
|
exit (0);
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
continue;
|
|
return 1;
|
|
}
|
|
ptr += p;
|
|
len -= p;
|
|
}
|
|
if (died)
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
(void)sigfillset(&set);
|
|
sigprocmask(SIG_UNBLOCK, &set, (sigset_t*)0);
|
|
|
|
(void)sigemptyset(&set);
|
|
(void)sigaddset(&set, SIGCHLD);
|
|
sigprocmask(SIG_BLOCK, &set, (sigset_t*)0);
|
|
|
|
sa.sa_flags = SA_RESTART;
|
|
sa.sa_handler = SIG_DFL;
|
|
sigemptyset (&sa.sa_mask);
|
|
|
|
sigaction(SIGHUP, &sa, (struct sigaction*)0);
|
|
sigaction(SIGINT, &sa, (struct sigaction*)0);
|
|
sigaction(SIGPIPE, &sa, (struct sigaction*)0);
|
|
sigaction(SIGTERM, &sa, (struct sigaction*)0);
|
|
sigaction(SIGURG, &sa, (struct sigaction*)0);
|
|
sigaction(SIGXFSZ, &sa, (struct sigaction*)0);
|
|
sigaction(SIGQUIT, &sa, (struct sigaction*)0);
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
sigaction(SIGQUIT, &sa, (struct sigaction*)0);
|
|
|
|
return execv(argv[1], &argv[1]);
|
|
}
|