X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fgspawn-win32.c;h=d98aba1269cfb779c9b04bf220c58a5b9e490aef;hb=20f6cc2a10ba26860e7a6d27c100deadb5497772;hp=297c9de129d567816412e035547f168de7fe019b;hpb=20d47d6f9b0930c1da15942aad847265aee1aaa4;p=platform%2Fupstream%2Fglib.git diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c index 297c9de..d98aba1 100644 --- a/glib/gspawn-win32.c +++ b/glib/gspawn-win32.c @@ -30,7 +30,7 @@ * before starting the child process. (There might be several threads * running, and the current directory is common for all threads.) * - * Thus, we must in most cases use a helper program to handle closing + * Thus, we must in many cases use a helper program to handle closing * of (inherited) file descriptors and changing of directory. The * helper process is also needed if the standard input, standard * output, or standard error of the process to be run are supposed to @@ -45,9 +45,10 @@ #include "config.h" #include "glib.h" +#include "glib-private.h" #include "gprintfint.h" #include "glibintl.h" -#include "galias.h" +#include "gthread.h" #include #include @@ -59,15 +60,9 @@ #include #include #include +#include -#ifdef __MINGW32__ -/* Mingw doesn't have prototypes for these */ -int _wspawnvpe (int, const wchar_t *, const wchar_t **, const wchar_t **); -int _wspawnvp (int, const wchar_t *, const wchar_t **); -int _wspawnve (int, const wchar_t *, const wchar_t **, const wchar_t **); -int _wspawnv (int, const wchar_t *, const wchar_t **); -#endif - +#ifndef GSPAWN_HELPER #ifdef G_SPAWN_WIN32_DEBUG static int debug = 1; #define SETUP_DEBUG() /* empty */ @@ -86,6 +81,7 @@ int _wspawnv (int, const wchar_t *, const wchar_t **); } \ G_STMT_END #endif +#endif enum { @@ -96,6 +92,7 @@ enum enum { ARG_CHILD_ERR_REPORT = 1, + ARG_HELPER_SYNC, ARG_STDIN, ARG_STDOUT, ARG_STDERR, @@ -107,9 +104,26 @@ enum { ARG_COUNT = ARG_PROGRAM }; +static int +dup_noninherited (int fd, + int mode) +{ + HANDLE filehandle; + + DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd), + GetCurrentProcess (), &filehandle, + 0, FALSE, DUPLICATE_SAME_ACCESS); + close (fd); + return _open_osfhandle ((gintptr) filehandle, mode | _O_NOINHERIT); +} + #ifndef GSPAWN_HELPER +#ifdef _WIN64 +#define HELPER_PROCESS "gspawn-win64-helper" +#else #define HELPER_PROCESS "gspawn-win32-helper" +#endif static gchar * protect_argv_string (const gchar *string) @@ -195,11 +209,8 @@ protect_argv (gchar **argv, return argc; } -GQuark -g_spawn_error_quark (void) -{ - return g_quark_from_static_string ("g-exec-error-quark"); -} +G_DEFINE_QUARK (g-exec-error-quark, g_spawn_error) +G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error) gboolean g_spawn_async_utf8 (const gchar *working_directory, @@ -250,7 +261,7 @@ read_data (GString *str, GError **error) { GIOStatus giostatus; - gssize bytes; + gsize bytes; gchar buf[4096]; again: @@ -268,8 +279,8 @@ read_data (GString *str, goto again; else if (giostatus == G_IO_STATUS_ERROR) { - g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, - _("Failed to read data from child process")); + g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, + _("Failed to read data from child process")); return READ_FAILED; } @@ -283,9 +294,11 @@ make_pipe (gint p[2], { if (_pipe (p, 4096, _O_BINARY) < 0) { + int errsv = errno; + g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Failed to create pipe for communicating with child process (%s)"), - g_strerror (errno)); + g_strerror (errsv)); return FALSE; } else @@ -297,50 +310,56 @@ make_pipe (gint p[2], */ static gboolean read_helper_report (int fd, - gint report[2], + gintptr report[2], GError **error) { gint bytes = 0; - while (bytes < sizeof(gint)*2) + while (bytes < sizeof(gintptr)*2) { gint chunk; if (debug) - g_print ("%s:read_helper_report: read %d...\n", + g_print ("%s:read_helper_report: read %" G_GSIZE_FORMAT "...\n", __FILE__, - sizeof(gint)*2 - bytes); + sizeof(gintptr)*2 - bytes); chunk = read (fd, ((gchar*)report) + bytes, - sizeof(gint)*2 - bytes); + sizeof(gintptr)*2 - bytes); if (debug) g_print ("...got %d bytes\n", chunk); if (chunk < 0) { + int errsv = errno; + /* Some weird shit happened, bail out */ - g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, _("Failed to read from child pipe (%s)"), - g_strerror (errno)); + g_strerror (errsv)); return FALSE; } else if (chunk == 0) - break; /* EOF */ + { + g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED, + _("Failed to read from child pipe (%s)"), + "EOF"); + break; /* EOF */ + } else bytes += chunk; } - if (bytes < sizeof(gint)*2) + if (bytes < sizeof(gintptr)*2) return FALSE; return TRUE; } static void -set_child_error (gint report[2], +set_child_error (gintptr report[2], const gchar *working_directory, GError **error) { @@ -406,14 +425,12 @@ do_spawn_directly (gint *exit_status, gchar **argv, char **envp, char **protected_argv, - GSpawnChildSetupFunc child_setup, - gpointer user_data, GPid *child_handle, GError **error) { const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT; char **new_argv; - int rc = -1; + gintptr rc = -1; int saved_errno; GError *conv_error = NULL; gint conv_error_index; @@ -455,9 +472,6 @@ do_spawn_directly (gint *exit_status, return FALSE; } - if (child_setup) - (* child_setup) (user_data); - if (flags & G_SPAWN_SEARCH_PATH) if (wenvp != NULL) rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp); @@ -508,7 +522,6 @@ do_spawn_with_pipes (gint *exit_status, char **envp, GSpawnFlags flags, GSpawnChildSetupFunc child_setup, - gpointer user_data, GPid *child_handle, gint *standard_input, gint *standard_output, @@ -520,27 +533,27 @@ do_spawn_with_pipes (gint *exit_status, char args[ARG_COUNT][10]; char **new_argv; int i; - int rc = -1; + gintptr rc = -1; int saved_errno; int argc; int stdin_pipe[2] = { -1, -1 }; int stdout_pipe[2] = { -1, -1 }; int stderr_pipe[2] = { -1, -1 }; int child_err_report_pipe[2] = { -1, -1 }; - int helper_report[2]; + int helper_sync_pipe[2] = { -1, -1 }; + gintptr helper_report[2]; static gboolean warned_about_child_setup = FALSE; GError *conv_error = NULL; gint conv_error_index; gchar *helper_process; CONSOLE_CURSOR_INFO cursor_info; wchar_t *whelper, **wargv, **wenvp; - - SETUP_DEBUG(); + gchar *glib_dll_directory; if (child_setup && !warned_about_child_setup) { warned_about_child_setup = TRUE; - g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32"); + g_warning ("passing a child setup function to the g_spawn functions is pointless on Windows and it is ignored"); } argc = protect_argv (argv, &protected_argv); @@ -556,8 +569,7 @@ do_spawn_with_pipes (gint *exit_status, gboolean retval = do_spawn_directly (exit_status, do_return_handle, flags, argv, envp, protected_argv, - child_setup, user_data, child_handle, - error); + child_handle, error); g_strfreev (protected_argv); return retval; } @@ -574,15 +586,36 @@ do_spawn_with_pipes (gint *exit_status, if (!make_pipe (child_err_report_pipe, error)) goto cleanup_and_fail; + if (!make_pipe (helper_sync_pipe, error)) + goto cleanup_and_fail; + new_argv = g_new (char *, argc + 1 + ARG_COUNT); - if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info)) + if (GetConsoleWindow () != NULL) helper_process = HELPER_PROCESS "-console.exe"; else helper_process = HELPER_PROCESS ".exe"; - new_argv[0] = helper_process; + + glib_dll_directory = _glib_get_dll_directory (); + if (glib_dll_directory != NULL) + { + helper_process = g_build_filename (glib_dll_directory, helper_process, NULL); + g_free (glib_dll_directory); + } + else + helper_process = g_strdup (helper_process); + + new_argv[0] = protect_argv_string (helper_process); + _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]); new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT]; + /* Make the read end of the child error report pipe + * noninherited. Otherwise it will needlessly be inherited by the + * helper process, and the started actual user process. As such that + * shouldn't harm, but it is unnecessary. + */ + child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY); + if (flags & G_SPAWN_FILE_AND_ARGV_ZERO) { /* Overload ARG_CHILD_ERR_REPORT to also encode the @@ -591,6 +624,17 @@ do_spawn_with_pipes (gint *exit_status, strcat (args[ARG_CHILD_ERR_REPORT], "#"); } + _g_sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]); + new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC]; + + /* Make the write end of the sync pipe noninherited. Otherwise the + * helper process will inherit it, and thus if this process happens + * to crash before writing the sync byte to the pipe, the helper + * process won't read but won't get any EOF either, as it has the + * write end open itself. + */ + helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY); + if (standard_input) { _g_sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]); @@ -658,6 +702,8 @@ do_spawn_with_pipes (gint *exit_status, for (i = 0; i <= argc; i++) new_argv[ARG_PROGRAM + i] = protected_argv[i]; + SETUP_DEBUG(); + if (debug) { g_print ("calling %s with argv:\n", helper_process); @@ -665,8 +711,6 @@ do_spawn_with_pipes (gint *exit_status, g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL")); } - whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL); - if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error)) { if (conv_error_index == ARG_WORKING_DIRECTORY) @@ -679,11 +723,12 @@ do_spawn_with_pipes (gint *exit_status, conv_error_index - ARG_PROGRAM, conv_error->message); g_error_free (conv_error); g_strfreev (protected_argv); + g_free (new_argv[0]); g_free (new_argv[ARG_WORKING_DIRECTORY]); g_free (new_argv); - g_free (whelper); + g_free (helper_process); - return FALSE; + goto cleanup_and_fail; } if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error)) @@ -693,21 +738,19 @@ do_spawn_with_pipes (gint *exit_status, conv_error->message); g_error_free (conv_error); g_strfreev (protected_argv); + g_free (new_argv[0]); g_free (new_argv[ARG_WORKING_DIRECTORY]); g_free (new_argv); - g_free (whelper); + g_free (helper_process); g_strfreev ((gchar **) wargv); - return FALSE; + goto cleanup_and_fail; } - if (child_setup) - (* child_setup) (user_data); + whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL); + g_free (helper_process); if (wenvp != NULL) - /* Let's hope envp hasn't mucked with PATH so that - * gspawn-win32-helper.exe isn't found. - */ rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp); else rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv); @@ -722,12 +765,14 @@ do_spawn_with_pipes (gint *exit_status, * otherwise the reader will never get EOF. */ close_and_invalidate (&child_err_report_pipe[1]); + close_and_invalidate (&helper_sync_pipe[0]); close_and_invalidate (&stdin_pipe[0]); close_and_invalidate (&stdout_pipe[1]); close_and_invalidate (&stderr_pipe[1]); g_strfreev (protected_argv); + g_free (new_argv[0]); g_free (new_argv[ARG_WORKING_DIRECTORY]); g_free (new_argv); @@ -748,6 +793,8 @@ do_spawn_with_pipes (gint *exit_status, */ g_assert (err_report != NULL); *err_report = child_err_report_pipe[0]; + write (helper_sync_pipe[1], " ", 1); + close_and_invalidate (&helper_sync_pipe[1]); } else { @@ -755,7 +802,7 @@ do_spawn_with_pipes (gint *exit_status, if (!read_helper_report (child_err_report_pipe[0], helper_report, error)) goto cleanup_and_fail; - close (child_err_report_pipe[0]); + close_and_invalidate (&child_err_report_pipe[0]); switch (helper_report[0]) { @@ -769,13 +816,21 @@ do_spawn_with_pipes (gint *exit_status, if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1], GetCurrentProcess (), (LPHANDLE) child_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) - *child_handle = 0; + { + char *emsg = g_win32_error_message (GetLastError ()); + g_print("%s\n", emsg); + *child_handle = 0; + } } else if (child_handle) *child_handle = 0; + write (helper_sync_pipe[1], " ", 1); + close_and_invalidate (&helper_sync_pipe[1]); break; default: + write (helper_sync_pipe[1], " ", 1); + close_and_invalidate (&helper_sync_pipe[1]); set_child_error (helper_report, working_directory, error); goto cleanup_and_fail; } @@ -795,12 +850,17 @@ do_spawn_with_pipes (gint *exit_status, return TRUE; cleanup_and_fail: + if (rc != -1) CloseHandle ((HANDLE) rc); if (child_err_report_pipe[0] != -1) close (child_err_report_pipe[0]); if (child_err_report_pipe[1] != -1) close (child_err_report_pipe[1]); + if (helper_sync_pipe[0] != -1) + close (helper_sync_pipe[0]); + if (helper_sync_pipe[1] != -1) + close (helper_sync_pipe[1]); if (stdin_pipe[0] != -1) close (stdin_pipe[0]); if (stdin_pipe[1] != -1) @@ -868,7 +928,6 @@ g_spawn_sync_utf8 (const gchar *working_directory, envp, flags, child_setup, - user_data, NULL, NULL, standard_output ? &outpipe : NULL, @@ -891,7 +950,7 @@ g_spawn_sync_utf8 (const gchar *working_directory, G_IO_IN | G_IO_ERR | G_IO_HUP, &outfd); if (debug) - g_print ("outfd=%x\n", outfd.fd); + g_print ("outfd=%p\n", (HANDLE) outfd.fd); } if (errpipe >= 0) @@ -904,7 +963,7 @@ g_spawn_sync_utf8 (const gchar *working_directory, G_IO_IN | G_IO_ERR | G_IO_HUP, &errfd); if (debug) - g_print ("errfd=%x\n", errfd.fd); + g_print ("errfd=%p\n", (HANDLE) errfd.fd); } /* Read data until we get EOF on all pipes. */ @@ -934,8 +993,8 @@ g_spawn_sync_utf8 (const gchar *working_directory, { failed = TRUE; - g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, - _("Unexpected error in g_io_channel_win32_poll() reading data from a child process")); + g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ, + _("Unexpected error in g_io_channel_win32_poll() reading data from a child process")); break; } @@ -1006,7 +1065,7 @@ g_spawn_sync_utf8 (const gchar *working_directory, /* Helper process was involved. Read its report now after the * grandchild has finished. */ - gint helper_report[2]; + gintptr helper_report[2]; if (!read_helper_report (reportpipe, helper_report, error)) failed = TRUE; @@ -1089,7 +1148,6 @@ g_spawn_async_with_pipes_utf8 (const gchar *working_directory, envp, flags, child_setup, - user_data, child_handle, standard_input, standard_output, @@ -1163,6 +1221,27 @@ g_spawn_close_pid (GPid pid) CloseHandle (pid); } +gboolean +g_spawn_check_exit_status (gint exit_status, + GError **error) +{ + gboolean ret = FALSE; + + if (exit_status != 0) + { + g_set_error (error, G_SPAWN_EXIT_ERROR, exit_status, + _("Child process exited with code %ld"), + (long) exit_status); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +#if !defined (_WIN64) + /* Binary compatibility versions that take system codepage pathnames, * argument vectors and environments. These get used only by code * built against 2.8.1 or earlier. Code built against 2.8.2 or later @@ -1428,7 +1507,6 @@ g_spawn_command_line_async (const gchar *command_line, return retval; } -#endif /* !GSPAWN_HELPER */ +#endif /* !_WIN64 */ -#define __G_SPAWN_C__ -#include "galiasdef.c" +#endif /* !GSPAWN_HELPER */