Если поддерживается более старый элемент msg_accrights, то длина должна быть равна размеру целого числа, а вновь созданный дескриптор возвращается через указатель recvfd вызывающего процесса.

В листинге 15.10 показана программа openfile. Она получает три аргумента командной строки, которые должны быть переданы, и вызывает обычную функцию open.

Листинг 15.10. Программа openfile: открытие файла и передача дескриптора обратно

//unixdomain/openfile.c

 1 #include "unp.h"

 2 int

 3 main(int argc, char **argv)

 4 {

 5  int fd;

 6  ssize_t n;

 7  if (argc != 4)

 8   err_quit("openfile <sockfd#> <filename> <mode>");

 9  if ((fd = open(argv[2], atoi(argv[3]))) < 0)

10   exit((errno > 0) ? errno : 255);

11  if ((n = write_fd(atoi(argv[1]), "", 1, fd)) < 0)

12   exit((errno > 0) ? errno : 255);

13  exit(0);

14 }

Аргументы командной строки

6-7 Поскольку два из трех аргументов командной строки были превращены в символьные строки функцией my_open, они преобразуются обратно в целые числа при помощи функции atoi.

Открытие файла

9-10 Файл открывается с помощью функции open. Если встречается ошибка, статус завершения этого процесса содержит значение переменной errno, соответствующее ошибке функции open.

Передача дескриптора обратно

11-12 Дескриптор передается обратно функцией write_fd, которую мы покажем в следующем листинге. Затем этот процесс завершается, но ранее в этой главе мы сказали, что отправляющий процесс может закрыть переданный дескриптор (это происходит, когда мы вызываем функцию exit), поскольку ядро знает, что дескриптор находится в состоянии передачи («в полете»), и оставляет его открытым для принимающего процесса.

ПРИМЕЧАНИЕ

Статус выхода должен лежать в пределах от 0 до 255. Максимальное значение переменной errno — около 150. Альтернативный способ, при котором не требуется, чтобы значение переменной errno было меньше 256, заключается в том, чтобы передать обратно указание на ошибку в виде обычных данных при вызове функции sendmsg.

В листинге 15.11 показана последняя функция, write_fd, вызывающая функцию sendmsg для отправки дескриптора (и, возможно, еще каких-либо данных, которые мы не используем) через доменный сокет Unix.

Листинг 15.11. Функция write_fd: передача дескриптора при помощи вызова функции sendmsg

//lib/write_fd.c

 1 #include "unp.h"

 2 ssize_t

 3 write_fd(int fd, void *ptr, size_t nbytes, int sendfd)

 4 {

 5  struct msghdr msg;

 6  struct iovec iov[1];

 7 #ifdef HAVE_MSGHDR_MSG_CONTROL

 8  union {

 9   struct cmsghdr cm;

10   char control[CMSG_SPACE(sizeof(int))];

11  } control_un;

12  struct cmsghdr *cmptr;

13  msg.msg_control = control_un.control;

14  msg.msg_controllen = sizeof(control_un.control);

15  cmptr = CMSG_FIRSTHDR(&msg);

16  cmptr->cmsg_len = CMSG_LEN(sizeof(int));

17  cmptr->cmsg_level = SOL_SOCKET;

18  cmptr->cmsg_type = SCM_RIGHTS;

19  *((int*)CMSG_DATA(cmptr)) = sendfd;

20 #else

21  msg.msg_accrights = (caddr_t)&sendfd;

22  msg.msg_accrightslen = sizeof(int);

23 #endif

24  msg.msg_name = NULL;

25  msg.msg_namelen = 0;

26  iov[0].iov_base = ptr;

27  iov[0].iov_len = nbytes;

28  msg.msg_iov = iov;

29  msg.msg_iovlen = 1;

30  return (sendmsg(fd, &msg, 0));

31 }

Как и в случае функции read_fg, эта функция обрабатывает либо вспомогательные данные, либо права доступа, которые предшествовали вспомогательным данным в более ранних реализациях. В любом случае инициализируется структура msghdr и затем вызывается функция sendmsg.

В разделе 28.7 мы приводим пример передачи дескриптора, в котором участвуют неродственные (unrelated) процессы, а в разделе 30.9 — пример, где задействованы родственные процессы. В них мы будем использовать функции read_fd и write_fd, которые только что описали.

15.8. Получение информации об отправителе

На рис. 14.4 мы показали другой тип информации, передаваемой через доменный сокет Unix в виде вспомогательных данных: информацию об отправителе, которая передается с помощью структуры cmsgcred, определяемой путем включения заголовочного файла <sys/socket.h>. Упаковка и формат данных зависят от операционной системы. Такая возможность появилась только в BSD/OS 2.1. Мы описываем FreeBSD, а прочие варианты Unix во многом подобны ей (проблема обычно состоит в выборе структуры, которую следует использовать для передачи данных). Рассказ об этой возможности мы считаем необходимым, поскольку это важное, хотя и простое дополнение доменных протоколов Unix. Когда клиент и сервер связываются с помощью этих протоколов, серверу часто бывает необходим способ точно узнать, кто является клиентом, чтобы убедиться, что клиент имеет право запрашивать определенный сервис.


Перейти на страницу:
Изменить размер шрифта: