monkeyking
monkeyking

Reputation: 6948

how to check if a stdout is closed

I got some code that prints to stdout, in pseudo code it looks like

int main(){
    //allocate data
    while(conditional){
      char *string = makedata();
      fprintf(stdout,"%s",string);
    }
    //cleanup 
}

This works fine, if the conditional is toggled to zero, but if I pipe the output like

./a.out |head -n10 >dumped

Then the code never reaches the cleanup part, I don't understand how to check if the stdout gets closed.

Thanks

Upvotes: 7

Views: 3903

Answers (4)

Robᵩ
Robᵩ

Reputation: 168626

Your stdout hasn't been closed, so checking for that will be useless. Your program has received a SIGPIPE and exited. A SIGPIPE is delivered whenever your program writes to a pipe on which there are no readers. In your example, that happens when head exits, closing its stdin.

You should ignore SIGPIPE if you want your program to continue. This code will ignore SIGPIPE:

(void)signal(SIGPIPE, SIG_IGN);

If you don't want to modify your program, you can arrange that something continues to read from the pipe, even after head closes its input. 1

./a.out | ( head -n10 >dumped ; cat > /dev/null )

1: The shell example is valid for bash, maybe not for csh.

Upvotes: 5

William Pursell
William Pursell

Reputation: 212248

As Rob points out in comments to his answer, you are not worried that stdout has been closed; rather, you are worried that the other end of the pipe is closed. This may be pedantry, but it leads to the solution of your problem. Namely, you do not care if stdout is closed, but only if your printf succeeds. You should check the return value of printf: if it is -1, then the write has failed.

As Simon Richter points out, you will never get the return value of printf if you do not ignore SIGPIPE, because a result of writing to stdout when the other side of the pipe has been closed is that SIG_PIPE will be sent to the process. So you need to do something like:

  signal( SIGPIPE, SIG_IGN ); /* Careful: you now must check the return of *all* writes */
  if( fprintf( stdout, ... ) == -1 ) {
    /* handle the error */
  }

Upvotes: 1

Simon Richter
Simon Richter

Reputation: 29586

It is closed by killing the process with SIGPIPE.

If you want to go on when your output is being ignored, then set up a SIG_IGN handler for SIGPIPE, and handle the error from the fprintf (which will be delayed by buffering, so you cannot assume data that has been written has actually reached the user).

Upvotes: 3

Sparky
Sparky

Reputation: 14057

I've not tried this, but provided you were using the standard file descriptor for stdout, you may be able to try performing an fstat(1, &stat_structure) and check the return and error codes.

Upvotes: 0

Related Questions