// this function is called to associate the port-specific callback with the
// child process. The return valus is port-specific.
extern int wxAddProcessCallback(wxEndProcessData *proc_data, int fd);
+// For ports (e.g. DARWIN) which can add callbacks based on the pid
+extern int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid);
#endif // _WX_UNIX_EXECUTE_H
// Name: utilsexec.cpp
// Purpose: Execution-related utilities
// Author: Stefan Csomor
-// Modified by:
+// Modified by: David Elliott
// Created: 1998-01-01
// RCS-ID: $Id$
// Copyright: (c) Stefan Csomor
#pragma implementation
#endif
+#include "wx/log.h"
#include "wx/utils.h"
#ifdef __DARWIN__
#include "wx/unix/execute.h"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <mach/mach.h>
+#include <CoreFoundation/CFMachPort.h>
#endif
#include <stdio.h>
#endif
#ifdef __DARWIN__
-int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
+void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
{
- wxFAIL_MSG( _T("wxAddProcessCallback() not yet implemented") );
- return 0;
+ wxEndProcessData *proc_data = (wxEndProcessData*)data;
+ wxLogDebug("Wow.. this actually worked!");
+ int status = 0;
+ int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
+ if(!rc)
+ {
+ wxLogDebug("Mach port was invalidated, but process hasn't terminated!");
+ return;
+ }
+ if((rc != -1) && WIFEXITED(status))
+ proc_data->exitcode = WEXITSTATUS(status);
+ else
+ proc_data->exitcode = -1;
+ wxHandleProcessTermination(proc_data);
+}
+
+int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
+{
+ if(pid < 1)
+ return -1;
+ kern_return_t kernResult;
+ mach_port_t taskOfOurProcess;
+ mach_port_t machPortForProcess;
+ taskOfOurProcess = mach_task_self();
+ if(taskOfOurProcess == MACH_PORT_NULL)
+ {
+ wxLogDebug("No mach_task_self()");
+ return -1;
+ }
+ wxLogDebug("pid=%d",pid);
+ kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
+ if(kernResult != KERN_SUCCESS)
+ {
+ wxLogDebug("no task_for_pid()");
+ // try seeing if it is already dead or something
+ // FIXME: a better method would be to call the callback function
+ // from idle time until the process terminates. Of course, how
+ // likely is it that it will take more than 0.1 seconds for the
+ // mach terminate event to make its way to the BSD subsystem?
+ usleep(100); // sleep for 0.1 seconds
+ wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data);
+ return -1;
+ }
+ CFMachPortContext termcb_contextinfo;
+ termcb_contextinfo.version = NULL;
+ termcb_contextinfo.info = (void*)proc_data;
+ termcb_contextinfo.retain = NULL;
+ termcb_contextinfo.release = NULL;
+ termcb_contextinfo.copyDescription = NULL;
+ CFMachPortRef CFMachPortForProcess;
+ Boolean ShouldFreePort;
+ CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort);
+ if(!CFMachPortForProcess)
+ {
+ wxLogDebug("No CFMachPortForProcess");
+ mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+ return -1;
+ }
+ if(ShouldFreePort)
+ {
+ kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+ if(kernResult!=KERN_SUCCESS)
+ {
+ wxLogDebug("Couldn't deallocate mach port");
+ return -1;
+ }
+ }
+ CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
+ CFRunLoopSourceRef runloopsource;
+ runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
+ if(!runloopsource)
+ {
+ wxLogDebug("Couldn't create runloopsource");
+ return -1;
+ }
+
+ CFRelease(CFMachPortForProcess);
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
+ CFRelease(runloopsource);
+ wxLogDebug("Successfully added notification to the runloop");
+ return 0;
}
#endif
// Name: utilsexec.cpp
// Purpose: Execution-related utilities
// Author: Stefan Csomor
-// Modified by:
+// Modified by: David Elliott
// Created: 1998-01-01
// RCS-ID: $Id$
// Copyright: (c) Stefan Csomor
#pragma implementation
#endif
+#include "wx/log.h"
#include "wx/utils.h"
#ifdef __DARWIN__
#include "wx/unix/execute.h"
+#include <unistd.h>
+#include <sys/wait.h>
+#include <mach/mach.h>
+#include <CoreFoundation/CFMachPort.h>
#endif
#include <stdio.h>
#endif
#ifdef __DARWIN__
-int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
+void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
{
- wxFAIL_MSG( _T("wxAddProcessCallback() not yet implemented") );
- return 0;
+ wxEndProcessData *proc_data = (wxEndProcessData*)data;
+ wxLogDebug("Wow.. this actually worked!");
+ int status = 0;
+ int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
+ if(!rc)
+ {
+ wxLogDebug("Mach port was invalidated, but process hasn't terminated!");
+ return;
+ }
+ if((rc != -1) && WIFEXITED(status))
+ proc_data->exitcode = WEXITSTATUS(status);
+ else
+ proc_data->exitcode = -1;
+ wxHandleProcessTermination(proc_data);
+}
+
+int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
+{
+ if(pid < 1)
+ return -1;
+ kern_return_t kernResult;
+ mach_port_t taskOfOurProcess;
+ mach_port_t machPortForProcess;
+ taskOfOurProcess = mach_task_self();
+ if(taskOfOurProcess == MACH_PORT_NULL)
+ {
+ wxLogDebug("No mach_task_self()");
+ return -1;
+ }
+ wxLogDebug("pid=%d",pid);
+ kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
+ if(kernResult != KERN_SUCCESS)
+ {
+ wxLogDebug("no task_for_pid()");
+ // try seeing if it is already dead or something
+ // FIXME: a better method would be to call the callback function
+ // from idle time until the process terminates. Of course, how
+ // likely is it that it will take more than 0.1 seconds for the
+ // mach terminate event to make its way to the BSD subsystem?
+ usleep(100); // sleep for 0.1 seconds
+ wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data);
+ return -1;
+ }
+ CFMachPortContext termcb_contextinfo;
+ termcb_contextinfo.version = NULL;
+ termcb_contextinfo.info = (void*)proc_data;
+ termcb_contextinfo.retain = NULL;
+ termcb_contextinfo.release = NULL;
+ termcb_contextinfo.copyDescription = NULL;
+ CFMachPortRef CFMachPortForProcess;
+ Boolean ShouldFreePort;
+ CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort);
+ if(!CFMachPortForProcess)
+ {
+ wxLogDebug("No CFMachPortForProcess");
+ mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+ return -1;
+ }
+ if(ShouldFreePort)
+ {
+ kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
+ if(kernResult!=KERN_SUCCESS)
+ {
+ wxLogDebug("Couldn't deallocate mach port");
+ return -1;
+ }
+ }
+ CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
+ CFRunLoopSourceRef runloopsource;
+ runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
+ if(!runloopsource)
+ {
+ wxLogDebug("Couldn't create runloopsource");
+ return -1;
+ }
+
+ CFRelease(CFMachPortForProcess);
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
+ CFRelease(runloopsource);
+ wxLogDebug("Successfully added notification to the runloop");
+ return 0;
}
#endif
wxChar **mb_argv = argv;
#endif // Unicode/ANSI
-#if wxUSE_GUI
+#if wxUSE_GUI && !defined(__DARWIN__)
// create pipes
wxPipe pipeEndProcDetect;
if ( !pipeEndProcDetect.Create() )
return ERROR_RETURN_CODE;
}
-#endif // wxUSE_GUI
+#endif // wxUSE_GUI && !defined(__DARWIN__)
// pipes for inter process communication
wxPipe pipeIn, // stdin
if ( fd == pipeIn[wxPipe::Read]
|| fd == pipeOut[wxPipe::Write]
|| fd == pipeErr[wxPipe::Write]
-#if wxUSE_GUI
+#if wxUSE_GUI && !defined(__DARWIN__)
|| fd == pipeEndProcDetect[wxPipe::Write]
-#endif // wxUSE_GUI
+#endif // wxUSE_GUI && !defined(__DARWIN__)
)
{
// don't close this one, we still need it
}
#endif // !__VMS
-#if wxUSE_GUI
+#if wxUSE_GUI && !defined(__DARWIN__)
// reading side can be safely closed but we should keep the write one
// opened
pipeEndProcDetect.Detach(wxPipe::Write);
pipeEndProcDetect.Close();
-#endif // wxUSE_GUI
+#endif // wxUSE_GUI && !defined(__DARWIN__)
// redirect stdin, stdout and stderr
if ( pipeIn.IsOk() )
#if wxUSE_GUI && !defined(__WXMICROWIN__)
wxEndProcessData *data = new wxEndProcessData;
+ // wxAddProcessCallback is now (with DARWIN) allowed to call the
+ // callback function directly if the process terminates before
+ // the callback can be added to the run loop. Set up the data.
+ if ( flags & wxEXEC_SYNC )
+ {
+ // we may have process for capturing the program output, but it's
+ // not used in wxEndProcessData in the case of sync execution
+ data->process = NULL;
+
+ // sync execution: indicate it by negating the pid
+ data->pid = -pid;
+ }
+ else
+ {
+ // async execution, nothing special to do - caller will be
+ // notified about the process termination if process != NULL, data
+ // will be deleted in GTK_EndProcessDetector
+ data->process = process;
+ data->pid = pid;
+ }
+
+
+#if defined(__DARWIN__)
+ data->tag = wxAddProcessCallbackForPid(data,pid);
+#else
data->tag = wxAddProcessCallback
(
data,
);
pipeEndProcDetect.Close();
+#endif // defined(__DARWIN__)
if ( flags & wxEXEC_SYNC )
{
- // we may have process for capturing the program output, but it's
- // not used in wxEndProcessData in the case of sync execution
- data->process = NULL;
-
- // sync execution: indicate it by negating the pid
- data->pid = -pid;
-
wxBusyCursor bc;
wxWindowDisabler wd;
}
else // async execution
{
- // async execution, nothing special to do - caller will be
- // notified about the process termination if process != NULL, data
- // will be deleted in GTK_EndProcessDetector
- data->process = process;
- data->pid = pid;
-
return pid;
}
#else // !wxUSE_GUI