You cannot transmit a socket (or any other file descriptor) from one process to another through shared memory. A file descriptor is just a small integer. Placing this integer in shared memory and accessing it from another process does not automatically make the same integer into a valid file descriptor from the point of view of the other process.
The correct way to send a file descriptor from one process to another is to send it as SCM_RIGHTS
ancillary data with sendmsg()
through an existing socket communication channel between the two processes.
First, create your communication channel with socketpair()
before you fork()
. Now, in the parent, close one end of the socket pair, and, in the child, close the other end. You can now sendmsg()
from the parent on one end of this socket and receive with recvmsg()
in the child using the other end.
Sending a message with SCM_RIGHTS
looks something like this:
struct msghdr m;
struct cmsghdr *cm;
struct iovec iov;
char buf[CMSG_SPACE(sizeof(int))];
char dummy[2];
memset(&m, 0, sizeof(m));
m.msg_controllen = CMSG_SPACE(sizeof(int));
m.msg_control = &buf;
memset(m.msg_control, 0, m.msg_controllen);
cm = CMSG_FIRSTHDR(&m);
cm->cmsg_level = SOL_SOCKET;
cm->cmsg_type = SCM_RIGHTS;
cm->cmsg_len = CMSG_LEN(sizeof(int));
*((int *)CMSG_DATA(cm)) = your_file_descriptor_to_send;
m.msg_iov = &iov;
m.msg_iovlen = 1;
iov.iov_base = dummy;
iov.iov_len = 1;
dummy[0] = 0; /* doesn't matter what data we send */
sendmsg(fd, &m, 0);
Receiving a message with SCM_RIGHTS
in it goes something like this:
struct msghdr m;
struct cmsghdr *cm;
struct iovec iov;
struct dummy[100];
char buf[CMSG_SPACE(sizeof(int))];
ssize_t readlen;
int *fdlist;
iov.iov_base = dummy;
iov.iov_len = sizeof(dummy);
memset(&m, 0, sizeof(m));
m.msg_iov = &iov;
m.msg_iovlen = 1;
m.msg_controllen = CMSG_SPACE(sizeof(int));
m.msg_control = buf;
readlen = recvmsg(fd, &m, 0);
/* Do your error handling here in case recvmsg fails */
received_file_descriptor = -1; /* Default: none was received */
for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) {
if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS) {
nfds = (cm->cmsg_len - CMSG_LEN(0)) / sizeof(int);
fdlist = (int *)CMSG_DATA(cm);
received_file_descriptor = *fdlist;
break;
}
}
accept
. On Linux a non-standard approach would be to useclone
withCLONE_FILES
instead offork
so that processes share file descriptors. – Timothy Baldwin