sweetmr@SCT60A.SUNYCT.EDU (michael sweet) (05/11/91)
I'm trying to fork and exec run In a project I am working on, I want to create fork and exec another process with pipes to *both& * the standard iinpunput and output of the new process. This will eventually be used to control the ''tar' program via a GUI. Anyways, my test programs all seem s to create the 2 sets of pipes and fork the new program fine, but when it comes time oto actually *write* anything to the stdandard input of the second process, the first process sits there waiting to (presumably blocked.) The code goes something like this: pipe(stdin_pupipes); pipe(stdout_pipes); child+_pid = fork(); i if (child_pid == )0) { [ { dup2(stdin_pipes,[0]9, 0); dup2(stdout_pipes[1], 1); close(stdoutin_pipses[1]); close(stdout_pipes[09)]); execlp("/bin/sh", "sh", "-c", command, NULL); exit ((_exit(errno); };; if (child_id < 0) /* error forking! */ { close(all filespipes); } else { close(stdin_pipes[0]); close(stdout_pipes[1]); child_stdin = fredopnen(stdin_pipes[1], "ew"); child_stdout = gfdopen(stdin_pipesout_pipes[0]9, "r"); }; The test program calls this with another test program (which read s fromnumber s from the standard input and outputs x, x^2, x^3, an and x&^3), and then tries to send the numbers 1-10. It blocks on the first number, and the other test rpprogram progrduces no output wis waiting for a number from stdin. Any nonone have any ideas? This really has me stumped! -Mike
pj@sam.wpd.sgi.com (Paul Jackson) (05/12/91)
In article <4250.on.Fri,.10.May.91.20:53:34.EDT.@sct60a.sunyct.edu>, sweetmr@SCT60A.SUNYCT.EDU (michael sweet) writes: |> |> In a project I am working on, I want to fork and exec another process with |> pipes to *both* the standard input and output of the new process. Only call pipe once, not twice. The pipe(2) system call provides a pair of file descriptors, one good for reading and one for writing. And after duping them in the child to its stdin/stdout, close the file descriptors that you got from pipe. See an example, using the command "cat" instead of "tar" on page 130, Section 6.3, of Marc J. Rochind's "Advanced Unix Programming", Prentice Hall,1985. -- I won't rest till it's the best ... Software Production Engineer Paul Jackson (pj@wpd.sgi.com), x1373
corbettm@cutmcvax.cs.curtin.edu.au (Michael Corbett) (05/13/91)
sweetmr@SCT60A.SUNYCT.EDU (michael sweet) writes: >In a project I am working on, I want to fork and exec another process with >pipes to *both* the standard input and output of the new process. This will >eventually be used to control the 'tar' program via a GUI. Anyways, my test >programs seems to create the 2 sets of pipes and fork the new program fine, >but when it comes time to actually *write* anything to the standard input of >the second process, the first process sits there waiting t (presumably blocked.) I think the problem is in closing all your pipes. You see a pipe doesn't get passed an EOF until all accesses to the writing end of the pipe have been closed. Someone else had a similar problem on this news group recently. Basically, all you need to ensure is that all the file descriptors open to the write end of your pipes have been close. Obviously, you should also close the read end of the pipe. >The code goes something like this: I have made the changes to the code which I think should work. And marked the additional lines with a <------ > pipe(stdin_pipes); > pipe(stdout_pipes); > child_pid = fork(); > if (child_pid == 0) > { > dup2(stdin_pipes[0], 0); > dup2(stdout_pipes[1], 1); close(stdin_pipes[0]); <------ > close(stdin_pipes[1]); > close(stdout_pipes[0]); close(stdout_pipes[1]); <------ > execlp("/bin/sh", "sh", "-c", command, NULL); > exit(errno); > }; > if (child_id < 0) /* error forking! */ > { > close(all pipes); > } else > { > close(stdin_pipes[0]); > close(stdout_pipes[1]); > child_stdin = fdopen(stdin_pipes[1], "w"); > child_stdout = fdopen(stdout_pipes[0]"r"); close(stdin_pipes[1]); <------ close(stdout_pipes[0]); <------ .... [insert your code] <------ fclose(child_stdin); <------ fclose(child_stdout); <------ > }; [stuff deleted] >me stumped! hope it helps/works :-) -- >> Michael Corbett | "Last night, I thought my arm was << >> corbettm@anger.cipal.cs.curtin.edu.au | hanging out of bed. So I got out << >> corbettm@cutmcvax.cs.curtin.edu.au | to push it in." <<
sysmark@aurora.physics.utoronto.ca (Mark Bartelt) (05/13/91)
Michael Sweet writes: | In a project I am working on, I want to fork and exec another process with | pipes to *both* the standard input and output of the new process. | [ ... ] my test | program seems to create the 2 sets of pipes and fork the new program fine, | but when it comes time to actually *write* anything to the standard input of | the second process, the first process sits there waiting (presumably blocked.) A couple days ago rec.humor.funny reprinted something from (I think) The Guardian, containing some entries from a "programming is like sex because" (or was it the reverse?) contest. Somehow, the problem of communicating bidirectionally with a child process seems like a good candidate: It's both a lot easier and a lot more fun once you've done it a few times :-) This sort of thing, while not terribly difficult, isn't exactly trivial, either. A couple of caveats follow, and a (working!) example is appended, but first a comment on Paul Jackson's followup: | Only call pipe once, not twice. The pipe(2) system call provides | a pair of file descriptors, one good for reading and one for writing. | And after duping them in the child to its stdin/stdout, close the | file descriptors that you got from pipe. I suspect that Paul didn't read Michael's original posting closely. Note that Michael wants to send stuff to the child's stdin *and* receive stuff from the child's stdout. I can't conceive of any way to do this without using two pipes: One for the parent to send stuff to the child, another for the child to send stuff to the parent. If this can be accomplished by using a single pipe, I'd be interested in seeing how it's done. Anyway, several things to watch out for ... (1) Be certain that all unnecessary file descriptors are closed. Michael's example doesn't close stdin_pipes[0] and stdout_pipes[1] in the child. Note that they're not needed, since the child has dup2()ed them to file descriptors 0 and 1. It's not clear that failing to close them is actually the source of the problem here, but it can't hurt. (2) If the child is producing output in response to input that it receives from the parent, the parent had better read it. If it doesn't, the "upward" (child-to-parent) pipe will eventually constipate, and the child will block. Because the child is blocked waiting for that pipe to partially drain, the "downward" (parent-to-child) pipe will also constipate as the parent writes more stuff into that pipe, so the parent will block as well, and the whole business will hang. (3) If you're using stdio routines instead of read()/write() to move data through the pipes, be sure that the streams are unbuffered (line buffered may be good enough), or else one process will block waiting for data that the other has written, but which hasn't actually gotten shoved into the back end of the pipe since it's sitting instead in a partially-filled stdio buffer. (4) If the child is one which won't produce any output until it's seen all its input (consider, for example, the child doing an exec() of /usr/bin/sort just after the fork() and dup2()s), the parent should be sure to call close (or fclose(), whichever is appropriate) on the back end of the "downward" pipe, so that the child will see EOF on stdin. (5) If the child produces output on stdout, but perhaps not necessarily in a predictable fashion (the example below produces exactly one line of output for every line of input; spawning "sort" means that the child will produce no output until it's seen all its input), the parent will have to sniff at the "upward" pipe in a non-blocking way, in order to see whether there's anything there that needs to be read (to keep the "upward" pipe from constipating). You can do this sort of thing in a BSD environment by using FIONREAD; I'm not certain of the best way to do this under IRIX. Anyway, the example follows. Note that these are really only templates, and shouldn't be construed as bullet-proof an any sense: No checking for error returns from pipe() and fork(), for example; or for line-too-long situations when calling fgets(); or several other things that really ought to be done. Nonetheless, they should be useful as a vague outline of how to do it ... Mark Bartelt 416/978-5619 Canadian Institute for mark@cita.toronto.edu Theoretical Astrophysics mark@cita.utoronto.ca ------------------------------------------------------------------------------ /* * Parent process -- reads a line from stdin, sends it to * child, reads a line back from child, sends it to stdout */ #include <stdio.h> #define PCR p_to_c[0] /* parent-to-child pipe, read fd */ #define PCW p_to_c[1] /* parent-to-child pipe, write fd */ #define CPR c_to_p[0] /* child-to-parent pipe, read fd */ #define CPW c_to_p[1] /* child-to-parent pipe, write fd */ #define BUFSIZE 200 main() { int p_to_c[2]; int c_to_p[2]; int fk; FILE * to_child; FILE * from_child; char buf[BUFSIZE]; pipe(p_to_c); pipe(c_to_p); if ( (fk=fork()) == 0 ) { /* Child process only */ dup2(PCR,0); dup2(CPW,1); } close(PCR); /* Both processes */ close(CPW); if ( fk == 0 ) { /* Child process only */ close(PCW); close(CPR); execl("./sqrt","sqrt",0); fprintf(stderr,"sqrt exec failure\\n"); exit(-1); } to_child = fdopen(PCW,"w"); from_child = fdopen(CPR,"r"); setlinebuf(to_child); while ( fgets(buf,BUFSIZE,stdin) != NULL ) { fputs(buf,to_child); fgets(buf,BUFSIZE,from_child); fputs(buf,stdout); } close(PCW); close(CPR); } ------------------------------------------------------------------------------ /* * Child process -- reads a line from stdin, writes a line to * stdout reporting square root of first numeric arg on input */ #include <stdio.h> #include <math.h> #define BUFSIZE 200 char buf[BUFSIZE]; main() { double val; setlinebuf(stdout); while ( fgets(buf,BUFSIZE,stdin) != NULL ) { sscanf(buf,"%lf",&val); printf("%.4f\n",sqrt(val)); } }
sysmark@aurora.physics.utoronto.ca (Mark Bartelt) (05/13/91)
Whoops, a minor bit of sloppiness in my previous posting. At the very end
of the example parent process, I had
close(PCW);
close(CPR);
The example works as originally written, but it's really more proper to
call fclose() with the appropriate arguments, since we've done an fdopen()
on the file descriptors. Also, a comment got dropped inadvertently. So
the last section of the example parent process should look like:
to_child = fdopen(PCW,"w"); /* Parent process only */
from_child = fdopen(CPR,"r");
setlinebuf(to_child);
while ( fgets(buf,BUFSIZE,stdin) != NULL ) {
fputs(buf,to_child);
fgets(buf,BUFSIZE,from_child);
fputs(buf,stdout);
}
fclose(to_child);
fclose(from_child);
There are undoubtedly other minor screwups in the example as well ...
Mark Bartelt 416/978-5619
Canadian Institute for mark@cita.toronto.edu
Theoretical Astrophysics mark@cita.utoronto.ca