Читать «UNIX: взаимодействие процессов» онлайн - страница 251

Уильям Ричард Стивенс

Подготовка аргументов и указателя на буфер возврата

16-22 Подготавливается структура door_arg_t. К размеру имени файла мы добавляем единицу, чтобы сервер мог дополнить его завершающим нулем.

Вызов процедуры сервера и проверка результатов

23-31 Мы вызываем процедуру сервера и проверяем результат. Должен возвращаться только один дескриптор и никаких данных. Вскоре мы увидим, что сервер возвращает данные (сообщение об ошибке) только в том случае, если он не может открыть файл. В этом случае функция err_quit выводит сообщение об ошибке.

Считывание дескриптора и копирование файла

32-34 Дескриптор извлекается из структуры door_desc_t, и файл копируется в стандартный поток вывода.

В листинге 15.16 приведен текст процедуры сервера. Функция main по сравнению с листингом 15.3 не изменилась.

Листинг 15.16. Процедура сервера, открывающая файл и возвращающая клиенту дескриптор

//doors/serverfd1.c

1  #include "unpipc.h"

2  void

3  servproc(void *cookie, char *dataptr, size_t datasize,

4   door_desc_t *descptr, size_t ndesc)

5  {

6   int fd;

7   char resbuf[BUFFSIZE];

8   door_desc_t desc;

9   dataptr[datasize-1] = 0; /* завершающий О */

10  if ((fd = open(dataptr, O_RDONLY)) == –1) {

11   /* ошибка, нужно сообщить клиенту */

12   snprintf(resbuf, BUFFSIZE, "%s: can't open, %s",

13   dataptr, strerror(errno));

14   Door_return(resbuf, strlen(resbuf), NULL, 0);

15  } else {

16   /* ОК, возвращаем дескриптор */

17   desc.d_data.d_desc.d_descriptor = fd;

18   desc.d_attributes = DOOR_DESCRIPTOR;

19   Door_return(NULL, 0, &desc, 1);

20  }

21 }

Открытие файла для клиента

9-14 Мы завершаем полное имя файла клиента нулем и делаем попытку открыть этот файл вызовом open. Если возникает ошибка, сообщение о ней возвращается клиенту.

Успешное открытие файла

15-20 Если файл был успешно открыт, клиенту возвращается только его дескриптор.

Запустим сервер и укажем ему имя двери /tmp/fd1, а затем запустим клиент:

solaris % clientfd1 /tmp/fd1

/etc/shadow

/etc/shadow: can't open. Permission denied

solaris % clientfd1 /tmp/fd1

/no/such/file

/no/such/file: can't open. No such file or directory

solaris % clientfd1 /tmp/fd1

/etc/ntp.conf файл из двух строк

multicastclient 224.0.1.1

driftfile /etc/ntp.drift

В первых двух случаях мы указываем имя файла, приводящее к возврату сообщения об ошибке. В третий раз сервер передает клиенту дескриптор файла из двух строк, который благополучно выводится.

ПРИМЕЧАНИЕ

Существует проблема, связанная с передачей дескриптора через дверь. Чтобы она проявилась в нашем примере, достаточно добавить вызов printf к процедуре сервера сразу после успешного вызова open. Вы увидите, что значение дескриптора каждый раз увеличивается на единицу. Проблема в том, что сервер не закрывает дескрипторы после передачи их клиенту. Сделать это, вообще говоря, нелегко. Логично было бы выполнять закрытие дескриптора после возврата из door_return, после успешной отправки дескриптора клиенту, но возврата из door_return не происходит! Если бы мы использовали sendmsg для передачи дескриптора через доменный сокет Unix или ioctl для передачи дескриптора через канал в SVR4, мы могли бы закрыть его после возврата из sendmsg или ioctl. Однако с дверьми все по-другому, поскольку возврата из функции door_return не происходит. Единственный способ обойти проблему заключается в том, что процедура сервера должна запоминать все открытые дескрипторы и закрывать их некоторое время спустя, что несколько запутывает код.

Эта проблема должна быть исправлена в Solaris 2.7 добавлением атрибута DOOR RELEASE. Отправитель устанавливает поле d_attributes равным DOOR DESCRIPTOR | DOOR_RELEASE, что говорит системе о необходимости закрывать дескриптор после передачи его клиенту.