Files
wxWidgets/src/unix/utilsunx.cpp
Vadim Zeitlin 3b81609771 Fix spurious errors when writing to the child process stdin under Unix.
Since the child pipe was made non-blocking in r65993, it became possible to
write to child process without deadlocking when the pipe became full. However
this still resulted in an error from wxFileOutputStream as it didn't handle
EAGAIN returned from write() any differently than any other error, even though
it is an expected situation in this particular case.

Change Unix wxExecute() to use wxPipeOutputStream which ignores EAGAIN unlike
wxFileOutputStream to fix this.

See #12636.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@66152 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2010-11-14 14:04:37 +00:00

1565 lines
43 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: src/unix/utilsunx.cpp
// Purpose: generic Unix implementation of many wx functions (for wxBase)
// Author: Vadim Zeitlin
// Id: $Id$
// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#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"
#include "wx/process.h"
#include "wx/thread.h"
#include "wx/cmdline.h"
#include "wx/wfstream.h"
#include "wx/private/selectdispatcher.h"
#include "wx/private/fdiodispatcher.h"
#include "wx/unix/execute.h"
#include "wx/unix/private.h"
#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
#include "wx/private/fdiodispatcher.h"
#endif
#include <pwd.h>
#include <sys/wait.h> // waitpid()
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#define HAS_PIPE_STREAMS (wxUSE_STREAMS && wxUSE_FILE)
#if HAS_PIPE_STREAMS
// define this to let wxexec.cpp know that we know what we're doing
#define _WX_USED_BY_WXEXECUTE_
#include "../common/execcmn.cpp"
#endif // HAS_PIPE_STREAMS
#if defined(__MWERKS__) && defined(__MACH__)
#ifndef WXWIN_OS_DESCRIPTION
#define WXWIN_OS_DESCRIPTION "MacOS X"
#endif
#ifndef HAVE_NANOSLEEP
#define HAVE_NANOSLEEP
#endif
#ifndef HAVE_UNAME
#define HAVE_UNAME
#endif
// our configure test believes we can use sigaction() if the function is
// available but Metrowekrs with MSL run-time does have the function but
// doesn't have sigaction struct so finally we can't use it...
#ifdef __MSL__
#undef wxUSE_ON_FATAL_EXCEPTION
#define wxUSE_ON_FATAL_EXCEPTION 0
#endif
#endif
// not only the statfs syscall is called differently depending on platform, but
// one of its incarnations, statvfs(), takes different arguments under
// different platforms and even different versions of the same system (Solaris
// 7 and 8): if you want to test for this, don't forget that the problems only
// appear if the large files support is enabled
#ifdef HAVE_STATFS
#ifdef __BSD__
#include <sys/param.h>
#include <sys/mount.h>
#else // !__BSD__
#include <sys/vfs.h>
#endif // __BSD__/!__BSD__
#define wxStatfs statfs
#ifndef HAVE_STATFS_DECL
// some systems lack statfs() prototype in the system headers (AIX 4)
extern "C" int statfs(const char *path, struct statfs *buf);
#endif
#endif // HAVE_STATFS
#ifdef HAVE_STATVFS
#include <sys/statvfs.h>
#define wxStatfs statvfs
#endif // HAVE_STATVFS
#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
// WX_STATFS_T is detected by configure
#define wxStatfs_t WX_STATFS_T
#endif
// SGI signal.h defines signal handler arguments differently depending on
// whether _LANGUAGE_C_PLUS_PLUS is set or not - do set it
#if defined(__SGI__) && !defined(_LANGUAGE_C_PLUS_PLUS)
#define _LANGUAGE_C_PLUS_PLUS 1
#endif // SGI hack
#include <stdarg.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <fcntl.h> // for O_WRONLY and friends
#include <time.h> // nanosleep() and/or usleep()
#include <ctype.h> // isspace()
#include <sys/time.h> // needed for FD_SETSIZE
#ifdef HAVE_UNAME
#include <sys/utsname.h> // for uname()
#endif // HAVE_UNAME
// Used by wxGetFreeMemory().
#ifdef __SGI__
#include <sys/sysmp.h>
#include <sys/sysinfo.h> // for SAGET and MINFO structures
#endif
// ----------------------------------------------------------------------------
// conditional compilation
// ----------------------------------------------------------------------------
// many versions of Unices have this function, but it is not defined in system
// headers - please add your system here if it is the case for your OS.
// SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this.
#if !defined(HAVE_USLEEP) && \
((defined(__SUN__) && !defined(__SunOs_5_6) && \
!defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
defined(__osf__) || defined(__EMX__))
extern "C"
{
#ifdef __EMX__
/* I copied this from the XFree86 diffs. AV. */
#define INCL_DOSPROCESS
#include <os2.h>
inline void usleep(unsigned long delay)
{
DosSleep(delay ? (delay/1000l) : 1l);
}
#else // Unix
int usleep(unsigned int usec);
#endif // __EMX__/Unix
};
#define HAVE_USLEEP 1
#endif // Unices without usleep()
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// sleeping
// ----------------------------------------------------------------------------
void wxSleep(int nSecs)
{
sleep(nSecs);
}
void wxMicroSleep(unsigned long microseconds)
{
#if defined(HAVE_NANOSLEEP)
timespec tmReq;
tmReq.tv_sec = (time_t)(microseconds / 1000000);
tmReq.tv_nsec = (microseconds % 1000000) * 1000;
// we're not interested in remaining time nor in return value
(void)nanosleep(&tmReq, NULL);
#elif defined(HAVE_USLEEP)
// uncomment this if you feel brave or if you are sure that your version
// of Solaris has a safe usleep() function but please notice that usleep()
// is known to lead to crashes in MT programs in Solaris 2.[67] and is not
// documented as MT-Safe
#if defined(__SUN__) && wxUSE_THREADS
#error "usleep() cannot be used in MT programs under Solaris."
#endif // Sun
usleep(microseconds);
#elif defined(HAVE_SLEEP)
// under BeOS sleep() takes seconds (what about other platforms, if any?)
sleep(microseconds * 1000000);
#else // !sleep function
#error "usleep() or nanosleep() function required for wxMicroSleep"
#endif // sleep function
}
void wxMilliSleep(unsigned long milliseconds)
{
wxMicroSleep(milliseconds*1000);
}
// ----------------------------------------------------------------------------
// process management
// ----------------------------------------------------------------------------
int wxKill(long pid, wxSignal sig, wxKillError *rc, int flags)
{
int err = kill((pid_t) (flags & wxKILL_CHILDREN) ? -pid : pid, (int)sig);
if ( rc )
{
switch ( err ? errno : 0 )
{
case 0:
*rc = wxKILL_OK;
break;
case EINVAL:
*rc = wxKILL_BAD_SIGNAL;
break;
case EPERM:
*rc = wxKILL_ACCESS_DENIED;
break;
case ESRCH:
*rc = wxKILL_NO_PROCESS;
break;
default:
// this goes against Unix98 docs so log it
wxLogDebug(wxT("unexpected kill(2) return value %d"), err);
// something else...
*rc = wxKILL_ERROR;
}
}
return err;
}
// Shutdown or reboot the PC
bool wxShutdown(int flags)
{
flags &= ~wxSHUTDOWN_FORCE;
wxChar level;
switch ( flags )
{
case wxSHUTDOWN_POWEROFF:
level = wxT('0');
break;
case wxSHUTDOWN_REBOOT:
level = wxT('6');
break;
case wxSHUTDOWN_LOGOFF:
// TODO: use dcop to log off?
return false;
default:
wxFAIL_MSG( wxT("unknown wxShutdown() flag") );
return false;
}
return system(wxString::Format("init %c", level).mb_str()) == 0;
}
// ----------------------------------------------------------------------------
// wxStream classes to support IO redirection in wxExecute
// ----------------------------------------------------------------------------
#if HAS_PIPE_STREAMS
bool wxPipeInputStream::CanRead() const
{
if ( m_lasterror == wxSTREAM_EOF )
return false;
// check if there is any input available
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
const int fd = m_file->fd();
fd_set readfds;
wxFD_ZERO(&readfds);
wxFD_SET(fd, &readfds);
switch ( select(fd + 1, &readfds, NULL, NULL, &tv) )
{
case -1:
wxLogSysError(_("Impossible to get child process input"));
// fall through
case 0:
return false;
default:
wxFAIL_MSG(wxT("unexpected select() return value"));
// still fall through
case 1:
// input available -- or maybe not, as select() returns 1 when a
// read() will complete without delay, but it could still not read
// anything
return !Eof();
}
}
size_t wxPipeOutputStream::OnSysWrite(const void *buffer, size_t size)
{
// We need to suppress error logging here, because on writing to a pipe
// which is full, wxFile::Write reports a system error. However, this is
// not an extraordinary situation, and it should not be reported to the
// user (but if really needed, the program can recognize it by checking
// whether LastRead() == 0.) Other errors will be reported below.
size_t ret;
{
wxLogNull logNo;
ret = m_file->Write(buffer, size);
}
switch ( m_file->GetLastError() )
{
// pipe is full
#ifdef EAGAIN
case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
case EWOULDBLOCK:
#endif
// do not treat it as an error
m_file->ClearLastError();
// fall through
// no error
case 0:
break;
// some real error
default:
wxLogSysError(_("Can't write to child process's stdin"));
m_lasterror = wxSTREAM_WRITE_ERROR;
}
return ret;
}
#endif // HAS_PIPE_STREAMS
// ----------------------------------------------------------------------------
// wxShell
// ----------------------------------------------------------------------------
static wxString wxMakeShellCommand(const wxString& command)
{
wxString cmd;
if ( !command )
{
// just an interactive shell
cmd = wxT("xterm");
}
else
{
// execute command in a shell
cmd << wxT("/bin/sh -c '") << command << wxT('\'');
}
return cmd;
}
bool wxShell(const wxString& command)
{
return wxExecute(wxMakeShellCommand(command), wxEXEC_SYNC) == 0;
}
bool wxShell(const wxString& command, wxArrayString& output)
{
wxCHECK_MSG( !command.empty(), false, wxT("can't exec shell non interactively") );
return wxExecute(wxMakeShellCommand(command), output);
}
namespace
{
// helper class for storing arguments as char** array suitable for passing to
// execvp(), whatever form they were passed to us
class ArgsArray
{
public:
ArgsArray(const wxArrayString& args)
{
Init(args.size());
for ( int i = 0; i < m_argc; i++ )
{
m_argv[i] = wxStrdup(args[i]);
}
}
#if wxUSE_UNICODE
ArgsArray(wchar_t **wargv)
{
int argc = 0;
while ( wargv[argc] )
argc++;
Init(argc);
for ( int i = 0; i < m_argc; i++ )
{
m_argv[i] = wxSafeConvertWX2MB(wargv[i]).release();
}
}
#endif // wxUSE_UNICODE
~ArgsArray()
{
for ( int i = 0; i < m_argc; i++ )
{
free(m_argv[i]);
}
delete [] m_argv;
}
operator char**() const { return m_argv; }
private:
void Init(int argc)
{
m_argc = argc;
m_argv = new char *[m_argc + 1];
m_argv[m_argc] = NULL;
}
int m_argc;
char **m_argv;
wxDECLARE_NO_COPY_CLASS(ArgsArray);
};
} // anonymous namespace
// ----------------------------------------------------------------------------
// wxExecute implementations
// ----------------------------------------------------------------------------
#if defined(__DARWIN__)
bool wxMacLaunch(char **argv);
#endif
long wxExecute(const wxString& command, int flags, wxProcess *process,
const wxExecuteEnv *env)
{
ArgsArray argv(wxCmdLineParser::ConvertStringToArgs(command,
wxCMD_LINE_SPLIT_UNIX));
return wxExecute(argv, flags, process, env);
}
#if wxUSE_UNICODE
long wxExecute(wchar_t **wargv, int flags, wxProcess *process,
const wxExecuteEnv *env)
{
ArgsArray argv(wargv);
return wxExecute(argv, flags, process, env);
}
#endif // wxUSE_UNICODE
// 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
// reading side can be safely closed but we should keep the write one
// opened, it will be only closed when the process terminates resulting
// in a read notification to the parent
execData.pipeEndProcDetect.Detach(wxPipe::Write);
execData.pipeEndProcDetect.Close();
// 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();
}
// 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."));
wxLog::FlushActive();
}
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
}
#undef ERROR_RETURN_CODE
// ----------------------------------------------------------------------------
// file and directory functions
// ----------------------------------------------------------------------------
const wxChar* wxGetHomeDir( wxString *home )
{
*home = wxGetUserHome();
wxString tmp;
if ( home->empty() )
*home = wxT("/");
#ifdef __VMS
tmp = *home;
if ( tmp.Last() != wxT(']'))
if ( tmp.Last() != wxT('/')) *home << wxT('/');
#endif
return home->c_str();
}
wxString wxGetUserHome( const wxString &user )
{
struct passwd *who = (struct passwd *) NULL;
if ( !user )
{
wxChar *ptr;
if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
{
return ptr;
}
if ((ptr = wxGetenv(wxT("USER"))) != NULL ||
(ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
{
who = getpwnam(wxSafeConvertWX2MB(ptr));
}
// make sure the user exists!
if ( !who )
{
who = getpwuid(getuid());
}
}
else
{
who = getpwnam (user.mb_str());
}
return wxSafeConvertMB2WX(who ? who->pw_dir : 0);
}
// ----------------------------------------------------------------------------
// network and user id routines
// ----------------------------------------------------------------------------
// private utility function which returns output of the given command, removing
// the trailing newline
static wxString wxGetCommandOutput(const wxString &cmd)
{
FILE *f = popen(cmd.ToAscii(), "r");
if ( !f )
{
wxLogSysError(wxT("Executing \"%s\" failed"), cmd.c_str());
return wxEmptyString;
}
wxString s;
char buf[256];
while ( !feof(f) )
{
if ( !fgets(buf, sizeof(buf), f) )
break;
s += wxString::FromAscii(buf);
}
pclose(f);
if ( !s.empty() && s.Last() == wxT('\n') )
s.RemoveLast();
return s;
}
// retrieve either the hostname or FQDN depending on platform (caller must
// check whether it's one or the other, this is why this function is for
// private use only)
static bool wxGetHostNameInternal(wxChar *buf, int sz)
{
wxCHECK_MSG( buf, false, wxT("NULL pointer in wxGetHostNameInternal") );
*buf = wxT('\0');
// we're using uname() which is POSIX instead of less standard sysinfo()
#if defined(HAVE_UNAME)
struct utsname uts;
bool ok = uname(&uts) != -1;
if ( ok )
{
wxStrlcpy(buf, wxSafeConvertMB2WX(uts.nodename), sz);
}
#elif defined(HAVE_GETHOSTNAME)
char cbuf[sz];
bool ok = gethostname(cbuf, sz) != -1;
if ( ok )
{
wxStrlcpy(buf, wxSafeConvertMB2WX(cbuf), sz);
}
#else // no uname, no gethostname
wxFAIL_MSG(wxT("don't know host name for this machine"));
bool ok = false;
#endif // uname/gethostname
if ( !ok )
{
wxLogSysError(_("Cannot get the hostname"));
}
return ok;
}
bool wxGetHostName(wxChar *buf, int sz)
{
bool ok = wxGetHostNameInternal(buf, sz);
if ( ok )
{
// BSD systems return the FQDN, we only want the hostname, so extract
// it (we consider that dots are domain separators)
wxChar *dot = wxStrchr(buf, wxT('.'));
if ( dot )
{
// nuke it
*dot = wxT('\0');
}
}
return ok;
}
bool wxGetFullHostName(wxChar *buf, int sz)
{
bool ok = wxGetHostNameInternal(buf, sz);
if ( ok )
{
if ( !wxStrchr(buf, wxT('.')) )
{
struct hostent *host = gethostbyname(wxSafeConvertWX2MB(buf));
if ( !host )
{
wxLogSysError(_("Cannot get the official hostname"));
ok = false;
}
else
{
// the canonical name
wxStrlcpy(buf, wxSafeConvertMB2WX(host->h_name), sz);
}
}
//else: it's already a FQDN (BSD behaves this way)
}
return ok;
}
bool wxGetUserId(wxChar *buf, int sz)
{
struct passwd *who;
*buf = wxT('\0');
if ((who = getpwuid(getuid ())) != NULL)
{
wxStrlcpy (buf, wxSafeConvertMB2WX(who->pw_name), sz);
return true;
}
return false;
}
bool wxGetUserName(wxChar *buf, int sz)
{
#ifdef HAVE_PW_GECOS
struct passwd *who;
*buf = wxT('\0');
if ((who = getpwuid (getuid ())) != NULL)
{
char *comma = strchr(who->pw_gecos, ',');
if (comma)
*comma = '\0'; // cut off non-name comment fields
wxStrlcpy(buf, wxSafeConvertMB2WX(who->pw_gecos), sz);
return true;
}
return false;
#else // !HAVE_PW_GECOS
return wxGetUserId(buf, sz);
#endif // HAVE_PW_GECOS/!HAVE_PW_GECOS
}
bool wxIsPlatform64Bit()
{
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"));
}
#ifdef __LINUX__
wxLinuxDistributionInfo wxGetLinuxDistributionInfo()
{
const wxString id = wxGetCommandOutput(wxT("lsb_release --id"));
const wxString desc = wxGetCommandOutput(wxT("lsb_release --description"));
const wxString rel = wxGetCommandOutput(wxT("lsb_release --release"));
const wxString codename = wxGetCommandOutput(wxT("lsb_release --codename"));
wxLinuxDistributionInfo ret;
id.StartsWith("Distributor ID:\t", &ret.Id);
desc.StartsWith("Description:\t", &ret.Description);
rel.StartsWith("Release:\t", &ret.Release);
codename.StartsWith("Codename:\t", &ret.CodeName);
return ret;
}
#endif
// these functions are in src/osx/utilsexc_base.cpp for wxMac
#ifndef __WXMAC__
wxOperatingSystemId wxGetOsVersion(int *verMaj, int *verMin)
{
// get OS version
int major, minor;
wxString release = wxGetCommandOutput(wxT("uname -r"));
if ( release.empty() ||
wxSscanf(release.c_str(), wxT("%d.%d"), &major, &minor) != 2 )
{
// failed to get version string or unrecognized format
major =
minor = -1;
}
if ( verMaj )
*verMaj = major;
if ( verMin )
*verMin = minor;
// try to understand which OS are we running
wxString kernel = wxGetCommandOutput(wxT("uname -s"));
if ( kernel.empty() )
kernel = wxGetCommandOutput(wxT("uname -o"));
if ( kernel.empty() )
return wxOS_UNKNOWN;
return wxPlatformInfo::GetOperatingSystemId(kernel);
}
wxString wxGetOsDescription()
{
return wxGetCommandOutput(wxT("uname -s -r -m"));
}
#endif // !__WXMAC__
unsigned long wxGetProcessId()
{
return (unsigned long)getpid();
}
wxMemorySize wxGetFreeMemory()
{
#if defined(__LINUX__)
// get it from /proc/meminfo
FILE *fp = fopen("/proc/meminfo", "r");
if ( fp )
{
long memFree = -1;
char buf[1024];
if ( fgets(buf, WXSIZEOF(buf), fp) && fgets(buf, WXSIZEOF(buf), fp) )
{
// /proc/meminfo changed its format in kernel 2.6
if ( wxPlatformInfo().CheckOSVersion(2, 6) )
{
unsigned long cached, buffers;
sscanf(buf, "MemFree: %ld", &memFree);
fgets(buf, WXSIZEOF(buf), fp);
sscanf(buf, "Buffers: %lu", &buffers);
fgets(buf, WXSIZEOF(buf), fp);
sscanf(buf, "Cached: %lu", &cached);
// add to "MemFree" also the "Buffers" and "Cached" values as
// free(1) does as otherwise the value never makes sense: for
// kernel 2.6 it's always almost 0
memFree += buffers + cached;
// values here are always expressed in kB and we want bytes
memFree *= 1024;
}
else // Linux 2.4 (or < 2.6, anyhow)
{
long memTotal, memUsed;
sscanf(buf, "Mem: %ld %ld %ld", &memTotal, &memUsed, &memFree);
}
}
fclose(fp);
return (wxMemorySize)memFree;
}
#elif defined(__SGI__)
struct rminfo realmem;
if ( sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0 )
return ((wxMemorySize)realmem.physmem * sysconf(_SC_PAGESIZE));
#elif defined(_SC_AVPHYS_PAGES)
return ((wxMemorySize)sysconf(_SC_AVPHYS_PAGES))*sysconf(_SC_PAGESIZE);
//#elif defined(__FREEBSD__) -- might use sysctl() to find it out, probably
#endif
// can't find it out
return -1;
}
bool wxGetDiskSpace(const wxString& path, wxDiskspaceSize_t *pTotal, wxDiskspaceSize_t *pFree)
{
#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
// the case to "char *" is needed for AIX 4.3
wxStatfs_t fs;
if ( wxStatfs((char *)(const char*)path.fn_str(), &fs) != 0 )
{
wxLogSysError( wxT("Failed to get file system statistics") );
return false;
}
// under Solaris we also have to use f_frsize field instead of f_bsize
// which is in general a multiple of f_frsize
#ifdef HAVE_STATVFS
wxDiskspaceSize_t blockSize = fs.f_frsize;
#else // HAVE_STATFS
wxDiskspaceSize_t blockSize = fs.f_bsize;
#endif // HAVE_STATVFS/HAVE_STATFS
if ( pTotal )
{
*pTotal = wxDiskspaceSize_t(fs.f_blocks) * blockSize;
}
if ( pFree )
{
*pFree = wxDiskspaceSize_t(fs.f_bavail) * blockSize;
}
return true;
#else // !HAVE_STATFS && !HAVE_STATVFS
return false;
#endif // HAVE_STATFS
}
// ----------------------------------------------------------------------------
// 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()
char *p = wxGetenv(var);
if ( !p )
return false;
if ( value )
{
*value = p;
}
return true;
}
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;
if ( value )
s << wxT('=') << value;
// transform to ANSI
const wxWX2MBbuf p = s.mb_str();
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;
#endif
}
bool wxSetEnv(const wxString& variable, const wxString& value)
{
return wxDoSetEnv(variable, value.mb_str());
}
bool wxUnsetEnv(const wxString& variable)
{
return wxDoSetEnv(variable, NULL);
}
// ----------------------------------------------------------------------------
// signal handling
// ----------------------------------------------------------------------------
#if wxUSE_ON_FATAL_EXCEPTION
#include <signal.h>
extern "C" void wxFatalSignalHandler(wxTYPE_SA_HANDLER)
{
if ( wxTheApp )
{
// give the user a chance to do something special about this
wxTheApp->OnFatalException();
}
abort();
}
bool wxHandleFatalExceptions(bool doit)
{
// old sig handlers
static bool s_savedHandlers = false;
static struct sigaction s_handlerFPE,
s_handlerILL,
s_handlerBUS,
s_handlerSEGV;
bool ok = true;
if ( doit && !s_savedHandlers )
{
// install the signal handler
struct sigaction act;
// some systems extend it with non std fields, so zero everything
memset(&act, 0, sizeof(act));
act.sa_handler = wxFatalSignalHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
ok &= sigaction(SIGFPE, &act, &s_handlerFPE) == 0;
ok &= sigaction(SIGILL, &act, &s_handlerILL) == 0;
ok &= sigaction(SIGBUS, &act, &s_handlerBUS) == 0;
ok &= sigaction(SIGSEGV, &act, &s_handlerSEGV) == 0;
if ( !ok )
{
wxLogDebug(wxT("Failed to install our signal handler."));
}
s_savedHandlers = true;
}
else if ( s_savedHandlers )
{
// uninstall the signal handler
ok &= sigaction(SIGFPE, &s_handlerFPE, NULL) == 0;
ok &= sigaction(SIGILL, &s_handlerILL, NULL) == 0;
ok &= sigaction(SIGBUS, &s_handlerBUS, NULL) == 0;
ok &= sigaction(SIGSEGV, &s_handlerSEGV, NULL) == 0;
if ( !ok )
{
wxLogDebug(wxT("Failed to uninstall our signal handler."));
}
s_savedHandlers = false;
}
//else: nothing to do
return ok;
}
#endif // wxUSE_ON_FATAL_EXCEPTION
// ----------------------------------------------------------------------------
// wxExecute support
// ----------------------------------------------------------------------------
int wxAppTraits::AddProcessCallback(wxEndProcessData *data, int fd)
{
// define a custom handler processing only the closure of the descriptor
struct wxEndProcessFDIOHandler : public wxFDIOHandler
{
wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
: m_data(data), m_fd(fd)
{
}
virtual void OnReadWaiting()
{
wxFDIODispatcher::Get()->UnregisterFD(m_fd);
close(m_fd);
wxHandleProcessTermination(m_data);
delete this;
}
virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
wxEndProcessData * const m_data;
const int m_fd;
};
wxFDIODispatcher::Get()->RegisterFD
(
fd,
new wxEndProcessFDIOHandler(data, fd),
wxFDIO_INPUT
);
return fd; // unused, but return something unique for the tag
}
bool wxAppTraits::CheckForRedirectedIO(wxExecuteData& execData)
{
#if HAS_PIPE_STREAMS
bool hasIO = false;
if ( execData.bufOut && execData.bufOut->Update() )
hasIO = true;
if ( execData.bufErr && execData.bufErr->Update() )
hasIO = true;
return hasIO;
#else // !HAS_PIPE_STREAMS
wxUnusedVar(execData);
return false;
#endif // HAS_PIPE_STREAMS/!HAS_PIPE_STREAMS
}
// helper classes/functions used by WaitForChild()
namespace
{
// convenient base class for IO handlers which are registered for read
// notifications only and which also stores the FD we're reading from
//
// the derived classes still have to implement OnReadWaiting()
class wxReadFDIOHandler : public wxFDIOHandler
{
public:
wxReadFDIOHandler(wxFDIODispatcher& disp, int fd) : m_fd(fd)
{
if ( fd )
disp.RegisterFD(fd, this, wxFDIO_INPUT);
}
virtual void OnWriteWaiting() { wxFAIL_MSG("unreachable"); }
virtual void OnExceptionWaiting() { wxFAIL_MSG("unreachable"); }
protected:
const int m_fd;
wxDECLARE_NO_COPY_CLASS(wxReadFDIOHandler);
};
// class for monitoring our end of the process detection pipe, simply sets a
// flag when input on the pipe (which must be due to EOF) is detected
class wxEndHandler : public wxReadFDIOHandler
{
public:
wxEndHandler(wxFDIODispatcher& disp, int fd)
: wxReadFDIOHandler(disp, fd)
{
m_terminated = false;
}
bool Terminated() const { return m_terminated; }
virtual void OnReadWaiting() { m_terminated = true; }
private:
bool m_terminated;
wxDECLARE_NO_COPY_CLASS(wxEndHandler);
};
#if HAS_PIPE_STREAMS
// class for monitoring our ends of child stdout/err, should be constructed
// with the FD and stream from wxExecuteData and will do nothing if they're
// invalid
//
// unlike wxEndHandler this class registers itself with the provided dispatcher
class wxRedirectedIOHandler : public wxReadFDIOHandler
{
public:
wxRedirectedIOHandler(wxFDIODispatcher& disp,
int fd,
wxStreamTempInputBuffer *buf)
: wxReadFDIOHandler(disp, fd),
m_buf(buf)
{
}
virtual void OnReadWaiting()
{
m_buf->Update();
}
private:
wxStreamTempInputBuffer * const m_buf;
wxDECLARE_NO_COPY_CLASS(wxRedirectedIOHandler);
};
#endif // HAS_PIPE_STREAMS
// helper function which calls waitpid() and analyzes the result
int DoWaitForChild(int pid, int flags = 0)
{
wxASSERT_MSG( pid > 0, "invalid PID" );
int status, rc;
// loop while we're getting EINTR
for ( ;; )
{
rc = waitpid(pid, &status, flags);
if ( rc != -1 || errno != EINTR )
break;
}
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 (OnReadWaiting() won't be called again, the
// descriptor is closed now).
wxLogDebug("Child process (PID %d) still alive but pipe closed so "
"generating a close notification", pid);
}
else if ( rc == -1 )
{
wxLogLastError(wxString::Format("waitpid(%d)", pid));
}
else // child did terminate
{
wxASSERT_MSG( rc == pid, "unexpected waitpid() return value" );
// notice that the caller expects the exit code to be signed, e.g. -1
// instead of 255 so don't assign WEXITSTATUS() to an int
signed char exitcode;
if ( WIFEXITED(status) )
exitcode = WEXITSTATUS(status);
else if ( WIFSIGNALED(status) )
exitcode = -WTERMSIG(status);
else
{
wxLogError("Child process (PID %d) exited for unknown reason, "
"status = %d", pid, status);
exitcode = -1;
}
return exitcode;
}
return -1;
}
} // anonymous namespace
int wxAppTraits::WaitForChild(wxExecuteData& execData)
{
if ( !(execData.flags & wxEXEC_SYNC) )
{
// asynchronous execution: just launch the process and return,
// endProcData will be destroyed when it terminates (currently we leak
// it if the process doesn't terminate before we do and this should be
// fixed but it's not a real leak so it's not really very high
// priority)
wxEndProcessData *endProcData = new wxEndProcessData;
endProcData->process = execData.process;
endProcData->pid = execData.pid;
endProcData->tag = AddProcessCallback
(
endProcData,
execData.GetEndProcReadFD()
);
endProcData->async = true;
return execData.pid;
}
//else: synchronous execution case
#if HAS_PIPE_STREAMS && wxUSE_SOCKETS
wxProcess * const process = execData.process;
if ( process && process->IsRedirected() )
{
// we can't simply block waiting for the child to terminate as we would
// dead lock if it writes more than the pipe buffer size (typically
// 4KB) bytes of output -- it would then block waiting for us to read
// the data while we'd block waiting for it to terminate
//
// so multiplex here waiting for any input from the child or closure of
// the pipe used to indicate its termination
wxSelectDispatcher disp;
wxEndHandler endHandler(disp, execData.GetEndProcReadFD());
wxRedirectedIOHandler outHandler(disp, execData.fdOut, execData.bufOut),
errHandler(disp, execData.fdErr, execData.bufErr);
while ( !endHandler.Terminated() )
{
disp.Dispatch();
}
}
//else: no IO redirection, just block waiting for the child to exit
#endif // HAS_PIPE_STREAMS
return DoWaitForChild(execData.pid);
}
void wxHandleProcessTermination(wxEndProcessData *data)
{
data->exitcode = DoWaitForChild(data->pid, WNOHANG);
// notify user about termination if required
if ( data->process )
{
data->process->OnTerminate(data->pid, data->exitcode);
}
if ( data->async )
{
// in case of asynchronous execution we don't need this data any more
// after the child terminates
delete data;
}
else // sync execution
{
// let wxExecute() know that the process has terminated
data->pid = 0;
}
}