19.4. pty_fork FunctionWe now use the two functions from the previous section, ptym_open and ptys_open, to write a new function that we call pty_fork. This new function combines the opening of the master and the slave with a call to fork, establishing the child as a session leader with a controlling terminal.
Figure 19.11. The pty_fork function#include "apue.h" #include <termios.h> #ifndef TIOCGWINSZ #include <sys/ioctl.h> #endif pid_t pty_fork(int *ptrfdm, 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) { /* * Return name of slave. Null terminate to handle case * where strlen(pts_name) > slave_namesz. */ strncpy(slave_name, pts_name, slave_namesz); slave_name[slave_namesz - 1] = '\0'; } if ((pid = fork()) < 0) { return(-1); } else if (pid == 0) { /* child */ if (setsid() < 0) err_sys("setsid error"); /* * System V acquires controlling terminal on open(). */ if ((fds = ptys_open(pts_name)) < 0) err_sys("can't open slave pty"); close(fdm); /* all done with master in child */ #if defined(TIOCSCTTY) /* * TIOCSCTTY is the BSD way to acquire a controlling terminal. */ if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) err_sys("TIOCSCTTY error"); #endif /* * Set slave's termios and window size. */ 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_sys("TIOCSWINSZ error on slave pty"); } /* * Slave becomes stdin/stdout/stderr of child. */ 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); /* child returns 0 just like fork() */ } else { /* parent */ *ptrfdm = fdm; /* return fd of master */ return(pid); /* parent returns pid of child */ } }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 (c) the child loses any association it might have had with its previous controlling terminal. Under Linux and Solaris, the slave becomes the controlling terminal of this new session when ptys_open is called. Under FreeBSD and Mac OS X, we have to call ioctl with an argument of TIOCSCTTY to allocate the controlling terminal. (Linux also supports the TIOCSCTTY ioctl command.) 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. |