From 8bb78dda609dbbdfcf11f37851727cd47f01cab5 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@Germany>
Date: Mon, 18 Jul 2016 13:37:39 +0200
Subject: [PATCH] ENH: provide a vfork/exec version of system (issue #185)

The normal library system() command uses 'fork', which causes
problems on IB+OPENMPI.

STYLE: add Foam:: qualifier to system calls to make them easier to spot.
---
 .../foamHelp/helpTypes/helpType/helpType.C    |   2 +-
 .../dataConversion/foamToVTK/foamToVTK.C      |   2 +-
 .../viewFactorsGen/viewFactorsGen.C           |  14 +-
 src/OSspecific/POSIX/POSIX.C                  | 151 +++++++++++++++++-
 .../dynamicLibrary/dynamicCode/dynamicCode.C  |  15 +-
 src/OpenFOAM/global/argList/argList.C         |   2 +-
 src/OpenFOAM/include/OSspecific.H             |  11 +-
 .../functionObjects/systemCall/controlDict    |   9 +-
 .../solarLoad/faceShading/faceShading.C       |  14 +-
 9 files changed, 197 insertions(+), 23 deletions(-)

diff --git a/applications/utilities/miscellaneous/foamHelp/helpTypes/helpType/helpType.C b/applications/utilities/miscellaneous/foamHelp/helpTypes/helpType/helpType.C
index 68ae82fa50a..a5b217e87ee 100644
--- a/applications/utilities/miscellaneous/foamHelp/helpTypes/helpType/helpType.C
+++ b/applications/utilities/miscellaneous/foamHelp/helpTypes/helpType/helpType.C
@@ -151,7 +151,7 @@ void Foam::helpType::displayDoc
         Info<< "Source file: " << classDirectory.c_str() << classFile << nl
             << endl;
 
-        system(docBrowser);
+        Foam::system(docBrowser);
     }
     else
     {
diff --git a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
index 3ac7b6f7668..51a3563f5e1 100644
--- a/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
+++ b/applications/utilities/postProcessing/dataConversion/foamToVTK/foamToVTK.C
@@ -1309,7 +1309,7 @@ int main(int argc, char *argv[])
                       + "_"
                       + procFile.name()
                     );
-                    if (system(cmd.c_str()) == -1)
+                    if (Foam::system(cmd.c_str()) == -1)
                     {
                         WarningInFunction
                             << "Could not execute command " << cmd << endl;
diff --git a/applications/utilities/preProcessing/viewFactorsGen/viewFactorsGen.C b/applications/utilities/preProcessing/viewFactorsGen/viewFactorsGen.C
index 11a8becd5ad..a8e20b188f6 100644
--- a/applications/utilities/preProcessing/viewFactorsGen/viewFactorsGen.C
+++ b/applications/utilities/preProcessing/viewFactorsGen/viewFactorsGen.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -191,9 +191,15 @@ void writeRays
             str << "l " << vertI-1 << ' ' << vertI << nl;
         }
     }
-    string cmd("objToVTK " + fName + " " + fName.lessExt() + ".vtk");
-    Pout<< "cmd:" << cmd << endl;
-    system(cmd);
+    str.flush();
+
+    DynamicList<string> cmd(3);
+    cmd.append("objToVTK");
+    cmd.append(fName);
+    cmd.append(fName.lessExt() + ".vtk");
+
+    Pout<< "cmd: objToVTK " << fName.c_str() << endl;
+    Foam::system(cmd);
 }
 
 
diff --git a/src/OSspecific/POSIX/POSIX.C b/src/OSspecific/POSIX/POSIX.C
index 0b9a541d940..8ecdaeb3e22 100644
--- a/src/OSspecific/POSIX/POSIX.C
+++ b/src/OSspecific/POSIX/POSIX.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2016 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -38,6 +38,8 @@ Description
 #include "timer.H"
 #include "IFstream.H"
 #include "DynamicList.H"
+#include "CStringList.H"
+#include "SubList.H"
 
 #include <fstream>
 #include <cstdlib>
@@ -49,6 +51,7 @@ Description
 #include <pwd.h>
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <netdb.h>
@@ -1166,9 +1169,153 @@ bool Foam::ping(const string& hostname, const label timeOut)
 }
 
 
