]> git.saurik.com Git - wxWidgets.git/blame - src/unix/utilsunx.cpp
fix for glib critical errors when closing an mdi child
[wxWidgets.git] / src / unix / utilsunx.cpp
CommitLineData
518b5d2f 1/////////////////////////////////////////////////////////////////////////////
7520f3da 2// Name: src/unix/utilsunx.cpp
518b5d2f
VZ
3// Purpose: generic Unix implementation of many wx functions
4// Author: Vadim Zeitlin
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
65571936 7// Licence: wxWindows licence
518b5d2f
VZ
8/////////////////////////////////////////////////////////////////////////////
9
10// ============================================================================
11// declarations
12// ============================================================================
13
14// ----------------------------------------------------------------------------
15// headers
16// ----------------------------------------------------------------------------
17
14f355c2
VS
18// for compilers that support precompilation, includes "wx.h".
19#include "wx/wxprec.h"
20
88a7a4e1
WS
21#include "wx/utils.h"
22
c0472c7c
VZ
23#define USE_PUTENV (!defined(HAVE_SETENV) && defined(HAVE_PUTENV))
24
7520f3da
WS
25#ifndef WX_PRECOMP
26 #include "wx/string.h"
88a7a4e1 27 #include "wx/intl.h"
e4db172a 28 #include "wx/log.h"
670f9935 29 #include "wx/app.h"
0cb7e05c 30 #include "wx/wxcrtvararg.h"
c0472c7c
VZ
31 #if USE_PUTENV
32 #include "wx/module.h"
33 #include "wx/hashmap.h"
34 #endif
7520f3da 35#endif
518b5d2f 36
46446cc2 37#include "wx/apptrait.h"
518b5d2f 38
518b5d2f 39#include "wx/process.h"
bdc72a22 40#include "wx/thread.h"
518b5d2f 41
80d6dc0a 42#include "wx/wfstream.h"
8b33ae2d 43
e2478fde 44#include "wx/unix/execute.h"
17a1ebd1
VZ
45#include "wx/unix/private.h"
46
46c7e1a1
VS
47#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
48#include "wx/private/fdiodispatcher.h"
49#endif
50
17a1ebd1 51#include <pwd.h>
bc855d09 52#include <sys/wait.h> // waitpid()
e2478fde 53
bc023abb
MW
54#ifdef HAVE_SYS_SELECT_H
55# include <sys/select.h>
56#endif
57
1f3b2af0
VS
58#define HAS_PIPE_INPUT_STREAM (wxUSE_STREAMS && wxUSE_FILE)
59
60#if HAS_PIPE_INPUT_STREAM
2887179b
VZ
61
62// define this to let wxexec.cpp know that we know what we're doing
63#define _WX_USED_BY_WXEXECUTE_
64#include "../common/execcmn.cpp"
65
1f3b2af0 66#endif // HAS_PIPE_INPUT_STREAM
2887179b 67
ec67cff1 68#if wxUSE_BASE
e2478fde 69
74c719ed
VZ
70#if defined(__MWERKS__) && defined(__MACH__)
71 #ifndef WXWIN_OS_DESCRIPTION
72 #define WXWIN_OS_DESCRIPTION "MacOS X"
73 #endif
74 #ifndef HAVE_NANOSLEEP
75 #define HAVE_NANOSLEEP
76 #endif
77 #ifndef HAVE_UNAME
78 #define HAVE_UNAME
79 #endif
80
81 // our configure test believes we can use sigaction() if the function is
82 // available but Metrowekrs with MSL run-time does have the function but
83 // doesn't have sigaction struct so finally we can't use it...
84 #ifdef __MSL__
85 #undef wxUSE_ON_FATAL_EXCEPTION
86 #define wxUSE_ON_FATAL_EXCEPTION 0
87 #endif
8d4f85ce
SC
88#endif
89
85da04e9
VZ
90// not only the statfs syscall is called differently depending on platform, but
91// one of its incarnations, statvfs(), takes different arguments under
92// different platforms and even different versions of the same system (Solaris
93// 7 and 8): if you want to test for this, don't forget that the problems only
94// appear if the large files support is enabled
eadd7bd2 95#ifdef HAVE_STATFS
85da04e9
VZ
96 #ifdef __BSD__
97 #include <sys/param.h>
98 #include <sys/mount.h>
99 #else // !__BSD__
100 #include <sys/vfs.h>
101 #endif // __BSD__/!__BSD__
102
103 #define wxStatfs statfs
84ae7ca4
VZ
104
105 #ifndef HAVE_STATFS_DECL
106 // some systems lack statfs() prototype in the system headers (AIX 4)
107 extern "C" int statfs(const char *path, struct statfs *buf);
108 #endif
eadd7bd2
VZ
109#endif // HAVE_STATFS
110
9952adac
VZ
111#ifdef HAVE_STATVFS
112 #include <sys/statvfs.h>
113
85da04e9
VZ
114 #define wxStatfs statvfs
115#endif // HAVE_STATVFS
116
117#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
118 // WX_STATFS_T is detected by configure
119 #define wxStatfs_t WX_STATFS_T
120#endif
9952adac 121
f6bcfd97
BP
122// SGI signal.h defines signal handler arguments differently depending on
123// whether _LANGUAGE_C_PLUS_PLUS is set or not - do set it
124#if defined(__SGI__) && !defined(_LANGUAGE_C_PLUS_PLUS)
125 #define _LANGUAGE_C_PLUS_PLUS 1
126#endif // SGI hack
127
518b5d2f
VZ
128#include <stdarg.h>
129#include <dirent.h>
130#include <string.h>
131#include <sys/stat.h>
132#include <sys/types.h>
518b5d2f 133#include <sys/wait.h>
e2478fde 134#include <unistd.h>
518b5d2f
VZ
135#include <errno.h>
136#include <netdb.h>
137#include <signal.h>
138#include <fcntl.h> // for O_WRONLY and friends
139#include <time.h> // nanosleep() and/or usleep()
fad866f4 140#include <ctype.h> // isspace()
b12915c1 141#include <sys/time.h> // needed for FD_SETSIZE
7bcb11d3 142
0fcdf6dc 143#ifdef HAVE_UNAME
518b5d2f
VZ
144 #include <sys/utsname.h> // for uname()
145#endif // HAVE_UNAME
146
9ad34f61
VZ
147// Used by wxGetFreeMemory().
148#ifdef __SGI__
149 #include <sys/sysmp.h>
150 #include <sys/sysinfo.h> // for SAGET and MINFO structures
151#endif
152
518b5d2f
VZ
153// ----------------------------------------------------------------------------
154// conditional compilation
155// ----------------------------------------------------------------------------
156
157// many versions of Unices have this function, but it is not defined in system
158// headers - please add your system here if it is the case for your OS.
159// SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this.
1363811b 160#if !defined(HAVE_USLEEP) && \
d67fee71 161 ((defined(__SUN__) && !defined(__SunOs_5_6) && \
518b5d2f 162 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
d67fee71 163 defined(__osf__) || defined(__EMX__))
518b5d2f
VZ
164 extern "C"
165 {
1a5e269b
VZ
166 #ifdef __EMX__
167 /* I copied this from the XFree86 diffs. AV. */
168 #define INCL_DOSPROCESS
169 #include <os2.h>
170 inline void usleep(unsigned long delay)
171 {
172 DosSleep(delay ? (delay/1000l) : 1l);
173 }
174 #else // Unix
1363811b 175 int usleep(unsigned int usec);
1a5e269b 176 #endif // __EMX__/Unix
518b5d2f 177 };
bdc72a22
VZ
178
179 #define HAVE_USLEEP 1
518b5d2f
VZ
180#endif // Unices without usleep()
181
518b5d2f
VZ
182// ============================================================================
183// implementation
184// ============================================================================
185
186// ----------------------------------------------------------------------------
187// sleeping
188// ----------------------------------------------------------------------------
189
190void wxSleep(int nSecs)
191{
192 sleep(nSecs);
193}
194
66cd9d7f 195void wxMicroSleep(unsigned long microseconds)
518b5d2f 196{
b12915c1 197#if defined(HAVE_NANOSLEEP)
518b5d2f 198 timespec tmReq;
66cd9d7f
VZ
199 tmReq.tv_sec = (time_t)(microseconds / 1000000);
200 tmReq.tv_nsec = (microseconds % 1000000) * 1000;
518b5d2f
VZ
201
202 // we're not interested in remaining time nor in return value
203 (void)nanosleep(&tmReq, (timespec *)NULL);
b12915c1 204#elif defined(HAVE_USLEEP)
518b5d2f
VZ
205 // uncomment this if you feel brave or if you are sure that your version
206 // of Solaris has a safe usleep() function but please notice that usleep()
207 // is known to lead to crashes in MT programs in Solaris 2.[67] and is not
208 // documented as MT-Safe
ea18eed9 209 #if defined(__SUN__) && wxUSE_THREADS
518b5d2f
VZ
210 #error "usleep() cannot be used in MT programs under Solaris."
211 #endif // Sun
212
66cd9d7f 213 usleep(microseconds);
b12915c1
VZ
214#elif defined(HAVE_SLEEP)
215 // under BeOS sleep() takes seconds (what about other platforms, if any?)
66cd9d7f 216 sleep(microseconds * 1000000);
518b5d2f 217#else // !sleep function
66cd9d7f 218 #error "usleep() or nanosleep() function required for wxMicroSleep"
518b5d2f
VZ
219#endif // sleep function
220}
221
66cd9d7f
VZ
222void wxMilliSleep(unsigned long milliseconds)
223{
224 wxMicroSleep(milliseconds*1000);
225}
226
518b5d2f
VZ
227// ----------------------------------------------------------------------------
228// process management
229// ----------------------------------------------------------------------------
230
e0f6b731 231int wxKill(long pid, wxSignal sig, wxKillError *rc, int flags)
518b5d2f 232{
e0f6b731 233 int err = kill((pid_t) (flags & wxKILL_CHILDREN) ? -pid : pid, (int)sig);
50567b69
VZ
234 if ( rc )
235 {
f0bce4d4 236 switch ( err ? errno : 0 )
50567b69
VZ
237 {
238 case 0:
239 *rc = wxKILL_OK;
240 break;
241
242 case EINVAL:
243 *rc = wxKILL_BAD_SIGNAL;
244 break;
245
246 case EPERM:
247 *rc = wxKILL_ACCESS_DENIED;
248 break;
249
250 case ESRCH:
251 *rc = wxKILL_NO_PROCESS;
252 break;
253
254 default:
255 // this goes against Unix98 docs so log it
256 wxLogDebug(_T("unexpected kill(2) return value %d"), err);
257
258 // something else...
259 *rc = wxKILL_ERROR;
260 }
261 }
262
263 return err;
518b5d2f
VZ
264}
265
fad866f4
KB
266#define WXEXECUTE_NARGS 127
267
62705a27
RN
268#if defined(__DARWIN__)
269long wxMacExecute(wxChar **argv,
270 int flags,
271 wxProcess *process);
272#endif
273
fbf456aa 274long wxExecute( const wxString& command, int flags, wxProcess *process )
518b5d2f 275{
8ea92b4d 276 wxCHECK_MSG( !command.empty(), 0, wxT("can't exec empty command") );
ba2eff22
VZ
277
278 wxLogTrace(wxT("exec"), wxT("Executing \"%s\""), command.c_str());
518b5d2f 279
647b8e37
VZ
280#if wxUSE_THREADS
281 // fork() doesn't mix well with POSIX threads: on many systems the program
282 // deadlocks or crashes for some reason. Probably our code is buggy and
283 // doesn't do something which must be done to allow this to work, but I
284 // don't know what yet, so for now just warn the user (this is the least we
285 // can do) about it
286 wxASSERT_MSG( wxThread::IsMain(),
287 _T("wxExecute() can be called only from the main thread") );
288#endif // wxUSE_THREADS
289
518b5d2f 290 int argc = 0;
05079acc 291 wxChar *argv[WXEXECUTE_NARGS];
fad866f4 292 wxString argument;
05079acc 293 const wxChar *cptr = command.c_str();
223d09f6 294 wxChar quotechar = wxT('\0'); // is arg quoted?
9d8aca48 295 bool escaped = false;
518b5d2f 296
0ed9a934 297 // split the command line in arguments
fad866f4
KB
298 do
299 {
7520f3da 300 argument = wxEmptyString;
223d09f6 301 quotechar = wxT('\0');
0ed9a934 302
fad866f4 303 // eat leading whitespace:
05079acc 304 while ( wxIsspace(*cptr) )
fad866f4 305 cptr++;
0ed9a934 306
223d09f6 307 if ( *cptr == wxT('\'') || *cptr == wxT('"') )
fad866f4 308 quotechar = *cptr++;
0ed9a934 309
fad866f4
KB
310 do
311 {
223d09f6 312 if ( *cptr == wxT('\\') && ! escaped )
fad866f4 313 {
9d8aca48 314 escaped = true;
fad866f4
KB
315 cptr++;
316 continue;
317 }
0ed9a934 318
fad866f4 319 // all other characters:
0ed9a934 320 argument += *cptr++;
9d8aca48 321 escaped = false;
0ed9a934
VZ
322
323 // have we reached the end of the argument?
324 if ( (*cptr == quotechar && ! escaped)
223d09f6
KB
325 || (quotechar == wxT('\0') && wxIsspace(*cptr))
326 || *cptr == wxT('\0') )
fad866f4 327 {
0ed9a934 328 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
223d09f6 329 wxT("too many arguments in wxExecute") );
0ed9a934 330
05079acc
OK
331 argv[argc] = new wxChar[argument.length() + 1];
332 wxStrcpy(argv[argc], argument.c_str());
fad866f4 333 argc++;
0ed9a934 334
fad866f4 335 // if not at end of buffer, swallow last character:
0ed9a934
VZ
336 if(*cptr)
337 cptr++;
338
fad866f4
KB
339 break; // done with this one, start over
340 }
0ed9a934
VZ
341 } while(*cptr);
342 } while(*cptr);
fad866f4 343 argv[argc] = NULL;
0ed9a934 344
62705a27
RN
345 long lRc;
346#if defined(__DARWIN__)
3afdfec4 347 // wxMacExecute only executes app bundles.
fb19fbab
SC
348 // It returns an error code if the target is not an app bundle, thus falling
349 // through to the regular wxExecute for non app bundles.
3afdfec4 350 lRc = wxMacExecute(argv, flags, process);
fb19fbab 351 if( lRc != ((flags & wxEXEC_SYNC) ? -1 : 0))
62705a27
RN
352 return lRc;
353#endif
354
0ed9a934 355 // do execute the command
62705a27 356 lRc = wxExecute(argv, flags, process);
518b5d2f 357
0ed9a934 358 // clean up
fad866f4 359 argc = 0;
0ed9a934 360 while( argv[argc] )
fad866f4 361 delete [] argv[argc++];
518b5d2f
VZ
362
363 return lRc;
364}
365
2c8e4738
VZ
366// ----------------------------------------------------------------------------
367// wxShell
368// ----------------------------------------------------------------------------
369
370static wxString wxMakeShellCommand(const wxString& command)
518b5d2f
VZ
371{
372 wxString cmd;
cd6ce4a9 373 if ( !command )
2c8e4738
VZ
374 {
375 // just an interactive shell
cd6ce4a9 376 cmd = _T("xterm");
2c8e4738 377 }
518b5d2f 378 else
2c8e4738
VZ
379 {
380 // execute command in a shell
381 cmd << _T("/bin/sh -c '") << command << _T('\'');
382 }
383
384 return cmd;
385}
386
387bool wxShell(const wxString& command)
388{
fbf456aa 389 return wxExecute(wxMakeShellCommand(command), wxEXEC_SYNC) == 0;
2c8e4738
VZ
390}
391
392bool wxShell(const wxString& command, wxArrayString& output)
393{
9d8aca48 394 wxCHECK_MSG( !command.empty(), false, _T("can't exec shell non interactively") );
518b5d2f 395
2c8e4738 396 return wxExecute(wxMakeShellCommand(command), output);
518b5d2f
VZ
397}
398
f6ba47d9
VZ
399// Shutdown or reboot the PC
400bool wxShutdown(wxShutdownFlags wFlags)
401{
402 wxChar level;
403 switch ( wFlags )
404 {
405 case wxSHUTDOWN_POWEROFF:
406 level = _T('0');
407 break;
408
409 case wxSHUTDOWN_REBOOT:
410 level = _T('6');
411 break;
412
413 default:
414 wxFAIL_MSG( _T("unknown wxShutdown() flag") );
9d8aca48 415 return false;
f6ba47d9
VZ
416 }
417
418 return system(wxString::Format(_T("init %c"), level).mb_str()) == 0;
419}
420
cd6ce4a9
VZ
421// ----------------------------------------------------------------------------
422// wxStream classes to support IO redirection in wxExecute
423// ----------------------------------------------------------------------------
6dc6fda6 424
1f3b2af0 425#if HAS_PIPE_INPUT_STREAM
1e6feb95 426
2b5f62a0 427bool wxPipeInputStream::CanRead() const
8b33ae2d 428{
cd6ce4a9 429 if ( m_lasterror == wxSTREAM_EOF )
9d8aca48 430 return false;
cd6ce4a9
VZ
431
432 // check if there is any input available
433 struct timeval tv;
434 tv.tv_sec = 0;
435 tv.tv_usec = 0;
436
80d6dc0a
VZ
437 const int fd = m_file->fd();
438
cd6ce4a9 439 fd_set readfds;
17a1ebd1
VZ
440
441 wxFD_ZERO(&readfds);
442 wxFD_SET(fd, &readfds);
443
80d6dc0a 444 switch ( select(fd + 1, &readfds, NULL, NULL, &tv) )
cd6ce4a9
VZ
445 {
446 case -1:
447 wxLogSysError(_("Impossible to get child process input"));
448 // fall through
8b33ae2d 449
cd6ce4a9 450 case 0:
9d8aca48 451 return false;
8b33ae2d 452
cd6ce4a9
VZ
453 default:
454 wxFAIL_MSG(_T("unexpected select() return value"));
455 // still fall through
456
457 case 1:
6f3d3c68
VZ
458 // input available -- or maybe not, as select() returns 1 when a
459 // read() will complete without delay, but it could still not read
460 // anything
461 return !Eof();
cd6ce4a9 462 }
8b33ae2d
GL
463}
464
1f3b2af0 465#endif // HAS_PIPE_INPUT_STREAM
1e6feb95 466
b477f956
VZ
467// ----------------------------------------------------------------------------
468// wxExecute: the real worker function
469// ----------------------------------------------------------------------------
79066131 470
17a1ebd1 471long wxExecute(wxChar **argv, int flags, wxProcess *process)
518b5d2f 472{
f6bcfd97 473 // for the sync execution, we return -1 to indicate failure, but for async
accb3257
VZ
474 // case we return 0 which is never a valid PID
475 //
476 // we define this as a macro, not a variable, to avoid compiler warnings
477 // about "ERROR_RETURN_CODE value may be clobbered by fork()"
fbf456aa 478 #define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0)
f6bcfd97 479
accb3257 480 wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );
518b5d2f 481
05079acc
OK
482#if wxUSE_UNICODE
483 int mb_argc = 0;
484 char *mb_argv[WXEXECUTE_NARGS];
485
e90c1d2a
VZ
486 while (argv[mb_argc])
487 {
69c928ef 488 wxWX2MBbuf mb_arg = wxSafeConvertWX2MB(argv[mb_argc]);
cd6ce4a9
VZ
489 mb_argv[mb_argc] = strdup(mb_arg);
490 mb_argc++;
05079acc
OK
491 }
492 mb_argv[mb_argc] = (char *) NULL;
e90c1d2a
VZ
493
494 // this macro will free memory we used above
495 #define ARGS_CLEANUP \
345b0247 496 for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
e90c1d2a
VZ
497 free(mb_argv[mb_argc])
498#else // ANSI
499 // no need for cleanup
500 #define ARGS_CLEANUP
501
05079acc 502 wxChar **mb_argv = argv;
e90c1d2a 503#endif // Unicode/ANSI
518b5d2f 504
e2478fde
VZ
505 // we want this function to work even if there is no wxApp so ensure that
506 // we have a valid traits pointer
507 wxConsoleAppTraits traitsConsole;
508 wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
509 if ( !traits )
510 traits = &traitsConsole;
511
512 // this struct contains all information which we pass to and from
513 // wxAppTraits methods
514 wxExecuteData execData;
515 execData.flags = flags;
516 execData.process = process;
517
518b5d2f 518 // create pipes
e2478fde 519 if ( !traits->CreateEndProcessPipe(execData) )
518b5d2f 520 {
cd6ce4a9 521 wxLogError( _("Failed to execute '%s'\n"), *argv );
e90c1d2a
VZ
522
523 ARGS_CLEANUP;
524
accb3257 525 return ERROR_RETURN_CODE;
518b5d2f
VZ
526 }
527
f6bcfd97 528 // pipes for inter process communication
b477f956
VZ
529 wxPipe pipeIn, // stdin
530 pipeOut, // stdout
531 pipeErr; // stderr
cd6ce4a9
VZ
532
533 if ( process && process->IsRedirected() )
8b33ae2d 534 {
b477f956 535 if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
8b33ae2d 536 {
cd6ce4a9 537 wxLogError( _("Failed to execute '%s'\n"), *argv );
8b33ae2d
GL
538
539 ARGS_CLEANUP;
540
accb3257 541 return ERROR_RETURN_CODE;
8b33ae2d
GL
542 }
543 }
8b33ae2d 544
518b5d2f 545 // fork the process
ef5f8ab6
VZ
546 //
547 // NB: do *not* use vfork() here, it completely breaks this code for some
548 // reason under Solaris (and maybe others, although not under Linux)
b2ddee86
JJ
549 // But on OpenVMS we do not have fork so we have to use vfork and
550 // cross our fingers that it works.
551#ifdef __VMS
552 pid_t pid = vfork();
553#else
554 pid_t pid = fork();
555#endif
556 if ( pid == -1 ) // error?
518b5d2f
VZ
557 {
558 wxLogSysError( _("Fork failed") );
e90c1d2a
VZ
559
560 ARGS_CLEANUP;
561
accb3257 562 return ERROR_RETURN_CODE;
518b5d2f 563 }
cd6ce4a9 564 else if ( pid == 0 ) // we're in child
518b5d2f 565 {
cd6ce4a9 566 // These lines close the open file descriptors to to avoid any
518b5d2f 567 // input/output which might block the process or irritate the user. If
cd6ce4a9
VZ
568 // one wants proper IO for the subprocess, the right thing to do is to
569 // start an xterm executing it.
fbf456aa 570 if ( !(flags & wxEXEC_SYNC) )
518b5d2f 571 {
35ef537b
VZ
572 // FD_SETSIZE is unsigned under BSD, signed under other platforms
573 // so we need a cast to avoid warnings on all platforms
574 for ( int fd = 0; fd < (int)FD_SETSIZE; fd++ )
518b5d2f 575 {
b477f956
VZ
576 if ( fd == pipeIn[wxPipe::Read]
577 || fd == pipeOut[wxPipe::Write]
578 || fd == pipeErr[wxPipe::Write]
e2478fde 579 || traits->IsWriteFDOfEndProcessPipe(execData, fd) )
cd6ce4a9
VZ
580 {
581 // don't close this one, we still need it
582 continue;
583 }
e90c1d2a 584
b477f956 585 // leave stderr opened too, it won't do any harm
e90c1d2a 586 if ( fd != STDERR_FILENO )
518b5d2f
VZ
587 close(fd);
588 }
0c9c4401 589 }
e1082c9f 590
e8e776aa 591#if !defined(__VMS) && !defined(__EMX__)
0c9c4401
VZ
592 if ( flags & wxEXEC_MAKE_GROUP_LEADER )
593 {
594 // Set process group to child process' pid. Then killing -pid
595 // of the parent will kill the process and all of its children.
596 setsid();
518b5d2f 597 }
0c9c4401
VZ
598#endif // !__VMS
599
0c9c4401
VZ
600 // reading side can be safely closed but we should keep the write one
601 // opened
e2478fde 602 traits->DetachWriteFDOfEndProcessPipe(execData);
518b5d2f 603
80d6dc0a 604 // redirect stdin, stdout and stderr
b477f956 605 if ( pipeIn.IsOk() )
cd6ce4a9 606 {
b477f956
VZ
607 if ( dup2(pipeIn[wxPipe::Read], STDIN_FILENO) == -1 ||
608 dup2(pipeOut[wxPipe::Write], STDOUT_FILENO) == -1 ||
609 dup2(pipeErr[wxPipe::Write], STDERR_FILENO) == -1 )
cd6ce4a9 610 {
f6bcfd97 611 wxLogSysError(_("Failed to redirect child process input/output"));
cd6ce4a9 612 }
518b5d2f 613
b477f956
VZ
614 pipeIn.Close();
615 pipeOut.Close();
616 pipeErr.Close();
cd6ce4a9 617 }
518b5d2f 618
05079acc 619 execvp (*mb_argv, mb_argv);
17a1ebd1 620
d264d709 621 fprintf(stderr, "execvp(");
8d4f85ce
SC
622 // CS changed ppc to ppc_ as ppc is not available under mac os CW Mach-O
623 for ( char **ppc_ = mb_argv; *ppc_; ppc_++ )
624 fprintf(stderr, "%s%s", ppc_ == mb_argv ? "" : ", ", *ppc_);
d264d709
VZ
625 fprintf(stderr, ") failed with error %d!\n", errno);
626
518b5d2f 627 // there is no return after successful exec()
518b5d2f 628 _exit(-1);
1d8dd65e
VZ
629
630 // some compilers complain about missing return - of course, they
631 // should know that exit() doesn't return but what else can we do if
632 // they don't?
b477f956
VZ
633 //
634 // and, sure enough, other compilers complain about unreachable code
635 // after exit() call, so we can just always have return here...
1d8dd65e
VZ
636#if defined(__VMS) || defined(__INTEL_COMPILER)
637 return 0;
638#endif
518b5d2f 639 }
cd6ce4a9 640 else // we're in parent
518b5d2f 641 {
cd6ce4a9
VZ
642 ARGS_CLEANUP;
643
7764f973
VZ
644 // save it for WaitForChild() use
645 execData.pid = pid;
646
80d6dc0a
VZ
647 // prepare for IO redirection
648
1f3b2af0 649#if HAS_PIPE_INPUT_STREAM
80d6dc0a
VZ
650 // the input buffer bufOut is connected to stdout, this is why it is
651 // called bufOut and not bufIn
652 wxStreamTempInputBuffer bufOut,
653 bufErr;
1f3b2af0 654#endif // HAS_PIPE_INPUT_STREAM
0e300ddd 655
cd6ce4a9
VZ
656 if ( process && process->IsRedirected() )
657 {
1f3b2af0 658#if HAS_PIPE_INPUT_STREAM
80d6dc0a
VZ
659 wxOutputStream *inStream =
660 new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
b477f956 661
79066131
VZ
662 wxPipeInputStream *outStream =
663 new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
b477f956 664
79066131
VZ
665 wxPipeInputStream *errStream =
666 new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
f6bcfd97 667
80d6dc0a 668 process->SetPipeStreams(outStream, inStream, errStream);
0e300ddd 669
80d6dc0a 670 bufOut.Init(outStream);
b477f956 671 bufErr.Init(errStream);
e2478fde
VZ
672
673 execData.bufOut = &bufOut;
674 execData.bufErr = &bufErr;
1f3b2af0 675#endif // HAS_PIPE_INPUT_STREAM
b477f956 676 }
1e6feb95 677
b477f956
VZ
678 if ( pipeIn.IsOk() )
679 {
680 pipeIn.Close();
681 pipeOut.Close();
682 pipeErr.Close();
cd6ce4a9
VZ
683 }
684
e2478fde 685 return traits->WaitForChild(execData);
518b5d2f 686 }
79656e30 687
17a1ebd1 688#if !defined(__VMS) && !defined(__INTEL_COMPILER)
79656e30 689 return ERROR_RETURN_CODE;
ef0ed19e 690#endif
17a1ebd1 691}
518b5d2f 692
accb3257 693#undef ERROR_RETURN_CODE
f6bcfd97
BP
694#undef ARGS_CLEANUP
695
518b5d2f
VZ
696// ----------------------------------------------------------------------------
697// file and directory functions
698// ----------------------------------------------------------------------------
699
05079acc 700const wxChar* wxGetHomeDir( wxString *home )
518b5d2f 701{
14d63513 702 *home = wxGetUserHome();
79066131 703 wxString tmp;
8ea92b4d 704 if ( home->empty() )
223d09f6 705 *home = wxT("/");
181cbcf4 706#ifdef __VMS
79066131
VZ
707 tmp = *home;
708 if ( tmp.Last() != wxT(']'))
709 if ( tmp.Last() != wxT('/')) *home << wxT('/');
181cbcf4 710#endif
518b5d2f
VZ
711 return home->c_str();
712}
713
14d63513 714wxString wxGetUserHome( const wxString &user )
518b5d2f
VZ
715{
716 struct passwd *who = (struct passwd *) NULL;
717
0fb67cd1 718 if ( !user )
518b5d2f 719 {
e90c1d2a 720 wxChar *ptr;
518b5d2f 721
223d09f6 722 if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
518b5d2f
VZ
723 {
724 return ptr;
725 }
14d63513
VZ
726
727 if ((ptr = wxGetenv(wxT("USER"))) != NULL ||
728 (ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
518b5d2f 729 {
69c928ef 730 who = getpwnam(wxSafeConvertWX2MB(ptr));
518b5d2f
VZ
731 }
732
14d63513
VZ
733 // make sure the user exists!
734 if ( !who )
518b5d2f
VZ
735 {
736 who = getpwuid(getuid());
737 }
738 }
739 else
740 {
05079acc 741 who = getpwnam (user.mb_str());
518b5d2f
VZ
742 }
743
69c928ef 744 return wxSafeConvertMB2WX(who ? who->pw_dir : 0);
518b5d2f
VZ
745}
746
747// ----------------------------------------------------------------------------
0fb67cd1 748// network and user id routines
518b5d2f
VZ
749// ----------------------------------------------------------------------------
750
8bb6b2c0
VZ
751// private utility function which returns output of the given command, removing
752// the trailing newline
753static wxString wxGetCommandOutput(const wxString &cmd)
754{
755 FILE *f = popen(cmd.ToAscii(), "r");
756 if ( !f )
757 {
758 wxLogSysError(_T("Executing \"%s\" failed"), cmd.c_str());
759 return wxEmptyString;
760 }
761
762 wxString s;
763 char buf[256];
764 while ( !feof(f) )
765 {
766 if ( !fgets(buf, sizeof(buf), f) )
767 break;
768
769 s += wxString::FromAscii(buf);
770 }
771
772 pclose(f);
773
774 if ( !s.empty() && s.Last() == _T('\n') )
775 s.RemoveLast();
776
777 return s;
778}
779
0fb67cd1
VZ
780// retrieve either the hostname or FQDN depending on platform (caller must
781// check whether it's one or the other, this is why this function is for
782// private use only)
05079acc 783static bool wxGetHostNameInternal(wxChar *buf, int sz)
518b5d2f 784{
9d8aca48 785 wxCHECK_MSG( buf, false, wxT("NULL pointer in wxGetHostNameInternal") );
518b5d2f 786
223d09f6 787 *buf = wxT('\0');
518b5d2f
VZ
788
789 // we're using uname() which is POSIX instead of less standard sysinfo()
790#if defined(HAVE_UNAME)
cc743a6f 791 struct utsname uts;
518b5d2f
VZ
792 bool ok = uname(&uts) != -1;
793 if ( ok )
794 {
69c928ef 795 wxStrncpy(buf, wxSafeConvertMB2WX(uts.nodename), sz - 1);
223d09f6 796 buf[sz] = wxT('\0');
518b5d2f
VZ
797 }
798#elif defined(HAVE_GETHOSTNAME)
1870f50b
RD
799 char cbuf[sz];
800 bool ok = gethostname(cbuf, sz) != -1;
801 if ( ok )
802 {
69c928ef 803 wxStrncpy(buf, wxSafeConvertMB2WX(cbuf), sz - 1);
1870f50b
RD
804 buf[sz] = wxT('\0');
805 }
0fb67cd1 806#else // no uname, no gethostname
223d09f6 807 wxFAIL_MSG(wxT("don't know host name for this machine"));
518b5d2f 808
9d8aca48 809 bool ok = false;
0fb67cd1 810#endif // uname/gethostname
518b5d2f
VZ
811
812 if ( !ok )
813 {
814 wxLogSysError(_("Cannot get the hostname"));
815 }
816
817 return ok;
818}
819
05079acc 820bool wxGetHostName(wxChar *buf, int sz)
0fb67cd1
VZ
821{
822 bool ok = wxGetHostNameInternal(buf, sz);
823
824 if ( ok )
825 {
826 // BSD systems return the FQDN, we only want the hostname, so extract
827 // it (we consider that dots are domain separators)
223d09f6 828 wxChar *dot = wxStrchr(buf, wxT('.'));
0fb67cd1
VZ
829 if ( dot )
830 {
831 // nuke it
223d09f6 832 *dot = wxT('\0');
0fb67cd1
VZ
833 }
834 }
835
836 return ok;
837}
838
05079acc 839bool wxGetFullHostName(wxChar *buf, int sz)
0fb67cd1
VZ
840{
841 bool ok = wxGetHostNameInternal(buf, sz);
842
843 if ( ok )
844 {
223d09f6 845 if ( !wxStrchr(buf, wxT('.')) )
0fb67cd1 846 {
69c928ef 847 struct hostent *host = gethostbyname(wxSafeConvertWX2MB(buf));
0fb67cd1
VZ
848 if ( !host )
849 {
850 wxLogSysError(_("Cannot get the official hostname"));
851
9d8aca48 852 ok = false;
0fb67cd1
VZ
853 }
854 else
855 {
856 // the canonical name
69c928ef 857 wxStrncpy(buf, wxSafeConvertMB2WX(host->h_name), sz);
0fb67cd1
VZ
858 }
859 }
860 //else: it's already a FQDN (BSD behaves this way)
861 }
862
863 return ok;
864}
865
05079acc 866bool wxGetUserId(wxChar *buf, int sz)
518b5d2f
VZ
867{
868 struct passwd *who;
869
223d09f6 870 *buf = wxT('\0');
518b5d2f
VZ
871 if ((who = getpwuid(getuid ())) != NULL)
872 {
69c928ef 873 wxStrncpy (buf, wxSafeConvertMB2WX(who->pw_name), sz - 1);
9d8aca48 874 return true;
518b5d2f
VZ
875 }
876
9d8aca48 877 return false;
518b5d2f
VZ
878}
879
05079acc 880bool wxGetUserName(wxChar *buf, int sz)
518b5d2f 881{
69c928ef 882#ifdef HAVE_PW_GECOS
518b5d2f 883 struct passwd *who;
518b5d2f 884
223d09f6 885 *buf = wxT('\0');
b12915c1
VZ
886 if ((who = getpwuid (getuid ())) != NULL)
887 {
b12915c1 888 char *comma = strchr(who->pw_gecos, ',');
518b5d2f
VZ
889 if (comma)
890 *comma = '\0'; // cut off non-name comment fields
69c928ef 891 wxStrncpy (buf, wxSafeConvertMB2WX(who->pw_gecos), sz - 1);
9d8aca48 892 return true;
518b5d2f
VZ
893 }
894
9d8aca48 895 return false;
69c928ef
VZ
896#else // !HAVE_PW_GECOS
897 return wxGetUserId(buf, sz);
898#endif // HAVE_PW_GECOS/!HAVE_PW_GECOS
518b5d2f
VZ
899}
900
c655557f
VZ
901bool wxIsPlatform64Bit()
902{
2f6aa043
VZ
903 const wxString machine = wxGetCommandOutput(wxT("uname -m"));
904
905 // the test for "64" is obviously not 100% reliable but seems to work fine
906 // in practice
907 return machine.Contains(wxT("64")) ||
908 machine.Contains(wxT("alpha"));
c655557f
VZ
909}
910
911// these functions are in mac/utils.cpp for wxMac
912#ifndef __WXMAC__
913
8bb6b2c0
VZ
914wxOperatingSystemId wxGetOsVersion(int *verMaj, int *verMin)
915{
916 // get OS version
917 int major, minor;
918 wxString release = wxGetCommandOutput(wxT("uname -r"));
86501081
VS
919 if ( release.empty() ||
920 wxSscanf(release.c_str(), wxT("%d.%d"), &major, &minor) != 2 )
8bb6b2c0 921 {
e1379e29 922 // failed to get version string or unrecognized format
8bb6b2c0
VZ
923 major =
924 minor = -1;
925 }
926
927 if ( verMaj )
928 *verMaj = major;
929 if ( verMin )
930 *verMin = minor;
931
932 // try to understand which OS are we running
933 wxString kernel = wxGetCommandOutput(wxT("uname -s"));
934 if ( kernel.empty() )
935 kernel = wxGetCommandOutput(wxT("uname -o"));
936
937 if ( kernel.empty() )
938 return wxOS_UNKNOWN;
939
940 return wxPlatformInfo::GetOperatingSystemId(kernel);
941}
942
bdc72a22
VZ
943wxString wxGetOsDescription()
944{
8bb6b2c0 945 return wxGetCommandOutput(wxT("uname -s -r -m"));
bdc72a22
VZ
946}
947
e2478fde 948#endif // !__WXMAC__
bd3277fe 949
c1cb4153
VZ
950unsigned long wxGetProcessId()
951{
952 return (unsigned long)getpid();
953}
954
9d8aca48 955wxMemorySize wxGetFreeMemory()
bd3277fe
VZ
956{
957#if defined(__LINUX__)
958 // get it from /proc/meminfo
959 FILE *fp = fopen("/proc/meminfo", "r");
960 if ( fp )
961 {
962 long memFree = -1;
963
964 char buf[1024];
965 if ( fgets(buf, WXSIZEOF(buf), fp) && fgets(buf, WXSIZEOF(buf), fp) )
966 {
7c28d921
VZ
967 // /proc/meminfo changed its format in kernel 2.6
968 if ( wxPlatformInfo().CheckOSVersion(2, 6) )
969 {
970 unsigned long cached, buffers;
971 sscanf(buf, "MemFree: %ld", &memFree);
972
973 fgets(buf, WXSIZEOF(buf), fp);
974 sscanf(buf, "Buffers: %lu", &buffers);
975
976 fgets(buf, WXSIZEOF(buf), fp);
977 sscanf(buf, "Cached: %lu", &cached);
978
979 // add to "MemFree" also the "Buffers" and "Cached" values as
980 // free(1) does as otherwise the value never makes sense: for
981 // kernel 2.6 it's always almost 0
982 memFree += buffers + cached;
983
984 // values here are always expressed in kB and we want bytes
985 memFree *= 1024;
986 }
987 else // Linux 2.4 (or < 2.6, anyhow)
988 {
989 long memTotal, memUsed;
990 sscanf(buf, "Mem: %ld %ld %ld", &memTotal, &memUsed, &memFree);
991 }
bd3277fe
VZ
992 }
993
994 fclose(fp);
995
9d8aca48 996 return (wxMemorySize)memFree;
bd3277fe 997 }
9ad34f61
VZ
998#elif defined(__SGI__)
999 struct rminfo realmem;
1000 if ( sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0 )
1001 return ((wxMemorySize)realmem.physmem * sysconf(_SC_PAGESIZE));
40f00746
VZ
1002#elif defined(_SC_AVPHYS_PAGES)
1003 return ((wxMemorySize)sysconf(_SC_AVPHYS_PAGES))*sysconf(_SC_PAGESIZE);
bd3277fe
VZ
1004//#elif defined(__FREEBSD__) -- might use sysctl() to find it out, probably
1005#endif
1006
1007 // can't find it out
1008 return -1;
1009}
1010
7ba7c4e6 1011bool wxGetDiskSpace(const wxString& path, wxDiskspaceSize_t *pTotal, wxDiskspaceSize_t *pFree)
eadd7bd2 1012{
9952adac 1013#if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
fbfb3fb3 1014 // the case to "char *" is needed for AIX 4.3
85da04e9
VZ
1015 wxStatfs_t fs;
1016 if ( wxStatfs((char *)(const char*)path.fn_str(), &fs) != 0 )
eadd7bd2 1017 {
401eb3de 1018 wxLogSysError( wxT("Failed to get file system statistics") );
eadd7bd2 1019
9d8aca48 1020 return false;
eadd7bd2
VZ
1021 }
1022
125cb99b
VZ
1023 // under Solaris we also have to use f_frsize field instead of f_bsize
1024 // which is in general a multiple of f_frsize
1025#ifdef HAVE_STATVFS
7ba7c4e6 1026 wxDiskspaceSize_t blockSize = fs.f_frsize;
125cb99b 1027#else // HAVE_STATFS
7ba7c4e6 1028 wxDiskspaceSize_t blockSize = fs.f_bsize;
125cb99b 1029#endif // HAVE_STATVFS/HAVE_STATFS
9952adac 1030
eadd7bd2
VZ
1031 if ( pTotal )
1032 {
7ba7c4e6 1033 *pTotal = wxDiskspaceSize_t(fs.f_blocks) * blockSize;
eadd7bd2
VZ
1034 }
1035
1036 if ( pFree )
1037 {
7ba7c4e6 1038 *pFree = wxDiskspaceSize_t(fs.f_bavail) * blockSize;
eadd7bd2
VZ
1039 }
1040
9d8aca48 1041 return true;
125cb99b 1042#else // !HAVE_STATFS && !HAVE_STATVFS
9d8aca48 1043 return false;
125cb99b 1044#endif // HAVE_STATFS
eadd7bd2
VZ
1045}
1046
8fd0d89b
VZ
1047// ----------------------------------------------------------------------------
1048// env vars
1049// ----------------------------------------------------------------------------
1050
c0472c7c
VZ
1051#if USE_PUTENV
1052
1053WX_DECLARE_STRING_HASH_MAP(char *, wxEnvVars);
1054
1055static wxEnvVars gs_envVars;
1056
1057class wxSetEnvModule : public wxModule
1058{
1059public:
1060 virtual bool OnInit() { return true; }
1061 virtual void OnExit()
1062 {
1063 for ( wxEnvVars::const_iterator i = gs_envVars.begin();
1064 i != gs_envVars.end();
1065 ++i )
1066 {
1067 free(i->second);
1068 }
1069
1070 gs_envVars.clear();
1071 }
1072
1073 DECLARE_DYNAMIC_CLASS(wxSetEnvModule)
1074};
1075
1076IMPLEMENT_DYNAMIC_CLASS(wxSetEnvModule, wxModule)
1077
1078#endif // USE_PUTENV
1079
97b305b7 1080bool wxGetEnv(const wxString& var, wxString *value)
308978f6
VZ
1081{
1082 // wxGetenv is defined as getenv()
86501081 1083 char *p = wxGetenv(var);
308978f6 1084 if ( !p )
9d8aca48 1085 return false;
308978f6
VZ
1086
1087 if ( value )
1088 {
1089 *value = p;
1090 }
1091
9d8aca48 1092 return true;
308978f6
VZ
1093}
1094
90a77e64 1095static bool wxDoSetEnv(const wxString& variable, const char *value)
8fd0d89b
VZ
1096{
1097#if defined(HAVE_SETENV)
a1353ea4
VZ
1098 if ( !value )
1099 {
1100#ifdef HAVE_UNSETENV
65fd2fc2
VZ
1101 // don't test unsetenv() return value: it's void on some systems (at
1102 // least Darwin)
1103 unsetenv(variable.mb_str());
022a8d02 1104 return true;
a1353ea4
VZ
1105#else
1106 value = ""; // we can't pass NULL to setenv()
1107#endif
1108 }
1109
90a77e64 1110 return setenv(variable.mb_str(), value, 1 /* overwrite */) == 0;
8fd0d89b
VZ
1111#elif defined(HAVE_PUTENV)
1112 wxString s = variable;
1113 if ( value )
1114 s << _T('=') << value;
1115
1116 // transform to ANSI
67479dbd 1117 const wxWX2MBbuf p = s.mb_str();
8fd0d89b 1118
8fd0d89b
VZ
1119 char *buf = (char *)malloc(strlen(p) + 1);
1120 strcpy(buf, p);
1121
c0472c7c
VZ
1122 // store the string to free() it later
1123 wxEnvVars::iterator i = gs_envVars.find(variable);
1124 if ( i != gs_envVars.end() )
1125 {
1126 free(i->second);
1127 i->second = buf;
1128 }
1129 else // this variable hadn't been set before
1130 {
1131 gs_envVars[variable] = buf;
1132 }
1133
8fd0d89b
VZ
1134 return putenv(buf) == 0;
1135#else // no way to set an env var
67479dbd 1136 return false;
8fd0d89b
VZ
1137#endif
1138}
1139
90a77e64
VS
1140bool wxSetEnv(const wxString& variable, const wxString& value)
1141{
1142 return wxDoSetEnv(variable, value.mb_str());
1143}
1144
1145bool wxUnsetEnv(const wxString& variable)
1146{
1147 return wxDoSetEnv(variable, NULL);
1148}
1149
a37a5a73
VZ
1150// ----------------------------------------------------------------------------
1151// signal handling
1152// ----------------------------------------------------------------------------
1153
1154#if wxUSE_ON_FATAL_EXCEPTION
1155
1156#include <signal.h>
1157
90350682 1158extern "C" void wxFatalSignalHandler(wxTYPE_SA_HANDLER)
a37a5a73
VZ
1159{
1160 if ( wxTheApp )
1161 {
1162 // give the user a chance to do something special about this
1163 wxTheApp->OnFatalException();
1164 }
1165
1166 abort();
1167}
1168
1169bool wxHandleFatalExceptions(bool doit)
1170{
1171 // old sig handlers
9d8aca48 1172 static bool s_savedHandlers = false;
a37a5a73
VZ
1173 static struct sigaction s_handlerFPE,
1174 s_handlerILL,
1175 s_handlerBUS,
1176 s_handlerSEGV;
1177
9d8aca48 1178 bool ok = true;
a37a5a73
VZ
1179 if ( doit && !s_savedHandlers )
1180 {
1181 // install the signal handler
1182 struct sigaction act;
1183
1184 // some systems extend it with non std fields, so zero everything
1185 memset(&act, 0, sizeof(act));
1186
1187 act.sa_handler = wxFatalSignalHandler;
1188 sigemptyset(&act.sa_mask);
1189 act.sa_flags = 0;
1190
1191 ok &= sigaction(SIGFPE, &act, &s_handlerFPE) == 0;
1192 ok &= sigaction(SIGILL, &act, &s_handlerILL) == 0;
1193 ok &= sigaction(SIGBUS, &act, &s_handlerBUS) == 0;
1194 ok &= sigaction(SIGSEGV, &act, &s_handlerSEGV) == 0;
1195 if ( !ok )
1196 {
1197 wxLogDebug(_T("Failed to install our signal handler."));
1198 }
1199
9d8aca48 1200 s_savedHandlers = true;
a37a5a73
VZ
1201 }
1202 else if ( s_savedHandlers )
1203 {
1204 // uninstall the signal handler
1205 ok &= sigaction(SIGFPE, &s_handlerFPE, NULL) == 0;
1206 ok &= sigaction(SIGILL, &s_handlerILL, NULL) == 0;
1207 ok &= sigaction(SIGBUS, &s_handlerBUS, NULL) == 0;
1208 ok &= sigaction(SIGSEGV, &s_handlerSEGV, NULL) == 0;
1209 if ( !ok )
1210 {
1211 wxLogDebug(_T("Failed to uninstall our signal handler."));
1212 }
1213
9d8aca48 1214 s_savedHandlers = false;
a37a5a73
VZ
1215 }
1216 //else: nothing to do
1217
1218 return ok;
1219}
1220
1221#endif // wxUSE_ON_FATAL_EXCEPTION
1222
ec67cff1 1223#endif // wxUSE_BASE
e2478fde
VZ
1224
1225#if wxUSE_GUI
1226
76c39b5d 1227#ifdef __DARWIN__
1a6d6b10
DE
1228 #include <sys/errno.h>
1229#endif
e2478fde
VZ
1230// ----------------------------------------------------------------------------
1231// wxExecute support
1232// ----------------------------------------------------------------------------
1233
a3261ffb
DE
1234/*
1235 NOTE: If this proves not to work well for wxMac then move back to the old
1236 behavior. If, however, it proves to work just fine, nuke all of the code
1237 for the old behavior. I strongly suggest backporting this to 2.8 as well.
1238 However, beware that while you can nuke the old code here, you cannot
1239 nuke the wxAddProcessCallbackForPid from the 2.8 branch (found in
1240 utilsexc_cf since it's an exported symbol).
1241 */
1242// #define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__))
1243#define USE_OLD_DARWIN_END_PROCESS_DETECT 0
1a6d6b10
DE
1244
1245// wxMac/wxCocoa don't use the same process end detection mechanisms so we don't
1246// need wxExecute-related helpers for them
1247#if !USE_OLD_DARWIN_END_PROCESS_DETECT
e2478fde
VZ
1248
1249bool wxGUIAppTraits::CreateEndProcessPipe(wxExecuteData& execData)
1250{
1251 return execData.pipeEndProcDetect.Create();
1252}
1253
1254bool wxGUIAppTraits::IsWriteFDOfEndProcessPipe(wxExecuteData& execData, int fd)
1255{
46446cc2 1256 return fd == (execData.pipeEndProcDetect)[wxPipe::Write];
e2478fde
VZ
1257}
1258
1259void wxGUIAppTraits::DetachWriteFDOfEndProcessPipe(wxExecuteData& execData)
1260{
1261 execData.pipeEndProcDetect.Detach(wxPipe::Write);
1262 execData.pipeEndProcDetect.Close();
1263}
1264
1265#else // !Darwin
1266
1267bool wxGUIAppTraits::CreateEndProcessPipe(wxExecuteData& WXUNUSED(execData))
1268{
1269 return true;
1270}
1271
1272bool
1273wxGUIAppTraits::IsWriteFDOfEndProcessPipe(wxExecuteData& WXUNUSED(execData),
1274 int WXUNUSED(fd))
1275{
1276 return false;
1277}
1278
1279void
1280wxGUIAppTraits::DetachWriteFDOfEndProcessPipe(wxExecuteData& WXUNUSED(execData))
1281{
1282 // nothing to do here, we don't use the pipe
1283}
1284
1285#endif // !Darwin/Darwin
1286
1287int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
1288{
1289 wxEndProcessData *endProcData = new wxEndProcessData;
1290
f38f6899
VZ
1291 const int flags = execData.flags;
1292
e2478fde
VZ
1293 // wxAddProcessCallback is now (with DARWIN) allowed to call the
1294 // callback function directly if the process terminates before
1295 // the callback can be added to the run loop. Set up the endProcData.
f38f6899 1296 if ( flags & wxEXEC_SYNC )
e2478fde
VZ
1297 {
1298 // we may have process for capturing the program output, but it's
1299 // not used in wxEndProcessData in the case of sync execution
1300 endProcData->process = NULL;
1301
1302 // sync execution: indicate it by negating the pid
1303 endProcData->pid = -execData.pid;
1304 }
1305 else
1306 {
1307 // async execution, nothing special to do -- caller will be
1308 // notified about the process termination if process != NULL, endProcData
1309 // will be deleted in GTK_EndProcessDetector
1310 endProcData->process = execData.process;
1311 endProcData->pid = execData.pid;
1312 }
1313
1314
bc855d09
VZ
1315 if ( !(flags & wxEXEC_NOEVENTS) )
1316 {
1a6d6b10 1317#if USE_OLD_DARWIN_END_PROCESS_DETECT
bc855d09 1318 endProcData->tag = wxAddProcessCallbackForPid(endProcData, execData.pid);
e2478fde 1319#else
bc855d09
VZ
1320 endProcData->tag = wxAddProcessCallback
1321 (
1322 endProcData,
1323 execData.pipeEndProcDetect.Detach(wxPipe::Read)
1324 );
e2478fde 1325
bc855d09 1326 execData.pipeEndProcDetect.Close();
1a6d6b10 1327#endif // USE_OLD_DARWIN_END_PROCESS_DETECT
bc855d09 1328 }
e2478fde 1329
f38f6899 1330 if ( flags & wxEXEC_SYNC )
e2478fde
VZ
1331 {
1332 wxBusyCursor bc;
bc855d09
VZ
1333 int exitcode = 0;
1334
1335 wxWindowDisabler *wd = flags & (wxEXEC_NODISABLE | wxEXEC_NOEVENTS)
1336 ? NULL
1337 : new wxWindowDisabler;
e2478fde 1338
bc855d09 1339 if ( flags & wxEXEC_NOEVENTS )
e2478fde 1340 {
bc855d09
VZ
1341 // just block waiting for the child to exit
1342 int status = 0;
05df0f1b 1343
bc855d09 1344 int result = waitpid(execData.pid, &status, 0);
1a6d6b10
DE
1345#ifdef __DARWIN__
1346 /* DE: waitpid manpage states that waitpid can fail with EINTR
1347 if the call is interrupted by a caught signal. I suppose
1348 that means that this ought to be a while loop.
1349
1350 The odd thing is that it seems to fail EVERY time. It fails
1351 with a quickly exiting process (e.g. echo), and fails with a
1352 slowly exiting process (e.g. sleep 2) but clearly after
1353 having waited for the child to exit. Maybe it's a bug in
1354 my particular version.
1355
1356 It works, however, from the CFSocket callback without this
1357 trick but in that case it's used only after CFSocket calls
1358 the callback and with the WNOHANG flag which would seem to
1359 preclude it from being interrupted or at least make it much
1360 less likely since it would not then be waiting.
1361
1362 If Darwin's man page is to be believed then this is definitely
1363 necessary. It's just weird that I've never seen it before
1364 and apparently no one else has either or you'd think they'd
1365 have reported it by now. Perhaps blocking the GUI while
1366 waiting for a child process to exit is simply not that common.
1367 */
1368 if(result == -1 && errno == EINTR)
1369 {
1370 result = waitpid(execData.pid, &status, 0);
1371 }
1372
1373#endif
bc855d09
VZ
1374
1375 if ( result == -1 )
05df0f1b 1376 {
bc855d09
VZ
1377 wxLogLastError(_T("waitpid"));
1378 exitcode = -1;
05df0f1b 1379 }
bc855d09 1380 else
05df0f1b 1381 {
bc855d09
VZ
1382 wxASSERT_MSG( result == execData.pid,
1383 _T("unexpected waitpid() return value") );
1384
1385 if ( WIFEXITED(status) )
1386 {
1387 exitcode = WEXITSTATUS(status);
1388 }
1389 else // abnormal termination?
1390 {
1391 wxASSERT_MSG( WIFSIGNALED(status),
1392 _T("unexpected child wait status") );
1393 exitcode = -1;
1394 }
05df0f1b 1395 }
bc855d09
VZ
1396 }
1397 else // !wxEXEC_NOEVENTS
1398 {
f8eb27bf
VS
1399 // endProcData->pid will be set to 0 from
1400 // wxHandleProcessTermination when the process terminates
bc855d09
VZ
1401 while ( endProcData->pid != 0 )
1402 {
1403 bool idle = true;
1404
1405#if HAS_PIPE_INPUT_STREAM
1406 if ( execData.bufOut )
1407 {
1408 execData.bufOut->Update();
1409 idle = false;
1410 }
1411
1412 if ( execData.bufErr )
1413 {
1414 execData.bufErr->Update();
1415 idle = false;
1416 }
1f3b2af0 1417#endif // HAS_PIPE_INPUT_STREAM
e2478fde 1418
bc855d09
VZ
1419 // don't consume 100% of the CPU while we're sitting in this
1420 // loop
1421 if ( idle )
1422 wxMilliSleep(1);
05df0f1b 1423
bc855d09
VZ
1424 // give GTK+ a chance to call GTK_EndProcessDetector here and
1425 // also repaint the GUI
1426 wxYield();
1427 }
e2478fde 1428
bc855d09
VZ
1429 exitcode = endProcData->exitcode;
1430 }
e2478fde 1431
f38f6899 1432 delete wd;
e2478fde
VZ
1433 delete endProcData;
1434
1435 return exitcode;
1436 }
1437 else // async execution
1438 {
1439 return execData.pid;
1440 }
1441}
1442
df405da5 1443#ifdef wxHAS_GENERIC_PROCESS_CALLBACK
46c7e1a1
VS
1444struct wxEndProcessFDIOHandler : public wxFDIOHandler
1445{
1446 wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
1447 : m_data(data), m_fd(fd)
1448 {}
1449
1450 virtual void OnReadWaiting()
1451 { wxFAIL_MSG("this isn't supposed to happen"); }
1452 virtual void OnWriteWaiting()
1453 { wxFAIL_MSG("this isn't supposed to happen"); }
1454
1455 virtual void OnExceptionWaiting()
1456 {
1457 int pid = (m_data->pid > 0) ? m_data->pid : -(m_data->pid);
1458 int status = 0;
1459
1460 // has the process really terminated?
1461 int rc = waitpid(pid, &status, WNOHANG);
1462 if ( rc == 0 )
1463 {
1464 // This can only happen if the child application closes our dummy
1465 // pipe that is used to monitor its lifetime; in that case, our
1466 // best bet is to pretend the process did terminate, because
1467 // otherwise wxExecute() would hang indefinitely
1468 // (OnExceptionWaiting() won't be called again, the descriptor
1469 // is closed now).
1470 wxLogDebug("Child process (PID %i) still alive, even though notification was received that it terminated.", pid);
1471 }
1472 else if ( rc == -1 )
1473 {
1474 // As above, if waitpid() fails, the best we can do is to log the
1475 // error and pretend the child terminated:
1476 wxLogSysError(_("Failed to check child process' status"));
1477 }
1478
1479 // set exit code to -1 if something bad happened
1480 m_data->exitcode = (rc > 0 && WIFEXITED(status))
1481 ? WEXITSTATUS(status)
1482 : -1;
1483
1484 wxLogTrace("exec",
1485 "Child process (PID %i) terminated with exit code %i",
1486 pid, m_data->exitcode);
1487
1488 // child exited, end waiting
1489 wxFDIODispatcher::Get()->UnregisterFD(m_fd);
1490 close(m_fd);
1491
1492 m_data->fdioHandler = NULL;
1493 wxHandleProcessTermination(m_data);
1494
1495 delete this;
1496 }
1497
1498 wxEndProcessData *m_data;
1499 int m_fd;
1500};
1501
1502int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
1503{
1504 proc_data->fdioHandler = new wxEndProcessFDIOHandler(proc_data, fd);
1505 wxFDIODispatcher::Get()->RegisterFD
1506 (
1507 fd,
1508 proc_data->fdioHandler,
1509 wxFDIO_EXCEPTION
1510 );
1511 return fd; // unused, but return something unique for the tag
1512}
1513#endif // wxHAS_GENERIC_PROCESS_CALLBACK
1514
2dbc444a
RD
1515#endif // wxUSE_GUI
1516#if wxUSE_BASE
1517
e2478fde
VZ
1518void wxHandleProcessTermination(wxEndProcessData *proc_data)
1519{
1520 // notify user about termination if required
1521 if ( proc_data->process )
1522 {
1523 proc_data->process->OnTerminate(proc_data->pid, proc_data->exitcode);
1524 }
1525
1526 // clean up
1527 if ( proc_data->pid > 0 )
1528 {
1529 delete proc_data;
1530 }
1531 else
1532 {
1533 // let wxExecute() know that the process has terminated
1534 proc_data->pid = 0;
1535 }
1536}
1537
2dbc444a 1538#endif // wxUSE_BASE