Псевдотерминалы
Еще одно применение модуля дисциплины линии связи заключается в поддержке работы так называемого псевдотерминала (pseudo terminal), применяемого для организации дистанционного доступа через сеть. Псевдотерминал предназначен для обеспечения соединения терминала одного компьютера с командным интерпретатором другого компьютера. Пример такого соединения приведен на рис. 9.4. На нем пользователь подключен к компьютеру А (клиенту), но использует командный интерпретатор на компьютере В (сервере). Стрелки на схеме показывают направление пересылки вводимых с клавиатуры символов. Здесь схема несколько упрощена за счет исключения стеков сетевых протоколов на клиентской и серверной системе.
Когда пользователь подключается к командному интерпретатору на другом компьютере (обычно при помощи команды rlogin), то локальное терминальное соединение должно быть изменено. По мере того как данные считываются с локального терминала, они должны без изменений передаваться через модуль дисциплины линии связи клиентскому процессу rlogin, выполняющемуся на локальном компьютере. Поэтому дисциплина линии связи на локальном компьютере должна работать в режиме прямого доступа. Следующий фрагмент должен напомнить, как включается такой режим:
uses stdio, linux;
var
attr:termios;
.
.
.
(* Получает текущую дисциплину линии связи *)
tcgetattr(0, attr);
(* Возврат из вызова fdread разрешен только после считывания одного символа *)
attr.c_cc[VMIN] := 1;
attr.c_cc(VTIME] := 0;
attr.c_lflag := attr.c_lflag and not (ISIG or ECHO or ICANON);
(* Устанавливает новую дисциплину линии связи *)
tcsetattr(0, TCSAFLUSH, attr);
Рис. 9.4. Удаленный вход в систему в ОС UNIX
Теперь процесс клиента rlogin может передавать данные по сети в исходном виде.
Когда сервер (В) получает первоначальный запрос на вход в систему, он выполняет вызовы fork и ехес, порождая новый командный интерпретатор. С новым командным интерпретатором не связан управляющий терминал, поэтому создается псевдотерминал, который имитирует обычный драйвер устройства терминала. Псевдотерминал (pseudo tty) действует подобно двунаправленному каналу и просто позволяет двум процессам передавать данные. В рассматриваемом примере псевдотерминал связывает командный интерпретатор с соответствующим сетевым процессом. Псевдотерминал является парой устройств, которые называются ведущим и ведомым устройствами, или портами псевдотерминала. Сетевой процесс открывает ведущий порт устройства псевдотерминала, а затем выполняет запись и чтение. Процесс командного интерпретатора открывает ведомый порт, а затем работает с ним (через дисциплину линии связи). Данные, записываемые в ведущий порт, попадают на вход ведомого порта, и наоборот. В результате пользователь на клиентском компьютере (А) как бы напрямую обращается к командному интерпретатору, который на самом деле выполняется на сервере (В). Аналогично, по мере того как данные выводятся командным интерпретатором на сервере, они обрабатываются дисциплиной линии связи на сервере (которая работает в каноническом режиме) и затем передаются без изменений клиентскому терминалу, не подвергаясь модификации со стороны дисциплины линии связи клиента.
Хотя средства, при помощи которых выполняется инициализация псевдотерминалов, были улучшены в новых версиях ОС
UNIX и спецификации XSI, они все еще остаются довольно громоздкими. Система UNIX обеспечивает конечное число псевдотерминалов, и процесс командного интерпретатора должен открыть следующий доступный псевдотерминал. В системе SVR4 это выполняется и помощи открытия устройства /dev/ptmx, которое определяет и открывает первое неиспользуемое ведущее устройство псевдотерминала. С каждым ведущим устройством связано ведомое устройство. Для того, чтобы предотвратить открытие ведомого устройства другим процессом, открытие устройства /dev/ptmx также блокирует соответствующее ведомое устройство.
uses linux;
var
mfd:longint;
.
.
.
(* Открыть псевдотерминал -
* получить дескриптор файла главного устройства *)
masterfd := fdopen ('/dev/ptmx', Open_RDWR);
if masterfd = -1 then
begin
perror('Ошибка при открытии главного устройства');
halt(1);
end;
Перед тем как открыть и «разблокировать» ведомое устройство, необходимо убедиться, что только один процесс с соответствующими правами доступа сможет выполнять чтение из устройства и запись в него. Функция grantpt изменяет режим доступа и идентификатор владельца ведомого устройства в соответствии с параметрами связанного с ним главного устройства. Функция unlockpt снимает флаг, блокирующий ведомое устройство (то есть делает его доступным). Далее нужно открыть ведомое устройство. Но его имя пока еще не известно. Функция ptsname возвращает имя ведомого устройства, связанного с заданным ведущим устройством, которое обычно имеет вид /dev/pts/pttyXX. Следующий фрагмент демонстрирует последовательность необходимых действий:
uses stdio, linux;
var
mfd, sfd:longint;
slavenm:pchar;
.
.
.
(* Открываем ведущее устройство, как и раньше *)
mfd := fdopen ('/dev/ptmx', Open_RDWR);
if mfd = -1 then
begin
perror('Ошибка при открытии ведущего устройства');
halt(1);
end;
(* Изменяем права доступа ведомого устройства *)
if grantpt (mfd) = -1 then
begin
perror('Невозможно разрешить доступ к ведомому устройству');
halt(1);
end;
(* Разблокируем ведомое устройство, связанное с mfd *)
if unlockpt(mfd) = -1 then
begin
perror('Невозможно разблокировать ведомое устройство');
halt(1);
end;
(* Получаем имя ведомого устройства и затем пытаемся открыть его *)
slavenm := ptsname (mfd);
if slavenm = nil then
begin
perror('Невозможно получить имя ведомого устройства');
halt(1);
end;
sfd := fdopen (slavenm, Open_RDWR);
if slavefd = -1 then
begin
perror('Ошибка при открытии ведомого устройства');
halt(1);
end;
Теперь, когда получен доступ к драйверу устройства псевдотерминала, нужно установить для него дисциплину линии связи. До сих пор дисциплина линии связи рассматривалась как единое целое, тогда как в действительности она состоит из набора внутренних модулей ядра, известных как модули STREAM.
Стандартная дисциплина линии связи псевдотерминала состоит из трех модулей: ldterm (модуль дисциплины линии связи терминала), ptem (модуль эмуляции псевдотерминала) и ведомой части псевдотерминала. Вместе они работают как настоящий терминал. Эта конфигурация показана на рис. 9.5.
ldterm |
|||
pterm |
Дисциплина линии связи |
||
Ведомый порт псевдотерминала |
|||
Для создания дисциплины линии связи нужно «вставить» дополнительные модули STREAM в ведомое устройство. Это достигается при помощи многоцелевой функции ioctl, например:
/*
* Заголовочный файл stdio содержит интерфейс STREAMS
* и определяет макрокоманду I_PUSH, используемую в качестве
* второго аргумента функции ioctl().
*/
uses stdio;
.
.
.
(* Открываем ведущее и ведомое устройства, как и раньше *)
(* Вставляем два модуля в ведомое устройство *)
ioctl(sfd, I_PUSH, 'ptem');
ioctl(sfd, I_PUSH, 'ldterm');
Обратимся теперь к главному примеру, программе tscript, которая использует псевдотерминал в пределах одного компьютера для перехвата вывода командного интерпретатора в процессе интерактивного сеанса, не влияя на ход этого сеанса. (Эта программа аналогична команде
UNIX script.) Данный пример можно расширить и для дистанционного входа через сеть.