#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