]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/utilsunx.cpp
Disable wxUSE_ENH_METAFILE for wxGTK builds.
[wxWidgets.git] / src / unix / utilsunx.cpp
index 219c7727cbb8da1d2c9ce6581c7611dd63927411..d757498cc3242cfdce0d3f7408403bb83fe4a5e3 100644 (file)
@@ -4,6 +4,7 @@
 // Author:      Vadim Zeitlin
 // Id:          $Id$
 // Copyright:   (c) 1998 Robert Roebling, Vadim Zeitlin
+//              (c) 2013 Rob Bresalier, Vadim Zeitlin
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
@@ -37,6 +38,7 @@
 #include "wx/apptrait.h"
 
 #include "wx/process.h"
+#include "wx/scopedptr.h"
 #include "wx/thread.h"
 
 #include "wx/cmdline.h"
@@ -48,9 +50,9 @@
 #include "wx/unix/execute.h"
 #include "wx/unix/private.h"
 
-#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
-#include "wx/private/fdiodispatcher.h"
-#endif
+#include "wx/evtloop.h"
+#include "wx/mstream.h"
+#include "wx/private/fdioeventloopsourcehandler.h"
 
 #include <pwd.h>
 #include <sys/wait.h>       // waitpid()
 #   include <sys/select.h>
 #endif
 
-#define HAS_PIPE_INPUT_STREAM (wxUSE_STREAMS && wxUSE_FILE)
-
-#if HAS_PIPE_INPUT_STREAM
+#define HAS_PIPE_STREAMS (wxUSE_STREAMS && wxUSE_FILE)
 
-// define this to let wxexec.cpp know that we know what we're doing
-#define _WX_USED_BY_WXEXECUTE_
-#include "../common/execcmn.cpp"
+#if HAS_PIPE_STREAMS
 
-#endif // HAS_PIPE_INPUT_STREAM
+#include "wx/private/pipestream.h"
+#include "wx/private/streamtempinput.h"
+#include "wx/unix/private/executeiohandler.h"
 
-#if defined(__MWERKS__) && defined(__MACH__)
-    #ifndef WXWIN_OS_DESCRIPTION
-        #define WXWIN_OS_DESCRIPTION "MacOS X"
-    #endif
-    #ifndef HAVE_NANOSLEEP
-        #define HAVE_NANOSLEEP
-    #endif
-    #ifndef HAVE_UNAME
-        #define HAVE_UNAME
-    #endif
-
-    // our configure test believes we can use sigaction() if the function is
-    // available but Metrowekrs with MSL run-time does have the function but
-    // doesn't have sigaction struct so finally we can't use it...
-    #ifdef __MSL__
-        #undef wxUSE_ON_FATAL_EXCEPTION
-        #define wxUSE_ON_FATAL_EXCEPTION 0
-    #endif
-#endif
+#endif // HAS_PIPE_STREAMS
 
 // not only the statfs syscall is called differently depending on platform, but
 // one of its incarnations, statvfs(), takes different arguments under
 #include <errno.h>
 #include <netdb.h>
 #include <signal.h>
-#include <fcntl.h>          // for O_WRONLY and friends
 #include <time.h>           // nanosleep() and/or usleep()
 #include <ctype.h>          // isspace()
 #include <sys/time.h>       // needed for FD_SETSIZE
     #include <sys/sysinfo.h>   // for SAGET and MINFO structures
 #endif
 
+#ifdef HAVE_SETPRIORITY
+    #include <sys/resource.h>   // for setpriority()
+#endif
+
 // ----------------------------------------------------------------------------
 // conditional compilation
 // ----------------------------------------------------------------------------
@@ -297,7 +282,7 @@ bool wxShutdown(int flags)
 // wxStream classes to support IO redirection in wxExecute
 // ----------------------------------------------------------------------------
 
-#if HAS_PIPE_INPUT_STREAM
+#if HAS_PIPE_STREAMS
 
 bool wxPipeInputStream::CanRead() const
 {
@@ -337,7 +322,46 @@ bool wxPipeInputStream::CanRead() const
     }
 }
 
