I was really surprised that sendmsg() returned EBADF on a valid fd; turns out I was using sendmsg with SCM_RIGHTS to send a closed fd, which gives EBADF (see test program below). But this is only obliquely referenced in unix(7): SCM_RIGHTS Send or receive a set of open file descriptors from another process. The data portion contains an integer array of the file descriptors. The passed file descriptors behave as though they have been created with dup(2). EBADF is not mentioned in the unix(7) ERRORS (it's mentioned in dup(2)). Thanks, Rusty. #include <assert.h> #include <errno.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> int fdpass_send(int sockout, int fd) { /* From the cmsg(3) manpage: */ struct msghdr msg = { 0 }; struct cmsghdr *cmsg; struct iovec iov; char c = 0; union { /* Ancillary data buffer, wrapped in a union in order to ensure it is suitably aligned */ char buf[CMSG_SPACE(sizeof(fd))]; struct cmsghdr align; } u; msg.msg_control = u.buf; msg.msg_controllen = sizeof(u.buf); memset(&u, 0, sizeof(u)); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd)); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; /* Keith Packard reports that 0-length sends don't work, so we * always send 1 byte. */ iov.iov_base = &c; iov.iov_len = 1; return sendmsg(sockout, &msg, 0); } int fdpass_recv(int sockin) { /* From the cmsg(3) manpage: */ struct msghdr msg = { 0 }; struct cmsghdr *cmsg; struct iovec iov; int fd; char c; union { /* Ancillary data buffer, wrapped in a union in order to ensure it is suitably aligned */ char buf[CMSG_SPACE(sizeof(fd))]; struct cmsghdr align; } u; msg.msg_control = u.buf; msg.msg_controllen = sizeof(u.buf); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; iov.iov_base = &c; iov.iov_len = 1; if (recvmsg(sockin, &msg, 0) < 0) return -1; cmsg = CMSG_FIRSTHDR(&msg); if (!cmsg || cmsg->cmsg_len != CMSG_LEN(sizeof(fd)) || cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { errno = -EINVAL; return -1; } memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd)); return fd; } static void child(int sockfd) { int newfd = fdpass_recv(sockfd); assert(newfd < 0); exit(0); } int main(void) { int sv[2]; int pid, ret; assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); pid = fork(); if (pid == 0) { close(sv[1]); child(sv[0]); } close(sv[0]); ret = fdpass_send(sv[1], sv[0]); printf("fdpass of bad fd return %i (%s)\n", ret, strerror(errno)); return 0; } -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html