#include <config.h>
+#include "config.h"
+
#include "glib.h"
+#include "gprintfint.h"
#include <string.h>
#include <stdlib.h>
ARG_COUNT = ARG_PROGRAM
};
+static gint
+protect_argv (gchar **argv,
+ gchar ***new_argv)
+{
+ gint i;
+ gint argc = 0;
+
+ while (argv[argc])
+ ++argc;
+ *new_argv = g_new (gchar *, argc+1);
+
+ /* Quote each argv element if necessary, so that it will get
+ * reconstructed correctly in the C runtime startup code. Note that
+ * the unquoting algorithm in the C runtime is really weird, and
+ * rather different than what Unix shells do. See stdargv.c in the C
+ * runtime sources (in the Platform SDK, in src/crt).
+ *
+ * Note that an new_argv[0] constructed by this function should
+ * *not* be passed as the filename argument to a spawn* or exec*
+ * family function. That argument should be the real file name
+ * without any quoting.
+ */
+ for (i = 0; i < argc; i++)
+ {
+ gchar *p = argv[i];
+ gchar *q;
+ gint len = 0;
+ gboolean need_dblquotes = FALSE;
+ while (*p)
+ {
+ if (*p == ' ' || *p == '\t')
+ need_dblquotes = TRUE;
+ else if (*p == '"')
+ len++;
+ else if (*p == '\\')
+ {
+ gchar *pp = p;
+ while (*pp && *pp == '\\')
+ pp++;
+ if (*pp == '"')
+ len++;
+ }
+ len++;
+ p++;
+ }
+
+ q = (*new_argv)[i] = g_malloc (len + need_dblquotes*2 + 1);
+ p = argv[i];
+
+ if (need_dblquotes)
+ *q++ = '"';
+
+ while (*p)
+ {
+ if (*p == '"')
+ *q++ = '\\';
+ else if (*p == '\\')
+ {
+ gchar *pp = p;
+ while (*pp && *pp == '\\')
+ pp++;
+ if (*pp == '"')
+ *q++ = '\\';
+ }
+ *q++ = *p;
+ p++;
+ }
+
+ if (need_dblquotes)
+ *q++ = '"';
+ *q++ = '\0';
+ /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
+ }
+ (*new_argv)[argc] = NULL;
+
+ return argc;
+}
+
#ifndef GSPAWN_HELPER
static gboolean make_pipe (gint p[2],
GError **error);
-static gboolean fork_exec_with_pipes (gboolean dont_wait,
+static gboolean do_spawn_with_pipes (gboolean dont_wait,
+ gboolean dont_return_handle,
const gchar *working_directory,
gchar **argv,
gchar **envp,
gboolean child_inherits_stdin,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
+ gint *child_pid,
gint *standard_input,
gint *standard_output,
gint *standard_error,
{
gint ret;
- ret = close (*fd);
- *fd = -1;
+ if (*fd < 0)
+ return -1;
+ else
+ {
+ ret = close (*fd);
+ *fd = -1;
+ }
return ret;
}
GError **error)
{
GIOStatus giostatus;
- gint bytes;
+ gssize bytes;
gchar buf[4096];
again:
{
gint outpipe = -1;
gint errpipe = -1;
+ gint pid;
GIOChannel *outchannel = NULL;
GIOChannel *errchannel = NULL;
GPollFD outfd, errfd;
if (standard_error)
*standard_error = NULL;
- if (!fork_exec_with_pipes (FALSE,
- working_directory,
- argv,
- envp,
- !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
- (flags & G_SPAWN_SEARCH_PATH) != 0,
- (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
- child_setup,
- user_data,
- NULL,
- standard_output ? &outpipe : NULL,
- standard_error ? &errpipe : NULL,
- &status,
- error))
+ if (!do_spawn_with_pipes (FALSE,
+ TRUE,
+ working_directory,
+ argv,
+ envp,
+ !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+ (flags & G_SPAWN_SEARCH_PATH) != 0,
+ (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+ (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+ (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+ child_setup,
+ user_data,
+ &pid,
+ NULL,
+ standard_output ? &outpipe : NULL,
+ standard_error ? &errpipe : NULL,
+ &status,
+ error))
return FALSE;
/* Read data from child. */
g_return_val_if_fail (standard_input == NULL ||
!(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
- return fork_exec_with_pipes (!(flags & G_SPAWN_DO_NOT_REAP_CHILD),
- working_directory,
- argv,
- envp,
- !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
- (flags & G_SPAWN_SEARCH_PATH) != 0,
- (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
- (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
- child_setup,
- user_data,
- standard_input,
- standard_output,
- standard_error,
- NULL,
- error);
+ return do_spawn_with_pipes (TRUE,
+ !(flags & G_SPAWN_DO_NOT_REAP_CHILD),
+ working_directory,
+ argv,
+ envp,
+ !(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+ (flags & G_SPAWN_SEARCH_PATH) != 0,
+ (flags & G_SPAWN_STDOUT_TO_DEV_NULL) != 0,
+ (flags & G_SPAWN_STDERR_TO_DEV_NULL) != 0,
+ (flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
+ child_setup,
+ user_data,
+ child_pid,
+ standard_input,
+ standard_output,
+ standard_error,
+ NULL,
+ error);
}
gboolean
}
static gint
-do_exec (gboolean dont_wait,
- gint child_err_report_fd,
- gint stdin_fd,
- gint stdout_fd,
- gint stderr_fd,
- const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- gboolean close_descriptors,
- gboolean search_path,
- gboolean stdout_to_null,
- gboolean stderr_to_null,
- gboolean child_inherits_stdin,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data)
+do_spawn (gboolean dont_wait,
+ gint child_err_report_fd,
+ gint stdin_fd,
+ gint stdout_fd,
+ gint stderr_fd,
+ const gchar *working_directory,
+ gchar **argv,
+ gchar **envp,
+ gboolean close_descriptors,
+ gboolean search_path,
+ gboolean stdout_to_null,
+ gboolean stderr_to_null,
+ gboolean child_inherits_stdin,
+ GSpawnChildSetupFunc child_setup,
+ gpointer user_data)
{
gchar **new_argv;
gchar args[ARG_COUNT][10];
gint i;
+ int rc;
int argc = 0;
SETUP_DEBUG();
new_argv = g_new (gchar *, argc + 1 + ARG_COUNT);
new_argv[0] = "gspawn-win32-helper";
- sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd);
+ _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_fd);
new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
if (stdin_fd >= 0)
{
- sprintf (args[ARG_STDIN], "%d", stdin_fd);
+ _g_sprintf (args[ARG_STDIN], "%d", stdin_fd);
new_argv[ARG_STDIN] = args[ARG_STDIN];
}
else if (child_inherits_stdin)
if (stdout_fd >= 0)
{
- sprintf (args[ARG_STDOUT], "%d", stdout_fd);
+ _g_sprintf (args[ARG_STDOUT], "%d", stdout_fd);
new_argv[ARG_STDOUT] = args[ARG_STDOUT];
}
else if (stdout_to_null)
if (stderr_fd >= 0)
{
- sprintf (args[ARG_STDERR], "%d", stderr_fd);
+ _g_sprintf (args[ARG_STDERR], "%d", stderr_fd);
new_argv[ARG_STDERR] = args[ARG_STDERR];
}
else if (stderr_to_null)
}
if (working_directory && *working_directory)
- new_argv[ARG_WORKING_DIRECTORY] = working_directory;
+ /* The g_strdup() to lose the constness */
+ new_argv[ARG_WORKING_DIRECTORY] = g_strdup (working_directory);
else
- new_argv[ARG_WORKING_DIRECTORY] = "-";
+ new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
if (close_descriptors)
new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
/* Let's hope envp hasn't mucked with PATH so that
* gspawn-win32-helper.exe isn't found.
*/
- spawnvpe (P_NOWAIT, "gspawn-win32-helper", new_argv, envp);
+ rc = spawnvpe (P_NOWAIT, "gspawn-win32-helper", new_argv, envp);
else
- spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
-
- /* FIXME: What if gspawn-win32-helper.exe isn't found? */
+ rc = spawnvp (P_NOWAIT, "gspawn-win32-helper", new_argv);
/* Close the child_err_report_fd and the other process's ends of the
* pipes in this process, otherwise the reader will never get
if (stderr_fd >= 0)
close (stderr_fd);
+ g_free (new_argv[ARG_WORKING_DIRECTORY]);
g_free (new_argv);
- return 0;
+ return rc;
}
static gboolean
}
static gboolean
-fork_exec_with_pipes (gboolean dont_wait,
- const gchar *working_directory,
- gchar **argv,
- gchar **envp,
- gboolean close_descriptors,
- gboolean search_path,
- gboolean stdout_to_null,
- gboolean stderr_to_null,
- gboolean child_inherits_stdin,
- GSpawnChildSetupFunc child_setup,
- gpointer user_data,
- gint *standard_input,
- gint *standard_output,
- gint *standard_error,
- gint *exit_status,
- GError **error)
+do_spawn_with_pipes (gboolean dont_wait,
+ gboolean dont_return_handle,
+ const gchar *working_directory,
+ gchar **argv,
+ gchar **envp,
+ gboolean close_descriptors,
+ gboolean search_path,
+ gboolean stdout_to_null,
+ gboolean stderr_to_null,
+ gboolean child_inherits_stdin,
+ GSpawnChildSetupFunc child_setup,
+ gpointer user_data,
+ gint *child_pid,
+ gint *standard_input,
+ gint *standard_output,
+ gint *standard_error,
+ gint *exit_status,
+ GError **error)
{
gint stdin_pipe[2] = { -1, -1 };
gint stdout_pipe[2] = { -1, -1 };
gint stderr_pipe[2] = { -1, -1 };
gint child_err_report_pipe[2] = { -1, -1 };
- gint status;
+ gint helper = -1;
gint buf[2];
gint n_ints = 0;
+ gint i;
+ gint argc;
+ gchar **new_argv;
if (!make_pipe (child_err_report_pipe, error))
return FALSE;
if (standard_error && !make_pipe (stderr_pipe, error))
goto cleanup_and_fail;
- status = do_exec (dont_wait,
- child_err_report_pipe[1],
- stdin_pipe[0],
- stdout_pipe[1],
- stderr_pipe[1],
- working_directory,
- argv,
- envp,
- close_descriptors,
- search_path,
- stdout_to_null,
- stderr_to_null,
- child_inherits_stdin,
- child_setup,
- user_data);
+ argc = protect_argv (argv, &new_argv);
+
+ helper = do_spawn (dont_wait,
+ child_err_report_pipe[1],
+ stdin_pipe[0],
+ stdout_pipe[1],
+ stderr_pipe[1],
+ working_directory,
+ new_argv,
+ envp,
+ close_descriptors,
+ search_path,
+ stdout_to_null,
+ stderr_to_null,
+ child_inherits_stdin,
+ child_setup,
+ user_data);
+ for (i = 0; i < argc; i++)
+ g_free (new_argv[i]);
+ g_free (new_argv);
+
+ /* do_spawn() returns -1 if gspawn-win32-helper couldn't be run */
+ if (helper == -1)
+ {
+ g_set_error (error,
+ G_SPAWN_ERROR,
+ G_SPAWN_ERROR_FAILED,
+ _("Failed to execute helper program"));
+ goto cleanup_and_fail;
+ }
+
if (!read_ints (child_err_report_pipe[0],
buf, 2, &n_ints,
- error))
+ error) ||
+ n_ints != 2)
goto cleanup_and_fail;
- if (n_ints == 2)
+ /* Error code from gspawn-win32-helper. */
+ switch (buf[0])
{
- /* Error from the child. */
-
- switch (buf[0])
+ case CHILD_NO_ERROR:
+ if (child_pid && dont_wait && !dont_return_handle)
{
- case CHILD_NO_ERROR:
- break;
-
- case CHILD_CHDIR_FAILED:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_CHDIR,
- _("Failed to change to directory '%s' (%s)"),
- working_directory,
- g_strerror (buf[1]));
- goto cleanup_and_fail;
-
- case CHILD_SPAWN_FAILED:
- g_set_error (error,
- G_SPAWN_ERROR,
- G_SPAWN_ERROR_FAILED,
- _("Failed to execute child process (%s)"),
- g_strerror (buf[1]));
- goto cleanup_and_fail;
+ /* helper is our HANDLE for gspawn-win32-helper. It has
+ * told us the HANDLE of its child. Duplicate that into
+ * a HANDLE valid in this process.
+ */
+ if (!DuplicateHandle ((HANDLE) helper, (HANDLE) buf[1],
+ GetCurrentProcess (), (LPHANDLE) child_pid,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ *child_pid = 0;
}
+ else if (child_pid)
+ *child_pid = 0;
+ break;
+
+ case CHILD_CHDIR_FAILED:
+ g_set_error (error,
+ G_SPAWN_ERROR,
+ G_SPAWN_ERROR_CHDIR,
+ _("Failed to change to directory '%s' (%s)"),
+ working_directory,
+ g_strerror (buf[1]));
+ goto cleanup_and_fail;
+
+ case CHILD_SPAWN_FAILED:
+ g_set_error (error,
+ G_SPAWN_ERROR,
+ G_SPAWN_ERROR_FAILED,
+ _("Failed to execute child process (%s)"),
+ g_strerror (buf[1]));
+ goto cleanup_and_fail;
}
/* Success against all odds! return the information */
if (standard_error)
*standard_error = stderr_pipe[0];
if (exit_status)
- *exit_status = status;
+ *exit_status = buf[1];
+ CloseHandle ((HANDLE) helper);
return TRUE;
cleanup_and_fail:
+ if (helper != -1)
+ CloseHandle ((HANDLE) helper);
close_and_invalidate (&child_err_report_pipe[0]);
close_and_invalidate (&child_err_report_pipe[1]);
close_and_invalidate (&stdin_pipe[0]);