#include "wx/process.h"
#include "wx/thread.h"
-#include "wx/stream.h"
+#include "wx/wfstream.h"
#ifdef HAVE_STATFS
# ifdef __BSD__
# endif
#endif // HAVE_STATFS
+// not only the statfs syscall is called differently depending on platform, but
+// we also can't use "struct statvfs" under Solaris because it breaks down if
+// HAVE_LARGEFILE_SUPPORT == 1 and we must use statvfs_t instead
#ifdef HAVE_STATVFS
#include <sys/statvfs.h>
#define statfs statvfs
-#endif // HAVE_STATVFS
+ #define wxStatFs statvfs_t
+#elif HAVE_STATFS
+ #define wxStatFs struct statfs
+#endif // HAVE_STAT[V]FS
#if wxUSE_GUI
#include "wx/unix/execute.h"
int err = kill((pid_t)pid, (int)sig);
if ( rc )
{
- switch ( err )
+ switch ( errno )
{
case 0:
*rc = wxKILL_OK;
#define WXEXECUTE_NARGS 127
-long wxExecute( const wxString& command, bool sync, wxProcess *process )
+long wxExecute( const wxString& command, int flags, wxProcess *process )
{
wxCHECK_MSG( !command.IsEmpty(), 0, wxT("can't exec empty command") );
argv[argc] = NULL;
// do execute the command
- long lRc = wxExecute(argv, sync, process);
+#if wxUSE_UNICODE
+ long lRc = -1;
+#else
+ long lRc = wxExecute(argv, flags, process);
+#endif
// clean up
argc = 0;
bool wxShell(const wxString& command)
{
- return wxExecute(wxMakeShellCommand(command), TRUE /* sync */) == 0;
+ return wxExecute(wxMakeShellCommand(command), wxEXEC_SYNC) == 0;
}
bool wxShell(const wxString& command, wxArrayString& output)
return wxExecute(wxMakeShellCommand(command), output);
}
+// Shutdown or reboot the PC
+bool wxShutdown(wxShutdownFlags wFlags)
+{
+ wxChar level;
+ switch ( wFlags )
+ {
+ case wxSHUTDOWN_POWEROFF:
+ level = _T('0');
+ break;
+
+ case wxSHUTDOWN_REBOOT:
+ level = _T('6');
+ break;
+
+ default:
+ wxFAIL_MSG( _T("unknown wxShutdown() flag") );
+ return FALSE;
+ }
+
+ return system(wxString::Format(_T("init %c"), level).mb_str()) == 0;
+}
+
+
#if wxUSE_GUI
void wxHandleProcessTermination(wxEndProcessData *proc_data)
#if wxUSE_STREAMS
-class wxProcessFileInputStream : public wxInputStream
-{
-public:
- wxProcessFileInputStream(int fd) { m_fd = fd; }
- ~wxProcessFileInputStream() { close(m_fd); }
-
- virtual bool Eof() const;
-
-protected:
- size_t OnSysRead(void *buffer, size_t bufsize);
-
-protected:
- int m_fd;
-};
+// ----------------------------------------------------------------------------
+// wxProcessFileInputStream: stream for reading from a pipe
+// ----------------------------------------------------------------------------
-class wxProcessFileOutputStream : public wxOutputStream
+class wxProcessFileInputStream : public wxFileInputStream
{
public:
- wxProcessFileOutputStream(int fd) { m_fd = fd; }
- ~wxProcessFileOutputStream() { close(m_fd); }
+ wxProcessFileInputStream(int fd) : wxFileInputStream(fd) { }
-protected:
- size_t OnSysWrite(const void *buffer, size_t bufsize);
-
-protected:
- int m_fd;
+ // return TRUE if we have anything to read, don't block
+ bool IsAvailable() const;
};
-bool wxProcessFileInputStream::Eof() const
+bool wxProcessFileInputStream::IsAvailable() const
{
if ( m_lasterror == wxSTREAM_EOF )
return TRUE;
tv.tv_sec = 0;
tv.tv_usec = 0;
+ const int fd = m_file->fd();
+
fd_set readfds;
FD_ZERO(&readfds);
- FD_SET(m_fd, &readfds);
- switch ( select(m_fd + 1, &readfds, NULL, NULL, &tv) )
+ FD_SET(fd, &readfds);
+ switch ( select(fd + 1, &readfds, NULL, NULL, &tv) )
{
case -1:
wxLogSysError(_("Impossible to get child process input"));
// still fall through
case 1:
- // input available: check if there is any
- return wxInputStream::Eof();
- }
-}
-
-size_t wxProcessFileInputStream::OnSysRead(void *buffer, size_t bufsize)
-{
- int ret = read(m_fd, buffer, bufsize);
- if ( ret == 0 )
- {
- m_lasterror = wxSTREAM_EOF;
- }
- else if ( ret == -1 )
- {
- m_lasterror = wxSTREAM_READ_ERROR;
- ret = 0;
- }
- else
- {
- m_lasterror = wxSTREAM_NOERROR;
- }
-
- return ret;
-}
-
-size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
-{
- int ret = write(m_fd, buffer, bufsize);
- if ( ret == -1 )
- {
- m_lasterror = wxSTREAM_WRITE_ERROR;
- ret = 0;
- }
- else
- {
- m_lasterror = wxSTREAM_NOERROR;
+ // input available
+ return TRUE;
}
-
- return ret;
}
// ----------------------------------------------------------------------------
-// wxStreamTempBuffer
+// wxStreamTempInputBuffer
// ----------------------------------------------------------------------------
/*
command we set up a pipe with a valid file descriptor on the reading side
when the output is redirected. So the subprocess happily writes to it ...
until the pipe buffer (which is usually quite big on Unix, I think the
- default is 4Mb) is full. Then the writing process stops and waits until we
+ default is 4Kb) is full. Then the writing process stops and waits until we
read some data from the pipe to be able to continue writing to it but we
never do it because we wait until it terminates to start reading and so we
have a classical deadlock.
Here is the fix: we now read the output as soon as it appears into a temp
- buffer (wxStreamTempBuffer object) and later just stuff it back into the
+ buffer (wxStreamTempInputBuffer object) and later just stuff it back into the
stream when the process terminates. See supporting code in wxExecute()
- itself as well.
+ itself as well.
+
+ Note that this is horribly inefficient for large amounts of output (count
+ the number of times we copy the data around) and so a better API is badly
+ needed!
*/
-class wxStreamTempBuffer
+class wxStreamTempInputBuffer
{
public:
- wxStreamTempBuffer();
+ wxStreamTempInputBuffer();
// call to associate a stream with this buffer, otherwise nothing happens
// at all
- void Init(wxInputStream *stream);
+ void Init(wxProcessFileInputStream *stream);
// check for input on our stream and cache it in our buffer if any
void Update();
- ~wxStreamTempBuffer();
+ ~wxStreamTempInputBuffer();
private:
// the stream we're buffering, if NULL we don't do anything at all
- wxInputStream *m_stream;
+ wxProcessFileInputStream *m_stream;
// the buffer of size m_size (NULL if m_size == 0)
void *m_buffer;
size_t m_size;
};
-wxStreamTempBuffer::wxStreamTempBuffer()
+wxStreamTempInputBuffer::wxStreamTempInputBuffer()
{
- m_stream = NULL;
+ m_stream = NULL;
m_buffer = NULL;
m_size = 0;
}
-void wxStreamTempBuffer::Init(wxInputStream *stream)
+void wxStreamTempInputBuffer::Init(wxProcessFileInputStream *stream)
{
m_stream = stream;
}
-void wxStreamTempBuffer::Update()
+void wxStreamTempInputBuffer::Update()
{
- if ( m_stream && !m_stream->Eof() )
+ if ( m_stream && m_stream->IsAvailable() )
{
- // realloc in blocks of 1Kb - surely not the best strategy but which
- // one is?
- static const size_t incSize = 1024;
+ // realloc in blocks of 4Kb: this is the default (and minimal) buffer
+ // size of the Unix pipes so it should be the optimal step
+ static const size_t incSize = 4096;
void *buf = realloc(m_buffer, m_size + incSize);
if ( !buf )
{
m_buffer = buf;
m_stream->Read((char *)m_buffer + m_size, incSize);
- m_size += incSize;
+ m_size += m_stream->LastRead();
}
}
}
-wxStreamTempBuffer::~wxStreamTempBuffer()
+wxStreamTempInputBuffer::~wxStreamTempInputBuffer()
{
if ( m_buffer )
{
#endif // wxUSE_STREAMS
+// ----------------------------------------------------------------------------
+// wxPipe: this encapsulates pipe() system call
+// ----------------------------------------------------------------------------
+
+class wxPipe
+{
+public:
+ // the symbolic names for the pipe ends
+ enum Direction
+ {
+ Read,
+ Write
+ };
+
+ enum
+ {
+ INVALID_FD = -1
+ };
+
+ // default ctor doesn't do anything
+ wxPipe() { m_fds[Read] = m_fds[Write] = INVALID_FD; }
+
+ // create the pipe, return TRUE if ok, FALSE on error
+ bool Create()
+ {
+ if ( pipe(m_fds) == -1 )
+ {
+ wxLogSysError(_("Pipe creation failed"));
+
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ // return TRUE if we were created successfully
+ bool IsOk() const { return m_fds[Read] != INVALID_FD; }
+
+ // return the descriptor for one of the pipe ends
+ int operator[](Direction which) const
+ {
+ wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_fds),
+ _T("invalid pipe index") );
+
+ return m_fds[which];
+ }
+
+ // detach a descriptor, meaning that the pipe dtor won't close it, and
+ // return it
+ int Detach(Direction which)
+ {
+ wxASSERT_MSG( which >= 0 && (size_t)which < WXSIZEOF(m_fds),
+ _T("invalid pipe index") );
+
+ int fd = m_fds[which];
+ m_fds[which] = INVALID_FD;
+
+ return fd;
+ }
+
+ // close the pipe descriptors
+ void Close()
+ {
+ for ( size_t n = 0; n < WXSIZEOF(m_fds); n++ )
+ {
+ if ( m_fds[n] != INVALID_FD )
+ close(m_fds[n]);
+ }
+ }
+
+ // dtor closes the pipe descriptors
+ ~wxPipe() { Close(); }
+
+private:
+ int m_fds[2];
+};
+
+// ----------------------------------------------------------------------------
+// wxExecute: the real worker function
+// ----------------------------------------------------------------------------
+#ifdef __VMS
+#pragma message disable codeunreachable
+#endif
+
long wxExecute(wxChar **argv,
- bool sync,
+ int flags,
wxProcess *process)
{
// for the sync execution, we return -1 to indicate failure, but for async
//
// 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 ((sync) ? -1 : 0)
+ #define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0)
wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );
#if wxUSE_GUI
// create pipes
- int end_proc_detect[2];
- if ( pipe(end_proc_detect) == -1 )
+ wxPipe pipeEndProcDetect;
+ if ( !pipeEndProcDetect.Create() )
{
- wxLogSysError( _("Pipe creation failed") );
wxLogError( _("Failed to execute '%s'\n"), *argv );
ARGS_CLEANUP;
#endif // wxUSE_GUI
// pipes for inter process communication
- int pipeIn[2], // stdin
- pipeOut[2], // stdout
- pipeErr[2]; // stderr
-
- pipeIn[0] = pipeIn[1] =
- pipeOut[0] = pipeOut[1] =
- pipeErr[0] = pipeErr[1] = -1;
+ wxPipe pipeIn, // stdin
+ pipeOut, // stdout
+ pipeErr; // stderr
if ( process && process->IsRedirected() )
{
- if ( pipe(pipeIn) == -1 || pipe(pipeOut) == -1 || pipe(pipeErr) == -1 )
+ if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
{
-#if wxUSE_GUI
- // free previously allocated resources
- close(end_proc_detect[0]);
- close(end_proc_detect[1]);
-#endif // wxUSE_GUI
-
- wxLogSysError( _("Pipe creation failed") );
wxLogError( _("Failed to execute '%s'\n"), *argv );
ARGS_CLEANUP;
if ( pid == -1 ) // error?
{
-#if wxUSE_GUI
- close(end_proc_detect[0]);
- close(end_proc_detect[1]);
- close(pipeIn[0]);
- close(pipeIn[1]);
- close(pipeOut[0]);
- close(pipeOut[1]);
- close(pipeErr[0]);
- close(pipeErr[1]);
-#endif // wxUSE_GUI
-
wxLogSysError( _("Fork failed") );
ARGS_CLEANUP;
}
else if ( pid == 0 ) // we're in child
{
-#if wxUSE_GUI
- close(end_proc_detect[0]); // close reading side
-#endif // wxUSE_GUI
-
// 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 ( !sync )
+ if ( !(flags & wxEXEC_SYNC) )
{
for ( int fd = 0; fd < FD_SETSIZE; fd++ )
{
- if ( fd == pipeIn[0] || fd == pipeOut[1] || fd == pipeErr[1]
+ if ( fd == pipeIn[wxPipe::Read]
+ || fd == pipeOut[wxPipe::Write]
+ || fd == pipeErr[wxPipe::Write]
#if wxUSE_GUI
- || fd == end_proc_detect[1]
+ || fd == pipeEndProcDetect[wxPipe::Write]
#endif // wxUSE_GUI
)
{
continue;
}
- // leave stderr opened too, it won't do any hurm
+ // leave stderr opened too, it won't do any harm
if ( fd != STDERR_FILENO )
close(fd);
}
}
- // redirect stdio, stdout and stderr
- if ( pipeIn[0] != -1 )
+#ifndef __VMS
+ if ( flags & wxEXEC_MAKE_GROUP_LEADER )
{
- if ( dup2(pipeIn[0], STDIN_FILENO) == -1 ||
- dup2(pipeOut[1], STDOUT_FILENO) == -1 ||
- dup2(pipeErr[1], STDERR_FILENO) == -1 )
+ // 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
+ // reading side can be safely closed but we should keep the write one
+ // opened
+ pipeEndProcDetect.Detach(wxPipe::Write);
+ pipeEndProcDetect.Close();
+#endif // wxUSE_GUI
+
+ // 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"));
}
- close(pipeIn[0]);
- close(pipeOut[1]);
- close(pipeErr[1]);
+ pipeIn.Close();
+ pipeOut.Close();
+ pipeErr.Close();
}
execvp (*mb_argv, mb_argv);
// 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
{
ARGS_CLEANUP;
- // pipe initialization: construction of the wxStreams
+ // prepare for IO redirection
+
#if wxUSE_STREAMS
- wxStreamTempBuffer bufIn, bufErr;
+ // 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
- // in/out for subprocess correspond to our out/in
- wxOutputStream *outStream = new wxProcessFileOutputStream(pipeIn[1]);
- wxInputStream *inStream = new wxProcessFileInputStream(pipeOut[0]);
- wxInputStream *errStream = new wxProcessFileInputStream(pipeErr[0]);
+ wxOutputStream *inStream =
+ new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
+
+ wxProcessFileInputStream *outStream =
+ new wxProcessFileInputStream(pipeOut.Detach(wxPipe::Read));
- process->SetPipeStreams(inStream, outStream, errStream);
+ wxProcessFileInputStream *errStream =
+ new wxProcessFileInputStream(pipeErr.Detach(wxPipe::Read));
- bufIn.Init(inStream);
- bufErr.Init(inStream);
+ process->SetPipeStreams(outStream, inStream, errStream);
+
+ bufOut.Init(outStream);
+ bufErr.Init(errStream);
#endif // wxUSE_STREAMS
+ }
- close(pipeIn[0]); // close reading side
- close(pipeOut[1]); // close writing side
- close(pipeErr[1]); // close writing side
+ if ( pipeIn.IsOk() )
+ {
+ pipeIn.Close();
+ pipeOut.Close();
+ pipeErr.Close();
}
#if wxUSE_GUI && !defined(__WXMICROWIN__)
wxEndProcessData *data = new wxEndProcessData;
- if ( sync )
+ data->tag = wxAddProcessCallback
+ (
+ data,
+ pipeEndProcDetect.Detach(wxPipe::Read)
+ );
+
+ pipeEndProcDetect.Close();
+
+ 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
// sync execution: indicate it by negating the pid
data->pid = -pid;
- data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
-
- close(end_proc_detect[1]); // close writing side
wxBusyCursor bc;
wxWindowDisabler wd;
while ( data->pid != 0 )
{
#if wxUSE_STREAMS
- bufIn.Update();
+ bufOut.Update();
bufErr.Update();
#endif // wxUSE_STREAMS
// will be deleted in GTK_EndProcessDetector
data->process = process;
data->pid = pid;
- data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
-
- close(end_proc_detect[1]); // close writing side
return pid;
}
#else // !wxUSE_GUI
- wxASSERT_MSG( sync, wxT("async execution not supported yet") );
+
+ wxASSERT_MSG( flags & wxEXEC_SYNC,
+ wxT("async execution not supported yet") );
int exitcode = 0;
if ( waitpid(pid, &exitcode, 0) == -1 || !WIFEXITED(exitcode) )
return exitcode;
#endif // wxUSE_GUI
}
+
+ return ERROR_RETURN_CODE;
}
+#ifdef __VMS
+#pragma message enable codeunreachable
+#endif
#undef ERROR_RETURN_CODE
#undef ARGS_CLEANUP
return FALSE;
}
+#ifndef __WXMAC__
wxString wxGetOsDescription()
{
#ifndef WXWIN_OS_DESCRIPTION
return WXWIN_OS_DESCRIPTION;
#endif
}
+#endif
// this function returns the GUI toolkit version in GUI programs, but OS
// version in non-GUI ones
#endif // !wxUSE_GUI
+unsigned long wxGetProcessId()
+{
+ return (unsigned long)getpid();
+}
+
long wxGetFreeMemory()
{
#if defined(__LINUX__)
return -1;
}
-#ifndef __WXMAC__
bool wxGetDiskSpace(const wxString& path, wxLongLong *pTotal, wxLongLong *pFree)
{
#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
- struct statfs fs;
- if ( statfs(path, &fs) != 0 )
+ // the case to "char *" is needed for AIX 4.3
+ wxStatFs fs;
+ if ( statfs((char *)(const char*)path.fn_str(), &fs) != 0 )
{
- wxLogSysError("Failed to get file system statistics");
+ wxLogSysError( wxT("Failed to get file system statistics") );
return FALSE;
}
- // under Solaris we might have to use fs.f_frsize instead as I think it
- // may be a multiple of the block size in general (TODO)
+ // 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
+ wxLongLong blockSize = fs.f_frsize;
+#else // HAVE_STATFS
+ wxLongLong blockSize = fs.f_bsize;
+#endif // HAVE_STATVFS/HAVE_STATFS
if ( pTotal )
{
- *pTotal = wxLongLong(fs.f_blocks) * fs.f_bsize;
+ *pTotal = wxLongLong(fs.f_blocks) * blockSize;
}
if ( pFree )
{
- *pFree = wxLongLong(fs.f_bavail) * fs.f_bsize;
+ *pFree = wxLongLong(fs.f_bavail) * blockSize;
}
return TRUE;
-#endif // HAVE_STATFS
-
+#else // !HAVE_STATFS && !HAVE_STATVFS
return FALSE;
+#endif // HAVE_STATFS
}
-#endif
// ----------------------------------------------------------------------------
// env vars
bool wxSetEnv(const wxString& variable, const wxChar *value)
{
#if defined(HAVE_SETENV)
- return setenv(variable.mb_str(), value ? wxString(value).mb_str().data()
- : NULL, 1 /* overwrite */) == 0;
+ return setenv(variable.mb_str(),
+ value ? (const char *)wxString(value).mb_str()
+ : NULL,
+ 1 /* overwrite */) == 0;
#elif defined(HAVE_PUTENV)
wxString s = variable;
if ( value )
#include <signal.h>
-static void wxFatalSignalHandler(wxTYPE_SA_HANDLER)
+extern "C" void wxFatalSignalHandler(wxTYPE_SA_HANDLER)
{
if ( wxTheApp )
{
// error and debug output routines (deprecated, use wxLog)
// ----------------------------------------------------------------------------
+#if WXWIN_COMPATIBILITY_2_2
+
void wxDebugMsg( const char *format, ... )
{
va_list ap;
exit(3); // the same exit code as for abort()
}
+#endif // WXWIN_COMPATIBILITY_2_2
+