Passing File Descriptors?

Is there a ruby way to pass file descriptors to separate processes?
I'm thinking of interfacing with a C program, which uses the
SCM_RIGHTS cmsg over a unix-domain socket ... is this idiom
implemented in Ruby? How would I marshall an IO object into (and out
of) the integer file descriptor, or does this "just work"?

Thanks!
-Mike

Untested: You can determine the descriptor id via io.fileno. The you can pass that to the child process via main's args when you exec after fork. Then the child should be able to reuse that descriptor. Alternatively you can of course transport the descriptor id via an environment variable.

Kind regards

  robert

···

On 19.03.2007 17:43, Worky Workerson wrote:

Is there a ruby way to pass file descriptors to separate processes?
I'm thinking of interfacing with a C program, which uses the
SCM_RIGHTS cmsg over a unix-domain socket ... is this idiom
implemented in Ruby? How would I marshall an IO object into (and out
of) the integer file descriptor, or does this "just work"?

After a little investigation, this turned out to be easier than
I thought. The following code was tested on Mac OS X with ruby 1.8.5.
There are three files below, don't cut and paste the whole thing:

    socketpair.rb # example with parent/child

    server.rb # example with unrelated processes
    client.rb

Socketpair.rb and client.rb both take a single argument, the name
of the data file to open.

Start server.rb before running client.rb.

If you try this on something other than Mac OS X ruby 1.8.5, let us
know how it works.

Gary Wright

$ cat socketpair.rb
# Scenario 1: parent and child related via fork
require 'socket'

read, write = UNIXSocket.pair

puts "parent end: #{read.inspect}, #{read.fileno}"
puts "child end: #{write.inspect}, #{write.fileno}"

pid = fork {
   file = File.open(ARGV[0])
   puts "child is sending: #{file.inspect}/#{file.fileno} connected to #{ARGV[0]}"
   write.send_io(file)
   exit
}

thefile = read.recv_io
puts "parent received: #{thefile.inspect}/#{thefile.fileno}, contents:"
puts thefile.read

# end of scenario 1

$ cat server.rb
require 'socket'

serv = UNIXServer.new("/tmp/server")
p "server listening for connections: #{serv.inspect}"

client = serv.accept
p "received connection: #{client.inspect}"

clientfile = client.recv_io
puts "parent received fd: #{clientfile.fileno}, contents:"

puts clientfile.read
# end of server.rb

$ cat client.rb
require 'socket'

server = UNIXSocket.new("/tmp/server")
puts "connected to : #{server.inspect}"

fd = File.open(ARGV[0])
puts "child is sending fd: #{fd.fileno} connected to #{ARGV[0]}"

server.send_io(fd) # we are *not* sending the data!

exit

···

On Mar 19, 2007, at 12:43 PM, Worky Workerson wrote:

Is there a ruby way to pass file descriptors to separate processes?
I'm thinking of interfacing with a C program, which uses the
SCM_RIGHTS cmsg over a unix-domain socket ... is this idiom
implemented in Ruby? How would I marshall an IO object into (and out
of) the integer file descriptor, or does this "just work"?

In case folks were looking for a use case.

A client makes a request to a server which opens the file
and sends the opened file descriptor back to the client.
Presumably the server process has special permissions to allow it
access to the files that the client doesn't have. The server
can also implement any additional security controls.

The client gets to read and write a file without having had
permission to open the file directly.

This only works via Unix Domain sockets and so it only
works between processes on the same machine. It won't work
across a network.

Gary Wright

···

On Mar 19, 2007, at 12:43 PM, Worky Workerson wrote:

Is there a ruby way to pass file descriptors to separate processes?
I'm thinking of interfacing with a C program, which uses the
SCM_RIGHTS cmsg over a unix-domain socket ... is this idiom
implemented in Ruby? How would I marshall an IO object into (and out
of) the integer file descriptor, or does this "just work"?

> Is there a ruby way to pass file descriptors to separate processes?
> I'm thinking of interfacing with a C program, which uses the
> SCM_RIGHTS cmsg over a unix-domain socket ... is this idiom
> implemented in Ruby? How would I marshall an IO object into (and out
> of) the integer file descriptor, or does this "just work"?

After a little investigation, this turned out to be easier than
I thought.

...

clientfile = client.recv_io
puts "parent received fd: #{clientfile.fileno}, contents:"

Sweet, thanks! I guess I had missed the {send,recv}_io methods.
They're not in the Ruby book ... guess I should have done to the
online docs. Ruby does make this trivial ... even works with my
existing C program.

-Mike

···

On 3/19/07, Gary Wright <gwtmp01@mac.com> wrote:

On Mar 19, 2007, at 12:43 PM, Worky Workerson wrote: