diff --git a/gio/gio.h b/gio/gio.h index 2d0a9c274..e37b6bb49 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -167,6 +167,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 2f2caf77a..7ca8bb715 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -1682,6 +1682,7 @@ test_nonce_tcp (void) gchar *nonce_file; gboolean res; const gchar *address; + int fd; test_guid = g_dbus_generate_guid (); loop = g_main_loop_new (NULL, FALSE); @@ -1780,7 +1781,9 @@ test_nonce_tcp (void) g_assert (c == NULL); /* Recreate the nonce-file so we can ensure the server deletes it when stopped. */ - g_assert_cmpint (g_creat (nonce_file, 0600), !=, -1); + fd = g_creat (nonce_file, 0600); + g_assert_cmpint (fd, !=, -1); + g_close (fd, NULL); g_dbus_server_stop (server); g_object_unref (server); diff --git a/gio/tests/socket.c b/gio/tests/socket.c index d271310c0..9a5092642 100644 --- a/gio/tests/socket.c +++ b/gio/tests/socket.c @@ -18,6 +18,7 @@ #include #include +#include "glib-private.h" #include #include @@ -32,6 +33,7 @@ #ifdef G_OS_WIN32 #include "giowin32-afunix.h" +#include #endif #include "gnetworkingprivate.h" @@ -139,6 +141,9 @@ create_server_full (GSocketFamily family, g_assert_cmpint (g_socket_get_family (server), ==, family); g_assert_cmpint (g_socket_get_socket_type (server), ==, socket_type); g_assert_cmpint (g_socket_get_protocol (server), ==, G_SOCKET_PROTOCOL_DEFAULT); +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (server))); +#endif g_socket_set_blocking (server, TRUE); @@ -475,7 +480,9 @@ test_ip_sync (GSocketFamily family) g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); - +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); +#endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); @@ -612,6 +619,9 @@ test_ip_sync_dgram (GSocketFamily family) g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); +#endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); @@ -849,6 +859,9 @@ test_ip_sync_dgram_timeouts (GSocketFamily family) g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_DATAGRAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); +#endif #ifdef G_OS_WIN32 /* Winsock can't recv() on unbound udp socket */ @@ -991,6 +1004,9 @@ test_close_graceful (void) g_assert_cmpint (g_socket_get_family (client), ==, family); g_assert_cmpint (g_socket_get_socket_type (client), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (client), ==, G_SOCKET_PROTOCOL_DEFAULT); +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); +#endif g_socket_set_blocking (client, TRUE); g_socket_set_timeout (client, 1); @@ -1258,6 +1274,9 @@ test_fd_reuse (void) g_assert_cmpint (g_socket_get_family (client2), ==, g_socket_get_family (client)); g_assert_cmpint (g_socket_get_socket_type (client2), ==, g_socket_get_socket_type (client)); g_assert_cmpint (g_socket_get_protocol (client2), ==, G_SOCKET_PROTOCOL_TCP); +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (client))); +#endif len = g_socket_send (client2, testbuf, strlen (testbuf) + 1, NULL, &error); g_assert_no_error (error); @@ -1377,6 +1396,9 @@ test_unix_from_fd (void) g_assert_cmpint (g_socket_get_family (s), ==, G_SOCKET_FAMILY_UNIX); g_assert_cmpint (g_socket_get_socket_type (s), ==, G_SOCKET_TYPE_STREAM); g_assert_cmpint (g_socket_get_protocol (s), ==, G_SOCKET_PROTOCOL_DEFAULT); +#ifdef G_OS_WIN32 + g_assert (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) ((HANDLE)(gintptr) g_socket_get_fd (s))); +#endif g_object_unref (s); } @@ -1511,6 +1533,35 @@ test_unix_connection_ancillary_data (void) } #endif +#ifdef G_OS_WIN32 +static void +test_handle_not_socket (void) +{ + GError *err = NULL; + gchar *name = NULL; + HANDLE hReadPipe, hWritePipe, h; + int fd; + + g_assert_true (CreatePipe (&hReadPipe, &hWritePipe, NULL, 2048)); + g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (hReadPipe)); + g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (hWritePipe)); + CloseHandle (hReadPipe); + CloseHandle (hWritePipe); + + h = (HANDLE) _get_osfhandle (1); + g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (h)); + + fd = g_file_open_tmp (NULL, &name, &err); + g_assert_no_error (err); + h = (HANDLE) _get_osfhandle (fd); + g_assert_false (GLIB_PRIVATE_CALL (g_win32_handle_is_socket) (h)); + g_close (fd, &err); + g_assert_no_error (err); + g_unlink (name); + g_free (name); +} +#endif + static gboolean postmortem_source_cb (GSocket *socket, GIOCondition condition, @@ -2327,6 +2378,9 @@ main (int argc, g_test_add_func ("/socket/unix-connection", test_unix_connection); #ifdef G_OS_UNIX g_test_add_func ("/socket/unix-connection-ancillary-data", test_unix_connection_ancillary_data); +#endif +#ifdef G_OS_WIN32 + g_test_add_func ("/socket/win32-handle-not-socket", test_handle_not_socket); #endif g_test_add_func ("/socket/source-postmortem", test_source_postmortem); g_test_add_func ("/socket/reuse/tcp", test_reuse_tcp); diff --git a/gio/tests/unix-fd.c b/gio/tests/unix-fd.c index 4d984dfb3..b29ddca1f 100644 --- a/gio/tests/unix-fd.c +++ b/gio/tests/unix-fd.c @@ -39,7 +39,7 @@ create_fd_list (gint *fd_list) } static void -test_fds (void) +test_scm (void) { GError *err = NULL; GUnixFDMessage *message; @@ -235,7 +235,7 @@ main (int argc, char **argv) { g_test_init (&argc, &argv, NULL); - g_test_add_func ("/unix-streams/file-descriptors", test_fds); + g_test_add_func ("/unix-fd/scm", test_scm); return g_test_run(); diff --git a/glib/glib-init.h b/glib/glib-init.h index b77164c73..73e186199 100644 --- a/glib/glib-init.h +++ b/glib/glib-init.h @@ -43,6 +43,9 @@ gboolean _g_win32_call_rtl_version (OSVERSIONINFOEXW *info); extern HMODULE glib_dll; gchar *g_win32_find_helper_executable_path (const gchar *process_name, void *dll_handle); +int g_win32_reopen_noninherited (int fd, int mode, GError **err); +gboolean g_win32_handle_is_socket (void *h); + #endif #endif /* __GLIB_INIT_H__ */ diff --git a/glib/glib-private.c b/glib/glib-private.c index 0a59c6f16..321efc1d6 100644 --- a/glib/glib-private.c +++ b/glib/glib-private.c @@ -55,6 +55,8 @@ glib__private__ (void) g_win32_readlink_utf8, g_win32_fstat, g_win32_find_helper_executable_path, + g_win32_reopen_noninherited, + g_win32_handle_is_socket, #endif }; diff --git a/glib/glib-private.h b/glib/glib-private.h index 943252f1b..39b7c0a8e 100644 --- a/glib/glib-private.h +++ b/glib/glib-private.h @@ -171,6 +171,13 @@ typedef struct { /* See gwin32.c */ gchar *(*g_win32_find_helper_executable_path) (const gchar *process_name, void *dll_handle); + + int (* g_win32_reopen_noninherited) (int fd, + int mode, + GError **err); + + gboolean (* g_win32_handle_is_socket) (void *handle); + #endif diff --git a/glib/gspawn-win32-helper.c b/glib/gspawn-win32-helper.c index 4729ee46e..ee68f8eb7 100644 --- a/glib/gspawn-win32-helper.c +++ b/glib/gspawn-win32-helper.c @@ -39,6 +39,7 @@ #include "glib.h" #define GSPAWN_HELPER #include "gspawn-win32.c" /* For shared definitions */ +#include "glib/glib-private.h" static void @@ -295,7 +296,8 @@ main (int ignored_argc, char **ignored_argv) /* GUI application do not necessarily have a stderr */ if (_fileno (stderr) == 2) { - saved_stderr_fd = reopen_noninherited (dup (2), _O_WRONLY); + saved_stderr_fd = GLIB_PRIVATE_CALL (g_win32_reopen_noninherited) ( + dup (2), _O_WRONLY, NULL); if (saved_stderr_fd == -1) write_err_and_exit (child_err_report_fd, CHILD_DUP_FAILED); } @@ -403,8 +405,10 @@ main (int ignored_argc, char **ignored_argv) /* We don't want our child to inherit the error report and * helper sync fds. */ - child_err_report_fd = reopen_noninherited (child_err_report_fd, _O_WRONLY); - helper_sync_fd = reopen_noninherited (helper_sync_fd, _O_RDONLY); + child_err_report_fd = GLIB_PRIVATE_CALL (g_win32_reopen_noninherited) ( + child_err_report_fd, _O_WRONLY, NULL); + helper_sync_fd = GLIB_PRIVATE_CALL (g_win32_reopen_noninherited) ( + helper_sync_fd, _O_RDONLY, NULL); if (helper_sync_fd == -1) write_err_and_exit (child_err_report_fd, CHILD_DUP_FAILED); diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c index fc2a96c94..ffda37f53 100644 --- a/glib/gspawn-win32.c +++ b/glib/gspawn-win32.c @@ -107,19 +107,6 @@ enum { ARG_COUNT = ARG_PROGRAM }; -static int -reopen_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 @@ -684,7 +671,10 @@ fork_exec (gint *exit_status, * helper process, and the started actual user process. As such that * shouldn't harm, but it is unnecessary. */ - child_err_report_pipe[0] = reopen_noninherited (child_err_report_pipe[0], _O_RDONLY); + child_err_report_pipe[0] = g_win32_reopen_noninherited ( + child_err_report_pipe[0], _O_RDONLY, error); + if (child_err_report_pipe[0] == -1) + goto cleanup_and_fail; if (flags & G_SPAWN_FILE_AND_ARGV_ZERO) { @@ -703,7 +693,10 @@ fork_exec (gint *exit_status, * process won't read but won't get any EOF either, as it has the * write end open itself. */ - helper_sync_pipe[1] = reopen_noninherited (helper_sync_pipe[1], _O_WRONLY); + helper_sync_pipe[1] = g_win32_reopen_noninherited ( + helper_sync_pipe[1], _O_WRONLY, error); + if (helper_sync_pipe[1] == -1) + goto cleanup_and_fail; if (stdin_fd != -1) { @@ -903,7 +896,8 @@ fork_exec (gint *exit_status, 0, TRUE, DUPLICATE_SAME_ACCESS)) { char *emsg = g_win32_error_message (GetLastError ()); - g_print("%s\n", emsg); + g_print ("%s\n", emsg); + g_free (emsg); *child_pid = 0; } } diff --git a/glib/gwin32.c b/glib/gwin32.c index b2b5ff69d..0a34ed98d 100644 --- a/glib/gwin32.c +++ b/glib/gwin32.c @@ -31,6 +31,7 @@ #include "glibconfig.h" +#include #include #include #include @@ -39,6 +40,7 @@ #include #define STRICT /* Strict typing, please */ +#include #include #undef STRICT #ifndef G_WITH_CYGWIN @@ -805,8 +807,6 @@ g_win32_get_command_line (void) return result; } -#ifdef G_OS_WIN32 - /* Binary compatibility versions. Not for newly compiled code. */ _GLIB_EXTERN gchar *g_win32_get_package_installation_directory_utf8 (const gchar *package, @@ -837,10 +837,6 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS G_GNUC_END_IGNORE_DEPRECATIONS } -#endif - -#ifdef G_OS_WIN32 - /* This function looks up two environment * variables, G_WIN32_ALLOC_CONSOLE and G_WIN32_ATTACH_CONSOLE. * G_WIN32_ALLOC_CONSOLE, if set to 1, makes the process @@ -1455,4 +1451,112 @@ g_win32_find_helper_executable_path (const gchar *executable_name, void *dll_han return executable_path; } -#endif +/* + * g_win32_handle_is_socket: + * @h: a win32 HANDLE + * + * Returns: %TRUE if the handle is a `SOCKET`. + */ +gboolean +g_win32_handle_is_socket (HANDLE h) +{ + int option = 0; + int optlen = sizeof (option); + + /* according to: https://stackoverflow.com/a/50981652/1277510, this is reasonable */ + if (getsockopt ((SOCKET) h, SOL_SOCKET, SO_DEBUG, (char *) &option, &optlen) == SOCKET_ERROR) + return FALSE; + + return TRUE; +} + +/* + * g_win32_reopen_noninherited: + * @fd: (transfer full): A file descriptor + * @mode: _open_osfhandle flags + * @error: A location to return an error of type %G_FILE_ERROR + * + * Reopen the given @fd with `_O_NOINHERIT`. + * + * The @fd is closed on success. + * + * Returns: (transfer full): The new file-descriptor, or -1 on error. + */ +int +g_win32_reopen_noninherited (int fd, + int mode, + GError **error) +{ + HANDLE h; + HANDLE duph; + int dupfd, errsv; + + h = (HANDLE) _get_osfhandle (fd); + errsv = errno; + + if (h == INVALID_HANDLE_VALUE) + { + const char *emsg = g_strerror (errsv); + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv), + "_get_osfhandle() failed: %s", emsg); + return -1; + } + + if (g_win32_handle_is_socket (h)) + { + WSAPROTOCOL_INFO info; + + if (WSADuplicateSocket ((SOCKET) h, + GetCurrentProcessId (), + &info)) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + "WSADuplicateSocket() failed: %s", emsg); + g_free (emsg); + return -1; + } + + duph = (HANDLE) WSASocket (FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &info, 0, 0); + if (duph == (HANDLE) INVALID_SOCKET) + { + gchar *emsg = g_win32_error_message (WSAGetLastError ()); + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + "WSASocket() failed: %s", emsg); + g_free (emsg); + return -1; + } + } + else if (DuplicateHandle (GetCurrentProcess (), h, + GetCurrentProcess (), &duph, + 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) + { + char *emsg = g_win32_error_message (GetLastError ()); + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + "DuplicateHandle() failed: %s", emsg); + g_free (emsg); + return -1; + } + + /* the duph ownership is transferred to dupfd */ + dupfd = _open_osfhandle ((gintptr) duph, mode | _O_NOINHERIT); + if (dupfd < 0) + { + g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + "_open_osfhandle() failed"); + CloseHandle (duph); + return -1; + } + + if (!g_close (fd, error)) + { + /* ignore extra errors in this case */ + g_close (dupfd, NULL); + return -1; + } + + return dupfd; +} diff --git a/glib/tests/meson.build b/glib/tests/meson.build index 07490a079..caa9b9c25 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -108,7 +108,9 @@ glib_tests = { 'sort' : {}, 'spawn-multithreaded' : {}, 'spawn-path-search' : {}, - 'spawn-singlethread' : {}, + 'spawn-singlethread' : { + 'dependencies' : [winsock2], + }, 'spawn-test' : {}, 'strfuncs' : {}, 'string' : {}, @@ -306,6 +308,13 @@ if host_machine.system() == 'windows' install_dir: installed_tests_execdir, install: installed_tests_enabled, ) + executable('spawn-test-win32-gui', 'spawn-test-win32-gui.c', + c_args : test_cargs, + dependencies : test_deps, + install_dir: installed_tests_execdir, + install: installed_tests_enabled, + win_subsystem: 'windows', + ) endif executable('testing-helper', 'testing-helper.c', diff --git a/glib/tests/spawn-singlethread.c b/glib/tests/spawn-singlethread.c index b17b47237..d0e3537ef 100644 --- a/glib/tests/spawn-singlethread.c +++ b/glib/tests/spawn-singlethread.c @@ -32,11 +32,13 @@ #ifdef G_OS_UNIX #include #include +#include #include #include #endif #ifdef G_OS_WIN32 +#include #include #define LINEEND "\r\n" #else @@ -353,6 +355,56 @@ test_spawn_sync (void) g_free (joined_args_str); } +static void +init_networking (void) +{ +#ifdef G_OS_WIN32 + WSADATA wsadata; + + if (WSAStartup (MAKEWORD (2, 0), &wsadata) != 0) + g_error ("Windows Sockets could not be initialized"); +#endif +} + +static void +test_spawn_stderr_socket (void) +{ + GError *error = NULL; + GPtrArray *argv; + int estatus; + int fd; + + g_test_summary ("Test calling g_spawn_sync() with its stderr FD set to a socket"); + + if (g_test_subprocess ()) + { + init_networking (); + fd = socket (AF_INET, SOCK_STREAM, 0); + g_assert_cmpint (fd, >=, 0); +#ifdef G_OS_WIN32 + fd = _open_osfhandle (fd, 0); + g_assert_cmpint (fd, >=, 0); +#endif + /* Set the socket as FD 2, stderr */ + estatus = dup2 (fd, 2); + g_assert_cmpint (estatus, >=, 0); + + argv = g_ptr_array_new (); + g_ptr_array_add (argv, echo_script_path); + g_ptr_array_add (argv, NULL); + + g_spawn_sync (NULL, (char**) argv->pdata, NULL, 0, NULL, NULL, NULL, NULL, NULL, &error); + g_assert_no_error (error); + g_ptr_array_free (argv, TRUE); + g_close (fd, &error); + g_assert_no_error (error); + return; + } + + g_test_trap_subprocess (NULL, 0, 0); + g_test_trap_assert_passed (); +} + /* Like test_spawn_sync but uses spawn flags that trigger the optimized * posix_spawn codepath. */ @@ -517,6 +569,7 @@ main (int argc, g_assert (g_file_test (echo_script_path, G_FILE_TEST_EXISTS)); g_test_add_func ("/gthread/spawn-single-sync", test_spawn_sync); + g_test_add_func ("/gthread/spawn-stderr-socket", test_spawn_stderr_socket); g_test_add_func ("/gthread/spawn-single-async", test_spawn_async); g_test_add_func ("/gthread/spawn-single-async-with-fds", test_spawn_async_with_fds); g_test_add_func ("/gthread/spawn-script", test_spawn_script); diff --git a/tests/spawn-test-win32-gui.c b/glib/tests/spawn-test-win32-gui.c similarity index 100% rename from tests/spawn-test-win32-gui.c rename to glib/tests/spawn-test-win32-gui.c diff --git a/meson.build b/meson.build index 1619cfa2e..89a405dfe 100644 --- a/meson.build +++ b/meson.build @@ -114,6 +114,9 @@ if cc.has_argument('-fno-strict-aliasing') add_project_arguments('-fno-strict-aliasing', language: 'c') endif +# dummy/empty dependency() object to declare fallbacks and simpler dependencies +not_found = dependency('', required: false) + ######################## # Configuration begins # ######################## @@ -2134,6 +2137,8 @@ endif if host_system == 'windows' winsock2 = cc.find_library('ws2_32') +else + winsock2 = not_found endif selinux_dep = [] diff --git a/tests/meson.build b/tests/meson.build index 9c8821132..907863bc8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -42,12 +42,6 @@ test_extra_programs = { 'assert-msg-test' : {}, } -if host_machine.system() == 'windows' - test_extra_programs += { - 'spawn-test-win32-gui' : {'win_subsystem' : 'windows'} - } -endif - module_suffix = [] # Keep the autotools convention for shared module suffix because GModule # depends on it: https://gitlab.gnome.org/GNOME/glib/issues/520