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