[comp.unix] pipes...

akrishn@csserv2.ic.sunysb.edu (Arvind Krishnan) (11/21/90)

two pipes for full duplex communications with its child (mailx). 
     
     1. stdout of the child is the stdin of the parent and
     2. stdout of the parent is the stdin of the child

For some unknown reason, '1' works but '2' doesn't. The parent process can 
read the output of its child, but the child doesn't seem to be reading the
output of the parent process.

Can someone please figure out whats happening and let me know!

The code fragment at fault is as follows.

Thanks a lot in advance. Any help will be greatly appreciated.

Arvind

-------------------------------------------------------------------------------
char command[] = "1";

int start_mail() 
{ 
  int to_par[2], to_child[2];
  char buf[8];
  int flag = 0;

  if (pipe(to_par))
     return -1; 
  if (pipe(to_child))
     return -1; 

  if (fork() == 0)
   {
     close(0);
     dup2(to_child[0], 0);
     close(1);
     dup2(to_par[1], 1);
     close(to_par[1]);
     close(to_child[0]);
     close(to_par[0]);
     close(to_child[1]);
     if (execl("/usr/bin/mailx", "mailx", (char *) 0))
        fprintf(stderr,"Exec Error\n");
   }
  close(1);
  dup2(to_child[1], 1);
  close(0);
  dup2(to_par[0], 0);
  close(to_child[1]);
  close(to_par[0]);
  close(to_child[0]);
  close(to_par[1]);
  fcntl(0, F_SETFL, O_NDELAY);
  while (1)
   {
     if (flag == 0) 
        while ((flag = read(0,buf,1)) == 0);
     write(2,buf,1);
     flag = read(0,buf,1);
     if (flag == 0)
        write(1, command, 1);
   }
} 

steve@altos86.Altos.COM (Steve Scherf) (11/28/90)

In article <2274@megadon.UUCP> akrishn@csserv2.ic.sunysb.edu (Arvind Krishnan) writes:
>two pipes for full duplex communications with its child (mailx). 
>     
>     1. stdout of the child is the stdin of the parent and
>     2. stdout of the parent is the stdin of the child
>
>For some unknown reason, '1' works but '2' doesn't. The parent process can 
>read the output of its child, but the child doesn't seem to be reading the
>output of the parent process.

Your pipes seem set up properly, but I do have a few comments about how you
do it. I also have a few questions about your interesting while loop.

>  if (fork() == 0)
>   {
This close() is unnecessary since dup2() closes the descriptor.
>     close(0);
>     dup2(to_child[0], 0);
Ditto.
>     close(1);
>     dup2(to_par[1], 1);
>     close(to_par[1]);
>     close(to_child[0]);
>     close(to_par[0]);
>     close(to_child[1]);
The below if() is unnecessary, since execl() will only return on error.
>     if (execl("/usr/bin/mailx", "mailx", (char *) 0))
>        fprintf(stderr,"Exec Error\n");
An exit() here would be nice.
>   }
...
And here's that while loop:
>  fcntl(0, F_SETFL, O_NDELAY);
>  while (1)
>   {
>     if (flag == 0) 
Just say no to busy-waits. This is bogus on a multiuser system.
>        while ((flag = read(0,buf,1)) == 0);
And just what does ^^ (flag) do here? flag just gets reset below anyway.
>     write(2,buf,1);
What's this read() all about?
>     flag = read(0,buf,1);
Huh?
>     if (flag == 0)
>        write(1, command, 1);
>   }
>} 

Your while loop seems to be written in such a way that you might end up
spinning in that busy-wait forever. It is also possible that you might never
actually write to the child. You are playing around with time-dependent
factors in a bad way. If your system has select(), you might look it up. I
would suggest scanning for the mailx input prompt, but it seems to suppress
printing it when input is not a tty. Also, you seem to throw away every other
byte that you get from the child. Actually, I'm exactly not sure what you're
trying to do.

Below is a version which might do what you want. You would also probably
want to write a signal handler for SIGCLD and SIGPIPE, in addition. It is
rough and untested, but should hopefully give you a hint.

readmail()
{
	char buf[100];
	int p2c[2], c2p[2], c;

	pipe(p2c);
	pipe(c2p);

	if((c = fork()) < 0)
		return -1;

	if(c) {
		/* parent */
		dup2(p2c[1], 1);
		dup2(c2p[0], 0);
	}
	else {
		/* child */
		dup2(c2p[1], 1);
		dup2(p2c[0], 0);
	}

	close(p2c[0]);
	close(p2c[1]);
	close(c2p[0]);
	close(c2p[1]);

	if(!c) {
		execl("/usr/bin/mailx", "mailx", 0);
		exit(1);
	}

	/* talk to child */
	write(1, "1\nx\n", 4);	/* tell it to print mail, then quit */

	/* listen to child */
	for(;;) {
		if((c = read(0, buf, 100)) <= 0)
			return c;
		write(2, buf, c);
	}
}

