]> git.saurik.com Git - wxWidgets.git/blob - src/unix/utilsunx.cpp
d8925aba2bd6be084936205a46233e56551251e7
[wxWidgets.git] / src / unix / utilsunx.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #include "wx/defs.h"
19 #include "wx/string.h"
20
21 #include "wx/intl.h"
22 #include "wx/log.h"
23
24 #include "wx/utils.h"
25 #include "wx/process.h"
26 #include "wx/thread.h"
27
28 #include "wx/stream.h"
29
30 #if wxUSE_GUI
31 #include "wx/unix/execute.h"
32 #endif
33
34 #include <stdarg.h>
35 #include <dirent.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <sys/wait.h>
41 #include <pwd.h>
42 #include <errno.h>
43 #include <netdb.h>
44 #include <signal.h>
45 #include <fcntl.h> // for O_WRONLY and friends
46 #include <time.h> // nanosleep() and/or usleep()
47 #include <ctype.h> // isspace()
48 #include <sys/time.h> // needed for FD_SETSIZE
49
50 #ifdef HAVE_UNAME
51 #include <sys/utsname.h> // for uname()
52 #endif // HAVE_UNAME
53
54 // ----------------------------------------------------------------------------
55 // conditional compilation
56 // ----------------------------------------------------------------------------
57
58 // many versions of Unices have this function, but it is not defined in system
59 // headers - please add your system here if it is the case for your OS.
60 // SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this.
61 #if !defined(HAVE_USLEEP) && \
62 (defined(__SUN__) && !defined(__SunOs_5_6) && \
63 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
64 defined(__osf__) || defined(__EMX__)
65 extern "C"
66 {
67 #ifdef __SUN__
68 int usleep(unsigned int usec);
69 #else // !Sun
70 #ifdef __EMX__
71 /* I copied this from the XFree86 diffs. AV. */
72 #define INCL_DOSPROCESS
73 #include <os2.h>
74 inline void usleep(unsigned long delay)
75 {
76 DosSleep(delay ? (delay/1000l) : 1l);
77 }
78 #else // !Sun && !EMX
79 void usleep(unsigned long usec);
80 #endif
81 #endif // Sun/EMX/Something else
82 };
83
84 #define HAVE_USLEEP 1
85 #endif // Unices without usleep()
86
87 // ============================================================================
88 // implementation
89 // ============================================================================
90
91 // ----------------------------------------------------------------------------
92 // sleeping
93 // ----------------------------------------------------------------------------
94
95 void wxSleep(int nSecs)
96 {
97 sleep(nSecs);
98 }
99
100 void wxUsleep(unsigned long milliseconds)
101 {
102 #if defined(HAVE_NANOSLEEP)
103 timespec tmReq;
104 tmReq.tv_sec = (time_t)(milliseconds / 1000);
105 tmReq.tv_nsec = (milliseconds % 1000) * 1000 * 1000;
106
107 // we're not interested in remaining time nor in return value
108 (void)nanosleep(&tmReq, (timespec *)NULL);
109 #elif defined(HAVE_USLEEP)
110 // uncomment this if you feel brave or if you are sure that your version
111 // of Solaris has a safe usleep() function but please notice that usleep()
112 // is known to lead to crashes in MT programs in Solaris 2.[67] and is not
113 // documented as MT-Safe
114 #if defined(__SUN__) && wxUSE_THREADS
115 #error "usleep() cannot be used in MT programs under Solaris."
116 #endif // Sun
117
118 usleep(milliseconds * 1000); // usleep(3) wants microseconds
119 #elif defined(HAVE_SLEEP)
120 // under BeOS sleep() takes seconds (what about other platforms, if any?)
121 sleep(milliseconds * 1000);
122 #else // !sleep function
123 #error "usleep() or nanosleep() function required for wxUsleep"
124 #endif // sleep function
125 }
126
127 // ----------------------------------------------------------------------------
128 // process management
129 // ----------------------------------------------------------------------------
130
131 int wxKill(long pid, wxSignal sig)
132 {
133 return kill((pid_t)pid, (int)sig);
134 }
135
136 #define WXEXECUTE_NARGS 127
137
138 long wxExecute( const wxString& command, bool sync, wxProcess *process )
139 {
140 wxCHECK_MSG( !command.IsEmpty(), 0, wxT("can't exec empty command") );
141
142 int argc = 0;
143 wxChar *argv[WXEXECUTE_NARGS];
144 wxString argument;
145 const wxChar *cptr = command.c_str();
146 wxChar quotechar = wxT('\0'); // is arg quoted?
147 bool escaped = FALSE;
148
149 // split the command line in arguments
150 do
151 {
152 argument=wxT("");
153 quotechar = wxT('\0');
154
155 // eat leading whitespace:
156 while ( wxIsspace(*cptr) )
157 cptr++;
158
159 if ( *cptr == wxT('\'') || *cptr == wxT('"') )
160 quotechar = *cptr++;
161
162 do
163 {
164 if ( *cptr == wxT('\\') && ! escaped )
165 {
166 escaped = TRUE;
167 cptr++;
168 continue;
169 }
170
171 // all other characters:
172 argument += *cptr++;
173 escaped = FALSE;
174
175 // have we reached the end of the argument?
176 if ( (*cptr == quotechar && ! escaped)
177 || (quotechar == wxT('\0') && wxIsspace(*cptr))
178 || *cptr == wxT('\0') )
179 {
180 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
181 wxT("too many arguments in wxExecute") );
182
183 argv[argc] = new wxChar[argument.length() + 1];
184 wxStrcpy(argv[argc], argument.c_str());
185 argc++;
186
187 // if not at end of buffer, swallow last character:
188 if(*cptr)
189 cptr++;
190
191 break; // done with this one, start over
192 }
193 } while(*cptr);
194 } while(*cptr);
195 argv[argc] = NULL;
196
197 // do execute the command
198 long lRc = wxExecute(argv, sync, process);
199
200 // clean up
201 argc = 0;
202 while( argv[argc] )
203 delete [] argv[argc++];
204
205 return lRc;
206 }
207
208 bool wxShell(const wxString& command)
209 {
210 wxString cmd;
211 if ( !command )
212 cmd = _T("xterm");
213 else
214 cmd = command;
215
216 return wxExecute(cmd) != 0;
217 }
218
219 #if wxUSE_GUI
220
221 void wxHandleProcessTermination(wxEndProcessData *proc_data)
222 {
223 int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid);
224
225 // waitpid is POSIX so should be available everywhere, however on older
226 // systems wait() might be used instead in a loop (until the right pid
227 // terminates)
228 int status = 0;
229 int rc;
230
231 // wait for child termination and if waitpid() was interrupted, try again
232 do
233 {
234 rc = waitpid(pid, &status, 0);
235 }
236 while ( rc == -1 && errno == EINTR );
237
238
239 if( rc == -1 || ! (WIFEXITED(status) || WIFSIGNALED(status)) )
240 {
241 wxLogSysError(_("Waiting for subprocess termination failed"));
242 /* AFAIK, this can only happen if something went wrong within
243 wxGTK, i.e. due to a race condition or some serious bug.
244 After having fixed the order of statements in
245 GTK_EndProcessDetector(). (KB)
246 */
247 }
248 else
249 {
250 // notify user about termination if required
251 if (proc_data->process)
252 {
253 proc_data->process->OnTerminate(proc_data->pid,
254 WEXITSTATUS(status));
255 }
256 // clean up
257 if ( proc_data->pid > 0 )
258 {
259 delete proc_data;
260 }
261 else
262 {
263 // wxExecute() will know about it
264 proc_data->exitcode = status;
265
266 proc_data->pid = 0;
267 }
268 }
269 }
270
271 #endif // wxUSE_GUI
272
273 // ----------------------------------------------------------------------------
274 // wxStream classes to support IO redirection in wxExecute
275 // ----------------------------------------------------------------------------
276
277 class wxProcessFileInputStream : public wxInputStream
278 {
279 public:
280 wxProcessFileInputStream(int fd) { m_fd = fd; }
281 ~wxProcessFileInputStream() { close(m_fd); }
282
283 virtual bool Eof() const;
284
285 protected:
286 size_t OnSysRead(void *buffer, size_t bufsize);
287
288 protected:
289 int m_fd;
290 };
291
292 class wxProcessFileOutputStream : public wxOutputStream
293 {
294 public:
295 wxProcessFileOutputStream(int fd) { m_fd = fd; }
296 ~wxProcessFileOutputStream() { close(m_fd); }
297
298 protected:
299 size_t OnSysWrite(const void *buffer, size_t bufsize);
300
301 protected:
302 int m_fd;
303 };
304
305 bool wxProcessFileInputStream::Eof() const
306 {
307 if ( m_lasterror == wxSTREAM_EOF )
308 return TRUE;
309
310 // check if there is any input available
311 struct timeval tv;
312 tv.tv_sec = 0;
313 tv.tv_usec = 0;
314
315 fd_set readfds;
316 FD_ZERO(&readfds);
317 FD_SET(m_fd, &readfds);
318 switch ( select(m_fd + 1, &readfds, NULL, NULL, &tv) )
319 {
320 case -1:
321 wxLogSysError(_("Impossible to get child process input"));
322 // fall through
323
324 case 0:
325 return TRUE;
326
327 default:
328 wxFAIL_MSG(_T("unexpected select() return value"));
329 // still fall through
330
331 case 1:
332 // input available: check if there is any
333 return wxInputStream::Eof();
334 }
335 }
336
337 size_t wxProcessFileInputStream::OnSysRead(void *buffer, size_t bufsize)
338 {
339 int ret = read(m_fd, buffer, bufsize);
340 if ( ret == 0 )
341 {
342 m_lasterror = wxSTREAM_EOF;
343 }
344 else if ( ret == -1 )
345 {
346 m_lasterror = wxSTREAM_READ_ERROR;
347 ret = 0;
348 }
349 else
350 {
351 m_lasterror = wxSTREAM_NOERROR;
352 }
353
354 return ret;
355 }
356
357 size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
358 {
359 int ret = write(m_fd, buffer, bufsize);
360 if ( ret == -1 )
361 {
362 m_lasterror = wxSTREAM_WRITE_ERROR;
363 ret = 0;
364 }
365 else
366 {
367 m_lasterror = wxSTREAM_NOERROR;
368 }
369
370 return ret;
371 }
372
373 long wxExecute(wxChar **argv,
374 bool sync,
375 wxProcess *process)
376 {
377 wxCHECK_MSG( *argv, 0, wxT("can't exec empty command") );
378
379 #if wxUSE_UNICODE
380 int mb_argc = 0;
381 char *mb_argv[WXEXECUTE_NARGS];
382
383 while (argv[mb_argc])
384 {
385 wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]);
386 mb_argv[mb_argc] = strdup(mb_arg);
387 mb_argc++;
388 }
389 mb_argv[mb_argc] = (char *) NULL;
390
391 // this macro will free memory we used above
392 #define ARGS_CLEANUP \
393 for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
394 free(mb_argv[mb_argc])
395 #else // ANSI
396 // no need for cleanup
397 #define ARGS_CLEANUP
398
399 wxChar **mb_argv = argv;
400 #endif // Unicode/ANSI
401
402 #if wxUSE_GUI
403 // create pipes
404 int end_proc_detect[2];
405 if ( pipe(end_proc_detect) == -1 )
406 {
407 wxLogSysError( _("Pipe creation failed") );
408 wxLogError( _("Failed to execute '%s'\n"), *argv );
409
410 ARGS_CLEANUP;
411
412 return 0;
413 }
414 #endif // wxUSE_GUI
415
416 int pipeIn[2];
417 int pipeOut[2];
418 pipeIn[0] = pipeIn[1] =
419 pipeOut[0] = pipeOut[1] = -1;
420
421 if ( process && process->IsRedirected() )
422 {
423 if ( pipe(pipeIn) == -1 || pipe(pipeOut) == -1 )
424 {
425 #if wxUSE_GUI
426 // free previously allocated resources
427 close(end_proc_detect[0]);
428 close(end_proc_detect[1]);
429 #endif // wxUSE_GUI
430
431 wxLogSysError( _("Pipe creation failed") );
432 wxLogError( _("Failed to execute '%s'\n"), *argv );
433
434 ARGS_CLEANUP;
435
436 return 0;
437 }
438 }
439
440 // fork the process
441 #ifdef HAVE_VFORK
442 pid_t pid = vfork();
443 #else
444 pid_t pid = fork();
445 #endif
446
447 if ( pid == -1 ) // error?
448 {
449 #if wxUSE_GUI
450 close(end_proc_detect[0]);
451 close(end_proc_detect[1]);
452 close(pipeIn[0]);
453 close(pipeIn[1]);
454 close(pipeOut[0]);
455 close(pipeOut[1]);
456 #endif // wxUSE_GUI
457
458 wxLogSysError( _("Fork failed") );
459
460 ARGS_CLEANUP;
461
462 return 0;
463 }
464 else if ( pid == 0 ) // we're in child
465 {
466 #if wxUSE_GUI
467 close(end_proc_detect[0]); // close reading side
468 #endif // wxUSE_GUI
469
470 // These lines close the open file descriptors to to avoid any
471 // input/output which might block the process or irritate the user. If
472 // one wants proper IO for the subprocess, the right thing to do is to
473 // start an xterm executing it.
474 if ( !sync )
475 {
476 for ( int fd = 0; fd < FD_SETSIZE; fd++ )
477 {
478 if ( fd == pipeIn[0] || fd == pipeOut[1]
479 #if wxUSE_GUI
480 || fd == end_proc_detect[1]
481 #endif // wxUSE_GUI
482 )
483 {
484 // don't close this one, we still need it
485 continue;
486 }
487
488 // leave stderr opened too, it won't do any hurm
489 if ( fd != STDERR_FILENO )
490 close(fd);
491 }
492 }
493
494 // redirect stdio and stdout
495 // (TODO: what about stderr?)
496 if ( pipeIn[0] != -1 )
497 {
498 if ( dup2(pipeIn[0], STDIN_FILENO) == -1 ||
499 dup2(pipeOut[1], STDOUT_FILENO) == -1 )
500 {
501 wxLogSysError(_("Failed to redirect child process "
502 "input/output"));
503 }
504
505 close(pipeIn[0]);
506 close(pipeOut[1]);
507 }
508
509 execvp (*mb_argv, mb_argv);
510
511 // there is no return after successful exec()
512 _exit(-1);
513 }
514 else // we're in parent
515 {
516 ARGS_CLEANUP;
517
518 // pipe initialization: construction of the wxStreams
519 if ( process && process->IsRedirected() )
520 {
521 // These two streams are relative to this process.
522 wxOutputStream *outStream = new wxProcessFileOutputStream(pipeIn[1]);
523 wxInputStream *inStream = new wxProcessFileInputStream(pipeOut[0]);
524 close(pipeIn[0]); // close reading side
525 close(pipeOut[1]); // close writing side
526
527 process->SetPipeStreams(inStream, outStream);
528 }
529
530 #if wxUSE_GUI
531 wxEndProcessData *data = new wxEndProcessData;
532
533 if ( sync )
534 {
535 // we may have process for capturing the program output, but it's
536 // not used in wxEndProcessData in the case of sync execution
537 data->process = NULL;
538
539 // sync execution: indicate it by negating the pid
540 data->pid = -pid;
541 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
542
543 close(end_proc_detect[1]); // close writing side
544
545 wxBusyCursor bc;
546 wxWindowDisabler wd;
547
548 // it will be set to 0 from GTK_EndProcessDetector
549 while (data->pid != 0)
550 wxYield();
551
552 int exitcode = data->exitcode;
553
554 delete data;
555
556 return exitcode;
557 }
558 else // async execution
559 {
560 // async execution, nothing special to do - caller will be
561 // notified about the process termination if process != NULL, data
562 // will be deleted in GTK_EndProcessDetector
563 data->process = process;
564 data->pid = pid;
565 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
566
567 close(end_proc_detect[1]); // close writing side
568
569 return pid;
570 }
571 #else // !wxUSE_GUI
572 wxASSERT_MSG( sync, wxT("async execution not supported yet") );
573
574 int exitcode = 0;
575 if ( waitpid(pid, &exitcode, 0) == -1 || !WIFEXITED(exitcode) )
576 {
577 wxLogSysError(_("Waiting for subprocess termination failed"));
578 }
579
580 return exitcode;
581 #endif // wxUSE_GUI
582 }
583
584 return 0;
585
586 #undef ARGS_CLEANUP
587 }
588
589 // ----------------------------------------------------------------------------
590 // file and directory functions
591 // ----------------------------------------------------------------------------
592
593 const wxChar* wxGetHomeDir( wxString *home )
594 {
595 *home = wxGetUserHome( wxString() );
596 if ( home->IsEmpty() )
597 *home = wxT("/");
598
599 return home->c_str();
600 }
601
602 #if wxUSE_UNICODE
603 const wxMB2WXbuf wxGetUserHome( const wxString &user )
604 #else // just for binary compatibility -- there is no 'const' here
605 char *wxGetUserHome( const wxString &user )
606 #endif
607 {
608 struct passwd *who = (struct passwd *) NULL;
609
610 if ( !user )
611 {
612 wxChar *ptr;
613
614 if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
615 {
616 return ptr;
617 }
618 if ((ptr = wxGetenv(wxT("USER"))) != NULL || (ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
619 {
620 who = getpwnam(wxConvertWX2MB(ptr));
621 }
622
623 // We now make sure the the user exists!
624 if (who == NULL)
625 {
626 who = getpwuid(getuid());
627 }
628 }
629 else
630 {
631 who = getpwnam (user.mb_str());
632 }
633
634 return wxConvertMB2WX(who ? who->pw_dir : 0);
635 }
636
637 // ----------------------------------------------------------------------------
638 // network and user id routines
639 // ----------------------------------------------------------------------------
640
641 // retrieve either the hostname or FQDN depending on platform (caller must
642 // check whether it's one or the other, this is why this function is for
643 // private use only)
644 static bool wxGetHostNameInternal(wxChar *buf, int sz)
645 {
646 wxCHECK_MSG( buf, FALSE, wxT("NULL pointer in wxGetHostNameInternal") );
647
648 *buf = wxT('\0');
649
650 // we're using uname() which is POSIX instead of less standard sysinfo()
651 #if defined(HAVE_UNAME)
652 struct utsname uts;
653 bool ok = uname(&uts) != -1;
654 if ( ok )
655 {
656 wxStrncpy(buf, wxConvertMB2WX(uts.nodename), sz - 1);
657 buf[sz] = wxT('\0');
658 }
659 #elif defined(HAVE_GETHOSTNAME)
660 bool ok = gethostname(buf, sz) != -1;
661 #else // no uname, no gethostname
662 wxFAIL_MSG(wxT("don't know host name for this machine"));
663
664 bool ok = FALSE;
665 #endif // uname/gethostname
666
667 if ( !ok )
668 {
669 wxLogSysError(_("Cannot get the hostname"));
670 }
671
672 return ok;
673 }
674
675 bool wxGetHostName(wxChar *buf, int sz)
676 {
677 bool ok = wxGetHostNameInternal(buf, sz);
678
679 if ( ok )
680 {
681 // BSD systems return the FQDN, we only want the hostname, so extract
682 // it (we consider that dots are domain separators)
683 wxChar *dot = wxStrchr(buf, wxT('.'));
684 if ( dot )
685 {
686 // nuke it
687 *dot = wxT('\0');
688 }
689 }
690
691 return ok;
692 }
693
694 bool wxGetFullHostName(wxChar *buf, int sz)
695 {
696 bool ok = wxGetHostNameInternal(buf, sz);
697
698 if ( ok )
699 {
700 if ( !wxStrchr(buf, wxT('.')) )
701 {
702 struct hostent *host = gethostbyname(wxConvertWX2MB(buf));
703 if ( !host )
704 {
705 wxLogSysError(_("Cannot get the official hostname"));
706
707 ok = FALSE;
708 }
709 else
710 {
711 // the canonical name
712 wxStrncpy(buf, wxConvertMB2WX(host->h_name), sz);
713 }
714 }
715 //else: it's already a FQDN (BSD behaves this way)
716 }
717
718 return ok;
719 }
720
721 bool wxGetUserId(wxChar *buf, int sz)
722 {
723 struct passwd *who;
724
725 *buf = wxT('\0');
726 if ((who = getpwuid(getuid ())) != NULL)
727 {
728 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
729 return TRUE;
730 }
731
732 return FALSE;
733 }
734
735 bool wxGetUserName(wxChar *buf, int sz)
736 {
737 struct passwd *who;
738
739 *buf = wxT('\0');
740 if ((who = getpwuid (getuid ())) != NULL)
741 {
742 // pw_gecos field in struct passwd is not standard
743 #if HAVE_PW_GECOS
744 char *comma = strchr(who->pw_gecos, ',');
745 if (comma)
746 *comma = '\0'; // cut off non-name comment fields
747 wxStrncpy (buf, wxConvertMB2WX(who->pw_gecos), sz - 1);
748 #else // !HAVE_PW_GECOS
749 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
750 #endif // HAVE_PW_GECOS/!HAVE_PW_GECOS
751 return TRUE;
752 }
753
754 return FALSE;
755 }
756
757 wxString wxGetOsDescription()
758 {
759 #ifndef WXWIN_OS_DESCRIPTION
760 #error WXWIN_OS_DESCRIPTION should be defined in config.h by configure
761 #else
762 return WXWIN_OS_DESCRIPTION;
763 #endif
764 }
765
766 // ----------------------------------------------------------------------------
767 // error and debug output routines (deprecated, use wxLog)
768 // ----------------------------------------------------------------------------
769
770 void wxDebugMsg( const char *format, ... )
771 {
772 va_list ap;
773 va_start( ap, format );
774 vfprintf( stderr, format, ap );
775 fflush( stderr );
776 va_end(ap);
777 }
778
779 void wxError( const wxString &msg, const wxString &title )
780 {
781 wxFprintf( stderr, _("Error ") );
782 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
783 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
784 wxFprintf( stderr, wxT(".\n") );
785 }
786
787 void wxFatalError( const wxString &msg, const wxString &title )
788 {
789 wxFprintf( stderr, _("Error ") );
790 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
791 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
792 wxFprintf( stderr, wxT(".\n") );
793 exit(3); // the same exit code as for abort()
794 }
795