[ruby-dev:47223] [ruby-trunk - Feature #4195][Rejected] option for Socket#sendmsg

From: "akr (Akira Tanaka)" <akr@...>
Date: 2013-04-04 14:49:04 UTC
List: ruby-dev #47223
Issue #4195 has been updated by akr (Akira Tanaka).

Status changed from Assigned to Rejected

頻度から考えて、とくに簡単にしなくてもいいんじゃないかと思います。
やりかたが複数になるのも気に入りませんし。
----------------------------------------
Feature #4195: option for Socket#sendmsg
https://bugs.ruby-lang.org/issues/4195#change-38223

Author: nobu (Nobuyoshi Nakada)
Status: Rejected
Priority: Low
Assignee: akr (Akira Tanaka)
Category: ext
Target version: next minor


=begin
 なかだです。
 
 Socket#recvmsgは scm_rights: true を指定するだけでメインのデータだけで
 なく簡単にIOを受け取ることができますが、一方でSocket#sendmsg側には対応
 する指定ができません。以下のようなオプションを追加するのはどうでしょう
 か。
 
   s.sendmsg("foo", scm_rights: STDIN)
   s.sendmsg("foo", scm_rights: [STDIN, STDOUT])
 
 
 diff --git i/ext/socket/ancdata.c w/ext/socket/ancdata.c
 index abaf19d..c329e0a 100644
 --- i/ext/socket/ancdata.c
 +++ w/ext/socket/ancdata.c
 @@ -2,6 +2,8 @@
  
  #include <time.h>
  
 +static ID sym_scm_rights;
 +
  #if defined(HAVE_ST_MSG_CONTROL)
  static VALUE rb_cAncillaryData;
  
 @@ -1126,17 +1128,63 @@ rb_sendmsg(int fd, const struct msghdr *msg, int flags)
      return rb_thread_blocking_region(nogvl_sendmsg_func, &args, RUBY_UBF_IO, 0);
  }
  
 +#if defined(HAVE_ST_MSG_CONTROL)
 +static size_t
 +io_to_fd(VALUE io)
 +{
 +    VALUE fnum = rb_check_to_integer(io, "to_int");
 +    if (NIL_P(fnum))
 +	fnum = rb_convert_type(io, T_FIXNUM, "Fixnum", "fileno");
 +    return NUM2UINT(fnum);
 +}
 +
 +static char *
 +prepare_msghdr(VALUE controls_str, int level, int type, long clen)
 +{
 +    struct cmsghdr cmh;
 +    char *cmsg;
 +    size_t cspace;
 +    long oldlen = RSTRING_LEN(controls_str);
 +    cspace = CMSG_SPACE(clen);
 +    rb_str_resize(controls_str, oldlen + cspace);
 +    cmsg = RSTRING_PTR(controls_str)+oldlen;
 +    memset((char *)cmsg, 0, cspace);
 +    memset((char *)&cmh, 0, sizeof(cmh));
 +    cmh.cmsg_level = level;
 +    cmh.cmsg_type = type;
 +    cmh.cmsg_len = (socklen_t)CMSG_LEN(clen);
 +    MEMCPY(cmsg, &cmh, char, sizeof(cmh));
 +    return cmsg+((char*)CMSG_DATA(&cmh)-(char*)&cmh);
 +}
 +
 +# if defined(__NetBSD__)
 +#   define TRIM_PADDING 1
 +# endif
 +# if TRIM_PADDING
 +#   define prepare_msghdr(controls_str, level, type, clen) \
 +    (last_pad = CMSG_SPACE(clen) - CMSG_LEN(clen), \
 +     prepare_msghdr((controls_str), \
 +		    last_level = (level), last_type = (type), \
 +		    (clen)))
 +# endif
 +#endif
 +
  static VALUE
  bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
  {
      rb_io_t *fptr;
 -    VALUE data, vflags, dest_sockaddr;
 +    VALUE data, vflags, dest_sockaddr, vopts = Qnil;
      VALUE *controls_ptr;
      int controls_num;
      struct msghdr mh;
      struct iovec iov;
  #if defined(HAVE_ST_MSG_CONTROL)
      volatile VALUE controls_str = 0;
 +# if TRIM_PADDING
 +    size_t last_pad = 0;
 +    int last_level = 0;
 +    int last_type = 0;
 +# endif
  #endif
      int flags;
      ssize_t ss;
 @@ -1152,6 +1200,8 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
  
      if (argc == 0)
          rb_raise(rb_eArgError, "mesg argument required");
 +    if (1 < argc && RB_TYPE_P(argv[argc-1], T_HASH))
 +        vopts = argv[--argc];
      data = argv[0];
      if (1 < argc) vflags = argv[1];
      if (2 < argc) dest_sockaddr = argv[2];
 @@ -1162,19 +1212,13 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
      if (controls_num) {
  #if defined(HAVE_ST_MSG_CONTROL)
  	int i;
 -	size_t last_pad = 0;
 -        int last_level = 0;
 -        int last_type = 0;
          controls_str = rb_str_tmp_new(0);
          for (i = 0; i < controls_num; i++) {
              VALUE elt = controls_ptr[i], v;
              VALUE vlevel, vtype;
              int level, type;
              VALUE cdata;
 -            long oldlen;
 -            struct cmsghdr cmh;
              char *cmsg;
 -            size_t cspace;
              v = rb_check_convert_type(elt, T_ARRAY, "Array", "to_ary");
              if (!NIL_P(v)) {
                  elt = v;
 @@ -1192,21 +1236,46 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
              level = rsock_level_arg(family, vlevel);
              type = rsock_cmsg_type_arg(family, level, vtype);
              StringValue(cdata);
 -            oldlen = RSTRING_LEN(controls_str);
 -            cspace = CMSG_SPACE(RSTRING_LEN(cdata));
 -            rb_str_resize(controls_str, oldlen + cspace);
 -            cmsg = RSTRING_PTR(controls_str)+oldlen;
 -            memset((char *)cmsg, 0, cspace);
 -            memset((char *)&cmh, 0, sizeof(cmh));
 -            cmh.cmsg_level = level;
 -            cmh.cmsg_type = type;
 -            cmh.cmsg_len = (socklen_t)CMSG_LEN(RSTRING_LEN(cdata));
 -            MEMCPY(cmsg, &cmh, char, sizeof(cmh));
 -            MEMCPY(cmsg+((char*)CMSG_DATA(&cmh)-(char*)&cmh), RSTRING_PTR(cdata), char, RSTRING_LEN(cdata));
 -            last_level = cmh.cmsg_level;
 -            last_type = cmh.cmsg_type;
 -	    last_pad = cspace - cmh.cmsg_len;
 +            cmsg = prepare_msghdr(controls_str, level, type, RSTRING_LEN(cdata));
 +            MEMCPY(cmsg, RSTRING_PTR(cdata), char, RSTRING_LEN(cdata));
          }
 +#else
 +      no_msg_control:
 +	rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented");
 +#endif
 +    }
 +    if (!NIL_P(vopts)) {
 +	VALUE rights = rb_hash_aref(vopts, sym_scm_rights);
 +	if (!NIL_P(rights)) {
 +#if defined(HAVE_ST_MSG_CONTROL)
 +	    VALUE tmp = rb_check_array_type(rights);
 +            long count = NIL_P(tmp) ? 1 : RARRAY_LEN(tmp);
 +            char *cmsg;
 +	    int fd;
 +	    if (!controls_str) controls_str = rb_str_tmp_new(0);
 +            cmsg = prepare_msghdr(controls_str, SOL_SOCKET, SCM_RIGHTS,
 +                                  count * sizeof(int));
 +            if (NIL_P(tmp)) {
 +                fd = io_to_fd(rights);
 +                MEMCPY(cmsg, &fd, int, 1);
 +            }
 +            else {
 +                long i;
 +                rights = tmp;
 +                for (i = 0; i < count && i < RARRAY_LEN(rights); ++i) {
 +                    fd = io_to_fd(RARRAY_PTR(rights)[i]);
 +                    MEMCPY(cmsg, &fd, int, 1);
 +                    cmsg += sizeof(int);
 +                }
 +            }
 +#else
 +	    goto no_msg_control;
 +#endif
 +	}
 +    }
 +#if defined(HAVE_ST_MSG_CONTROL)
 +    {
 +# if TRIM_PADDING
  	if (last_pad) {
              /*
               * This code removes the last padding from msg_controllen.
 @@ -1228,15 +1297,12 @@ bsock_sendmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
               * Basically, msg_controllen should contains the padding.
               * So the padding is removed only if a problem really exists.
               */
 -#if defined(__NetBSD__)
              if (last_level == SOL_SOCKET && last_type == SCM_RIGHTS)
                  rb_str_set_len(controls_str, RSTRING_LEN(controls_str)-last_pad);
 -#endif
  	}
 -#else
 -	rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented");
 -#endif
 +# endif
      }
 +#endif
  
      flags = NIL_P(vflags) ? 0 : NUM2INT(vflags);
  #ifdef MSG_DONTWAIT
 @@ -1492,7 +1558,7 @@ bsock_recvmsg_internal(int argc, VALUE *argv, VALUE sock, int nonblock)
      grow_buffer = NIL_P(vmaxdatlen) || NIL_P(vmaxctllen);
  
      request_scm_rights = 0;
 -    if (!NIL_P(vopts) && RTEST(rb_hash_aref(vopts, ID2SYM(rb_intern("scm_rights")))))
 +    if (!NIL_P(vopts) && RTEST(rb_hash_aref(vopts, sym_scm_rights)))
          request_scm_rights = 1;
  
      GetOpenFile(sock, fptr);
 @@ -1795,5 +1861,7 @@ rsock_init_ancdata(void)
      rb_define_method(rb_cAncillaryData, "ipv6_pktinfo", ancillary_ipv6_pktinfo, 0);
      rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_addr", ancillary_ipv6_pktinfo_addr, 0);
      rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_ifindex", ancillary_ipv6_pktinfo_ifindex, 0);
 +
 +    sym_scm_rights = ID2SYM(rb_intern("scm_rights"));
  #endif
  }
 diff --git i/test/socket/test_unix.rb w/test/socket/test_unix.rb
 index bde17cf..e9db22e 100644
 --- i/test/socket/test_unix.rb
 +++ w/test/socket/test_unix.rb
 @@ -31,7 +31,7 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
      end
    end
  
 -  def test_fd_passing_n
 +  def fd_passing_test
      io_ary = []
      return if !defined?(Socket::SCM_RIGHTS)
      io_ary.concat IO.pipe
 @@ -42,8 +42,7 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
        send_io_ary << io
        UNIXSocket.pair {|s1, s2|
          begin
 -          ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS,
 -                                          send_io_ary.map {|io2| io2.fileno }.pack("i!*")])
 +          ret = yield(s1, send_io_ary)
          rescue NotImplementedError
            return
          end
 @@ -66,48 +65,38 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
      io_ary.each {|io| io.close if !io.closed? }
    end
  
 +  def test_fd_passing_n
 +    fd_passing_test do |s, ios|
 +      s.sendmsg("\0", 0, nil,
 +                [Socket::SOL_SOCKET, Socket::SCM_RIGHTS, ios.map(&:fileno).pack("i!*")])
 +    end
 +  end
 +
    def test_fd_passing_n2
 -    io_ary = []
 -    return if !defined?(Socket::SCM_RIGHTS)
 -    return if !defined?(Socket::AncillaryData)
 -    io_ary.concat IO.pipe
 -    io_ary.concat IO.pipe
 -    io_ary.concat IO.pipe
 -    send_io_ary = []
 -    io_ary.each {|io|
 -      send_io_ary << io
 -      UNIXSocket.pair {|s1, s2|
 -        begin
 -          ancdata = Socket::AncillaryData.unix_rights(*send_io_ary)
 -          ret = s1.sendmsg("\0", 0, nil, ancdata)
 -        rescue NotImplementedError
 -          return
 -        end
 -        assert_equal(1, ret)
 -        ret = s2.recvmsg(:scm_rights=>true)
 -        data, srcaddr, flags, *ctls = ret
 -        recv_io_ary = []
 -        ctls.each {|ctl|
 -          next if ctl.level != Socket::SOL_SOCKET || ctl.type != Socket::SCM_RIGHTS
 -          recv_io_ary.concat ctl.unix_rights
 -        }
 -        assert_equal(send_io_ary.length, recv_io_ary.length)
 -        send_io_ary.length.times {|i|
 -          assert_not_equal(send_io_ary[i].fileno, recv_io_ary[i].fileno)
 -          assert(File.identical?(send_io_ary[i], recv_io_ary[i]))
 -        }
 -      }
 -    }
 -  ensure
 -    io_ary.each {|io| io.close if !io.closed? }
 +    fd_passing_test do |s, ios|
 +      ancdata = Socket::AncillaryData.unix_rights(*ios)
 +      s.sendmsg("\0", 0, nil, ancdata)
 +    end
 +  end
 +
 +  def test_fd_passing_n3
 +    fd_passing_test do |s, ios|
 +      s.sendmsg("\0", 0, nil, scm_rights: ios.map(&:fileno))
 +    end
 +  end
 +
 +  def test_fd_passing_n4
 +    fd_passing_test do |s, ios|
 +      s.sendmsg("\0", 0, nil, scm_rights: ios)
 +    end
    end
  
 -  def test_sendmsg
 +  def sendmsg_test
      return if !defined?(Socket::SCM_RIGHTS)
      IO.pipe {|r1, w|
        UNIXSocket.pair {|s1, s2|
          begin
 -          ret = s1.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS, [r1.fileno].pack("i!")])
 +          ret = yield(s1, r1)
          rescue NotImplementedError
            return
          end
 @@ -122,6 +111,24 @@ class TestSocket_UNIXSocket < Test::Unit::TestCase
      }
    end
  
 +  def test_sendmsg_1
 +    sendmsg_test do |s, r|
 +      s.sendmsg("\0", 0, nil, [Socket::SOL_SOCKET, Socket::SCM_RIGHTS, [r.fileno].pack("i!")])
 +    end
 +  end
 +
 +  def test_sendmsg_2
 +    sendmsg_test do |s, r|
 +      s.sendmsg("\0", 0, nil, scm_rights: r.fileno)
 +    end
 +  end
 +
 +  def test_sendmsg_3
 +    sendmsg_test do |s, r|
 +      s.sendmsg("\0", 0, nil, scm_rights: r)
 +    end
 +  end
 +
    def test_sendmsg_ancillarydata_int
      return if !defined?(Socket::SCM_RIGHTS)
      return if !defined?(Socket::AncillaryData)
 
 
 -- 
 --- 僕の前にBugはない。
 --- 僕の後ろにBugはできる。
     中田 伸悦
=end



-- 
http://bugs.ruby-lang.org/

In This Thread

Prev Next