Reputation: 675
Some context first: there is the main program (a web server) which forks a bunch of workers, and waits for incoming connections. When it accepts one, it send the socket descriptor to one of the workers via the sendmsg() primitive.
This is the code that is supposed to send the descriptor:
int send_msg (worker_t * l, struct message_t * m)
{
#ifdef __WIN32__
#else
struct msghdr msg;
struct cmsghdr * cmsg;
char anc [CMSG_SPACE (sizeof(int))];
int * fd_ptr;
int buf [1] = {m->msg};
struct iovec vec;
vec.iov_base = buf;
vec.iov_len = sizeof (int);
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
if (m->msg == GC_SOCKET) {
msg.msg_control = anc;
msg.msg_controllen = sizeof anc;
cmsg = CMSG_FIRSTHDR (&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN (sizeof (int));
fd_ptr = (int *) CMSG_DATA (cmsg);
*fd_ptr = m->sockfd;
msg.msg_controllen = cmsg->cmsg_len;
}
msg.msg_flags = 0;
if (sendmsg (l->channel.chan, &msg, 0) == -1) {
i_log (1, "Unable to send message to listener.");
return -1;
}
else {
if (m->msg == GC_SOCKET && m->sockfd)
close (m->sockfd);
return 1;
}
#endif
}
In the code above, l->channel.chan
is the AF_UNIX
socket, and struct message_t
is:
struct message_t {
int msg;
int sockfd;
};
where msg
can get values GC_SOCKET
or GC_CLOSE
. In the former case the field sockfd contains the descriptor of the socket passed, and in the latter no ancillary data is sent (and the worker is supposed to kill himself).
Now, this stuff doesn't work. This is what strace says about it, when the main program sends GC_CLOSE to a worker:
...
[pid 6229] sendmsg(5, {msg_name(6229)={...}, msg_iov(1)=[{"\1\0\0\0", 4}],
msg_controllen=0, msg_flags=0}, 0) = -1 EINVAL (Invalid argument)
...
Any help is really appreciated.
Upvotes: 1
Views: 5730
Reputation: 249293
I think you are not initializing msg.msg_name
and msg.msg_namelen
. It would be safer if you did this:
struct msghdr msg = {}
This is equivalent to calling memset(0) or bzero(), but more concise. Anyway right now it looks like you're passing uninitialized who-knows-what, which could easily result in EINVAL.
Also, now and in the future, try running your program under valgrind--it can help catch errors like this.
Upvotes: 4