add support for asynchronous execution in wxBase (patch 1906889)
[wxWidgets.git] / src / unix / utilsunx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/utilsunx.cpp
3 // Purpose: generic Unix implementation of many wx functions
4 // Author: Vadim Zeitlin
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #include "wx/utils.h"
22
23 #define USE_PUTENV (!defined(HAVE_SETENV) && defined(HAVE_PUTENV))
24
25 #ifndef WX_PRECOMP
26 #include "wx/string.h"
27 #include "wx/intl.h"
28 #include "wx/log.h"
29 #include "wx/app.h"
30 #include "wx/wxcrtvararg.h"
31 #if USE_PUTENV
32 #include "wx/module.h"
33 #include "wx/hashmap.h"
34 #endif
35 #endif
36
37 #include "wx/apptrait.h"
38
39 #include "wx/process.h"
40 #include "wx/thread.h"
41
42 #include "wx/wfstream.h"
43
44 #include "wx/unix/execute.h"
45 #include "wx/unix/private.h"
46
47 #ifdef wxHAS_GENERIC_PROCESS_CALLBACK
48 #include "wx/private/fdiodispatcher.h"
49 #endif
50
51 #include <pwd.h>
52 #include <sys/wait.h> // waitpid()
53
54 #ifdef HAVE_SYS_SELECT_H
55 # include <sys/select.h>
56 #endif
57
58 #define HAS_PIPE_INPUT_STREAM (wxUSE_STREAMS && wxUSE_FILE)
59
60 #if HAS_PIPE_INPUT_STREAM
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
66 #endif // HAS_PIPE_INPUT_STREAM
67
68 #if wxUSE_BASE
69
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
88 #endif
89
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
95 #ifdef HAVE_STATFS
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
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
109 #endif // HAVE_STATFS
110
111 #ifdef HAVE_STATVFS
112 #include <sys/statvfs.h>
113
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
121
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
128 #include <stdarg.h>
129 #include <dirent.h>
130 #include <string.h>
131 #include <sys/stat.h>
132 #include <sys/types.h>
133 #include <sys/wait.h>
134 #include <unistd.h>
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()
140 #include <ctype.h> // isspace()
141 #include <sys/time.h> // needed for FD_SETSIZE
142
143 #ifdef HAVE_UNAME
144 #include <sys/utsname.h> // for uname()
145 #endif // HAVE_UNAME
146
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
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.
160 #if !defined(HAVE_USLEEP) && \
161 ((defined(__SUN__) && !defined(__SunOs_5_6) && \
162 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
163 defined(__osf__) || defined(__EMX__))
164 extern "C"
165 {
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
175 int usleep(unsigned int usec);
176 #endif // __EMX__/Unix
177 };
178
179 #define HAVE_USLEEP 1
180 #endif // Unices without usleep()
181
182 // ============================================================================
183 // implementation
184 // ============================================================================
185
186 // ----------------------------------------------------------------------------
187 // sleeping
188 // ----------------------------------------------------------------------------
189
190 void wxSleep(int nSecs)
191 {
192 sleep(nSecs);
193 }
194
195 void wxMicroSleep(unsigned long microseconds)
196 {
197 #if defined(HAVE_NANOSLEEP)
198 timespec tmReq;
199 tmReq.tv_sec = (time_t)(microseconds / 1000000);
200 tmReq.tv_nsec = (microseconds % 1000000) * 1000;
201
202 // we're not interested in remaining time nor in return value
203 (void)nanosleep(&tmReq, (timespec *)NULL);
204 #elif defined(HAVE_USLEEP)
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
209 #if defined(__SUN__) && wxUSE_THREADS
210 #error "usleep() cannot be used in MT programs under Solaris."
211 #endif // Sun
212
213 usleep(microseconds);
214 #elif defined(HAVE_SLEEP)
215 // under BeOS sleep() takes seconds (what about other platforms, if any?)
216 sleep(microseconds * 1000000);
217 #else // !sleep function
218 #error "usleep() or nanosleep() function required for wxMicroSleep"
219 #endif // sleep function
220 }
221
222 void wxMilliSleep(unsigned long milliseconds)
223 {
224 wxMicroSleep(milliseconds*1000);
225 }
226
227 // ----------------------------------------------------------------------------
228 // process management
229 // ----------------------------------------------------------------------------
230
231 int wxKill(long pid, wxSignal sig, wxKillError *rc, int flags)
232 {
233 int err = kill((pid_t) (flags & wxKILL_CHILDREN) ? -pid : pid, (int)sig);
234 if ( rc )
235 {
236 switch ( err ? errno : 0 )
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;
264 }
265
266 #define WXEXECUTE_NARGS 127
267
268 #if defined(__DARWIN__)
269 long wxMacExecute(wxChar **argv,
270 int flags,
271 wxProcess *process);
272 #endif
273
274 long wxExecute( const wxString& command, int flags, wxProcess *process )
275 {
276 wxCHECK_MSG( !command.empty(), 0, wxT("can't exec empty command") );
277
278 wxLogTrace(wxT("exec"), wxT("Executing \"%s\""), command.c_str());
279
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
290 int argc = 0;
291 wxChar *argv[WXEXECUTE_NARGS];
292 wxString argument;
293 const wxChar *cptr = command.c_str();
294 wxChar quotechar = wxT('\0'); // is arg quoted?
295 bool escaped = false;
296
297 // split the command line in arguments
298 do
299 {
300 argument = wxEmptyString;
301 quotechar = wxT('\0');
302
303 // eat leading whitespace:
304 while ( wxIsspace(*cptr) )
305 cptr++;
306
307 if ( *cptr == wxT('\'') || *cptr == wxT('"') )
308 quotechar = *cptr++;
309
310 do
311 {
312 if ( *cptr == wxT('\\') && ! escaped )
313 {
314 escaped = true;
315 cptr++;
316 continue;
317 }
318
319 // all other characters:
320 argument += *cptr++;
321 escaped = false;
322
323 // have we reached the end of the argument?
324 if ( (*cptr == quotechar && ! escaped)
325 || (quotechar == wxT('\0') && wxIsspace(*cptr))
326 || *cptr == wxT('\0') )
327 {
328 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
329 wxT("too many arguments in wxExecute") );
330
331 argv[argc] = new wxChar[argument.length() + 1];
332 wxStrcpy(argv[argc], argument.c_str());
333 argc++;
334
335 // if not at end of buffer, swallow last character:
336 if(*cptr)
337 cptr++;
338
339 break; // done with this one, start over
340 }
341 } while(*cptr);
342 } while(*cptr);
343 argv[argc] = NULL;
344
345 long lRc;
346 #if defined(__DARWIN__)
347 // wxMacExecute only executes app bundles.
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.
350 lRc = wxMacExecute(argv, flags, process);
351 if( lRc != ((flags & wxEXEC_SYNC) ? -1 : 0))
352 return lRc;
353 #endif
354
355 // do execute the command
356 lRc = wxExecute(argv, flags, process);
357
358 // clean up
359 argc = 0;
360 while( argv[argc] )
361 delete [] argv[argc++];
362
363 return lRc;
364 }
365
366 // ----------------------------------------------------------------------------
367 // wxShell
368 // ----------------------------------------------------------------------------
369
370 static wxString wxMakeShellCommand(const wxString& command)
371 {
372 wxString cmd;
373 if ( !command )
374 {
375 // just an interactive shell
376 cmd = _T("xterm");
377 }
378 else
379 {
380 // execute command in a shell
381 cmd << _T("/bin/sh -c '") << command << _T('\'');
382 }
383
384 return cmd;
385 }
386
387 bool wxShell(const wxString& command)
388 {
389 return wxExecute(wxMakeShellCommand(command), wxEXEC_SYNC) == 0;
390 }
391
392 bool wxShell(const wxString& command, wxArrayString& output)
393 {
394 wxCHECK_MSG( !command.empty(), false, _T("can't exec shell non interactively") );
395
396 return wxExecute(wxMakeShellCommand(command), output);
397 }
398
399 // Shutdown or reboot the PC
400 bool 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") );
415 return false;
416 }
417
418 return system(wxString::Format(_T("init %c"), level).mb_str()) == 0;
419 }
420
421 // ----------------------------------------------------------------------------
422 // wxStream classes to support IO redirection in wxExecute
423 // ----------------------------------------------------------------------------
424
425 #if HAS_PIPE_INPUT_STREAM
426
427 bool wxPipeInputStream::CanRead() const
428 {
429 if ( m_lasterror == wxSTREAM_EOF )
430 return false;
431
432 // check if there is any input available
433 struct timeval tv;
434 tv.tv_sec = 0;
435 tv.tv_usec = 0;
436
437 const int fd = m_file->fd();
438
439 fd_set readfds;
440
441 wxFD_ZERO(&readfds);
442 wxFD_SET(fd, &readfds);
443
444 switch ( select(fd + 1, &readfds, NULL, NULL, &tv) )
445 {
446 case -1:
447 wxLogSysError(_("Impossible to get child process input"));
448 // fall through
449
450 case 0:
451 return false;
452
453 default:
454 wxFAIL_MSG(_T("unexpected select() return value"));
455 // still fall through
456
457 case 1:
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();
462 }
463 }
464
465 #endif // HAS_PIPE_INPUT_STREAM
466
467 // ----------------------------------------------------------------------------
468 // wxExecute: the real worker function
469 // ----------------------------------------------------------------------------
470
471 long wxExecute(wxChar **argv, int flags, wxProcess *process)
472 {
473 // for the sync execution, we return -1 to indicate failure, but for async
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()"
478 #define ERROR_RETURN_CODE ((flags & wxEXEC_SYNC) ? -1 : 0)
479
480 wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );
481
482 #if wxUSE_UNICODE
483 int mb_argc = 0;
484 char *mb_argv[WXEXECUTE_NARGS];
485
486 while (argv[mb_argc])
487 {
488 wxWX2MBbuf mb_arg = wxSafeConvertWX2MB(argv[mb_argc]);
489 mb_argv[mb_argc] = strdup(mb_arg);
490 mb_argc++;
491 }
492 mb_argv[mb_argc] = (char *) NULL;
493
494 // this macro will free memory we used above
495 #define ARGS_CLEANUP \
496 for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
497 free(mb_argv[mb_argc])
498 #else // ANSI
499 // no need for cleanup
500 #define ARGS_CLEANUP
501
502 wxChar **mb_argv = argv;
503 #endif // Unicode/ANSI
504
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
518 // create pipes
519 if ( !traits->CreateEndProcessPipe(execData) )
520 {
521 wxLogError( _("Failed to execute '%s'\n"), *argv );
522
523 ARGS_CLEANUP;
524
525 return ERROR_RETURN_CODE;
526 }
527
528 // pipes for inter process communication
529 wxPipe pipeIn, // stdin
530 pipeOut, // stdout
531 pipeErr; // stderr
532
533 if ( process && process->IsRedirected() )
534 {
535 if ( !pipeIn.Create() || !pipeOut.Create() || !pipeErr.Create() )
536 {
537 wxLogError( _("Failed to execute '%s'\n"), *argv );
538
539 ARGS_CLEANUP;
540
541 return ERROR_RETURN_CODE;
542 }
543 }
544
545 // fork the process
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)
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?
557 {
558 wxLogSysError( _("Fork failed") );
559
560 ARGS_CLEANUP;
561
562 return ERROR_RETURN_CODE;
563 }
564 else if ( pid == 0 ) // we're in child
565 {
566 // These lines close the open file descriptors to to avoid any
567 // input/output which might block the process or irritate the user. If
568 // one wants proper IO for the subprocess, the right thing to do is to
569 // start an xterm executing it.
570 if ( !(flags & wxEXEC_SYNC) )
571 {
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++ )
575 {
576 if ( fd == pipeIn[wxPipe::Read]
577 || fd == pipeOut[wxPipe::Write]
578 || fd == pipeErr[wxPipe::Write]
579 || traits->IsWriteFDOfEndProcessPipe(execData, fd) )
580 {
581 // don't close this one, we still need it
582 continue;
583 }
584
585 // leave stderr opened too, it won't do any harm
586 if ( fd != STDERR_FILENO )
587 close(fd);
588 }
589 }
590
591 #if !defined(__VMS) && !defined(__EMX__)
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();
597 }
598 #endif // !__VMS
599
600 // reading side can be safely closed but we should keep the write one
601 // opened
602 traits->DetachWriteFDOfEndProcessPipe(execData);
603
604 // redirect stdin, stdout and stderr
605 if ( pipeIn.IsOk() )
606 {
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 )
610 {
611 wxLogSysError(_("Failed to redirect child process input/output"));
612 }
613
614 pipeIn.Close();
615 pipeOut.Close();
616 pipeErr.Close();
617 }
618
619 execvp (*mb_argv, mb_argv);
620
621 fprintf(stderr, "execvp(");
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_);
625 fprintf(stderr, ") failed with error %d!\n", errno);
626
627 // there is no return after successful exec()
628 _exit(-1);
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?
633 //
634 // and, sure enough, other compilers complain about unreachable code
635 // after exit() call, so we can just always have return here...
636 #if defined(__VMS) || defined(__INTEL_COMPILER)
637 return 0;
638 #endif
639 }
640 else // we're in parent
641 {
642 ARGS_CLEANUP;
643
644 // save it for WaitForChild() use
645 execData.pid = pid;
646
647 // prepare for IO redirection
648
649 #if HAS_PIPE_INPUT_STREAM
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;
654 #endif // HAS_PIPE_INPUT_STREAM
655
656 if ( process && process->IsRedirected() )
657 {
658 #if HAS_PIPE_INPUT_STREAM
659 wxOutputStream *inStream =
660 new wxFileOutputStream(pipeIn.Detach(wxPipe::Write));
661
662 wxPipeInputStream *outStream =
663 new wxPipeInputStream(pipeOut.Detach(wxPipe::Read));
664
665 wxPipeInputStream *errStream =
666 new wxPipeInputStream(pipeErr.Detach(wxPipe::Read));
667
668 process->SetPipeStreams(outStream, inStream, errStream);
669
670 bufOut.Init(outStream);
671 bufErr.Init(errStream);
672
673 execData.bufOut = &bufOut;
674 execData.bufErr = &bufErr;
675 #endif // HAS_PIPE_INPUT_STREAM
676 }
677
678 if ( pipeIn.IsOk() )
679 {
680 pipeIn.Close();
681 pipeOut.Close();
682 pipeErr.Close();
683 }
684
685 return traits->WaitForChild(execData);
686 }
687
688 #if !defined(__VMS) && !defined(__INTEL_COMPILER)
689 return ERROR_RETURN_CODE;
690 #endif
691 }
692
693 #undef ERROR_RETURN_CODE
694 #undef ARGS_CLEANUP
695
696 // ----------------------------------------------------------------------------
697 // file and directory functions
698 // ----------------------------------------------------------------------------
699
700 const wxChar* wxGetHomeDir( wxString *home )
701 {
702 *home = wxGetUserHome();
703 wxString tmp;
704 if ( home->empty() )
705 *home = wxT("/");
706 #ifdef __VMS
707 tmp = *home;
708 if ( tmp.Last() != wxT(']'))
709 if ( tmp.Last() != wxT('/')) *home << wxT('/');
710 #endif
711 return home->c_str();
712 }
713
714 wxString wxGetUserHome( const wxString &user )
715 {
716 struct passwd *who = (struct passwd *) NULL;
717
718 if ( !user )
719 {
720 wxChar *ptr;
721
722 if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
723 {
724 return ptr;
725 }
726
727 if ((ptr = wxGetenv(wxT("USER"))) != NULL ||
728 (ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
729 {
730 who = getpwnam(wxSafeConvertWX2MB(ptr));
731 }
732
733 // make sure the user exists!
734 if ( !who )
735 {
736 who = getpwuid(getuid());
737 }
738 }
739 else
740 {
741 who = getpwnam (user.mb_str());
742 }
743
744 return wxSafeConvertMB2WX(who ? who->pw_dir : 0);
745 }
746
747 // ----------------------------------------------------------------------------
748 // network and user id routines
749 // ----------------------------------------------------------------------------
750
751 // private utility function which returns output of the given command, removing
752 // the trailing newline
753 static 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
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)
783 static bool wxGetHostNameInternal(wxChar *buf, int sz)
784 {
785 wxCHECK_MSG( buf, false, wxT("NULL pointer in wxGetHostNameInternal") );
786
787 *buf = wxT('\0');
788
789 // we're using uname() which is POSIX instead of less standard sysinfo()
790 #if defined(HAVE_UNAME)
791 struct utsname uts;
792 bool ok = uname(&uts) != -1;
793 if ( ok )
794 {
795 wxStrncpy(buf, wxSafeConvertMB2WX(uts.nodename), sz - 1);
796 buf[sz] = wxT('\0');
797 }
798 #elif defined(HAVE_GETHOSTNAME)
799 char cbuf[sz];
800 bool ok = gethostname(cbuf, sz) != -1;
801 if ( ok )
802 {
803 wxStrncpy(buf, wxSafeConvertMB2WX(cbuf), sz - 1);
804 buf[sz] = wxT('\0');
805 }
806 #else // no uname, no gethostname
807 wxFAIL_MSG(wxT("don't know host name for this machine"));
808
809 bool ok = false;
810 #endif // uname/gethostname
811
812 if ( !ok )
813 {
814 wxLogSysError(_("Cannot get the hostname"));
815 }
816
817 return ok;
818 }
819
820 bool wxGetHostName(wxChar *buf, int sz)
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)
828 wxChar *dot = wxStrchr(buf, wxT('.'));
829 if ( dot )
830 {
831 // nuke it
832 *dot = wxT('\0');
833 }
834 }
835
836 return ok;
837 }
838
839 bool wxGetFullHostName(wxChar *buf, int sz)
840 {
841 bool ok = wxGetHostNameInternal(buf, sz);
842
843 if ( ok )
844 {
845 if ( !wxStrchr(buf, wxT('.')) )
846 {
847 struct hostent *host = gethostbyname(wxSafeConvertWX2MB(buf));
848 if ( !host )
849 {
850 wxLogSysError(_("Cannot get the official hostname"));
851
852 ok = false;
853 }
854 else
855 {
856 // the canonical name
857 wxStrncpy(buf, wxSafeConvertMB2WX(host->h_name), sz);
858 }
859 }
860 //else: it's already a FQDN (BSD behaves this way)
861 }
862
863 return ok;
864 }
865
866 bool wxGetUserId(wxChar *buf, int sz)
867 {
868 struct passwd *who;
869
870 *buf = wxT('\0');
871 if ((who = getpwuid(getuid ())) != NULL)
872 {
873 wxStrncpy (buf, wxSafeConvertMB2WX(who->pw_name), sz - 1);
874 return true;
875 }
876
877 return false;
878 }
879
880 bool wxGetUserName(wxChar *buf, int sz)
881 {
882 #ifdef HAVE_PW_GECOS
883 struct passwd *who;
884
885 *buf = wxT('\0');
886 if ((who = getpwuid (getuid ())) != NULL)
887 {
888 char *comma = strchr(who->pw_gecos, ',');
889 if (comma)
890 *comma = '\0'; // cut off non-name comment fields
891 wxStrncpy (buf, wxSafeConvertMB2WX(who->pw_gecos), sz - 1);
892 return true;
893 }
894
895 return false;
896 #else // !HAVE_PW_GECOS
897 return wxGetUserId(buf, sz);
898 #endif // HAVE_PW_GECOS/!HAVE_PW_GECOS
899 }
900
901 bool wxIsPlatform64Bit()
902 {
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"));
909 }
910
911 // these functions are in mac/utils.cpp for wxMac
912 #ifndef __WXMAC__
913
914 wxOperatingSystemId wxGetOsVersion(int *verMaj, int *verMin)
915 {
916 // get OS version
917 int major, minor;
918 wxString release = wxGetCommandOutput(wxT("uname -r"));
919 if ( release.empty() ||
920 wxSscanf(release.c_str(), wxT("%d.%d"), &major, &minor) != 2 )
921 {
922 // failed to get version string or unrecognized format
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
943 wxString wxGetOsDescription()
944 {
945 return wxGetCommandOutput(wxT("uname -s -r -m"));
946 }
947
948 #endif // !__WXMAC__
949
950 unsigned long wxGetProcessId()
951 {
952 return (unsigned long)getpid();
953 }
954
955 wxMemorySize wxGetFreeMemory()
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 {
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 }
992 }
993
994 fclose(fp);
995
996 return (wxMemorySize)memFree;
997 }
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));
1002 #elif defined(_SC_AVPHYS_PAGES)
1003 return ((wxMemorySize)sysconf(_SC_AVPHYS_PAGES))*sysconf(_SC_PAGESIZE);
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
1011 bool wxGetDiskSpace(const wxString& path, wxDiskspaceSize_t *pTotal, wxDiskspaceSize_t *pFree)
1012 {
1013 #if defined(HAVE_STATFS) || defined(HAVE_STATVFS)
1014 // the case to "char *" is needed for AIX 4.3
1015 wxStatfs_t fs;
1016 if ( wxStatfs((char *)(const char*)path.fn_str(), &fs) != 0 )
1017 {
1018 wxLogSysError( wxT("Failed to get file system statistics") );
1019
1020 return false;
1021 }
1022
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
1026 wxDiskspaceSize_t blockSize = fs.f_frsize;
1027 #else // HAVE_STATFS
1028 wxDiskspaceSize_t blockSize = fs.f_bsize;
1029 #endif // HAVE_STATVFS/HAVE_STATFS
1030
1031 if ( pTotal )
1032 {
1033 *pTotal = wxDiskspaceSize_t(fs.f_blocks) * blockSize;
1034 }
1035
1036 if ( pFree )
1037 {
1038 *pFree = wxDiskspaceSize_t(fs.f_bavail) * blockSize;
1039 }
1040
1041 return true;
1042 #else // !HAVE_STATFS && !HAVE_STATVFS
1043 return false;
1044 #endif // HAVE_STATFS
1045 }
1046
1047 // ----------------------------------------------------------------------------
1048 // env vars
1049 // ----------------------------------------------------------------------------
1050
1051 #if USE_PUTENV
1052
1053 WX_DECLARE_STRING_HASH_MAP(char *, wxEnvVars);
1054
1055 static wxEnvVars gs_envVars;
1056
1057 class wxSetEnvModule : public wxModule
1058 {
1059 public:
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
1076 IMPLEMENT_DYNAMIC_CLASS(wxSetEnvModule, wxModule)
1077
1078 #endif // USE_PUTENV
1079
1080 bool wxGetEnv(const wxString& var, wxString *value)
1081 {
1082 // wxGetenv is defined as getenv()
1083 char *p = wxGetenv(var);
1084 if ( !p )
1085 return false;
1086
1087 if ( value )
1088 {
1089 *value = p;
1090 }
1091
1092 return true;
1093 }
1094
1095 static bool wxDoSetEnv(const wxString& variable, const char *value)
1096 {
1097 #if defined(HAVE_SETENV)
1098 if ( !value )
1099 {
1100 #ifdef HAVE_UNSETENV
1101 // don't test unsetenv() return value: it's void on some systems (at
1102 // least Darwin)
1103 unsetenv(variable.mb_str());
1104 return true;
1105 #else
1106 value = ""; // we can't pass NULL to setenv()
1107 #endif
1108 }
1109
1110 return setenv(variable.mb_str(), value, 1 /* overwrite */) == 0;
1111 #elif defined(HAVE_PUTENV)
1112 wxString s = variable;
1113 if ( value )
1114 s << _T('=') << value;
1115
1116 // transform to ANSI
1117 const wxWX2MBbuf p = s.mb_str();
1118
1119 char *buf = (char *)malloc(strlen(p) + 1);
1120 strcpy(buf, p);
1121
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
1134 return putenv(buf) == 0;
1135 #else // no way to set an env var
1136 return false;
1137 #endif
1138 }
1139
1140 bool wxSetEnv(const wxString& variable, const wxString& value)
1141 {
1142 return wxDoSetEnv(variable, value.mb_str());
1143 }
1144
1145 bool wxUnsetEnv(const wxString& variable)
1146 {
1147 return wxDoSetEnv(variable, NULL);
1148 }
1149
1150 // ----------------------------------------------------------------------------
1151 // signal handling
1152 // ----------------------------------------------------------------------------
1153
1154 #if wxUSE_ON_FATAL_EXCEPTION
1155
1156 #include <signal.h>
1157
1158 extern "C" void wxFatalSignalHandler(wxTYPE_SA_HANDLER)
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
1169 bool wxHandleFatalExceptions(bool doit)
1170 {
1171 // old sig handlers
1172 static bool s_savedHandlers = false;
1173 static struct sigaction s_handlerFPE,
1174 s_handlerILL,
1175 s_handlerBUS,
1176 s_handlerSEGV;
1177
1178 bool ok = true;
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
1200 s_savedHandlers = true;
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
1214 s_savedHandlers = false;
1215 }
1216 //else: nothing to do
1217
1218 return ok;
1219 }
1220
1221 #endif // wxUSE_ON_FATAL_EXCEPTION
1222
1223 #endif // wxUSE_BASE
1224
1225 #ifdef __DARWIN__
1226 #include <sys/errno.h>
1227 #endif
1228 // ----------------------------------------------------------------------------
1229 // wxExecute support
1230 // ----------------------------------------------------------------------------
1231
1232 /*
1233 NOTE: If this proves not to work well for wxMac then move back to the old
1234 behavior. If, however, it proves to work just fine, nuke all of the code
1235 for the old behavior. I strongly suggest backporting this to 2.8 as well.
1236 However, beware that while you can nuke the old code here, you cannot
1237 nuke the wxAddProcessCallbackForPid from the 2.8 branch (found in
1238 utilsexc_cf since it's an exported symbol).
1239 */
1240 // #define USE_OLD_DARWIN_END_PROCESS_DETECT (defined(__DARWIN__) && defined(__WXMAC__))
1241 #define USE_OLD_DARWIN_END_PROCESS_DETECT 0
1242
1243 // wxMac/wxCocoa don't use the same process end detection mechanisms so we don't
1244 // need wxExecute-related helpers for them
1245 #if !USE_OLD_DARWIN_END_PROCESS_DETECT
1246
1247 bool wxAppTraits::CreateEndProcessPipe(wxExecuteData& execData)
1248 {
1249 return execData.pipeEndProcDetect.Create();
1250 }
1251
1252 bool wxAppTraits::IsWriteFDOfEndProcessPipe(wxExecuteData& execData, int fd)
1253 {
1254 return fd == (execData.pipeEndProcDetect)[wxPipe::Write];
1255 }
1256
1257 void wxAppTraits::DetachWriteFDOfEndProcessPipe(wxExecuteData& execData)
1258 {
1259 execData.pipeEndProcDetect.Detach(wxPipe::Write);
1260 execData.pipeEndProcDetect.Close();
1261 }
1262
1263 #else // !Darwin
1264
1265 bool wxAppTraits::CreateEndProcessPipe(wxExecuteData& WXUNUSED(execData))
1266 {
1267 return true;
1268 }
1269
1270 bool
1271 wxAppTraits::IsWriteFDOfEndProcessPipe(wxExecuteData& WXUNUSED(execData),
1272 int WXUNUSED(fd))
1273 {
1274 return false;
1275 }
1276
1277 void
1278 wxAppTraits::DetachWriteFDOfEndProcessPipe(wxExecuteData& WXUNUSED(execData))
1279 {
1280 // nothing to do here, we don't use the pipe
1281 }
1282
1283 #endif // !Darwin/Darwin
1284
1285 #if wxUSE_GUI
1286
1287 int wxGUIAppTraits::WaitForChild(wxExecuteData& execData)
1288 {
1289 wxEndProcessData *endProcData = new wxEndProcessData;
1290
1291 const int flags = execData.flags;
1292
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.
1296 if ( flags & wxEXEC_SYNC )
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
1315 if ( !(flags & wxEXEC_NOEVENTS) )
1316 {
1317 #if USE_OLD_DARWIN_END_PROCESS_DETECT
1318 endProcData->tag = wxAddProcessCallbackForPid(endProcData, execData.pid);
1319 #else
1320 endProcData->tag = wxAddProcessCallback
1321 (
1322 endProcData,
1323 execData.pipeEndProcDetect.Detach(wxPipe::Read)
1324 );
1325
1326 execData.pipeEndProcDetect.Close();
1327 #endif // USE_OLD_DARWIN_END_PROCESS_DETECT
1328 }
1329
1330 if ( flags & wxEXEC_SYNC )
1331 {
1332 wxBusyCursor bc;
1333 int exitcode = 0;
1334
1335 wxWindowDisabler *wd = flags & (wxEXEC_NODISABLE | wxEXEC_NOEVENTS)
1336 ? NULL
1337 : new wxWindowDisabler;
1338
1339 if ( flags & wxEXEC_NOEVENTS )
1340 {
1341 // just block waiting for the child to exit
1342 int status = 0;
1343
1344 int result = waitpid(execData.pid, &status, 0);
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
1374
1375 if ( result == -1 )
1376 {
1377 wxLogLastError(_T("waitpid"));
1378 exitcode = -1;
1379 }
1380 else
1381 {
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 }
1395 }
1396 }
1397 else // !wxEXEC_NOEVENTS
1398 {
1399 // endProcData->pid will be set to 0 from
1400 // wxHandleProcessTermination when the process terminates
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 }
1417 #endif // HAS_PIPE_INPUT_STREAM
1418
1419 // don't consume 100% of the CPU while we're sitting in this
1420 // loop
1421 if ( idle )
1422 wxMilliSleep(1);
1423
1424 // give GTK+ a chance to call GTK_EndProcessDetector here and
1425 // also repaint the GUI
1426 wxYield();
1427 }
1428
1429 exitcode = endProcData->exitcode;
1430 }
1431
1432 delete wd;
1433 delete endProcData;
1434
1435 return exitcode;
1436 }
1437 else // async execution
1438 {
1439 return execData.pid;
1440 }
1441 }
1442
1443 #endif //wxUSE_GUI
1444
1445 #if wxUSE_BASE
1446
1447 #ifdef wxHAS_GENERIC_PROCESS_CALLBACK
1448 struct wxEndProcessFDIOHandler : public wxFDIOHandler
1449 {
1450 wxEndProcessFDIOHandler(wxEndProcessData *data, int fd)
1451 : m_data(data), m_fd(fd)
1452 {}
1453
1454 virtual void OnReadWaiting()
1455 { wxFAIL_MSG("this isn't supposed to happen"); }
1456 virtual void OnWriteWaiting()
1457 { wxFAIL_MSG("this isn't supposed to happen"); }
1458
1459 virtual void OnExceptionWaiting()
1460 {
1461 int pid = (m_data->pid > 0) ? m_data->pid : -(m_data->pid);
1462 int status = 0;
1463
1464 // has the process really terminated?
1465 int rc = waitpid(pid, &status, WNOHANG);
1466 if ( rc == 0 )
1467 {
1468 // This can only happen if the child application closes our dummy
1469 // pipe that is used to monitor its lifetime; in that case, our
1470 // best bet is to pretend the process did terminate, because
1471 // otherwise wxExecute() would hang indefinitely
1472 // (OnExceptionWaiting() won't be called again, the descriptor
1473 // is closed now).
1474 wxLogDebug("Child process (PID %i) still alive, even though notification was received that it terminated.", pid);
1475 }
1476 else if ( rc == -1 )
1477 {
1478 // As above, if waitpid() fails, the best we can do is to log the
1479 // error and pretend the child terminated:
1480 wxLogSysError(_("Failed to check child process' status"));
1481 }
1482
1483 // set exit code to -1 if something bad happened
1484 m_data->exitcode = (rc > 0 && WIFEXITED(status))
1485 ? WEXITSTATUS(status)
1486 : -1;
1487
1488 wxLogTrace("exec",
1489 "Child process (PID %i) terminated with exit code %i",
1490 pid, m_data->exitcode);
1491
1492 // child exited, end waiting
1493 wxFDIODispatcher::Get()->UnregisterFD(m_fd);
1494 close(m_fd);
1495
1496 m_data->fdioHandler = NULL;
1497 wxHandleProcessTermination(m_data);
1498
1499 delete this;
1500 }
1501
1502 wxEndProcessData *m_data;
1503 int m_fd;
1504 };
1505
1506 int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
1507 {
1508 proc_data->fdioHandler = new wxEndProcessFDIOHandler(proc_data, fd);
1509 wxFDIODispatcher::Get()->RegisterFD
1510 (
1511 fd,
1512 proc_data->fdioHandler,
1513 wxFDIO_EXCEPTION
1514 );
1515 return fd; // unused, but return something unique for the tag
1516 }
1517 #endif // wxHAS_GENERIC_PROCESS_CALLBACK
1518
1519 void wxHandleProcessTermination(wxEndProcessData *proc_data)
1520 {
1521 // notify user about termination if required
1522 if ( proc_data->process )
1523 {
1524 proc_data->process->OnTerminate(proc_data->pid, proc_data->exitcode);
1525 }
1526
1527 // clean up
1528 if ( proc_data->pid > 0 )
1529 {
1530 delete proc_data;
1531 }
1532 else
1533 {
1534 // let wxExecute() know that the process has terminated
1535 proc_data->pid = 0;
1536 }
1537 }
1538
1539 #endif // wxUSE_BASE