More on how to do a pipe (was re: system() problem)

Maarten Litmaath maart at nat.vu.nl
Fri Mar 8 10:20:37 AEST 1991


In article <2506 at travis.csd.harris.com>,
	brad at SSD.CSD.HARRIS.COM (Brad Appleton) writes:
>[...]
>I hate to be critical but I didnt care for the way the status codes from
>dup2 and pipe were ignored.

Really?  Amazing...

>I have a piece of code from a while back that
>was created specifically for the purpose of illustrating how to use fork and
>how to redirect parent/child i/o. Its a bit longer than yours but is cleaner
>(IMHO) and is easy to read. (BTW let me know ASAP of any errors - I thought
>they were all worked out be now but who knows):

OK, I've patched up your program a little, especially your dup2()
alternative, which was completely wrong in various ways; you wrote:

># define  dup2(to,from)  ( (close(from) || (to = dup()) < 0) ? -1 : 0 )

	1) the call is dup2(from, to);

	2) dup() is supposed to have an argument (!);

	3) upon success dup2() returns the new descriptor,
	   instead of always 0;

	4) the return value of close() is unimportant (its argument
	   must be checked for validity, though);

	5) if its arguments are equal, dup2() is not to perform the
	   close() at all.

Right, compare the new version with the original code!

--------------------cut here for the patched version--------------------
/* I/O Redirection example */
#include <stdio.h>

#define  BUF_LEN  256
#define  NULLSTR  (char *)NULL

  /* read & write ends of a pipe */
#define  READ_END  0
#define  WRITE_END 1

  /*
  ** function to replace the dup2 system call (if it's not present)
  */
#ifdef DONT_HAVE_DUP2_SYSTEM_CALL
#include	<sys/param.h>
#include	<errno.h>

#ifndef	NOFILE
#define		NOFILE		20
#endif	/* !NOFILE */

int	dup2(old, new)
int	old, new;
{
	int	fd;
	void	_dup2();

	if ((unsigned) new >= NOFILE) {
		errno = EBADF;
		return -1;
	}

	if ((fd = dup(old)) < 0) {
		if (errno == EBADF)
			return -1;
	} else if (fd == new)
		return new;

	if (old != new) {
		close(new);
		_dup2(old, new);
	}

	if (fd >= 0)
		close(fd);

	return new;
}


static	void	_dup2(old, new)
int	old, new;
{
	int	fd;

	if ((fd = dup(old)) != new) {
		_dup2(old, new);
		close(fd);
	}
}
#endif

main( argc, argv )
  int  argc;
  char *argv[];
{
  int  nbytes,         /* number of bytes read */
       status,         /* return-code/status for various system calls */
       pid,            /* process id of child */
       pipe_fd[2];     /* pipe file-descriptor array */ 
  char buf[ BUF_LEN ]; /* character buffer*/

  /* open a pipe */
  if ( pipe(pipe_fd) < 0 ) {
    perror( "unable to open unnamed pipe\n" );
    exit( 1 );
  }
  
  switch ( pid = fork() ) {
    case -1: /* failure */
      perror( "Unable to fork off a process" );
      exit( 1 );
      break;

    case  0: /* child */
      /* redirect stdout to the write end of the pipe */

      if ( dup2( pipe_fd[ WRITE_END ], fileno(stdout) ) < 0 ) {
        perror( "unable to redirect STDOUT to write end of pipe" );
        exit( 1 );
      }

      /*
      **  The following close() is not necessary in this particular case,
      **  but it definitely is in general!  Else the reader of the pipe
      **  may hang later (think about it!).
      */
      close( pipe_fd[ WRITE_END ] );

      /*
      **  Likewise for the read end of the pipe: as long as there is
      **  a potential reader, a writer will not receive a SIGPIPE.
      */
      close( pipe_fd[ READ_END ] );

      fprintf( stderr, "Child: Going to exec uptime (pid=%d).\n", getpid() );
      fflush( stderr );
      execl( "/usr/ucb/uptime", "uptime", NULLSTR );
      break;

    default: /* parent */

      /*
      **  The following close() is not necessary in this particular case,
      **  but it definitely is in general!  Else the reader of the pipe
      **  may hang later (think about it!).
      */
      close( pipe_fd[ WRITE_END ] );

      do {  /* read output from child (until see '\n' ) */
        printf( "\n\nParent: reading from pipe\n\n" );
        fflush( stdout );

        if ( (nbytes=read( pipe_fd[ READ_END ], buf, BUF_LEN )) <0 ) {
          perror( "failed on pipe read" );
	  exit( 1 );
        }

        if ( write( fileno(stdout), buf, nbytes ) != nbytes ) {
          perror( "failed on STDOUT write" );
	  exit( 1 );
        }
      } while ( buf[ nbytes-1 ] != '\n' );

      printf( "Parent: Waiting for child (childpid=%d).\n", pid );
      fflush( stdout );

      wait( &status );
      printf( "Parent: Child died (status=0x%x).\n", status );
      fflush( stdout );
      close( pipe_fd[ READ_END ] );
      break;
  }

  exit( 0 );
}



More information about the Comp.unix.programmer mailing list