Passing File Descriptors?


#1

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


#2

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”?

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


#3

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”?

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 W.

$ 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


#4

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 W.


#5

On 3/19/07, Gary W. removed_email_address@domain.invalid wrote:

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”?

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