X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c0472c7c9e1e9984f7d48eef7857e631c3adbd0d..a625c5b6c0d1c0f137bfbbdd98608c194017da75:/src/unix/utilsunx.cpp?ds=sidebyside diff --git a/src/unix/utilsunx.cpp b/src/unix/utilsunx.cpp index 784cf1031f..12d1e9eb06 100644 --- a/src/unix/utilsunx.cpp +++ b/src/unix/utilsunx.cpp @@ -44,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() @@ -695,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("/"); @@ -707,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; @@ -721,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()); } @@ -1098,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; @@ -1215,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) { @@ -1291,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 @@ -1301,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 ) @@ -1319,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 ) { @@ -1344,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; @@ -1388,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