Hope this helps.
-- 
Steve Scherf
steve@Altos.COM    ...!{sun|sco|pyramid|amdahl|uunet}!altos!steve

These opinions are solely mine, but others may share them if they like.

bliss@sp64.csrd.uiuc.edu (Brian Bliss) (12/07/90)

I've done this before, and ran into this problem.
well, you perfrom the fork, test whether you are child or
parent, and then get ready to dup the pipes:


	if(c) {
		/* parent */
		dup2(p2c[1], 1);
		dup2(c2p[0], 0);
	}
	else {
		/* child */
		dup2(c2p[1], 1);
		dup2(p2c[0], 0);
	}


consider the case of the parent:  what if c2p[0] is fd 1?
then the first dup2 call overwrites fd 1 before it can be
copied to 0.

it's really a pain if you need to dup fd 0 to 1, and fd 1 to 0,
but here's a fix I wrote that should do what you want:



/*****************************************************************************/
/* this file, "fork_cpp.c", is the source for a command which exec's a       */
/* child process, sets the child's stdin to be to_child and returns a FILE   */
/* pointer to its stdout (*from_child) and the child's process id (the       */
/* function's return val).                                                   */
/*****************************************************************************/



# include <stdio.h>
# include <signal.h>



#define	RDR	0
#define	WTR	1



int ae_fork_cpp (cmd, to_child, from_child)
/*****************************************************************************/
/* ae_popen2 exec's cmd, sets the child's stdin to *to_child, and creats a   */
/* a pipe from the child's stdout to *from_child.  ae_fork_cpp () returns    */
/* the process id of the child, or -1 for an error.   It is used by the      */
/* application executive to invoke the C preprocessor.                       */
/*****************************************************************************/
   char *cmd;
   FILE *to_child;
   FILE **from_child;
   {
      int c2p[2];
      register int child_stdin;
      register int child_stdout;
      register int parent_reader;
      register int pid;

      if (pipe (c2p) < 0) {
         return (-1);
      }
      child_stdout = c2p[WTR];
      parent_reader = c2p[RDR];
      child_stdin = fileno (to_child);

      /*************************************************/
      /* if child_stdout == 0, it will be overwritten  */
      /* when dup2'ing child_stdin to 0, so change it. */
      /* this should be done before the fork, in case  */
      /* of an error.                                  */
      /*************************************************/
      if (child_stdout == 0) {
         if ((child_stdout = dup (child_stdout)) == -1) {
            close (child_stdout);
            close (parent_reader);
            return (-1);
         }
         close (child_stdout);
      }

      /********************/
      /* perform the fork */
      /********************/
      if ((pid = fork ()) == -1) {
         close (child_stdout);
         close (parent_reader);
         return (-1);
      }
      if (pid == 0) {
         /**************************************************/
         /* we are the child process - close the parent's  */
         /* file descriptor, and copy our file descriptors */
         /* to 0 and 1, our stdin and stdout fd's.  then,  */
         /* exec the appropriate command.                  */
         /**************************************************/
            close (parent_reader);
            if (child_stdout != 0) {
               if (child_stdin != 0) {
                  dup2 (child_stdin, 0);
                  close (child_stdin);
               }
               if (child_stdout != 1) {
                  dup2 (child_stdout, 1);
                  close (child_stdout);
               }
            }
            else {
               int temp;
               temp = open ("/dev/null", "w");
               dup2 (child_stdout, temp);
               close (child_stdout);
               if (child_stdin != 0) {
                  dup2 (child_stdin, 0);
                  close (child_stdin);
               }
               dup2 (temp, 1);
               close (temp);
            }
            execl("/bin/sh", "sh", "-c", cmd, 0);
            /* it is an error to get past here */
            exit(1);
	}
      else {
         /**************************************************/
         /* we are the parent process - close the child's  */
         /* stdout file descriptor, set the from_child     */
         /* file pointer, and return the pid of the child. */
         /**************************************************/
         close (child_stdout);
         *from_child = fdopen (parent_reader, "r");
         return(pid);
      }
   }

msdos@cs.mcgill.ca (Mark SOKOLOWSKI) (12/27/90)

USER =(1) MASTER =(2) SLAVE where SLAVE is any program using
(stdin,stdout), (1) is the (stdin,stdout) pair and (2) is a set of
2 pipes. SLAVE sees (2) as (stdin,stdout), while MASTER sees it
as (Pipe[1],Pipe[0]).

Mark