1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/corefoundation/utilsexec_cf.cpp 
   3 // Purpose:     Execution-related utilities for Darwin 
   4 // Author:      David Elliott, Ryan Norton (wxMacExecute) 
   5 // Modified by: Stefan Csomor (added necessary wxT for unicode builds) 
   8 // Copyright:   (c) David Elliott, Ryan Norton 
   9 // Licence:     wxWindows licence 
  10 // Notes:       This code comes from src/mac/carbon/utilsexc.cpp,1.11 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 #include "wx/wxprec.h" 
  17 #endif //ndef WX_PRECOMP 
  18 #include "wx/unix/execute.h" 
  19 #include "wx/stdpaths.h" 
  21 #include "wx/apptrait.h" 
  22 #include "wx/thread.h" 
  23 #include "wx/process.h" 
  27 // Use polling instead of Mach ports, which doesn't work on Intel 
  28 // due to task_for_pid security issues. 
  30 // http://developer.apple.com/technotes/tn/tn2050.html 
  32 // What's a better test for Intel vs PPC? 
  33 #ifdef WORDS_BIGENDIAN 
  42 class wxProcessTerminationEventHandler
: public wxEvtHandler
 
  45     wxProcessTerminationEventHandler(wxEndProcessData
* data
) 
  48         Connect(-1, wxEVT_END_PROCESS
, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate
)); 
  51     void OnTerminate(wxProcessEvent
& event
) 
  53         Disconnect(-1, wxEVT_END_PROCESS
, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate
)); 
  54         wxHandleProcessTermination(m_data
); 
  56         // NOTE: We don't use this to delay destruction until the next idle run but rather to 
  57         // avoid killing ourselves while our caller (which is our wxEvtHandler superclass 
  58         // ProcessPendingEvents) still needs our m_eventsLocker to be valid. 
  59         // Since we're in the GUI library we can guarantee that ScheduleForDestroy is using 
  60         // the GUI implementation which delays destruction and not the base implementation 
  61         // which does it immediately. 
  62         wxTheApp
->GetTraits()->ScheduleForDestroy(this); 
  65     wxEndProcessData
* m_data
; 
  68 class wxProcessTerminationThread
: public wxThread
 
  71     wxProcessTerminationThread(wxEndProcessData
* data
, wxProcessTerminationEventHandler
* handler
): wxThread(wxTHREAD_DETACHED
) 
  77     virtual void* Entry(); 
  79     wxProcessTerminationEventHandler
* m_handler
; 
  80     wxEndProcessData
* m_data
; 
  83 // The problem with this is that we may be examining the 
  84 // process e.g. in OnIdle at the point this cleans up the process, 
  85 // so we need to delay until it's safe. 
  87 void* wxProcessTerminationThread::Entry() 
  93         int rc 
= waitpid(abs(m_data
->pid
), & status
, 0); 
  96             if ((rc 
!= -1) && WIFEXITED(status
)) 
  97                 m_data
->exitcode 
= WEXITSTATUS(status
); 
  99                 m_data
->exitcode 
= -1; 
 101             wxProcessEvent event
; 
 102             wxPostEvent(m_handler
, event
); 
 111 int wxAddProcessCallbackForPid(wxEndProcessData 
*proc_data
, int pid
) 
 116     wxProcessTerminationEventHandler
* handler 
= new wxProcessTerminationEventHandler(proc_data
); 
 117     wxProcessTerminationThread
* thread 
= new wxProcessTerminationThread(proc_data
, handler
); 
 119     if (thread
->Create() != wxTHREAD_NO_ERROR
) 
 121         wxLogDebug(wxT("Could not create termination detection thread.")); 
 131 #else // !wxUSE_THREADS 
 132 int wxAddProcessCallbackForPid(wxEndProcessData
*, int) 
 134     wxLogDebug(wxT("Could not create termination detection thread.")); 
 137 #endif // wxUSE_THREADS/!wxUSE_THREADS 
 139 #else // !USE_POLLING 
 141 #include <CoreFoundation/CFMachPort.h> 
 143 #include <mach/mach.h> 
 146 void wxMAC_MachPortEndProcessDetect(CFMachPortRef 
WXUNUSED(port
), void *data
) 
 148     wxEndProcessData 
*proc_data 
= (wxEndProcessData
*)data
; 
 149     wxLogDebug(wxT("Process ended")); 
 151     int rc 
