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?
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";
}