void MainFrame::OnQueryCompletion(wxCommandEvent& WXUNUSED(event)) { m_busy = false; m_queryThread->m_flags = 0; m_queryThread->singlefile = FALSE; wxUnsetEnv(L"UD_CUT_FILTER"); }
bool nsEnvVars::EnvvarDiscard(const wxString &key) { #if defined(TRACE_ENVVARS) Manager::Get()->GetLogManager()->DebugLog(F(_T("EnvvarDiscard"))); #endif // Replace all macros the user might have setup for the key wxString the_key = key; Manager::Get()->GetMacrosManager()->ReplaceMacros(the_key); if (the_key.Trim().IsEmpty()) return false; if (!wxGetEnv(the_key, NULL)) return false; // envvar was not set - nothing to do. std::map<wxString,wxString>::iterator it = nsEnvVars::EnvVarsStack.find(the_key); if (it!=nsEnvVars::EnvVarsStack.end()) // found an old envvar on the stack return nsEnvVars::EnvvarApply(the_key, it->second); // restore old value if (!wxUnsetEnv(the_key)) { Manager::Get()->GetLogManager()->Log(F( _("Unsetting environment variable '%s' failed."), the_key.wx_str()) ); EV_DBGLOG(_T("EnvVars: Unsetting environment variable '%s' failed."), the_key.wx_str()); return false; } return true; }// EnvvarDiscard
bool nsEnvVars::EnvvarDiscard(const wxString &key) { #if TRACE_ENVVARS Manager::Get()->GetLogManager()->DebugLog(F(_T("EnvvarDiscard"))); #endif // Replace all macros the user might have setup for the key wxString the_key = key; Manager::Get()->GetMacrosManager()->ReplaceMacros(the_key); if (!wxUnsetEnv(the_key)) { Manager::Get()->GetLogManager()->Log(F( _("Unsetting environment variable '%s' failed."), #if wxCHECK_VERSION(2, 9, 0) the_key.wx_str()) #else the_key.c_str()) #endif ); EV_DBGLOG(_T("EnvVars: Unsetting environment variable '%s' failed."), #if wxCHECK_VERSION(2, 9, 0) the_key.wx_str()); #else the_key.c_str()); #endif return false; }
//--------------------------------------------------------- bool CSG_Module_Library::Create(const CSG_String &File_Name) { Destroy(); TSG_PFNC_MLB_Initialize MLB_Initialize; TSG_PFNC_MLB_Get_Interface MLB_Get_Interface; wxString sPath; wxFileName fName(File_Name.c_str()); fName.MakeAbsolute(); m_File_Name = fName.GetFullPath(); //----------------------------------------------------- if( wxGetEnv(ENV_LIB_PATH, &sPath) && sPath.Length() > 0 ) { wxSetEnv(ENV_LIB_PATH, CSG_String::Format(SG_T("%s%c%s"), sPath.c_str(), ENV_LIB_SEPA, SG_File_Get_Path(m_File_Name).c_str())); } else { wxSetEnv(ENV_LIB_PATH, SG_File_Get_Path(m_File_Name).c_str()); } //----------------------------------------------------- if( m_pLibrary->Load(m_File_Name.c_str()) && (MLB_Get_Interface = (TSG_PFNC_MLB_Get_Interface) m_pLibrary->GetSymbol(SYMBOL_MLB_Get_Interface)) != NULL && (MLB_Initialize = (TSG_PFNC_MLB_Initialize) m_pLibrary->GetSymbol(SYMBOL_MLB_Initialize) ) != NULL && MLB_Initialize(m_File_Name) ) { m_pInterface = MLB_Get_Interface(); } //----------------------------------------------------- if( sPath.Length() > 0 ) { wxSetEnv(ENV_LIB_PATH, sPath); } else { wxUnsetEnv(ENV_LIB_PATH); } //----------------------------------------------------- if( Get_Count() > 0 ) { for(int i=0; i<Get_Count(); i++) Get_Module(i)->Set_Managed(false); return( true ); } Destroy(); return( false ); }
void CrtTestCase::SetGetEnv() { wxString val; wxSetEnv(_T("TESTVAR"), _T("value")); CPPUNIT_ASSERT( wxGetEnv(_T("TESTVAR"), &val) == true ); CPPUNIT_ASSERT( val == _T("value") ); wxSetEnv(_T("TESTVAR"), _T("something else")); CPPUNIT_ASSERT( wxGetEnv(_T("TESTVAR"), &val) ); CPPUNIT_ASSERT( val == _T("something else") ); CPPUNIT_ASSERT( wxUnsetEnv(_T("TESTVAR")) ); CPPUNIT_ASSERT( wxGetEnv(_T("TESTVAR"), NULL) == false ); }
bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path) { // Since we now have builtin VST support, ignore the VST bridge as it // causes duplicate menu entries to appear. wxFileName f(path); if (f.GetName().CmpNoCase(wxT("vst-bridge")) == 0) { return false; } // As a courtesy to some plug-ins that might be bridges to // open other plug-ins, we set the current working // directory to be the plug-in's directory. wxString envpath; bool hadpath = wxGetEnv(wxT("PATH"), &envpath); wxSetEnv(wxT("PATH"), f.GetPath() + wxFILE_SEP_PATH + envpath); wxString saveOldCWD = f.GetCwd(); f.SetCwd(); int index = 0; LADSPA_Descriptor_Function mainFn = NULL; wxDynamicLibrary lib; if (lib.Load(path, wxDL_NOW)) { wxLogNull logNo; mainFn = (LADSPA_Descriptor_Function) lib.GetSymbol(wxT("ladspa_descriptor")); if (mainFn) { const LADSPA_Descriptor *data; for (data = mainFn(index); data; data = mainFn(++index)) { LadspaEffect effect(path, index); if (effect.SetHost(NULL)) { pm.RegisterPlugin(this, &effect); } } } } if (lib.IsLoaded()) { // PRL: I suspect Bug1257 -- Crash when enabling Amplio2 -- is the fault of a timing- // dependent multi-threading bug in the Amplio2 library itself, in case the unload of the .dll // comes too soon after the load. I saw the bug in Release builds but not Debug. // A sleep of even 1 ms was enough to fix the problem for me, but let's be even more generous. ::wxMilliSleep(10); lib.Unload(); } wxSetWorkingDirectory(saveOldCWD); hadpath ? wxSetEnv(wxT("PATH"), envpath) : wxUnsetEnv(wxT("PATH")); return index > 0; }
bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxString & path) { // Since we now have builtin VST support, ignore the VST bridge as it // causes duplicate menu entries to appear. wxFileName f(path); if (f.GetName().CmpNoCase(wxT("vst-bridge")) == 0) { return false; } // As a courtesy to some plug-ins that might be bridges to // open other plug-ins, we set the current working // directory to be the plug-in's directory. wxString envpath; bool hadpath = wxGetEnv(wxT("PATH"), &envpath); wxSetEnv(wxT("PATH"), f.GetPath() + wxFILE_SEP_PATH + envpath); wxString saveOldCWD = f.GetCwd(); f.SetCwd(); int index = 0; LADSPA_Descriptor_Function mainFn = NULL; wxDynamicLibrary lib; if (lib.Load(path, wxDL_NOW)) { wxLogNull logNo; mainFn = (LADSPA_Descriptor_Function) lib.GetSymbol(wxT("ladspa_descriptor")); if (mainFn) { const LADSPA_Descriptor *data; for (data = mainFn(index); data; data = mainFn(++index)) { LadspaEffect effect(path, index); if (effect.SetHost(NULL)) { pm.RegisterPlugin(this, &effect); } } } } if (lib.IsLoaded()) { lib.Unload(); } wxSetWorkingDirectory(saveOldCWD); hadpath ? wxSetEnv(wxT("PATH"), envpath) : wxUnsetEnv(wxT("PATH")); return index > 0; }
void CrtTestCase::SetGetEnv() { wxString val; wxSetEnv(_T("TESTVAR"), _T("value")); CPPUNIT_ASSERT( wxGetEnv(_T("TESTVAR"), &val) == true ); CPPUNIT_ASSERT( val == _T("value") ); wxSetEnv(_T("TESTVAR"), _T("something else")); CPPUNIT_ASSERT( wxGetEnv(_T("TESTVAR"), &val) ); CPPUNIT_ASSERT( val == _T("something else") ); // this test doesn't work under Unix systems without setenv(): putenv() can // only be used to add or modify environment variables but not to unset // them #if !defined(__UNIX__) || defined(HAVE_SETENV) CPPUNIT_ASSERT( wxUnsetEnv(_T("TESTVAR")) ); CPPUNIT_ASSERT( wxGetEnv(_T("TESTVAR"), NULL) == false ); #endif }
void EnvTestCase::GetSet() { const wxChar *var = wxT("wxTestVar"); wxString contents; CPPUNIT_ASSERT(!wxGetEnv(var, &contents)); CPPUNIT_ASSERT(contents.empty()); wxSetEnv(var, wxT("value for wxTestVar")); CPPUNIT_ASSERT(wxGetEnv(var, &contents)); CPPUNIT_ASSERT(contents == wxT("value for wxTestVar")); wxSetEnv(var, wxT("another value")); CPPUNIT_ASSERT(wxGetEnv(var, &contents)); CPPUNIT_ASSERT(contents == wxT("another value")); wxUnsetEnv(var); CPPUNIT_ASSERT(!wxGetEnv(var, &contents)); }
void CrtTestCase::SetGetEnv() { #define TESTVAR_NAME wxT("WXTESTVAR") wxString val; wxSetEnv(TESTVAR_NAME, wxT("value")); CPPUNIT_ASSERT( wxGetEnv(TESTVAR_NAME, &val) ); CPPUNIT_ASSERT_EQUAL( "value", val ); CPPUNIT_ASSERT_EQUAL( "value", wxString(wxGetenv(TESTVAR_NAME)) ); wxSetEnv(TESTVAR_NAME, wxT("something else")); CPPUNIT_ASSERT( wxGetEnv(TESTVAR_NAME, &val) ); CPPUNIT_ASSERT_EQUAL( "something else", val ); CPPUNIT_ASSERT_EQUAL( "something else", wxString(wxGetenv(TESTVAR_NAME)) ); CPPUNIT_ASSERT( wxUnsetEnv(TESTVAR_NAME) ); CPPUNIT_ASSERT( !wxGetEnv(TESTVAR_NAME, NULL) ); CPPUNIT_ASSERT( !wxGetenv(TESTVAR_NAME) ); #undef TESTVAR_NAME }
wxWindow *pluginUtilityFactory::StartDialog(frmMain *form, pgObject *obj) { wxString execCmd = command; wxArrayString environment = set_env; // Remember this as the last plugin used form->SetLastPluginUtility(this); // Replace all the place holders with appropriate values if (HaveDatabase(obj)) { execCmd.Replace(wxT("$$HOSTNAME"), obj->GetConnection()->GetHostName()); execCmd.Replace(wxT("$$HOSTADDR"), obj->GetConnection()->GetHostName()); execCmd.Replace(wxT("$$PORT"), NumToStr((long)obj->GetConnection()->GetPort())); execCmd.Replace(wxT("$$SSLMODE"), obj->GetConnection()->GetSslModeName()); execCmd.Replace(wxT("$$DATABASE"), obj->GetConnection()->GetDbname()); execCmd.Replace(wxT("$$USERNAME"), obj->GetConnection()->GetUser()); execCmd.Replace(wxT("$$PASSWORD"), obj->GetConnection()->GetPassword()); // Set the PGPASSWORD variable if required. if (set_password && !obj->GetConnection()->GetPassword().IsEmpty()) wxSetEnv(wxT("PGPASSWORD"), obj->GetConnection()->GetPassword()); // Pass the SSL settings via the environment switch (obj->GetConnection()->GetSslMode()) { case 1: wxSetEnv(wxT("PGREQUIRESSL"), wxT("1")); break; case 2: wxSetEnv(wxT("PGREQUIRESSL"), wxT("0")); break; } wxSetEnv(wxT("PGSSLMODE"), obj->GetConnection()->GetSslModeName()); wxSetEnv(wxT("PGSSLCERT"), obj->GetConnection()->GetSSLCert()); wxSetEnv(wxT("PGSSLKEY"), obj->GetConnection()->GetSSLKey()); wxSetEnv(wxT("PGSSLROOTCERT"), obj->GetConnection()->GetSSLRootCert()); wxSetEnv(wxT("PGSSLCRL"), obj->GetConnection()->GetSSLCrl()); } else { // Blank the rest execCmd.Replace(wxT("$$HOSTNAME"), wxEmptyString); execCmd.Replace(wxT("$$HOSTADDR"), wxEmptyString); execCmd.Replace(wxT("$$PORT"), wxEmptyString); execCmd.Replace(wxT("$$SSLMODE"), wxEmptyString); execCmd.Replace(wxT("$$DATABASE"), wxEmptyString); execCmd.Replace(wxT("$$USERNAME"), wxEmptyString); execCmd.Replace(wxT("$$PASSWORD"), wxEmptyString); } // Name if (obj && obj->IsCollection()) execCmd.Replace(wxT("$$OBJECTNAME"), wxT("*")); else if (obj) execCmd.Replace(wxT("$$OBJECTNAME"), obj->GetName()); else execCmd.Replace(wxT("$$OBJECTNAME"), wxEmptyString); // Object type if (obj && obj->GetFactory()) execCmd.Replace(wxT("$$OBJECTTYPE"), wxString(obj->GetFactory()->GetTypeName()).Upper()); else execCmd.Replace(wxT("$$OBJECTTYPE"), wxEmptyString); // Schema if (obj) { if (obj->GetMetaType() == PGM_SCHEMA) execCmd.Replace(wxT("$$SCHEMA"), obj->GetName()); else if (obj->GetSchema()) execCmd.Replace(wxT("$$SCHEMA"), obj->GetSchema()->GetName()); } else execCmd.Replace(wxT("$$SCHEMA"), wxEmptyString); // Table if (obj) { if (obj->GetMetaType() == PGM_TABLE || obj->GetMetaType() == GP_PARTITION) execCmd.Replace(wxT("$$TABLE"), obj->GetName()); else if (obj->GetTable()) execCmd.Replace(wxT("$$TABLE"), obj->GetTable()->GetName()); } else execCmd.Replace(wxT("$$TABLE"), wxEmptyString); // Directory substitutions execCmd.Replace(wxT("$$BINDIR"), loadPath); execCmd.Replace(wxT("$$WORKINGDIR"), wxGetCwd()); execCmd.Replace(wxT("$$PGBINDIR"), settings->GetPostgresqlPath()); execCmd.Replace(wxT("$$EDBBINDIR"), settings->GetEnterprisedbPath()); execCmd.Replace(wxT("$$SLONYBINDIR"), settings->GetSlonyPath()); // set Environment variable. for (size_t i = 0 ; i < environment.GetCount() ; i++) { wxString str = environment.Item(i); wxSetEnv(str.BeforeFirst('='), str.AfterFirst('=')); } // Let's go!! if (wxExecute(execCmd) == 0) { wxLogError(_("Failed to execute plugin %s (%s)"), title.c_str(), command.c_str()); } // Reset the environment variables set by us wxUnsetEnv(wxT("PGPASSWORD")); wxUnsetEnv(wxT("PGSSLMODE")); wxUnsetEnv(wxT("PGREQUIRESSL")); wxUnsetEnv(wxT("PGSSLCERT")); wxUnsetEnv(wxT("PGSSLKEY")); wxUnsetEnv(wxT("PGSSLROOTCERT")); wxUnsetEnv(wxT("PGSSLCRL")); return 0; }
// wxExecute: the real worker function long wxExecute(char **argv, int flags, wxProcess *process, const wxExecuteEnv *env) { // 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_THREADS // fork() doesn't mix well with POSIX threads: on many systems the program // deadlocks or crashes for some reason. Probably our code is buggy and // doesn't do something which must be done to allow this to work, but I // don't know what yet, so for now just warn the user (this is the least we // can do) about it wxASSERT_MSG( wxThread::IsMain(), wxT("wxExecute() can be called only from the main thread") ); #endif // wxUSE_THREADS #if defined(__DARWIN__) && !defined(__WXOSX_IPHONE__) // wxMacLaunch() only executes app bundles and only does it asynchronously. // It returns false if the target is not an app bundle, thus falling // through to the regular code for non app bundles. if ( !(flags & wxEXEC_SYNC) && wxMacLaunch(argv) ) { // we don't have any PID to return so just make up something non null return -1; } #endif // __DARWIN__ // this struct contains all information which we use for housekeeping wxScopedPtr<wxExecuteData> execDataPtr(new wxExecuteData); wxExecuteData& execData = *execDataPtr; execData.flags = flags; execData.process = process; // create 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 ); return ERROR_RETURN_CODE; } } // priority: we need to map wxWidgets priority which is in the range 0..100 // to Unix nice value which is in the range -20..19. As there is an odd // number of elements in our range and an even number in the Unix one, we // have to do it in this rather ugly way to guarantee that: // 1. wxPRIORITY_{MIN,DEFAULT,MAX} map to -20, 0 and 19 respectively. // 2. The mapping is monotonously increasing. // 3. The mapping is onto the target range. int prio = process ? int(process->GetPriority()) : int(wxPRIORITY_DEFAULT); if ( prio <= 50 ) prio = (2*prio)/5 - 20; else if ( prio < 55 ) prio = 1; else prio = (2*prio)/5 - 21; // 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") ); return ERROR_RETURN_CODE; } else if ( pid == 0 ) // we're in child { // NB: we used to close all the unused descriptors of the child here // but this broke some programs which relied on e.g. FD 1 being // always opened so don't do it any more, after all there doesn't // seem to be any real problem with keeping them opened #if !defined(__VMS) 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 defined(HAVE_SETPRIORITY) if ( prio && setpriority(PRIO_PROCESS, 0, prio) != 0 ) { wxLogSysError(_("Failed to set process priority")); } #endif // HAVE_SETPRIORITY // 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(); } // Close all (presumably accidentally) inherited file descriptors to // avoid descriptor leaks. This means that we don't allow inheriting // them purposefully but this seems like a lesser evil in wx code. // Ideally we'd provide some flag to indicate that none (or some?) of // the descriptors do not need to be closed but for now this is better // than never closing them at all as wx code never used FD_CLOEXEC. // TODO: Iterating up to FD_SETSIZE is both inefficient (because it may // be quite big) and incorrect (because in principle we could // have more opened descriptions than this number). Unfortunately // there is no good portable solution for closing all descriptors // above a certain threshold but non-portable solutions exist for // most platforms, see [http://stackoverflow.com/questions/899038/ // getting-the-highest-allocated-file-descriptor] for ( int fd = 0; fd < (int)FD_SETSIZE; ++fd ) { if ( fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO ) { close(fd); } } // Process additional options if we have any if ( env ) { // Change working directory if it is specified if ( !env->cwd.empty() ) wxSetWorkingDirectory(env->cwd); // Change environment if needed. // // NB: We can't use execve() currently because we allow using // non full paths to wxExecute(), i.e. we want to search for // the program in PATH. However it just might be simpler/better // to do the search manually and use execve() envp parameter to // set up the environment of the child process explicitly // instead of doing what we do below. if ( !env->env.empty() ) { wxEnvVariableHashMap oldenv; wxGetEnvMap(&oldenv); // Remove unwanted variables wxEnvVariableHashMap::const_iterator it; for ( it = oldenv.begin(); it != oldenv.end(); ++it ) { if ( env->env.find(it->first) == env->env.end() ) wxUnsetEnv(it->first); } // And add the new ones (possibly replacing the old values) for ( it = env->env.begin(); it != env->env.end(); ++it ) wxSetEnv(it->first, it->second); } } execvp(*argv, argv); fprintf(stderr, "execvp("); for ( char **a = argv; *a; a++ ) fprintf(stderr, "%s%s", a == argv ? "" : ", ", *a); 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 { // prepare for IO redirection #if HAS_PIPE_STREAMS if ( process && process->IsRedirected() ) { // Avoid deadlocks which could result from trying to write to the // child input pipe end while the child itself is writing to its // output end and waiting for us to read from it. if ( !pipeIn.MakeNonBlocking(wxPipe::Write) ) { // This message is not terrible useful for the user but what // else can we do? Also, should we fail here or take the risk // to continue and deadlock? Currently we choose the latter but // it might not be the best idea. wxLogSysError(_("Failed to set up non-blocking pipe, " "the program might hang.")); #if wxUSE_LOG wxLog::FlushActive(); #endif } wxOutputStream *inStream = new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write)); const int fdOut = pipeOut.Detach(wxPipe::Read); wxPipeInputStream *outStream = new wxPipeInputStream(fdOut); const int fdErr = pipeErr.Detach(wxPipe::Read); wxPipeInputStream *errStream = new wxPipeInputStream(fdErr); process->SetPipeStreams(outStream, inStream, errStream); if ( flags & wxEXEC_SYNC ) { execData.bufOut.Init(outStream); execData.bufErr.Init(errStream); execData.fdOut = fdOut; execData.fdErr = fdErr; } } #endif // HAS_PIPE_STREAMS if ( pipeIn.IsOk() ) { pipeIn.Close(); pipeOut.Close(); pipeErr.Close(); } if ( !(flags & wxEXEC_SYNC) ) { // Ensure that the housekeeping data is kept alive, it will be // destroyed only when the child terminates. execDataPtr.release(); } // Put the housekeeping data into the child process lookup table. // Note that when running asynchronously, if the child has already // finished this call will delete the execData and call any // wxProcess's OnTerminate() handler immediately. execData.OnStart(pid); // For the asynchronous case we don't have to do anything else, just // let the process run (if not already finished). if ( !(flags & wxEXEC_SYNC) ) return pid; // If we don't need to dispatch any events, things are relatively // simple and we don't need to delegate to wxAppTraits. if ( flags & wxEXEC_NOEVENTS ) { return BlockUntilChildExit(execData); } // If we do need to dispatch events, enter a local event loop waiting // until the child exits. As the exact kind of event loop depends on // the sort of application we're in (console or GUI), we delegate this // to wxAppTraits which virtualizes all the differences between the // console and the GUI programs. return wxApp::GetValidTraits().WaitForChild(execData); } #if !defined(__VMS) && !defined(__INTEL_COMPILER) return ERROR_RETURN_CODE; #endif }
// wxExecute: the real worker function long wxExecute(char **argv, int flags, wxProcess *process, const wxExecuteEnv *env) { // 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_THREADS // fork() doesn't mix well with POSIX threads: on many systems the program // deadlocks or crashes for some reason. Probably our code is buggy and // doesn't do something which must be done to allow this to work, but I // don't know what yet, so for now just warn the user (this is the least we // can do) about it wxASSERT_MSG( wxThread::IsMain(), wxT("wxExecute() can be called only from the main thread") ); #endif // wxUSE_THREADS #if defined(__WXCOCOA__) || ( defined(__WXOSX_MAC__) && wxOSX_USE_COCOA_OR_CARBON ) // wxMacLaunch() only executes app bundles and only does it asynchronously. // It returns false if the target is not an app bundle, thus falling // through to the regular code for non app bundles. if ( !(flags & wxEXEC_SYNC) && wxMacLaunch(argv) ) { // we don't have any PID to return so just make up something non null return -1; } #endif // __DARWIN__ // this struct contains all information which we use for housekeeping wxExecuteData execData; execData.flags = flags; execData.process = process; // create pipes if ( !execData.pipeEndProcDetect.Create() ) { wxLogError( _("Failed to execute '%s'\n"), *argv ); return ERROR_RETURN_CODE; } // 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 ); 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") ); return ERROR_RETURN_CODE; } else if ( pid == 0 ) // we're in child { // NB: we used to close all the unused descriptors of the child here // but this broke some programs which relied on e.g. FD 1 being // always opened so don't do it any more, after all there doesn't // seem to be any real problem with keeping them opened #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 // 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(); } // Close all (presumably accidentally) inherited file descriptors to // avoid descriptor leaks. This means that we don't allow inheriting // them purposefully but this seems like a lesser evil in wx code. // Ideally we'd provide some flag to indicate that none (or some?) of // the descriptors do not need to be closed but for now this is better // than never closing them at all as wx code never used FD_CLOEXEC. // Note that while the reading side of the end process detection pipe // can be safely closed, we should keep the write one opened, it will // be only closed when the process terminates resulting in a read // notification to the parent const int fdEndProc = execData.pipeEndProcDetect.Detach(wxPipe::Write); execData.pipeEndProcDetect.Close(); // TODO: Iterating up to FD_SETSIZE is both inefficient (because it may // be quite big) and incorrect (because in principle we could // have more opened descriptions than this number). Unfortunately // there is no good portable solution for closing all descriptors // above a certain threshold but non-portable solutions exist for // most platforms, see [http://stackoverflow.com/questions/899038/ // getting-the-highest-allocated-file-descriptor] for ( int fd = 0; fd < (int)FD_SETSIZE; ++fd ) { if ( fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO && fd != fdEndProc ) { close(fd); } } // Process additional options if we have any if ( env ) { // Change working directory if it is specified if ( !env->cwd.empty() ) wxSetWorkingDirectory(env->cwd); // Change environment if needed. // // NB: We can't use execve() currently because we allow using // non full paths to wxExecute(), i.e. we want to search for // the program in PATH. However it just might be simpler/better // to do the search manually and use execve() envp parameter to // set up the environment of the child process explicitly // instead of doing what we do below. if ( !env->env.empty() ) { wxEnvVariableHashMap oldenv; wxGetEnvMap(&oldenv); // Remove unwanted variables wxEnvVariableHashMap::const_iterator it; for ( it = oldenv.begin(); it != oldenv.end(); ++it ) { if ( env->env.find(it->first) == env->env.end() ) wxUnsetEnv(it->first); } // And add the new ones (possibly replacing the old values) for ( it = env->env.begin(); it != env->env.end(); ++it ) wxSetEnv(it->first, it->second); } } execvp(*argv, argv); fprintf(stderr, "execvp("); for ( char **a = argv; *a; a++ ) fprintf(stderr, "%s%s", a == argv ? "" : ", ", *a); 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 { // save it for WaitForChild() use execData.pid = pid; if (execData.process) execData.process->SetPid(pid); // and also in the wxProcess // prepare for IO redirection #if HAS_PIPE_STREAMS // the input buffer bufOut is connected to stdout, this is why it is // called bufOut and not bufIn wxStreamTempInputBuffer bufOut, bufErr; if ( process && process->IsRedirected() ) { // Avoid deadlocks which could result from trying to write to the // child input pipe end while the child itself is writing to its // output end and waiting for us to read from it. if ( !pipeIn.MakeNonBlocking(wxPipe::Write) ) { // This message is not terrible useful for the user but what // else can we do? Also, should we fail here or take the risk // to continue and deadlock? Currently we choose the latter but // it might not be the best idea. wxLogSysError(_("Failed to set up non-blocking pipe, " "the program might hang.")); #if wxUSE_LOG wxLog::FlushActive(); #endif } wxOutputStream *inStream = new wxPipeOutputStream(pipeIn.Detach(wxPipe::Write)); const int fdOut = pipeOut.Detach(wxPipe::Read); wxPipeInputStream *outStream = new wxPipeInputStream(fdOut); const int fdErr = pipeErr.Detach(wxPipe::Read); wxPipeInputStream *errStream = new wxPipeInputStream(fdErr); process->SetPipeStreams(outStream, inStream, errStream); bufOut.Init(outStream); bufErr.Init(errStream); execData.bufOut = &bufOut; execData.bufErr = &bufErr; execData.fdOut = fdOut; execData.fdErr = fdErr; } #endif // HAS_PIPE_STREAMS if ( pipeIn.IsOk() ) { pipeIn.Close(); pipeOut.Close(); pipeErr.Close(); } // we want this function to work even if there is no wxApp so ensure // that we have a valid traits pointer wxConsoleAppTraits traitsConsole; wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL; if ( !traits ) traits = &traitsConsole; return traits->WaitForChild(execData); } #if !defined(__VMS) && !defined(__INTEL_COMPILER) return ERROR_RETURN_CODE; #endif }