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) 6// Created: 2004-11-04 7// RCS-ID: $Id: utilsexc_cf.cpp 49240 2007-10-19 04:42:23Z DE $ 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///////////////////////////////////////////////////////////////////////////// 12 13#include "wx/wxprec.h" 14#ifndef WX_PRECOMP 15 #include "wx/log.h" 16 #include "wx/utils.h" 17#endif //ndef WX_PRECOMP 18#include "wx/unix/execute.h" 19#include "wx/stdpaths.h" 20#include "wx/app.h" 21#include "wx/apptrait.h" 22#include "wx/thread.h" 23#include "wx/process.h" 24 25#include <sys/wait.h> 26 27// Use polling instead of Mach ports, which doesn't work on Intel 28// due to task_for_pid security issues. 29 30// http://developer.apple.com/technotes/tn/tn2050.html 31 32// What's a better test for Intel vs PPC? 33#ifdef WORDS_BIGENDIAN 34#define USE_POLLING 0 35#else 36#define USE_POLLING 1 37#endif 38 39#if USE_POLLING 40 41#if wxUSE_THREADS 42class wxProcessTerminationEventHandler: public wxEvtHandler 43{ 44 public: 45 wxProcessTerminationEventHandler(wxEndProcessData* data) 46 { 47 m_data = data; 48 Connect(-1, wxEVT_END_PROCESS, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate)); 49 } 50 51 void OnTerminate(wxProcessEvent& event) 52 { 53 Disconnect(-1, wxEVT_END_PROCESS, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate)); 54 wxHandleProcessTermination(m_data); 55 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); 63 } 64 65 wxEndProcessData* m_data; 66}; 67 68class wxProcessTerminationThread: public wxThread 69{ 70 public: 71 wxProcessTerminationThread(wxEndProcessData* data, wxProcessTerminationEventHandler* handler): wxThread(wxTHREAD_DETACHED) 72 { 73 m_data = data; 74 m_handler = handler; 75 } 76 77 virtual void* Entry(); 78 79 wxProcessTerminationEventHandler* m_handler; 80 wxEndProcessData* m_data; 81}; 82 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. 86 87void* wxProcessTerminationThread::Entry() 88{ 89 while (true) 90 { 91 usleep(100); 92 int status = 0; 93 int rc = waitpid(abs(m_data->pid), & status, 0); 94 if (rc != 0) 95 { 96 if ((rc != -1) && WIFEXITED(status)) 97 m_data->exitcode = WEXITSTATUS(status); 98 else 99 m_data->exitcode = -1; 100 101 wxProcessEvent event; 102 wxPostEvent(m_handler, event); 103 104 break; 105 } 106 } 107 108 return NULL; 109} 110 111int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid) 112{ 113 if (pid < 1) 114 return -1; 115 116 wxProcessTerminationEventHandler* handler = new wxProcessTerminationEventHandler(proc_data); 117 wxProcessTerminationThread* thread = new wxProcessTerminationThread(proc_data, handler); 118 119 if (thread->Create() != wxTHREAD_NO_ERROR) 120 { 121 wxLogDebug(wxT("Could not create termination detection thread.")); 122 delete thread; 123 delete handler; 124 return -1; 125 } 126 127 thread->Run(); 128 129 return 0; 130} 131#else // !wxUSE_THREADS 132int wxAddProcessCallbackForPid(wxEndProcessData*, int) 133{ 134 wxLogDebug(wxT("Could not create termination detection thread.")); 135 return -1; 136} 137#endif // wxUSE_THREADS/!wxUSE_THREADS 138 139#else // !USE_POLLING 140 141#include <CoreFoundation/CFMachPort.h> 142extern "C" { 143#include <mach/mach.h> 144} 145 146void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data) 147{ 148 wxEndProcessData *proc_data = (wxEndProcessData*)data; 149 wxLogDebug(wxT("Process ended")); 150 int status = 0; 151 int rc = waitpid(abs(proc_data->pid), &status, WNOHANG); 152 if(!rc) 153 { 154 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!")); 155 return; 156 } 157 if((rc != -1) && WIFEXITED(status)) 158 proc_data->exitcode = WEXITSTATUS(status); 159 else 160 proc_data->exitcode = -1; 161 wxHandleProcessTermination(proc_data); 162} 163 164int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid) 165{ 166 if(pid < 1) 167 return -1; 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) 173 { 174 wxLogDebug(wxT("No mach_task_self()")); 175 return -1; 176 } 177 wxLogDebug(wxT("pid=%d"),pid); 178 kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess); 179 if(kernResult != KERN_SUCCESS) 180 { 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); 189 return -1; 190 } 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) 201 { 202 wxLogDebug(wxT("No CFMachPortForProcess")); 203 mach_port_deallocate(taskOfOurProcess, machPortForProcess); 204 return -1; 205 } 206 if(ShouldFreePort) 207 { 208 kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess); 209 if(kernResult!=KERN_SUCCESS) 210 { 211 wxLogDebug(wxT("Couldn't deallocate mach port")); 212 return -1; 213 } 214 } 215 CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect); 216 CFRunLoopSourceRef runloopsource; 217 runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0); 218 if(!runloopsource) 219 { 220 wxLogDebug(wxT("Couldn't create runloopsource")); 221 return -1; 222 } 223 224 CFRelease(CFMachPortForProcess); 225 226 CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode); 227 CFRelease(runloopsource); 228 wxLogDebug(wxT("Successfully added notification to the runloop")); 229 return 0; 230} 231 232#endif // USE_POLLING/!USE_POLLING 233 234///////////////////////////////////////////////////////////////////////////// 235// New implementation avoiding mach ports entirely. 236 237#include <CoreFoundation/CFSocket.h> 238 239/*! 240 Called due to source signal detected by the CFRunLoop. 241 This is nearly identical to the wxGTK equivalent. 242 */ 243extern "C" void WXCF_EndProcessDetector(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, void const *data, void *info) 244{ 245 wxEndProcessData * const proc_data = static_cast<wxEndProcessData*>(info); 246 247/// This code could reasonably be shared between wxMac/wxCocoa and wxGTK /// 248 // PID is always positive on UNIX but wx uses the sign bit as a flag. 249 int pid = (proc_data->pid > 0) ? proc_data->pid : -proc_data->pid; 250 int status = 0; 251 int rc = waitpid(pid, &status, WNOHANG); 252 if(rc == 0) 253 { 254 // Keep waiting in case we got a spurious notification 255 // NOTE: In my limited testing, this doesn't happen. 256 return; 257 } 258 259 if(rc == -1) 260 { // Error.. really shouldn't happen but try to gracefully handle it 261 wxLogLastError(_T("waitpid")); 262 proc_data->exitcode = -1; 263 } 264 else 265 { // Process ended for some reason 266 wxASSERT_MSG(rc == pid, _T("unexpected waitpid() return value")); 267 268 if(WIFEXITED(status)) 269 proc_data->exitcode = WEXITSTATUS(status); 270 else if(WIFSIGNALED(status)) 271 // wxGTK doesn't do this but why not? 272 proc_data->exitcode = -WTERMSIG(status); 273 else 274 { // Should NEVER happen according to waitpid docs 275 wxLogError(wxT("waitpid indicates process exited but not due to exiting or signalling")); 276 proc_data->exitcode = -1; 277 } 278 } 279/// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK /// 280 281 /* 282 Either waitpid failed or the process ended successfully. Either way, 283 we're done. It's not if waitpid is going to magically succeed when 284 we get fired again. CFSocketInvalidate closes the fd for us and also 285 invalidates the run loop source for us which should cause it to 286 release the CFSocket (thus causing it to be deallocated) and remove 287 itself from the runloop which should release it and cause it to also 288 be deallocated. Of course, it's possible the RunLoop hangs onto 289 one or both of them by retaining/releasing them within its stack 290 frame. However, that shouldn't be depended on. Assume that s is 291 deallocated due to the following call. 292 */ 293 CFSocketInvalidate(s); 294 295 // Now tell wx that the process has ended. 296 wxHandleProcessTermination(proc_data); 297} 298 299/*! 300 Implements the GUI-specific wxAddProcessCallback for both wxMac and 301 wxCocoa using the CFSocket/CFRunLoop API which is available to both. 302 Takes advantage of the fact that sockets on UNIX are just regular 303 file descriptors and thus even a non-socket file descriptor can 304 apparently be used with CFSocket so long as you only tell CFSocket 305 to do things with it that would be valid for a non-socket fd. 306 */ 307int wxAddProcessCallback(wxEndProcessData *proc_data, int fd) 308{ 309 static int s_last_tag = 0; 310 CFSocketContext context = 311 { 0 312 , static_cast<void*>(proc_data) 313 , NULL 314 , NULL 315 , NULL 316 }; 317 CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context); 318 if(cfSocket == NULL) 319 { 320 wxLogError(wxT("Failed to create socket for end process detection")); 321 return 0; 322 } 323 CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0); 324 if(runLoopSource == NULL) 325 { 326 wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection")); 327 // closes the fd.. we can't really stop it, nor do we necessarily want to. 328 CFSocketInvalidate(cfSocket); 329 CFRelease(cfSocket); 330 return 0; 331 } 332 // Now that the run loop source has the socket retained and we no longer 333 // need to refer to it within this method, we can release it. 334 CFRelease(cfSocket); 335 336 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes); 337 // Now that the run loop has the source retained we can release it. 338 CFRelease(runLoopSource); 339 340 /* 341 Feed wx some bullshit.. we don't use it since CFSocket helpfully passes 342 itself into our callback and that's enough to be able to 343 CFSocketInvalidate it which is all we need to do to get everything we 344 just created to be deallocated. 345 */ 346 return ++s_last_tag; 347} 348 349///////////////////////////////////////////////////////////////////////////// 350 351// NOTE: This doesn't really belong here but this was a handy file to 352// put it in because it's already compiled for wxCocoa and wxMac GUI lib. 353#if wxUSE_GUI 354 355static wxStandardPathsCF gs_stdPaths; 356wxStandardPathsBase& wxGUIAppTraits::GetStandardPaths() 357{ 358 return gs_stdPaths; 359} 360 361#endif // wxUSE_GUI 362 363