# class UNIXSocket

`UNIXSocket` represents a UNIX domain stream client socket.

### Public Class Methods

new(path) → unixsocket
Source

    static VALUE
    unix_init(VALUE self, VALUE path)
    {
        return rsock_init_unixsock(self, path, 0);
    }

Creates a new UNIX client socket connected to path.

    require 'socket'

    s = UNIXSocket.new("/tmp/sock")
    s.send "hello", 0

pair([type [, protocol]]) → [unixsocket1, unixsocket2]
socketpair([type [, protocol]]) → [unixsocket1, unixsocket2]
Source

    static VALUE
    unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
    {
        VALUE domain, type, protocol;
        VALUE args[3];

        domain = INT2FIX(PF_UNIX);
        rb_scan_args(argc, argv, "02", &type, &protocol);
        if (argc == 0)
            type = INT2FIX(SOCK_STREAM);
        if (argc <= 1)
            protocol = INT2FIX(0);

        args[0] = domain;
        args[1] = type;
        args[2] = protocol;

        return rsock_sock_s_socketpair(3, args, klass);
    }

Creates a pair of sockets connected to each other.

type should be a socket type such as: :STREAM, :DGRAM, :RAW, etc.

protocol should be a protocol defined in the domain. 0 is default protocol for the domain.

    s1, s2 = UNIXSocket.pair
    s1.send "a", 0
    s1.send "b", 0
    p s2.recv(10) #=> "ab"

pair([type [, protocol]]) → [unixsocket1, unixsocket2]
socketpair([type [, protocol]]) → [unixsocket1, unixsocket2]
Source

    static VALUE
    unix_s_socketpair(int argc, VALUE *argv, VALUE klass)
    {
        VALUE domain, type, protocol;
        VALUE args[3];

        domain = INT2FIX(PF_UNIX);
        rb_scan_args(argc, argv, "02", &type, &protocol);
        if (argc == 0)
            type = INT2FIX(SOCK_STREAM);
        if (argc <= 1)
            protocol = INT2FIX(0);

        args[0] = domain;
        args[1] = type;
        args[2] = protocol;

        return rsock_sock_s_socketpair(3, args, klass);
    }

Creates a pair of sockets connected to each other.

type should be a socket type such as: :STREAM, :DGRAM, :RAW, etc.

protocol should be a protocol defined in the domain. 0 is default protocol for the domain.

    s1, s2 = UNIXSocket.pair
    s1.send "a", 0
    s1.send "b", 0
    p s2.recv(10) #=> "ab"

### Public Instance Methods

addr → [address_family, unix_path]
Source

    static VALUE
    unix_addr(VALUE sock)
    {
        rb_io_t *fptr;
        struct sockaddr_un addr;
        socklen_t len = (socklen_t)sizeof addr;
        socklen_t len0 = len;

        GetOpenFile(sock, fptr);

        if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
            rsock_sys_fail_path("getsockname(2)", fptr->pathv);
        if (len0 < len) len = len0;
        return rsock_unixaddr(&addr, len);
    }

Returns the local address as an array which contains address_family and unix_path.

Example

    serv = UNIXServer.new("/tmp/sock")
    p serv.addr #=> ["AF_UNIX", "/tmp/sock"]

path → path
Source

    static VALUE
    unix_path(VALUE sock)
    {
        rb_io_t *fptr;

        GetOpenFile(sock, fptr);
        if (NIL_P(fptr->pathv)) {
            struct sockaddr_un addr;
            socklen_t len = (socklen_t)sizeof(addr);
            socklen_t len0 = len;
            if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
                rsock_sys_fail_path("getsockname(2)", fptr->pathv);
            if (len0 < len) len = len0;
            fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len));
        }
        return rb_str_dup(fptr->pathv);
    }

Returns the path of the local address of unixsocket.

    s = UNIXServer.new("/tmp/sock")
    p s.path #=> "/tmp/sock"

peeraddr → [address_family, unix_path]
Source

    static VALUE
    unix_peeraddr(VALUE sock)
    {
        rb_io_t *fptr;
        struct sockaddr_un addr;
        socklen_t len = (socklen_t)sizeof addr;
        socklen_t len0 = len;

        GetOpenFile(sock, fptr);

        if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0)
            rsock_sys_fail_path("getpeername(2)", fptr->pathv);
        if (len0 < len) len = len0;
        return rsock_unixaddr(&addr, len);
    }

Returns the remote address as an array which contains address_family and unix_path.

