int wxGUIAppTraits::WaitForChild(wxExecuteData& execData) { wxEndProcessData *endProcData = new wxEndProcessData; const int flags = execData.flags; // wxAddProcessCallback is now (with DARWIN) allowed to call the // callback function directly if the process terminates before // the callback can be added to the run loop. Set up the endProcData. if ( flags & wxEXEC_SYNC ) { // we may have process for capturing the program output, but it's // not used in wxEndProcessData in the case of sync execution endProcData->process = NULL; // sync execution: indicate it by negating the pid endProcData->pid = -execData.pid; } else { // async execution, nothing special to do -- caller will be // notified about the process termination if process != NULL, endProcData // will be deleted in GTK_EndProcessDetector endProcData->process = execData.process; endProcData->pid = execData.pid; } #if defined(__DARWIN__) && (defined(__WXMAC__) || defined(__WXCOCOA__)) endProcData->tag = wxAddProcessCallbackForPid(endProcData, execData.pid); #else endProcData->tag = wxAddProcessCallback ( endProcData, execData.pipeEndProcDetect.Detach(wxPipe::Read) ); execData.pipeEndProcDetect.Close(); #endif // defined(__DARWIN__) && (defined(__WXMAC__) || defined(__WXCOCOA__)) if ( flags & wxEXEC_SYNC ) { wxBusyCursor bc; wxWindowDisabler *wd = flags & wxEXEC_NODISABLE ? NULL : new wxWindowDisabler; // endProcData->pid will be set to 0 from GTK_EndProcessDetector when the // process terminates while ( endProcData->pid != 0 ) { bool idle = true; #if wxUSE_STREAMS if ( execData.bufOut ) { execData.bufOut->Update(); idle = false; } if ( execData.bufErr ) { execData.bufErr->Update(); idle = false; } #endif // wxUSE_STREAMS // don't consume 100% of the CPU while we're sitting in this // loop if ( idle ) wxMilliSleep(1); // give GTK+ a chance to call GTK_EndProcessDetector here and // also repaint the GUI wxYield(); } int exitcode = endProcData->exitcode; delete wd; delete endProcData; return exitcode; } else // async execution { return execData.pid; } }
long wxExecute(wxChar **argv, int flags, wxProcess *process) { // for the sync execution, we return -1 to indicate failure, but for async // case we return 0 which is never a valid PID // // we define this as a macro, not a variable, to avoid compiler warnings // about "ERROR_RETURN_CODE value may be clobbered by fork()" #define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0) wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") ); #if wxUSE_UNICODE int mb_argc = 0; char *mb_argv[WXEXECUTE_NARGS]; while (argv[mb_argc]) { wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]); mb_argv[mb_argc] = strdup(mb_arg); mb_argc++; } mb_argv[mb_argc] = (char *) NULL; // this macro will free memory we used above #define ARGS_CLEANUP \ for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \ free(mb_argv[mb_argc]) #else // ANSI // no need for cleanup #define ARGS_CLEANUP wxChar **mb_argv = argv; #endif // Unicode/ANSI #if wxUSE_GUI && !defined(__DARWIN__) // create pipes wxPipe pipeEndProcDetect; if ( !pipeEndProcDetect.Create() ) { wxLogError( _("Failed to execute '%s'\n"), *argv ); ARGS_CLEANUP; return ERROR_RETURN_CODE; } #endif // wxUSE_GUI && !defined(__DARWIN__) // pipes for inter process communication wxPipe pipeIn, // stdin pipeOut, // stdout pipeErr; // stderr if ( process && process->IsRedirected() ) { if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() ) { wxLogError( _("Failed to execute '%s'\n"), *argv ); ARGS_CLEANUP; return ERROR_RETURN_CODE; } } // fork the process // // NB: do *not* use vfork() here, it completely breaks this code for some // reason under Solaris (and maybe others, although not under Linux) // But on OpenVMS we do not have fork so we have to use vfork and // cross our fingers that it works. #ifdef __VMS pid_t pid = vfork(); #else pid_t pid = fork(); #endif if ( pid == -1 ) // error? { wxLogSysError( _("Fork failed") ); ARGS_CLEANUP; return ERROR_RETURN_CODE; } else if ( pid == 0 ) // we're in child { // These lines close the open file descriptors to to avoid any // input/output which might block the process or irritate the user. If // one wants proper IO for the subprocess, the right thing to do is to // start an xterm executing it. if ( !(flags & wxEXEC_SYNC) ) { for ( int fd = 0; fd < FD_SETSIZE; fd++ ) { if ( fd == pipeIn[wxPipe::Read] || fd == pipeOut[wxPipe::Write] || fd == pipeErr[wxPipe::Write] #if wxUSE_GUI && !defined(__DARWIN__) || fd == pipeEndProcDetect[wxPipe::Write] #endif // wxUSE_GUI && !defined(__DARWIN__) ) { // don't close this one, we still need it continue; } // leave stderr opened too, it won't do any harm if ( fd != STDERR_FILENO ) close(fd); } } #if !defined(__VMS) && !defined(__EMX__) if ( flags & wxEXEC_MAKE_GROUP_LEADER ) { // Set process group to child process' pid. Then killing -pid // of the parent will kill the process and all of its children. setsid(); } #endif // !__VMS #if wxUSE_GUI && !defined(__DARWIN__) // reading side can be safely closed but we should keep the write one // opened pipeEndProcDetect.Detach(wxPipe::Write); pipeEndProcDetect.Close(); #endif // wxUSE_GUI && !defined(__DARWIN__) // redirect stdin, stdout and stderr if ( pipeIn.IsOk() ) { if ( dup2(pipeIn[wxPipe::Read], STDIN_FILENO) == -1 || dup2(pipeOut[wxPipe::Write], STDOUT_FILENO) == -1 || dup2(pipeErr[wxPipe::Write], STDERR_FILENO) == -1 ) { wxLogSysError(_("Failed to redirect child process input/output")); } pipeIn.Close(); pipeOut.Close(); pipeErr.Close(); } execvp (*mb_argv, mb_argv); fprintf(stderr, "execvp("); for ( char **ppc = mb_argv; *ppc; ppc++ ) fprintf(stderr, "%s%s", ppc == mb_argv ? "" : ", ", *ppc); fprintf(stderr, ") failed with error %d!\n", errno); // there is no return after successful exec() _exit(-1); // some compilers complain about missing return - of course, they // should know that exit() doesn't return but what else can we do if // they don't? // // and, sure enough, other compilers complain about unreachable code // after exit() call, so we can just always have return here... #if defined(__VMS) || defined(__INTEL_COMPILER) return 0; #endif } else // we're in parent { ARGS_CLEANUP; // prepare for IO redirection #if wxUSE_STREAMS // the input buffer bufOut is connected to stdout, this is why it is // called bufOut and not bufIn wxStreamTempInputBuffer bufOut, bufErr; #endif // wxUSE_STREAMS if ( process && process->IsRedirected() ) { #if wxUSE_STREAMS wxOutputStream *inStream = new wxFileOutputStream(pipeIn.Detach(wxPipe::Write)); wxPipeInputStream *outStream = new wxPipeInputStream(pipeOut.Detach(wxPipe::Read)); wxPipeInputStream *errStream = new wxPipeInputStream(pipeErr.Detach(wxPipe::Read)); process->SetPipeStreams(outStream, inStream, errStream); bufOut.Init(outStream); bufErr.Init(errStream); #endif // wxUSE_STREAMS } if ( pipeIn.IsOk() ) { pipeIn.Close(); pipeOut.Close(); pipeErr.Close(); } #if wxUSE_GUI && !defined(__WXMICROWIN__) wxEndProcessData *data = new wxEndProcessData; // wxAddProcessCallback is now (with DARWIN) allowed to call the // callback function directly if the process terminates before // the callback can be added to the run loop. Set up the data. if ( flags & wxEXEC_SYNC ) { // we may have process for capturing the program output, but it's // not used in wxEndProcessData in the case of sync execution data->process = NULL; // sync execution: indicate it by negating the pid data->pid = -pid; } else { // async execution, nothing special to do - caller will be // notified about the process termination if process != NULL, data // will be deleted in GTK_EndProcessDetector data->process = process; data->pid = pid; } #if defined(__DARWIN__) data->tag = wxAddProcessCallbackForPid(data,pid); #else data->tag = wxAddProcessCallback ( data, pipeEndProcDetect.Detach(wxPipe::Read) ); pipeEndProcDetect.Close(); #endif // defined(__DARWIN__) if ( flags & wxEXEC_SYNC ) { wxBusyCursor bc; wxWindowDisabler wd; // data->pid will be set to 0 from GTK_EndProcessDetector when the // process terminates while ( data->pid != 0 ) { #if wxUSE_STREAMS bufOut.Update(); bufErr.Update(); #endif // wxUSE_STREAMS // give GTK+ a chance to call GTK_EndProcessDetector here and // also repaint the GUI wxYield(); } int exitcode = data->exitcode; delete data; return exitcode; } else // async execution { return pid; } #else // !wxUSE_GUI wxASSERT_MSG( flags & wxEXEC_SYNC, wxT("async execution not supported yet") ); int exitcode = 0; if ( waitpid(pid, &exitcode, 0) == -1 || !WIFEXITED(exitcode) ) { wxLogSysError(_("Waiting for subprocess termination failed")); } return exitcode; #endif // wxUSE_GUI } return ERROR_RETURN_CODE; }