+
+/*************************************************************************
+ * vlc_execve: Execute an external program with a given environment,
+ * wait until it finishes and return its standard output
+ *************************************************************************/
+int __vlc_execve( vlc_object_t *p_object, int i_argc, char *const *ppsz_argv,
+ char *const *ppsz_env, const char *psz_cwd,
+ const char *p_in, size_t i_in,
+ char **pp_data, size_t *pi_data )
+{
+ (void)i_argc; // <-- hmph
+#ifdef HAVE_FORK
+# define BUFSIZE 1024
+ int fds[2], i_status;
+
+ if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds))
+ return -1;
+
+ pid_t pid = -1;
+ if ((fds[0] > 2) && (fds[1] > 2))
+ pid = fork ();
+
+ switch (pid)
+ {
+ case -1:
+ msg_Err (p_object, "unable to fork (%m)");
+ close (fds[0]);
+ close (fds[1]);
+ return -1;
+
+ case 0:
+ {
+ sigset_t set;
+ sigemptyset (&set);
+ pthread_sigmask (SIG_SETMASK, &set, NULL);
+
+ /* NOTE:
+ * Like it or not, close can fail (and not only with EBADF)
+ */
+ if ((close (0) == 0) && (close (1) == 0) && (close (2) == 0)
+ && (dup (fds[1]) == 0) && (dup (fds[1]) == 1)
+ && (open ("/dev/null", O_RDONLY) == 2)
+ && ((psz_cwd == NULL) || (chdir (psz_cwd) == 0)))
+ execve (ppsz_argv[0], ppsz_argv, ppsz_env);
+
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ close (fds[1]);
+
+ *pi_data = 0;
+ if (*pp_data)
+ free (*pp_data);
+ *pp_data = NULL;
+
+ if (i_in == 0)
+ shutdown (fds[0], SHUT_WR);
+
+ while (!p_object->b_die)
+ {
+ struct pollfd ufd[1];
+ memset (ufd, 0, sizeof (ufd));
+ ufd[0].fd = fds[0];
+ ufd[0].events = POLLIN;
+
+ if (i_in > 0)
+ ufd[0].events |= POLLOUT;
+
+ if (poll (ufd, 1, 10) <= 0)
+ continue;
+
+ if (ufd[0].revents & ~POLLOUT)
+ {
+ char *ptr = realloc (*pp_data, *pi_data + BUFSIZE + 1);
+ if (ptr == NULL)
+ break; /* safely abort */
+
+ *pp_data = ptr;
+
+ ssize_t val = read (fds[0], ptr + *pi_data, BUFSIZE);
+ switch (val)
+ {
+ case -1:
+ case 0:
+ shutdown (fds[0], SHUT_RD);
+ break;
+
+ default:
+ *pi_data += val;
+ }
+ }
+
+ if (ufd[0].revents & POLLOUT)
+ {
+ ssize_t val = write (fds[0], p_in, i_in);
+ switch (val)
+ {
+ case -1:
+ case 0:
+ i_in = 0;
+ shutdown (fds[0], SHUT_WR);
+ break;
+
+ default:
+ i_in -= val;
+ p_in += val;
+ }
+ }
+ }
+
+ close (fds[0]);
+
+ while (waitpid (pid, &i_status, 0) == -1);
+
+ if (WIFEXITED (i_status))
+ {
+ i_status = WEXITSTATUS (i_status);
+ if (i_status)
+ msg_Warn (p_object, "child %s (PID %d) exited with error code %d",
+ ppsz_argv[0], (int)pid, i_status);
+ }
+ else
+ if (WIFSIGNALED (i_status)) // <-- this should be redumdant a check
+ {
+ i_status = WTERMSIG (i_status);
+ msg_Warn (p_object, "child %s (PID %d) exited on signal %d (%s)",
+ ppsz_argv[0], (int)pid, i_status, strsignal (i_status));
+ }
+
+#elif defined( WIN32 ) && !defined( UNDER_CE )
+ SECURITY_ATTRIBUTES saAttr;
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+ BOOL bFuncRetn = FALSE;
+ HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
+ DWORD i_status;
+ char *psz_cmd = NULL, *p_env = NULL, *p = NULL;
+ char **ppsz_parser;
+ int i_size;
+
+ /* Set the bInheritHandle flag so pipe handles are inherited. */
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ /* Create a pipe for the child process's STDOUT. */
+ if ( !CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) )
+ {
+ msg_Err( p_object, "stdout pipe creation failed" );
+ return -1;
+ }
+
+ /* Ensure the read handle to the pipe for STDOUT is not inherited. */
+ SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0 );
+
+ /* Create a pipe for the child process's STDIN. */
+ if ( !CreatePipe( &hChildStdinRd, &hChildStdinWr, &saAttr, 0 ) )
+ {
+ msg_Err( p_object, "stdin pipe creation failed" );
+ return -1;
+ }
+
+ /* Ensure the write handle to the pipe for STDIN is not inherited. */
+ SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, 0 );
+
+ /* Set up members of the PROCESS_INFORMATION structure. */
+ ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
+
+ /* Set up members of the STARTUPINFO structure. */
+ ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.hStdError = hChildStdoutWr;
+ siStartInfo.hStdOutput = hChildStdoutWr;
+ siStartInfo.hStdInput = hChildStdinRd;
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+
+ /* Set up the command line. */
+ psz_cmd = malloc(32768);
+ if( !psz_cmd )
+ return -1;
+ psz_cmd[0] = '\0';
+ i_size = 32768;
+ ppsz_parser = &ppsz_argv[0];
+ while ( ppsz_parser[0] != NULL && i_size > 0 )
+ {
+ /* Protect the last argument with quotes ; the other arguments
+ * are supposed to be already protected because they have been
+ * passed as a command-line option. */
+ if ( ppsz_parser[1] == NULL )
+ {
+ strncat( psz_cmd, "\"", i_size );
+ i_size--;
+ }
+ strncat( psz_cmd, *ppsz_parser, i_size );
+ i_size -= strlen( *ppsz_parser );
+ if ( ppsz_parser[1] == NULL )
+ {
+ strncat( psz_cmd, "\"", i_size );
+ i_size--;
+ }
+ strncat( psz_cmd, " ", i_size );
+ i_size--;
+ ppsz_parser++;
+ }
+
+ /* Set up the environment. */
+ p = p_env = malloc(32768);
+ if( !p )
+ {
+ free( psz_cmd );
+ return -1;
+ }
+
+ i_size = 32768;
+ ppsz_parser = &ppsz_env[0];
+ while ( *ppsz_parser != NULL && i_size > 0 )
+ {
+ memcpy( p, *ppsz_parser,
+ __MIN((int)(strlen(*ppsz_parser) + 1), i_size) );
+ p += strlen(*ppsz_parser) + 1;
+ i_size -= strlen(*ppsz_parser) + 1;
+ ppsz_parser++;
+ }
+ *p = '\0';
+
+ /* Create the child process. */
+ bFuncRetn = CreateProcess( NULL,
+ psz_cmd, // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ p_env,
+ psz_cwd,
+ &siStartInfo, // STARTUPINFO pointer
+ &piProcInfo ); // receives PROCESS_INFORMATION
+
+ free( psz_cmd );
+ free( p_env );
+
+ if ( bFuncRetn == 0 )
+ {
+ msg_Err( p_object, "child creation failed" );
+ return -1;
+ }
+
+ /* Read from a file and write its contents to a pipe. */
+ while ( i_in > 0 && !p_object->b_die )
+ {
+ DWORD i_written;
+ if ( !WriteFile( hChildStdinWr, p_in, i_in, &i_written, NULL ) )
+ break;
+ i_in -= i_written;
+ p_in += i_written;
+ }
+
+ /* Close the pipe handle so the child process stops reading. */
+ CloseHandle(hChildStdinWr);
+
+ /* Close the write end of the pipe before reading from the
+ * read end of the pipe. */
+ CloseHandle(hChildStdoutWr);
+
+ /* Read output from the child process. */
+ *pi_data = 0;
+ if( *pp_data )
+ free( pp_data );
+ *pp_data = NULL;
+ *pp_data = malloc( 1025 ); /* +1 for \0 */
+
+ while ( !p_object->b_die )
+ {
+ DWORD i_read;
+ if ( !ReadFile( hChildStdoutRd, &(*pp_data)[*pi_data], 1024, &i_read,
+ NULL )
+ || i_read == 0 )
+ break;
+ *pi_data += i_read;
+ *pp_data = realloc( *pp_data, *pi_data + 1025 );
+ }
+
+ while ( !p_object->b_die
+ && !GetExitCodeProcess( piProcInfo.hProcess, &i_status )
+ && i_status != STILL_ACTIVE )
+ msleep( 10000 );
+
+ CloseHandle(piProcInfo.hProcess);
+ CloseHandle(piProcInfo.hThread);
+
+ if ( i_status )
+ msg_Warn( p_object,
+ "child %s returned with error code %ld",
+ ppsz_argv[0], i_status );
+
+#else
+ msg_Err( p_object, "vlc_execve called but no implementation is available" );
+ return -1;
+
+#endif
+
+ if (*pp_data == NULL)
+ return -1;
+
+ (*pp_data)[*pi_data] = '\0';
+ return 0;
+}