[comp.unix.questions] pipe

ARaman@massey.ac.nz (A.V. Raman) (03/22/90)

When I tried to set up a pipe in one of my programs, I found that
it failed.  When I put in some debug printfs in the code, I
found that it worked whenever there was a printf done just before the
dup() in the parent.   I also found that write() does not work in
place of printf().

I'm posting a reduced version of the code that caused the problem.
I've also removed all error checks at dups() and closes().  However, I
must mention that no errors were encountered even in the code that
included the checking.

Any help (by email please) would be greatly appreciated.

---- Code begins ----

#include <stdio.h>
setup()
{
   int fd[2];

   if (pipe(fd) < 0) {  /* write to fd[1] and read fd[0] */
      perror("pipe");
      exit(1);
   }
   if (fork() == 0) {   /* set up child to read pipe as stdin */
      close(0);
      dup(fd[0]);
      close(fd[1]);
      execl("/bin/cat","cat","-n",NULL);  /* ucb cat -n to number lines */
      perror("exec");
   } else {            /* set up parent to write to pipe as stdout */
/**/  printf (" ");    /* program fails if this line is removed */
      close(1);
      dup(fd[1]);
      close(fd[0]);
   }
}

main(argc,argv)
char **argv;
{
   FILE *fp;
   char buf [BUFSIZ];
   
   if (argc == 1)
      exit(1);
   setup();			/* setup pipe with cat reading other end */
   if ((fp = fopen(argv[1],"r")) == NULL) {
      perror(argv[1]);
      exit(1);
   }
   while (fgets(buf,BUFSIZ,fp) != NULL)
      printf("%s", buf);        /* write into pipe */
   fclose(fp);
   close(1);
   return 0;
}
---- Code ends ----

Thanks.
&

ARaman@massey.ac.nz (A.V. Raman) (03/27/90)

For the sake of other readers who may be interested, I'm restating the
problem here and posting a summary of the replies I received.

---- Faulty Code begins ----
#include <stdio.h>
setup()
{
   int fd[2];

   if (pipe(fd) < 0) {  /* write to fd[1] and read fd[0] */
      perror("pipe");
      exit(1);
   }
   if (fork() == 0) {   /* set up child to read pipe as stdin */
      close(0);
      dup(fd[0]);
      close(fd[1]);
      execl("/bin/cat","cat","-n",NULL);  /* ucb cat -n to number lines */
      perror("exec");
   } else {            /* set up parent to write to pipe as stdout */
/**/  printf (" ");    /* program fails if this line is removed */
      close(1);
      dup(fd[1]);
      close(fd[0]);
   }
}

main(argc,argv)
char **argv;
{
   FILE *fp;
   char buf [BUFSIZ];
   
   if (argc == 1)
      exit(1);
   setup();			/* setup pipe with cat reading other end */
   if ((fp = fopen(argv[1],"r")) == NULL) {
      perror(argv[1]);
      exit(1);
   }
   while (fgets(buf,BUFSIZ,fp) != NULL)
      printf("%s", buf);        /* write into pipe */
   fclose(fp);
   close(1);
   return 0;
}
---- Code ends ----

Thanks for all the prompt solutions that I received for the problem.
The fault in my program narrows down to my mixing buffered and unbuffered
IO incorrectly.

Reproduced with the permission of Cuong T. Nguyen of Center for Integrated
Systems, Stanford University:

>The problem is with stdio buffering, which can be none, line,
>or block.  When the program is started, stdio is typically fully
>(block) buffered.  When your first printf() is called (actually,
>_flsbuf()), IF stdout is a tty, buffering is set to line.  Otherwise
>it is left alone.  That's why the null print("  ") insertion helps,
>and also why write() doesn't, since it is not part of stdio.
>
>When your printf's are fully buffered, anything left unflushed
>in stdout before the pipes close is lost.  The simplest thing
>to do is an fflush(stdout) at the end.  Else you can do something
>a la setlinebuf() to force the buffering type you like.

- Anand