]> git.saurik.com Git - wxWidgets.git/blob - src/unix/utilsunx.cpp
5ce6dc6c1160045638b3f118cd029185c3b20a4e
[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
27 #include "wx/unix/execute.h"
28
29 #include <stdarg.h>
30 #include <dirent.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <sys/wait.h>
36 #include <pwd.h>
37 #include <errno.h>
38 #include <netdb.h>
39 #include <signal.h>
40 #include <fcntl.h> // for O_WRONLY and friends
41 #include <time.h> // nanosleep() and/or usleep()
42 #include <ctype.h> // isspace()
43
44 // JACS: needed for FD_SETSIZE
45 #include <sys/time.h>
46
47 #if HAVE_UNAME
48 #include <sys/utsname.h> // for uname()
49 #endif // HAVE_UNAME
50
51 // ----------------------------------------------------------------------------
52 // conditional compilation
53 // ----------------------------------------------------------------------------
54
55 // many versions of Unices have this function, but it is not defined in system
56 // headers - please add your system here if it is the case for your OS.
57 // SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this.
58 #if !defined(HAVE_USLEEP) && \
59 (defined(__SUN__) && !defined(__SunOs_5_6) && \
60 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
61 defined(__osf__)
62 extern "C"
63 {
64 #ifdef __SUN__
65 int usleep(unsigned int usec);
66 #else // !Sun
67 void usleep(unsigned long usec);
68 #endif // Sun/!Sun
69 };
70 #endif // Unices without usleep()
71
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // sleeping
78 // ----------------------------------------------------------------------------
79
80 void wxSleep(int nSecs)
81 {
82 sleep(nSecs);
83 }
84
85 void wxUsleep(unsigned long milliseconds)
86 {
87 #if HAVE_NANOSLEEP
88 timespec tmReq;
89 tmReq.tv_sec = milliseconds / 1000;
90 tmReq.tv_nsec = (milliseconds % 1000) * 1000 * 1000;
91
92 // we're not interested in remaining time nor in return value
93 (void)nanosleep(&tmReq, (timespec *)NULL);
94 #elif HAVE_USLEEP
95 // uncomment this if you feel brave or if you are sure that your version
96 // of Solaris has a safe usleep() function but please notice that usleep()
97 // is known to lead to crashes in MT programs in Solaris 2.[67] and is not
98 // documented as MT-Safe
99 #if defined(__SUN__) && wxUSE_THREADS
100 #error "usleep() cannot be used in MT programs under Solaris."
101 #endif // Sun
102
103 usleep(milliseconds * 1000); // usleep(3) wants microseconds
104 #else // !sleep function
105 #error "usleep() or nanosleep() function required for wxUsleep"
106 #endif // sleep function
107 }
108
109 // ----------------------------------------------------------------------------
110 // process management
111 // ----------------------------------------------------------------------------
112
113 int wxKill(long pid, wxSignal sig)
114 {
115 return kill(pid, (int)sig);
116 }
117
118 #define WXEXECUTE_NARGS 127
119
120 long wxExecute( const wxString& command, bool sync, wxProcess *process )
121 {
122 wxCHECK_MSG( !command.IsEmpty(), 0, _T("can't exec empty command") );
123
124 int argc = 0;
125 wxChar *argv[WXEXECUTE_NARGS];
126 wxString argument;
127 const wxChar *cptr = command.c_str();
128 wxChar quotechar = _T('\0'); // is arg quoted?
129 bool escaped = FALSE;
130
131 // split the command line in arguments
132 do
133 {
134 argument=_T("");
135 quotechar = _T('\0');
136
137 // eat leading whitespace:
138 while ( wxIsspace(*cptr) )
139 cptr++;
140
141 if ( *cptr == _T('\'') || *cptr == _T('"') )
142 quotechar = *cptr++;
143
144 do
145 {
146 if ( *cptr == _T('\\') && ! escaped )
147 {
148 escaped = TRUE;
149 cptr++;
150 continue;
151 }
152
153 // all other characters:
154 argument += *cptr++;
155 escaped = FALSE;
156
157 // have we reached the end of the argument?
158 if ( (*cptr == quotechar && ! escaped)
159 || (quotechar == _T('\0') && wxIsspace(*cptr))
160 || *cptr == _T('\0') )
161 {
162 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
163 _T("too many arguments in wxExecute") );
164
165 argv[argc] = new wxChar[argument.length() + 1];
166 wxStrcpy(argv[argc], argument.c_str());
167 argc++;
168
169 // if not at end of buffer, swallow last character:
170 if(*cptr)
171 cptr++;
172
173 break; // done with this one, start over
174 }
175 } while(*cptr);
176 } while(*cptr);
177 argv[argc] = NULL;
178
179 // do execute the command
180 long lRc = wxExecute(argv, sync, process);
181
182 // clean up
183 argc = 0;
184 while( argv[argc] )
185 delete [] argv[argc++];
186
187 return lRc;
188 }
189
190 bool wxShell(const wxString& command)
191 {
192 wxString cmd;
193 if ( !!command )
194 cmd.Printf(_T("xterm -e %s"), command.c_str());
195 else
196 cmd = command;
197
198 return wxExecute(cmd) != 0;
199 }
200
201 void wxHandleProcessTermination(wxEndProcessData *proc_data)
202 {
203 int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid);
204
205 // waitpid is POSIX so should be available everywhere, however on older
206 // systems wait() might be used instead in a loop (until the right pid
207 // terminates)
208 int status = 0;
209 if ( waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) )
210 {
211 wxLogSysError(_("Waiting for subprocess termination failed"));
212 }
213 else
214 {
215 // notify user about termination if required
216 if (proc_data->process)
217 {
218 proc_data->process->OnTerminate(proc_data->pid,
219 WEXITSTATUS(status));
220 }
221 }
222
223 // clean up
224 if ( proc_data->pid > 0 )
225 {
226 delete proc_data;
227 }
228 else
229 {
230 // wxExecute() will know about it
231 proc_data->exitcode = status;
232
233 proc_data->pid = 0;
234 }
235 }
236
237 long wxExecute( wxChar **argv, bool sync, wxProcess *process )
238 {
239 wxCHECK_MSG( *argv, 0, _T("can't exec empty command") );
240
241 int end_proc_detect[2];
242 #if wxUSE_UNICODE
243 int mb_argc = 0;
244 char *mb_argv[WXEXECUTE_NARGS];
245
246 while (argv[mb_argc]) {
247 wxWX2MBbuf mb_arg = wxConv_libc.cWX2MB(argv[mb_argc]);
248 mb_argv[mb_argc] = strdup(mb_arg);
249 mb_argc++;
250 }
251 mb_argv[mb_argc] = (char *) NULL;
252 #else
253 wxChar **mb_argv = argv;
254 #endif
255
256 // create pipes
257 if (pipe(end_proc_detect) == -1)
258 {
259 wxLogSysError( _("Pipe creation failed") );
260 return 0;
261 }
262
263 // fork the process
264 #ifdef HAVE_VFORK
265 pid_t pid = vfork();
266 #else
267 pid_t pid = fork();
268 #endif
269 if (pid == -1)
270 {
271 wxLogSysError( _("Fork failed") );
272 return 0;
273 }
274 else if (pid == 0)
275 {
276 // we're in child
277 close(end_proc_detect[0]); // close reading side
278
279 // These three lines close the open file descriptors to to avoid any
280 // input/output which might block the process or irritate the user. If
281 // one wants proper IO for the subprocess, the right thing to do is
282 // to start an xterm executing it.
283 if (sync == 0)
284 {
285 // leave stderr opened, it won't do any hurm
286 for ( int fd = 0; fd < FD_SETSIZE; fd++ )
287 {
288 if ( fd != end_proc_detect[1] && fd != STDERR_FILENO )
289 close(fd);
290 }
291 }
292
293 #if 0
294 close(STDERR_FILENO);
295
296 // some programs complain about stderr not being open, so redirect
297 // them:
298 open("/dev/null", O_RDONLY); // stdin
299 open("/dev/null", O_WRONLY); // stdout
300 open("/dev/null", O_WRONLY); // stderr
301 #endif
302
303 execvp (*mb_argv, mb_argv);
304
305 // there is no return after successful exec()
306 wxFprintf(stderr, _("Can't execute '%s'\n"), *argv);
307
308 _exit(-1);
309 }
310 else
311 {
312 // we're in parent
313 close(end_proc_detect[1]); // close writing side
314
315 wxEndProcessData *data = new wxEndProcessData;
316 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
317
318 #if wxUSE_UNICODE
319 mb_argc = 0;
320 while (mb_argv[mb_argc])
321 free(mb_argv[mb_argc++]);
322 #endif
323
324 if ( sync )
325 {
326 wxASSERT_MSG( !process, _T("wxProcess param ignored for sync exec") );
327 data->process = NULL;
328
329 // sync execution: indicate it by negating the pid
330 data->pid = -pid;
331
332 // it will be set to 0 from GTK_EndProcessDetector
333 while (data->pid != 0)
334 wxYield();
335
336 int exitcode = data->exitcode;
337
338 delete data;
339
340 return exitcode;
341 }
342 else
343 {
344 // async execution, nothing special to do - caller will be
345 // notified about the process terminationif process != NULL, data
346 // will be deleted in GTK_EndProcessDetector
347 data->process = process;
348 data->pid = pid;
349
350 return pid;
351 }
352 }
353 }
354
355 // ----------------------------------------------------------------------------
356 // file and directory functions
357 // ----------------------------------------------------------------------------
358
359 const wxChar* wxGetHomeDir( wxString *home )
360 {
361 *home = wxGetUserHome( wxString() );
362 if ( home->IsEmpty() )
363 *home = _T("/");
364
365 return home->c_str();
366 }
367
368 #if wxUSE_UNICODE
369 const wxMB2WXbuf wxGetUserHome( const wxString &user )
370 #else // just for binary compatibility
371 char *wxGetUserHome( const wxString &user )
372 #endif
373 {
374 struct passwd *who = (struct passwd *) NULL;
375
376 if ( !user )
377 {
378 register wxChar *ptr;
379
380 if ((ptr = wxGetenv(_T("HOME"))) != NULL)
381 {
382 return ptr;
383 }
384 if ((ptr = wxGetenv(_T("USER"))) != NULL || (ptr = wxGetenv(_T("LOGNAME"))) != NULL)
385 {
386 who = getpwnam(wxConv_libc.cWX2MB(ptr));
387 }
388
389 // We now make sure the the user exists!
390 if (who == NULL)
391 {
392 who = getpwuid(getuid());
393 }
394 }
395 else
396 {
397 who = getpwnam (user.mb_str());
398 }
399
400 #if wxUSE_UNICODE
401 return who ? wxConv_libc.cMB2WX(who->pw_dir) : (wxMB2WXbuf)((wxChar*)NULL);
402 #else
403 return who ? who->pw_dir : ((char*)NULL);
404 #endif
405 }
406
407 // ----------------------------------------------------------------------------
408 // network and user id routines
409 // ----------------------------------------------------------------------------
410
411 // retrieve either the hostname or FQDN depending on platform (caller must
412 // check whether it's one or the other, this is why this function is for
413 // private use only)
414 static bool wxGetHostNameInternal(wxChar *buf, int sz)
415 {
416 wxCHECK_MSG( buf, FALSE, _T("NULL pointer in wxGetHostNameInternal") );
417
418 *buf = _T('\0');
419
420 // we're using uname() which is POSIX instead of less standard sysinfo()
421 #if defined(HAVE_UNAME)
422 struct utsname uts;
423 bool ok = uname(&uts) != -1;
424 if ( ok )
425 {
426 wxStrncpy(buf, wxConv_libc.cMB2WX(uts.nodename), sz - 1);
427 buf[sz] = _T('\0');
428 }
429 #elif defined(HAVE_GETHOSTNAME)
430 bool ok = gethostname(buf, sz) != -1;
431 #else // no uname, no gethostname
432 wxFAIL_MSG(_T("don't know host name for this machine"));
433
434 bool ok = FALSE;
435 #endif // uname/gethostname
436
437 if ( !ok )
438 {
439 wxLogSysError(_("Cannot get the hostname"));
440 }
441
442 return ok;
443 }
444
445 bool wxGetHostName(wxChar *buf, int sz)
446 {
447 bool ok = wxGetHostNameInternal(buf, sz);
448
449 if ( ok )
450 {
451 // BSD systems return the FQDN, we only want the hostname, so extract
452 // it (we consider that dots are domain separators)
453 wxChar *dot = wxStrchr(buf, _T('.'));
454 if ( dot )
455 {
456 // nuke it
457 *dot = _T('\0');
458 }
459 }
460
461 return ok;
462 }
463
464 bool wxGetFullHostName(wxChar *buf, int sz)
465 {
466 bool ok = wxGetHostNameInternal(buf, sz);
467
468 if ( ok )
469 {
470 if ( !wxStrchr(buf, _T('.')) )
471 {
472 struct hostent *host = gethostbyname(wxConv_libc.cWX2MB(buf));
473 if ( !host )
474 {
475 wxLogSysError(_("Cannot get the official hostname"));
476
477 ok = FALSE;
478 }
479 else
480 {
481 // the canonical name
482 wxStrncpy(buf, wxConv_libc.cMB2WX(host->h_name), sz);
483 }
484 }
485 //else: it's already a FQDN (BSD behaves this way)
486 }
487
488 return ok;
489 }
490
491 bool wxGetUserId(wxChar *buf, int sz)
492 {
493 struct passwd *who;
494
495 *buf = _T('\0');
496 if ((who = getpwuid(getuid ())) != NULL)
497 {
498 wxStrncpy (buf, wxConv_libc.cMB2WX(who->pw_name), sz - 1);
499 return TRUE;
500 }
501
502 return FALSE;
503 }
504
505 bool wxGetUserName(wxChar *buf, int sz)
506 {
507 struct passwd *who;
508 char *comma;
509
510 *buf = _T('\0');
511 if ((who = getpwuid (getuid ())) != NULL) {
512 comma = strchr(who->pw_gecos, ',');
513 if (comma)
514 *comma = '\0'; // cut off non-name comment fields
515 wxStrncpy (buf, wxConv_libc.cMB2WX(who->pw_gecos), sz - 1);
516 return TRUE;
517 }
518
519 return FALSE;
520 }
521
522 // ----------------------------------------------------------------------------
523 // error and debug output routines (deprecated, use wxLog)
524 // ----------------------------------------------------------------------------
525
526 void wxDebugMsg( const char *format, ... )
527 {
528 va_list ap;
529 va_start( ap, format );
530 vfprintf( stderr, format, ap );
531 fflush( stderr );
532 va_end(ap);
533 }
534
535 void wxError( const wxString &msg, const wxString &title )
536 {
537 wxFprintf( stderr, _("Error ") );
538 if (!title.IsNull()) wxFprintf( stderr, _T("%s "), WXSTRINGCAST(title) );
539 if (!msg.IsNull()) wxFprintf( stderr, _T(": %s"), WXSTRINGCAST(msg) );
540 wxFprintf( stderr, _T(".\n") );
541 }
542
543 void wxFatalError( const wxString &msg, const wxString &title )
544 {
545 wxFprintf( stderr, _("Error ") );
546 if (!title.IsNull()) wxFprintf( stderr, _T("%s "), WXSTRINGCAST(title) );
547 if (!msg.IsNull()) wxFprintf( stderr, _T(": %s"), WXSTRINGCAST(msg) );
548 wxFprintf( stderr, _T(".\n") );
549 exit(3); // the same exit code as for abort()
550 }