Switch to side-by-side view

--- a/src/utils/execmd.cpp
+++ b/src/utils/execmd.cpp
@@ -15,7 +15,11 @@
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 #ifndef TEST_EXECMD
+#ifdef RECOLL_DATADIR
+#include "autoconfig.h"
+#else
 #include "config.h"
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -31,6 +35,16 @@
 
 #include <vector>
 #include <string>
+#ifdef HAVE_SPAWN_H
+#ifndef __USE_GNU
+#define __USE_GNU
+#define undef__USE_GNU
+#endif
+#include <spawn.h>
+#ifdef undef__USE_GNU
+#undef __USE_GNU
+#endif
+#endif
 
 #include "execmd.h"
 
@@ -150,7 +164,18 @@
     return false;
 }
 
-void  ExecCmd::putenv(const string &ea)
+void ExecCmd::useVfork(bool on)
+{
+    // Just in case: there are competent people who believe that the
+    // dynamic linker can sometimes deadlock if execve() is resolved
+    // inside the vfork/exec window. Make sure it's done now. If "/" is
+    // an executable file, we have a problem.
+    const char *argv[] = {"/", 0};
+    execve("/", (char *const *)argv, environ);
+    o_useVfork  = on;
+}
+
+void ExecCmd::putenv(const string &ea)
 {
     m_env.push_back(ea);
 }
@@ -256,10 +281,17 @@
     }
 
     // Restore SIGTERM to default. Really, signal handling should be
-    // specified when creating the execmd. Help Recoll get rid of its
-    // filter children though. To be fixed one day... Not sure that
-    // all of this is needed. But an ignored sigterm and the masks are
-    // normally inherited.
+    // specified when creating the execmd, there might be other
+    // signals to reset. Resetting SIGTERM helps Recoll get rid of its
+    // filter children for now though. To be fixed one day...
+    // Note that resetting to SIG_DFL is a portable use of
+    // signal(). No need for sigaction() here.
+
+    // There is supposedely a risk of problems if another thread was
+    // calling a signal-affecting function when vfork was called. This
+    // seems acceptable though as no self-respecting thread is going
+    // to mess with the global process signal disposition.
+
     if (signal(SIGTERM, SIG_DFL) == SIG_ERR) {
 	//LOGERR(("ExecCmd::DOCHILD: signal() failed, errno %d\n", errno));
     }
@@ -307,9 +339,9 @@
     libclf_closefrom(3);
 
     execve(cmd.c_str(), (char *const*)argv, (char *const*)envv);
-    // Hu ho. This should never happened as we checked the existence of the
-    // executable before calling dochild... Until we did this, this was 
-    // the chief cause of LOG mutex deadlock
+    // Hu ho. This should never have happened as we checked the
+    // existence of the executable before calling dochild... Until we
+    // did this check, this was the chief cause of LOG mutex deadlock
     LOGERR(("ExecCmd::DOCHILD: execve(%s) failed. errno %d\n", cmd.c_str(),
 	    errno));
     _exit(127);
@@ -392,11 +424,85 @@
         free(envv);
         return -1;
     }
-////////////////////////////////
-
+//////////////////////////////// End vfork child prepare section.
+
+#if HAVE_POSIX_SPAWN && USE_POSIX_SPAWN
+    {
+        posix_spawnattr_t attrs;
+        posix_spawnattr_init (&attrs);
+        short flags;
+        posix_spawnattr_getflags(&attrs, &flags);
+
+        flags |=  POSIX_SPAWN_USEVFORK;
+
+        posix_spawnattr_setpgroup(&attrs, 0);
+        flags |= POSIX_SPAWN_SETPGROUP;
+
+        sigset_t sset;
+        sigemptyset(&sset);
+        posix_spawnattr_setsigmask (&attrs, &sset);
+        flags |= POSIX_SPAWN_SETSIGMASK;
+
+        sigemptyset(&sset);
+        sigaddset(&sset, SIGTERM);
+        posix_spawnattr_setsigdefault(&attrs, &sset);
+        flags |= POSIX_SPAWN_SETSIGDEF;
+
+        posix_spawnattr_setflags(&attrs, flags);
+
+        posix_spawn_file_actions_t facts;
+        posix_spawn_file_actions_init(&facts);
+
+        if (has_input) {
+            posix_spawn_file_actions_addclose(&facts, m_pipein[1]);
+            if (m_pipein[0] != 0) {
+                posix_spawn_file_actions_adddup2(&facts, m_pipein[0], 0);
+                posix_spawn_file_actions_addclose(&facts, m_pipein[0]);
+            }
+        }
+        if (has_output) {
+            posix_spawn_file_actions_addclose(&facts, m_pipeout[0]);
+            if (m_pipeout[1] != 1) {
+                posix_spawn_file_actions_adddup2(&facts, m_pipeout[1], 1);
+                posix_spawn_file_actions_addclose(&facts, m_pipeout[1]);
+            }
+        }
+
+        // Do we need to redirect stderr ?
+        if (!m_stderrFile.empty()) {
+            int oflags = O_WRONLY|O_CREAT;
+#ifdef O_APPEND
+            oflags |= O_APPEND;
+#endif
+            posix_spawn_file_actions_addopen(&facts, 2, m_stderrFile.c_str(), 
+                                             oflags, 0600);
+        }
+        LOGDEB1(("using SPAWN\n"));
+
+        // posix_spawn() does not have any standard way to ask for
+        // calling closefrom(). Afaik there is a solaris extension for this,
+        // but let's just add all fds
+        for (int i = 3; i < libclf_maxfd(); i++) {
+            posix_spawn_file_actions_addclose(&facts, i);
+        }
+
+        int ret = posix_spawn(&m_pid, exe.c_str(), &facts, &attrs, 
+                              (char *const *)argv, (char *const *)envv);
+        posix_spawnattr_destroy(&attrs);
+        posix_spawn_file_actions_destroy(&facts);
+        if (ret) {
+            LOGERR(("ExecCmd::startExec: posix_spawn() failed. errno %d\n", 
+                    ret));
+            return -1;
+        }
+    }
+
+#else
     if (o_useVfork) {
+        LOGDEB1(("using VFORK\n"));
 	m_pid = vfork();
     } else {
+        LOGDEB1(("using FORK\n"));
 	m_pid = fork();
     }
     if (m_pid < 0) {
@@ -411,6 +517,7 @@
 	// dochild does not return. Just in case...
 	_exit(1);
     }
+#endif
 
     // Father process