[mojosetup] [3/3] Prevent GUI plugins from intefering with each other.
Francois Gouget
fgouget at codeweavers.com
Mon Aug 10 13:38:35 EDT 2020
The GTK 2 and GTK 3 plugins cannot cannot coexist in the same process
so the corresponding plugins must be unloaded as soon as we have their
priority. But even unloading + reloading GTK 2 or GTK 3 can lead to
random crashes.
So hold the GUI plugin tryouts in a subprocess.
Since this is a platform-specific operation add the necessary
infrastructure in platform_{unix,windows}.c.
Reuse MojoSetup_loadGuiPlugin() to simplify initGuiPluginsByPriority().
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
Unlike the previous patch this could be considered to be a bug in GTK 2
/ GTK 3. In practice expecting it to be possible to dlopen() + dlclose()
libgtk2 / libgtk3 at will is unrealistic and in the current situation
just leads to random (but frequent) crashes on startup.
gui.c | 82 ++++++++++++++++++++--------------------------
platform.h | 10 ++++++
platform_unix.c | 37 ++++++++++++++++++++-
platform_windows.c | 11 +++++++
4 files changed, 92 insertions(+), 48 deletions(-)
diff --git a/gui.c b/gui.c
index 8feda49..e81e993 100644
--- a/gui.c
+++ b/gui.c
@@ -76,6 +76,21 @@ static MojoGuiPluginPriority calcGuiPriority(const MojoGui *gui)
} // calcGuiPriority
+const MojoGui *MojoSetup_loadGuiPlugin(const uint8 *img, uint32 len, void **lib)
+{
+ *lib = MojoPlatform_dlopen(img, len);
+ if (*lib != NULL)
+ {
+ void *addr = MojoPlatform_dlsym(*lib, MOJOGUI_ENTRY_POINT_STR);
+ MojoGuiEntryPoint entry = (MojoGuiEntryPoint) addr;
+ if (entry != NULL)
+ return entry(MOJOGUI_INTERFACE_REVISION, &GEntryPoints);
+ } // if
+
+ return NULL;
+} // MojoSetup_loadGuiPlugin
+
+
static PluginList *initGuiPluginsByPriority(PluginList *plugins)
{
MojoGuiPluginPriority p;
@@ -88,16 +103,7 @@ static PluginList *initGuiPluginsByPriority(PluginList *plugins)
continue;
if (i->img != NULL)
- {
- i->lib = MojoPlatform_dlopen(i->img, i->imglen);
- if (i->lib != NULL)
- {
- void *addr = MojoPlatform_dlsym(i->lib, MOJOGUI_ENTRY_POINT_STR);
- MojoGuiEntryPoint entry = (MojoGuiEntryPoint) addr;
- if (entry != NULL)
- i->gui = entry(MOJOGUI_INTERFACE_REVISION, &GEntryPoints);
- } // if
- } // if
+ i->gui = MojoSetup_loadGuiPlugin(i->img, i->imglen, &i->lib);
if (i->gui && i->gui->init())
{
@@ -132,22 +138,17 @@ static void deleteGuiPlugin(PluginList *plugin)
} // deleteGuiPlugin
-static PluginList *tryGuiPlugin(PluginList *plugins, MojoGuiEntryPoint entry)
+static PluginList *addGuiPlugin(PluginList *plugins, const MojoGui *gui, MojoGuiPluginPriority priority)
{
- PluginList *plug = NULL;
- const MojoGui *gui = entry(MOJOGUI_INTERFACE_REVISION, &GEntryPoints);
- if (gui != NULL)
- {
- plug = xmalloc(sizeof (PluginList));
- plug->lib = NULL;
- plug->gui = gui;
- plug->priority = calcGuiPriority(gui);
- plug->next = plugins->next;
- plugins->next = plug;
- } // if
+ PluginList *plug = xmalloc(sizeof (PluginList));
+ plug->lib = NULL;
+ plug->gui = gui;
+ plug->priority = priority;
+ plug->next = plugins->next;
+ plugins->next = plug;
return plug;
-} // tryGuiPlugin
+} // addGuiPlugin
static void loadStaticGuiPlugins(PluginList *plugins)
@@ -155,8 +156,11 @@ static void loadStaticGuiPlugins(PluginList *plugins)
int i;
for (i = 0; staticGui[i].entry != NULL; i++)
{
+ const MojoGui *gui;
logInfo("Testing static GUI plugin %0", staticGui[i].name);
- tryGuiPlugin(plugins, staticGui[i].entry);
+ gui = staticGui[i].entry(MOJOGUI_INTERFACE_REVISION, &GEntryPoints);
+ if (gui != NULL)
+ addGuiPlugin(plugins, gui, calcGuiPriority(gui));
}
} // loadStaticGuiPlugins
@@ -167,37 +171,21 @@ static boolean loadDynamicGuiPlugin(PluginList *plugins, MojoArchive *ar)
MojoInput *io = ar->openCurrentEntry(ar);
if (io != NULL)
{
- void *lib = NULL;
const uint32 imglen = (uint32) io->length(io);
uint8 *img = (uint8 *) xmalloc(imglen);
const uint32 br = (uint32) io->read(io, img, imglen);
io->close(io);
if (br == imglen)
{
- lib = MojoPlatform_dlopen(img, imglen);
- if (lib != NULL)
+ MojoGuiPluginPriority priority;
+ priority = MojoPlatform_getGuiPriority(img, imglen);
+ if (priority != MOJOGUI_PRIORITY_NEVER_TRY)
{
- void *addr = MojoPlatform_dlsym(lib, MOJOGUI_ENTRY_POINT_STR);
- MojoGuiEntryPoint entry = (MojoGuiEntryPoint) addr;
- if (entry != NULL)
- {
- plug = tryGuiPlugin(plugins, entry);
- if (plug)
- {
- plug->img = img;
- plug->imglen = imglen;
- } // if
- } // if
-
- // always close, because GTK+2 and GTK+3 can't coexist.
- // we'll reload them when trying them!
- MojoPlatform_dlclose(lib);
- plug->gui = NULL;
- } // if
+ plug = addGuiPlugin(plugins, NULL, priority);
+ plug->img = img;
+ plug->imglen = imglen;
+ }
} // if
-
- if (!plug)
- free(img);
} // if
return plug != NULL;
diff --git a/platform.h b/platform.h
index 8ebe6ad..65cccbc 100644
--- a/platform.h
+++ b/platform.h
@@ -10,6 +10,7 @@
#define _INCL_PLATFORM_H_
#include "universal.h"
+#include "gui.h"
#ifdef __cplusplus
extern "C" {
@@ -18,6 +19,10 @@ extern "C" {
// this is called by your mainline.
int MojoSetup_main(int argc, char **argv);
+// Loads the specified dynamic plugin and returns its library handle (see
+// MojoPlatform_dlopen()) and GUI entry point table.
+const MojoGui *MojoSetup_loadGuiPlugin(const uint8 *img, uint32 imglen, void **lib);
+
// Caller must free returned string!
char *MojoPlatform_appBinaryPath(void);
@@ -225,6 +230,11 @@ void *MojoPlatform_dlopen(const uint8 *img, size_t len);
void *MojoPlatform_dlsym(void *lib, const char *sym);
void MojoPlatform_dlclose(void *lib);
+// Temporarily load a GUI plugin (see MojoPlatform_dlopen()) and returns its
+// priority. This may be called many times and, if necessary, steps should
+// be taken so the plugins don't interfere with each other.
+MojoGuiPluginPriority MojoPlatform_getGuiPriority(const uint8 *img, size_t len);
+
// Launch the user's preferred browser to view the URL (url).
// Returns true if the browser launched, false otherwise. We can't know
// if the URL actually loaded, just if the browser launched. The hope is that
diff --git a/platform_unix.c b/platform_unix.c
index 230fb36..9046b7f 100644
--- a/platform_unix.c
+++ b/platform_unix.c
@@ -62,7 +62,6 @@ void beos_usleep(unsigned long ticks);
#endif
#include "platform.h"
-#include "gui.h"
#include "fileio.h"
static struct timeval startup_time;
@@ -1002,6 +1001,42 @@ void MojoPlatform_dlclose(void *lib)
dlclose(lib);
} // MojoPlatform_dlclose
+MojoGuiPluginPriority MojoPlatform_getGuiPriority(const uint8 *img, size_t len)
+{
+ MojoGuiPluginPriority priority = MOJOGUI_PRIORITY_NEVER_TRY;
+
+ // GTK+2 and GTK+3 cannot coexist in the same process so the corresponding
+ // plugins must be unloaded as soon as we have their priority. But even
+ // unloading + reloading GTK+2 or GTK+3 can lead to random crashes.
+ // So hold the GUI plugin tryouts in a subprocess.
+ pid_t pid = fork();
+ if (pid == 0)
+ {
+ void *lib;
+ const MojoGui *gui = MojoSetup_loadGuiPlugin(img, len, &lib);
+ if (gui)
+ priority = gui->priority(MojoPlatform_istty());
+ // some libraries react badly to dlclose() so don't bother before
+ // exit()
+ exit(priority);
+ }
+ else if (pid == -1) // -1 == fork() failed.
+ logError("Unable to fork the test child: %0", strerror(errno));
+ else
+ {
+ int status;
+ pid_t rc;
+ rc = waitpid(pid, &status, 0);
+ if (rc <= 0)
+ logError("Unable to wait for the test child: %0", strerror(errno));
+ else if (rc == pid && WIFEXITED(status))
+ priority = WEXITSTATUS(status);
+ else
+ logInfo("The test child failed");
+ }
+
+ return priority;
+} // MojoPlatform_getGuiPriority
static int runScriptString(const char *str, boolean devnull, const char **_argv)
{
diff --git a/platform_windows.c b/platform_windows.c
index 8fa5467..4f60836 100644
--- a/platform_windows.c
+++ b/platform_windows.c
@@ -1511,6 +1511,17 @@ void MojoPlatform_dlclose(void *_lib)
} // if
} // MojoPlatform_dlclose
+MojoGuiPluginPriority MojoPlatform_getGuiPriority(const uint8 *img, size_t len)
+{
+ void *lib;
+ MojoGui *gui = MojoSetup_loadGuiPlugin(img, len, &lib);
+ MojoGuiPluginPriority priority = MOJOGUI_PRIORITY_NEVER_TRY;
+ if (gui)
+ priority = gui->priority(MojoPlatform_istty());
+ if (lib)
+ MojoSetup_dlclose(lib);
+ return priority;
+} // MojoPlatform_getGuiPriority
uint64 MojoPlatform_getuid(void)
{
--
2.20.1
More information about the mojosetup
mailing list