ayzkub
ayzkub

Reputation: 15

Q: Reading from pipe to screen

I'm trying to write a program that reads some text from a file and prints it to the screen. The parent will read the content of the file write it to n number of pipes and the children will read it and then print it.

So far this is what I've got:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>

int main (void)
{
  pid_t pid;
  char c;
  FILE *fd;
  char buf[100];
  int N_CHILDREN = 2;
  int p[N_CHILDREN][2];
  int i,j;

  for(i=0; i<N_CHILDREN; i++)
  {
    pipe(p[i]);
  }

  fd=fopen("123.txt","r");

  for(j=0; j < N_CHILDREN;j++)
  { 
    pid = fork ();

    if (pid == 0)
     {     
           close (p[j][1]);
           while(read(p[j][0], &fd,sizeof(buf)) > 0)
            printf("\n%c",&fd);

      }
    if (pid < 0) 
     {
       //Fork Failed
           fprintf (stderr, "Fork failure.\n");
           return EXIT_FAILURE;
         }
    if ( pid > 0) //Parent
     {  
             close (p[j][0]);
             write(p[j][1], fd ,sizeof(buf));

     }
  }
}

Problem is it's not really reading the content from the file. I've tried sending it a string of characters instead of reading from a file and it worked as intended, both children printed the message one time and the program ended.

Any thoughts about it? After reading the manuals I still can't see where the problem is.

Upvotes: 1

Views: 197

Answers (3)

hexasoft
hexasoft

Reputation: 677

Several problems here:

  1. you make bad usage of read function by passing &fd, which is a FILE*. This function needs a pointer to the "buffer" to print, here I guess buf.
  2. you don't check errors. For example if fopen fails.
  3. you never read data from your file, so you have "nothing" to send to children.
  4. you have to get returned value of read (in children) because it is the effective amount of data that you get. So it is the amount of data that you have to print after that (to stdout).

So here is an example code, see comments inside:

// put here all the needed includes (see manpages of functions)

// it is better to create a function for the child: the code
// is easier to read
// the child just get the file descriptor to read (the pipe)
void child(int fd) {
  char buf[100];  // buffer to store data read
  int ret;        // the number of bytes that are read

  // we read from 'fd', into 'buf'. It returns the number of bytes
  // really read (could be smaller than size). Return <=0 when over
  while((ret = read(fd, buf, sizeof(buf))) > 0) {
    // write the 'ret' bytes to STDOUT (which as file descriptor 1)
    write(1, buf, ret);
  }
}

int main (void) {
  pid_t pid;
  char buf[100];
  int N_CHILDREN = 2;
  int p[N_CHILDREN][2];
  int i,j, ret;
  int fdi;
  // create the pipes
  for(i=0; i<N_CHILDREN; i++) {
    if (pipe(p[i]) == -1) {
      perror("pipe");  // ALWAYS check for errors
      exit(1);
    }
  }
  // open the file (with 'open' not 'fopen', more suitable for
  // reading raw data
  fdi = open("123.txt",O_RDONLY);
  if (fdi < 0) {
    perror("open"); // ALWAYS check for errors
    exit(1);
  }
  // just spawn the children
  for(j=0; j < N_CHILDREN;j++) { 
    pid = fork();
    if (pid < 0) {
      perror("fork"); // ALWAYS check for errors
      exit(1);
    }
    if (pid == 0) {  // child
      close(p[j][1]);  // close the writing part
      child(p[j][0]);  // call child function with corresp. FD
      exit(0);  // leave : the child should do nothing else
    }
  }
  // don't need that part
  for(j=0; j<N_CHILDREN; j++) {
    close(p[j][0]);  // close the read-part of pipes
  }
  // need to read file content, see comment in child() function
  while ((ret = read(fdi, buf, sizeof(buf))) > 0) {
    // write the data to all children
    for(j=0; j<N_CHILDREN; j++) {
      write(p[j][1], buf , ret); // we write the size we get
    }
  }
  // close everithing
  for(j=0; j<N_CHILDREN; j++) {
    close(p[j][1]);  // needed, see text after
  }
  close(fdi); // close read file
  return(0); // main returns a int, 0 is "ok"
}

You have to close every parts of pipes when not needed or when it is over. Until a file descriptor is open a read will block the process. Only when last write counterpart is closed the read returns <=0.

Note: 1. the correct usage of read/write function 2. checking for errors 3. reading from the file and writing to the pipe(s) 4. dealing with effective amount of data read (ret variable) so that you can write (to "screen" or to an other file descriptor the right amount of data.

Upvotes: 1

Jens
Jens

Reputation: 72697

You are confusing C Standard I/O streams (created with fopen(); written to with fprintf() et al., read with fscanf() et al.) with Unix file descriptor I/O (created with open() or pipe() et al., written to with write() et al., read with read() et al.)

Standard I/O functions take an opaque FILE * as a handle; Unix I/O functions take a file descriptor (a small int) as a handle.

Once you understand the conceptual difference, I'm sure you will realize that

FILE *fd = ...
read(..., &fd, ...);

is reading into a pointer-to-FILE -- not terribly useful :-)

Upvotes: 3

swiley
swiley

Reputation: 72

You're not reading anything in to buf as far as I can tell.

Upvotes: 0

Related Questions