Example

    serv = UNIXServer.new("/tmp/sock")
    c = UNIXSocket.new("/tmp/sock")
    p c.peeraddr #=> ["AF_UNIX", "/tmp/sock"]

recv_io([klass [, mode]]) → io
Source

    static VALUE
    unix_recv_io(int argc, VALUE *argv, VALUE sock)
    {
        VALUE klass, mode;
        rb_io_t *fptr;
        struct iomsg_arg arg;
        struct iovec vec[2];
        char buf[1];
        unsigned int gc_reason = 0;
        enum {
            GC_REASON_EMSGSIZE = 0x1,
            GC_REASON_TRUNCATE = 0x2,
            GC_REASON_ENOMEM = 0x4
        };

        int fd;
    #if FD_PASSING_BY_MSG_CONTROL
        union {
            struct cmsghdr hdr;
            char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
        } cmsg;
    #endif

        rb_scan_args(argc, argv, "02", &klass, &mode);
        if (argc == 0)
            klass = rb_cIO;
        if (argc <= 1)
            mode = Qnil;

    retry:
        GetOpenFile(sock, fptr);

        arg.msg.msg_name = NULL;
        arg.msg.msg_namelen = 0;

        vec[0].iov_base = buf;
        vec[0].iov_len = sizeof(buf);
        arg.msg.msg_iov = vec;
        arg.msg.msg_iovlen = 1;

    #if FD_PASSING_BY_MSG_CONTROL
        arg.msg.msg_control = (caddr_t)&cmsg;
        arg.msg.msg_controllen = (socklen_t)CMSG_SPACE(sizeof(int));
        arg.msg.msg_flags = 0;
        cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int));
        cmsg.hdr.cmsg_level = SOL_SOCKET;
        cmsg.hdr.cmsg_type = SCM_RIGHTS;
        fd = -1;
        memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int));
    #else
        arg.msg.msg_accrights = (caddr_t)&fd;
        arg.msg.msg_accrightslen = sizeof(fd);
        fd = -1;
    #endif

        arg.fd = fptr->fd;
        while ((int)rb_io_blocking_region(fptr, recvmsg_blocking, &arg) == -1) {
            int e = errno;
            if (e == EMSGSIZE && !(gc_reason & GC_REASON_EMSGSIZE)) {
                /* FreeBSD gets here when we're out of FDs */
                gc_reason |= GC_REASON_EMSGSIZE;
                rb_gc_for_fd(EMFILE);
                goto retry;
            }
            else if (e == ENOMEM && !(gc_reason & GC_REASON_ENOMEM)) {
                /* ENOMEM is documented in recvmsg manpages */
                gc_reason |= GC_REASON_ENOMEM;
                rb_gc_for_fd(e);
                goto retry;
            }
            if (!rb_io_wait_readable(arg.fd))
                rsock_syserr_fail_path(e, "recvmsg(2)", fptr->pathv);
        }

    #if FD_PASSING_BY_MSG_CONTROL
        if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) {
            /* FreeBSD and Linux both get here when we're out of FDs */
            if (!(gc_reason & GC_REASON_TRUNCATE)) {
                gc_reason |= GC_REASON_TRUNCATE;
                rb_gc_for_fd(EMFILE);
                goto retry;
            }
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)",
                     (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr));
        }
        if (cmsg.hdr.cmsg_level != SOL_SOCKET) {
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (cmsg_level=%d, %d expected)",
                     cmsg.hdr.cmsg_level, SOL_SOCKET);
        }
        if (cmsg.hdr.cmsg_type != SCM_RIGHTS) {
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (cmsg_type=%d, %d expected)",
                     cmsg.hdr.cmsg_type, SCM_RIGHTS);
        }
        if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) {
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)",
                     (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int)));
        }
        if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) {
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)",
                     (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int)));
        }
        if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) {
            rsock_discard_cmsg_resource(&arg.msg, 0);
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (cmsg_len=%d, %d expected)",
                     (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int)));
        }
    #else
        if (arg.msg.msg_accrightslen != sizeof(fd)) {
            rb_raise(rb_eSocket,
                     "file descriptor was not passed (accrightslen=%d, %d expected)",
                     arg.msg.msg_accrightslen, (int)sizeof(fd));
        }
    #endif

    #if FD_PASSING_BY_MSG_CONTROL
        memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int));
    #endif

        rb_update_max_fd(fd);
        rb_maygvl_fd_fix_cloexec(fd);

        if (klass == Qnil)
            return INT2FIX(fd);
        else {
            ID for_fd;
            int ff_argc;
            VALUE ff_argv[2];
            CONST_ID(for_fd, "for_fd");
            ff_argc = mode == Qnil ? 1 : 2;
            ff_argv[0] = INT2FIX(fd);
            ff_argv[1] = mode;
            return rb_funcallv(klass, for_fd, ff_argc, ff_argv);
        }
    }

