diff options
Diffstat (limited to 'ext/socket/unixsocket.c')
| -rw-r--r-- | ext/socket/unixsocket.c | 268 |
1 files changed, 170 insertions, 98 deletions
diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c index 75da9c1a78..2ec9376074 100644 --- a/ext/socket/unixsocket.c +++ b/ext/socket/unixsocket.c @@ -10,37 +10,57 @@ #include "rubysocket.h" -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN struct unixsock_arg { struct sockaddr_un *sockaddr; socklen_t sockaddrlen; - int fd; + VALUE io; }; static VALUE unixsock_connect_internal(VALUE a) { struct unixsock_arg *arg = (struct unixsock_arg *)a; - return (VALUE)rsock_connect(arg->fd, (struct sockaddr*)arg->sockaddr, - arg->sockaddrlen, 0); + return (VALUE)rsock_connect(arg->io, (struct sockaddr*)arg->sockaddr, arg->sockaddrlen, 0, RUBY_IO_TIMEOUT_DEFAULT); +} + +static VALUE +unixsock_path_value(VALUE path) +{ +#ifdef __linux__ +#define TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE 0 + + VALUE name = path; +#if TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE + const int isstr = !NIL_P(name = rb_check_string_type(name)); +#else + const int isstr = RB_TYPE_P(name, T_STRING); +#endif + if (isstr) { + if (RSTRING_LEN(name) == 0 || RSTRING_PTR(name)[0] == '\0') { + return name; /* ignore encoding */ + } + } +#endif + path = rb_get_path(path); +#ifdef _WIN32 + /* UNIXSocket requires UTF-8 per spec. */ + path = rb_str_export_to_enc(path, rb_utf8_encoding()); +#endif + return path; } VALUE -rsock_init_unixsock(VALUE sock, VALUE path, int server) +rsock_init_unixsock(VALUE self, VALUE path, int server) { struct sockaddr_un sockaddr; socklen_t sockaddrlen; int fd, status; rb_io_t *fptr; - SafeStringValue(path); - fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - rb_sys_fail("socket(2)"); - } + path = unixsock_path_value(path); - MEMZERO(&sockaddr, struct sockaddr_un, 1); - sockaddr.sun_family = AF_UNIX; + INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un)); if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) { rb_raise(rb_eArgError, "too long unix socket path (%ldbytes given but %dbytes max)", RSTRING_LEN(path), (int)sizeof(sockaddr.sun_path)); @@ -48,41 +68,51 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server) memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); sockaddrlen = rsock_unix_sockaddr_len(path); + fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + rsock_sys_fail_path("socket(2)", path); + } + + VALUE io = rsock_init_sock(self, fd); + RB_IO_POINTER(io, fptr); + if (server) { status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen); } else { - int prot; - struct unixsock_arg arg; - arg.sockaddr = &sockaddr; - arg.sockaddrlen = sockaddrlen; - arg.fd = fd; - status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot); - if (prot) { - close(fd); - rb_jump_tag(prot); - } + int error_tag; + struct unixsock_arg arg; + arg.sockaddr = &sockaddr; + arg.sockaddrlen = sockaddrlen; + arg.io = io; + + status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &error_tag); + + if (error_tag) { + rb_io_close(io); + rb_jump_tag(error_tag); + } } if (status < 0) { - close(fd); - rb_sys_fail_str(rb_inspect(path)); + int e = errno; + rb_io_close(io); + rsock_syserr_fail_path(e, "connect(2)", path); } if (server) { - if (listen(fd, SOMAXCONN) < 0) { - close(fd); - rb_sys_fail("listen(2)"); - } + if (listen(fd, SOMAXCONN) < 0) { + int e = errno; + rb_io_close(io); + rsock_syserr_fail_path(e, "listen(2)", path); + } } - rsock_init_sock(sock, fd); if (server) { - GetOpenFile(sock, fptr); fptr->pathv = rb_str_new_frozen(path); } - return sock; + return io; } /* @@ -91,14 +121,16 @@ rsock_init_unixsock(VALUE sock, VALUE path, int server) * * Creates a new UNIX client socket connected to _path_. * + * require 'socket' + * * s = UNIXSocket.new("/tmp/sock") * s.send "hello", 0 * */ static VALUE -unix_init(VALUE sock, VALUE path) +unix_init(VALUE self, VALUE path) { - return rsock_init_unixsock(sock, path, 0); + return rsock_init_unixsock(self, path, 0); } /* @@ -118,20 +150,20 @@ unix_path(VALUE sock) 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) - rb_sys_fail(0); + 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)); + fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len)); } return rb_str_dup(fptr->pathv); } /* * call-seq: - * unixsocket.recvfrom(maxlen [, flags]) => [mesg, unixaddress] + * unixsocket.recvfrom(maxlen [, flags[, outbuf]]) => [mesg, unixaddress] * * Receives a message via _unixsocket_. * @@ -139,6 +171,9 @@ unix_path(VALUE sock) * * _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) @@ -158,13 +193,13 @@ unix_recvfrom(int argc, VALUE *argv, VALUE sock) return rsock_s_recvfrom(sock, argc, argv, RECV_UNIX); } -#if defined(HAVE_ST_MSG_CONTROL) && defined(SCM_RIGHTS) +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && defined(SCM_RIGHTS) #define FD_PASSING_BY_MSG_CONTROL 1 #else #define FD_PASSING_BY_MSG_CONTROL 0 #endif -#if defined(HAVE_ST_MSG_ACCRIGHTS) +#if defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS) #define FD_PASSING_BY_MSG_ACCRIGHTS 1 #else #define FD_PASSING_BY_MSG_ACCRIGHTS 0 @@ -198,6 +233,8 @@ sendmsg_blocking(void *data) * 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. */ static VALUE unix_send_io(VALUE sock, VALUE val) @@ -209,22 +246,22 @@ unix_send_io(VALUE sock, VALUE val) char buf[1]; #if FD_PASSING_BY_MSG_CONTROL - struct { - struct cmsghdr hdr; - char pad[8+sizeof(int)+8]; + 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; + GetOpenFile(val, valfptr); + fd = valfptr->fd; } else if (FIXNUM_P(val)) { fd = FIX2INT(val); } else { - rb_raise(rb_eTypeError, "neither IO nor file descriptor"); + rb_raise(rb_eTypeError, "neither IO nor file descriptor"); } GetOpenFile(sock, fptr); @@ -254,9 +291,9 @@ unix_send_io(VALUE sock, VALUE val) #endif arg.fd = fptr->fd; - while ((int)BLOCKING_REGION_FD(sendmsg_blocking, &arg) == -1) { - if (!rb_io_wait_writable(arg.fd)) - rb_sys_fail("sendmsg(2)"); + 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; @@ -278,6 +315,8 @@ recvmsg_blocking(void *data) * call-seq: * unixsocket.recv_io([klass [, mode]]) => io * + * Example + * * UNIXServer.open("/tmp/sock") {|serv| * UNIXSocket.open("/tmp/sock") {|c| * s = serv.accept @@ -292,6 +331,11 @@ recvmsg_blocking(void *data) * } * } * + * _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 */ static VALUE unix_recv_io(int argc, VALUE *argv, VALUE sock) @@ -301,21 +345,28 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock) 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 - struct { - struct cmsghdr hdr; - char pad[8+sizeof(int)+8]; + 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; + klass = rb_cIO; if (argc <= 1) - mode = Qnil; + mode = Qnil; +retry: GetOpenFile(sock, fptr); arg.msg.msg_name = NULL; @@ -342,67 +393,88 @@ unix_recv_io(int argc, VALUE *argv, VALUE sock) #endif arg.fd = fptr->fd; - while ((int)BLOCKING_REGION_FD(recvmsg_blocking, &arg) == -1) { - if (!rb_io_wait_readable(arg.fd)) - rb_sys_fail("recvmsg(2)"); + 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)) { - 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)); + /* 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); + 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); + 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))); + 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))); + 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))); + 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", - arg.msg.msg_accrightslen, (int)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_fd_fix_cloexec(fd); + + rb_update_max_fd(fd); + rb_maygvl_fd_fix_cloexec(fd); if (klass == Qnil) - return INT2FIX(fd); + 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_funcall2(klass, for_fd, ff_argc, ff_argv); + 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); } } #else @@ -431,7 +503,7 @@ unix_addr(VALUE sock) GetOpenFile(sock, fptr); if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail("getsockname(2)"); + rsock_sys_fail_path("getsockname(2)", fptr->pathv); if (len0 < len) len = len0; return rsock_unixaddr(&addr, len); } @@ -459,7 +531,7 @@ unix_peeraddr(VALUE sock) GetOpenFile(sock, fptr); if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail("getpeername(2)"); + rsock_sys_fail_path("getpeername(2)", fptr->pathv); if (len0 < len) len = len0; return rsock_unixaddr(&addr, len); } @@ -469,9 +541,9 @@ unix_peeraddr(VALUE sock) * UNIXSocket.pair([type [, protocol]]) => [unixsocket1, unixsocket2] * UNIXSocket.socketpair([type [, protocol]]) => [unixsocket1, unixsocket2] * - * Creates a pair of sockets connected each other. + * Creates a pair of sockets connected to each other. * - * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc. + * _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. @@ -491,9 +563,9 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass) domain = INT2FIX(PF_UNIX); rb_scan_args(argc, argv, "02", &type, &protocol); if (argc == 0) - type = INT2FIX(SOCK_STREAM); + type = INT2FIX(SOCK_STREAM); if (argc <= 1) - protocol = INT2FIX(0); + protocol = INT2FIX(0); args[0] = domain; args[1] = type; @@ -506,7 +578,7 @@ unix_s_socketpair(int argc, VALUE *argv, VALUE klass) void rsock_init_unixsocket(void) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * Document-class: UNIXSocket < BasicSocket * |
