Search code examples
c++linuxshellc++11system

Will std::system execute even after parent process has exited?


I am running a C++ console application in an embedded linux environment. I want to run a std::system command just like this. I have taken tar for an example.

int main(int argc, char *argv[]) {
    std::system("tar xvzf /path/to/some/file.tar.gz");
    exit 0;
}

Question:
If I exit the application right after the tar command like above, will the tar command continue to execute?
I understand that it depends a bit on how tar is implemented itself. But lets say tar doesn't work after parent process exits (considering worst case scenario), is there a way I can run the command std::system command safely in background and exit my app trusting that it will complete its job after my app or parent process has exited?


Solution

  • The commands executed by system() will usually not continue after system() returns. system() starts a new process (using fork() + exec*() or CreateProcess() etc.) and then waits until that process is finished before it returns. If the command however spawns orphaned children, then they may live on.

    This may have that effect depending on the SHELL used by system():

    std::system("nohup tar xvzf /path/to/some/file.tar.gz &");
    

    Since system() starts the command using a shell (probably /bin/sh) and that it in turn uses the process's current environment (most notably PATH and variables that may be used to affect which shared libraries that are used by the command) - and that you can also send command strings with redirects, putting commands in the background (as shown above) etc. - it's often considered a security risk. One way to minimize the risk is to create your own system function that does not use a shell or the environment. Example:

    #include <iostream>
    #include <array>
    #include <type_traits> // std::common_type_t
    #include <cstdlib>     // std::exit
    #include <utility>     // std::forward
    
    // fork, exec, waitpid
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    template<typename... Ts>
    int mysystem(Ts&&... ts) {
        int wstatus=-1;
    
        pid_t pid = fork();
    
        if(pid==0) {          // in child process
            std::array<std::common_type_t<Ts...>, sizeof...(ts) + 1> cmd{ std::forward<Ts>(ts)... };
            execv(cmd[0], const_cast<char* const*>( cmd.data() ));
            std::exit(1); // we'll only get here if execv failed starting the command
    
        } else if(pid!=-1) { // in parent process
            // wait for the child to terminate
            // the exit status from the child will be returned in wstatus
            waitpid(pid, &wstatus, 0); // 0 = wait forever
    
        } // else { /* fork() failed */ }
    
        return wstatus;
    }
    
    int main() {
        //int ws = mysystem("/usr/bin/find", ".");
        //int ws = mysystem("/usr/bin/bash", "-i");
        int ws = mysystem("/usr/bin/tar", "xvzf", "/path/to/some/file.tar.gz");
        std::cout << "--------------------\n"
                     "Exit status:        " << WEXITSTATUS(ws) << "\n"
                     "Termination signal: " << WTERMSIG(ws) << "\n"
                     "Core dumped:        " << std::boolalpha << WCOREDUMP(ws) << "\n";
    }