4. После вызова fork() родителю остается только убедиться в том, что в целом числе, на которое указывает аргумент masterFd, находится файловый дескриптор первичного устройства псевдотерминала
5. После вызова fork() потомок выполняет следующие действия.
1) Вызывает setsid(), чтобы создать новую сессию (см. раздел 34.3)
2) Закрывает файловый дескриптор для первичного устройства, поскольку он ему не нужен
3) Открывает вторичное устройство псевдотерминала
4) Выполняет операцию для файлового дескриптора вторичного устройства TIOCSCTTY ioctl()
5) Если аргумент slaveTermios не равен NULL, то делает вызов tcsetattr(), чтобы установить атрибуты вторичного устройства в соответствии со значениями внутри структуры termios, на которую указывает данный аргумент
6) Если аргумент slaveWS не равен NULL, то выполняет операцию ioctl() TIOCSWINSZ с целью установить размер окна вторичного устройства псевдотерминала в соответствии со значениями в структуре winsize, на которую указывает данный аргумент
7) Использует вызов dup2() для дублирования вторичного файлового дескриптора в качестве стандартного ввода, вывода и потока stderr
По аналогии с вызовом fork() функция ptyFork() возвращает либо идентификатор потомка (в родительском процессе), либо 0 (в дочернем процессе), либо –1 в случае ошибки.
В какой-то момент дочерний процесс, созданный с помощью ptyFork(), будет завершен. Если после этого родитель продолжает работу, то должен отследить своего потомка, чтобы устранить процесс-зомби. Однако данная процедура часто является необязательной, так как приложения, использующие псевдотерминалы, обычно спроектированы с расчетом на одновременное завершение родителя и потомка.
Системы семейства BSD предоставляют две похожие нестандартные функции для работы с псевдотерминалами. Первая, openpty(), открывает первичное и вторичное устройства, возвращая их файловые дескрипторы. Она также может вернуть имя вторичного устройства и установить размер окна и атрибуты терминала, взятые из аргументов, аналогичных slaveTermios и slaveWS. Другая функция, forkpty(), является аналогом нашей реализации ptyFork(); разница лишь в том, что она не поддерживает аргумент, похожий на snLen. В Linux обе эти функции предоставляются библиотекой glibc и задокументированы на странице openpty(3) руководства.
Листинг 60.2. Реализация функции ptyFork()
pty/pty_fork.c
#include
#include
#include
#include "pty_master_open.h"
#include "pty_fork.h" /* Объявляет ptyFork() */
#include "tlpi_hdr.h"
#define MAX_SNAME 1000
pid_t
ptyFork(int *masterFd, char *slaveName, size_t snLen,
const struct termios *slaveTermios, const struct winsize *slaveWS)
{
int mfd, slaveFd, savedErrno;
pid_t childPid;
char slname[MAX_SNAME];
if (mfd == -1)
return -1;
if (strlen(slname) < snLen) {
strncpy(slaveName, slname, snLen);
} else { /* Имя 'slaveName' слишком короткое */
close(mfd);
errno = EOVERFLOW;
return -1;
}
}
if (childPid == -1) { /* Вызов fork() завершился неудачно */
savedErrno = errno; /* Операция close() может изменить 'errno' */
close(mfd); /* Освобождаем ресурс файлового дескриптора */
errno = savedErrno;
return -1;
}
*masterFd = mfd; /* Только родитель получает первичный fd */
return childPid; /* Как родитель вызова fork()*/
}
/* Потомок переходит в эту точку */
err_exit("ptyFork: setsid");
if (slaveFd == -1)
err_exit("ptyFork: open-slave");
if (ioctl(slaveFd, TIOCSCTTY, 0) == -1)
err_exit("ptyFork: ioctl-TIOCSCTTY");
#endif
if (tcsetattr(slaveFd, TCSANOW, slaveTermios) == -1)
err_exit("ptyFork: tcsetattr");