[glib: 1/5] Fix process spawning with static build on Windows




commit 41f8bbd02d3fdcd75899ff37954d993509a76c68
Author: Loic Le Page <llepage fluendo com>
Date:   Wed Jan 5 18:06:01 2022 +0100

    Fix process spawning with static build on Windows
    
    On Windows, process spawning needs an external helper exe which is found
    relatively to the glib DLL file. If glib has been built statically this
    file doesn't exist anymore and reference path is not the DLL path
    anymore but the current executable path.
    
    This patch searches for the helper exe taking as starting point the
    current executable path, relative 'bin', 'lib', 'glib' and 'gio' folders
    and then gets one level up until the root path. If this search doesn't
    give result then the helper exe is searched using the PATH variable.

 glib/glib-init.h    |   2 +
 glib/glib-private.c |   1 +
 glib/glib-private.h |   5 ++-
 glib/gspawn-win32.c |  16 ++-----
 glib/gutils.c       |  41 ------------------
 glib/gwin32.c       | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 130 insertions(+), 54 deletions(-)
---
diff --git a/glib/glib-init.h b/glib/glib-init.h
index 4c812d9d6..b77164c73 100644
--- a/glib/glib-init.h
+++ b/glib/glib-init.h
@@ -40,7 +40,9 @@ void g_clock_win32_init (void);
 void g_crash_handler_win32_init (void);
 void g_crash_handler_win32_deinit (void);
 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);
 #endif
 
 #endif /* __GLIB_INIT_H__ */
diff --git a/glib/glib-private.c b/glib/glib-private.c
index 1c0da1947..0a59c6f16 100644
--- a/glib/glib-private.c
+++ b/glib/glib-private.c
@@ -54,6 +54,7 @@ glib__private__ (void)
     g_win32_lstat_utf8,
     g_win32_readlink_utf8,
     g_win32_fstat,
+    g_win32_find_helper_executable_path,
 #endif
   };
 
diff --git a/glib/glib-private.h b/glib/glib-private.h
index 85987e272..943252f1b 100644
--- a/glib/glib-private.h
+++ b/glib/glib-private.h
@@ -120,7 +120,6 @@ gboolean                g_check_setuid                  (void);
 GMainContext *          g_main_context_new_with_next_id (guint next_id);
 
 #ifdef G_OS_WIN32
-gchar *_glib_get_dll_directory (void);
 GLIB_AVAILABLE_IN_ALL
 gchar *_glib_get_locale_dir    (void);
 #endif
