Send file descriptor from Go to C via Unix socket

1,167 views
Skip to first unread message

Yaohua Li

unread,
Jan 10, 2013, 10:55:08 AM1/10/13
to golan...@googlegroups.com
I have a Go application that needs to send a file descriptor to a C application via Unix socket.

There are facilities that helps to send file descriptor from Go to Go, or from C to C. But it won't work from Go to C.

I guess it could be that the underlying data format is not same, but I haven't tried to examine yet.

Anyone has idea of solution for this?

Thanks!

minux

unread,
Jan 10, 2013, 11:09:35 AM1/10/13
to Yaohua Li, golan...@googlegroups.com
Please post a minimum reproducible test case (both Go and C source involved) as we can't
give any suggestions for this problem description alone.

Yaohua Li

unread,
Jan 10, 2013, 11:49:37 AM1/10/13
to golan...@googlegroups.com, Yaohua Li
Thank you for the remind.

For the Go side, I am using this package.
(Not sure if it is compatible with the latest Go. I am using a Go version before 1.0 for some reason I won't update. I changed this package a little bit to run with my old Go)
https://github.com/burke/zeus/blob/master/go/unixsocket/unixsocket_test.go

Below is send FD part.
func main () {
        file, err := os.Open ("/home/yl/test")
        if err != nil {
                panic (err)
        }

        fd := file.Fd()

        conn, err := net.Dial ("unix", "mysock")
        usock := unixsocket.NewUsock (conn.(*net.UnixConn))

        usock.WriteFD(fd)

        conn.Close()
        file.Close()
}

Below is the C part to receive FD. the recv_fd function is borrowed from
http://www.thomasstover.com/uds.html

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

int recv_fd(int socket)
{
        int sent_fd, available_ancillary_element_buffer_space;
        struct msghdr socket_message;
        struct iovec io_vector[1];
        struct cmsghdr *control_message = NULL;
        char message_buffer[1];
        char ancillary_element_buffer[CMSG_SPACE(sizeof(int))];

        /* start clean */
        memset(&socket_message, 0, sizeof(struct msghdr));
        memset(ancillary_element_buffer, 0, CMSG_SPACE(sizeof(int)));

        /* setup a place to fill in message contents */
        io_vector[0].iov_base = message_buffer;
        io_vector[0].iov_len = 1;
        socket_message.msg_iov = io_vector;
        socket_message.msg_iovlen = 1;

        /* provide space for the ancillary data */
        socket_message.msg_control = ancillary_element_buffer;
        socket_message.msg_controllen = CMSG_SPACE(sizeof(int));

        if(recvmsg(socket, &socket_message, MSG_CMSG_CLOEXEC) < 0)
                return -1;

        int i;
        for (i=0; i<sizeof(socket_message); i++)
        {
                printf ("%u ", (char *)&socket_message + i);
        }
        printf("\n");

        if(message_buffer[0] != 'F')
        {
                /* this did not originate from the above function */
                return -1;
        }

        if((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
        {
                /* we did not provide enough space for the ancillary element array */
                return -1;
        }

        /* iterate ancillary elements */
        for(control_message = CMSG_FIRSTHDR(&socket_message);
                   control_message != NULL;
                   control_message = CMSG_NXTHDR(&socket_message, control_message))
        {
                if( (control_message->cmsg_level == SOL_SOCKET) &&
                   (control_message->cmsg_type == SCM_RIGHTS) )
                {
                        sent_fd = *((int *) CMSG_DATA(control_message));
                        return sent_fd;
                }
        }

        return -1;
}

int main()
{
        int rv, lsock, sock;
        struct sockaddr_un local;
        struct sockaddr remote;

        if ((lsock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
        {
                perror ("socket failed");
                exit (lsock);
        }

        local.sun_family = AF_UNIX;
        strcpy (local.sun_path, "mysock");
        unlink (local.sun_path);

        int len = strlen (local.sun_path) + sizeof(local.sun_family);
        if ((rv = bind(lsock, (struct sockaddr *)&local, len)) < 0)
        {
                perror ("bind error");
                exit (rv);
        }

        if ((rv = listen(lsock, 50)) < 0)
        {
                perror ("listen error");
                exit (rv);
        }

        unsigned int length = sizeof (struct sockaddr_un);
        if ((sock = accept(lsock, &remote, &length)) < 0)
        {
                perror ("accept error");
                exit (sock);
        }

        int fd = recv_fd (sock);
        FILE *fp = fdopen (fd, "r");

        char buf[100];
        buf[99] = '\0';
        fscanf (fp, "%s", buf);
        printf("Read from file: %s\n", buf);
        fclose (fp);

        close (sock);
        close (lsock);
        return 0;

Kyle Lemons

unread,
Jan 10, 2013, 1:49:19 PM1/10/13
to Yaohua Li, golang-nuts
All you've said is that it doesn't work.  How does it fail?


--
 
 

Yaohua Li

unread,
Jan 10, 2013, 3:15:07 PM1/10/13
to golan...@googlegroups.com, Yaohua Li
I finally find the problem. It is not a big deal.

The author of the C function recv_fd also provided a send_fd function. And in actual message they send/receive only "F".

if(message_buffer[0] != 'F')

My Go code won't send this "F", so it was not successful. Now it works well.

Thank you all!
Reply all
Reply to author
Forward
0 new messages