fix(appimage): launch external processes with bundled linker

This ensures that external processes (including our updater and
Minecraft itself) maintain the same compatibility guarantees as the main
binary

Signed-off-by: Seth Flynn <getchoo@tuta.io>
This commit is contained in:
Seth Flynn
2025-12-04 09:57:01 -05:00
parent 92738feeba
commit c305ed4506
6 changed files with 105 additions and 32 deletions

View File

@@ -26,7 +26,6 @@
#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QProcess>
#include <QProgressDialog>
#include <QSettings>
#include <QTimer>
@@ -35,6 +34,7 @@
#include "StringUtils.h"
#include "BuildConfig.h"
#include "FileSystem.h"
#include "ui/dialogs/UpdateAvailableDialog.h"
@@ -97,14 +97,9 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
progress.show();
QCoreApplication::processEvents();
QProcess proc;
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
#if defined Q_OS_WIN32
exe_name.append(".exe");
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc.setProcessEnvironment(env);
#else
exe_name = QString("bin/%1").arg(exe_name);
#endif
@@ -113,15 +108,21 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
if (priv->allowBeta)
args.append("--pre-release");
proc.start(priv->appDir.absoluteFilePath(exe_name), args);
auto result_start = proc.waitForStarted(5000);
auto proc = FS::createProcess(priv->appDir.absoluteFilePath(exe_name), args);
#if defined Q_OS_WIN32
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc->setProcessEnvironment(env);
#endif
proc->start(proc->program(), proc->arguments());
auto result_start = proc->waitForStarted(5000);
if (!result_start) {
auto err = proc.error();
auto err = proc->error();
qDebug() << "Failed to start updater after 5 seconds."
<< "reason:" << err << proc.errorString();
<< "reason:" << err << proc->errorString();
auto msgBox =
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
tr("Failed to start after 5 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
msgBox.exec();
@@ -133,16 +134,16 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
}
QCoreApplication::processEvents();
auto result_finished = proc.waitForFinished(60000);
auto result_finished = proc->waitForFinished(60000);
if (!result_finished) {
proc.kill();
auto err = proc.error();
auto output = proc.readAll();
proc->kill();
auto err = proc->error();
auto output = proc->readAll();
qDebug() << "Updater failed to close after 60 seconds."
<< "reason:" << err << proc.errorString();
<< "reason:" << err << proc->errorString();
auto msgBox =
QMessageBox(QMessageBox::Information, tr("Update Check Failed"),
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc.errorString()), QMessageBox::Ok, priv->parent);
tr("Updater failed to close 60 seconds\nReason: %1.").arg(proc->errorString()), QMessageBox::Ok, priv->parent);
msgBox.setDetailedText(output);
msgBox.setMinimumWidth(460);
msgBox.adjustSize();
@@ -154,10 +155,10 @@ void PrismExternalUpdater::checkForUpdates(bool triggeredByUser)
return;
}
auto exit_code = proc.exitCode();
auto exit_code = proc->exitCode();
auto std_output = proc.readAllStandardOutput();
auto std_error = proc.readAllStandardError();
auto std_output = proc->readAllStandardOutput();
auto std_error = proc->readAllStandardError();
progress.hide();
QCoreApplication::processEvents();
@@ -335,14 +336,9 @@ void PrismExternalUpdater::offerUpdate(const QString& version_name, const QStrin
void PrismExternalUpdater::performUpdate(const QString& version_tag)
{
QProcess proc;
auto exe_name = QStringLiteral("%1_updater").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
#if defined Q_OS_WIN32
exe_name.append(".exe");
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc.setProcessEnvironment(env);
#else
exe_name = QString("bin/%1").arg(exe_name);
#endif
@@ -351,9 +347,16 @@ void PrismExternalUpdater::performUpdate(const QString& version_tag)
if (priv->allowBeta)
args.append("--pre-release");
auto result = proc.startDetached(priv->appDir.absoluteFilePath(exe_name), args);
auto proc = FS::createProcess(exe_name, args);
#if defined Q_OS_WIN32
auto env = QProcessEnvironment::systemEnvironment();
env.insert("__COMPAT_LAYER", "RUNASINVOKER");
proc->setProcessEnvironment(env);
#endif
auto result = proc->startDetached(priv->appDir.absoluteFilePath(exe_name), args);
if (!result) {
qDebug() << "Failed to start updater:" << proc.error() << proc.errorString();
qDebug() << "Failed to start updater:" << proc->error() << proc->errorString();
}
QCoreApplication::exit();
}

View File

@@ -188,7 +188,19 @@ PrismUpdaterApp::PrismUpdaterApp(int& argc, char** argv) : QApplication(argc, ar
m_allowPreRelease = parser.isSet("pre-release");
QString origCwdPath = QDir::currentPath();
#if defined(Q_OS_LINUX)
// NOTE(@getchoo): In order for `go-appimage` to generate self-contained AppImages, it executes apps from a bundled linker at
// <root>/lib64
// This is not the path to our actual binary, which we want
QString binPath;
if (DesktopServices::isSelfContained()) {
binPath = FS::PathCombine(applicationDirPath(), "../usr/bin");
} else {
binPath = applicationDirPath();
}
#else
QString binPath = applicationDirPath();
#endif
{ // find data director
// Root path is used for updates and portable data
@@ -809,13 +821,16 @@ QFileInfo PrismUpdaterApp::downloadAsset(const GitHubReleaseAsset& asset)
bool PrismUpdaterApp::callAppImageUpdate()
{
auto appimage_path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE"));
QProcess proc = QProcess();
qDebug() << "Calling: AppImageUpdate" << appimage_path;
proc.setProgram(FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage"));
proc.setArguments({ appimage_path });
auto result = proc.startDetached();
const auto program = FS::PathCombine(m_rootPath, "bin", "AppImageUpdate.AppImage");
auto proc = FS::createProcess(program, { appimage_path });
if (!proc) {
qCritical() << "Unable to create process:" << program;
return false;
}
auto result = proc->startDetached();
if (!result)
qDebug() << "Failed to start AppImageUpdate reason:" << proc.errorString();
qDebug() << "Failed to start AppImageUpdate reason:" << proc->errorString();
return result;
}