/* Copyright status: this file is in the public domain. */ /* * , the documented include file for termios stuff, defines * struct termios and many associated manifest constants. Including * it does not, however, define TIOCSCTTY - even though tty_ioctl(4) * describes TIOCSCTTY and says is the correct file to * include for it. * * TIOCSCTTY is actually defined in exactly one place: * /usr/include/asm-generic/ioctls.h. (All further relative paths in * this paragraph are relative to /usr/include.) asm-generic/ioctls.h * is included from exactly one place, x86_64-linux-gnu/asm/ioctls.h. * asm/ioctls.h is included from a handful of places. The correct one * for us to get it from is bits/ioctls.h, via sys/ioctl.h, but it * also included from asm-generic/termios.h, which is included from * x86_64-linux-gnu/asm/termios.h, which is included from * linux/termios.h. But if we include , we get a * struct termios that is substantially smaller than the real thing, * leading tcgetattr to scribble on the stack frame! (The proper * struct termios has 32 c_cc entries and c_[io]speed members. If we * include , we get asm-generic/termbits.h, by way of * various other files, and that version has only 19 entries in c_cc * and no c_[io]speed.) * * So, is not enough without , contrary to the * documentation, and there are severely broken variants of things * lurking in the include files to surprise the unwary. */ #define _XOPEN_SOURCE /* necessary to get ptsname() declared (!) */ #define _XOPEN_SOURCE_EXTENDED /* necessary to get strdup() declared (!!) */ #define _BSD_SOURCE /* necessary to get ECHOKE and ECHOCTL (!!!) */ #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "ptyimpl.h" static int ptypair(int *mfdp, int *sfdp, char **spathp) { int i; int mfd; int sfd; int tfd; char *p; struct termios tio; struct termios tty_tio; tfd = open("/dev/tty",O_RDONLY,0); if (tfd >= 0) { if (tcgetattr(tfd,&tty_tio) == 0) { close(tfd); tfd = 1; } else { close(tfd); tfd = 0; } } else { tfd = 0; } mfd = posix_openpt(O_RDWR|O_NOCTTY); if (mfd < 0) return(-1); if (grantpt(mfd) < 0) return(-1); if (unlockpt(mfd) < 0) return(-1); p = ptsname(mfd); if (! p) return(-1); sfd = open(p,O_RDWR,0); if (sfd < 0) return(-1); if (tcgetattr(sfd,&tio) == 0) { tio.c_iflag = ICRNL | IXON | IXANY | IMAXBEL; tio.c_oflag = OPOST | ONLCR; tio.c_cflag = (tio.c_cflag & (CSIZE|CSTOPB|PARENB|PARODD)) | CREAD | HUPCL; tio.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOKE | ECHOCTL; tio.c_cc[VEOF] = CEOF; tio.c_cc[VEOL] = CEOL; tio.c_cc[VERASE] = CERASE; tio.c_cc[VINTR] = CINTR; tio.c_cc[VKILL] = CKILL; tio.c_cc[VQUIT] = CQUIT; tio.c_cc[VSUSP] = CSUSP; tio.c_cc[VSTART] = CSTART; tio.c_cc[VSTOP] = CSTOP; tio.c_cc[VLNEXT] = CLNEXT; tio.c_cc[VDISCARD] = CDISCARD; tio.c_cc[VWERASE] = CWERASE; tio.c_cc[VREPRINT] = CREPRINT; if (tfd) { for (i=(sizeof(tio.c_cc)/sizeof(tio.c_cc[0]))-1;i>=0;i--) tio.c_cc[i] = tty_tio.c_cc[i]; } tio.c_cc[VMIN] = CMIN; tio.c_cc[VTIME] = CTIME; tcsetattr(sfd,TCSANOW,&tio); } *mfdp = mfd; *sfdp = sfd; *spathp = strdup(p); return(0); } void ptypair_setup(int *mfdp, int *sfdp, char **spathp, char **errp) { char *spath; if (ptypair(mfdp,sfdp,&spath) < 0) { *errp = strdup("no pty pairs available"); return; } if (spathp) *spathp = spath; else free(spath); *errp = 0; } void conspty_setup(int *mfdp, int *sfdp, const char **spathp, char **errp) { char *spath; if (ptypair(mfdp,sfdp,&spath) < 0) { asprintf(errp,"no pty pairs available: %s",strerror(errno)); return; } if (spathp) *spathp = spath; else free(spath); *errp = 0; } void ptyexport_setup(const char *prog, int *ptymp, int *ptysp, char **errp) { int mfd; int sfd; char *spath; pid_t kid; int xp[2]; int e; int r; pid_t wk; int ws; if (ptypair(&mfd,&sfd,&spath) < 0) { asprintf(errp,"no pty pairs available: %s",strerror(errno)); return; } do { if (socketpair(AF_LOCAL,SOCK_STREAM,0,&xp[0]) < 0) { asprintf(errp,"socketpair: %s",strerror(errno)); break; } do <"err"> { fflush(0); kid = fork(); if (kid < 0) { asprintf(errp,"fork: %s",strerror(errno)); break; } if (kid == 0) { close(xp[0]); fcntl(xp[1],F_SETFD,1); execlp(prog,prog,"up",spath,(char *)0); e = errno; send(xp[1],&e,sizeof(int),0); _exit(0); } close(xp[1]); xp[1] = -1; r = recv(xp[0],&e,sizeof(int),MSG_WAITALL); if (r < 0) { asprintf(errp,"exec pipe recv: %s",strerror(errno)); break; } if (r == sizeof(int)) { asprintf(errp,"exec: %s",strerror(e)); break; } if (r != 0) { asprintf(errp,"exec protocol error (wanted %d, got %d)",(int)sizeof(int),r); break; } close(xp[0]); while (1) { wk = waitpid(kid,&ws,0); if (wk < 0) { asprintf(errp,"exec protocol wait: %s",strerror(errno)); break <"err">; } if (wk == 0) { *errp = strdup("exec protocol lost child"); break <"err">; } if (wk == kid) break; } if (WIFEXITED(ws)) { if (WEXITSTATUS(ws) == 0) { *ptymp = mfd; *ptysp = sfd; *errp = 0; return; } asprintf(errp,"%s: exit %d",prog,WEXITSTATUS(ws)); } else if (WIFSIGNALED(ws)) { asprintf(errp,"%s: signal %d",prog,WTERMSIG(ws)); } else if (WIFSTOPPED(ws)) { asprintf(errp,"%s: stopped on signal %d",prog,WSTOPSIG(ws)); } else { asprintf(errp,"%s: mystery exit status %#x",prog,ws); } } while (0); if (xp[0] >= 0) close(xp[0]); if (xp[1] >= 0) close(xp[1]); } while (0); close(mfd); close(sfd); } void pty_ctty(int sfd) { int arg; setsid(); arg = 0; ioctl(sfd,TIOCSCTTY,&arg); }