dlopen edge case for non-multi-instance apps avoided.

This commit is contained in:
Scott Duensing 2026-03-18 01:27:38 -05:00
parent fc3a513ada
commit 1e59499529

View file

@ -32,7 +32,7 @@ static void appTaskWrapper(void *arg);
static const char *baseName(const char *path); static const char *baseName(const char *path);
static void cleanupTempFile(ShellAppT *app); static void cleanupTempFile(ShellAppT *app);
static int32_t copyFile(const char *src, const char *dst); static int32_t copyFile(const char *src, const char *dst);
static bool isPathLoaded(const char *path); static ShellAppT *findLoadedPath(const char *path);
static int32_t makeTempPath(const char *origPath, int32_t id, char *out, int32_t outSize); static int32_t makeTempPath(const char *origPath, int32_t id, char *out, int32_t outSize);
void shellAppInit(void); void shellAppInit(void);
void shellForceKillApp(AppContextT *ctx, ShellAppT *app); void shellForceKillApp(AppContextT *ctx, ShellAppT *app);
@ -135,15 +135,16 @@ static int32_t copyFile(const char *src, const char *dst) {
} }
// Check if a DXE path is already loaded in any active slot. // Find an active slot with the given DXE path. Returns the slot pointer,
static bool isPathLoaded(const char *path) { // or NULL if no running app was loaded from this path.
static ShellAppT *findLoadedPath(const char *path) {
for (int32_t i = 1; i < SHELL_MAX_APPS; i++) { for (int32_t i = 1; i < SHELL_MAX_APPS; i++) {
if (sApps[i].state != AppStateFreeE && strcmp(sApps[i].path, path) == 0) { if (sApps[i].state != AppStateFreeE && strcmp(sApps[i].path, path) == 0) {
return true; return &sApps[i];
} }
} }
return false; return NULL;
} }
@ -264,29 +265,23 @@ int32_t shellLoadApp(AppContextT *ctx, const char *path) {
return -1; return -1;
} }
// Check if this DXE is already loaded. If so, we need to inspect its // Check if this DXE is already loaded. If so, check whether the app
// descriptor to decide whether to allow a second instance. // allows multiple instances. We read the descriptor from the existing
// slot directly — no need to dlopen again.
const char *loadPath = path; const char *loadPath = path;
char tempPath[260] = {0}; char tempPath[260] = {0};
ShellAppT *existing = findLoadedPath(path);
if (isPathLoaded(path)) { if (existing) {
// Peek at the descriptor from the already-loaded module to check // Read multiInstance from the already-loaded descriptor
// multiInstance. Since it's already loaded, dlopen returns the AppDescriptorT *existDesc = (AppDescriptorT *)dlsym(existing->dxeHandle, "_appDescriptor");
// existing handle (cheap, no actual reload).
void *peek = dlopen(path, RTLD_GLOBAL);
if (peek) { if (!existDesc || !existDesc->multiInstance) {
AppDescriptorT *peekDesc = (AppDescriptorT *)dlsym(peek, "_appDescriptor");
bool allow = peekDesc && peekDesc->multiInstance;
dlclose(peek);
if (!allow) {
char msg[320]; char msg[320];
snprintf(msg, sizeof(msg), "%s is already running.", baseName(path)); snprintf(msg, sizeof(msg), "%s is already running.", baseName(path));
dvxMessageBox(ctx, "Error", msg, MB_OK | MB_ICONERROR); dvxMessageBox(ctx, "Error", msg, MB_OK | MB_ICONERROR);
return -1; return -1;
} }
}
// Multi-instance allowed: copy to a temp file so dlopen gets // Multi-instance allowed: copy to a temp file so dlopen gets
// an independent code+data image. // an independent code+data image.