/* Copyright status: this file is in the public domain. */ #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 ptyname[16]; struct termios tio; struct termios tty_tio; tfd = open("/dev/tty",O_RDONLY,0); for (i=0;;i++) { sprintf(&ptyname[0],"/dev/pty%c%x",'p'+(i/16),i%16); mfd = open(&ptyname[0],O_RDWR,0); if (mfd < 0) { int e; e = errno; if (e == ENOENT) { close(tfd); errno = e; return(-1); } continue; } ptyname[5] = 't'; /* sprintf(&ptyname[0],"/dev/tty%c%x",'p'+(i/16),i%16); */ sfd = open(&ptyname[0],O_RDWR,0); if (sfd < 0) { close(mfd); continue; } if (tcgetattr(sfd,&tio) < 0) { close(mfd); close(sfd); continue; } 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[VSTATUS] = CSTATUS; tio.c_cc[VKILL] = CKILL; tio.c_cc[VQUIT] = CQUIT; tio.c_cc[VSUSP] = CSUSP; tio.c_cc[VDSUSP] = CDSUSP; 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 >= 0) { if (tcgetattr(tfd,&tty_tio) == 0) { for (i=(sizeof(tio.c_cc)/sizeof(tio.c_cc[0]))-1;i>=0;i--) tio.c_cc[i] = tty_tio.c_cc[i]; } close(tfd); } tio.c_cc[VMIN] = CMIN; tio.c_cc[VTIME] = CTIME; tcsetattr(sfd,TCSANOW|TCSASOFT,&tio); *mfdp = mfd; *sfdp = sfd; *spathp = strdup(&ptyname[0]); 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 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) { *errp = strdup("no pty pairs available"); 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]); xp[0] = -1; 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%s",prog,WTERMSIG(ws),WCOREDUMP(ws)?" (core dumped)":""); } 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(sfd); close(mfd); } void pty_ctty(int sfd) { setsid(); ioctl(sfd,TIOCSCTTY,0); }