0

Basically, I want to store entire program output in buffers for longer runtimes. Issue is, popen(3) forks a process and since data to be read by poll(2) is delivered in buffered blocks, any intermittent lines between each subsequent call of poll(2) may result in output being skipped in the child process. I was hoping blocking with poll(2) would temporarily halt the forked process, but this is not the case. Is there some way to gain control of this forked process that would allow it to pause, read input, pause, read input, ...? I don't see any way of dropping popen(3), but it looks like poll(2) should be replaced.

/tmp/script.sh

#!/bin/bash

for i in {1..5}
do
    echo $i
    sleep 1
done

main.c

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>

#define OUT_LEN 256
#define CMD_LEN 128

void die(char *msg)
{
    printf("Error: %s\n", msg);
    exit(EXIT_FAILURE);
}

int main()
{
    FILE *fp;
    char cmd[CMD_LEN+1] = "/tmp/script.sh";
    char output[OUT_LEN+1];
    struct pollfd *ps;
    int n = 1, c, x;

    if((fp = popen(cmd, "r")) == NULL)
        die("failed in popen()");

    output[OUT_LEN] = '\0';
    ps = calloc(n, sizeof(struct pollfd));
    ps[0].fd = fp->_fileno;
    ps[0].events = POLLIN;

    for(x = 0; (c = fgetc(fp)) != EOF && x < OUT_LEN; ++x)
    {   
        if(poll(ps, n, 0) == -1) { die("poll()"); }
        if(ps[0].revents & POLLIN)
            output[x] = c;
    }   
    if(x >= OUT_LEN)
        die("LEN too short for stdout of cmd");

    printf("see output below:\n%s", output);

    return 0;
}

Desired output:

see output below:
1
2
3
4
5

Actual output:

see output below:
lwoivresbo
  • 13
  • 1
  • 4
  • 1
    I do not understand. So do not use `poll`, you already read stuff with `fgetc`. Not to mention, it's not valid to use file descriptor with `FILE*` at the same time. – KamilCuk Jun 10 '22 at 21:09
  • The pipe which `popen` uses is an ordinary one, and the writer — your child — will in general automatically pause (block) if it is full, so you don't lose data. If that's not happening, you may need to investigate what that child process might be doing. – Steve Summit Jun 10 '22 at 21:13
  • 3
    *since `poll(2)` uses buffered blocks to read input* huh? poll doesn't read anything; it just tells you when a descriptor has data ready to read. – Shawn Jun 10 '22 at 21:15
  • I agree with what the above comments said - however you do want to consider using `select()` here instead of `poll()`. – BadZen Jun 10 '22 at 21:17
  • Don't use `fp->_fileno`. There's a standard Unix function `fileno()` that does this. – Barmar Jun 10 '22 at 21:22
  • @KamilCuk Oddly enough this has made some progress. I assumed FILE * was just a struct using a file descriptor under the hood. – lwoivresbo Jun 10 '22 at 21:38
  • Your code seems to be backwards -- you're calling `fgetc(fp)` *before* you call `poll()`. This probably isn't interacting well with stdio's buffering. When I added some printf's to the code, I found that `poll()` is returning but `ps[0].revents & POLLIN` is never true. – Barmar Jun 10 '22 at 21:38
  • @Barmar Yes, it is now working with ```char c[1]```, ```read(ps[0].fd, c, 1)```, and placing it after the ```poll()```. – lwoivresbo Jun 10 '22 at 21:43

0 Answers0