Attached is a basic forkpty-aix.c that I put together from tmux and
glib projects and my own testing. I've been using it successfully for
over a month now, so it should be OK. Additional patches to come later
today in this thread.
-Ross
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
/* Open a pseudo-terminal. */
static int
openpty (int *amaster, int *aslave, char *name,
struct termios const *termp, struct winsize const *winp)
{
int master;
char *slave_name;
int slave;
master = open ("/dev/ptc", O_RDWR | O_NOCTTY);
if (master < 0)
return -1;
if (grantpt (master))
goto fail;
if (unlockpt (master))
goto fail;
slave_name = ptsname (master);
if (slave_name == NULL)
goto fail;
slave = open (slave_name, O_RDWR | O_NOCTTY);
if (slave == -1)
goto fail;
/* XXX Should we ignore errors here? */
if (termp)
tcsetattr (slave, TCSAFLUSH, termp);
if (winp)
ioctl (slave, TIOCSWINSZ, winp);
*amaster = master;
*aslave = slave;
if (name != NULL)
strcpy (name, slave_name);
return 0;
fail:
close (master);
return -1;
}
/* Assign a given terminal as controlling terminal and as standard input,
standard output, standard error of the current process. */
static int
login_tty (int slave_fd)
{
int i;
/* Create a new session. */
setsid ();
/* Make fd the controlling terminal for the current process.
On Solaris:
A terminal becomes the controlling terminal of a session
if it is being open()ed, at a moment when
1. it is not already the controlling terminal of some session, and
2. the process that open()s it is a session leader that does not have
a controlling terminal.
We assume condition 1, try to ensure condition 2, and then open() it.
*/
for (i = 0; i < 3; i++)
if (i != slave_fd)
close (i);
char *slave_name;
int dummy_fd;
slave_name = ttyname (slave_fd);
if (slave_name == NULL)
return -1;
dummy_fd = open (slave_name, O_RDWR);
if (dummy_fd < 0)
return -1;
close (dummy_fd);
/* Assign fd to the standard input, standard output, and standard error of
the current process. */
for (i = 0; i < 3; i++)
if (slave_fd != i)
while (dup2 (slave_fd, i) == -1 && errno == EBUSY);
// if (dup2 (slave_fd, i) < 0)
// return -1;
if (slave_fd >= 3)
close (slave_fd);
return 0;
}
/* Fork a child process attached to the slave of a pseudo-terminal. */
int
forkpty (int *amaster, char *name,
const struct termios *termp, const struct winsize *winp)
{
int master, slave, pid;
if (openpty (&master, &slave, name, termp, winp) == -1)
return -1;
switch (pid = fork ())
{
case -1:
close (master);
close (slave);
return -1;
case 0:
/* Child. */
close (master);
if (login_tty (slave))
_exit (1);
return 0;
default:
/* Parent. */
*amaster = master;
close (slave);
// sleep(1);
return pid;
}
}