Re: Passing file descriptors

From: Josiah Carlson (jcarlson_at_uci.edu)
Date: 06/10/04


Date: Thu, 10 Jun 2004 00:25:07 -0700


> When I first saw the subject, I was about to point out that you had
> the wrong newsgroup: you want comp.lang.perl.misc
>
> I see you were talking about something else though.

No perl here.

> Last I checked, you have to use a Unix-domain socket to do this in
> Linux.

No dice...

     [jcarlson@dev jcarlson]$ python fdpass.py
     Traceback (most recent call last):
       File "fdpass.py", line 63, in ?
         ret = fcntl.ioctl(pRead, 1, s)
     IOError: [Errno 22] Invalid argument
     [Errno 14] Bad address
     [jcarlson@dev jcarlson]$

I've tested the data transfer and it works fine.

Using the below modified code, unix domain sockets do not work. Any
other ideas?

  - Josiah

-----cut here-----

#!/usr/pd/bin/python
#
# fdpass.py

#
# Example of passing an open filedescriptor with Python. Will only work
# on UNIX dialects that support the I_SENDFD and I_RECVFD ioctl() calls.
#

import fcntl, os, sys, struct, socket

#
# fork() off!
#

pid = os.fork()

port = '10001'

if pid != 0:
     # We're in the parent.

     s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
     s.bind(port)
     s.listen(1)
     pWrite, addr = s.accept()

     # Open a file for passing along to child. Use own source code,
     # which is guaranteed to exist. :)

     fileObj = open('./fdpass.py', 'r')

     # ioctl() will only pass raw filedescriptors. Find fd of fileObj.
     fd = fileObj.fileno()

     # Send to the child using ioctl().
     try:

         retval = fcntl.ioctl(pWrite, fcntl.I_SENDFD, fd)

         # Should probably check retval rather than just printing it. :)
         print "Parent ioctl() returned %d" % retval
     except Exception, e:
         print e

     # Wait for child to terminate, then exit.
     os.waitpid(pid, 0)
     sys.exit(0)

else:
     import time
     time.sleep(1)
     # We're in the child.

     pRead = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
     pRead.connect(port)

     # Create a string representing the strrecvfd structure that ioctl()
     # will return.
     s = struct.pack('iii', 0, 0, 0)

     # Receive filedescriptor. Will block until descriptor is sent.
     ret = fcntl.ioctl(pRead, fcntl.I_RECVFD, s)

     # Unpack the strrecvfd-structure that ioctl() should return.
     # fd is the filedescriptor, uid/gid the user/group id of the
     # sending stream.
     (fd, uid, gid) = struct.unpack('iii', ret)

     # Reopen the filedescriptor as a Python File-object.
     fileObj = os.fdopen(fd, 'r')

     # Example usage: Read file, print the first line.
     lines = fileObj.readlines()
     print lines[0],
     sys.exit(0)