[comp.unix.wizards] How to do a pipe & fork?

pratt@zztop.rutgers.edu (Lorien Y. Pratt) (11/03/88)

OK, an even easier question than I posted last time, I'd really appreciate
your help.

I have two processes that I want to communicate.  I want the parent to
be able to fprintf to the child's stdin and to fscanf from the child's
stdout.  I know that I need to fork and then in the child do an execlp,
but I can't figure out how to set up the file descriptors so I can do
what I want. The Sun IPC guide does not help me here.  I know that I
need to do a dup and a fork and a pipe, but I'm not sure in what order
and with what parameters.

Here's what I have so far, which I know is wrong.

#include <netdb.h>
#include <stdio.h>
#define STRLEN 40

main()
{
int pid;
FILE *fdopen();
FILE *sql;
char from_sql[256];
char *fgets();
int i;

pid = fork();

if (pid == 0)   /* We are the children */
{
  /* This part works.  I tested it in its own program without being a child. */
  i = execlp("rsh", "rsh", "topaz", "/u2/ingres/bin/sql", "spam", 0);
  printf("execlp didn't work, return code is %d\n", i);
}
else
{
  printf( "Child's process ID is %d\n", pid ); fflush(stdout);
  /* Open file descriptor to talk to child.  I know it's wrong to pass a pid
     to fdopen, but how do I get the right fdes instead? */
  sql = fdopen( pid, "a+" );
  printf( "result of fdopen is %d\n", sql ); fflush(stdout);

  /* Start talking to child */
  fgets(from_sql, 256, sql );
  printf("SQL says: %s\n", from_sql );

  fclose( sql );
}

}

Of course, this core dumps on the fgets call, because fdopen returns
zero because I'm passing it a useless first argument.  I thought that
my best shot would help you to help me, though.

AdTHANKSvance!
   --Lori
-- 
-------------------------------------------------------------------
Lorien Y. Pratt                            Computer Science Department
pratt@paul.rutgers.edu                     Rutgers University
                                           Busch Campus
(201) 932-4634                             Piscataway, NJ  08854

keith@execu.UUCP (Keith Pyle) (11/03/88)

In article <Nov.2.14.51.36.1988.8260@zztop.rutgers.edu> pratt@zztop.rutgers.edu (Lorien Y. Pratt) writes:
>OK, an even easier question than I posted last time, I'd really appreciate
>your help.
>
>I have two processes that I want to communicate.  I want the parent to
>be able to fprintf to the child's stdin and to fscanf from the child's
>stdout.  I know that I need to fork and then in the child do an execlp,
>but I can't figure out how to set up the file descriptors so I can do
>what I want. The Sun IPC guide does not help me here.  I know that I
>need to do a dup and a fork and a pipe, but I'm not sure in what order
>and with what parameters.
>
> [code deleted...]

Here's a quickly hacked version of the code Lorien posted that illustrates
setting up a pipe:

#include <stdio.h>

main()
{
int pid;
FILE *fdopen();
FILE *sql;
char from_sql[256];
char *fgets();
int i;
int filedes[2];

pipe(filedes);
pid = fork();

if (pid == 0)   /* We are the children */
{
  close(1);
  i = dup(filedes[1]);
  i = execlp("rsh", "rsh", "mesquite", "cat /usr/keith/bin/chkmail", 0);
  printf("execlp didn't work, return code is %d\n", i);
}
else
{
  printf( "Child's process ID is %d\n", pid ); fflush(stdout);
  sql = fdopen( filedes[0], "r" );
  printf( "result of fdopen is %d\n", sql ); fflush(stdout);

  /* Start talking to child */
  fgets(from_sql, 256, sql );
  printf("SQL says: %s\n", from_sql );

  fclose( sql );
}

}

The basic problem was that you need to call pipe(2) prior to fork(2) and
then dup(2) the 'write' file descriptor to be the stdout of the child
process.  For the parent, you need to call fdopen(3s) with the file descriptor
for reading created by pipe(2). Note also that the mode supplied to fdopen(3s)
must match the with which the file descriptor was created.  In the example
code by Lorien, the mode was "a+".

-----------------------------------------------------------------------------
Keith Pyle   ...!{rutgers | tut.cis.ohio-state.edu}!cs.utexas.edu!execu!keith