-#endif // HAS_PIPE_INPUT_STREAM
+size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t size)
+{
+    // We need to suppress error logging here, because on writing to a pipe
+    // which is full, wxFile::Write reports a system error. However, this is
+    // not an extraordinary situation, and it should not be reported to the
+    // user (but if really needed, the program can recognize it by checking
+    // whether LastRead() == 0.) Other errors will be reported below.
+    size_t ret;
+    {
+        wxLogNull logNo;
+        ret = m_file->Write(buffer, size);
+    }
+
+    switch ( m_file->GetLastError() )
+    {
+       // pipe is full
+#ifdef EAGAIN
+       case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+       case EWOULDBLOCK:
+#endif
+           // do not treat it as an error
+           m_file->ClearLastError();
+           // fall through
+
+       // no error
+       case 0:
+           break;
+
+       // some real error
+       default:
+           wxLogSysError(_("Can't write to child process's stdin"));
+           m_lasterror = wxSTREAM_WRITE_ERROR;
+    }
+
+    return ret;
+}
+
+#endif // HAS_PIPE_STREAMS
 
 // ----------------------------------------------------------------------------
 // wxShell
@@ -394,7 +418,7 @@ public:
     ArgsArray(wchar_t **wargv)
     {
         int argc = 0;
-        while ( *wargv++ )
+        while ( wargv[argc] )
             argc++;
 
         Init(argc);
@@ -442,27 +466,100 @@ private:
 bool wxMacLaunch(char **argv);
 #endif
 
-long wxExecute(const wxString& command, int flags, wxProcess *process)
+long wxExecute(const wxString& command, int flags, wxProcess *process,
+        const wxExecuteEnv *env)
 {
     ArgsArray argv(wxCmdLineParser::ConvertStringToArgs(command,
                                                         wxCMD_LINE_SPLIT_UNIX));
 
-    return wxExecute(argv, flags, process);
+    return wxExecute(argv, flags, process, env);
 }
 
 #if wxUSE_UNICODE
 
-long wxExecute(wchar_t **wargv, int flags, wxProcess *process)
+long wxExecute(wchar_t **wargv, int flags, wxProcess *process,
+        const wxExecuteEnv *env)
 {
     ArgsArray argv(wargv);
 
-    return wxExecute(argv, flags, process);
+    return wxExecute(argv, flags, process, env);
 }
 
 #endif // wxUSE_UNICODE
 
+namespace
+{
+
+// Helper function of wxExecute(): wait for the process termination without
+// dispatching any events.
+//
+// This is used in wxEXEC_NOEVENTS case.
+int BlockUntilChildExit(wxExecuteData& execData)
+{
+    wxCHECK_MSG( wxTheApp, -1,
+                    wxS("Can't block until child exit without wxTheApp") );
+
+    // Even if we don't want to dispatch events, we still need to handle
+    // child IO notifications and process termination concurrently, i.e.
+    // we can't simply block waiting for the child to terminate as we would
+    // dead lock if it writes more than the pipe buffer size (typically
+    // 4KB) bytes of output -- it would then block waiting for us to read
+    // the data while we'd block waiting for it to terminate.
+    //
+    // So while we don't use the full blown event loop, we still do use a
+    // dispatcher with which we register just the 3 FDs we're interested
+    // in: the child stdout and stderr and the pipe written to by the
+    // signal handler so that we could react to the child process
+    // termination too.
+
+    // Notice that we must create a new dispatcher object here instead of
+    // reusing the global wxFDIODispatcher::Get() because we want to
+    // monitor only the events on the FDs explicitly registered with this
+    // one and not all the other ones that could be registered with the
+    // global dispatcher (think about the case of nested wxExecute() calls).
+    wxSelectDispatcher dispatcher;
+
+    // Do register all the FDs we want to monitor here: first, the one used to
+    // handle the signals asynchronously.
+    wxScopedPtr<wxFDIOHandler>
+        signalHandler(wxTheApp->RegisterSignalWakeUpPipe(dispatcher));
+
+#if wxUSE_STREAMS
+    // And then the two for the child output and error streams if necessary.
+    wxScopedPtr<wxFDIOHandler>
+        stdoutHandler,
+        stderrHandler;
+    if ( execData.IsRedirected() )
+    {
+        stdoutHandler.reset(new wxExecuteFDIOHandler
+                                (
+                                    dispatcher,
+                                    execData.fdOut,
+                                    execData.bufOut
+                                ));
+        stderrHandler.reset(new wxExecuteFDIOHandler
+                                (
+                                    dispatcher,
+                                    execData.fdErr,
+                                    execData.bufErr
+                                ));
+    }
+#endif // wxUSE_STREAMS
+
+    // And dispatch until the PID is reset from wxExecuteData::OnExit().
+    while ( execData.pid )
+    {
+        dispatcher.Dispatch();
+    }
+
+    return execData.exitcode;
+}
+
+} // anonymous namespace
+
 // wxExecute: the real worker function
-long wxExecute(char **argv, int flags, wxProcess *process)
+long wxExecute(char **argv, int flags, wxProcess *process,
+        const wxExecuteEnv *env)
 {
     // for the sync execution, we return -1 to indicate failure, but for async
     // case we return 0 which is never a valid PID
@@ -494,21 +591,14 @@ long wxExecute(char **argv, int flags, wxProcess *process)
     }
 #endif // __DARWIN__
 
-
     // this struct contains all information which we use for housekeeping
-    wxExecuteData execData;
+    wxScopedPtr<wxExecuteData> execDataPtr(new wxExecuteData);
+    wxExecuteData& execData = *execDataPtr;
+
     execData.flags = flags;
     execData.process = process;
 
-    // create pipes
-    if ( !execData.pipeEndProcDetect.Create() )
-    {
-        wxLogError( _("Failed to execute '%s'\n"), *argv );
-
-        return ERROR_RETURN_CODE;
-    }
-
-    // pipes for inter process communication
+    // create pipes for inter process communication
     wxPipe pipeIn,      // stdin
            pipeOut,     // stdout
            pipeErr;     // stderr
@@ -523,6 +613,21 @@ long wxExecute(char **argv, int flags, wxProcess *process)
         }
     }
 
+    // priority: we need to map wxWidgets priority which is in the range 0..100
+    // to Unix nice value which is in the range -20..19. As there is an odd
+    // number of elements in our range and an even number in the Unix one, we
+    // have to do it in this rather ugly way to guarantee that:
+    //  1. wxPRIORITY_{MIN,DEFAULT,MAX} map to -20, 0 and 19 respectively.
+    //  2. The mapping is monotonously increasing.
+    //  3. The mapping is onto the target range.
+    int prio = process ? process->GetPriority() : 0;
+    if ( prio <= 50 )
+        prio = (2*prio)/5 - 20;
+    else if ( prio < 55 )
+        prio = 1;
+    else
+        prio = (2*prio)/5 - 21;
+
     // fork the process
     //
     // NB: do *not* use vfork() here, it completely breaks this code for some
@@ -556,11 +661,12 @@ long wxExecute(char **argv, int flags, wxProcess *process)
         }
 #endif // !__VMS
 
-        // reading side can be safely closed but we should keep the write one
-        // opened, it will be only closed when the process terminates resulting
-        // in a read notification to the parent
-        execData.pipeEndProcDetect.Detach(wxPipe::Write);
-        execData.pipeEndProcDetect.Close();
+#if defined(HAVE_SETPRIORITY)
+        if ( prio && setpriority(PRIO_PROCESS, 0, prio) != 0 )
+        {
+            wxLogSysError(_("Failed to set process priority"));
+        }
+#endif // HAVE_SETPRIORITY
 
         // redirect stdin, stdout and stderr
         if ( pipeIn.IsOk() )
@@ -577,6 +683,65 @@ long wxExecute(char **argv, int flags, wxProcess *process)
             pipeErr.Close();
         }
 
+        // Close all (presumably accidentally) inherited file descriptors to
+        // avoid descriptor leaks. This means that we don't allow inheriting
+        // them purposefully but this seems like a lesser evil in wx code.
+        // Ideally we'd provide some flag to indicate that none (or some?) of
+        // the descriptors do not need to be closed but for now this is better
+        // than never closing them at all as wx code never used FD_CLOEXEC.
+
+        // TODO: Iterating up to FD_SETSIZE is both inefficient (because it may
+        //       be quite big) and incorrect (because in principle we could
+        //       have more opened descriptions than this number). Unfortunately
+        //       there is no good portable solution for closing all descriptors
+        //       above a certain threshold but non-portable solutions exist for
+        //       most platforms, see [http://stackoverflow.com/questions/899038/
+        //          getting-the-highest-allocated-file-descriptor]
+        for ( int fd = 0; fd < (int)FD_SETSIZE; ++fd )
+        {
+            if ( fd != STDIN_FILENO  &&
+                 fd != STDOUT_FILENO &&
+                 fd != STDERR_FILENO )
+            {
+                close(fd);
+            }
+        }
+
+
+        // Process additional options if we have any
+        if ( env )
+        {
+            // Change working directory if it is specified
+            if ( !env->cwd.empty() )
+                wxSetWorkingDirectory(env->cwd);
+
+            // Change environment if needed.
+            //
+            // NB: We can't use execve() currently because we allow using
+            //     non full paths to wxExecute(), i.e. we want to search for
+            //     the program in PATH. However it just might be simpler/better
+            //     to do the search manually and use execve() envp parameter to
+            //     set up the environment of the child process explicitly
+            //     instead of doing what we do below.
+            if ( !env->env.empty() )
+            {
+                wxEnvVariableHashMap oldenv;
+                wxGetEnvMap(&oldenv);
+
+                // Remove unwanted variables
+                wxEnvVariableHashMap::const_iterator it;
+                for ( it = oldenv.begin(); it != oldenv.end(); ++it )
+                {
+                    if ( env->env.find(it->first) == env->env.end() )
+                        wxUnsetEnv(it->first);
+                }
+
+                // And add the new ones (possibly replacing the old values)
+                for ( it = env->env.begin(); it != env->env.end(); ++it )
+                    wxSetEnv(it->first, it->second);
+            }
+        }
+
         execvp(*argv, argv);
 
         fprintf(stderr, "execvp(");
@@ -599,23 +764,32 @@ long wxExecute(char **argv, int flags, wxProcess *process)
     }
     else // we're in parent
     {
-        // save it for WaitForChild() use
-        execData.pid = pid;
-        if (execData.process)
-            execData.process->SetPid(pid);  // and also in the wxProcess
+        execData.OnStart(pid);
 
         // prepare for IO redirection
 
-#if HAS_PIPE_INPUT_STREAM
-        // the input buffer bufOut is connected to stdout, this is why it is
-        // called bufOut and not bufIn
-        wxStreamTempInputBuffer bufOut,
-                                bufErr;
+#if HAS_PIPE_STREAMS
 
         if ( process && process->IsRedirected() )
         {
+            // Avoid deadlocks which could result from trying to write to the
+            // child input pipe end while the child itself is writing to its
+            // output end and waiting for us to read from it.
+            if ( !pipeIn.MakeNonBlocking(wxPipe::Write) )
+            {
+                // This message is not terrible useful for the user but what
+                // else can we do? Also, should we fail here or take the risk
+                // to continue and deadlock? Currently we choose the latter but
+                // it might not be the best idea.
+                wxLogSysError(_("Failed to set up non-blocking pipe, "
+                                "the program might hang."));
+#if wxUSE_LOG
+                wxLog::FlushActive();
+#endif
+            }
+
             wxOutputStream *inStream =
-                new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
+                new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write));
 
             const int fdOut = pipeOut.Detach(wxPipe::Read);
             wxPipeInputStream *outStream = new wxPipeInputStream(fdOut);
@@ -625,16 +799,16 @@ long wxExecute(char **argv, int flags, wxProcess *process)
 
             process->SetPipeStreams(outStream, inStream, errStream);
 
-            bufOut.Init(outStream);
-            bufErr.Init(errStream);
-
-            execData.bufOut = &bufOut;
-            execData.bufErr = &bufErr;
+            if ( flags & wxEXEC_SYNC )
+            {
+                execData.bufOut.Init(outStream);
+                execData.bufErr.Init(errStream);
 
-            execData.fdOut = fdOut;
-            execData.fdErr = fdErr;
+                execData.fdOut = fdOut;
+                execData.fdErr = fdErr;
+            }
         }
-#endif // HAS_PIPE_INPUT_STREAM
+#endif // HAS_PIPE_STREAMS
 
         if ( pipeIn.IsOk() )
         {
@@ -643,14 +817,32 @@ long wxExecute(char **argv, int flags, wxProcess *process)
             pipeErr.Close();
         }
 
-        // we want this function to work even if there is no wxApp so ensure
-        // that we have a valid traits pointer
-        wxConsoleAppTraits traitsConsole;
-        wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
-        if ( !traits )
-            traits = &traitsConsole;
+        // For the asynchronous case we don't have to do anything else, just
+        // let the process run.
+        if ( !(flags & wxEXEC_SYNC) )
+        {
+            // Ensure that the housekeeping data is kept alive, it will be
+            // destroyed only when the child terminates.
+            execDataPtr.release();
+
+            return execData.pid;
+        }
+
+
+        // If we don't need to dispatch any events, things are relatively
+        // simple and we don't need to delegate to wxAppTraits.
+        if ( flags & wxEXEC_NOEVENTS )
+        {
+            return BlockUntilChildExit(execData);
+        }
+
 
-        return traits->WaitForChild(execData);
+        // If we do need to dispatch events, enter a local event loop waiting
+        // until the child exits. As the exact kind of event loop depends on
+        // the sort of application we're in (console or GUI), we delegate this
+        // to wxAppTraits which virtualizes all the differences between the
+        // console and the GUI programs.
+        return wxApp::GetValidTraits().WaitForChild(execData);
     }
 
 #if !defined(__VMS) && !defined(__INTEL_COMPILER)
@@ -719,11 +911,16 @@ wxString wxGetUserHome( const wxString &user )
 // the trailing newline
 static wxString wxGetCommandOutput(const wxString &cmd)
 {
-    FILE *f = popen(cmd.ToAscii(), "r");
+    // Suppress stderr from the shell to avoid outputting errors if the command
+    // doesn't exist.
+    FILE *f = popen((cmd + " 2>/dev/null").ToAscii(), "r");
     if ( !f )
     {
-        wxLogSysError(wxT("Executing \"%s\" failed"), cmd.c_str());
-        return wxEmptyString;
+        // Notice that this doesn't happen simply if the command doesn't exist,
+        // but only in case of some really catastrophic failure inside popen()
+        // so we should really notify the user about this as this is not normal.
+        wxLogSysError(wxT("Executing \"%s\" failed"), cmd);
+        return wxString();
     }
 
     wxString s;
@@ -893,7 +1090,7 @@ wxLinuxDistributionInfo wxGetLinuxDistributionInfo()
 #endif
 
 // these functions are in src/osx/utilsexc_base.cpp for wxMac
-#ifndef __WXMAC__
+#ifndef __DARWIN__
 
 wxOperatingSystemId wxGetOsVersion(int *verMaj, int *verMin)
 {
@@ -929,7 +1126,7 @@ wxString wxGetOsDescription()
     return wxGetCommandOutput(wxT("uname -s -r -m"));
 }
 
-#endif // !__WXMAC__
+#endif // !__DARWIN__
 
 unsigned long wxGetProcessId()
 {
@@ -1208,139 +1405,67 @@ bool wxHandleFatalExceptions(bool doit)
 // wxExecute support
 // ----------------------------------------------------------------------------
 
-int wxAppTraits::AddProcessCallback(wxEndProcessData *data, int fd)
-{
-    // define a custom handler processing only the closure of the descriptor
-    struct wxEndProcessFDIOHandler : public wxFDIOHandler
-    {
-        wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
-            : m_data(data), m_fd(fd)
-        {
-        }
-
-        virtual void OnReadWaiting()
-        {
-            wxFDIODispatcher::Get()->UnregisterFD(m_fd);
-            close(m_fd);
-
-            wxHandleProcessTermination(m_data);
-
-            delete this;
-        }
-
-        virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
-        virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
-
-        wxEndProcessData * const m_data;
-        const int m_fd;
-    };
-
-    wxFDIODispatcher::Get()->RegisterFD
-                             (
-                                 fd,
-                                 new wxEndProcessFDIOHandler(data, fd),
-                                 wxFDIO_INPUT
-                             );
-    return fd; // unused, but return something unique for the tag
-}
-
-bool wxAppTraits::CheckForRedirectedIO(wxExecuteData& execData)
+int wxAppTraits::WaitForChild(wxExecuteData& execData)
 {
-#if HAS_PIPE_INPUT_STREAM
-    bool hasIO = false;
-
-    if ( execData.bufOut && execData.bufOut->Update() )
-        hasIO = true;
-
-    if ( execData.bufErr && execData.bufErr->Update() )
-        hasIO = true;
-
-    return hasIO;
-#else // !HAS_PIPE_INPUT_STREAM
-    return false;
-#endif // HAS_PIPE_INPUT_STREAM/!HAS_PIPE_INPUT_STREAM
+    wxConsoleEventLoop loop;
+    return RunLoopUntilChildExit(execData, loop);
 }
 
-// helper classes/functions used by WaitForChild()
-namespace
-{
-
-// convenient base class for IO handlers which are registered for read
-// notifications only and which also stores the FD we're reading from
+// This function is common code for both console and GUI applications and used
+// by wxExecute() to wait for the child exit while dispatching events.
 //
-// the derived classes still have to implement OnReadWaiting()
-class wxReadFDIOHandler : public wxFDIOHandler
+// Notice that it should not be used for all the other cases, e.g. when we
+// don't need to wait for the child (wxEXEC_ASYNC) or when the events must not
+// dispatched (wxEXEC_NOEVENTS).
+int
+wxAppTraits::RunLoopUntilChildExit(wxExecuteData& execData,
+                                   wxEventLoopBase& loop)
 {
-public:
-    wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
+    // It is possible that wxExecuteData::OnExit() had already been called
+    // and reset the PID to 0, in which case we don't need to do anything
+    // at all.
+    if ( !execData.pid )
+        return execData.exitcode;
+
+#if wxUSE_STREAMS
+    // Monitor the child streams if necessary.
+    wxScopedPtr<wxEventLoopSourceHandler>
+        stdoutHandler,
+        stderrHandler;
+    if ( execData.IsRedirected() )
     {
-        if ( fd )
-            disp.RegisterFD(fd, this, wxFDIO_INPUT);
+        stdoutHandler.reset(new wxExecuteEventLoopSourceHandler
+                                (
+                                    execData.fdOut, execData.bufOut
+                                ));
+        stderrHandler.reset(new wxExecuteEventLoopSourceHandler
+                                (
+                                    execData.fdErr, execData.bufErr
+                                ));
     }
+#endif // wxUSE_STREAMS
 
-    virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
-    virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
-
-protected:
-    const int m_fd;
-
-    wxDECLARE_NO_COPY_CLASS(wxReadFDIOHandler);
-};
-
-// class for monitoring our end of the process detection pipe, simply sets a
-// flag when input on the pipe (which must be due to EOF) is detected
-class wxEndHandler : public wxReadFDIOHandler
-{
-public:
-    wxEndHandler(wxFDIODispatcher& disp, int fd)
-        : wxReadFDIOHandler(disp, fd)
-    {
-        m_terminated = false;
-    }
+    // Store the event loop in the data associated with the child
+    // process so that it could exit the loop when the child exits.
+    execData.syncEventLoop = &loop;
 
-    bool Terminated() const { return m_terminated; }
+    // And run it.
+    loop.Run();
 
-    virtual void OnReadWaiting() { m_terminated = true; }
-
-private:
-    bool m_terminated;
-
-    wxDECLARE_NO_COPY_CLASS(wxEndHandler);
-};
+    // The exit code will have been set when the child termination was detected.
+    return execData.exitcode;
+}
 
-#if HAS_PIPE_INPUT_STREAM
+// ----------------------------------------------------------------------------
+// wxExecuteData
+// ----------------------------------------------------------------------------
 
-// class for monitoring our ends of child stdout/err, should be constructed
-// with the FD and stream from wxExecuteData and will do nothing if they're
-// invalid
-//
-// unlike wxEndHandler this class registers itself with the provided dispatcher
-class wxRedirectedIOHandler : public wxReadFDIOHandler
+namespace
 {
-public:
-    wxRedirectedIOHandler(wxFDIODispatcher& disp,
-                          int fd,
-                          wxStreamTempInputBuffer *buf)
-        : wxReadFDIOHandler(disp, fd),
-          m_buf(buf)
-    {
-    }
-
-    virtual void OnReadWaiting()
-    {
-        m_buf->Update();
-    }
-
-private:
-    wxStreamTempInputBuffer * const m_buf;
-
-    wxDECLARE_NO_COPY_CLASS(wxRedirectedIOHandler);
-};
 
-#endif // HAS_PIPE_INPUT_STREAM
-
-// helper function which calls waitpid() and analyzes the result
-int DoWaitForChild(int pid, int flags = 0)
+// Helper function that checks whether the child with the given PID has exited
+// and fills the provided parameter with its return code if it did.
+bool CheckForChildExit(int pid, int* exitcodeOut)
 {
     wxASSERT_MSG( pid > 0, "invalid PID" );
 
@@ -1349,124 +1474,150 @@ int DoWaitForChild(int pid, int flags = 0)
     // loop while we're getting EINTR
     for ( ;; )
     {
-        rc = waitpid(pid, &status, flags);
+        rc = waitpid(pid, &status, WNOHANG);
 
         if ( rc != -1 || errno != EINTR )
             break;
     }
 
-    if ( rc == 0 )
-    {
-        // This can only happen if the child application closes our dummy pipe
-        // that is used to monitor its lifetime; in that case, our best bet is
-        // to pretend the process did terminate, because otherwise wxExecute()
-        // would hang indefinitely (OnReadWaiting() won't be called again, the
-        // descriptor is closed now).
-        wxLogDebug("Child process (PID %d) still alive but pipe closed so "
-                   "generating a close notification", pid);
-    }
-    else if ( rc == -1 )
-    {
-        wxLogLastError(wxString::Format("waitpid(%d)", pid));
-    }
-    else // child did terminate
+    switch ( rc )
     {
-        wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
-
-        // notice that the caller expects the exit code to be signed, e.g. -1
-        // instead of 255 so don't assign WEXITSTATUS() to an int
-        signed char exitcode;
-        if ( WIFEXITED(status) )
-            exitcode = WEXITSTATUS(status);
-        else if ( WIFSIGNALED(status) )
-            exitcode = -WTERMSIG(status);
-        else
-        {
-            wxLogError("Child process (PID %d) exited for unknown reason, "
-                       "status = %d", pid, status);
-            exitcode = -1;
-        }
+        case 0:
+            // No error but the child is still running.
+            return false;
 
-        return exitcode;
-    }
+        case -1:
+            // Checking child status failed. Invalid PID?
+            wxLogLastError(wxString::Format("waitpid(%d)", pid));
+            return false;
 
-    return -1;
+        default:
+            // Child did terminate.
+            wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
+
+            // notice that the caller expects the exit code to be signed, e.g. -1
+            // instead of 255 so don't assign WEXITSTATUS() to an int
+            signed char exitcode;
+            if ( WIFEXITED(status) )
+                exitcode = WEXITSTATUS(status);
+            else if ( WIFSIGNALED(status) )
+                exitcode = -WTERMSIG(status);
+            else
+            {
+                wxLogError("Child process (PID %d) exited for unknown reason, "
+                           "status = %d", pid, status);
+                exitcode = -1;
+            }
+
+            if ( exitcodeOut )
+                *exitcodeOut = exitcode;
+
+            return true;
+    }
 }
 
 } // anonymous namespace
 
-int wxAppTraits::WaitForChild(wxExecuteData& execData)
+wxExecuteData::ChildProcessesData wxExecuteData::ms_childProcesses;
+
+/* static */
+void wxExecuteData::OnSomeChildExited(int WXUNUSED(sig))
 {
-    if ( !(execData.flags & wxEXEC_SYNC) )
-    {
-        // asynchronous execution: just launch the process and return,
-        // endProcData will be destroyed when it terminates (currently we leak
-        // it if the process doesn't terminate before we do and this should be
-        // fixed but it's not a real leak so it's not really very high
-        // priority)
-        wxEndProcessData *endProcData = new wxEndProcessData;
-        endProcData->process = execData.process;
-        endProcData->pid = execData.pid;
-        endProcData->tag = AddProcessCallback
-                           (
-                             endProcData,
-                             execData.GetEndProcReadFD()
-                           );
-        endProcData->async = true;
-
-        return execData.pid;
-    }
-    //else: synchronous execution case
+    // We know that some child process has terminated, but we don't know which
+    // one, so check all of them (notice that more than one could have exited).
+    //
+    // An alternative approach would be to call waitpid(-1, &status, WNOHANG)
+    // (in a loop to take care of the multiple children exiting case) and
+    // perhaps this would be more efficient. But for now this seems to work.
 
-#if HAS_PIPE_INPUT_STREAM && wxUSE_SOCKETS
-    wxProcess * const process = execData.process;
-    if ( process && process->IsRedirected() )
+
+    // Make a copy of the list before iterating over it to avoid problems due
+    // to deleting entries from it in the process.
+    const ChildProcessesData allChildProcesses = ms_childProcesses;
+    for ( ChildProcessesData::const_iterator it = allChildProcesses.begin();
+          it != allChildProcesses.end();
+          ++it )
     {
-        // we can't simply block waiting for the child to terminate as we would
-        // dead lock if it writes more than the pipe buffer size (typically
-        // 4KB) bytes of output -- it would then block waiting for us to read
-        // the data while we'd block waiting for it to terminate
+        const int pid = it->first;
+
+        // Check whether this child exited.
+        int exitcode;
+        if ( !CheckForChildExit(pid, &exitcode) )
+            continue;
+
+        // And handle its termination if it did.
         //
-        // so multiplex here waiting for any input from the child or closure of
-        // the pipe used to indicate its termination
-        wxSelectDispatcher disp;
+        // Notice that this will implicitly remove it from ms_childProcesses.
+        it->second->OnExit(exitcode);
+    }
+}
 
-        wxEndHandler endHandler(disp, execData.GetEndProcReadFD());
+void wxExecuteData::OnStart(int pid_)
+{
+    wxCHECK_RET( wxTheApp,
+                 wxS("Ensure wxTheApp is set before calling wxExecute()") );
 
-        wxRedirectedIOHandler outHandler(disp, execData.fdOut, execData.bufOut),
-                              errHandler(disp, execData.fdErr, execData.bufErr);
+    // Setup the signal handler for SIGCHLD to be able to detect the child
+    // termination.
+    //
+    // Notice that SetSignalHandler() is idempotent, so it's fine to call
+    // it more than once with the same handler.
+    wxTheApp->SetSignalHandler(SIGCHLD, OnSomeChildExited);
 
-        while ( !endHandler.Terminated() )
-        {
-            disp.Dispatch();
-        }
-    }
-    //else: no IO redirection, just block waiting for the child to exit
-#endif // HAS_PIPE_INPUT_STREAM
 
-    return DoWaitForChild(execData.pid);
+    // Remember the child PID to be able to wait for it later.
+    pid = pid_;
+
+    // Also save it in wxProcess where it will be accessible to the user code.
+    if ( process )
+        process->SetPid(pid);
+
+    // Finally, add this object itself to the list of child processes so that
+    // we can check for its termination the next time we get SIGCHLD.
+    ms_childProcesses[pid] = this;
 }
 
-void wxHandleProcessTermination(wxEndProcessData *data)
+void wxExecuteData::OnExit(int exitcode_)
 {
-    data->exitcode = DoWaitForChild(data->pid, WNOHANG);
+    // Remove this process from the hash list of child processes that are
+    // still open as soon as possible to ensure we don't process it again even
+    // if another SIGCHLD happens.
+    if ( !ms_childProcesses.erase(pid) )
+    {
+        wxFAIL_MSG(wxString::Format(wxS("Data for PID %d not in the list?"), pid));
+    }
 
-    // notify user about termination if required
-    if ( data->process )
+
+    exitcode = exitcode_;
+
+#if wxUSE_STREAMS
+    if ( IsRedirected() )
     {
-        data->process->OnTerminate(data->pid, data->exitcode);
+        // Read the remaining data in a blocking way: this is fine because the
+        // child has already exited and hence all the data must be already
+        // available in the streams buffers.
+        bufOut.ReadAll();
+        bufErr.ReadAll();
     }
+#endif // wxUSE_STREAMS
 
-    if ( data->async )
+    // Notify user about termination if required
+    if ( !(flags & wxEXEC_SYNC) )
     {
-        // in case of asynchronous execution we don't need this data any more
+        if ( process )
+            process->OnTerminate(pid, exitcode);
+
+        // in case of asynchronous execution we don't need this object any more
         // after the child terminates
-        delete data;
+        delete this;
     }
     else // sync execution
     {
         // let wxExecute() know that the process has terminated
-        data->pid = 0;
+        pid = 0;
+
+        // Stop the event loop for synchronous wxExecute() if we're running one.
+        if ( syncEventLoop )
+            syncEventLoop->ScheduleExit();
     }
 }
-