Ubuntu Linux descripteur de fichier d’envoi avec Unix Domain Socket

J’essaie d’envoyer un descripteur de fichier entre un socketpair avec le code collé ci-dessous. Ce code provient de: http://www.thomasstover.com/uds.html . Je suis sous Ubuntu 16.04 64 bits.

Le problème est que le descripteur de fichier reçu pour mon exécution du programme est “3” et non “4”. Je ne peux pas non plus lire de données dans le processus de réception. Pourquoi ça ne marche pas?

La sortie de la console ressemble à ceci:

Parent at work FILE TO SEND HAS DESCRIPTOR: 4 Parent read: [[hello phil ]] Child at play Read 3! Done: 0 Success! Parent exits 

Code:

 #include  #include  #include  #include  #include  #include  #include  #include  #include  int send_fd(int socket, int fd_to_send) { struct msghdr socket_message; struct iovec io_vector[1]; struct cmsghdr *control_message = NULL; char message_buffer[1]; /* storage space needed for an ancillary element with a paylod of length is CMSG_SPACE(sizeof(length)) */ char ancillary_element_buffer[CMSG_SPACE(sizeof(int))]; int available_ancillary_element_buffer_space; /* at least one vector of one byte must be sent */ message_buffer[0] = 'F'; io_vector[0].iov_base = message_buffer; io_vector[0].iov_len = 1; /* initialize socket message */ memset(&socket_message, 0, sizeof(struct msghdr)); socket_message.msg_iov = io_vector; socket_message.msg_iovlen = 1; /* provide space for the ancillary data */ available_ancillary_element_buffer_space = CMSG_SPACE(sizeof(int)); memset(ancillary_element_buffer, 0, available_ancillary_element_buffer_space); socket_message.msg_control = ancillary_element_buffer; socket_message.msg_controllen = available_ancillary_element_buffer_space; /* initialize a single ancillary data element for fd passing */ control_message = CMSG_FIRSTHDR(&socket_message); control_message->cmsg_level = SOL_SOCKET; control_message->cmsg_type = SCM_RIGHTS; control_message->cmsg_len = CMSG_LEN(sizeof(int)); *((int *) CMSG_DATA(control_message)) = fd_to_send; return sendmsg(socket, &socket_message, 0); } int recv_fd(int socket) { int sent_fd, available_ancillary_element_buffer_space; struct msghdr socket_message; struct iovec io_vector[1]; struct cmsghdr *control_message = NULL; char message_buffer[1]; char ancillary_element_buffer[CMSG_SPACE(sizeof(int))]; /* start clean */ memset(&socket_message, 0, sizeof(struct msghdr)); memset(ancillary_element_buffer, 0, CMSG_SPACE(sizeof(int))); /* setup a place to fill in message contents */ io_vector[0].iov_base = message_buffer; io_vector[0].iov_len = 1; socket_message.msg_iov = io_vector; socket_message.msg_iovlen = 1; /* provide space for the ancillary data */ socket_message.msg_control = ancillary_element_buffer; socket_message.msg_controllen = CMSG_SPACE(sizeof(int)); if(recvmsg(socket, &socket_message, MSG_CMSG_CLOEXEC) cmsg_level == SOL_SOCKET) && (control_message->cmsg_type == SCM_RIGHTS) ) { sent_fd = *((int *) CMSG_DATA(control_message)); return sent_fd; } } return -1; } int main(int argc, char **argv) { const char *filename = "/tmp/z7.c"; if (argc > 1) filename = argv[1]; int sv[2]; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) != 0) fprintf(stderr,"Failed to create Unix-domain socket pair\n"); int pid = fork(); if (pid > 0) // in parent { fprintf(stderr,"Parent at work\n"); close(sv[1]); int sock = sv[0]; int fd = open(filename, O_RDONLY); if (fd  0) fprintf(stderr,"Parent read: [[%.*s]]\n", nbytes, buffer); send_fd(sock, fd); close(fd); sleep(4); fprintf(stderr,"Parent exits\n"); } else // in child { fprintf(stderr,"Child at play\n"); close(sv[0]); int sock = sv[1]; sleep(2); int fd = recv_fd(sock); printf("Read %d!\n", fd); char buffer[256]; ssize_t nbytes; while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0) { fprintf(stderr,"WRITING: %d\n",nbytes); write(1, buffer, nbytes); } printf("Done: %d %s!\n",nbytes,strerror(errno)); close(fd); } return 0; } 

Le décalage de fichier est partagé par les deux processus. Ainsi, lorsque le processus parent lit jusqu’à EOF, le processus enfant n’a plus rien à lire.

C’est la même chose que lorsque deux processus héritent d’un descripteur de fichier d’un parent, par exemple la commande shell:

 { echo first cat; cat ; echo second cat ; cat ; } < filename 

La première commande cat lira tout le fichier et le second n'aura rien à lire.

Citation de Richard Stevens (Programmation de réseaux UNIX):

“Il est normal que le numéro du descripteur dans le processus de réception diffère de celui du processus d’envoi. Le passage d’un descripteur ne transmet pas le numéro du descripteur, mais un nouveau descripteur est créé dans le processus de réception qui pointe vers la même entrée de fichier. dans le kernel en tant que descripteur envoyé par le processus de transmission. ”