I try to send multiple file descriptors over a unix socket at once. This is no problem for a single socket. Although when I try to append another one with CMSG_NXTHDR
I get back a null pointer that indicates that my buffer was too short. For one file descriptor I calculated the buffer size with CMSG_SPACE
and I assumed that I just need to multiply this. Although this doesn't seem to be sufficient.
I wrote a short test program to check this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
int main(int argc, char *argv[])
{
struct msghdr msg;
if(argc != 2){
return 1;
}
union {
struct cmsghdr cmsghdr;
char control[CMSG_SPACE(sizeof(int)) * atoi(argv[1])];
} cmsgu;
struct cmsghdr *cmsg;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = 0;
msg.msg_iovlen = 0;
msg.msg_control = cmsgu.control;
msg.msg_controllen = sizeof(cmsgu.control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*((int *)CMSG_DATA(cmsg)) = -1;
cmsg = CMSG_NXTHDR(&msg, cmsg);
fprintf(stderr, "%p\n", cmsg);
return 0;
}
When I call this with 1 it outputs a null pointer which is expected. Altough my expectation would be that if it is called with 2 CMSG_NXTHDR
would return a valid pointer. The first working value is 5 (so additional 120 bytes). I thought CMSG_SPACE
would take care of this. Is there a way to go to calculate the required space? Or is there any more straight forward method to send multiple file descriptors in a single message?
See this question and answer. Your code should work if you memset() cmsgu to 0s.
However (and to answer your second question), if you want to pass multiple file descriptors, you could use use a single cmsghdr
containing an array of ints. Modifying your example,
int n = atoi(argv[1]);
int myfds[n]; // use this later
union {
struct cmsghdr cmsghdr; // for alignment
char control[CMSG_SPACE(sizeof(int) * n)]; // space for one message
} cmsgu;
struct cmsghdr *cmsg;
memset(&cmsgu, 0, sizeof(cmsgu)); // as noted
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = 0;
msg.msg_iovlen = 0;
msg.msg_control = cmsgu.control;
msg.msg_controllen = sizeof(cmsgu.control);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
int *fdptr = (int *) CMSG_DATA(cmsg);
memcpy(fdptr, myfds, sizeof(int) * n);
This is basically the same as the example in the cmsg(3) man page.