Условия соглашения

Для нормальной работы приведенного исходного кода нужно соблюдать несколько условий, нарушение которых приведет скорее всего к тому, что код не будет работать как задумывалось.

А именно:

Работает только между клиентами в группах с одинаковыми 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;
}