struct winsize ws;
fd_set inFds;
char buf[BUF_SIZE];
ssize_t numRead;
pid_t childPid;
errExit("tcgetattr");
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
errExit("ioctl-TIOCGWINSZ");
if (childPid == -1)
errExit("ptyFork");
if (childPid == 0) { /* Потомок запускает командную оболочку во вторичном pty */
if (shell == NULL || *shell == '\0')
shell = "/bin/sh";
errExit("execlp");
/* Если мы добрались до этой строчки, значит что-то пошло не так */
}
/* Родитель передает данные между терминалом и первичным pty */
O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (scriptFd == -1)
errExit("open typescript");
errExit("atexit");
FD_ZERO(&inFds);
FD_SET(STDIN_FILENO, &inFds);
FD_SET(masterFd, &inFds);
errExit("select");
numRead = read(STDIN_FILENO, buf, BUF_SIZE);
if (numRead <= 0)
exit(EXIT_SUCCESS);
if (write(masterFd, buf, numRead)!= numRead)
fatal("partial/failed write (masterFd)");
}
numRead = read(masterFd, buf, BUF_SIZE);
if (numRead <= 0)
exit(EXIT_SUCCESS);
if (write(STDOUT_FILENO, buf, numRead)!= numRead)
fatal("partial/failed write (STDOUT_FILENO)");
if (write(scriptFd, buf, numRead)!= numRead)
fatal("partial/failed write (scriptFd)");
}
}
}
pty/script.c
Применение программы из листинга 60.3 демонстрируется в следующей сессии командной строки. Сначала мы выводим имя псевдотерминала, которое используется в терминале xterm, выполняющем нашу сессию, а также идентификатор процесса командной оболочки. Эта информация пригодится в дальнейшем.
$ tty
/dev/pts/1
$ echo $$
7979
Теперь запустим экземпляр нашей программы script, вызывающей еще одну командную оболочку. Опять же, мы выводим имя терминала, в котором выполняется сессия, и идентификатор процесса командной оболочки:
$ ./script
$ tty
/dev/pts/24
$ echo $$
29825
Теперь воспользуемся утилитой ps(1), чтобы вывести сведения о двух командных оболочках и процессе, выполняющем программу script, после чего завершим оболочку, запущенную этой программой:
$ ps — p 7979 — p 29825 — C script — o "pid ppid sid tty cmd"
PID PPID SID TT CMD
7979 7972 7979 pts/1 /bin/bash
29824 7979 7979 pts/1./script
29825 29824 29825 pts/24 /bin/bash
$ exit
Вывод утилиты ps(1) показывает, что исходная командная строка, процесс, выполняющий программу script, и вторая командная оболочка, запущенная этой программой, имеют родственные связи.
На данном этапе мы вернулись в исходную командную строку. Если вывести содержимое файла typescript, то можно увидеть запись всех входящих и исходящих данных, которые были сгенерированы во время работы программы script:
$ cat typescript
$ tty
/dev/pts/24
$ echo $$
29825
$ ps — p 7979 — p 29825 — C script — o "pid ppid sid tty cmd"
PID PPID SID TT CMD
7979 7972 7979 pts/1 /bin/bash
29824 7979 7979 pts/1./script
29825 29824 29825 pts/24 /bin/bash
$ exit
Первичное и вторичное устройства используют одни и те же структуры с атрибутами терминала (termios) и размером окна (winsize), которые были описаны в главе 58. Это значит, что программа, работающая в первичном устройстве псевдотерминала, может изменять указанные атрибуты для своего вторичного конца, применяя к первичному файловому дескриптору операции tcsetattr() и ioctl().
Одним из примеров того, как можно применять подобные изменения, является программа script. Представьте, что мы запускаем данную программу в окне эмулятора терминала и изменяем размер данного окна. В таком случае эмулятор оповестит ядро об изменении размера соответствующего терминального устройства, однако это не затронет отдельную запись, выделенную ядром для вторичного конца псевдотерминала (см. рис. 60.4). Следовательно, программы, которые манипулируют экраном (такие как vi) и выполняются во вторичном устройстве псевдотерминала, начнут генерировать некорректный вывод, поскольку их представление о размере окна терминала будет отличаться от его реального размера. Эту проблему можно решить следующим образом.
1. Устанавливаем в родительском процессе программы script обработчик сигнала SIGWINCH с целью получать уведомления об изменениях размера окна терминала.