Disclaimer: What??  You actually believed me?
-----------------------------------------------------------------------------

rar@nascom.UUCP (Alan Ramacher) (11/03/88)

In article <Nov.2.14.51.36.1988.8260@zztop.rutgers.edu>, pratt@zztop.rutgers.edu (Lorien Y. Pratt) writes:
> 
> I have two processes that I want to communicate.  I want the parent to

	(text and code deleted)

	(code deleted)

see:	Advanced Unix Programmin by Marc Rochkind
	Chapter 6 Basic Interprocess Communications

	After reading this, you will have no difficulity
	writing the code you need. 

avr@mtgzz.att.com (a.v.reed) (11/04/88)

In article <Nov.2.14.51.36.1988.8260@zztop.rutgers.edu>, pratt@zztop.rutgers.edu (Lorien Y. Pratt) writes:
> I have two processes that I want to communicate.  I want the parent to
> be able to fprintf to the child's stdin and to fscanf from the child's
> stdout.  I know that I need to fork and then in the child do an execlp,
> but I can't figure out how to set up the file descriptors so I can do
> what I want. The Sun IPC guide does not help me here.  I know that I
> need to do a dup and a fork and a pipe, but I'm not sure in what order
> and with what parameters.

The most legible, general, portable (at least to other Sun and AT&T
derived systems), easy to maintain and modify, and only slightly less
then optimally efficient solution is to start up your process from a
shell script after creating a named pipe, and passing its name as an
argument to the process:

if /etc/mknod /usr/tmp/pipe$$ p
then exec $LIB/yourprocess -p /usr/tmp/pipe$$
fi
echo "yourprocess: can't make named pipe /usr/tmp/pipe$$; exiting"
exit 5 # EIO

In the executable $LIB/yourprocess, read the pipe name with getopt(),
fopen it for "r+", then fork and exec, write in the parent and read in
the child. When the child is finished fclose() the pipe fd, and,
in the parent, fclose() and unlink the named pipe when an attempt to
write sets errno to EPIPE.
				Adam Reed (avr@mtgzz.ATT.COM)

avr@mtgzz.att.com (a.v.reed) (11/04/88)

In article <4629@mtgzz.att.com>, I wrote:
< In article <Nov.2.14.51.36.1988.8260@zztop.rutgers.edu>, pratt@zztop.rutgers.edu (Lorien Y. Pratt) writes:
< > I have two processes that I want to communicate.  I want the parent to
< > be able to fprintf to the child's stdin and to fscanf from the child's
< > stdout.  I know that I need to fork and then in the child do an execlp,
< > but I can't figure out how to set up the file descriptors so I can do
< > what I want. The Sun IPC guide does not help me here.  I know that I
< > need to do a dup and a fork and a pipe, but I'm not sure in what order
< > and with what parameters.
< 
< The most legible, general, portable (at least to other Sun and AT&T
< derived systems), easy to maintain and modify, and only slightly less
< then optimally efficient solution is to start up your process from a
< shell script after creating a named pipe, and passing its name as an
< argument to the process:
< 
< if /etc/mknod /usr/tmp/pipe$$ p
< then exec $LIB/yourprocess -p /usr/tmp/pipe$$
< fi
< echo "yourprocess: can't make named pipe /usr/tmp/pipe$$; exiting"
< exit 5 # EIO
< 
< In the executable $LIB/yourprocess, read the pipe name with getopt(),
< fopen it for "r+", then fork and exec, write in the parent and read in
< the child. When the child is finished fclose() the pipe fd, and,
< in the parent, fclose() and unlink the named pipe when an attempt to
< write sets errno to EPIPE.
< 				Adam Reed (avr@mtgzz.ATT.COM)

To clarify: popen(), about which you can read in section 3S of the
manual, or just with $ man popen, will do what Lorien Y. Pratt wants to
do, perfectly well, as long as only ONE pipe, in ONE direction, is all
that is needed. The solution I outlined above has the advantage of
generality, in that it can be readily extended to bidirectional
communication (one named pipe in each direction) and to an arbitrarily
complex network of communicating processes with arbitrarily many pipes.

				Adam Reed (avr@mtgzz.ATT.COM)

syeh@caip.rutgers.edu (Simon Yeh) (11/04/88)