+namespace Foam
+{
+//! \cond fileScope
+static int waitpid(const pid_t pid)
+{
+    // child status, return code from the exec etc.
+    int status = 0;
+
+    // in parent - blocking wait
+    // modest treatment of signals (in child)
+    // treat 'stopped' like exit (suspend/continue)
+    while (true)
+    {
+        pid_t wpid = ::waitpid(pid, &status, WUNTRACED);
+
+        if (wpid == -1)
+        {
+            FatalErrorInFunction
+                << "some error occurred in child"
+                << exit(FatalError);
+            break;
+        }
+
+        if (WIFEXITED(status))
+        {
+            // child exited, get its return status
+            return WEXITSTATUS(status);
+        }
+
+        if (WIFSIGNALED(status))
+        {
+            // child terminated by some signal
+            return WTERMSIG(status);
+        }
+
+        if (WIFSTOPPED(status))
+        {
+            // child stopped by some signal
+            return WSTOPSIG(status);
+        }
+
+        FatalErrorInFunction
+            << "programming error, status from waitpid() not handled: "
+            << status
+            << exit(FatalError);
+    }
+
+    return -1;  // should not happen
+}
+//! \endcond
+}
+
+
 int Foam::system(const std::string& command)
 {
-    return ::system(command.c_str());
+    if (command.empty())
+    {
+        // Treat an empty command as a successful no-op.
+        // From 'man sh' POSIX (man sh):
+        //   "If the command_string operand is an empty string,
+        //    sh shall exit with a zero exit status."
+        return 0;
+    }
+
+    pid_t child_pid = ::vfork();   // NB: vfork, not fork!
+    if (child_pid == -1)
+    {
+        FatalErrorInFunction
+            << "vfork() failed for system command " << command
+            << exit(FatalError);
+    }
+
+    if (child_pid == 0)
+    {
+        // in child
+
+        // execl uses the current environ
+        (void) ::execl
+        (
+            "/bin/sh",          // Path of the shell
+            "sh",               // Command-name (name for the shell)
+            "-c",               // Read commands from the command_string operand.
+            command.c_str(),    // Command string
+            reinterpret_cast<char *>(0)
+        );
+
+        // obviously failed, since exec should not return at all
+        FatalErrorInFunction
+            << "exec failed: " << command
+            << exit(FatalError);
+    }
+
+
+    // in parent - blocking wait
+    return waitpid(child_pid);
+}
+
+
+int Foam::system(const Foam::UList<Foam::string>& command)
+{
+    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 NULL-terminated list of c-strings
+
+        CStringList args(SubList<string>(command, 0));
+        if (argc > 1)
+        {
+            args.reset(SubList<string>(command, argc-1, 1));
+        }
+
+        // execvp uses the current environ
+        (void) ::execvp(command[0].c_str(), args.strings());
+
+        // obviously failed, since exec should not return at all
+        FatalErrorInFunction
+            << "exec(" << command[0] << ", ...) failed"
+            << exit(FatalError);
+    }
+
+
+    // in parent - blocking wait
+    return waitpid(child_pid);
 }
 
 
