Passing Sockets (or other FDs) to target of Exec()

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

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

Then passing:

$nCS = ARGV[1]
$connected = ARGV[2]

Since Exec replaces the process, it should maintain the files until the
process is destroyed, correct?

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!

Sean S. [email protected] 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.

That’s actually exactly what I was originally trying to do;
unfortunately I couldn’t figure out how to get ahold of just the file
descriptor!

All I have to do is reinitialize “connected” with the user data from the
file using the file descriptor as the link and everyone should be able
to maintain their connection during a hot reboot.

You’ve been a -huge- help, and saved me a lot of headaches. Thank you so
much!