After opening the PTY master, fork is called. As we mentioned before, we want to wait to call ptys_open until in the child and after calling setsid to establish a new session. When it calls setsid, the child is not a process group leader, so the three steps listed in Section 9.5 occur: (a) a new session is created with the child as the session leader, (b) a new process group is created for the child, and © the child loses any association it might have had with its previous controlling terminal. Under Linux, Mac OS X, and Solaris, the slave becomes the controlling terminal of this new session when ptys_open is called. Under FreeBSD, we have to use the TIOCSCTTY ioctl command to allocate the controlling terminal. (Recall Figure 9.8 — the other three platforms also support TIOCSCTTY, but we need to call it only on FreeBSD.)
The two structures termios and winsize are then initialized in the child. Finally, the slave file descriptor is duplicated onto standard input, standard output, and standard error in the child. This means that whatever process the caller execs from the child will have these three descriptors connected to the slave PTY (its controlling terminal).
After the call to fork, the parent just returns the PTY master descriptor and the process ID of the child. In the next section, we use the pty_fork function in the pty program.
#include "apue.h"
#include <termios.h>
pid_t pty_fork(int *ptrfm, char *slave_name, int slave_namesz,
const struct termios *slave_termios,
const struct winsize *slave_winsize)
{
int fdm, fds;
pid_t pid;
char pts_name[20];
if((fdm=ptym_open(pts_name, sizeof(pts_name)))<0)
err_sys("can't open master pty: %s, error %d", pts_name, fdm);
if(slave_name!=NULL)
{
strncpy(slave_name, pts_name, slave_namesz);
slave_name[slave_namesz-1]='\0';
}
if((pid=fork())<0)
{
return -1;
}
else if(pid==0)
{
if(setsid()<0)
err_sys("setid error");
if((fds=ptys_open(pts_name))<0)
err_sy("can't open slave pty");
close(fdm);
#if defined(BSD)
if(ioctl(fds, TIOCSCTTY, (char *)0)<0)
err_sys("TIOCSCTTY error");
#endif
if(slave_termios!=NULL)
{
if(tcsetattr(fds,TCSANOW, slave_termios)<0)
err_sys("tcsetattr error on slave pty");
}
if(slave_winsize!=NULL)
{
if(ioctl(fds, TIOCSWINSZ, slave_winsize)<0)
err_sy("TIOCSWINSZ error on slave pty");
}
if(dup2(fds, STDIN_FILENO)!=STDIN_FILENO)
err_sys("dup2 error to stdin");
if(dup2(fds, STDOUT_FILENO)!=STDOUT_FILENO)
err_sys("dup2 error to stdout");
if(dup2(fds, STDERR_FILENO)!=STDERR_FILENO)
err_sys("dup2 error to stderr");
if(fds!=STDIN_FILENO&&fds!=STDOUT_FILENO&&fds!=STDERR_FILENO)
close(fds);
return 0;
}
else
{
*ptrfdm=fdm;
retur pid;
}
}
The pty_fork function