[comp.protocols.misc] SUMMARY - Re: Pass an open socket to another process???

bill@tifsil.UUCP (Bill Stoltz) (02/11/88)

Thanks to everyone who sent me mail on the passing an open socket
request.

A quick summary is that this capability does exist on 4.[23] BSD
systems, and in a more restricted environment on AT&T V.3.
We are running Ultrix 2.0 (a 4.[23] derivative) here, and I have
tested this capability and it does work.  I did not hear any
thing from anyone else (no VMS or Xenix systems) so I don't know if 
this will work on any of these system. (Maybe someone can post 
something on whether these systems support this type of system call).

The following is a summary of the responses that I received during
the last week. Again thanks to everyone who sent in a response.
At the end I will give an account of the day and a half I spent 
getting a test case working.

Most of the responses were to use sendmsg/recvmsg in 4.[23] BSD.
These calls are defined in the recv(2) and send(2) man pages.
Thanks to the explanation by several people, I was informed that the
accessrights field is actually an array of file descriptors.

Doug McCallum <ti-csl!im4u!ut-sally!violet.ico.isc.com!dougm>, explained it
the best when he said: 

> On a 4.2/4.3 BSD system, there is the concept of access rights
> passing.  It only works in the AF_UNIX domain but could be made to
> work with other protocols given a little work.  Basically, it lets you
> pass any file descriptor to another process.  It uses the AF_UNIX
> SOCK_DGRAM mechanism and is an array of int's in the structure used in
> recvmsg/sendmsg.  I've used this as a way to implement a file password
> server to allow opening a restricted access file if you knew the
> password.
> 
> It works like a "dup" across process boundaries.

Doug went on to say that this capability is a standard feature of AT&T V.3. 

> On V.3, the Streams mechanism allows a process to send a stream
> descriptor over a connected stream pipe (a stream pipe is a stream
> that connects two processes on the same machine).  Since Streams is
> protocol independent, you could implement any protocol underneath and
> have the same capability.  Note that Streams by itself isn't a
> protocol and V.3 may come with Streams but no protocols.  V.3 does
> have a stream pipe which is very simple minded and appears to exist
> solely as a way to get other streams descriptors to an unrelated
> process.
> 
> Streams is the abstraction that AT&T is using for future network
> support.
> 
> The big difference between the two mechanisms is that BSD allows any
> file descriptor and V.3 only allows streams file descriptors.

A couple people also mentioned the "inetd" or Sun inet program.  They
felt that this also provided this type of cabability.  I did not spend
much time looking into this option,  The Ultrix 2.0 inetd did not
use the sendmsg/recvmsg calls so I stopped looking at it.

Once I received all this information, I decided that I could put together 
a simple test case in a matter of hours.  Well, I found a few stumbling 
blocks along the way that I would like to let everyone know about.  Hopefully
I can help some people and save them some time.

First the send(2) and recv(2) man pages that describe the sendmsg/recvmsg
calls are VERY cryptic.  They briefly mention the parameters by name but
give you very little information on what to put in them.

To get started I took an already working program and hacked it into 
2 programs.  The first process was the main server and the second process
was basically one of the subroutines from the old server that only
sent back a datetime string.

At this point I now had to add a socket(2) and a bind(2) call to both
processes.  The socket used AF_UNIX and SOCK_DGRAM parameters.  The bind
call was a little tricky.  You have to give bind a valid Unix file name.
So, I set up both processes the same way, except for different file names.


	struct	sockaddr_un sktname;

	sc = socket(AF_UNIX, SOCK_DGRAM, 0);
	sktname.sun_family = AF_UNIX;
	unlink(path);
	strcpy(sktname.sun_path, "/tmp/test.serv");
	err = bind(sc, &sktname, sizeof(sktname));

The bind actually creates a file on the system.  The unlink is there to 
remove the socket if it already exists, so the bind can create a new one.
I don't know if this is necessary, but I noticed this in the elcsd(8) program.

The next step was to modify the server process to send the file descriptor
to the datetime process, instead of calling this subroutine to send back
the response.  The following is the code I inserted to send over the 
connection.

struct	msghdr	sktmsg;
struct	sockaddr_un sktname;
struct	iovec 	iov;
	char	buf[80];
	int	rights[1];
	int	socket2pass;

	sktname.sun_family = AF_UNIX;
	strcpy(sktname.sun_path, "/tmp/test.dt");
	iov.iov_base = buf;
	iov.iov_len = 0;
	rights[0] = socket2pass;

	sktmsg.msg_name = (caddr_t) &sktname;
	sktmsg.msg_namelen = sizeof(sktname);
	sktmsg.msg_iov = &iov;
	sktmsg.msg_iovlen = 1;
	sktmsg.msg_accrights = rights;
	sktmsg.msg_accrightslen = sizeof(rights);

	sendmsg(sc, &sktmsg, 0);

Notice several things that are not clear in the man pages.  First, you must
pass in a sockaddr structure for the name of the socket to send this too,
and the size is the size of the structure, not the length of the string name.
If you just pass in the path name and length of the string you will get errors
like "No such file or directory".  Second the iovlen is the number of iovec
structures passed in.  Also, on a send, you must provide at least one iovec
structure.  I also filled in the buffer address, but left the length at 0,
I have not tried it to see if a buffer is required, my guess is yes.

Last is the accrights.  I made the assumption that the rights worked like the
iovec structures, WRONG.  For this you give the total number of bytes
you want the kernel to read in.  You must also be sure that every value you
put into the rights array is a valid file descriptor for the sending process
or you will get back an EBADF error.

Once I got all the parameters straight it worked great.

I then changed the new datetime process to block on a recvmsg call waiting
for this new connection so I could send back the datetime string.
The following is what I added to the datetime program.


struct	iovec 	iov;
	int	rights[5];
struct	msghdr	sktmsg;
	char	buf[80];

	iov.iov_base = buf;
	iov.iov_len = sizeof(buf);
	sktmsg.msg_name = 0;
	sktmsg.msg_namelen = 0;
	sktmsg.msg_iov = iov;
	sktmsg.msg_iovlen = 1;
	sktmsg.msg_accrights = rights;
	sktmsg.msg_accrightslen = sizeof(rights);

	recvmsg(sc, &sktmsg, 0);

Since this was a test program I ignored the name on the receive side.
I setup the iovec buffer, even though I knew there was no data coming in.

The last step was to retrieve the new file descriptor and issue the
send of the datetime string.  The file descriptor was now converted to
a new unused descriptor for the datetime process.  After sending out the
string, I just closed the new file descriptor and went back to wait for the
next message.  The server also closed the connection right after the
sendmsg call.

	new_fd = rights[0];
	dt_send(new_fd);
	close(new_fd);


This has been a rather long summary of the responses I got and the
work that I have done.  I hope this helps some of the people that asked 
me for a response.

I feel very comfortable with this solution.  I am recommending to the
other people in our group to use the sendmsg/recvmsg calls as a way
to implement the passing of a socket to a non-related process.  I hope 
this will help someone, I know it has helped me.

Thanks again for the responses.

bill

-----------
Bill Stoltz
Texas Instruments
Process Automation Center
P.O. Box 655012, M/S 3635
Dallas, TX 75243

UUCP: 	{uiucdcs!convex!smu, {rice, sun!texsun}!ti-csl}!tifsie!bill
DECNET: TIFSIE::bill
Voice:	(214) 995-2786