16.6.2. Простые способы открытия псевдотерминалов
16.6.2. Простые способы открытия псевдотерминалов
В библиотеке libutil glibc предлагает две функции — openpty() и forkpty(), — выполняющие почти всю работу по поддержке псевдотерминалов.
#include <pty.h>
int openpty(int * masterfd, int * slavefd, char * name,
struct termios * term, struct winsize * winp);
int forkpty(int * masterfd, char * name,
struct termios * term, struct winsize * winp);
Функция openpty() открывает ведущие и подчиненные псевдотерминалы, необязательно используя структуры struct termios и struct winsize, передаваемые как опции настройки псевдотерминала, возвращая 0 в случае успеха и -1 в случае ошибки. Файловые дескрипторы ведущего устройства и подчиненного компонента возвращаются аргументам masterfd и slavefd соответственно. Аргументы term и winp могут быть NULL, в случае чего они игнорируются, и настройка не выполняется.
Функция forkpty() работает так же, как и openpty(), но вместо возврата файлового дескриптора подчиненного компонента она разветвляет псевдотерминал как управляющий терминал stdin, stdout и stderr для дочернего процесса, а затем, подобно fork(), возвращает идентификатор дочернего процесса родительскому и 0 дочернему либо -1 при возникновении ошибки.
Даже с этими удобными интерфейсами связана значительная проблема: аргумент name был изначально предназначен для возврата имени устройства псевдотерминала вызывающему коду, но его использование небезопасно, поскольку openpty() и forkpty() не знают размера буфера. Всегда передавайте NULL в аргументе name. Используйте функцию ttyname(), описанную в начале этой главы, чтобы получить путевое имя файла устройства псевдотерминала.
Предпочтительный способ работы с struct termios заключается в использовании цикла чтение-модификация-запись, но данному случаю это не соответствует по двум причинам. Можно передать NULL и принять значения по умолчанию, что достаточно в большинстве случаев; а когда вы хотите предоставить настройки termios, вы часто заимствуете настройки у другого tty, или знаете точно, какими они должны быть (например, в случае концентратора последовательного порта SCSI, описанного ранее в этой главе).
tcgetattr(STDIN_FILENO, &term);
ioctl(STDIN_FILENO, TIOCGWINSZ, &ws);
pid = forkpty(&masterfd, NULL, &term, &ws);