= waitpid(abs(proc_data
->pid
), &status
, WNOHANG
); 
 154         wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!")); 
 157     if((rc 
!= -1) && WIFEXITED(status
)) 
 158         proc_data
->exitcode 
= WEXITSTATUS(status
); 
 160         proc_data
->exitcode 
= -1; 
 161     wxHandleProcessTermination(proc_data
); 
 164 int wxAddProcessCallbackForPid(wxEndProcessData 
*proc_data
, int pid
) 
 168     kern_return_t    kernResult
; 
 169     mach_port_t    taskOfOurProcess
; 
 170     mach_port_t    machPortForProcess
; 
 171     taskOfOurProcess 
= mach_task_self(); 
 172     if(taskOfOurProcess 
== MACH_PORT_NULL
) 
 174         wxLogDebug(wxT("No mach_task_self()")); 
 177     wxLogDebug(wxT("pid=%d"),pid
); 
 178     kernResult 
= task_for_pid(taskOfOurProcess
,pid
, &machPortForProcess
); 
 179     if(kernResult 
!= KERN_SUCCESS
) 
 181         wxLogDebug(wxT("no task_for_pid()")); 
 182         // try seeing if it is already dead or something 
 183         // FIXME: a better method would be to call the callback function 
 184         // from idle time until the process terminates. Of course, how 
 185         // likely is it that it will take more than 0.1 seconds for the 
 186         // mach terminate event to make its way to the BSD subsystem? 
 187         usleep(100); // sleep for 0.1 seconds 
 188         wxMAC_MachPortEndProcessDetect(NULL
, (void*)proc_data
); 
 191     CFMachPortContext termcb_contextinfo
; 
 192     termcb_contextinfo
.version 
= 0; 
 193     termcb_contextinfo
.info 
= (void*)proc_data
; 
 194     termcb_contextinfo
.retain 
= NULL
; 
 195     termcb_contextinfo
.release 
= NULL
; 
 196     termcb_contextinfo
.copyDescription 
= NULL
; 
 197     CFMachPortRef    CFMachPortForProcess
; 
 198     Boolean        ShouldFreePort
; 
 199     CFMachPortForProcess 
= CFMachPortCreateWithPort(NULL
, machPortForProcess
, NULL
, &termcb_contextinfo
, &ShouldFreePort
); 
 200     if(!CFMachPortForProcess
) 
 202         wxLogDebug(wxT("No CFMachPortForProcess")); 
 203         mach_port_deallocate(taskOfOurProcess
, machPortForProcess
); 
 208         kernResult 
= mach_port_deallocate(taskOfOurProcess
, machPortForProcess
); 
 209         if(kernResult
!=KERN_SUCCESS
) 
 211             wxLogDebug(wxT("Couldn't deallocate mach port")); 
 215     CFMachPortSetInvalidationCallBack(CFMachPortForProcess
, &wxMAC_MachPortEndProcessDetect
); 
 216     CFRunLoopSourceRef    runloopsource
; 
 217     runloopsource 
= CFMachPortCreateRunLoopSource(NULL
,CFMachPortForProcess
, (CFIndex
)0); 
 220         wxLogDebug(wxT("Couldn't create runloopsource")); 
 224     CFRelease(CFMachPortForProcess
); 
 226     CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource
,kCFRunLoopDefaultMode
); 
 227     CFRelease(runloopsource
); 
 228     wxLogDebug(wxT("Successfully added notification to the runloop")); 
 232 #endif // USE_POLLING/!USE_POLLING 
 234 ///////////////////////////////////////////////////////////////////////////// 
 235 // New implementation avoiding mach ports entirely. 
 237 #include <CoreFoundation/CFSocket.h> 
 240     Called due to source signal detected by the CFRunLoop. 
 241     This is nearly identical to the wxGTK equivalent. 
 243 extern "C" void WXCF_EndProcessDetector(CFSocketRef s
, 
 244                                         CFSocketCallBackType 
WXUNUSED(callbackType
), 
 245                                         CFDataRef 
WXUNUSED(address
), 
 246                                         void const *WXUNUSED(data
), 
 249     wxEndProcessData 
* const proc_data 
= static_cast<wxEndProcessData
*>(info
); 
 251 /// This code could reasonably be shared between wxMac/wxCocoa and wxGTK /// 
 252     // PID is always positive on UNIX but wx uses the sign bit as a flag. 
 253     int pid 
= (proc_data
->pid 
> 0) ? proc_data
->pid 
: -proc_data
->pid
; 
 255     int rc 
= waitpid(pid
, &status
, WNOHANG
); 
 258         // Keep waiting in case we got a spurious notification 
 259         // NOTE: In my limited testing, this doesn't happen. 
 264     {   // Error.. really shouldn't happen but try to gracefully handle it 
 265         wxLogLastError(_T("waitpid")); 
 266         proc_data
->exitcode 
= -1; 
 269     {   // Process ended for some reason 
 270         wxASSERT_MSG(rc 
== pid
, _T("unexpected waitpid() return value")); 
 272         if(WIFEXITED(status
)) 
 273             proc_data
->exitcode 
= WEXITSTATUS(status
); 
 274         else if(WIFSIGNALED(status
)) 
 275             // wxGTK doesn't do this but why not? 
 276             proc_data
->exitcode 
= -WTERMSIG(status
); 
 278         {   // Should NEVER happen according to waitpid docs 
 279             wxLogError(wxT("waitpid indicates process exited but not due to exiting or signalling")); 
 280             proc_data
->exitcode 
= -1; 
 283 /// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK /// 
 286         Either waitpid failed or the process ended successfully.  Either way, 
 287         we're done.  It's not if waitpid is going to magically succeed when 
 288         we get fired again.  CFSocketInvalidate closes the fd for us and also 
 289         invalidates the run loop source for us which should cause it to 
 290         release the CFSocket (thus causing it to be deallocated) and remove 
 291         itself from the runloop which should release it and cause it to also 
 292         be deallocated.  Of course, it's possible the RunLoop hangs onto 
 293         one or both of them by retaining/releasing them within its stack 
 294         frame.  However, that shouldn't be depended on.  Assume that s is 
 295         deallocated due to the following call. 
 297     CFSocketInvalidate(s
); 
 299     // Now tell wx that the process has ended. 
 300     wxHandleProcessTermination(proc_data
); 
 304     Implements the GUI-specific wxAddProcessCallback for both wxMac and 
 305     wxCocoa using the CFSocket/CFRunLoop API which is available to both. 
 306     Takes advantage of the fact that sockets on UNIX are just regular 
 307     file descriptors and thus even a non-socket file descriptor can 
 308     apparently be used with CFSocket so long as you only tell CFSocket 
 309     to do things with it that would be valid for a non-socket fd. 
 311 int wxAddProcessCallback(wxEndProcessData 
*proc_data
, int fd
) 
 313     static int s_last_tag 
= 0; 
 314     CFSocketContext context 
= 
 316     ,   static_cast<void*>(proc_data
) 
 321     CFSocketRef cfSocket 
= CFSocketCreateWithNative(kCFAllocatorDefault
,fd
,kCFSocketReadCallBack
,&WXCF_EndProcessDetector
,&context
); 
 324         wxLogError(wxT("Failed to create socket for end process detection")); 
 327     CFRunLoopSourceRef runLoopSource 
= CFSocketCreateRunLoopSource(kCFAllocatorDefault
, cfSocket
, /*highest priority:*/0); 
 328     if(runLoopSource 
== NULL
) 
 330         wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection")); 
 331         // closes the fd.. we can't really stop it, nor do we necessarily want to. 
 332         CFSocketInvalidate(cfSocket
); 
 336     // Now that the run loop source has the socket retained and we no longer 
 337     // need to refer to it within this method, we can release it. 
 340     CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource
, kCFRunLoopCommonModes
); 
 341     // Now that the run loop has the source retained we can release it. 
 342     CFRelease(runLoopSource
); 
 345         Feed wx some bullshit.. we don't use it since CFSocket helpfully passes 
 346         itself into our callback and that's enough to be able to 
 347         CFSocketInvalidate it which is all we need to do to get everything we 
 348         just created to be deallocated. 
 353 ///////////////////////////////////////////////////////////////////////////// 
 355 // NOTE: This doesn't really belong here but this was a handy file to 
 356 // put it in because it's already compiled for wxCocoa and wxMac GUI lib. 
 360 static wxStandardPathsCF gs_stdPaths
; 
 361 wxStandardPathsBase
& wxGUIAppTraits::GetStandardPaths()