Example

    UNIXServer.open("/tmp/sock") {|serv|
      UNIXSocket.open("/tmp/sock") {|c|
        s = serv.accept

        c.send_io STDOUT
        stdout = s.recv_io

        p STDOUT.fileno #=> 1
        p stdout.fileno #=> 7

        stdout.puts "hello" # outputs "hello\n" to standard output.
      }
    }

klass will determine the class of io returned (using the `IO.for_fd` singleton method or similar). If klass is `nil`, an integer file descriptor is returned.

mode is the same as the argument passed to `IO.for_fd`

recvfrom(maxlen [, flags[, outbuf]]) → [mesg, unixaddress]
Source

    static VALUE
    unix_recvfrom(int argc, VALUE *argv, VALUE sock)
    {
        return rsock_s_recvfrom(sock, argc, argv, RECV_UNIX);
    }

Receives a message via unixsocket.

maxlen is the maximum number of bytes to receive.

flags should be a bitwise OR of Socket::MSG_* constants.

outbuf will contain only the received data after the method call even if it is not empty at the beginning.

    s1 = Socket.new(:UNIX, :DGRAM, 0)
    s1_ai = Addrinfo.unix("/tmp/sock1")
    s1.bind(s1_ai)

    s2 = Socket.new(:UNIX, :DGRAM, 0)
    s2_ai = Addrinfo.unix("/tmp/sock2")
    s2.bind(s2_ai)
    s3 = UNIXSocket.for_fd(s2.fileno)

    s1.send "a", 0, s2_ai
    p s3.recvfrom(10) #=> ["a", ["AF_UNIX", "/tmp/sock1"]]

send_io(io) → nil
Source

    static VALUE
    unix_send_io(VALUE sock, VALUE val)
    {
        int fd;
        rb_io_t *fptr;
        struct iomsg_arg arg;
        struct iovec vec[1];
        char buf[1];

    #if FD_PASSING_BY_MSG_CONTROL
        union {
            struct cmsghdr hdr;
            char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8];
        } cmsg;
    #endif

        if (rb_obj_is_kind_of(val, rb_cIO)) {
            rb_io_t *valfptr;
            GetOpenFile(val, valfptr);
            fd = valfptr->fd;
        }
        else if (FIXNUM_P(val)) {
            fd = FIX2INT(val);
        }
        else {
            rb_raise(rb_eTypeError, "neither IO nor file descriptor");
        }

        GetOpenFile(sock, fptr);

        arg.msg.msg_name = NULL;
        arg.msg.msg_namelen = 0;

        /* Linux and Solaris doesn't work if msg_iov is NULL. */
        buf[0] = '\0';
        vec[0].iov_base = buf;
        vec[0].iov_len = 1;
        arg.msg.msg_iov = vec;
        arg.msg.msg_iovlen = 1;

    #if FD_PASSING_BY_MSG_CONTROL
        arg.msg.msg_control = (caddr_t)&cmsg;
        arg.msg.msg_controllen = (socklen_t)CMSG_LEN(sizeof(int));
        arg.msg.msg_flags = 0;
        MEMZERO((char*)&cmsg, char, sizeof(cmsg));
        cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int));
        cmsg.hdr.cmsg_level = SOL_SOCKET;
        cmsg.hdr.cmsg_type = SCM_RIGHTS;
        memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int));
    #else
        arg.msg.msg_accrights = (caddr_t)&fd;
        arg.msg.msg_accrightslen = sizeof(fd);
    #endif

        arg.fd = fptr->fd;
        while ((int)rb_io_blocking_region(fptr, sendmsg_blocking, &arg) == -1) {
            if (!rb_io_wait_writable(arg.fd))
                rsock_sys_fail_path("sendmsg(2)", fptr->pathv);
        }

        return Qnil;
    }

Sends io as file descriptor passing.

    s1, s2 = UNIXSocket.pair

    s1.send_io STDOUT
    stdout = s2.recv_io

    p STDOUT.fileno #=> 1
    p stdout.fileno #=> 6

    stdout.puts "hello" # outputs "hello\n" to standard output.

io may be any kind of `IO` object or integer file descriptor.