In article <Nov.2.14.51.36.1988.8260@zztop.rutgers.edu>, pratt@zztop.rutgers.edu (Lorien Y. Pratt) writes:
>OK, an even easier question than I posted last time, I'd really appreciate
>your help.
>Here's what I have so far, which I know is wrong.

You may try this....[Note: lines starting with /*@*/ are what I added]

 #include <netdb.h>
 #include <stdio.h>
 #define STRLEN 40
 
 main()
 {
 int pid;
 char from_sql[256];
 char *fgets();
 int i;
 /*@*/ int fd[2]; /* store file descriptors for pipe */
 
 /*@*/ pipe(fd);  /* creat pipe */
 
 pid = fork();
 
 if (pid == 0)   /* We are the children */
 {
   /*@*/ dup2(fd[1], 1); /* redirect child's stdout to fd[1] 
                            so child can talk to parent */
   /*@*/ close(df[0]); close(fd[1]);
   /* This part works.  I tested it in its own program without being a child. */
   i = execlp("rsh", "rsh", "topaz", "/u2/ingres/bin/sql", "spam", 0);
   printf("execlp didn't work, return code is %d\n", i);
   /*@*/ exit(1);
 }
 else
 {
   printf( "Child's process ID is %d\n", pid ); fflush(stdout);
   /*@*/ close(fd[1]);
 
   /* Start talking to child */
   /*@*/ fgets(from_sql, 256, fd[0] ); /* read what child says from fd[0] */
   printf("SQL says: %s\n", from_sql );
 
   /*@*/ fclose( fd[0] );
   ......
 }
 .....
}

>Lorien Y. Pratt                            Computer Science Department
>pratt@paul.rutgers.edu                     Rutgers University

Hope this will help.

--- Simon Yeh

m2@insyte.UUCP (Mike Arena) (11/10/88)

In article <Nov.2.14.51.36.1988.8260@zztop.rutgers.edu> pratt@zztop.rutgers.edu (Lorien Y. Pratt) writes:
>OK, an even easier question than I posted last time, I'd really appreciate
>your help.
>
>I have two processes that I want to communicate.  I want the parent to
>be able to fprintf to the child's stdin and to fscanf from the child's
>stdout. 

... stuff deleted ...

>Here's what I have so far, which I know is wrong.
... code deleted ...

>   --Lori
>-- 
>-------------------------------------------------------------------
>Lorien Y. Pratt                            Computer Science Department
>pratt@paul.rutgers.edu                     Rutgers University
>                                           Busch Campus
>(201) 932-4634                             Piscataway, NJ  08854

Here is a procedure to execute a command and creates three pipes to the child.
The pid of the child is returned.  The parent then can write to the child's
standard input (fdin) and read from the childs standard output and error
(fdout and fderr).

If you need FILE pointers, then use the fdopen() function of the three file
descriptors.

Obviously, you should do more error checking than what I did below.

Also, I used vfork() instead of fork() since an exec is done in the child
immediately.  Vfork() saves a lot of time and space.

You will have to change the arguments for the function if you need to use
execlp() or whatever instead of execvp().

int
execute(name,args,fdin,fdout,fderr)
char	*name, *args[];
int	*fdin, *fdout, *fderr;
{
    int	Pfdin[2], Pfdout[2], Pfderr[2], pid;

    pipe (Pfdin);
    pipe (Pfdout);
    pipe (Pfderr);

    switch (pid = vfork ()) 
    {
    case -1:
	printf("vfork error");
	
    case 0:
	close(0);	/* Close child's stdin stream then ...    */
	dup(Pfdin[0]);	/* make child's stdin be the first pipe   */
	close(1);	/* Close child's stdout stream then ...   */
	dup(Pfdout[1]);	/* make child's stdout be the second pipe */
	close(2);	/* Close child's stderr stream then ...   */
	dup(Pfderr[1]);	/* make child's stderr be the third pipe  */

	execvp (name, args);
    }

    *fdin  = Pfdin[1];	/* Parent will write to child's stdin   */
    *fdout = Pfdout[0];	/* Parent will read from child's stdout */
    *fderr = Pfderr[0];	/* Parent will read from child's stderr */
    
    return( pid );
}
-- 
Michael J. Arena
Innovative Systems Techniques (INSYTE)
1 Gateway Center, Newton, MA  (617) 965-8450
UUCP: ...harvard!linus!axiom!insyte!m2