From 402315849781d02cdf418952996ccc7b404a4fb4 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Wed, 22 Nov 2017 10:48:55 +0100 Subject: [PATCH] ENH: provide system() command with CStringList - allows (for example) splitting a user string on whitespace and passing this to system as a list of arguments, thus bypassing any implicit use of 'sh'. - system() with optional background, for spawning processes. --- src/OSspecific/POSIX/POSIX.C | 99 +++++++++++++++++++++++++++++-- src/OpenFOAM/include/OSspecific.H | 30 ++++++++-- 2 files changed, 119 insertions(+), 10 deletions(-) diff --git a/src/OSspecific/POSIX/POSIX.C b/src/OSspecific/POSIX/POSIX.C index 432dc8ccef..188ee0f2d1 100644 --- a/src/OSspecific/POSIX/POSIX.C +++ b/src/OSspecific/POSIX/POSIX.C @@ -1406,7 +1406,11 @@ static int waitpid(const pid_t pid) } -int Foam::system(const std::string& command) +int Foam::system +( + const std::string& command, + const bool background +) { if (command.empty()) { @@ -1436,7 +1440,7 @@ int Foam::system(const std::string& command) "sh", // Command-name (name for the shell) "-c", // Read commands from command_string operand command.c_str(), // Command string - reinterpret_cast(0) + reinterpret_cast(0) ); // obviously failed, since exec should not return at all @@ -1446,12 +1450,24 @@ int Foam::system(const std::string& command) } - // in parent - blocking wait + // In parent: + + if (background) + { + // Started as background process + return 0; + } + + // blocking wait for the child return waitpid(child_pid); } -int Foam::system(const Foam::UList& command) +int Foam::system +( + const CStringList& command, + const bool background +) { const int argc = command.size(); @@ -1478,7 +1494,70 @@ int Foam::system(const Foam::UList& command) if (child_pid == 0) { - // in child: + // In child: + // Need command and arguments separately. + // args is a nullptr-terminated list of c-strings + + // execvp uses the current environ + (void) ::execvp(command[0], command.strings(1)); + + // obviously failed, since exec should not return at all + FatalErrorInFunction + << "exec(" << command[0] << ", ...) failed" + << exit(FatalError); + } + + + // In parent: + + if (background) + { + // Started as background process + return 0; + } + + // blocking wait for the child + return waitpid(child_pid); +} + + +int Foam::system +( + const Foam::UList& command, + const bool background +) +{ + // In the future simply call the CStringList version: + // + // const CStringList cmd(command); + // return Foam::system(cmd, background); + + const int argc = command.size(); + + if (!argc) + { + // Treat an empty command as a successful no-op. + // For consistency with POSIX (man sh) behaviour for (sh -c command), + // which is what is mostly being replicated here. + return 0; + } + + // NB: use vfork, not fork! + // vfork behaves more like a thread and avoids copy-on-write problems + // triggered by fork. + // The normal system() command has a fork buried in it that causes + // issues with infiniband and openmpi etc. + pid_t child_pid = ::vfork(); + if (child_pid == -1) + { + FatalErrorInFunction + << "vfork() failed for system command " << command[0] + << exit(FatalError); + } + + if (child_pid == 0) + { + // In child: // Need command and arguments separately. // args is a nullptr-terminated list of c-strings @@ -1498,7 +1577,15 @@ int Foam::system(const Foam::UList& command) } - // in parent - blocking wait + // In parent: + + if (background) + { + // Started as background process + return 0; + } + + // blocking wait for the child return waitpid(child_pid); } diff --git a/src/OpenFOAM/include/OSspecific.H b/src/OpenFOAM/include/OSspecific.H index 421ec9e25f..bdd87752c4 100644 --- a/src/OpenFOAM/include/OSspecific.H +++ b/src/OpenFOAM/include/OSspecific.H @@ -46,6 +46,10 @@ SourceFiles namespace Foam { +// Forward declarations +class CStringList; + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // //- Return the PID of this process @@ -206,14 +210,32 @@ bool ping(const std::string& host, const label timeOut=10); // Uses vfork/execl internally. // Where possible, use the list version instead. // -// Treats an empty command as a successful no-op. -int system(const std::string& command); +// \param background return immediately to parent process instead of waiting +// for the child. Can be used (with moderation) to create background +// processes. +// +// \note treats an empty command as a successful no-op. +int system(const std::string& command, const bool background=false); //- Execute the specified command with arguments. // Uses vfork/execvp internally // -// Treats an empty command as a successful no-op. -int system(const UList& command); +// \param background return immediately to parent process instead of waiting +// for the child. Can be used (with moderation) to create background +// processes. +// +// \note treats an empty command as a successful no-op. +int system(const UList& command, const bool background=false); + +//- Execute the specified command with arguments. +// Uses vfork/execvp internally +// +// \param background return immediately to parent process instead of waiting +// for the child. Can be used (with moderation) to create background +// processes. +// +// \note treats an empty command as a successful no-op. +int system(const CStringList& command, const bool background=false); //- Open a shared library and return handle to library. // Print error message if library cannot be loaded (suppress with check=true)