From 427ee86b059228c3eb51c920d7d560432189d700 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 15 Jul 2015 08:19:55 +0000 Subject: emulate sd_listen_fds for systemd support systemd socket emulation shares FDs across execve, just like the built-in SIGUSR2 upgrade process in unicorn. Thus it is easy to support inheriting sockets from systemd. --- lib/yahns/server.rb | 16 +++++++++++++--- test/test_bin.rb | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/lib/yahns/server.rb b/lib/yahns/server.rb index b6663e1..01334ca 100644 --- a/lib/yahns/server.rb +++ b/lib/yahns/server.rb @@ -312,12 +312,22 @@ class Yahns::Server # :nodoc: # because that can completely break the non-blocking one. # Unfortunately, there is no one-off MSG_DONTWAIT-like flag for # accept4(2). - inherited = ENV['YAHNS_FD'].to_s.split(',').map! do |fd| - io = Socket.for_fd(fd.to_i) + inherited = ENV['YAHNS_FD'].to_s.split(',') + + # emulate sd_listen_fds() for systemd + sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS') + if sd_pid && sd_pid.to_i == $$ + # 3 = SD_LISTEN_FDS_START + inherited.concat((3...(3 + sd_fds.to_i)).map { |fd| Socket.for_fd(fd) }) + end + # to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS + + inherited.map! do |fd| + io = String === fd ? Socket.for_fd(fd.to_i) : fd opts = sock_opts(io) io = server_cast(io, opts) set_server_sockopt(io, opts) - @logger.info "inherited addr=#{sock_name(io)} fd=#{fd}" + @logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}" io end diff --git a/test/test_bin.rb b/test/test_bin.rb index 8944eb3..b778a0d 100644 --- a/test/test_bin.rb +++ b/test/test_bin.rb @@ -11,6 +11,40 @@ class TestBin < Testcase @cmd = %W(ruby -I lib bin/yahns) end + def test_listen_fd3 + return unless RUBY_VERSION.to_f > 2.3 # Fixed in ruby/trunk r51209, actually + @srv.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 0) + host, port = @srv.addr[3], @srv.addr[1] + + ru = tmpfile(%w(test_bin_daemon .ru)) + ru.write("require 'rack/lobster'; run Rack::Lobster.new\n") + cmd = %W(ruby -I lib bin/yahns-rackup + -E none -p #{port} -o #{host} #{ru.path}) + pid = fork do # emulate a systemd environment + env = { + 'LISTEN_PID' => $$.to_s, + 'LISTEN_FDS' => '1', + } + exec env, *cmd, 3 => @srv, err: @err.path + end + Net::HTTP.start(host, port) do |http| + req = Net::HTTP::Get.new("/") + res = http.request(req) + assert_equal 200, res.code.to_i + assert_equal "keep-alive", res["Connection"] + end + + assert_equal 1, @srv.getsockopt(:SOL_SOCKET, :SO_KEEPALIVE).int, + 'ensure the inheriting process applies TCP socket options' + ensure + if pid + Process.kill(:QUIT, pid) + _, status = Process.waitpid2(pid) + assert status.success?, status.inspect + end + ru.close! if ru + end + def test_bin_daemon_noworker_inherit bin_daemon(false, true) end -- cgit v1.2.3-24-ge0c7