+public:
+ wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
+ {
+ if ( fd )
+ disp.RegisterFD(fd, this, wxFDIO_INPUT);
+ }
+
+ 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;
+ }
+
+ bool Terminated() const { return m_terminated; }
+
+ virtual void OnReadWaiting() { m_terminated = true; }
+
+private:
+ bool m_terminated;
+
+ wxDECLARE_NO_COPY_CLASS(wxEndHandler);
+};
+
+#if HAS_PIPE_STREAMS
+
+// 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
+{
+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_STREAMS
+
+// helper function which calls waitpid() and analyzes the result
+int DoWaitForChild(int pid, int flags = 0)
+{
+ wxASSERT_MSG( pid > 0, "invalid PID" );
+
+ int status, rc;
+
+ // loop while we're getting EINTR
+ for ( ;; )
+ {
+ rc = waitpid(pid, &status, flags);
+
+ 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
+ {
+ 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;
+ }
+
+ return exitcode;
+ }
+
+ return -1;
+}
+
+} // anonymous namespace
+
+int wxAppTraits::WaitForChild(wxExecuteData& execData)
+{
+ 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
+
+#if HAS_PIPE_STREAMS && wxUSE_SOCKETS
+ wxProcess * const process = execData.process;
+ if ( process && process->IsRedirected() )
+ {
+ // 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 multiplex here waiting for any input from the child or closure of
+ // the pipe used to indicate its termination
+ wxSelectDispatcher disp;
+
+ wxEndHandler endHandler(disp, execData.GetEndProcReadFD());
+
+ wxRedirectedIOHandler outHandler(disp, execData.fdOut, execData.bufOut),
+ errHandler(disp, execData.fdErr, execData.bufErr);
+
+ while ( !endHandler.Terminated() )
+ {
+ disp.Dispatch();
+ }
+ }
+ //else: no IO redirection, just block waiting for the child to exit
+#endif // HAS_PIPE_STREAMS
+
+ return DoWaitForChild(execData.pid);
+}
+
+void wxHandleProcessTermination(wxEndProcessData *data)
+{
+ data->exitcode = DoWaitForChild(data->pid, WNOHANG);
+
+ // notify user about termination if required
+ if ( data->process )
+ {
+ data->process->OnTerminate(data->pid, data->exitcode);
+ }
+
+ if ( data->async )
+ {
+ // in case of asynchronous execution we don't need this data any more
+ // after the child terminates
+ delete data;
+ }
+ else // sync execution
+ {
+ // let wxExecute() know that the process has terminated
+ data->pid = 0;
+ }