#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 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 outpipe = -1;
gint errpipe = -1;
+ gint pid;
GIOChannel *outchannel = NULL;
GIOChannel *errchannel = NULL;
GPollFD outfd, errfd;
*standard_error = NULL;
if (!do_spawn_with_pipes (FALSE,
+ TRUE,
working_directory,
argv,
envp,
(flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
child_setup,
user_data,
+ &pid,
NULL,
standard_output ? &outpipe : NULL,
standard_error ? &errpipe : NULL,
!(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
return do_spawn_with_pipes (TRUE,
+ !(flags & G_SPAWN_DO_NOT_REAP_CHILD),
working_directory,
argv,
envp,
(flags & G_SPAWN_CHILD_INHERITS_STDIN) != 0,
child_setup,
user_data,
+ child_pid,
standard_input,
standard_output,
standard_error,
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
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 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_spawn (dont_wait,
+ 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,
- argv,
+ new_argv,
envp,
close_descriptors,
search_path,
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]);