Passing a file descriptor from `open_memstream` to `dup2`

Viewed 132

I am trying to redirect output from an exec()ed function into a buffer, so I though I would try and use open_memstream to handle the dynamic buffering

I put together this to test it out:

#include <stdio.h>
#include <unistd.h>

int main() {
    char* buffer;
    size_t buffer_len;
    FILE* stream = open_memstream(&buffer, &buffer_len);
    if(!stream) perror("Something went wrong with `open_memstream`!");
    fflush(stream);

    puts("Start");

    if(dup2(fileno(stream), STDOUT_FILENO) == -1) perror("Something went wrong!");

    puts("Internal");

    fclose(stream);

    FILE* f = fopen("out.txt", "w+");
    fputs(buffer, f);
    fclose(f);
}

But running it gives me the error bad file descriptor on dup2, which shouldn't be the case since open_memstream doesn't return NULL which it is supposed to do on error.

Is there something about the implementation of open_memstream that makes it nonviable to manipulate its underlying descriptor? Or am I just being dumb and using a function wrong?

Cheers in advance for any help given, and if this is impossible to do with open_memstream, is there a way to handle it with FILE* instead of using fds directly?

1 Answers

You should check return value (and subsequently errno) after every operation that can go wrong. Here, you are missing a check for fileno(stream) return value.

FILE* stream = open_memstream(&buffer, &buffer_len);
if(!stream) perror("Failed to open_memstream");

int fd = fileno(stream);
if (fd == -1) {
    perror("Failed to get memstream fileno");
    exit(1);
}

When you add the above, your program will fail with message

Failed to get memstream fileno: Bad file descriptor

The reason for this failure is already explained in comments on the question.

Have look at open with the O_TMPFILE parameter, or at memfd_create, which is similar to open_memstream but returns a file descriptor.

These approaches force you to forgo the convenience of having &buffer, &buffer_len. But nothing is actually lost. One can use lseek to learn the tmp file size and then mmap to access it as a memory buffer, getting all the conveniences back.

Related