Commit 4342dbbe authored by Vasily Galkin's avatar Vasily Galkin

gdbus, win32: move most W32 dbus autolaunch code to gdbusprivate

This allows referencig them from more than single .c file.

Implementation moved without changes
from gdbusaddress.c to gdbusprivate.c

g_win32_run_session_bus signature also kept, so ABI unchanged.
parent 4ed5abda
......@@ -37,8 +37,6 @@
#include "gtask.h"
#include "glib-private.h"
#include "gdbusprivate.h"
#include "giomodule-priv.h"
#include "gdbusdaemon.h"
#include "gstdio.h"
#ifdef G_OS_UNIX
......@@ -50,8 +48,6 @@
#ifdef G_OS_WIN32
#include <windows.h>
#include <io.h>
#include <conio.h>
#endif
#include "glibintl.h"
......@@ -1195,367 +1191,12 @@ get_session_address_dbus_launch (GError **error)
/* end of G_OS_UNIX case */
#elif defined(G_OS_WIN32)
#define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo"
#define DBUS_DAEMON_MUTEX "DBusDaemonMutex"
#define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex"
#define DBUS_AUTOLAUNCH_MUTEX "DBusAutolaunchMutex"
static void
release_mutex (HANDLE mutex)
{
ReleaseMutex (mutex);
CloseHandle (mutex);
}
static HANDLE
acquire_mutex (const char *mutexname)
{
HANDLE mutex;
DWORD res;
mutex = CreateMutexA (NULL, FALSE, mutexname);
if (!mutex)
return 0;
res = WaitForSingleObject (mutex, INFINITE);
switch (res)
{
case WAIT_ABANDONED:
release_mutex (mutex);
return 0;
case WAIT_FAILED:
case WAIT_TIMEOUT:
return 0;
}
return mutex;
}
static gboolean
is_mutex_owned (const char *mutexname)
{
HANDLE mutex;
gboolean res = FALSE;
mutex = CreateMutexA (NULL, FALSE, mutexname);
if (WaitForSingleObject (mutex, 10) == WAIT_TIMEOUT)
res = TRUE;
else
ReleaseMutex (mutex);
CloseHandle (mutex);
return res;
}
static char *
read_shm (const char *shm_name)
{
HANDLE shared_mem;
char *shared_data;
char *res;
int i;
res = NULL;
for (i = 0; i < 20; i++)
{
shared_mem = OpenFileMappingA (FILE_MAP_READ, FALSE, shm_name);
if (shared_mem != 0)
break;
Sleep (100);
}
if (shared_mem != 0)
{
shared_data = MapViewOfFile (shared_mem, FILE_MAP_READ, 0, 0, 0);
/* It looks that a race is possible here:
* if the dbus process already created mapping but didn't fill it
* the code below may read incorrect address.
* Also this is a bit complicated by the fact that
* any change in the "synchronization contract" between processes
* should be accompanied with renaming all of used win32 named objects:
* otherwise libgio-2.0-0.dll of different versions shipped with
* different apps may break each other due to protocol difference.
*/
if (shared_data != NULL)
{
res = g_strdup (shared_data);
UnmapViewOfFile (shared_data);
}
CloseHandle (shared_mem);
}
return res;
}
static HANDLE
set_shm (const char *shm_name, const char *value)
{
HANDLE shared_mem;
char *shared_data;
shared_mem = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, strlen (value) + 1, shm_name);
if (shared_mem == 0)
return 0;
shared_data = MapViewOfFile (shared_mem, FILE_MAP_WRITE, 0, 0, 0 );
if (shared_data == NULL)
return 0;
strcpy (shared_data, value);
UnmapViewOfFile (shared_data);
return shared_mem;
}
/* These keep state between publish_session_bus and unpublish_session_bus */
static HANDLE published_daemon_mutex;
static HANDLE published_shared_mem;
static gboolean
publish_session_bus (const char *address)
{
HANDLE init_mutex;
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
published_daemon_mutex = CreateMutexA (NULL, FALSE, DBUS_DAEMON_MUTEX);
if (WaitForSingleObject (published_daemon_mutex, 10 ) != WAIT_OBJECT_0)
{
release_mutex (init_mutex);
CloseHandle (published_daemon_mutex);
published_daemon_mutex = NULL;
return FALSE;
}
published_shared_mem = set_shm (DBUS_DAEMON_ADDRESS_INFO, address);
if (!published_shared_mem)
{
release_mutex (init_mutex);
CloseHandle (published_daemon_mutex);
published_daemon_mutex = NULL;
return FALSE;
}
release_mutex (init_mutex);
return TRUE;
}
static void
unpublish_session_bus (void)
{
HANDLE init_mutex;
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
CloseHandle (published_shared_mem);
published_shared_mem = NULL;
release_mutex (published_daemon_mutex);
published_daemon_mutex = NULL;
release_mutex (init_mutex);
}
static void
wait_console_window (void)
{
FILE *console = fopen ("CONOUT$", "w");
SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window.");
fprintf (console, _("(Type any character to close this window)\n"));
fflush (console);
_getch ();
}
static void
open_console_window (void)
{
if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE ||
(HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ())
{
if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE)
freopen ("CONOUT$", "w", stdout);
if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE)
freopen ("CONOUT$", "w", stderr);
SetConsoleTitleW (L"gdbus-daemon debug output.");
atexit (wait_console_window);
}
}
static void
idle_timeout_cb (GDBusDaemon *daemon, gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
/* Satisfies STARTF_FORCEONFEEDBACK */
static void
turn_off_the_starting_cursor (void)
{
MSG msg;
BOOL bRet;
PostQuitMessage (0);
while ((bRet = GetMessage (&msg, 0, 0, 0)) != 0)
{
if (bRet == -1)
continue;
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
__declspec(dllexport) void CALLBACK g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow);
__declspec(dllexport) void CALLBACK
g_win32_run_session_bus (HWND hwnd, HINSTANCE hinst, char *cmdline, int nCmdShow)
{
GDBusDaemon *daemon;
GMainLoop *loop;
const char *address;
GError *error = NULL;
turn_off_the_starting_cursor ();
if (g_getenv ("GDBUS_DAEMON_DEBUG") != NULL)
open_console_window ();
loop = g_main_loop_new (NULL, FALSE);
address = "nonce-tcp:";
daemon = _g_dbus_daemon_new (address, NULL, &error);
if (daemon == NULL)
{
g_printerr ("Can't init bus: %s\n", error->message);
g_error_free (error);
return;
}
/* There is a subtle detail with "idle-timeout" signal of dbus daemon:
* It is fired on idle after last client disconnection,
* but (at least with glib 2.59.1) it is NEVER fired
* if no clients connect to daemon at all.
* This may lead to infinite run of this daemon process.
*/
g_signal_connect (daemon, "idle-timeout", G_CALLBACK (idle_timeout_cb), loop);
if (publish_session_bus (_g_dbus_daemon_get_address (daemon)))
{
g_main_loop_run (loop);
unpublish_session_bus ();
}
g_main_loop_unref (loop);
g_object_unref (daemon);
}
static gchar *
get_session_address_dbus_launch (GError **error)
{
HANDLE autolaunch_mutex, init_mutex;
char *address = NULL;
autolaunch_mutex = acquire_mutex (DBUS_AUTOLAUNCH_MUTEX);
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
if (is_mutex_owned (DBUS_DAEMON_MUTEX))
address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
release_mutex (init_mutex);
if (address == NULL)
{
/* rundll32 doesn't support neither spaces, nor quotes in cmdline:
* https://support.microsoft.com/en-us/help/164787/info-windows-rundll-and-rundll32-interface
* Since the dll path may have spaces, it is used as working directory,
* and the plain dll name is passed as argument to rundll32 like
* "C:\Windows\System32\rundll32.exe" .\libgio-2.0-0.dll,g_win32_run_session_bus
*
* Using relative path to dll rises a question if correct dll is loaded.
* According to docs if relative path like .\libgio-2.0-0.dll is passed
* the System32 directory may be searched before current directory:
* https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order#standard-search-order-for-desktop-applications
* Internally rundll32 uses "undefined behavior" parameter combination
* LoadLibraryExW(".\libgio-2.0-0.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
* Actual testing shows that if relative name starts from ".\"
* the current directory is searched before System32 (win7, win10 1607).
* So wrong dll would be loaded only if the BOTH of the following holds:
* - rundll32 will change so it would prefer system32 even for .\ paths
* - some app would place libgio-2.0-0.dll in system32 directory
*
* In point of that using .\libgio-2.0-0.dll looks fine.
*/
wchar_t gio_path[MAX_PATH + 2] = { 0 };
int gio_path_len = GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH + 1);
/* The <= MAX_PATH check prevents truncated path usage */
if (gio_path_len > 0 && gio_path_len <= MAX_PATH)
{
PROCESS_INFORMATION pi = { 0 };
STARTUPINFOW si = { 0 };
BOOL res = FALSE;
wchar_t rundll_path[MAX_PATH + 100] = { 0 };
wchar_t args[MAX_PATH*2 + 100] = { 0 };
/* calculate index of first char of dll file name inside full path */
int gio_name_index = gio_path_len;
for (; gio_name_index > 0; --gio_name_index)
{
wchar_t prev_char = gio_path[gio_name_index - 1];
if (prev_char == L'\\' || prev_char == L'/')
break;
}
GetWindowsDirectoryW (rundll_path, MAX_PATH);
wcscat (rundll_path, L"\\rundll32.exe");
if (GetFileAttributesW (rundll_path) == INVALID_FILE_ATTRIBUTES)
{
GetSystemDirectoryW (rundll_path, MAX_PATH);
wcscat (rundll_path, L"\\rundll32.exe");
}
wcscpy (args, L"\"");
wcscat (args, rundll_path);
wcscat (args, L"\" .\\");
wcscat (args, gio_path + gio_name_index);
#if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
wcscat (args, L",g_win32_run_session_bus");
#elif defined (_MSC_VER)
wcscat (args, L",_g_win32_run_session_bus@16");
#else
wcscat (args, L",g_win32_run_session_bus@16");
#endif
gio_path[gio_name_index] = L'\0';
res = CreateProcessW (rundll_path, args,
0, 0, FALSE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS,
0, gio_path,
&si, &pi);
if (res)
address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
}
}
release_mutex (autolaunch_mutex);
if (address == NULL)
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
_("Session dbus not running, and autolaunch failed"));
return address;
return _g_dbus_win32_get_session_address_dbus_launch (error);
}
#else /* neither G_OS_UNIX nor G_OS_WIN32 */
static gchar *
get_session_address_dbus_launch (GError **error)
......
......@@ -33,6 +33,8 @@
#include "gdbusproxy.h"
#include "gdbuserror.h"
#include "gdbusintrospection.h"
#include "gdbusdaemon.h"
#include "giomodule-priv.h"
#include "gtask.h"
#include "ginputstream.h"
#include "gmemoryinputstream.h"
......@@ -51,6 +53,8 @@
#ifdef G_OS_WIN32
#include <windows.h>
#include <io.h>
#include <conio.h>
#endif
#include "glibintl.h"
......@@ -2054,6 +2058,368 @@ out:
CloseHandle (h);
return ret;
}
#define DBUS_DAEMON_ADDRESS_INFO "DBusDaemonAddressInfo"
#define DBUS_DAEMON_MUTEX "DBusDaemonMutex"
#define UNIQUE_DBUS_INIT_MUTEX "UniqueDBusInitMutex"
#define DBUS_AUTOLAUNCH_MUTEX "DBusAutolaunchMutex"
static void
release_mutex (HANDLE mutex)
{
ReleaseMutex (mutex);
CloseHandle (mutex);
}
static HANDLE
acquire_mutex (const char *mutexname)
{
HANDLE mutex;
DWORD res;
mutex = CreateMutexA (NULL, FALSE, mutexname);
if (!mutex)
return 0;
res = WaitForSingleObject (mutex, INFINITE);
switch (res)
{
case WAIT_ABANDONED:
release_mutex (mutex);
return 0;
case WAIT_FAILED:
case WAIT_TIMEOUT:
return 0;
}
return mutex;
}
static gboolean
is_mutex_owned (const char *mutexname)
{
HANDLE mutex;
gboolean res = FALSE;
mutex = CreateMutexA (NULL, FALSE, mutexname);
if (WaitForSingleObject (mutex, 10) == WAIT_TIMEOUT)
res = TRUE;
else
ReleaseMutex (mutex);
CloseHandle (mutex);
return res;
}
static char *
read_shm (const char *shm_name)
{
HANDLE shared_mem;
char *shared_data;
char *res;
int i;
res = NULL;
for (i = 0; i < 20; i++)
{
shared_mem = OpenFileMappingA (FILE_MAP_READ, FALSE, shm_name);
if (shared_mem != 0)
break;
Sleep (100);
}
if (shared_mem != 0)
{
shared_data = MapViewOfFile (shared_mem, FILE_MAP_READ, 0, 0, 0);
/* It looks that a race is possible here:
* if the dbus process already created mapping but didn't fill it
* the code below may read incorrect address.
* Also this is a bit complicated by the fact that
* any change in the "synchronization contract" between processes
* should be accompanied with renaming all of used win32 named objects:
* otherwise libgio-2.0-0.dll of different versions shipped with
* different apps may break each other due to protocol difference.
*/
if (shared_data != NULL)
{
res = g_strdup (shared_data);
UnmapViewOfFile (shared_data);
}
CloseHandle (shared_mem);
}
return res;
}
static HANDLE
set_shm (const char *shm_name, const char *value)
{
HANDLE shared_mem;
char *shared_data;
shared_mem = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
0, strlen (value) + 1, shm_name);
if (shared_mem == 0)
return 0;
shared_data = MapViewOfFile (shared_mem, FILE_MAP_WRITE, 0, 0, 0 );
if (shared_data == NULL)
return 0;
strcpy (shared_data, value);
UnmapViewOfFile (shared_data);
return shared_mem;
}
/* These keep state between publish_session_bus and unpublish_session_bus */
static HANDLE published_daemon_mutex;
static HANDLE published_shared_mem;
static gboolean
publish_session_bus (const char *address)
{
HANDLE init_mutex;
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
published_daemon_mutex = CreateMutexA (NULL, FALSE, DBUS_DAEMON_MUTEX);
if (WaitForSingleObject (published_daemon_mutex, 10 ) != WAIT_OBJECT_0)
{
release_mutex (init_mutex);
CloseHandle (published_daemon_mutex);
published_daemon_mutex = NULL;
return FALSE;
}
published_shared_mem = set_shm (DBUS_DAEMON_ADDRESS_INFO, address);
if (!published_shared_mem)
{
release_mutex (init_mutex);
CloseHandle (published_daemon_mutex);
published_daemon_mutex = NULL;
return FALSE;
}
release_mutex (init_mutex);
return TRUE;
}
static void
unpublish_session_bus (void)
{
HANDLE init_mutex;
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
CloseHandle (published_shared_mem);
published_shared_mem = NULL;
release_mutex (published_daemon_mutex);
published_daemon_mutex = NULL;
release_mutex (init_mutex);
}
static void
wait_console_window (void)
{
FILE *console = fopen ("CONOUT$", "w");
SetConsoleTitleW (L"gdbus-daemon output. Type any character to close this window.");
fprintf (console, _("(Type any character to close this window)\n"));
fflush (console);
_getch ();
}
static void
open_console_window (void)
{
if (((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE ||
(HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE) && AllocConsole ())
{
if ((HANDLE) _get_osfhandle (fileno (stdout)) == INVALID_HANDLE_VALUE)
freopen ("CONOUT$", "w", stdout);
if ((HANDLE) _get_osfhandle (fileno (stderr)) == INVALID_HANDLE_VALUE)
freopen ("CONOUT$", "w", stderr);
SetConsoleTitleW (L"gdbus-daemon debug output.");
atexit (wait_console_window);
}
}
static void
idle_timeout_cb (GDBusDaemon *daemon, gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
/* Satisfies STARTF_FORCEONFEEDBACK */
static void
turn_off_the_starting_cursor (void)
{
MSG msg;
BOOL bRet;
PostQuitMessage (0);
while ((bRet = GetMessage (&msg, 0, 0, 0)) != 0)
{
if (bRet == -1)
continue;
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
__declspec(dllexport) void __stdcall
g_win32_run_session_bus (void* hwnd, void* hinst, const char* cmdline, int cmdshow)
{
GDBusDaemon *daemon;
GMainLoop *loop;
const char *address;
GError *error = NULL;
turn_off_the_starting_cursor ();
if (g_getenv ("GDBUS_DAEMON_DEBUG") != NULL)
open_console_window ();
loop = g_main_loop_new (NULL, FALSE);
address = "nonce-tcp:";
daemon = _g_dbus_daemon_new (address, NULL, &error);
if (daemon == NULL)
{
g_printerr ("Can't init bus: %s\n", error->message);
g_error_free (error);
return;
}
/* There is a subtle detail with "idle-timeout" signal of dbus daemon:
* It is fired on idle after last client disconnection,
* but (at least with glib 2.59.1) it is NEVER fired
* if no clients connect to daemon at all.
* This may lead to infinite run of this daemon process.
*/
g_signal_connect (daemon, "idle-timeout", G_CALLBACK (idle_timeout_cb), loop);
if (publish_session_bus (_g_dbus_daemon_get_address (daemon)))
{
g_main_loop_run (loop);
unpublish_session_bus ();
}
g_main_loop_unref (loop);
g_object_unref (daemon);
}
gchar *
_g_dbus_win32_get_session_address_dbus_launch (GError **error)
{
HANDLE autolaunch_mutex, init_mutex;
char *address = NULL;
autolaunch_mutex = acquire_mutex (DBUS_AUTOLAUNCH_MUTEX);
init_mutex = acquire_mutex (UNIQUE_DBUS_INIT_MUTEX);
if (is_mutex_owned (DBUS_DAEMON_MUTEX))
address = read_shm (DBUS_DAEMON_ADDRESS_INFO);
release_mutex (init_mutex);
if (address == NULL)
{
/* rundll32 doesn't support neither spaces, nor quotes in cmdline:
* https://support.microsoft.com/en-us/help/164787/info-windows-rundll-and-rundll32-interface
* Since the dll path may have spaces, it is used as working directory,
* and the plain dll name is passed as argument to rundll32 like
* "C:\Windows\System32\rundll32.exe" .\libgio-2.0-0.dll,g_win32_run_session_bus
*
* Using relative path to dll rises a question if correct dll is loaded.
* According to docs if relative path like .\libgio-2.0-0.dll is passed
* the System32 directory may be searched before current directory:
* https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order#standard-search-order-for-desktop-applications
* Internally rundll32 uses "undefined behavior" parameter combination
* LoadLibraryExW(".\libgio-2.0-0.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
* Actual testing shows that if relative name starts from ".\"
* the current directory is searched before System32 (win7, win10 1607).
* So wrong dll would be loaded only if the BOTH of the following holds:
* - rundll32 will change so it would prefer system32 even for .\ paths
* - some app would place libgio-2.0-0.dll in system32 directory
*
* In point of that using .\libgio-2.0-0.dll looks fine.
*/
wchar_t gio_path[MAX_PATH + 2] = { 0 };
int gio_path_len = GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH + 1);
/* The <= MAX_PATH check prevents truncated path usage */
if (gio_path_len > 0 && gio_path_len <= MAX_PATH)
{
PROCESS_INFORMATION pi = { 0 };
STARTUPINFOW si = { 0 };
BOOL res = FALSE;
wchar_t rundll_path[MAX_PATH + 100] = { 0 };
wchar_t args[MAX_PATH*2 + 100] = { 0 };
/* calculate index of first char of dll file name inside full path */
int gio_name_index = gio_path_len;
for (; gio_name_index > 0; --gio_name_index)
{
wchar_t prev_char = gio_path[gio_name_index - 1];
if (prev_char == L'\\' || prev_char == L'/')
break;
}
GetWindowsDirectoryW (rundll_path, MAX_PATH);
wcscat (rundll_path, L"\\rundll32.exe");
if (GetFileAttributesW (rundll_path) == INVALID_FILE_ATTRIBUTES)
{
GetSystemDirectoryW (rundll_path, MAX_PATH);
wcscat (rundll_path, L"\\rundll32.exe");
}
wcscpy (args, L"\"");
wcscat (args, rundll_path);
wcscat (args, L"\" .\\");
wcscat (args, gio_path + gio_name_index);
#if defined(_WIN64) || defined(_M_X64) || defined(_M_AMD64)
wcscat (args, L",g_win32_run_session_bus");
#elif defined (_MSC_VER)
wcscat (args, L",_g_win32_run_session_bus@16");
#else
wcscat (args, L",g_win32_run_session_bus@16");
#endif