Reputation: 6615
I am using unix domain sockets to send open file descriptor between different processes. Unix domain sockets work fine but when i used sendmsg
to send file descriptor something wierd happened. The function returns just after sendmsg
and sendmsg not returning anything. And then recvmsg is giving Invalid Argument
error. Here is my code.
Client (sending the file descriptor):
#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int send_err(int fd, int errcode, const char *msg);
int send_fd(int fd, int fd_to_send);
int main(int argc, char const *argv[])
{
int fd_to_send;
if((fd_to_send = open("vi",O_RDONLY)) < 0)
printf("vi open failed");
struct sockaddr_un address;
int socket_fd, nbytes;
char buffer[256];
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("socket() failed\n");
return 1;
}
/* start with a clean address structure */
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");
if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
{
printf("connect() failed\n");
return 1;
}
nbytes = snprintf(buffer, 256, "hello from a client");
write(socket_fd, buffer, nbytes);
nbytes = read(socket_fd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM SERVER: %s\n", buffer);
//sending the file descriptor
printf("From send_fd %d \n",send_fd(socket_fd,fd_to_send));
close(socket_fd);
exit(0);
}
int send_err(int fd, int errcode, const char *msg)
{
int n;
if ((n = strlen(msg)) > 0)
if (write(fd, msg, n) != n) /* send the error message */
return(-1);
if (errcode >= 0)
errcode = -1; /* must be negative */
if (send_fd(fd, errcode) < 0)
return(-1);
return(0);
}
int send_fd(int fd, int fd_to_send)
{
int temp;
struct iovec iov[1];
struct msghdr msg;
char buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
iov[0].iov_base = buf;
iov[0].iov_len = 2;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (fd_to_send < 0) {
msg.msg_control = NULL;
msg.msg_controllen = 0;
buf[1] = -fd_to_send; /* nonzero status means error */
if (buf[1] == 0)
buf[1] = 1; /* -256, etc. would screw up protocol */
} else {
if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
cmptr->cmsg_len = CONTROLLEN;
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
*(int *)CMSG_DATA(cmptr) = fd_to_send; /* the fd to pass */
buf[1] = 0; /* zero status means OK */
}
buf[0] = 0; /* null byte flag to recv_fd() */
printf("before sendmsg \n");
if (temp = sendmsg(fd, &msg, 0) != 2)
{
printf("inside sendmsg condition %d\n",temp);
return(-1);
}
printf("after sendmsg %d\n",temp);
return(0);
}
Server (recv the file descriptor):
#include <stropts.h>
#include "accesories.c"
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#define MAXLINE 10
/* size of control buffer to send/recv one file descriptor */
#define CONTROLLEN CMSG_LEN(sizeof(int))
static struct cmsghdr *cmptr = NULL; /* malloc'ed first time */
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t));
ssize_t errcheckfunc(int a,const void *b, size_t c);
int connection_handler(int connection_fd);
int main(int argc, char const *argv[])
{
struct sockaddr_un address;
int socket_fd, connection_fd;
socklen_t address_length;
pid_t child;
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("socket() failed\n");
return 1;
}
unlink("./demo_socket");
/* start with a clean address structure */
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");
if(bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
{
printf("bind() failed\n");
return 1;
}
if(listen(socket_fd, 5) != 0)
{
printf("listen() failed\n");
return 1;
}
while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -1)
{
connection_handler(connection_fd);
int fd_to_recv;
fd_to_recv = recv_fd(socket_fd,&errcheckfunc);
if(read(fd_to_recv,msgbuf,5) < 0)
printf("message read failed");
printf("message received:%s\n",msgbuf);
close(connection_fd);
}
close(socket_fd);
unlink("./demo_socket");
return 0;
}
int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t))
{
int newfd, nr, status;
char *ptr;
char buf[MAXLINE];
struct iovec iov[1];
struct msghdr msg;
status = -1;
for ( ; ; )
{
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
return(-1);
msg.msg_control = cmptr;
msg.msg_controllen = CONTROLLEN;
if ((nr = recvmsg(fd, &msg, 0)) < 0)
{
printf("recvmsg errrrror %d %d %s\n",nr,errno,strerror(errno));
//perror("recvmsg errrrror");
} else if (nr == 0)
{
perror("connection closed by server");
return(-1);
}
/*
* See if this is the final data with null & status. Null
* is next to last byte of buffer; status byte is last byte.
* Zero status means there is a file descriptor to receive.
*/
for (ptr = buf; ptr < &buf[nr]; )
{
if (*ptr++ == 0)
{
if (ptr != &buf[nr-1])
perror("message format error");
status = *ptr & 0xFF; /* prevent sign extension */
if (status == 0)
{
if (msg.msg_controllen != CONTROLLEN)
perror("status = 0 but no fd");
newfd = *(int *)CMSG_DATA(cmptr);
} else
{
newfd = -status;
}
nr -= 2;
}
}
if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
return(-1);
if (status >= 0) /* final data has arrived */
return(newfd); /* descriptor, or -status */
}
}
ssize_t errcheckfunc(int a,const void *b, size_t c)
{
return 0;
}
int connection_handler(int connection_fd)
{
int nbytes;
char buffer[256];
nbytes = read(connection_fd, buffer, 256);
buffer[nbytes] = 0;
printf("MESSAGE FROM CLIENT: %s\n", buffer);
nbytes = snprintf(buffer, 256, "hello from the server");
write(connection_fd, buffer, nbytes);
return 0;
}
output of client:
MESSAGE FROM SERVER: hello from the server
before sendmsg
Not even inside sendmsg condition
and after sendmsg
prints?
Upvotes: 1
Views: 3231
Reputation: 19375
Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
Your call to recvmsg() is trying to receive data from a listening socket rather than a connected socket, because your main loop is passing socket_fd to recv_fd() rather than connection_fd. – Matthew Slattery
Upvotes: 3