#include "config.h" #if !defined(HAVE_PTY_H) && defined(HAVE__DEV_PTMX) && !defined(HAVE_FORKPTY) /* * Substitute for the nonstandard BSD/GNU extension 'forkpty' using * SysV STREAMS (the /dev/ptmx pseudoterminal multiplexer). * * dgc@uchicago.edu */ #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif #include <string.h> #include <unistd.h> #include <fcntl.h> #include <limits.h> /* PATH_MAX */ #include <stdlib.h> #include <sys/types.h> #include <sys/termios.h> #include <stropts.h> #define DEV_PTMX "/dev/ptmx" enum { EPTMX_OK = 0, EPTMX_OPEN, EPTMX_GRANT, EPTMX_UNLOCK, EPTMX_NAME, EPTMX_FIND, EPTMX_PUSH_PTEM, EPTMX_PUSH_LDTERM, EPTMX_PUSH_TTCOMPAT, EPTMX_END } ptmx_errs; int open_master(char *name, int sz) { char *sname; int fd; strncpy(name, DEV_PTMX, sz); name[sz-1] = '\0'; fd = open(name, O_RDWR); if (fd < 0) return EPTMX_OPEN; if (grantpt(fd) < 0) { close(fd); return EPTMX_GRANT; } if (unlockpt(fd) < 0) { close(fd); return EPTMX_UNLOCK; } sname = ptsname(fd); if (sname == NULL) { close(fd); return EPTMX_NAME; } strncpy(name, sname, sz); name[sz-1] = '\0'; return fd; } int open_slave(char *name) { int fd; int status; fd = open(name, O_RDWR); if (fd < 0) return EPTMX_OPEN; status = ioctl(fd, I_FIND, "ldterm"); if (status < 0) { close(fd); return EPTMX_FIND; } if (status > 0) return fd; if (ioctl(fd, I_PUSH, "ptem") < 0) { close(fd); return EPTMX_PUSH_PTEM; } if (ioctl(fd, I_PUSH, "ldterm") < 0) { close(fd); return EPTMX_PUSH_LDTERM; } if (ioctl(fd, I_PUSH, "ttcompat") < 0) { close(fd); return EPTMX_PUSH_TTCOMPAT; } return fd; } int login_tty(int fd) { setsid(); if (ioctl(fd, TIOCSCTTY, NULL) == -1) return -1; dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); return 0; } pid_t forkpty(int *masterp, char *name, struct termios *termp, struct winsize *winp) { int master, slave; char ptname[PATH_MAX]; pid_t pid; master = open_master(ptname, sizeof(ptname)); if (master < 0) { return -1; } slave = open_slave(ptname); if (slave < 0) { close(master); return -1; } if (name) strcpy(name, ptname); if (termp) tcsetattr(slave, TCSAFLUSH, termp); if (winp) ioctl(slave, TIOCSWINSZ, winp); pid = fork(); if (pid < 0) { close(slave); close(master); return -1; } else if (pid == 0) { /* child/slave */ close(master); login_tty(slave); return 0; } /* parent/master */ *masterp = master; close(slave); return pid; } #endif