diff --git a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C
index 04932fceea3..19882241e34 100644
--- a/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C
+++ b/src/OpenFOAM/db/dynamicLibrary/dynamicCode/dynamicCode.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2015 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -493,10 +493,17 @@ bool Foam::dynamicCode::copyOrCreateFiles(const bool verbose) const
 
 bool Foam::dynamicCode::wmakeLibso() const
 {
-    const Foam::string wmakeCmd("wmake -s libso " + this->codePath());
-    Info<< "Invoking " << wmakeCmd << endl;
+    DynamicList<string> cmd(4);
+    cmd.append("wmake");
+    cmd.append("-s");
+    cmd.append("libso");
+    cmd.append(this->codePath());
 
-    if (Foam::system(wmakeCmd))
+    // NOTE: could also resolve wmake command explicitly
+    //   cmd[0] = stringOps::expand("$WM_PROJECT_DIR/wmake/wmake");
+
+    Info<< "Invoking wmake libso " << this->codePath().c_str() << endl;
+    if (Foam::system(cmd))
     {
         return false;
     }
diff --git a/src/OpenFOAM/global/argList/argList.C b/src/OpenFOAM/global/argList/argList.C
index 5d391afaafe..8f489589b02 100644
--- a/src/OpenFOAM/global/argList/argList.C
+++ b/src/OpenFOAM/global/argList/argList.C
@@ -1170,7 +1170,7 @@ void Foam::argList::displayDoc(bool source) const
 
         Info<< "Show documentation: " << docBrowser.c_str() << endl;
 
-        system(docBrowser);
+        Foam::system(docBrowser);
     }
     else
     {
diff --git a/src/OpenFOAM/include/OSspecific.H b/src/OpenFOAM/include/OSspecific.H
index d190f67045e..4adc48aeeed 100644
--- a/src/OpenFOAM/include/OSspecific.H
+++ b/src/OpenFOAM/include/OSspecific.H
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2011-2015 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -37,6 +37,7 @@ SourceFiles
 #define OSspecific_H
 
 #include "fileNameList.H"
+#include "stringList.H"
 
 #include <sys/types.h>
 
@@ -193,9 +194,15 @@ bool ping(const string&, const label port, const label timeOut);
 //- Check if machine is up by pinging port 22 (ssh) and 222 (rsh)
 bool ping(const string&, const label timeOut=10);
 
-//- Execute the specified command
+//- Execute the specified command via the shell.
+//  Uses vfork/execl internally.
+//  Where possible, use the list version instead.
 int system(const std::string& command);
 
+//- Execute the specified command with arguments.
+//  Uses vfork/execvp internally
+int system(const UList<string>& command);
+
 //- Open a shared library. Return handle to library. Print error message
 //  if library cannot be loaded (check = true)
 void* dlOpen(const fileName& lib, const bool check = true);
diff --git a/src/postProcessing/functionObjects/systemCall/controlDict b/src/postProcessing/functionObjects/systemCall/controlDict
index ff192e197cf..124c5406b9b 100644
--- a/src/postProcessing/functionObjects/systemCall/controlDict
+++ b/src/postProcessing/functionObjects/systemCall/controlDict
@@ -62,14 +62,15 @@ functions
         // called at the end of the run
         endCalls
         (
-            "echo \*\*\* writing .bashrc \*\*\*"
-            "cat ~/.bashrc"
-            "echo \*\*\* done \*\*\*"
+            // Note: single quotes to avoid shell expansion
+            "echo '*** listing ~/.bashrc ***'"
+            "cat ~/.bashrc; echo '*** done ***'"
         );
 
-        // called every ouput time
+        // called every output time
         writeCalls
         (
+            // Note: can also backslash to escape shell meta-characters
             "echo \*\*\* writing data \*\*\*"
         );
     }
diff --git a/src/thermophysicalModels/radiation/radiationModels/solarLoad/faceShading/faceShading.C b/src/thermophysicalModels/radiation/radiationModels/solarLoad/faceShading/faceShading.C
index 6188b294681..d162635f342 100644
--- a/src/thermophysicalModels/radiation/radiationModels/solarLoad/faceShading/faceShading.C
+++ b/src/thermophysicalModels/radiation/radiationModels/solarLoad/faceShading/faceShading.C
@@ -3,7 +3,7 @@
   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
    \\    /   O peration     |
     \\  /    A nd           | Copyright (C) 2015 OpenFOAM Foundation
-     \\/     M anipulation  |
+     \\/     M anipulation  | Copyright (C) 2016 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -62,9 +62,15 @@ void Foam::faceShading::writeRays
         vertI++;
         str << "l " << vertI-1 << ' ' << vertI << nl;
     }
-    string cmd("objToVTK " + fName + " " + fName.lessExt() + ".vtk");
-    Pout<< "cmd:" << cmd << endl;
-    system(cmd);
+    str.flush();
+
+    DynamicList<string> cmd(3);
+    cmd.append("objToVTK");
+    cmd.append(fName);
+    cmd.append(fName.lessExt() + ".vtk");
+
+    Pout<< "cmd: objToVTK " << fName.c_str() << endl;
+    Foam::system(cmd);
 }
 
 
-- 
GitLab