Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SocketError when connecting to Unix datagram socket #6504

Open
mohamedhafez opened this issue Dec 22, 2020 · 6 comments
Open

SocketError when connecting to Unix datagram socket #6504

mohamedhafez opened this issue Dec 22, 2020 · 6 comments
Assignees

Comments

@mohamedhafez
Copy link
Contributor

when I run the simple script below, it works in MRI, but raises SocketError (No message available) in JRuby 9.2.14.0 on Ubuntu 18.04. It would be awesome to get this working so I could have my JRuby daemons communicate with systemd

Addrinfo.unix("/run/systemd/notify", :DGRAM).connect do |s|
  s.close_on_exec = true
  s.write("READY=1")
end

here's the stack trace:

Traceback (most recent call last):
        9: from /home/web/.rvm/rubies/jruby-9.2.14.0/bin/irb:13:in `<main>'
        8: from org/jruby/RubyKernel.java:1189:in `catch'
        7: from org/jruby/RubyKernel.java:1189:in `catch'
        6: from org/jruby/RubyKernel.java:1442:in `loop'
        5: from org/jruby/RubyKernel.java:1048:in `eval'
        4: from (irb):2:in `evaluate'
        3: from /home/web/.rvm/rubies/jruby-9.2.14.0/lib/ruby/stdlib/socket.rb:139:in `connect'
        2: from /home/web/.rvm/rubies/jruby-9.2.14.0/lib/ruby/stdlib/socket.rb:66:in `connect_internal'
        1: from org/jruby/ext/socket/RubySocket.java:215:in `connect'
SocketError (No message available)
@mohamedhafez mohamedhafez changed the title SocketError when sending Unix socket datagrams SocketError when connecting to Unix datagram socket Dec 22, 2020
@headius headius added this to the JRuby 9.3.0.0 milestone Mar 6, 2021
@headius headius self-assigned this Mar 6, 2021
@headius
Copy link
Member

headius commented Mar 8, 2021

Could you come up with a reproduction that does not depend on systemd?

This may work better on master (9.3) which has better support for Addrinfo.

@mohamedhafez
Copy link
Contributor Author

I tried using master, but got the same error, gives this stack trace:

Traceback (most recent call last):
       11: from /home/web/.rvm/rubies/jruby-head/bin/irb:23:in `<main>'
       10: from org/jruby/RubyKernel.java:1049:in `load'
        9: from /home/web/.rvm/rubies/jruby-head/lib/ruby/gems/shared/gems/irb-1.0.0/exe/irb:11:in `<main>'
        8: from org/jruby/RubyKernel.java:1234:in `catch'
        7: from org/jruby/RubyKernel.java:1234:in `catch'
        6: from org/jruby/RubyKernel.java:1504:in `loop'
        5: from org/jruby/RubyKernel.java:1088:in `eval'
        4: from (irb):3:in `evaluate'
        3: from /home/web/.rvm/rubies/jruby-head/lib/ruby/stdlib/socket.rb:139:in `connect'
        2: from /home/web/.rvm/rubies/jruby-head/lib/ruby/stdlib/socket.rb:66:in `connect_internal'
        1: from org/jruby/ext/socket/RubySocket.java:207:in `connect'
SocketError (No message available)

Been banging my head against the wall trying to get netcat to listen on a unix datagram socket for half an hour now, for some reason nc -lvUu /tmp/mysock and nc -lvUus /tmp/mysock aren't doing what I expect.

The systemd socket /run/systemd/notify is always just there listening as a unix datagram socket on Ubuntu and anyone can send to it, sorry I can't seem to come up with a way to reproduce this more generally

@mohamedhafez
Copy link
Contributor Author

It looks like on Ubuntu, /dev/log is also a listening unix datagram socket that has nothing to do with systemd, and the same issue occurs there. The following code works on MRI, but not JRuby master (it fails with the same stack trace as above):

require 'socket'
Addrinfo.unix("/dev/log", :DGRAM).connect do |s|
  s.close_on_exec = true
  s.write("READY=1")
end

@headius
Copy link
Member

headius commented Mar 8, 2021

The problem is that we assume dgram sockets are not UNIX sockets and always create an IP DatagramSocket for them here:

protected ChannelFD initChannelFD(Ruby runtime) {
try {
Channel channel;
switch (soType) {
case SOCK_STREAM:
if ( soProtocolFamily == ProtocolFamily.PF_UNIX ||
soProtocolFamily == ProtocolFamily.PF_LOCAL ) {
channel = UnixSocketChannel.open();
}
else if ( soProtocolFamily == ProtocolFamily.PF_INET ||
soProtocolFamily == ProtocolFamily.PF_INET6 ||
soProtocolFamily == ProtocolFamily.PF_UNSPEC ) {
channel = SocketChannel.open();
}
else {
throw runtime.newArgumentError("unsupported protocol family `" + soProtocolFamily + "'");
}
break;
case SOCK_DGRAM:
channel = DatagramChannel.open();
break;
default:
throw runtime.newArgumentError("unsupported socket type `" + soType + "'");
}
return newChannelFD(runtime, channel);
}
catch (IOException e) {
throw sockerr(runtime, "initialize: " + e.toString(), e);
}
}

We need to have similar family-checking logic in the dgram path here.

@headius
Copy link
Member

headius commented Mar 8, 2021

I have added the right pieces to do datagram things with a UnixDatagramChannel but I am not sure the actual channel is doing the right thing. I can connect now but when writing it complains about having no destination. It seems the datagram version of the Unix socket channel is not passing address, maybe?

@headius headius modified the milestones: JRuby 9.3.0.0, JRuby 9.4.0.0 Aug 2, 2021
@headius headius modified the milestones: JRuby 9.4.0.0, JRuby 9.5.0.0 Nov 16, 2022
mohamedhafez added a commit to mohamedhafez/puma that referenced this issue Feb 12, 2023
The systemd integration will fail for JRuby at https://github.com/puma/puma/blob/e3d5794a7ebe47577ced4d4dfdd6a6cc969ded01/lib/puma/sd_notify.rb#L140, because JRuby doesn't support UNIX datagram sockets yet, and won't for a while. See jruby/jruby#6504. So turning it off here, so that JRuby users can integrate with systemd on their own if they wish without errors.
nateberkopec pushed a commit to puma/puma that referenced this issue Feb 24, 2023
* Don't use the systemd plugin on JRuby

The systemd integration will fail for JRuby at https://github.com/puma/puma/blob/e3d5794a7ebe47577ced4d4dfdd6a6cc969ded01/lib/puma/sd_notify.rb#L140, because JRuby doesn't support UNIX datagram sockets yet, and won't for a while. See jruby/jruby#6504. So turning it off here, so that JRuby users can integrate with systemd on their own if they wish without errors.

* Improved skipping systemd for JRuby

Added a comment to explain the situation, and used Puma's JRuby detection method instead of re-coding it.

* test that systemd plugin isn't loaded on JRuby

* rename to test_plugin_systemd_jruby.rb, fix lint

* rename test to test_systemd_plugin_not_loaded

* make and use skip_unless :linux
nateberkopec pushed a commit to puma/puma that referenced this issue Feb 28, 2023
* Don't use the systemd plugin on JRuby

The systemd integration will fail for JRuby at https://github.com/puma/puma/blob/e3d5794a7ebe47577ced4d4dfdd6a6cc969ded01/lib/puma/sd_notify.rb#L140, because JRuby doesn't support UNIX datagram sockets yet, and won't for a while. See jruby/jruby#6504. So turning it off here, so that JRuby users can integrate with systemd on their own if they wish without errors.

* Improved skipping systemd for JRuby

Added a comment to explain the situation, and used Puma's JRuby detection method instead of re-coding it.

* test that systemd plugin isn't loaded on JRuby

* rename to test_plugin_systemd_jruby.rb, fix lint

* rename test to test_systemd_plugin_not_loaded

* make and use skip_unless :linux
@ccutrer
Copy link
Contributor

ccutrer commented Dec 20, 2023

I'm working around this for presumably the same use case for now like this:

def jnr
  Java::Jnr
end

module SdNotify
  def self.notify(state, unset_env = false)
    sock = ENV["NOTIFY_SOCKET"]

    return nil unless sock

    ENV.delete("NOTIFY_SOCKET") if unset_env

    begin
      addr = jnr.unixsocket.UnixSocketAddress.new(sock)
      channel = jnr.unixsocket.UnixDatagramChannel.open

      channel.send(java.nio.ByteBuffer.wrap(state.to_java_bytes), addr)
    rescue StandardError => e
      raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
    ensure
      channel&.close
    end
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants