event type constants to make using Bind() less verbose. E.g.
wxEVT_COMMAND_MENU_SELECTED is now wxEVT_MENU (but the old name remains
available for compatibility) (Catalin Raceanu).
+- Fix wxExecute() implementation under Unix (Rob Bresalier).
- Add wxEvtHandler::CallAfter() method for asynchronous method calls.
- Add support for symlinks to wxFileName (David Hart).
- Add wxDIR_NO_FOLLOW flag for wxDir traversal (David Hart).
// Register the signal wake up pipe with the given dispatcher.
//
- // This is not used anywhere yet but will be soon.
+ // This is used by wxExecute(wxEXEC_NOEVENTS) implementation only.
//
// The pointer to the handler used for processing events on this descriptor
// is returned so that it can be deleted when we no longer needed it.
#ifndef _WX_UNIX_APPTBASE_H_
#define _WX_UNIX_APPTBASE_H_
-struct wxEndProcessData;
-struct wxExecuteData;
+#include "wx/evtloop.h"
+#include "wx/evtloopsrc.h"
+
+class wxExecuteData;
class wxFDIOManager;
class wxEventLoopSourcesManagerBase;
// wxExecute() support methods
// ---------------------------
- // wait for the process termination, return whatever wxExecute() must
- // return
+ // Wait for the process termination and return its exit code or -1 on error.
//
- // base class implementation handles all cases except wxEXEC_SYNC without
- // wxEXEC_NOEVENTS one which is implemented at the GUI level
+ // Notice that this is only used when execData.flags contains wxEXEC_SYNC
+ // and does not contain wxEXEC_NOEVENTS, i.e. when we need to really wait
+ // until the child process exit and dispatch the events while doing it.
virtual int WaitForChild(wxExecuteData& execData);
- // integrate the monitoring of the given fd with the port-specific event
- // loop: when this fd, which corresponds to a dummy pipe opened between the
- // parent and child processes, is closed by the child, the parent is
- // notified about this via a call to wxHandleProcessTermination() function
- //
- // the default implementation uses wxFDIODispatcher and so is suitable for
- // the console applications or ports which don't have any specific event
- // loop
- virtual int AddProcessCallback(wxEndProcessData *data, int fd);
-
#if wxUSE_SOCKETS
// return a pointer to the object which should be used to integrate
// monitoring of the file descriptors to the event loop (currently this is
virtual wxEventLoopSourcesManagerBase* GetEventLoopSourcesManager();
protected:
- // a helper for the implementation of WaitForChild() in wxGUIAppTraits:
- // checks the streams used for redirected IO in execData and returns true
- // if there is any activity in them
- bool CheckForRedirectedIO(wxExecuteData& execData);
+ // Wait for the process termination by running the given event loop until
+ // this happens.
+ //
+ // This is used by the public WaitForChild() after creating the event loop
+ // of the appropriate kind.
+ int RunLoopUntilChildExit(wxExecuteData& execData, wxEventLoopBase& loop);
};
#endif // _WX_UNIX_APPTBASE_H_
public:
virtual wxEventLoopBase *CreateEventLoop();
virtual int WaitForChild(wxExecuteData& execData);
-#ifdef wxHAS_GUI_PROCESS_CALLBACKS
- virtual int AddProcessCallback(wxEndProcessData *data, int fd);
-#endif
#if wxUSE_TIMER
virtual wxTimerImpl *CreateTimerImpl(wxTimer *timer);
#endif
// Author: Vadim Zeitlin
// Id: $Id$
// Copyright: (c) 1998 Robert Roebling, Julian Smart, Vadim Zeitlin
+// (c) 2013 Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifndef _WX_UNIX_EXECUTE_H
#define _WX_UNIX_EXECUTE_H
-#include "wx/unix/pipe.h"
-
-class WXDLLIMPEXP_FWD_BASE wxProcess;
-class wxStreamTempInputBuffer;
+#include "wx/app.h"
+#include "wx/hashmap.h"
+#include "wx/process.h"
-struct wxEndProcessData
-{
- wxEndProcessData()
- {
- pid =
- tag =
- exitcode = -1;
- process = NULL;
- async = false;
- }
+#include "wx/unix/pipe.h"
+#include "wx/private/streamtempinput.h"
- int pid; // pid of the process
- int tag; // port dependent value
- wxProcess *process; // if !NULL: notified on process termination
- int exitcode; // the exit code
- bool async; // if true, delete us on process termination
-};
+class wxEventLoopBase;
-// struct in which information is passed from wxExecute() to wxAppTraits
-// methods
-struct wxExecuteData
+// Information associated with a running child process.
+class wxExecuteData
{
+public:
wxExecuteData()
{
flags =
pid = 0;
+ exitcode = -1;
process = NULL;
-#if wxUSE_STREAMS
- bufOut =
- bufErr = NULL;
+ syncEventLoop = NULL;
+#if wxUSE_STREAMS
fdOut =
fdErr = wxPipe::INVALID_FD;
#endif // wxUSE_STREAMS
}
- // get the FD corresponding to the read end of the process end detection
- // pipe and close the write one
- int GetEndProcReadFD()
- {
- const int fd = pipeEndProcDetect.Detach(wxPipe::Read);
- pipeEndProcDetect.Close();
- return fd;
- }
+ // This must be called in the parent process as soon as fork() returns to
+ // update us with the effective child PID. It also ensures that we handle
+ // SIGCHLD to be able to detect when this PID exits, so wxTheApp must be
+ // available.
+ void OnStart(int pid);
+
+ // Called when the child process exits.
+ void OnExit(int exitcode);
+
+ // Return true if we should (or already did) redirect the child IO.
+ bool IsRedirected() const { return process && process->IsRedirected(); }
// wxExecute() flags
// the pid of the child process
int pid;
+ // The exit code of the process, set once the child terminates.
+ int exitcode;
+
// the associated process object or NULL
wxProcess *process;
- // pipe used for end process detection
- wxPipe pipeEndProcDetect;
+ // Local event loop used to wait for the child process termination in
+ // synchronous execution case. We can't create it ourselves as its exact
+ // type depends on the application kind (console/GUI), so we rely on
+ // wxAppTraits setting up this pointer to point to the appropriate object.
+ wxEventLoopBase *syncEventLoop;
#if wxUSE_STREAMS
// the input buffer bufOut is connected to stdout, this is why it is
// called bufOut and not bufIn
- wxStreamTempInputBuffer *bufOut,
- *bufErr;
+ wxStreamTempInputBuffer bufOut,
+ bufErr;
// the corresponding FDs, -1 if not redirected
int fdOut,
fdErr;
#endif // wxUSE_STREAMS
-};
-// this function is called when the process terminates from port specific
-// callback function and is common to all ports (src/unix/utilsunx.cpp)
-extern WXDLLIMPEXP_BASE void wxHandleProcessTermination(wxEndProcessData *proc_data);
+
+private:
+ // SIGCHLD signal handler that checks whether any of the currently running
+ // children have exited.
+ static void OnSomeChildExited(int sig);
+
+ // All currently running child processes indexed by their PID.
+ //
+ // Notice that the container doesn't own its elements.
+ WX_DECLARE_HASH_MAP(int, wxExecuteData*, wxIntegerHash, wxIntegerEqual,
+ ChildProcessesData);
+ static ChildProcessesData ms_childProcesses;
+
+ wxDECLARE_NO_COPY_CLASS(wxExecuteData);
+};
#endif // _WX_UNIX_EXECUTE_H
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// Name: wx/unix/private/executeiohandler.h
+// Purpose: IO handler class for the FD used by wxExecute() under Unix
+// Author: Rob Bresalier, Vadim Zeitlin
+// Created: 2013-01-06
+// RCS-ID: $Id$
+// Copyright: (c) 2013 Rob Bresalier, Vadim Zeitlin
+// Licence: wxWindows licence
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_UNIX_PRIVATE_EXECUTEIOHANDLER_H_
+#define _WX_UNIX_PRIVATE_EXECUTEIOHANDLER_H_
+
+#include "wx/private/streamtempinput.h"
+
+// This class handles IO events on the pipe FD connected to the child process
+// stdout/stderr and is used by wxExecute().
+//
+// Currently it can derive from either wxEventLoopSourceHandler or
+// wxFDIOHandler depending on the kind of dispatcher/event loop it is used
+// with. In the future, when we get rid of wxFDIOHandler entirely, it will
+// derive from wxEventLoopSourceHandler only.
+template <class T>
+class wxExecuteIOHandlerBase : public T
+{
+public:
+ wxExecuteIOHandlerBase(int fd, wxStreamTempInputBuffer& buf)
+ : m_fd(fd),
+ m_buf(buf)
+ {
+ m_callbackDisabled = false;
+ }
+
+ // Called when the associated descriptor is available for reading.
+ virtual void OnReadWaiting()
+ {
+ // Sync process, process all data coming at us from the pipe so that
+ // the pipe does not get full and cause a deadlock situation.
+ m_buf.Update();
+
+ if ( m_buf.Eof() )
+ DisableCallback();
+ }
+
+ // These methods are never called as we only monitor the associated FD for
+ // reading, but we still must implement them as they're pure virtual in the
+ // base class.
+ virtual void OnWriteWaiting() { }
+ virtual void OnExceptionWaiting() { }
+
+ // Disable any future calls to our OnReadWaiting(), can be called when
+ // we're sure that no more input is forthcoming.
+ void DisableCallback()
+ {
+ if ( !m_callbackDisabled )
+ {
+ m_callbackDisabled = true;
+
+ DoDisable();
+ }
+ }
+
+protected:
+ const int m_fd;
+
+private:
+ virtual void DoDisable() = 0;
+
+ wxStreamTempInputBuffer& m_buf;
+
+ // If true, DisableCallback() had been already called.
+ bool m_callbackDisabled;
+
+ wxDECLARE_NO_COPY_CLASS(wxExecuteIOHandlerBase);
+};
+
+// This is the version used with wxFDIODispatcher, which must be passed to the
+// ctor in order to register this handler with it.
+class wxExecuteFDIOHandler : public wxExecuteIOHandlerBase<wxFDIOHandler>
+{
+public:
+ wxExecuteFDIOHandler(wxFDIODispatcher& dispatcher,
+ int fd,
+ wxStreamTempInputBuffer& buf)
+ : wxExecuteIOHandlerBase<wxFDIOHandler>(fd, buf),
+ m_dispatcher(dispatcher)
+ {
+ dispatcher.RegisterFD(fd, this, wxFDIO_INPUT);
+ }
+
+ virtual ~wxExecuteFDIOHandler()
+ {
+ DisableCallback();
+ }
+
+private:
+ virtual void DoDisable()
+ {
+ m_dispatcher.UnregisterFD(m_fd);
+ }
+
+ wxFDIODispatcher& m_dispatcher;
+
+ wxDECLARE_NO_COPY_CLASS(wxExecuteFDIOHandler);
+};
+
+// And this is the version used with an event loop. As AddSourceForFD() is
+// static, we don't require passing the event loop to the ctor but an event
+// loop must be running to handle our events.
+class wxExecuteEventLoopSourceHandler
+ : public wxExecuteIOHandlerBase<wxEventLoopSourceHandler>
+{
+public:
+ wxExecuteEventLoopSourceHandler(int fd, wxStreamTempInputBuffer& buf)
+ : wxExecuteIOHandlerBase<wxEventLoopSourceHandler>(fd, buf)
+ {
+ m_source = wxEventLoop::AddSourceForFD(fd, this, wxEVENT_SOURCE_INPUT);
+ }
+
+ virtual ~wxExecuteEventLoopSourceHandler()
+ {
+ DisableCallback();
+ }
+
+private:
+ virtual void DoDisable()
+ {
+ delete m_source;
+ m_source = NULL;
+ }
+
+ wxEventLoopSource* m_source;
+
+ wxDECLARE_NO_COPY_CLASS(wxExecuteEventLoopSourceHandler);
+};
+
+#endif // _WX_UNIX_PRIVATE_EXECUTEIOHANDLER_H_
// Name: src/cocoa/evtloop.mm
// Purpose: implements wxEventLoop for Cocoa
// Author: David Elliott
-// Modified by:
// Created: 2003/10/02
// RCS-ID: $Id$
// Copyright: (c) 2003 David Elliott <dfe@cox.net>
+// (c) 2013 Rob Bresalier
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/utils.h"
#endif
-#include "wx/unix/execute.h"
-
#if 0
#ifndef WX_PRECOMP
// Name: src/common/evtloopcmn.cpp
// Purpose: common wxEventLoop-related stuff
// Author: Vadim Zeitlin
-// Modified by:
// Created: 2006-01-12
// RCS-ID: $Id$
-// Copyright: (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
+// Copyright: (c) 2006, 2013 Vadim Zeitlin <vadim@wxwindows.org>
+// (c) 2013 Rob Bresalier
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/utils.h"
#include "wx/evtloop.h"
#include "wx/apptrait.h"
-#include "wx/unix/execute.h"
#include "wx/unix/private/timer.h"
#ifndef WX_PRECOMP
// Name: src/gtk/evtloop.cpp
// Purpose: implements wxEventLoop for GTK+
// Author: Vadim Zeitlin
-// Modified by:
// Created: 10.07.01
// RCS-ID: $Id$
// Copyright: (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
+// (c) 2013 Rob Bresalier, Vadim Zeitlin
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#include "wx/apptrait.h"
#include "wx/process.h"
#include "wx/sysopt.h"
-#ifdef __UNIX__
-#include "wx/unix/execute.h"
-#endif
#include "wx/gtk/private/timer.h"
#include "wx/evtloop.h"
#endif
}
-// ----------------------------------------------------------------------------
-// subprocess routines
-// ----------------------------------------------------------------------------
-
-#ifdef __UNIX__
-
-extern "C" {
-static gboolean EndProcessDetector(GIOChannel* source, GIOCondition, void* data)
-{
- wxEndProcessData * const
- proc_data = static_cast<wxEndProcessData *>(data);
-
- // child exited, end waiting
- close(g_io_channel_unix_get_fd(source));
-
- wxHandleProcessTermination(proc_data);
-
- // don't call us again!
- return false;
-}
-}
-
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- GIOChannel* channel = g_io_channel_unix_new(fd);
- GIOCondition cond = GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR);
- unsigned id = g_io_add_watch(channel, cond, EndProcessDetector, proc_data);
- g_io_channel_unref(channel);
- return int(id);
-}
-
-#endif // __UNIX__
-
// ----------------------------------------------------------------------------
// wxPlatformInfo-related
// ----------------------------------------------------------------------------
#include "wx/evtloop.h"
#include "wx/process.h"
-#include "wx/unix/execute.h"
-
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
// subprocess routines
// ----------------------------------------------------------------------------
-extern "C" {
-static
-void GTK_EndProcessDetector(gpointer data, gint source,
- GdkInputCondition WXUNUSED(condition) )
-{
- wxEndProcessData * const
- proc_data = static_cast<wxEndProcessData *>(data);
-
- // child exited, end waiting
- close(source);
-
- // don't call us again!
- gdk_input_remove(proc_data->tag);
-
- wxHandleProcessTermination(proc_data);
-}
-}
-
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- int tag = gdk_input_add(fd,
- GDK_INPUT_READ,
- GTK_EndProcessDetector,
- (gpointer)proc_data);
-
- return tag;
-}
-
#if wxUSE_TIMER
wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
#pragma message disable nosimpint
#endif
-#include "wx/unix/execute.h"
-
#include <Xm/Xm.h>
#include <Xm/Frame.h>
}
}
-// ----------------------------------------------------------------------------
-// wxExecute stuff
-// ----------------------------------------------------------------------------
-
-static void xt_notify_end_process(XtPointer data, int *WXUNUSED(fid),
- XtInputId *id)
-{
- wxEndProcessData *proc_data = (wxEndProcessData *)data;
-
- wxHandleProcessTermination(proc_data);
-
- // VZ: I think they should be the same...
- wxASSERT( (int)*id == proc_data->tag );
-
- XtRemoveInput(*id);
-}
-
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- XtInputId id = XtAppAddInput((XtAppContext) wxTheApp->GetAppContext(),
- fd,
- (XtPointer *) XtInputReadMask,
- (XtInputCallbackProc) xt_notify_end_process,
- (XtPointer) proc_data);
-
- return (int)id;
-}
-
// ----------------------------------------------------------------------------
// misc
// ----------------------------------------------------------------------------
}
return sError;
} // end of wxPMErrorToStr
-
-// replacement for implementation in unix/utilsunx.cpp,
-// to be used by all X11 based ports.
-struct wxEndProcessData;
-
-void wxHandleProcessTermination(wxEndProcessData *WXUNUSED(proc_data))
-{
- // For now, just do nothing. To be filled in as needed.
-}
// Created: 2009-10-18
// RCS-ID: $Id$
// Copyright: (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
+// (c) 2013 Rob Bresalier
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// Created: 2004-11-04
// RCS-ID: $Id$
// Copyright: (c) David Elliott, Ryan Norton
+// (c) 2013 Rob Bresalier
// Licence: wxWindows licence
// Notes: This code comes from src/osx/carbon/utilsexc.cpp,1.11
/////////////////////////////////////////////////////////////////////////////
#include "wx/log.h"
#include "wx/utils.h"
#endif //ndef WX_PRECOMP
-#include "wx/unix/execute.h"
#include "wx/stdpaths.h"
#include "wx/app.h"
#include "wx/apptrait.h"
#include <CoreFoundation/CFSocket.h>
-/*!
- Called due to source signal detected by the CFRunLoop.
- This is nearly identical to the wxGTK equivalent.
- */
-extern "C" void WXCF_EndProcessDetector(CFSocketRef s,
- CFSocketCallBackType WXUNUSED(callbackType),
- CFDataRef WXUNUSED(address),
- void const *WXUNUSED(data),
- void *info)
-{
- /*
- Either our pipe was closed or the process ended successfully. Either way,
- we're done. It's not if waitpid is going to magically succeed when
- we get fired again. CFSocketInvalidate closes the fd for us and also
- invalidates the run loop source for us which should cause it to
- release the CFSocket (thus causing it to be deallocated) and remove
- itself from the runloop which should release it and cause it to also
- be deallocated. Of course, it's possible the RunLoop hangs onto
- one or both of them by retaining/releasing them within its stack
- frame. However, that shouldn't be depended on. Assume that s is
- deallocated due to the following call.
- */
- CFSocketInvalidate(s);
-
- // Now tell wx that the process has ended.
- wxHandleProcessTermination(static_cast<wxEndProcessData *>(info));
-}
-
-/*!
- Implements the GUI-specific AddProcessCallback() for both wxMac and
- wxCocoa using the CFSocket/CFRunLoop API which is available to both.
- Takes advantage of the fact that sockets on UNIX are just regular
- file descriptors and thus even a non-socket file descriptor can
- apparently be used with CFSocket so long as you only tell CFSocket
- to do things with it that would be valid for a non-socket fd.
- */
-int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
-{
- static int s_last_tag = 0;
- CFSocketContext context =
- { 0
- , static_cast<void*>(proc_data)
- , NULL
- , NULL
- , NULL
- };
- CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context);
- if(cfSocket == NULL)
- {
- wxLogError(wxT("Failed to create socket for end process detection"));
- return 0;
- }
- CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
- if(runLoopSource == NULL)
- {
- wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection"));
- // closes the fd.. we can't really stop it, nor do we necessarily want to.
- CFSocketInvalidate(cfSocket);
- CFRelease(cfSocket);
- return 0;
- }
- // Now that the run loop source has the socket retained and we no longer
- // need to refer to it within this method, we can release it.
- CFRelease(cfSocket);
-
- CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
- // Now that the run loop has the source retained we can release it.
- CFRelease(runLoopSource);
-
- /*
- Feed wx some bullshit.. we don't use it since CFSocket helpfully passes
- itself into our callback and that's enough to be able to
- CFSocketInvalidate it which is all we need to do to get everything we
- just created to be deallocated.
- */
- return ++s_last_tag;
-}
-
#if wxUSE_EVENTLOOP_SOURCE
namespace
void *ctxData)
{
wxLogTrace(wxTRACE_EVT_SOURCE,
- "CFSocket callback, type=%d", callbackType);
+ "CFSocket callback, type=%d", static_cast<int>(callbackType));
wxCFEventLoopSource * const
source = static_cast<wxCFEventLoopSource *>(ctxData);
#endif // WX_PRECOMP
#include "wx/unix/execute.h"
+#include "wx/evtloop.h"
// ============================================================================
// implementation
int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
{
- const int flags = execData.flags;
- if ( !(flags & wxEXEC_SYNC) || (flags & wxEXEC_NOEVENTS) )
- {
- // async or blocking sync cases are already handled by the base class
- // just fine, no need to duplicate its code here
- return wxAppTraits::WaitForChild(execData);
- }
-
- // here we're dealing with the case of synchronous execution when we want
- // to process the GUI events while waiting for the child termination
-
- wxEndProcessData endProcData;
- endProcData.pid = execData.pid;
- endProcData.tag = AddProcessCallback
- (
- &endProcData,
- execData.GetEndProcReadFD()
- );
- endProcData.async = false;
-
-
// prepare to wait for the child termination: show to the user that we're
// busy and refuse all input unless explicitly told otherwise
wxBusyCursor bc;
- wxWindowDisabler wd(!(flags & wxEXEC_NODISABLE));
+ wxWindowDisabler wd(!(execData.flags & wxEXEC_NODISABLE));
- // endProcData.pid will be set to 0 from wxHandleProcessTermination() when
- // the process terminates
- while ( endProcData.pid != 0 )
- {
- // don't consume 100% of the CPU while we're sitting in this
- // loop
- if ( !CheckForRedirectedIO(execData) )
- wxMilliSleep(1);
-
- // give the toolkit a chance to call wxHandleProcessTermination() here
- // and also repaint the GUI and handle other accumulated events
- wxYield();
- }
-
- return endProcData.exitcode;
+ // Allocate an event loop that will be used to wait for the process
+ // to terminate, will handle stdout, stderr, and any other events and pass
+ // it to the common (to console and GUI) code which will run it.
+ wxGUIEventLoop loop;
+ return RunLoopUntilChildExit(execData, loop);
}
-
// Created: 2007-05-07
// RCS-ID: $Id$
// Copyright: (c) 2006 Zork Lukasz Michalski
+// (c) 2009, 2013 Vadim Zeitlin <vadim@wxwidgets.org>
+// (c) 2013 Rob Bresalier
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// Author: Vadim Zeitlin
// Id: $Id$
// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
+// (c) 2013 Rob Bresalier, Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#include "wx/apptrait.h"
#include "wx/process.h"
+#include "wx/scopedptr.h"
#include "wx/thread.h"
#include "wx/cmdline.h"
#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 "wx/private/pipestream.h"
#include "wx/private/streamtempinput.h"
+#include "wx/unix/private/executeiohandler.h"
#endif // HAS_PIPE_STREAMS
#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,
const wxExecuteEnv *env)
}
#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
// 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.
- // Note that while the reading side of the end process detection pipe
- // can be safely closed, we should keep the write one opened, it will
- // be only closed when the process terminates resulting in a read
- // notification to the parent
- const int fdEndProc = execData.pipeEndProcDetect.Detach(wxPipe::Write);
- execData.pipeEndProcDetect.Close();
-
// 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
{
if ( fd != STDIN_FILENO &&
fd != STDOUT_FILENO &&
- fd != STDERR_FILENO &&
- fd != fdEndProc )
+ fd != STDERR_FILENO )
{
close(fd);
}
}
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_STREAMS
- // the input buffer bufOut is connected to stdout, this is why it is
- // called bufOut and not bufIn
- wxStreamTempInputBuffer bufOut,
- bufErr;
if ( process && process->IsRedirected() )
{
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_STREAMS
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;
+ }
+
- return traits->WaitForChild(execData);
+ // 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);
+ }
+
+
+ // 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)
// 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_STREAMS
- bool hasIO = false;
-
- if ( execData.bufOut && execData.bufOut->Update() )
- hasIO = true;
-
- if ( execData.bufErr && execData.bufErr->Update() )
- hasIO = true;
-
- return hasIO;
-#else // !HAS_PIPE_STREAMS
- wxUnusedVar(execData);
-
- return false;
-#endif // HAS_PIPE_STREAMS/!HAS_PIPE_STREAMS
+ 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
-{
-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
+// 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:
- wxEndHandler(wxFDIODispatcher& disp, int fd)
- : wxReadFDIOHandler(disp, 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() )
{
- m_terminated = false;
+ stdoutHandler.reset(new wxExecuteEventLoopSourceHandler
+ (
+ execData.fdOut, execData.bufOut
+ ));
+ stderrHandler.reset(new wxExecuteEventLoopSourceHandler
+ (
+ execData.fdErr, execData.bufErr
+ ));
}
+#endif // wxUSE_STREAMS
- bool Terminated() const { return m_terminated; }
+ // 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;
- virtual void OnReadWaiting() { m_terminated = true; }
-
-private:
- bool m_terminated;
+ // And run it.
+ loop.Run();
- wxDECLARE_NO_COPY_CLASS(wxEndHandler);
-};
+ // The exit code will have been set when the child termination was detected.
+ return execData.exitcode;
+}
-#if HAS_PIPE_STREAMS
+// ----------------------------------------------------------------------------
+// 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_STREAMS
-
-// 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" );
// 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 )
+ switch ( rc )
{
- 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;
- }
+ 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_STREAMS && 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_STREAMS
- 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();
}
}
-
#pragma message disable nosimpint
#endif
-#include "wx/unix/execute.h"
-
#include "wx/x11/private.h"
#include "X11/Xutil.h"
// Created: 2009-01-10
// RCS-ID: $Id$
// Copyright: (c) 2009 Francesco Montorsi
+// (c) 2013 Rob Bresalier, Vadim Zeitlin
+// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------