Posted to tcl by evilotto at Fri Feb 04 01:33:16 GMT 2011view pretty

#include "tcl.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>

/* 
 * sender:
 * xfd_send path $fd
 *
 * receiver:
 * set fdx [xfd_listen path]
 * set fd [xfd_get $fdx]
 */

Tcl_ObjCmdProc xfdSendCmd, xfdListenCmd, xfdGetCmd;

int xfdSendCmd(ClientData cd, Tcl_Interp *interp, int ac, Tcl_Obj *const av[]) {
    int sfd,l;
    char *path=Tcl_GetStringFromObj(av[1],&l);
    struct sockaddr_un sa;
    struct msghdr msg;
    struct cmsghdr *cmsg;
    int fds[1];
    char buf[CMSG_SPACE(sizeof(fds))];

    int s=socket(AF_UNIX, SOCK_DGRAM, 0);
    memset(&sa,0,sizeof(sa));
    sa.sun_family=AF_UNIX;
    strncpy(sa.sun_path,path,l);
    connect(s,(struct sockaddr *)&sa,sizeof(sa));

    Tcl_GetChannelHandle(Tcl_GetChannel(interp,Tcl_GetString(av[2]),NULL),TCL_READABLE,(ClientData *)&sfd);
    fds[0]=sfd;

    msg.msg_iov=NULL;
    msg.msg_iovlen=0;
    msg.msg_name=NULL;
    msg.msg_namelen=0;

    msg.msg_control=buf;
    msg.msg_controllen=sizeof(buf);
    cmsg=CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level=SOL_SOCKET;
    cmsg->cmsg_type=SCM_RIGHTS;
    cmsg->cmsg_len=CMSG_LEN(sizeof(int));
    memcpy(CMSG_DATA(cmsg), fds, sizeof(int));
    sendmsg(s,&msg,0);
    return TCL_OK;
}

int xfdListenCmd(ClientData cd, Tcl_Interp *interp, int ac, Tcl_Obj *const av[]) {
    int l;
    char *path=Tcl_GetStringFromObj(av[1],&l);
    struct sockaddr_un sa;

    int s=socket(AF_UNIX, SOCK_DGRAM, 0);
    sa.sun_family=AF_UNIX;
    strncpy(sa.sun_path,path,l);
    unlink(path);
    bind(s,(struct sockaddr *)&sa,sizeof(sa));
    /* listen(s,5); */
    Tcl_SetObjResult(interp, Tcl_NewIntObj(s));
    return TCL_OK;
}

int xfdGetCmd(ClientData cd, Tcl_Interp *interp, int ac, Tcl_Obj *const av[]) {
    int rfd;
    struct msghdr msg;
    struct cmsghdr *cmsg;
    int fds[1];
    char buf[CMSG_SPACE(sizeof(fds))];
    Tcl_Channel new;

    Tcl_GetIntFromObj(interp,av[1],&rfd);

    msg.msg_control=buf;
    msg.msg_controllen=sizeof(buf);
    recvmsg(rfd,&msg,0);

    cmsg=CMSG_FIRSTHDR(&msg);
    memcpy(fds,CMSG_DATA(cmsg), sizeof(int));
    fprintf(stderr,"fd %d\n",fds[0]);
    new=Tcl_MakeFileChannel((ClientData)fds[0],TCL_READABLE|TCL_WRITABLE);
    Tcl_RegisterChannel(interp,new);
    Tcl_SetResult(interp,Tcl_GetChannelName(new),TCL_VOLATILE);
    return TCL_OK;
}

int Xfd_Init(Tcl_Interp *interp) {
    Tcl_CreateObjCommand(interp,"xfd_send",xfdSendCmd,NULL,NULL);
    Tcl_CreateObjCommand(interp,"xfd_listen",xfdListenCmd,NULL,NULL);
    Tcl_CreateObjCommand(interp,"xfd_get",xfdGetCmd,NULL,NULL);
    return TCL_OK;
}


/*
# send an open fd to another process 

load xfd.so
set f [open /etc/passwd]

xfd_send /tmp/xfr $f

close $f

*/


/* 
# receive an open fd from another process 

load xfd.so
set fdx [xfd_listen /tmp/xfr]
set f [xfd_get $fdx]

puts [read $f]

close $f

*/