Here's a little
creature of the night that sends a
file descriptor between two
Unix processes via a Unix domain
socket.
I'm the author (i.e. no copyright problems). It has been on my web site for a few years now and is provided here for your entertainment and enlightenment.
Being able to pass a file descriptor between two
processes is actually sometimes useful. For example, process A can prove to process B that process A is owned by root by opening a file that only root could open and then passing the file descriptor to process B.
Process B then uses the fstat system call to verify that the file associated with the passed file descriptor could only have been opened by root (e.g. owned by root with no rights to group or others.
In order to ensure that B doesn't clobber the file, the chosen file should be opened read-only.
/*
* Send a file descriptor through a Unix domain socket
*
* Unfortunately, this program is not exactly 100% portable.
*
* One strange wrinkle required by at least some versions of Linux
* is that you must also send a non-empty message (that's why the code
* below sends the string "hello" in addition to the file descriptor).
*
* This demonstration program opens the file "/etc/motd" in one process,
* sends the file descriptor to a second process which prints the contents
* of the file onto stdout.
*
* IMPORTANT: this is a DEMONSTRATION program. If it does something evil
* to your system or opens a hole in the space-time continuum then I don't
* want to hear about it!
*
* Author: Daniel Boulet (danny@obtuse.com)
*
* Copyright 1997, 1999 Daniel Boulet
* All rights reserved
*
* Permission is granted to use this software in any way you like as long
* as this entire comment is included in the source code.
*
* THIS SOFTWARE IS PROVIDED WITHOUT WARRANTEE OF ANY KIND WHAT-SO-EVER.
*/
/*
* Portability experience:
*
* OS Experience
* --------------------------------------------------------------------
* BSD/OS seems to work as-is
* Linux kernel 2.2 seems to work as-is
* Linux kernel 2.4 seems to work as-is
* AIX 4.3 seems to work as-is
* OpenBSD seems to work as-is
* Solaris 2.6 requires a few changes:
* - msg_control renamed as msg_accrights
* - msg_controllen renamed as msg_accrightslen
* - set msg_accrights to point at int containing fd
* - set msg_accrights to sizeof(int)
*
* Please let me know your experiences on other Unix variants.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <stdio.h>
main()
{
int pair[2];
struct msghdr mh;
struct cmsghdr cmh[2];
struct iovec iov;
char tbuf[100];
int fd;
int rlen;
pid_t pid;
char buffer[1024];
if ( socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0 ) {
perror("socketpair");
exit(1);
}
fflush(stdout);
fflush(stderr);
switch ( pid = fork() ) {
case 0: /* Child sends the file descriptor */
fd = open("/etc/motd",0);
if ( fd < 0 ) {
perror("open");
exit(0);
}
memset(&mh,0,sizeof(mh));
mh.msg_name = 0;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = (caddr_t)&cmh[0];
mh.msg_controllen = sizeof(cmh[0]) + sizeof(int);
mh.msg_flags = 0;
iov.iov_base = "hello";
iov.iov_len = strlen(iov.iov_base) + 1;
cmh[0].cmsg_level = SOL_SOCKET;
cmh[0].cmsg_type = SCM_RIGHTS;
cmh[0].cmsg_len = sizeof(cmh[0]) + sizeof(int);
*(int *)&cmh[1] = fd;
if ( sendmsg(pair[1],&mh,0) < 0 ) {
perror("sendmsg");
exit(1);
}
close(fd);
printf("child done\n");
fflush(stdout);
exit(0);
case -1: /* Oops! */
perror("fork");
exit(1);
default: /* Parent receives the file descriptor */
mh.msg_name = 0;
mh.msg_namelen = 0;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = (caddr_t)&cmh[0];
mh.msg_controllen = sizeof(cmh[0]) * 2;
iov.iov_base = tbuf;
iov.iov_len = sizeof(tbuf);
cmh[0].cmsg_len = sizeof(cmh[0]) + sizeof(int);
if ( (rlen = recvmsg(pair[0],&mh,0)) < 0 ) {
perror("recvmsg");
exit(1);
}
fd = *(int *)&cmh[1];
printf("rlen = %d msg_controllen = %d, cmsg_len = %d\n",
rlen,mh.msg_controllen,cmh[0].cmsg_len);
printf("cmsg_level = %d, cmsg_type = %d, fd = %d\n",
cmh[0].cmsg_level,cmh[0].cmsg_type,fd);
while ( (rlen = read(fd,buffer,sizeof(buffer))) > 0 ) {
write(1,buffer,rlen);
}
if ( rlen < 0 ) {
perror("read");
exit(1);
}
exit(0);
}
}