[comp.unix.programmer] generate EOF on socketpair?

rpotter@grip.cis.upenn.edu (Robert Potter) (04/01/91)

I am trying to write a replacement for popen() that returns a socket for
two-way communication with some program's standard I/O.  My implementation is
along these lines:

int process_open(...)
{
    int sock[2];

    socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
    if (vfork() == 0) {		/* are we the child? */
	dup2(sock[0], 0);
	dup2(sock[0], 1);
	execv(...);
    }
    return sock[1];
}

My problem:  how do I indicate to the program that it has reached EOF on its
standard input?  I can't just close the socket, since I want to keep the socket
open for reading.  Do I have to set up two separate sockets (or pipes)?

	-Robert

jik@athena.mit.edu (Jonathan I. Kamens) (04/04/91)

In article <40164@netnews.upenn.edu>, rpotter@grip.cis.upenn.edu (Robert Potter) writes:
|> My problem:  how do I indicate to the program that it has reached EOF on its
|> standard input?  I can't just close the socket, since I want to keep the socket
|> open for reading.  Do I have to set up two separate sockets (or pipes)?

  You could create two pipe()s, one for input to the sub-process and one for
output from it, and close the input pipe in the parent when you are done
sending input to the child.  I'm sure that'll work, and I've done it before.

  Alternatively, you could use the shutdown(2) system call to shutdown sends
on the socket in the parent, to signify that you won't be doing any more
writing to it.  I'm not sure this'll work.  So I guess I'll write a program to
test it.

  First, the pipe version (with most error-checking omitted to conserve space):

	#include <stdio.h>

	main()
	{
	     int tochild[2], fromchild[2];
	     char buf[3];
	     
	     pipe(tochild);
	     pipe(fromchild);
	     
	     if (! fork()) {
		  /* child */
		  close(tochild[1]);
		  close(fromchild[0]);
		  dup2(tochild[0], 0); /* over stdin */
		  dup2(fromchild[1], 1); /* over stdout */
		  execl("/usr/bin/tr", "tr", "[A-Z]", "[a-z]", 0);
		  fprintf(stderr, "Exec of /usr/bin/tr failed!\n");
		  exit(1);
		  /* NOTREACHED */
	     }
	     else {
		  /* parent */
		  close(tochild[0]);
		  close(fromchild[1]);
		  fputs("Sending ABC\n", stdout);
		  write(tochild[1], "ABC", 3);
		  close(tochild[1]);
		  read(fromchild[0], buf, 3);
		  printf("Received %3s\n", buf);
		  exit(0);
	     }
	}

And here's what happens when I run it:

	% ./a.out
	Sending ABC
	Received abc

  Now, the socketpair version:

	#include <stdio.h>
	#include <sys/types.h>
	#include <sys/socket.h>

	main()
	{
	     int sockets[2];
	     char buf[3];
	     
	     socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
	     
	     if (! fork()) {
		  /* child */
		  close(sockets[0]);
		  dup2(sockets[1], 0);
		  dup2(sockets[1], 1);
		  execl("/usr/bin/tr", "tr", "[A-Z]", "[a-z]", 0);
		  fprintf(stderr, "Exec of /usr/bin/tr failed!\n");
		  exit(1);
		  /* NOTREACHED */
	     }
	     else {
		  /* parent */
		  close(sockets[1]);
		  fputs("Sending ABC\n", stdout);
		  write(sockets[0], "ABC", 3);
		  shutdown(sockets[0], 1);
		  read(sockets[0], buf, 3);
		  printf("Received %3s\n", buf);
		  exit(0);
	     }
	}

And here's what happens when I run it:

	% ./a.out
	Sending ABC

And it just stays there until I interrupt it.  In other words, it looks to me
like the shutdown() *doesn't* work.  I can't think of any other way to achieve
what you're trying to achive.  My recommendation would be to use to pipe()s or
socketpair()s.

  I hope someone can correct me and tell me how to get this to work, because
now I'm interested in it :-).

-- 
Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik@Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8085			      Home: 617-782-0710