1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/mac/classic/utilsexec.cpp
3// Purpose:     Execution-related utilities
4// Author:      Stefan Csomor
5// Modified by: David Elliott
6// Created:     1998-01-01
7// RCS-ID:      $Id: utilsexc.cpp 38920 2006-04-26 08:21:31Z ABX $
8// Copyright:   (c) Stefan Csomor
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
14#include "wx/utils.h"
15
16#ifndef WX_PRECOMP
17    #include "wx/log.h"
18#endif
19
20#ifdef __DARWIN__
21#include "wx/unix/execute.h"
22#include <unistd.h>
23#include <sys/wait.h>
24extern "C" {
25#include <mach/mach.h>
26}
27#include <CoreFoundation/CFMachPort.h>
28#endif
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33
34#ifndef __DARWIN__
35#define wxEXECUTE_WIN_MESSAGE 10000
36
37#include "wx/mac/private.h"
38
39/*
40Below FinderLaunch function comes from:
41http://developer.apple.com/technotes/tn/tn1002.html#fndrask
42*/
43    /* FinderLaunch converts a list of nTargets FSSpec records
44    pointed to by the targetList parameter and converts the
45    list to an Apple Event.  It then sends that event to the
46    Finder.  The array of FSSpec records pointed to by the
47    targetList parameter may contain references to files,
48    folders, or applications.  The net effect of this command
49    is equivalent to the user selecting an icon in one of the
50    Finder's windows and then choosing the open command from
51    the Finder's file menu. */
52static OSErr FinderLaunch(long nTargets, FSSpec *targetList) {
53    OSErr err;
54    AppleEvent theAEvent, theReply;
55    AEAddressDesc fndrAddress;
56    AEDescList targetListDesc;
57    OSType fndrCreator;
58    Boolean wasChanged;
59    AliasHandle targetAlias;
60    long index;
61
62        /* set up locals  */
63    AECreateDesc(typeNull, NULL, 0, &theAEvent);
64    AECreateDesc(typeNull, NULL, 0, &fndrAddress);
65    AECreateDesc(typeNull, NULL, 0, &theReply);
66    AECreateDesc(typeNull, NULL, 0, &targetListDesc);
67    targetAlias = NULL;
68    fndrCreator = 'MACS';
69
70        /* verify parameters */
71    if ((nTargets == 0) || (targetList == NULL)) {
72        err = paramErr;
73        goto bail;
74    }
75
76        /* create an open documents event targeting the
77        finder */
78    err = AECreateDesc(typeApplSignature, (Ptr) &fndrCreator,
79        sizeof(fndrCreator), &fndrAddress);
80    if (err != noErr) goto bail;
81    err = AECreateAppleEvent(kCoreEventClass, kAEOpenDocuments,
82        &fndrAddress, kAutoGenerateReturnID,
83        kAnyTransactionID, &theAEvent);
84    if (err != noErr) goto bail;
85
86        /* create the list of files to open */
87    err = AECreateList(NULL, 0, false, &targetListDesc);
88    if (err != noErr) goto bail;
89    for ( index=0; index < nTargets; index++) {
90        if (targetAlias == NULL)
91            err = NewAlias(NULL, (targetList + index),
92                  &targetAlias);
93        else err = UpdateAlias(NULL, (targetList + index),
94                   targetAlias, &wasChanged);
95        if (err != noErr) goto bail;
96        HLock((Handle) targetAlias);
97        err = AEPutPtr(&targetListDesc, (index + 1),
98              typeAlias, *targetAlias,
99              GetHandleSize((Handle) targetAlias));
100        HUnlock((Handle) targetAlias);
101        if (err != noErr) goto bail;
102    }
103
104        /* add the file list to the Apple Event */
105    err = AEPutParamDesc(&theAEvent, keyDirectObject,
106          &targetListDesc);
107    if (err != noErr) goto bail;
108
109        /* send the event to the Finder */
110    err = AESend(&theAEvent, &theReply, kAENoReply,
111        kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
112
113        /* clean up and leave */
114bail:
115    if (targetAlias != NULL) DisposeHandle((Handle) targetAlias);
116    AEDisposeDesc(&targetListDesc);
117    AEDisposeDesc(&theAEvent);
118    AEDisposeDesc(&fndrAddress);
119    AEDisposeDesc(&theReply);
120    return err;
121}
122
123long wxExecute(const wxString& command, int flags, wxProcess *WXUNUSED(handler))
124{
125    wxASSERT_MSG( flags == wxEXEC_ASYNC,
126        wxT("wxExecute: Only wxEXEC_ASYNC is supported") );
127
128    FSSpec fsSpec;
129    wxMacFilename2FSSpec(command, &fsSpec);
130
131    // 0 means execution failed. Returning non-zero is a PID, but not
132    // on Mac where PIDs are 64 bits and won't fit in a long, so we
133    // return a dummy value for now.
134    return ( FinderLaunch(1 /*one file*/, &fsSpec) == noErr ) ? -1 : 0;
135}
136
137#endif
138
139#ifdef __DARWIN__
140void wxMAC_MachPortEndProcessDetect(CFMachPortRef port, void *data)
141{
142    wxEndProcessData *proc_data = (wxEndProcessData*)data;
143    wxLogDebug(wxT("Wow.. this actually worked!"));
144    int status = 0;
145    int rc = waitpid(abs(proc_data->pid), &status, WNOHANG);
146    if(!rc)
147    {
148        wxLogDebug(wxT("Mach port was invalidated, but process hasn't terminated!"));
149        return;
150    }
151    if((rc != -1) && WIFEXITED(status))
152        proc_data->exitcode = WEXITSTATUS(status);
153    else
154        proc_data->exitcode = -1;
155    wxHandleProcessTermination(proc_data);
156}
157
158int wxAddProcessCallbackForPid(wxEndProcessData *proc_data, int pid)
159{
160    if(pid < 1)
161        return -1;
162    kern_return_t    kernResult;
163    mach_port_t    taskOfOurProcess;
164    mach_port_t    machPortForProcess;
165    taskOfOurProcess = mach_task_self();
166    if(taskOfOurProcess == MACH_PORT_NULL)
167    {
168        wxLogDebug(wxT("No mach_task_self()"));
169        return -1;
170    }
171    wxLogDebug(wxT("pid=%d"),pid);
172    kernResult = task_for_pid(taskOfOurProcess,pid, &machPortForProcess);
173    if(kernResult != KERN_SUCCESS)
174    {
175        wxLogDebug(wxT("no task_for_pid()"));
176        // try seeing if it is already dead or something
177        // FIXME: a better method would be to call the callback function
178        // from idle time until the process terminates. Of course, how
179        // likely is it that it will take more than 0.1 seconds for the
180        // mach terminate event to make its way to the BSD subsystem?
181        usleep(100); // sleep for 0.1 seconds
182        wxMAC_MachPortEndProcessDetect(NULL, (void*)proc_data);
183        return -1;
184    }
185    CFMachPortContext termcb_contextinfo;
186    termcb_contextinfo.version = NULL;
187    termcb_contextinfo.info = (void*)proc_data;
188    termcb_contextinfo.retain = NULL;
189    termcb_contextinfo.release = NULL;
190    termcb_contextinfo.copyDescription = NULL;
191    CFMachPortRef    CFMachPortForProcess;
192    Boolean        ShouldFreePort;
193    CFMachPortForProcess = CFMachPortCreateWithPort(NULL, machPortForProcess, NULL, &termcb_contextinfo, &ShouldFreePort);
194    if(!CFMachPortForProcess)
195    {
196        wxLogDebug(wxT("No CFMachPortForProcess"));
197        mach_port_deallocate(taskOfOurProcess, machPortForProcess);
198        return -1;
199    }
200    if(ShouldFreePort)
201    {
202        kernResult = mach_port_deallocate(taskOfOurProcess, machPortForProcess);
203        if(kernResult!=KERN_SUCCESS)
204        {
205            wxLogDebug(wxT("Couldn't deallocate mach port"));
206            return -1;
207        }
208    }
209    CFMachPortSetInvalidationCallBack(CFMachPortForProcess, &wxMAC_MachPortEndProcessDetect);
210    CFRunLoopSourceRef    runloopsource;
211    runloopsource = CFMachPortCreateRunLoopSource(NULL,CFMachPortForProcess, (CFIndex)0);
212    if(!runloopsource)
213    {
214        wxLogDebug(wxT("Couldn't create runloopsource"));
215        return -1;
216    }
217
218    CFRelease(CFMachPortForProcess);
219
220    CFRunLoopAddSource(CFRunLoopGetCurrent(),runloopsource,kCFRunLoopDefaultMode);
221    CFRelease(runloopsource);
222    wxLogDebug(wxT("Successfully added notification to the runloop"));
223    return 0;
224}
225#endif
226