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"
20 #include "wx/apptrait.h"
21 #include "wx/thread.h"
22 #include "wx/process.h"
26 // Use polling instead of Mach ports, which doesn't work on Intel
27 // due to task_for_pid security issues.
29 // http://developer.apple.com/technotes/tn/tn2050.html
31 // What's a better test for Intel vs PPC?
32 #ifdef WORDS_BIGENDIAN
41 class wxProcessTerminationEventHandler
: public wxEvtHandler
44 wxProcessTerminationEventHandler(wxEndProcessData
* data
)
47 Connect(-1, wxEVT_END_PROCESS
, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate
));
50 void OnTerminate(wxProcessEvent
& event
)
52 Disconnect(-1, wxEVT_END_PROCESS
, wxProcessEventHandler(wxProcessTerminationEventHandler::OnTerminate
));
53 wxHandleProcessTermination(m_data
);
57 wxEndProcessData
* m_data
;
60 class wxProcessTerminationThread
: public wxThread
63 wxProcessTerminationThread(wxEndProcessData
* data
, wxProcessTerminationEventHandler
* handler
): wxThread(wxTHREAD_DETACHED
)
69 virtual void* Entry();
71 wxProcessTerminationEventHandler
* m_handler
;
72 wxEndProcessData
* m_data
;
75 // The problem with this is that we may be examining the
76 // process e.g. in OnIdle at the point this cleans up the process,
77 // so we need to delay until it's safe.
79 void* wxProcessTerminationThread::Entry()
85 int rc
= waitpid(abs(m_data
->pid
), & status
, 0);
88 if ((rc
!= -1) && WIFEXITED(status
))
89 m_data
->exitcode
= WEXITSTATUS(status
);
91 m_data
->exitcode
= -1;
94 wxPostEvent(m_handler
, event
);
103 int wxAddProcessCallbackForPid(wxEndProcessData
*proc_data
, int pid
)
108 wxProcessTerminationEventHandler
* handler
= new wxProcessTerminationEventHandler(proc_data
);
109 wxProcessTerminationThread
* thread
= new wxProcessTerminationThread(proc_data
, handler
);
111 if (thread
->Create() != wxTHREAD_NO_ERROR
)
113 wxLogDebug(wxT("Could not create termination detection thread."));
123 #else // !wxUSE_THREADS
124 int wxAddProcessCallbackForPid(wxEndProcessData
*, int)
126 wxLogDebug(wxT("Could not create termination detection thread."));
129 #endif // wxUSE_THREADS/!wxUSE_THREADS
131 #else // !USE_POLLING
133 #include <CoreFoundation/CFMachPort.h>
135 #include <mach/mach.h>
138 void wxMAC_MachPortEndProcessDetect(CFMachPortRef port
, void *data
)
140 wxEndProcessData
*proc_data
= (wxEndProcessData
*)data
;
141 wxLogDebug(wxT("Process ended"));
143 int rc
= waitpid(abs(proc_data
->pid
), &status
, WNOHANG
);
146 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
149 if((rc
!= -1) && WIFEXITED(status
))
150 proc_data
->exitcode
= WEXITSTATUS(status
);
152 proc_data
->exitcode
= -1;
153 wxHandleProcessTermination(proc_data
);
156 int wxAddProcessCallbackForPid(wxEndProcessData
*proc_data
, int pid
)
160 kern_return_t kernResult
;
161 mach_port_t taskOfOurProcess
;
162 mach_port_t machPortForProcess
;
163 taskOfOurProcess
= mach_task_self();
164 if(taskOfOurProcess
== MACH_PORT_NULL
)
166 wxLogDebug(wxT("No mach_task_self()"));
169 wxLogDebug(wxT("pid=%d"),pid
);
170 kernResult
= task_for_pid(taskOfOurProcess
,pid
, &machPortForProcess
);
171 if(kernResult
!= KERN_SUCCESS
)
173 wxLogDebug(wxT("no task_for_pid()"));
174 // try seeing if it is already dead or something
175 // FIXME: a better method would be to call the callback function
176 // from idle time until the process terminates. Of course, how
177 // likely is it that it will take more than 0.1 seconds for the
178 // mach terminate event to make its way to the BSD subsystem?
179 usleep(100); // sleep for 0.1 seconds
180 wxMAC_MachPortEndProcessDetect(NULL
, (void*)proc_data
);
183 CFMachPortContext termcb_contextinfo
;
184 termcb_contextinfo
.version
= 0;
185 termcb_contextinfo
.info
= (void*)proc_data
;
186 termcb_contextinfo
.retain
= NULL
;
187 termcb_contextinfo
.release
= NULL
;
188 termcb_contextinfo
.copyDescription
= NULL
;
189 CFMachPortRef CFMachPortForProcess
;
190 Boolean ShouldFreePort
;
191 CFMachPortForProcess
= CFMachPortCreateWithPort(NULL
, machPortForProcess
, NULL
, &termcb_contextinfo
, &ShouldFreePort
);
192 if(!CFMachPortForProcess
)
194 wxLogDebug(wxT("No CFMachPortForProcess"));
195 mach_port_deallocate(taskOfOurProcess
, machPortForProcess
);
200 kernResult
= mach_port_deallocate(taskOfOurProcess
, machPortForProcess
);
201 if(kernResult
!=KERN_SUCCESS
)
203 wxLogDebug(wxT("Couldn't deallocate mach port"));
207 CFMachPortSetInvalidationCallBack(CFMachPortForProcess
, &wxMAC_MachPortEndProcessDetect
);
208 CFRunLoopSourceRef runloopsource
;
209 runloopsource
= CFMachPortCreateRunLoopSource(NULL
,CFMachPortForProcess
, (CFIndex
)0);
212 wxLogDebug(wxT("Couldn't create runloopsource"));
216 CFRelease(CFMachPortForProcess
);
218 CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource
,kCFRunLoopDefaultMode
);
219 CFRelease(runloopsource
);
220 wxLogDebug(wxT("Successfully added notification to the runloop"));
224 #endif // USE_POLLING/!USE_POLLING
226 /////////////////////////////////////////////////////////////////////////////
227 // New implementation avoiding mach ports entirely.
229 #include <CoreFoundation/CFSocket.h>
232 Called due to source signal detected by the CFRunLoop.
233 This is nearly identical to the wxGTK equivalent.
235 extern "C" void WXCF_EndProcessDetector(CFSocketRef s
, CFSocketCallBackType callbackType
, CFDataRef address
, void const *data
, void *info
)
237 wxEndProcessData
* const proc_data
= static_cast<wxEndProcessData
*>(info
);
239 /// This code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
240 // PID is always positive on UNIX but wx uses the sign bit as a flag.
241 int pid
= (proc_data
->pid
> 0) ? proc_data
->pid
: -proc_data
->pid
;
243 int rc
= waitpid(pid
, &status
, WNOHANG
);
246 // Keep waiting in case we got a spurious notification
247 // NOTE: In my limited testing, this doesn't happen.
252 { // Error.. really shouldn't happen but try to gracefully handle it
253 wxLogLastError(_T("waitpid"));
254 proc_data
->exitcode
= -1;
257 { // Process ended for some reason
258 wxASSERT_MSG(rc
== pid
, _T("unexpected waitpid() return value"));
260 if(WIFEXITED(status
))
261 proc_data
->exitcode
= WEXITSTATUS(status
);
262 else if(WIFSIGNALED(status
))
263 // wxGTK doesn't do this but why not?
264 proc_data
->exitcode
= -WTERMSIG(status
);
266 { // Should NEVER happen according to waitpid docs
267 wxLogError(wxT("waitpid indicates process exited but not due to exiting or signalling"));
268 proc_data
->exitcode
= -1;
271 /// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
274 Either waitpid failed or the process ended successfully. Either way,
275 we're done. It's not if waitpid is going to magically succeed when
276 we get fired again. CFSocketInvalidate closes the fd for us and also
277 invalidates the run loop source for us which should cause it to
278 release the CFSocket (thus causing it to be deallocated) and remove
279 itself from the runloop which should release it and cause it to also
280 be deallocated. Of course, it's possible the RunLoop hangs onto
281 one or both of them by retaining/releasing them within its stack
282 frame. However, that shouldn't be depended on. Assume that s is
283 deallocated due to the following call.
285 CFSocketInvalidate(s
);
287 // Now tell wx that the process has ended.
288 wxHandleProcessTermination(proc_data
);
292 Implements the GUI-specific wxAddProcessCallback for both wxMac and
293 wxCocoa using the CFSocket/CFRunLoop API which is available to both.
294 Takes advantage of the fact that sockets on UNIX are just regular
295 file descriptors and thus even a non-socket file descriptor can
296 apparently be used with CFSocket so long as you only tell CFSocket
297 to do things with it that would be valid for a non-socket fd.
299 int wxAddProcessCallback(wxEndProcessData
*proc_data
, int fd
)
301 static int s_last_tag
= 0;
302 CFSocketContext context
=
304 , static_cast<void*>(proc_data
)
309 CFSocketRef cfSocket
= CFSocketCreateWithNative(kCFAllocatorDefault
,fd
,kCFSocketReadCallBack
,&WXCF_EndProcessDetector
,&context
);
312 wxLogError("Failed to create socket for end process detection");
315 CFRunLoopSourceRef runLoopSource
= CFSocketCreateRunLoopSource(kCFAllocatorDefault
, cfSocket
, /*highest priority:*/0);
316 if(runLoopSource
== NULL
)
318 wxLogError("Failed to create CFRunLoopSource from CFSocket for end process detection");
319 // closes the fd.. we can't really stop it, nor do we necessarily want to.
320 CFSocketInvalidate(cfSocket
);
324 // Now that the run loop source has the socket retained and we no longer
325 // need to refer to it within this method, we can release it.
328 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource
, kCFRunLoopCommonModes
);
329 // Now that the run loop has the source retained we can release it.
330 CFRelease(runLoopSource
);
333 Feed wx some bullshit.. we don't use it since CFSocket helpfully passes
334 itself into our callback and that's enough to be able to
335 CFSocketInvalidate it which is all we need to do to get everything we
336 just created to be deallocated.
341 /////////////////////////////////////////////////////////////////////////////
343 // NOTE: This doesn't really belong here but this was a handy file to
344 // put it in because it's already compiled for wxCocoa and wxMac GUI lib.
348 static wxStandardPathsCF gs_stdPaths
;
349 wxStandardPathsBase
& wxGUIAppTraits::GetStandardPaths()