[net.unix] Dissimilar Program Interaction?

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]
--------------------------------------------------------------------------------