Пакетный режим не предусмотрен стандартом SUSv3, и некоторые детали его реализации варьируются в зависимости от системы. Больше подробностей о нем, в том числе и о значениях битовой маски, описывающей изменения состояния, можно найти на странице tty_ioctl(4) руководства).

60.6. Реализация программы script(1)

Теперь мы готовы к написанию простой версии стандартной программы script(1). Эта программа создает новую сессию командной строки и записывает весь ее ввод и вывод в файл. Большинство сессий командной строки, представленных в настоящей книге, было записано с ее помощью.

В обычной сессии командная оболочка соединяется непосредственно с пользовательским терминалом. После запуска программа script становится промежуточным звеном между оболочкой и терминалом пользователя, используя псевдотерминал для общения с оболочкой (см. рис. 60.4). Командная оболочка подключается ко вторичному устройству псевдотерминала, а программа script — к первичному. Процесс script выполняет для пользователя роль прокси, принимая данные, введенные в терминале, и записывая их в первичный конец псевдотерминала, а также направляя вывод из данного конца обратно в терминал.

Кроме того, script создает исходящий файл (который по умолчанию называется typescript), содержащий копию всех данных, выведенных на первичное устройство псевдотерминала. Это касается не только вывода, сгенерированного сессией командной оболочки, но и ее ввода. Ввод записывается, потому что, как и в случае с обычным терминалом, ядро экранирует вводимые символы, копируя их в исходящую очередь (см. рис. 58.1). Но если выключить эхо-контроль, как делается в программах, считывающих пароли, ввод вторичного устройства псевдотерминала не будет копироваться в исходящую очередь и, следовательно, не попадет в исходящий файл программы script.

Наша реализация программы script представлена в листинге 60.3. Она выполняет следующие действия.

1. Получает размер окна и атрибуты терминала, в котором запущена программа . Эти данные будут переданы последующему вызову ptyFork(), устанавливающему с их помощью соответствующие значения для вторичного устройства псевдотерминала.

2. Вызывает нашу функцию ptyFork() (см. листинг 60.2), чтобы создать дочерний процесс, соединенный с родителем путем псевдотерминала .

3. После вызова ptyFork() потомок запускает командную оболочку . Тип оболочки определяется с помощью переменной среды SHELL . Если эта переменная не установлена или содержит пустую строку, то потомок запускает /bin/sh.

Рис. 60.4.Программа script

4. После вызова ptyFork() родитель выполняет следующие шаги.

1) Открывает файл вывода . Если указан аргумент командной строки, то он служит в качестве имени файла. В противном случае берется стандартное имя (typescript).

2) Переключает терминал в режим без обработки (используя функцию ttySetRaw(), представленную в листинге 58.3), чтобы вводимые символы направлялись непосредственно в программу script и не изменялись драйвером терминала . Символы, выводимые программой script, тоже не подлежат изменению.

Факт нахождения терминала в режиме без обработки вовсе не означает, что командная оболочка (или любая другая группа процессов, которая является активной по отношению ко вторичному устройству псевдотерминала) будет получать управляющие символы в их исходном виде или что эти символы дойдут неизмененными до пользовательского терминала. Интерпретация специальных символов происходит во вторичном устройстве (если только оно тоже не было переключено в режим без обработки). Используя режим без обработки в терминале пользователя, мы отключаем второй уровень интерпретации входящих и исходящих символов.

3) Вызывает функцию atexit() с целью установить обработчик, который при завершении программы возвращает терминал в его исходный режим .

4) Выполняет цикл, передающий данные от терминала к первичному устройству псевдотерминала и наоборот . В начале каждой итерации делается вызов select() (см. подраздел 59.2.1), отслеживающий ввод на обоих концах соединения . Если в терминале доступен ввод, то программа считывает какую-то его часть и записывает ее в первичное устройство псевдотерминала . Аналогично при обнаружении ввода в первичном устройстве программа считывает его часть и записывает ее в терминал и в файл программы script . Цикл выполняется, пока в одном из наблюдаемых дескрипторов не будет обнаружен конец файла или не произойдет ошибка.

Листинг 60.3. Простая реализация программы script(1)

pty/script.c

#include

#include

#include

#include

#include

#include "pty_fork.h" /* Объявление ptyFork() */

#include "tty_functions.h" /* Объявление ttySetRaw() */

#include "tlpi_hdr.h"

#define BUF_SIZE 256

#define MAX_SNAME 1000

struct termios ttyOrig;

static void /* Сбрасываем режим терминала при выходе */

ttyReset(void)

{

if (tcsetattr(STDIN_FILENO, TCSANOW, &ttyOrig) == -1)

errExit("tcsetattr");

}

int

main(int argc, char *argv[])

{

char slaveName[MAX_SNAME];

char *shell;

int masterFd, scriptFd;

Перейти на страницу:

Похожие книги