0

I want to fork a process and then do the following in the parent:

  1. Wait until it terminates naturally or timeout period set by the parent expires (something like waitforsingalobject in windows) after which I will kill the process using kill(pid);

  2. Get the exit code of the child process (assuming it exited naturally)

  3. I need to have access to the std::cout of the child process from the parent.

I attempted to use waitpid() however while this allows me access to the return code I cannot implement a timeout using this function.

I also looked at the following solution (https://www.linuxprogrammingblog.com/code-examples/signal-waiting-sigtimedwait) which allows me to implement a time-out however there doesnt seem a way to get the return code.

I geuss my question boils down to, Whats the correct way achieving this in linux?

SFD
  • 565
  • 7
  • 18
  • You could use [`waitpid`](http://man7.org/linux/man-pages/man2/waitpid.2.html) with the `WNOHANG` option to *poll* the status of the child process. Do it in a loop while reading the output of the child process until your timeout (or the child exited). – Some programmer dude May 23 '17 at 13:23
  • Also, if you look closer at the example you link to, it has a `waitpid` call after the child process ended (or was killed) letting you get the exit code. – Some programmer dude May 23 '17 at 13:24
  • the second parameter of ``waitpid`` is the status. You can get the return code from it with the ``WEXITSTATUS`` macro. – nefas May 23 '17 at 13:26
  • Using waitpid with WNOHAND seems like an inefficient and arguably ugly solution. I am not entrily sure what the purpose of the last call to waitpid. Lets assume the child proc doesnt terminate then it exits natrualy, so wont the proccess be gone by the time the waitpid is called? or am I missing something here? – SFD May 23 '17 at 13:34
  • If a child process exits, it will become what is called a "zombie" and linger on in the system (even if nothing is running) until you call one of the `wait` functions to "reap" the status of the process. I think you need to search around a little and read more about how processes in POSIX systems (like Linux and macOS) works. In short, the program you linked to should work exactly like you want to (regarding timeout) if you just get the status from that `waitpid` call (instead of passing `NULL`). – Some programmer dude May 23 '17 at 13:38
  • As for getting the output from a child process, you need to read about *pipes*. It's a common thing to do and there are thousands of examples and tutorials out there. – Some programmer dude May 23 '17 at 13:40
  • You can take a look at the source for [GNU timeout](http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/timeout.c), which does the first 2 things you need. Here's what it does: The timer will send the parent `SIGALRM` when the timer expires, and this will interrupt `waitpid`. The signal handler can call `kill` to send a termination signal to the child. (`kill` is one of the allowable system calls that can be used in a signal handler). – Mark Plotnick May 23 '17 at 14:17
  • Regarding output, I still want the child process to keep outputing to terminal. I just want to get a copy of all its output when it terminates is somethign like this possible? – SFD May 23 '17 at 15:01

1 Answers1

0

You can do #1 and #2 with sigtimedwait function and #3 with pipe:

#include <unistd.h>
#include <signal.h>
#include <iostream>

int main() {
    // Block SIGCHLD, so that it only gets delivered while in sigtimedwait.
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGCHLD);
    sigprocmask(SIG_BLOCK, &sigset, nullptr);

    // Make a pipe to communicate with the child process.
    int child_stdout[2];
    if(pipe(child_stdout))
        abort();

    std::cout.flush();
    std::cerr.flush();
    auto child_pid = fork();
    if(-1 == child_pid)
        abort();

    if(!child_pid) { // In the child process.
        dup2(child_stdout[1], STDOUT_FILENO); // Redirect stdout into the pipe.
        std::cout << "Hello from the child process.\n";
        std::cout.flush();
        sleep(3);
        _exit(3);
    }

    // In the parent process.
    dup2(child_stdout[0], STDIN_FILENO); // Redirect stdin to stdout of the child.
    std::string line;
    getline(std::cin, line);
    std::cout << "Child says: " << line << '\n';

    // Wait for the child to terminate or timeout.
    timespec timeout = {1, 0};
    siginfo_t info;
    auto signo = sigtimedwait(&sigset, &info, &timeout);
    if(-1 == signo) {
        if(EAGAIN == errno) { // Timed out.
            std::cout << "Killing child.\n";
            kill(child_pid, SIGTERM);
        }
        else
            abort();
    }
    else { // The child has terminated.
        std::cout << "Child process terminated with code " << info.si_status << ".\n";
    }
}

Outputs:

Child says: Hello from the child process.
Killing child.

If sleep is commented out:

Child says: Hello from the child process.
Child process terminated with code 3.
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271