1

I have a program which spawns off a child script. The child script simply respews any input 1/2 the time back to STDOUT and STDERR. The other half the time, it quietly consumes it. What I am getting is a mis-timing of the results from the writes to the child:

Line1: STDOUT Line number 1
Line3: STDERR Line number 1
Line3: STDOUT Line number 3
Getting leftovers
endLine: STDERR Line number 3

Line number 1 should have been read via the same Line1 read. Similarly, Line number 3 should also have been picked up by the same Line3 attempt.

The problem I am trying to solve is that I want to be able to write a line of data to the child, check for any response(s) and repeat. Here are the test programs:

Child script:

#! /usr/bin/perl 

$| = 1;
select (STDERR);
$|=1;

my $i = 0;
open (F,">> e.out");
select F;
$|=1;
select (STDOUT);

while (<>) {
  chomp;
  print F "($_)\n";
  if ($i++) {
    print "STDOUT $_\n";
    print STDERR "STDERR $_\n";
  }
  $i %= 2;
}
close F;

Parent C program:

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>

main () {
  pid_t pid;
  int p2child[2];
  int c2parent[2];

  pipe (p2child);
  pipe (c2parent);

  if ((pid = fork()) < 0) {
    fprintf (stderr, "Fork error: %s\n", strerror(errno));

/*
  Child Process
*/
  } else if (pid == 0) {
    close (p2child[1]);
    dup2 (p2child[0], STDIN_FILENO);
    close (c2parent[0]);
    dup2 (c2parent[1], STDOUT_FILENO);
    dup2 (c2parent[1], STDERR_FILENO);

    if (execlp ("./e", "./e", 0 )) {
perror("Exec failed");
    }
/*
  Parent Process
*/
  } else {
    FILE* istream;
    FILE* ostream;
    char line[80];
    fd_set set;
    struct timeval timeout;
    int ret;
    int counter;

    close (p2child[0]);
    close (c2parent[1]);

    ostream = fdopen (p2child[1], "w");
    istream = fdopen (c2parent[0], "r");

    for (counter = 0; counter < 5; counter++) {
      fprintf (ostream, "Line number %d\n", counter);
      fflush (ostream);

      do {

        FD_ZERO(&set);
        FD_SET(c2parent[0], &set);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;
        ret = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
        if (ret > 0) {
          fgets(line, 80, istream);
          fprintf (stdout, "Line%d: %s", counter, line);
          fflush (stdout);
        }
      } while (ret > 0);
    }

fprintf (stdout, "Getting leftovers\n");
    while (fgets(line, 80, istream)) {
      fprintf (stdout, "endLine: %s", line);
      fflush (stdout);
    }

    close (p2child[1]);
    close (c2parent[0]);

    waitpid (pid, NULL, 0);
  }
  fprintf (stderr, "Exiting\n");
}
1
  • An additional note. I have found that no matter how many lines the child program returns, only the first line is picked up after it is sent. All remaining lines get picked up 2 iterations later + the 1 line from that iteration. Commented Jan 24, 2012 at 22:29

1 Answer 1

0

When you call fgets(), you read a line input from the stream but stdio itself may have read more and buffered it; this is your problem. select() is returning 0 earlier than you expect because the prior fgets() call caused stdio to soak up all the remaining input. As a test, replace

                fgets(line, 80, istream);

in the select loop with

                char *p = line;
                do {
                    read(c2parent[0], p, 1);
                } while (*p++ != '\n');

and you should see the reads and writes fall into lockstep with no leftover input.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.