Книга: Разработка приложений в среде Linux. Второе издание
17.4.6. Передача файловых дескрипторов
17.4.6. Передача файловых дескрипторов
Сокеты домена Unix обладают уникальным свойством: через них могут передаваться файловые дескрипторы. Ни один из прочих механизмов IPC не поддерживает подобную возможность. Она позволяет процессу открыть файл и передать файловый дескриптор в другой (возможно, несвязанный) процесс. Все проверки доступа выполняются при открытии файла, поэтому получающий процесс приобретает те же самые права доступа к файлу, что и исходный процесс.
Файловые дескрипторы передаются как часть более сложного сообщения, которое отправляется с помощью системного вызова sendmsg()
и принимается через recvmsg()
.
#include <sys/socket.h>
int sendmsg(int fd, const struct msghdr * msg, unsigned int flags);
int recvmsg(int fd, struct msghdr * msg, unsigned int flags);
Параметр fd
является файловым дескриптором, через который передается сообщение; второй параметр служит указателем на структуру, описывающую сообщение. Параметр flags
обычно не используется и для большинства приложений должен быть равен нулю. В специализированных книгах по программированию для сетей обсуждаются доступные флаги [33].
Сообщение описывается показанной ниже структурой.
#include <sys/socket.h>
#include <sys/un.h>
struct msghdr {
void * msg_name; /* дополнительный адрес */
unsigned int msg_namelen; /* размер msg_name */
struct iovec * msg_iov; /* массив для чтения вразброс/сборной записи*/
unsigned int msg_iovlen; /* количество элементов в msg_iov */
void * msg_control; /* вспомогательные данные */
unsigned int msg_controllen;/* длина буфера вспомогательных данных */
int msg_flags; /* флаги на получаемом сообщении */
};
Первые два члена msg_name
и msg_namelen
не используются в потоковых протоколах. Приложения, посылающие сообщения через потоковые сокеты, должны устанавливать для msg_name
значение NULL
, для msg_namelen
— ноль.
msg_iov
и msg_iovlen
описывают набор буферов, которые отправляют или принимают. Чтение вразброс и сборная запись, а также struct iovec
, обсуждаются в конце главы 13. Последний член структуры msg_flags
в настоящее время не используется и должен равняться нулю.
Два элемента, которые мы пропустили, msg_control
и msg_controllen
предоставляют возможность передачи файлового дескриптора. Член msg_control
указывает на массив заголовков управляющих сообщений; msg_controllen
устанавливает количество байт, которые содержит массив. Каждое управляющее сообщение состоит из структуры struct cmsghdr
, которая сопровождается дополнительными данными.
#include <sys/socket.h>
struct cmsghdr {
unsigned int cmsg_len; /* длина управляющего сообщения */
int cmsg_level; /* SOL_SOCKET */
int cmsg_type; /* SCM_RIGHTS */
int cmsg_data[0]; /* здесь должен быть файловый дескриптор */
};
Размер управляющего сообщения, включая заголовок, хранится в переменной cmsg_len
. В текущий момент определен только один тип управляющих сообщений — SCM_RIGHTS
, который передает файловые дескрипторы[125]. Для данного типа сообщений параметры cmsg_level
и cmsg
_type должны быть равны соответственно SOL_SOCKET
и SCM_RIGHTS
. Последний член cmsg_data
является массивом нулевого размера. Это расширение gcc, которое позволяет приложению копировать данные в конец структуры (в следующей программе показан пример).
Получение файлового дескриптора происходит аналогично. Необходимо выделить достаточное пространство буфера для управляющего сообщения, и каждая приходящая структура struct cmsghdr
будет сопровождаться новым файловым дескриптором.
Для иллюстрации использования таких вложенных структур мы написали пример программы, которая по нашей прихоти названа просто cat
. Она принимает имя файла в качестве единственного аргумента, открывает указанный файл в дочернем процессе и передает результирующий файловый дескриптор в родительский процесс через сокет домена Unix. Родительский процесс затем копирует файл на стандартный вывод. Имя файла посылается вместе с файловым дескриптором с демонстрационной целью.
1: /* passfd.с */
2:
3: /* Программа ведет себя подобно обычной команде /bin/cat, которая обрабатывает
4: только один аргумент (имя файла). Мы создаем сокеты домена Unix при помощи
5: socketpair(), затем разветвляем через fork(). Дочерний процесс открывает файл,
6: имя которого передается в командной строке, пересылает файловый дескриптор и
7: имя файла обратно в порождающий процесс, после этого завершается. Родительский
8: процесс ожидает файловый дескриптор от дочернего процесса, а потом копирует
9: данные из файлового дескриптора в stdout до тех пор, пока данные не
10: заканчиваются. Затем родительский процесс завершается. */
11:
12: #include <alloca.h>
13: #include <fcntl.h>
14: #include <stdio.h>
15: #include <string.h>
16: #include <sys/socket.h>
17: #include <sys/uio.h>
18: #include <sys/un.h>
19: #include <sys/wait.h>
20: #include <unistd.h>
21:
22: #include "sockutil.h" /* простые служебные функции */
23:
24: /* Дочерний процесс. Он пересылает файловый дескриптор. */
25: int childProcess(char * filename, int sock) {
26: int fd;
27: struct iovec vector; /* некоторые данные для передачи fd в w/ */
28: struct msghdr msg; /* полное сообщение */
29: struct cmsghdr * cmsg; /* управляющее сообщение, которое */
30: /* включает в себя fd */
31:
32: /* Открыть файл, дескриптор которого будет передан. */
33: if ((fd = open(filename, O_RDONLY)) < 0) {
34: perror("open");
35: return 1;
36: }
37:
38: /* Передать имя файла через сокет, включая завершающий
39: символ '' */
40: vector.iov_base = filename;
41: vector.iov_len = strlen(filename) + 1;
42:
43: /* Соединить первую часть сообщения. Включить
44: имя файла iovec */
45: msg.msg_name = NULL;
46: msg.msg_namelen = 0;
47: msg.msg_iov = &vector;
48: msg.msg_iovlen = 1;
49:
50: /* Теперь управляющее сообщение. Мы должны выделить участок памяти
51: для файлового дескриптора. */
52: cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd));
53: cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd);
54: cmsg->cmsg_level = SOL_SOCKET;
55: cmsg->cmsg_type = SCM_RIGHTS;
56:
57: /* Копировать файловый дескриптор в конец
58: управляющего сообщения */
59: memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
60:
61: msg.msg_control = cmsg;
62: msg.msg_controllen = cmsg->cmsg_len;
63:
64: if (sendmsg(sock, &msg, 0) != vector.iov_len)
65: die("sendmsg");
66:
67: return 0;
68: }
69:
70: /* Родительский процесс. Он получает файловый дескриптор. */
71: int parentProcess(int sock) {
72: char buf[80]; /* пространство для передачи имени файла */
73: struct iovec vector; /* имя файла от дочернего процесса */
74: struct msghdr msg; /* полное сообщение */
75: struct cmsghdr * cmsg; /* управляющее сообщение с fd */
76: int fd;
77:
78: /* установка iovec для имени файла */
79: vector.iov_base = buf;
80: vector.iov_len = 80;
81:
82: /* сообщение, которое мы хотим получить */
83:
84: msg.msg_name = NULL;
85: msg.msg_namelen = 0;
86: msg.msg_iov = &vector;
87: msg.msg_iovlen = 1;
88:
89: /* динамическое распределение (чтобы мы могли выделить участок
90: памяти для файлового дескриптора) */
91: cmsg = alloca(sizeof(struct cmsghdr) + sizeof(fd));
92: cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(fd);
93: msg.msg_control = cmsg;
94: msg.msg_controllen = cmsg->cmsg_len;
95:
96: if (!recvmsg(sock, &msg, 0))
97: return 1;
98:
99: printf("получен файловый дескриптор для '%s'n",
100: (char *) vector.iov_base);
101:
102: /* присвоение файлового дескриптора из управляющей структуры */
103: memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
104:
105: copyData(fd, 1);
106:
107: return 0;
108: }
109:
110: int main(int argc, char ** argv) {
111: int socks[2];
112: int status;
113:
114: if (argc != 2) {
115: fprintf(stderr, "поддерживается только один аргументn");
116: return 1;
117: }
118:
119: /* Создание сокетов. Один служит для родительского процесса,
120: второй — для дочернего (мы можем поменять их местами,
121: если нужно). */
122: if (socketpair(PF_UNIX, SOCK_STREAM, 0, socks))
123: die("socketpair");
124:
125: if (!fork()) {
126: /* дочерний процесс */
127: close(socks[0]);
128: return childProcess(argv[1], socks[1]);
129: }
130:
131: /* родительский процесс */
132: close(socks[1]);
133: parentProcess(socks[0]);
134:
135: /* закрытие дочернего процесса */
136: wait(&status);
137:
138: if (WEXITSTATUS(status))
139: fprintf(stderr, "childfailedn");
140:
141: return 0;
142: }
- Глава 10 Передача файлов
- 15.7. Передача дескрипторов
- Резервное копирование многофайловых баз данных
- Восстановление из резервных копий многофайловых баз данных
- Передача прав
- 5.14 МОНТИРОВАНИЕ И ДЕМОНТИРОВАНИЕ ФАЙЛОВЫХ СИСТЕМ
- 6.4.2. Передача номенклатурных позиций между ячейками склада
- Глава 11 Передача во временное пользование и заказы
- Глава десятая. Из истории файловых систем
- 5.3.8. Защищенная передача данных
- Максимальное число дескрипторов для функции select
- 10.1.3. Передача файлов