Passing file descriptors in Python?

Tres Seaver tseaver at starbase.neosoft.com
Wed Oct 6 22:27:49 EDT 1999


In article <37F85D6E.D50D1771 at compaq.com>,
Greg Ewing  <greg.ewing at compaq.com> wrote:
>Guido van Rossum wrote:
>> 
>> I would guess that creating a
>> server AF_UNIX socket in the parent and a client AF_UNIX socket in the
>> child, connecting the latter to the former, would allow you to do the
>> same thing.
>
>Try using an ordinary pipe first. On BSD-flavoured unices, pipes
>are often implemented as a pair of unix-domain sockets.
>
>You'll definitely need sendmsg() and recvmsg(), though, so
>you'll still have to write some C.
>
>Greg

Pipes would be less portable (SysV IPC uses another mechanism).  Here is a
rendition of the actual read/write FD code which works on my Linux boxes,
and should work on *BSD as well:

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

include <sys/socket.h>
#include <iovec.h>


typedef struct msghdr   MsgHdr;
typedef struct cmsghdr  CMsgHdr;
typedef struct iovec    IOVec;

typedef union                   /* force alignment of trailing data */
        {
            CMsgHdr     cm;
            char        control[ CMSG_SPACE( sizeof( int ) ) ];
        }               CMsgHdrUnion;


/*
 *  Pass a descriptor across a Unix domain stream socket.  Return number of
 *  bytes of main payload passed (caller should pass at least one byte, to
 *  allow peer to detect EOF cleanly).
 *
 *  See W. R. Stevens, Unix Network Programming (2nd ed), vol 1, p. 389.
 *
 *  In "pre-forked child" use case, sockFD may be one end of a socketpair();
 *  for unrelated servers, should be created by bind()/connect().
 */

ssize_t
write_fd( int       sockFD  /* AF_UNIX stream socket                        */
        , void*     payload /* Payload buffer                               */
        , size_t    nPL     /* Size of payload                              */
        , int       sendFD  /* Descriptor to be passed                      */
        )
{
    MsgHdr          msg;    /* sendmsg() buffer                             */
    IOVec           iov[1]; /* scatter-gather payload vector                */

    CMsgHdrUnion    controlX;

    CMsgHdr*        cmptr;

    msg.msg_name        = NULL;     /* name not used for stream sockets.    */
    msg.msg_namelen     = 0;

    iov[0].iov_base     = payload;  /* set up scatter-gather.               */
    iov[0].iov_len      = nPL;
    msg.msg_iov         = iov;
    msg.msg_iovlen      = 1;

    /*
     *  XXX:  Older Unixen may use msg_accrights instead of msg_control.
     */
    msg.msg_control     = controlX.control;
    msg.msg_controllen  = sizeof( controlX.control );

    cmptr               = CMSG_FIRSTHDR( &msg );
    cmptr->cmsg_len     = CMSG_LEN( sizeof( int ) );
    cmptr->cmsg_level   = SOL_SOCKET;
    cmptr->cmsg_type    = SCM_RIGHTS;

    *( (int*) CMSG_DATA( cmptr ) ) = sendFD;

    return( sendmsg( sockFD, &msg, 0 ) );
}


/*
 *  Receive a descriptor across a Unix domain stream socket.  Return number of
 *  bytes of main payload passed (should be least one byte, to distinguish
 *  EOF).
 *
 *  See W. R. Stevens, Unix Network Programming (2nd ed), vol 1, p. 387.
 *
 *  In "pre-forked child" use case, sockFD may be one end of a socketpair();
 *  for unrelated servers, should be created by bind()/connect().
 */
ssize_t
read_fd( int        sockFD  /* AF_UNIX socket over which to pass descriptor */
       , void*      payload /* Payload buffer                               */
       , size_t     maxPL   /* Size of payload buffer                       */
       , int*       recvFD  /* Address of descriptor being received         */
       )
{
    MsgHdr          msg;    /* recvmsg() message buffer                     */
    IOVec           iov[1]; /* Scatter-gather payload vector                */
    ssize_t         nPL;    /* Size of actual payload received              */
    CMsgHdrUnion    controlX;

    CMsgHdr*        cmptr;

    msg.msg_name        = NULL; /* name not used for stream sockets         */
    msg.msg_namelen     = 0;

    iov[0].iov_base     = payload;  /* set up scatter-gather                */
    iov[0].iov_len      = maxPL;
    msg.msg_iov         = iov;
    msg.msg_iovlen      = 1;

    /*
     *  XXX:  Older Unixen may use msg_accrights instead of msg_control.
     */
    msg.msg_control     = controlX.control;
    msg.msg_controllen  = sizeof( controlX.control );

    /*
     *  XXX:  Stevens does'nt pre-initialize cm, but RH 5.2 acts flaky
     *        without it.
     */
    cmptr               = CMSG_FIRSTHDR( &msg );
    cmptr->cmsg_len     = CMSG_LEN( sizeof( int ) );
    cmptr->cmsg_level   = SOL_SOCKET;
    cmptr->cmsg_type    = SCM_RIGHTS;

    *( (int*) CMSG_DATA( cmptr ) ) = -1;

    nPL = recvmsg( sockFD, &msg, 0 );

    if ( nPL <= 0 )
        return( nPL );

    /*
     *  XXX:    Might realloc, so reseat?
     */
    cmptr = CMSG_FIRSTHDR( &msg );

    if ( cmptr != NULL
      && cmptr->cmsg_len    == CMSG_LEN( sizeof( int ) )
      && cmptr->cmsg_level  == SOL_SOCKET
      && cmptr->cmsg_type   == SCM_RIGHTS
       )
        *recvFD = *( (int*) CMSG_DATA( cmptr ) );
    else
        *recvFD = -1;       /* descriptor was not passed */

    return( nPL );
}

-- 
---------------------------------------------------------------
Tres Seaver           tseaver at palladion.com       713-523-6582
Palladion Software    http://www.palladion.com




More information about the Python-list mailing list