]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/utilsexc_cf.cpp
unifying CFTypes
[wxWidgets.git] / src / mac / corefoundation / utilsexc_cf.cpp
CommitLineData
5d553c56
DE
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/mac/corefoundation/utilsexec_cf.cpp
3// Purpose: Execution-related utilities for Darwin
62705a27 4// Author: David Elliott, Ryan Norton (wxMacExecute)
5d553c56
DE
5// Modified by: Stefan Csomor (added necessary wxT for unicode builds)
6// Created: 2004-11-04
7// RCS-ID: $Id$
62705a27 8// Copyright: (c) David Elliott, Ryan Norton
24498521 9// Licence: wxWindows licence
5d553c56
DE
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"
fc480dc1 19#include "wx/stdpaths.h"
5a9d14c1 20#include "wx/app.h"
fc480dc1 21#include "wx/apptrait.h"
110ffa16
JS
22#include "wx/thread.h"
23#include "wx/process.h"
5d553c56 24
f523d7ce
VZ
25#include <sys/wait.h>
26
110ffa16
JS
27// Use polling instead of Mach ports, which doesn't work on Intel
28// due to task_for_pid security issues.
62705a27 29
653a54d0
SC
30// http://developer.apple.com/technotes/tn/tn2050.html
31
110ffa16
JS
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
4fa167e6 41#if wxUSE_THREADS
110ffa16
JS
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);
5a9d14c1
DE
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);
110ffa16
JS
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;
eff41cb9 93 int rc = waitpid(abs(m_data->pid), & status, 0);
110ffa16
JS
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 }
9e34f56b 107
110ffa16
JS
108 return NULL;
109}
110
111int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
112{
113 if (pid < 1)
114 return -1;
115
9e34f56b 116 wxProcessTerminationEventHandler* handler = new wxProcessTerminationEventHandler(proc_data);
110ffa16 117 wxProcessTerminationThread* thread = new wxProcessTerminationThread(proc_data, handler);
9e34f56b 118
110ffa16
JS
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();
9e34f56b 128
110ffa16
JS
129 return 0;
130}
4fa167e6
PC
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
110ffa16 138
f523d7ce 139#else // !USE_POLLING
62705a27 140
5d553c56 141#include <CoreFoundation/CFMachPort.h>
5d553c56
DE
142extern "C" {
143#include <mach/mach.h>
144}
145
89954433 146void wxMAC_MachPortEndProcessDetect(CFMachPortRef WXUNUSED(port), void *data)
5d553c56 147{
24498521 148 wxEndProcessData *proc_data = (wxEndProcessData*)data;
3bdb628b 149 wxLogDebug(wxT("Process ended"));
24498521
DE
150 int status = 0;
151 int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
152 if(!rc)
153 {
f523d7ce
VZ
154 wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
155 return;
24498521
DE
156 }
157 if((rc != -1) && WIFEXITED(status))
f523d7ce 158 proc_data->exitcode = WEXITSTATUS(status);
24498521 159 else
f523d7ce 160 proc_data->exitcode = -1;
24498521 161 wxHandleProcessTermination(proc_data);
5d553c56
DE
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;
b409fa19 192 termcb_contextinfo.version = 0;
5d553c56
DE
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 }
9e34f56b 223
5d553c56
DE
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
f523d7ce 232#endif // USE_POLLING/!USE_POLLING
110ffa16 233
a3261ffb
DE
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 */
89954433
VZ
243extern "C" void WXCF_EndProcessDetector(CFSocketRef s,
244 CFSocketCallBackType WXUNUSED(callbackType),
245 CFDataRef WXUNUSED(address),
246 void const *WXUNUSED(data),
247 void *info)
a3261ffb
DE
248{
249 wxEndProcessData * const proc_data = static_cast<wxEndProcessData*>(info);
250
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;
254 int status = 0;
255 int rc = waitpid(pid, &status, WNOHANG);
256 if(rc == 0)
257 {
258 // Keep waiting in case we got a spurious notification
259 // NOTE: In my limited testing, this doesn't happen.
260 return;
261 }
262
263 if(rc == -1)
264 { // Error.. really shouldn't happen but try to gracefully handle it
265 wxLogLastError(_T("waitpid"));
266 proc_data->exitcode = -1;
267 }
268 else
269 { // Process ended for some reason
270 wxASSERT_MSG(rc == pid, _T("unexpected waitpid() return value"));
271
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);
277 else
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;
281 }
282 }
283/// The above code could reasonably be shared between wxMac/wxCocoa and wxGTK ///
284
285 /*
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.
296 */
297 CFSocketInvalidate(s);
298
299 // Now tell wx that the process has ended.
300 wxHandleProcessTermination(proc_data);
301}
302
303/*!
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.
310 */
311int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
312{
313 static int s_last_tag = 0;
314 CFSocketContext context =
315 { 0
316 , static_cast<void*>(proc_data)
317 , NULL
318 , NULL
319 , NULL
320 };
321 CFSocketRef cfSocket = CFSocketCreateWithNative(kCFAllocatorDefault,fd,kCFSocketReadCallBack,&WXCF_EndProcessDetector,&context);
322 if(cfSocket == NULL)
323 {
2263f62d 324 wxLogError(wxT("Failed to create socket for end process detection"));
a3261ffb
DE
325 return 0;
326 }
327 CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, /*highest priority:*/0);
328 if(runLoopSource == NULL)
329 {
2263f62d 330 wxLogError(wxT("Failed to create CFRunLoopSource from CFSocket for end process detection"));
a3261ffb
DE
331 // closes the fd.. we can't really stop it, nor do we necessarily want to.
332 CFSocketInvalidate(cfSocket);
333 CFRelease(cfSocket);
334 return 0;
335 }
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.
338 CFRelease(cfSocket);
339
340 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
341 // Now that the run loop has the source retained we can release it.
342 CFRelease(runLoopSource);
343
344 /*
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.
349 */
350 return ++s_last_tag;
351}
352
353/////////////////////////////////////////////////////////////////////////////
354
9e34f56b 355// NOTE: This doesn't really belong here but this was a handy file to
fc480dc1 356// put it in because it's already compiled for wxCocoa and wxMac GUI lib.
9e34f56b
VZ
357#if wxUSE_GUI
358
531814a7 359#if wxUSE_STDPATHS
fc480dc1
DE
360static wxStandardPathsCF gs_stdPaths;
361wxStandardPathsBase& wxGUIAppTraits::GetStandardPaths()
362{
363 return gs_stdPaths;
364}
531814a7 365#endif
fc480dc1 366
9e34f56b
VZ
367#endif // wxUSE_GUI
368