From: Eric Wong Date: 2012-09-07T02:32:59+09:00 Subject: Re: Passing Sockets (or other FDs) to target of Exec() Sean Slavik wrote: > I've been playing around with an issue that is giving me a bit of a > headache and I'm hoping someone can point me in a more useful > direction... > > On a standard startup, I have two variables: > > $nCS = TCPServer.new(HOST, PORT) > $connected = Array.new You'll need: $nCS.close_on_exec = false if $nCS.respond_to?(:close_on_exec=) for compatibility with Ruby 2.0.0 in the future Also, you probably don't need $ global variables > As users connect to $nCS and login, their connection (and user data) is > passed to $connected. > > I have a command that triggers a function "copyover" that is intended to > hot-reboot the server, hopefully maintaining the connections. While I've > tried writing the connections to a file and reloading them that way, the > closest I seem to have gotten is the following: > > exec "./main.rb", "--copyover", *$nCS, *$connected You can't just pass an object over the command-line, the file descriptor number is all you can pass: exec "./main.rb", "--copyover", $nCS.fileno.to_s, *$connected > Then passing: > > $nCS = ARGV[1] You need to convert the file descriptor number back to an object: $nCS = TCPServer.for_fd(ARGV[1].to_i) > $connected = ARGV[2] I think you need to do something similar for $connected, too, you can't pass over a Ruby array, either, so maybe something along these lines: # in the original, stringify a list of integer file descriptors: # (you'll get something like "4,5,6,7,8") connected_args = $connected.map do |sock| sock.close_on_exec = false if sock.respond_to?(:close_on_exec=) sock.fileno.to_s end.join(",") exec "./main.rb", "--copyover", $nCS.fileno.to_s, connected_args # in the exec-ed version: # parse the stringified list of descriptors: connected_args = ARGV[2] connected = connected_args.split(/,/).map do |sockfd| # I'm assuming TCPSocket is the correct class based on your use of # TCPServer, but any subclass of IO will support for_fd. TCPSocket.for_fd(sock_fd) end Unrelated to your original problem, you should also check out the optparse or similar libraries for parsing command-line options. They can even handle the split(/,/) part for you. > Since Exec replaces the process, it should maintain the files until the > process is destroyed, correct? Yes. However, Ruby 2.0.0 will set close_on_exec=true by default, so you need to set close_on_exec=false to future-proof your code. > However, this still tells me "ERROR: Caught error in Client Thread: > Socket is not connected" > > Can anyone point me in the right direction on this? I'm stumped! Hopefully I didn't make any typos or mistakes in my examples :> But the basic idea is you can't pass Ruby objects over the command-line (nor can you marshall IO objects to strings). Passing stringified file descriptor numbers is required in your case. You can also use UNIX sockets with send_io/recv_io, but you still need to know the class you're recv_io-ing.