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