X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/90a77e64841dfcaf37103b6411987402739baa0b..a766986f6d443ba1d50e3af25f3306fa56e4518e:/src/unix/utilsunx.cpp diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 88e0e5101f..12d1e9eb06 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -20,12 +20,18 @@ #include "wx/utils.h" +#define USE_PUTENV (!defined(HAVE_SETENV) && defined(HAVE_PUTENV)) + #ifndef WX_PRECOMP #include "wx/string.h" #include "wx/intl.h" #include "wx/log.h" #include "wx/app.h" #include "wx/wxcrtvararg.h" + #if USE_PUTENV + #include "wx/module.h" + #include "wx/hashmap.h" + #endif #endif #include "wx/apptrait.h" @@ -38,6 +44,10 @@ #include "wx/unix/execute.h" #include "wx/unix/private.h" +#ifdef wxHAS_GENERIC_PROCESS_CALLBACK +#include "wx/private/fdiodispatcher.h" +#endif + #include #include // waitpid() @@ -689,7 +699,7 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process) const wxChar* wxGetHomeDir( wxString *home ) { - *home = wxGetUserHome( wxEmptyString ); + *home = wxGetUserHome(); wxString tmp; if ( home->empty() ) *home = wxT("/"); @@ -701,11 +711,7 @@ const wxChar* wxGetHomeDir( wxString *home ) return home->c_str(); } -#if wxUSE_UNICODE -const wxMB2WXbuf wxGetUserHome( const wxString &user ) -#else // just for binary compatibility -- there is no 'const' here -char *wxGetUserHome( const wxString &user ) -#endif +wxString wxGetUserHome( const wxString &user ) { struct passwd *who = (struct passwd *) NULL; @@ -715,20 +721,17 @@ char *wxGetUserHome( const wxString &user ) if ((ptr = wxGetenv(wxT("HOME"))) != NULL) { -#if wxUSE_UNICODE - wxWCharBuffer buffer( ptr ); - return buffer; -#else return ptr; -#endif } - if ((ptr = wxGetenv(wxT("USER"))) != NULL || (ptr = wxGetenv(wxT("LOGNAME"))) != NULL) + + if ((ptr = wxGetenv(wxT("USER"))) != NULL || + (ptr = wxGetenv(wxT("LOGNAME"))) != NULL) { who = getpwnam(wxSafeConvertWX2MB(ptr)); } - // We now make sure the the user exists! - if (who == NULL) + // make sure the user exists! + if ( !who ) { who = getpwuid(getuid()); } @@ -897,16 +900,12 @@ bool wxGetUserName(wxChar *buf, int sz) bool wxIsPlatform64Bit() { - wxString machine = wxGetCommandOutput(wxT("uname -m")); - - // NOTE: these tests are not 100% reliable! - return machine.Contains(wxT("AMD64")) || - machine.Contains(wxT("IA64")) || - machine.Contains(wxT("x64")) || - machine.Contains(wxT("X64")) || - machine.Contains(wxT("alpha")) || - machine.Contains(wxT("hppa64")) || - machine.Contains(wxT("ppc64")); + const wxString machine = wxGetCommandOutput(wxT("uname -m")); + + // the test for "64" is obviously not 100% reliable but seems to work fine + // in practice + return machine.Contains(wxT("64")) || + machine.Contains(wxT("alpha")); } // these functions are in mac/utils.cpp for wxMac @@ -1049,6 +1048,35 @@ bool wxGetDiskSpace(const wxString& path, wxDiskspaceSize_t *pTotal, wxDiskspace // env vars // ---------------------------------------------------------------------------- +#if USE_PUTENV + +WX_DECLARE_STRING_HASH_MAP(char *, wxEnvVars); + +static wxEnvVars gs_envVars; + +class wxSetEnvModule : public wxModule +{ +public: + virtual bool OnInit() { return true; } + virtual void OnExit() + { + for ( wxEnvVars::const_iterator i = gs_envVars.begin(); + i != gs_envVars.end(); + ++i ) + { + free(i->second); + } + + gs_envVars.clear(); + } + + DECLARE_DYNAMIC_CLASS(wxSetEnvModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxSetEnvModule, wxModule) + +#endif // USE_PUTENV + bool wxGetEnv(const wxString& var, wxString *value) { // wxGetenv is defined as getenv() @@ -1067,6 +1095,18 @@ bool wxGetEnv(const wxString& var, wxString *value) static bool wxDoSetEnv(const wxString& variable, const char *value) { #if defined(HAVE_SETENV) + if ( !value ) + { +#ifdef HAVE_UNSETENV + // don't test unsetenv() return value: it's void on some systems (at + // least Darwin) + unsetenv(variable.mb_str()); + return true; +#else + value = ""; // we can't pass NULL to setenv() +#endif + } + return setenv(variable.mb_str(), value, 1 /* overwrite */) == 0; #elif defined(HAVE_PUTENV) wxString s = variable; @@ -1076,10 +1116,21 @@ static bool wxDoSetEnv(const wxString& variable, const char *value) // transform to ANSI const wxWX2MBbuf p = s.mb_str(); - // the string will be free()d by libc char *buf = (char *)malloc(strlen(p) + 1); strcpy(buf, p); + // store the string to free() it later + wxEnvVars::iterator i = gs_envVars.find(variable); + if ( i != gs_envVars.end() ) + { + free(i->second); + i->second = buf; + } + else // this variable hadn't been set before + { + gs_envVars[variable] = buf; + } + return putenv(buf) == 0; #else // no way to set an env var return false; @@ -1173,13 +1224,27 @@ bool wxHandleFatalExceptions(bool doit) #if wxUSE_GUI +#ifdef __DARWIN__ + #include +#endif // ---------------------------------------------------------------------------- // wxExecute support // ---------------------------------------------------------------------------- -// Darwin doesn't use the same process end detection mechanisms so we don't -// need wxExecute-related helpers for it -#if !(defined(__DARWIN__) && defined(__WXMAC__)) +/* + NOTE: If this proves not to work well for wxMac then move back to the old + behavior. If, however, it proves to work just fine, nuke all of the code + for the old behavior. I strongly suggest backporting this to 2.8 as well. + However, beware that while you can nuke the old code here, you cannot + nuke the wxAddProcessCallbackForPid from the 2.8 branch (found in + utilsexc_cf since it's an exported symbol). + */ +// #define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__)) +#define USE_OLD_DARWIN_END_PROCESS_DETECT 0 + +// wxMac/wxCocoa don't use the same process end detection mechanisms so we don't +// need wxExecute-related helpers for them +#if !USE_OLD_DARWIN_END_PROCESS_DETECT bool wxGUIAppTraits::CreateEndProcessPipe(wxExecuteData& execData) { @@ -1249,7 +1314,7 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData) if ( !(flags & wxEXEC_NOEVENTS) ) { -#if defined(__DARWIN__) && (defined(__WXMAC__) || defined(__WXCOCOA__)) +#if USE_OLD_DARWIN_END_PROCESS_DETECT endProcData->tag = wxAddProcessCallbackForPid(endProcData, execData.pid); #else endProcData->tag = wxAddProcessCallback @@ -1259,7 +1324,7 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData) ); execData.pipeEndProcDetect.Close(); -#endif // defined(__DARWIN__) && (defined(__WXMAC__) || defined(__WXCOCOA__)) +#endif // USE_OLD_DARWIN_END_PROCESS_DETECT } if ( flags & wxEXEC_SYNC ) @@ -1277,6 +1342,35 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData) int status = 0; int result = waitpid(execData.pid, &status, 0); +#ifdef __DARWIN__ + /* DE: waitpid manpage states that waitpid can fail with EINTR + if the call is interrupted by a caught signal. I suppose + that means that this ought to be a while loop. + + The odd thing is that it seems to fail EVERY time. It fails + with a quickly exiting process (e.g. echo), and fails with a + slowly exiting process (e.g. sleep 2) but clearly after + having waited for the child to exit. Maybe it's a bug in + my particular version. + + It works, however, from the CFSocket callback without this + trick but in that case it's used only after CFSocket calls + the callback and with the WNOHANG flag which would seem to + preclude it from being interrupted or at least make it much + less likely since it would not then be waiting. + + If Darwin's man page is to be believed then this is definitely + necessary. It's just weird that I've never seen it before + and apparently no one else has either or you'd think they'd + have reported it by now. Perhaps blocking the GUI while + waiting for a child process to exit is simply not that common. + */ + if(result == -1 && errno == EINTR) + { + result = waitpid(execData.pid, &status, 0); + } + +#endif if ( result == -1 ) { @@ -1302,8 +1396,8 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData) } else // !wxEXEC_NOEVENTS { - // endProcData->pid will be set to 0 from GTK_EndProcessDetector when the - // process terminates + // endProcData->pid will be set to 0 from + // wxHandleProcessTermination when the process terminates while ( endProcData->pid != 0 ) { bool idle = true; @@ -1346,6 +1440,78 @@ int wxGUIAppTraits::WaitForChild(wxExecuteData& execData) } } +#ifdef wxHAS_GENERIC_PROCESS_CALLBACK +struct wxEndProcessFDIOHandler : public wxFDIOHandler +{ + wxEndProcessFDIOHandler(wxEndProcessData *data, int fd) + : m_data(data), m_fd(fd) + {} + + virtual void OnReadWaiting() + { wxFAIL_MSG("this isn't supposed to happen"); } + virtual void OnWriteWaiting() + { wxFAIL_MSG("this isn't supposed to happen"); } + + virtual void OnExceptionWaiting() + { + int pid = (m_data->pid > 0) ? m_data->pid : -(m_data->pid); + int status = 0; + + // has the process really terminated? + int rc = waitpid(pid, &status, WNOHANG); + if ( rc == 0 ) + { + // This can only happen if the child application closes our dummy + // pipe that is used to monitor its lifetime; in that case, our + // best bet is to pretend the process did terminate, because + // otherwise wxExecute() would hang indefinitely + // (OnExceptionWaiting() won't be called again, the descriptor + // is closed now). + wxLogDebug("Child process (PID %i) still alive, even though notification was received that it terminated.", pid); + } + else if ( rc == -1 ) + { + // As above, if waitpid() fails, the best we can do is to log the + // error and pretend the child terminated: + wxLogSysError(_("Failed to check child process' status")); + } + + // set exit code to -1 if something bad happened + m_data->exitcode = (rc > 0 && WIFEXITED(status)) + ? WEXITSTATUS(status) + : -1; + + wxLogTrace("exec", + "Child process (PID %i) terminated with exit code %i", + pid, m_data->exitcode); + + // child exited, end waiting + wxFDIODispatcher::Get()->UnregisterFD(m_fd); + close(m_fd); + + m_data->fdioHandler = NULL; + wxHandleProcessTermination(m_data); + + delete this; + } + + wxEndProcessData *m_data; + int m_fd; +}; + +int wxAddProcessCallback(wxEndProcessData *proc_data, int fd) +{ + proc_data->fdioHandler = new wxEndProcessFDIOHandler(proc_data, fd); + wxFDIODispatcher::Get()->RegisterFD + ( + fd, + proc_data->fdioHandler, + wxFDIO_EXCEPTION + ); + return fd; // unused, but return something unique for the tag +} +#endif // wxHAS_GENERIC_PROCESS_CALLBACK + #endif // wxUSE_GUI #if wxUSE_BASE