Для нормальной работы приведенного исходного кода нужно соблюдать несколько условий, нарушение которых приведет скорее всего к тому, что код не будет работать как задумывалось.
А именно:
Работает только между клиентами в группах с одинаковыми uid или от root.
Работает только для нормальных файловых дескрипторов, а не для тех, у которых выставлен флаг _NTO_SIDE_CHANNEL.
Сервер должен поддерживать сообщение _IO_DUP (поддерживается многими серверами по умолчанию)
Хотелось бы завершить данную статью упражнением. Модифицируйте исходный код таким образом, чтобы вместо дублирования файлового дескриптора использовать сообщение _IO_OPENFD для получения файлового дескриптора, открытого другим процессом.
Полный пример
————
#include
#include
#include
#include
#include
#include
#include
#include
#define MY_MQ_NAME «/fdpass»
/*
Создать пакет данных (сообщение), которое будет послано с
использованием func() для дублирования файлового дескриптора fdtosend
*/
int send_fd(int fdtosend, int flags, void *arg, int (*func)(void *arg, void *data, int len)) {
int ret;
io_dup_t dupmsg;
memset(&dupmsg;, 0, sizeof(dupmsg));
dupmsg.i.type = _IO_DUP;
dupmsg.i.combine_len = sizeof(dupmsg.i);
if((ret = ConnectServerInfo(0, fdtosend, &dupmsg.i.info;)) == -1) {
return ret;
}
/* Используем thread_id для передачи идентификатора нашего процесса */
dupmsg.i.info.tid = getpid();
printf(«SFD: client pid: %d\n»
» server nd: 0x%x pid 0x%x chid 0x%x scoid 0x%x coid 0x%x\n»,
dupmsg.i.info.tid, dupmsg.i.info.nd, dupmsg.i.info.pid,
dupmsg.i.info.chid, dupmsg.i.info.scoid, dupmsg.i.info.coid);
/* Осуществим отправку данных с помощью переданной нам функции */
if(func) {
ret = func(arg, &dupmsg;, sizeof(dupmsg));
} else {
ret = 0;
}
return ret;
}
/*
Функция получает данные data длиной len, проверяет их на правильность
и пытается получить новый файловый дескриптор.
*/
int receive_fd(void *data, int len) {
io_dup_t *pdupmsg = (io_dup_t *)data;
pid_t otherpid;
int ret, newfd;
/* Простейшая проверка на допустимый тип сообщения */
if(len < sizeof(*pdupmsg) || pdupmsg->i.type != _IO_DUP) {
errno = EINVAL;
return -1;
}
/* Извлечь значение, используемое нами для передачи pid Клиента 1 */
otherpid = pdupmsg->i.info.tid;
pdupmsg->i.info.tid = 0;
printf(«RFD: other pid: %d\n»
» server nd: 0x%x pid 0x%x chid 0x%x scoid 0x%x coid 0x%x\n»,
otherpid,
pdupmsg->i.info.nd, pdupmsg->i.info.pid,
pdupmsg->i.info.chid, pdupmsg->i.info.scoid,
pdupmsg->i.info.coid);
/* Пытаемся установить связь с каналом */
if ((newfd = ConnectAttach(pdupmsg->i.info.nd,
pdupmsg->i.info.pid, pdupmsg->i.info.chid, 0, 0)) < 0) {
return -1;
}
/* Делаем вид, что мы Клиент 1 */
pdupmsg->i.info.pid = otherpid;
/*
Послать сообщение серверу с просьбой продублировать соединение,
которое установил Клиент 1. В случае успешного выполнения данного
запроса мы будем привязаны к ресурсу сервера. Если запрос завершится
ошибкой, то тогда просто отсоединяемся от канала.
*/
if(MsgSendnc(newfd, &pdupmsg-;>i, sizeof(pdupmsg->i), 0, 0) == -1) {
ConnectDetach_r(newfd);
return -1;
}
return newfd;
}
int mq_write(void *arg, void *data, int len) {
return mq_send((int)arg, data, len, 0);
}
int main(int argc, char **argv) {
mqd_t mqfd;
int fd, ret, sender;
char c;
sender = -1;
while((ret = getopt(argc, argv, «sr»)) != -1) {
switch(ret) {
case ‘s’:
sender = 1;
break;
case ‘r’:
sender = 0;
break;
}
}
if(sender < 0) {
printf(«Usage %s -s | -r \n», argv[0]);
printf(«Where:\n»
» -s Indicates program should send the fd\n»
» -r Indicates program should receive the fd\n»);
return 1;
}
if((mqfd = mq_open(MY_MQ_NAME, O_CREAT | O_RDWR, 0666, NULL)) == -1) {
perror(«Can’t open/create mqueue»);
return 1;
}
if(sender) {
/* Если мы – Клиент 1, то открываем fd, который пошлем Клиенту 2 */
if((fd = open(«/etc/passwd», O_RDONLY)) == -1) {
perror(«Can’t open file to send»);
return 1;
}
/* Для затравки прочитаем первую строку и заодно сменим текущую позицию файла */
printf(«SFD: first line [«);
while(read(fd, &c;, 1) > 0) {
if(c == ‘\n’) {
break;
}
printf(«%c», c);
}
printf(«]\n»);
/* Отошлем файловый дескриптор Клиенту 2 */
ret = send_fd(fd, 0, (void *)mqfd, mq_write);
//Sleep for a bit to ensure the receiver is run
/* Подождем немного что бы дать Клиенту 2 отработать */
sleep(10);
} else {
char *msg;
struct mq_attr mqstat;
if(mq_getattr(mqfd, &mqstat;) == -1) {
perror(«Can’t get mqueue attribute»);
return 1;
}
msg = alloca(mqstat.mq_msgsize);
/* Если мы – Клиент 2, то мы получаем fd */
if(!msg || mq_receive(mqfd, msg, mqstat.mq_msgsize, NULL) == -1) {
perror(«Can’t receive message»);
return 1;
}
/* Передать полученные данные для дублирования fd */
if((fd = receive_fd(msg, mqstat.mq_msgsize)) == -1) {
perror(«Can’t create file descriptor»);
return 1;
}
/* Прочитать следующую строку, чтобы убедиться,
что мы правильно продублировали fd*/
printf(«RFD: next line [«);
while(read(fd, &c;, 1) > 0) {
if(c == ‘\n’) {
break;
}
printf(«%c», c);
}
printf(«]\n»);
}
/* Уберемся за собой перед завершением… */
printf(«%s: cleaning up and exiting \n», (sender) ? «SFD» : «RFD»);
close(fd);
mq_close(mqfd);
return 0;
}