kadie@uiucdcsb.cs.uiuc.edu (10/01/86)
I need to allow a number of very dissimilar programs to interact. Each of the programs reads from standard input and writes to standard output. I imagine that I want Unix pipes connecting each of the sub-programs to an executive program. Looking through the UNIX library I found a function that does almost what I want, namely popen. As in: FILE *pstrm; pstrm = popen("cat >response","w") This will start cat running such that the executive can make stream writes (fprint) to it. The problem is that I want to both fscan and fprint with each subprocesses. An alternative function is the pipe command. It allows read and write. Unfortunately I don't know how to connect the pipe(s) to standard input and standard output of the sub-program. Also this does not permit fscan and fprint. Any advice or leads will be desperately appreciated. Carl Kadie
adm@cbneb.UUCP (10/02/86)
/***** cbneb:net.unix / uiucdcsb!kadie / 4:53 am Oct 2, 1986 */ > Unfortunately I don't know how to > connect the pipe(s) to standard input and standard output > of the sub-program. > Also this does not permit fscan and fprint. /* The following code does not do any error checking */ int fildes[2]; int pid; int c; pipe (fildes); if( (pid = fork()) == 0 ) { /* CHILD */ close(0); dup (fildes[0]); /* Redirect stdin of child from parent*/ close(1); dup (fildes[1]); /* Redirect stdout of child to parent */ close (fildes[0]); /* Don't need these anymore */ close (fildes[1]); exec_ (the_child); /* * Now when the child writes to its file descriptor 1, it is * actually going down the pipe's write end. Likewise its read * on file descriptor 0 is actually reading from the read end of * the pipe. */ } /* PARENT */ while(read(fildes[0], &c, 1)) { munch (c); } /* * This entire sequence of code can be repeated in an intelligent * fashion for multiple child processes. You can simply poll the * various pipes by setting N_DELAY on the read end of the pipes * using fcntl(), and looping around them. * * Caveat : Don't use fscanf (buffered i/o) on pipes as the child's * buffering mechanism might not be exactly what you want. * Follow the low-level 'read' with 'sscanf', which might * be more deterministic. */ -------------------------------------------------------------------------------- S. Srinivasan UUCP: {cbosgd,ihnp4}!cbneb!srini [AT&T Bell Labs,MiddleOfTheRoadOhio] -------------------------------------------------------------------------------- Now stick that in your pipe and smoke it !
stuart@BMS-AT.UUCP (Stuart D. Gathman) (10/03/86)
In article <19300054@uiucdcsb>, kadie@uiucdcsb.cs.uiuc.edu writes: > Looking through the UNIX library I found a function that > does almost what I want, namely popen. As in: > FILE *pstrm; > pstrm = popen("cat >response","w") > This will start cat running such that the executive can > make stream writes (fprint) to it. The problem is > that I want to both fscan and fprint with each subprocesses. > An alternative function is the pipe command. It allows > read and write. Unfortunately I don't know how to > connect the pipe(s) to standard input and standard output > of the sub-program. /* coming right up . . . */ int twoway_pipe(p,cmd) int *p; char *cmd; { int pr[2], pw[2], pid; if (pipe(pr)) return -1; if (pipe(pw)) { close(pr[0]); close(pr[1]); return -1; } pid = fork(); if (pid<0) return -1; if (pid) { /* parent */ close(pr[1]); close(pw[0]); p[0] = pr[0]; p[1] = pw[1]; return 0; } close(0); close(1); dup(pw[0]); dup(pr[1]); /* define stdin & stdout */ /* you might want to close other (all>2) files here */ close(pr[0]); close(pw[1]); /* close original pipe fd's */ close(pr[1]); close(pw[0]); execlp("/bin/sh","sh","-c",cmd,0); return -1; } > Also this does not permit fscan and fprint. { FILE *in, *out; int p[2]; if (twoway_pipe(p,"mycommand")) perror("makepipe"); rdstrm = fdopen(p[0],"r"); /* make fd's into streams */ wrstrm = fdopen(p[1],"w"); . . . } P.S. I can't stand fscan. I can't stand null terminated strings either. Fortunately 'C' doesn't force you to use them. -- Stuart D. Gathman <..!seismo!{vrdxhq|dgis}!BMS-AT!stuart>
jon@msunix.UUCP (10/05/86)
In article <5263@cbneb.UUCP>, adm@cbneb.UUCP writes: > pipe (fildes); > > if( (pid = fork()) == 0 ) > { > /* CHILD */ > close(0); > dup (fildes[0]); /* Redirect stdin of child from parent*/ > close(1); > dup (fildes[1]); /* Redirect stdout of child to parent */ > close (fildes[0]); /* Don't need these anymore */ > close (fildes[1]); > exec_ (the_child); > /* > * Now when the child writes to its file descriptor 1, it is > * actually going down the pipe's write end. Likewise its read > * on file descriptor 0 is actually reading from the read end of > * the pipe. > */ > } Wait a minute. Does this mean I've been doing pipes wrong all my life? I thought if you wanted to have bidirectional communication with a pipe, you needed two pipe system calls. The example looks like the child could write down the pipe, and both the child and parent could then read the data, causing mass confusion. The way I always did it for bidirectional pipes was to first call pipe to make a channel for the parent to talk to the child. Then I would close the read end of the parent and the write end of the child so EOF can be detected. Then I would call pipe again for a channel for the child to talk to the parent, and close the write end of the parent and the read end of the child, again so EOF could be detected. The code would look something like (well, maybe not this bad): int pid, p2c_fd[2], c2p_fd[2], newfd; if (pipe(p2c_fd) == -1) { /* parent talks to child */ perror("pipe"); exit(1); } if (pipe(c2p_fd) == -1) { /* child talks to parent */ perror("pipe"); exit(1); } if ((pid = fork()) == -1) { perror("fork"); exit(1) } if (!pid) { /* * fix up child's fd and exec new program */ close(p2c_fd[1]); /* child doesn't write on this one */ close(c2p_fd[0]); /* child doesn't read on this one */ /* * If you have dup2() available, you don't have to do the close first */ close(0); /* close child stdin fd */ close(1); /* close child stdout fd */ /* * should get duped onto fd 0 */ if (newfd = dup(p2c_fd[0])) { fprintf(stderr, "Huh? Duped onto %d instead of 0\n", newfd); /* * should get duped onto fd 1 */ if ((newfd = dup(c2p_fd[1])) != 1) { fprintf(stderr, "Huh? Duped onto %d instead of 1\n", newfd); close(p2c_fd[0]); /* not needed anymore, */ close(c2p_fd[1]); /* close so EOF works */ /* * Confuse people running ps */ execl("program_filename", "Gobot Death Squad", "first_arg", 0); perror("execl"); fputs("What the heck? I'm dead\n"); _exit(); } /* * parent */ else { close(p2c_fd[0]); /* parent doesn't read on this one */ close(c2p_fd[1]); /* parent doesn't write on this one */ } /* * Now, write to child's stdin on p2c_fd[1] and read from child's stdout on * c2p_fd[0]. */ Am I way off? I could swear this is how I read it out of the UNIX manual a few years ago. Wouldn't using only one pipe with both parent and child reading out of it reference only a single open file, and reads by one would advance the pointer in the file for both parent and child? In fact, I could swear I've seen that behavior when trying to use just one pipe for bidirectional communication. "If we did it like everyone else, Jonathan Hue what would distinguish us from Via Visuals Inc. every other company in Silicon Valley?" sun!sunncal\ >!leadsv!msunix!jon "A profit?" amdcad!cae780/
chris@umcp-cs.UUCP (Chris Torek) (10/06/86)
In article <5263@cbneb.UUCP> adm@cbneb.UUCP writes: > /* The following code does not do any error checking */ It is also not quite right: > int fildes[2]; > int pid; > int c; Note the type of `c' ... > pipe (fildes); We now have one (1) pipe. The pair of pipe descriptors are each unidirectional: whatever you write on fildes[1] appears on fildes[0] for reading. > if( (pid = fork()) == 0 ) > { > /* CHILD */ > close(0); > dup (fildes[0]); /* Redirect stdin of child from parent*/ > close(1); > dup (fildes[1]); /* Redirect stdout of child to parent */ The child'd stdout is now connected to the child's stdin. This tends not to be useful. [stuff deleted] > /* PARENT */ > while(read(fildes[0], &c, 1)) > { > munch (c); > } Read takes a pointer to char, not a pointer to int. This code breaks on 68000s (something similar appears in older UUCPs) due to byte order problems. The basic idea is right: create a pipe or two, and pass the appropriate end or ends to the child process, which can be an arbitrary program. If, e.g., you need only read from a program, the code in `child' should be changed to (void) close(1); (void) dup(fildes[1]); ... and the type of c to `char'. There are a few important potential errors, including failure of any of the system calls; but there is also this to consider: The original program might not have a stdin or stdout. In this case, fildes[1] might well be 1 already, and the close above will discard it. So, a more realistic example: /* file descriptors for speaking with our progeny */ int to_child, from_child; int doit() { int pid, p0[2], p1[2]; if (pipe(p0)) /* oops */ return (-1); if (pipe(p1)) { (void) close(p0[0]); (void) close(p0[1]); return (-1); } switch (pid = fork()) { case -1: /* oops */ (void) close(p1[0]); (void) close(p1[1]); (void) close(p0[0]); (void) close(p0[1]); return (-1); case 0: /* child */ /* * Set child's stdin to read from p0. * Discard p0 write descriptor. */ if (p0[0] != 0) { (void) close(0); /* * Not much to do if the dup fails, * hence the `(void)'. */ (void) dup(p0[0]); (void) close(p0[0]); } (void) close(p0[1]); /* * Set child's stdout to write to p1; * discard p1 read descriptor. * * Note that p1[0] and p1[1] must be at * least 2 and 3 respectively (0 and 1 * having been consumed by p0). */ (void) close(1); (void) dup(p1[1]); (void) close(p1[1]); (void) close(p1[0]); /* * Fire up child. If not there, quit. */ execl("foo", "foo", (char *) NULL); _exit(1); /* NOTREACHED */ } /* * Parent. Discard read end of pipe to child's stdin, * and write end of pipe to child's stdout. Set those * global variables so we know which descriptors talk * to the child. */ (void) close(p0[0]); (void) close(p1[1]); to_child = p0[1]; from_child = p1[0]; return (pid); /* success */ } Rather longer than the previous version; but that is what happens when one adds error checks.... -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516) UUCP: seismo!umcp-cs!chris CSNet: chris@umcp-cs ARPA: chris@mimsy.umd.edu
rdg@hpfclj.HP.COM (Rob Gardner) (10/07/86)
> > Unfortunately I don't know how to > > connect the pipe(s) to standard input and standard output > > of the sub-program. > > Also this does not permit fscan and fprint. > > int fildes[2]; > int pid; > int c; > > pipe (fildes); > > if( (pid = fork()) == 0 ) > { > /* CHILD */ > > close(0); > dup (fildes[0]); /* Redirect stdin of child from parent*/ > close(1); > dup (fildes[1]); /* Redirect stdout of child to parent */ > > close (fildes[0]); /* Don't need these anymore */ > close (fildes[1]); > > exec_ (the_child); > /* > * Now when the child writes to its file descriptor 1, it is > * actually going down the pipe's write end. Likewise its read > * on file descriptor 0 is actually reading from the read end of > * the pipe. > */ > } Be warned: the above code does NOT work. Pipes are unidirectional, and you need two of them to do what you want. The above code runs a program with its stdout connected to its stdin! What you really want is something like: int input[2], output[2]; pipe(input); /* parent reads 0, child writes 1 */ pipe(output); /* parent writes 1, child reads 0 */ if (fork() == 0) { close(0); dup(output[0]); close(1); dup(input[1]); close(output[1]); close(input[0]); exec... } close(output[0]); close(input[1]); /* now reads on input[0] will read stuff output by child, and writes on output[1] will be read by child. another neat thing is that when the child dies, the reads will fail inthe parent, and the writes will produce SIGPIPE */ Rob Gardner {ihnp4,hplabs,hpbbn}!hpfcla!rdg Hewlett Packard or rdg%hpfcla@hplabs.hp.com Fort Collins, Colorado 303-229-2048
adm@cbneb.UUCP (10/09/86)
/***** cbneb:net.unix / msunix!jon / 10:52 pm Oct 6, 1986 */ > Wait a minute. Does this mean I've been doing pipes wrong all my life? > I thought if you wanted to have bidirectional communication with a pipe, > you needed two pipe system calls. You are right. I never meant to imply that the code I posted was meant for bi-directional communication. Even tho it may have sounded so. I've typically used pipes for things like constantly updating the screen with the output of 'ps' or any other command, where the child does the 'ps' and the parent does the curses part. So all you need is one-way traffic. SO your last few years of life have not been pipe dreams ! Live on. -------------------------------------------------------------------------------- S. Srinivasan UUCP: {cbosgd,ihnp4}!cbneb!srini [AT&T Bell Labs,MiddleOfTheRoadOhio] --------------------------------------------------------------------------------