Ubuntu Linux envía el descriptor de archivo con Unix Domain Socket

Estoy tratando de enviar un descriptor de archivo entre un par de sockets con el código pegado a continuación. Este código es de: http://www.thomasstover.com/uds.html . Estoy corriendo en Ubuntu 16.04 de 64 bits.

El problema es que el descriptor de archivo recibido para mi ejecución del progtwig es “3” y no “4”. Tampoco puedo leer ningún dato del mismo en el proceso de recepción. ¿Por qué no funciona?

La salida de la consola se ve así:

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

Código:

 #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; } 

El desplazamiento del archivo es compartido por ambos procesos. Entonces, cuando el proceso padre lee hasta EOF, no queda nada para que el proceso hijo lea.

Esto es lo mismo que cuando dos procesos heredan un descriptor de archivo de un padre, por ejemplo, el comando de shell:

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

El primer comando cat leerá todo el archivo y el segundo cat no tendrá nada para leer.

Citando a Richard Stevens (Progtwigndo redes UNIX):

“Es normal que el número del descriptor en el proceso de recepción difiera del número del descriptor en el proceso de envío. Pasar un descriptor no pasa el número del descriptor, en su lugar se crea un nuevo descriptor en el proceso de recepción que apunta a la misma entrada de archivo en el núcleo como el descriptor que fue enviado por el proceso de transmisión “.