summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--ext/socket/extconf.rb40
-rw-r--r--ext/socket/socket.c194
3 files changed, 247 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index a58d25f919..da8b961a7c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+Fri Mar 1 06:25:49 2002 Tanaka Akira <akr@m17n.org>
+
+ * ext/socket/extconf.rb (have_struct_member): new method.
+ check msg_control and msg_accrights in struct msghdr. check sys/uio.h.
+
+ * socket/socket.c: include sys/uio.h if available.
+ (thread_read_select): new function.
+ (unix_send_io): ditto.
+ (unix_recv_io): ditto.
+ (unix_s_socketpair): ditto.
+ (Init_socket): define UNIXSocket#send_io, UNIXSocket#recv_io,
+ UNIXSocket#socketpair and UNIXSocket#pair.
+
Wed Feb 27 16:30:50 2002 Yukihiro Matsumoto <matz@ruby-lang.org>
* eval.c (rb_mod_include): load modules in argument order.
diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb
index c795d3f814..a306b3f9c5 100644
--- a/ext/socket/extconf.rb
+++ b/ext/socket/extconf.rb
@@ -2,6 +2,42 @@ require 'mkmf'
$CPPFLAGS += " -Dss_family=__ss_family -Dss_len=__ss_len"
+def have_struct_member(type, member, header=nil)
+ printf "checking for %s.%s... ", type, member
+ STDOUT.flush
+
+ libs = $libs
+ src =
+ if /mswin32|mingw/ =~ RUBY_PLATFORM
+ r = <<"SRC"
+#include <windows.h>
+#include <winsock.h>
+SRC
+ else
+ ""
+ end
+ unless header.nil?
+ header = [header] unless header.kind_of? Array
+ header.each {|h|
+ src << <<"SRC"
+#include <#{h}>
+SRC
+ }
+ end
+ src << <<"SRC"
+int main() { return 0; }
+int s = (char *)&((#{type}*)0)->#{member} - (char *)0;
+SRC
+ r = try_link(src, libs) # xxx try_compile is not available.
+ unless r
+ print "no\n"
+ return false
+ end
+ $defs.push(format("-DHAVE_ST_%s", member.upcase))
+ print "yes\n"
+ return true
+end
+
case RUBY_PLATFORM
when /mswin32|mingw/
test_func = "WSACleanup"
@@ -176,6 +212,9 @@ end
have_header("netinet/tcp.h") if not /cygwin/ === RUBY_PLATFORM # for cygwin 1.1.5
have_header("netinet/udp.h")
+have_struct_member('struct msghdr', 'msg_control', header=['sys/types.h', 'sys/socket.h'])
+have_struct_member('struct msghdr', 'msg_accrights', header=['sys/types.h', 'sys/socket.h'])
+
$getaddr_info_ok = false
if not enable_config("wide-getaddrinfo", false) and try_run(<<EOF)
#include <sys/types.h>
@@ -329,6 +368,7 @@ EOF
end
have_header("sys/un.h")
+have_header("sys/uio.h")
if have_func(test_func)
have_func("hsterror")
diff --git a/ext/socket/socket.c b/ext/socket/socket.c
index 5580725fd9..281a1fe1ea 100644
--- a/ext/socket/socket.c
+++ b/ext/socket/socket.c
@@ -20,6 +20,10 @@
#include <unistd.h>
#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
#ifndef NT
#if defined(__BEOS__)
# include <net/socket.h>
@@ -684,6 +688,17 @@ ruby_socket(domain, type, proto)
}
static void
+thread_read_select(fd)
+ int fd;
+{
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ rb_thread_select(fd+1, &fds, 0, 0, 0);
+}
+
+static void
thread_write_select(fd)
int fd;
{
@@ -1382,6 +1397,161 @@ unix_recvfrom(argc, argv, sock)
}
static VALUE
+unix_send_io(sock, val)
+ VALUE sock, val;
+{
+#if defined(HAVE_ST_MSG_CONTROL) || defined(HAVE_ST_MSG_ACCRIGHTS)
+ int fd;
+ OpenFile *fptr;
+ struct msghdr msg;
+ struct iovec vec[1];
+ char buf[1];
+
+#if defined(HAVE_ST_MSG_CONTROL)
+ struct {
+ struct cmsghdr hdr;
+ int fd;
+ } cmsg;
+#endif
+
+ if (rb_obj_is_kind_of(val, rb_cIO)) {
+ OpenFile *valfptr;
+ GetOpenFile(val, valfptr);
+ fd = fileno(valfptr->f);
+ }
+ else if (FIXNUM_P(val)) {
+ fd = FIX2INT(val);
+ }
+ else {
+ rb_raise(rb_eTypeError, "IO nor file descriptor");
+ }
+
+ GetOpenFile(sock, fptr);
+
+ msg.msg_name = NULL;
+ 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;
+ msg.msg_iov = vec;
+ msg.msg_iovlen = 1;
+
+#if defined(HAVE_ST_MSG_CONTROL)
+ msg.msg_control = (caddr_t)&cmsg;
+ msg.msg_controllen = sizeof(struct cmsghdr) + sizeof(int);
+ msg.msg_flags = 0;
+ cmsg.hdr.cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_RIGHTS;
+ cmsg.fd = fd;
+#else
+ msg.msg_accrights = (caddr_t)&fd;
+ msg.msg_accrightslen = sizeof(fd);
+#endif
+
+ if (sendmsg(fileno(fptr->f), &msg, 0) == -1)
+ rb_sys_fail("sendmsg(2)");
+
+ return Qnil;
+#else
+ rb_notimplement();
+#endif
+}
+
+static VALUE
+unix_recv_io(argc, argv, sock)
+ int argc;
+ VALUE *argv;
+ VALUE sock;
+{
+#if defined(HAVE_ST_MSG_CONTROL) || defined(HAVE_ST_MSG_ACCRIGHTS)
+ VALUE klass, mode;
+ OpenFile *fptr;
+ struct msghdr msg;
+ struct iovec vec[2];
+ char buf[1];
+
+ int fd;
+#if defined(HAVE_ST_MSG_CONTROL)
+ struct {
+ struct cmsghdr hdr;
+ int fd;
+ } cmsg;
+#endif
+
+ rb_scan_args(argc, argv, "02", &klass, &mode);
+ if (argc == 0)
+ klass = rb_cIO;
+ if (argc <= 1)
+ mode = Qnil;
+
+ GetOpenFile(sock, fptr);
+
+ thread_read_select(fileno(fptr->f));
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ vec[0].iov_base = buf;
+ vec[0].iov_len = sizeof(buf);
+ msg.msg_iov = vec;
+ msg.msg_iovlen = 1;
+
+#if defined(HAVE_ST_MSG_CONTROL)
+ msg.msg_control = (caddr_t)&cmsg;
+ msg.msg_controllen = sizeof(struct cmsghdr) + sizeof(int);
+ msg.msg_flags = 0;
+ cmsg.hdr.cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_RIGHTS;
+ cmsg.fd = -1;
+#else
+ msg.msg_accrights = (caddr_t)&fd;
+ msg.msg_accrightslen = sizeof(fd);
+ fd = -1;
+#endif
+
+ if (recvmsg(fileno(fptr->f), &msg, 0) == -1)
+ rb_sys_fail("recvmsg(2)");
+
+ if (
+#if defined(HAVE_ST_MSG_CONTROL)
+ msg.msg_controllen != sizeof(struct cmsghdr) + sizeof(int) ||
+ cmsg.hdr.cmsg_len != sizeof(struct cmsghdr) + sizeof(int) ||
+ cmsg.hdr.cmsg_level != SOL_SOCKET ||
+ cmsg.hdr.cmsg_type != SCM_RIGHTS
+#else
+ msg.msg_accrightslen != sizeof(fd)
+#endif
+ ) {
+ rb_raise(rb_eSocket, "File descriptor was not passed");
+ }
+
+#if defined(HAVE_ST_MSG_CONTROL)
+ fd = cmsg.fd;
+#endif
+
+ if (klass == Qnil)
+ return INT2FIX(fd);
+ else {
+ static ID for_fd = 0;
+ int ff_argc;
+ VALUE ff_argv[2];
+ if (!for_fd)
+ for_fd = rb_intern("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);
+ }
+#else
+ rb_notimplement();
+#endif
+}
+
+static VALUE
unix_accept(sock)
VALUE sock;
{
@@ -1564,6 +1734,26 @@ sock_s_socketpair(klass, domain, type, protocol)
#endif
}
+#ifdef HAVE_SYS_UN_H
+static VALUE
+unix_s_socketpair(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE domain, type, protocol;
+ 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);
+
+ return sock_s_socketpair(klass, domain, type, protocol);
+}
+#endif
+
static VALUE
sock_connect(sock, addr)
VALUE sock, addr;
@@ -2177,6 +2367,10 @@ Init_socket()
rb_define_method(rb_cUNIXSocket, "addr", unix_addr, 0);
rb_define_method(rb_cUNIXSocket, "peeraddr", unix_peeraddr, 0);
rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1);
+ rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1);
+ rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1);
+ rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1);
+ rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1);
rb_cUNIXServer = rb_define_class("UNIXServer", rb_cUNIXSocket);
rb_define_global_const("UNIXserver", rb_cUNIXServer);