@@ -168,6 +167,10 @@ typedef struct {
 
   int                   (* g_win32_fstat)               (int                 fd,
                                                          GWin32PrivateStat  *buf);
+
+  /* See gwin32.c */
+  gchar *(*g_win32_find_helper_executable_path) (const gchar *process_name,
+                                                 void *dll_handle);
 #endif
 
 
diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c
index 182928cb4..b43778156 100644
--- a/glib/gspawn-win32.c
+++ b/glib/gspawn-win32.c
@@ -42,10 +42,11 @@
 
 #include "config.h"
 
-#include "glib.h"
+#include "glib-init.h"
 #include "glib-private.h"
-#include "gprintfint.h"
+#include "glib.h"
 #include "glibintl.h"
+#include "gprintfint.h"
 #include "gspawn-private.h"
 #include "gthread.h"
 
@@ -586,7 +587,6 @@ fork_exec (gint                  *exit_status,
   gint conv_error_index;
   gchar *helper_process;
   wchar_t *whelper, **wargv, **wenvp;
-  gchar *glib_dll_directory;
   int stdin_pipe[2] = { -1, -1 };
   int stdout_pipe[2] = { -1, -1 };
   int stderr_pipe[2] = { -1, -1 };
@@ -651,16 +651,8 @@ fork_exec (gint                  *exit_status,
     helper_process = HELPER_PROCESS "-console.exe";
   else
     helper_process = HELPER_PROCESS ".exe";
-  
-  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);
 
+  helper_process = g_win32_find_helper_executable_path (helper_process, glib_dll);
   new_argv[0] = protect_argv_string (helper_process);
 
   _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
diff --git a/glib/gutils.c b/glib/gutils.c
index 6cc450607..c6aec9e6f 100644
--- a/glib/gutils.c
+++ b/glib/gutils.c
@@ -104,47 +104,6 @@
 #include <langinfo.h>
 #endif
 
-#ifdef G_PLATFORM_WIN32
-
-gchar *
-_glib_get_dll_directory (void)
-{
-  gchar *retval;
-  gchar *p;
-  wchar_t wc_fn[MAX_PATH];
-
-#ifdef DLL_EXPORT
-  if (glib_dll == NULL)
-    return NULL;
-#endif
-
-  /* This code is different from that in
-   * g_win32_get_package_installation_directory_of_module() in that
-   * here we return the actual folder where the GLib DLL is. We don't
-   * do the check for it being in a "bin" or "lib" subfolder and then
-   * returning the parent of that.
-   *
-   * In a statically built GLib, glib_dll will be NULL and we will
-   * thus look up the application's .exe file's location.
-   */
-  if (!GetModuleFileNameW (glib_dll, wc_fn, MAX_PATH))
-    return NULL;
-
-  retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL);
-
-  p = strrchr (retval, G_DIR_SEPARATOR);
-  if (p == NULL)
-    {
-      /* Wtf? */
-      return NULL;
-    }
-  *p = '\0';
-
-  return retval;
-}
-
-#endif
-
 /**
  * g_memmove: 
  * @dest: the destination address to copy the bytes to.
diff --git a/glib/gwin32.c b/glib/gwin32.c
index e2f73e135..b2b5ff69d 100644
--- a/glib/gwin32.c
+++ b/glib/gwin32.c
@@ -1336,4 +1336,123 @@ g_crash_handler_win32_deinit (void)
   WinVEH_handle = NULL;
 }
 
+/**
+ * g_win32_find_helper_executable_path:
+ * @executable_name: (transfer none): name of the helper executable to find
+ * (something like gspawn-win64-helper.exe or gdbus.exe for example).
+ * @dll_handle: handle of the DLL to use as searching base path. Pass NULL
+ * to take current process executable as searching base path.
+ *
+ * Find an external executable path and name starting in the same folder
+ * as a specified DLL or current process executable path. Helper executables
+ * (like gspawn-win64-helper.exe, gspawn-win64-helper-console.exe or
+ * gdbus.exe for example) are generally installed in the same folder as the
+ * corresponding DLL file.
+ *
+ * So, if package has been correctly installed, with a dynamic build of GLib,
+ * the helper executable should be in the same directory as the corresponding
+ * DLL file and searching should be straightforward.
+ *
+ * But if built statically, DLL handle is not available and we have to start
+ * searching from the directory holding current executable. It may be very
+ * different from the directory containing the helper program. In order to
+ * find the right helper program automatically in all common situations, we
+ * use this pattern:
+ *
+ * current directory
+ *             |-- ???
+ *             |-- bin
+ *             |    |-- ???
+ *             |-- lib
+ *             |    |-- ???
+ *             |-- glib
+ *             |    |-- ???
+ *             |-- gio
+ *                  |-- ???
+ *
+ * starting at base searching path (DLL or current executable directory) and
+ * getting up until the root path. If we cannot still find the helper program,
+ * we'll rely on PATH as the last resort.
+ *
+ * Returns: (transfer full) (type filename) (nullable): the helper executable
+ * path and name in the GLib filename encoding or NULL in case of error. It
+ * should be deallocated with g_free().
+ */
+gchar *
+g_win32_find_helper_executable_path (const gchar *executable_name, void *dll_handle)
+{
+  static const gchar *const subdirs[] = { "", "bin", "lib", "glib", "gio" };
+  static const gsize nb_subdirs = G_N_ELEMENTS (subdirs);
+
+  DWORD module_path_len;
+  wchar_t module_path[MAX_PATH + 2] = { 0 };
+  gchar *base_searching_path;
+  gchar *p;
+  gchar *executable_path;
+  gsize i;
+
+  g_return_val_if_fail (executable_name && *executable_name, NULL);
+
+  module_path_len = GetModuleFileNameW (dll_handle, module_path, MAX_PATH + 1);
+  /* The > MAX_PATH check prevents truncated module path usage */
+  if (module_path_len == 0 || module_path_len > MAX_PATH)
+    return NULL;
+
+  base_searching_path = g_utf16_to_utf8 (module_path, -1, NULL, NULL, NULL);
+  if (base_searching_path == NULL)
+    return NULL;
+
+  p = strrchr (base_searching_path, G_DIR_SEPARATOR);
+  if (p == NULL)
+    {
+      g_free (base_searching_path);
+      return NULL;
+    }
+  *p = '\0';
+
+  for (;;)
+    {
+      /* Search in subdirectories */
+      for (i = 0; i < nb_subdirs; ++i)
+        {
+          /* As this function is exclusively used on Windows, the
+           * executable_path is always an absolute path. At worse, when
+           * reaching the root of the filesystem, base_searching_path may
+           * equal something like "[Drive letter]:" but never "/" like on
+           * Linux or Mac.
+           * For the peace of mind we still assert this, just in case that
+           * one day someone tries to use this function on Linux or Mac.
+           */
+          executable_path = g_build_filename (base_searching_path, subdirs[i], executable_name, NULL);
+          g_assert (g_path_is_absolute (executable_path));
+          if (g_file_test (executable_path, G_FILE_TEST_IS_REGULAR))
+            break;
+
+          g_free (executable_path);
+          executable_path = NULL;
+        }
+
+      if (executable_path != NULL)
+        break;
+
+      /* Let's get one directory level up */
+      p = strrchr (base_searching_path, G_DIR_SEPARATOR);
+      if (p == NULL)
+        break;
+
+      *p = '\0';
+    }
+  g_free (base_searching_path);
+
+  if (executable_path == NULL)
+    {
+      /* Search in system PATH */
+      executable_path = g_find_program_in_path (executable_name);
+      if (executable_path == NULL)
+        executable_path = g_strdup (executable_name);
+    }
+
+  return executable_path;
+}
+
 #endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]