FreeBASIC  0.91.0
sys_execex.c
Go to the documentation of this file.
1 #include "../fb.h"
2 #include "fb_private_console.h"
3 #include <sys/wait.h>
4 
5 FBCALL int fb_ExecEx( FBSTRING *program, FBSTRING *args, int do_fork )
6 {
7  char buffer[MAX_PATH+1], *application, *arguments, **argv, *p;
8  int i, argc = 0, res = -1, status;
9  ssize_t len_program, len_arguments;
10  pid_t pid;
11 
12  if( (program == NULL) || (program->data == NULL) )
13  {
14  fb_hStrDelTemp( args );
15  fb_hStrDelTemp( program );
16  return -1;
17  }
18 
19  application = fb_hGetShortPath( program->data, buffer, MAX_PATH );
20  DBG_ASSERT( application!=NULL );
21  if( application==program->data )
22  {
23  len_program = FB_STRSIZE( program );
24  application = buffer;
25  FB_MEMCPY(application, program->data, len_program );
26  application[len_program] = 0;
27  }
28 
29  fb_hConvertPath( application );
30 
31  if( args==NULL ) {
32  arguments = "";
33  } else {
34  len_arguments = FB_STRSIZE( args );
35  arguments = alloca( len_arguments + 1 );
36  DBG_ASSERT( arguments!=NULL );
37  arguments[len_arguments] = 0;
38  if( len_arguments )
39  argc = fb_hParseArgs( arguments, args->data, len_arguments );
40  }
41 
42  FB_STRLOCK();
43 
44  fb_hStrDelTemp_NoLock( args );
45  fb_hStrDelTemp_NoLock( program );
46 
47  FB_STRUNLOCK();
48 
49  if( argc == -1 )
50  return -1;
51 
52  argc++; /* add 1 for program name */
53 
54  argv = alloca( sizeof(char*) * (argc + 1 ));
55  DBG_ASSERT( argv!=NULL );
56 
57  argv[0] = application;
58 
59  /* scan the processed args and set pointers */
60  p = arguments;
61  for( i=1 ; i<argc; i++) {
62  argv[i] = p; /* set pointer to current argument */
63  while( *p++ ); /* skip to 1 char past next null char */
64  }
65  argv[argc] = NULL;
66 
67 
68  /* Launch */
69  FB_LOCK( );
71  FB_UNLOCK( );
72 
73  if( do_fork ) {
74  pid = fork();
75  if( pid != -1 ) {
76  if (pid == 0) {
77  /* execvp() only returns if it failed */
78  execvp( application, argv );
79  /* HACK: execvp() failed, this must be communiated to the parent process *somehow*,
80  so fb_ExecEx() can return -1 there */
81  /* Using _Exit() instead of exit() to prevent the child from flusing file I/O and
82  running global destructors (especially the rtlib's own cleanup), which may try
83  to wait on threads to finish (e.g. hinit.c::bg_thread()), but fork() doesn't
84  duplicate other threads besides the current one, so their pthread handles will be
85  invalid here in the child process. */
86  _Exit( 255 );
87  /* FIXME: won't be able to tell the difference if the exec'ed program returned 255.
88  Maybe a pipe could be used instead of the 255 exit code? Unless that's too slow/has side-effects */
89  } else if( (waitpid(pid, &status, 0) > 0) && WIFEXITED(status) ) {
90  res = WEXITSTATUS(status);
91  if( res == 255 ) {
92  /* See the HACK above */
93  res = -1;
94  }
95  }
96  }
97  } else {
98  res = execvp( application, argv );
99  }
100 
101  FB_LOCK( );
102  fb_hInitConsole();
103  FB_UNLOCK( );
104 
105  return res;
106 }