• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/amule/wxWidgets-2.8.12/src/mac/corefoundation/
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