From 51419a3aa4f037869e2c9dd52377277bc4695010 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sun, 31 May 2026 13:20:21 -0500 Subject: fix updater restart and log cleanup --- docker/build-inner.sh | 34 ++++++++++- libs/plugin_python/src/proxy/CMakeLists.txt | 2 - src/src/mainwindow.cpp | 2 + src/src/organizercore.cpp | 91 +++++++++++++++++++++++++++++ src/src/organizercore.h | 12 ++-- src/src/settingsdialogupdates.cpp | 41 +++++++++---- 6 files changed, 163 insertions(+), 19 deletions(-) diff --git a/docker/build-inner.sh b/docker/build-inner.sh index a392645..9c05783 100755 --- a/docker/build-inner.sh +++ b/docker/build-inner.sh @@ -173,6 +173,8 @@ SKIP_PATTERN="linux-vdso|ld-linux|libc\.so|libm\.so|libdl\.so|librt\.so|libpthre SKIP_PATTERN="${SKIP_PATTERN}|libGL\.so|libEGL|libGLX|libGLdispatch|libdrm|libvulkan|libX11|libxcb|libwayland-client|libwayland-server|libwayland-cursor|libwayland-egl|libxkbcommon" # libpython — user provides via system Python; do not bundle. SKIP_PATTERN="${SKIP_PATTERN}|libpython" +# OpenSSL should come from the host so we don't pin users to a stale TLS stack. +SKIP_PATTERN="${SKIP_PATTERN}|libssl\.so|libcrypto\.so" collect_deps() { ldd "$1" 2>/dev/null | grep "=>" | awk '{print $3}' | grep "^/" | sort -u @@ -211,6 +213,31 @@ for _xcb_cursor in /lib/x86_64-linux-gnu/libxcb-cursor.so* \ echo "Bundled ${_xcb_cursor}" done +# xdg-mime's KDE path calls an unversioned `qtpaths` helper. Bundle it so +# users do not need distro Qt tools packages just to register nxm:// links. +QT6_BIN_DIR="" +for _candidate in \ + "${Qt6_DIR:-}/bin" \ + "/opt/qt6/6.11.1/gcc_64/bin" \ + "/usr/lib/qt6/bin"; do + if [ -d "${_candidate}" ]; then + QT6_BIN_DIR="${_candidate}" + break + fi +done +for _qtpaths in \ + "${QT6_BIN_DIR}/qtpaths" \ + "${QT6_BIN_DIR}/qtpaths6" \ + "$(command -v qtpaths 2>/dev/null || true)" \ + "$(command -v qtpaths6 2>/dev/null || true)"; do + if [ -n "${_qtpaths}" ] && [ -x "${_qtpaths}" ]; then + cp -Lf "${_qtpaths}" "${OUT_DIR}/lib/qtpaths" + chmod +x "${OUT_DIR}/lib/qtpaths" + echo "Bundled qtpaths from ${_qtpaths}" + break + fi +done + # ── Qt6 platform plugins ── # Prefer aqtinstall location (Docker), then system, then qtpaths6 fallback. QT6_PLUGIN_DIR="" @@ -530,6 +557,11 @@ if [ "${HERE_REAL}" != "${DST_REAL}" ]; then exit 1 fi + # Do not retain stale bundled OpenSSL runtimes from older releases. + # TLS should resolve against the host so certificate handling can be + # updated by the OS instead of being pinned to our old package. + rm -f "${BIN_DST}"/lib/libssl.so* "${BIN_DST}"/lib/libcrypto.so* 2>/dev/null || true + # Refresh the manifest at the destination so the next update has it. cp -af "${MANIFEST}" "${BIN_DST}/fluorine-manifest.txt" echo "${CURRENT_VER}" > "${MARKER}" @@ -560,7 +592,7 @@ fi # Run from the synced location. RUN="${BIN_DST}" -export PATH="${RUN}:${PATH}" +export PATH="${RUN}/lib:${RUN}:${PATH}" # Steam game mode injects its scout/soldier runtime into LD_LIBRARY_PATH. # Those old libraries (libssl, libz, etc.) break Python extension modules # and Qt internals that don't have RPATH pointing to our bundled libs. diff --git a/libs/plugin_python/src/proxy/CMakeLists.txt b/libs/plugin_python/src/proxy/CMakeLists.txt index f7e6f31..c666b6c 100644 --- a/libs/plugin_python/src/proxy/CMakeLists.txt +++ b/libs/plugin_python/src/proxy/CMakeLists.txt @@ -49,8 +49,6 @@ if(WIN32) file(GLOB dlls_to_install ${Python_HOME}/dlls/libffi*.dll ${Python_HOME}/dlls/sqlite*.dll - ${Python_HOME}/dlls/libssl*.dll - ${Python_HOME}/dlls/libcrypto*.dll ${Python_HOME}/python${Python_VERSION_MAJOR}*.dll) install(FILES ${dlls_to_install} DESTINATION ${DLL_DIRS}) endif() diff --git a/src/src/mainwindow.cpp b/src/src/mainwindow.cpp index f8f7e97..ca92499 100644 --- a/src/src/mainwindow.cpp +++ b/src/src/mainwindow.cpp @@ -1457,6 +1457,8 @@ void MainWindow::paintEvent(QPaintEvent* event) void MainWindow::onBeforeClose() { storeSettings(); + m_ArchiveListWriter.writeImmediately(true); + m_OrganizerCore.pluginsWriter().writeImmediately(true); } void MainWindow::closeEvent(QCloseEvent* event) diff --git a/src/src/organizercore.cpp b/src/src/organizercore.cpp index d8e6da4..3721cf4 100644 --- a/src/src/organizercore.cpp +++ b/src/src/organizercore.cpp @@ -52,9 +52,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include #include @@ -91,6 +94,41 @@ using namespace MOBase; static env::CoreDumpTypes g_coreDumpType = env::CoreDumpTypes::Mini; +namespace +{ + +QString uniqueFilePath(const QDir& dir, const QString& fileName) +{ + QString candidate = dir.filePath(fileName); + if (!QFileInfo::exists(candidate)) { + return candidate; + } + + const QFileInfo info(fileName); + const QString base = info.completeBaseName(); + const QString suffix = info.suffix(); + const QString timestamp = + QDateTime::currentDateTime().toString(QStringLiteral("yyyyMMdd_hhmmss")); + + for (int i = 1; i < 1000; ++i) { + const QString numbered = + suffix.isEmpty() + ? QStringLiteral("%1_%2_%3").arg(base, timestamp).arg(i) + : QStringLiteral("%1_%2_%3.%4").arg(base, timestamp).arg(i).arg(suffix); + candidate = dir.filePath(numbered); + if (!QFileInfo::exists(candidate)) { + return candidate; + } + } + + return dir.filePath( + suffix.isEmpty() + ? QStringLiteral("%1_%2").arg(base, timestamp) + : QStringLiteral("%1_%2.%3").arg(base, timestamp, suffix)); +} + +} // namespace + template QStringList toStringList(InputIterator current, InputIterator end) { @@ -756,6 +794,58 @@ void OrganizerCore::prepareVFS() void OrganizerCore::unmountVFS() { m_USVFS.unmount(); + movePGPatcherLogsToLogsFolder(); +} + +void OrganizerCore::movePGPatcherLogsToLogsFolder() +{ + const QString dataPath = qApp->property("dataPath").toString(); + if (dataPath.isEmpty()) { + log::warn("PGPatcher log cleanup skipped: dataPath is not set"); + return; + } + + QDir logsDir(QDir(dataPath).filePath(QString::fromStdWString(AppConfig::logPath()))); + if (!logsDir.exists() && !QDir().mkpath(logsDir.absolutePath())) { + log::warn("PGPatcher log cleanup skipped: failed to create '{}'", + logsDir.absolutePath()); + return; + } + + const QStringList roots = { + QDir::fromNativeSeparators(m_Settings.paths().overwrite()), + QDir::fromNativeSeparators(m_Settings.paths().mods()), + }; + + int moved = 0; + for (const QString& root : roots) { + if (root.isEmpty() || !QDir(root).exists()) { + continue; + } + + QDirIterator it(root, QStringList{QStringLiteral("PGPatcher.log")}, + QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, + QDirIterator::Subdirectories); + while (it.hasNext()) { + const QString source = it.next(); + const QString destination = + uniqueFilePath(logsDir, QFileInfo(source).fileName()); + + if (QFile::rename(source, destination) || + (QFile::copy(source, destination) && QFile::remove(source))) { + ++moved; + log::info("Moved PGPatcher log '{}' -> '{}'", source, destination); + } else { + log::warn("Failed to move PGPatcher log '{}' -> '{}'", source, + destination); + } + } + } + + if (moved > 0) { + log::info("PGPatcher log cleanup moved {} file(s) to '{}'", moved, + logsDir.absolutePath()); + } } void OrganizerCore::trackOverwriteMove(const QString& relativePath, @@ -2770,6 +2860,7 @@ void OrganizerCore::afterRun(const QFileInfo& binary, DWORD exitCode) // and tears down the FUSE session. This mirrors Windows behaviour where // USVFS is only active while a hooked process is running. m_USVFS.unmount(); + movePGPatcherLogsToLogsFolder(); // Restore write permissions on the game directory. In rare cases // (crashes, unclean Wine shutdown) file permissions can be changed to diff --git a/src/src/organizercore.h b/src/src/organizercore.h index 494e835..973ee64 100644 --- a/src/src/organizercore.h +++ b/src/src/organizercore.h @@ -534,11 +534,13 @@ private: const QString& customOverwrite); std::vector fileMapping(const QString& dataPath, const QString& relPath, - const MOShared::DirectoryEntry* base, - const MOShared::DirectoryEntry* directoryEntry, - int createDestination); - -private slots: + const MOShared::DirectoryEntry* base, + const MOShared::DirectoryEntry* directoryEntry, + int createDestination); + + void movePGPatcherLogsToLogsFolder(); + +private slots: void onDirectoryRefreshed(); void downloadRequested(QNetworkReply* reply, QString gameName, int modID, diff --git a/src/src/settingsdialogupdates.cpp b/src/src/settingsdialogupdates.cpp index 8f76bae..57f5957 100644 --- a/src/src/settingsdialogupdates.cpp +++ b/src/src/settingsdialogupdates.cpp @@ -2,6 +2,7 @@ #include "fluorineupdater.h" #include "settings.h" +#include "shared/util.h" #include "ui_settingsdialog.h" #include @@ -10,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +36,17 @@ #include +namespace +{ + +bool isLauncherFile(const QString& path) +{ + const QFileInfo info(path); + return info.isFile(); +} + +} // namespace + UpdatesSettingsTab::UpdatesSettingsTab(Settings& s, SettingsDialog& d) : SettingsTab(s, d) { @@ -324,22 +335,29 @@ void UpdatesSettingsTab::onInstall() QDir extract(extractDir); QString newLauncher = extract.absoluteFilePath(QStringLiteral("fluorine-manager")); - if (!QFileInfo::exists(newLauncher)) { + if (!isLauncherFile(newLauncher)) { const QStringList tops = extract.entryList( QDir::Dirs | QDir::NoDotAndDotDot); for (const QString& top : tops) { const QString candidate = extract.absoluteFilePath( top + QStringLiteral("/fluorine-manager")); - if (QFileInfo::exists(candidate)) { + if (isLauncherFile(candidate)) { newLauncher = candidate; break; } } } - if (!QFileInfo::exists(newLauncher)) { + if (isLauncherFile(newLauncher)) { + QFile::setPermissions( + newLauncher, + QFile::permissions(newLauncher) | QFile::ExeOwner | + QFile::ExeGroup | QFile::ExeOther); + } + if (!isLauncherFile(newLauncher) || + !QFileInfo(newLauncher).isExecutable()) { m_statusLabel->setText( tr("Install failed: launcher not found in " - "extracted archive")); + "extracted archive, or it is not executable")); m_progressBar->setVisible(false); m_installButton->setEnabled(true); m_checkNowButton->setEnabled(true); @@ -367,14 +385,13 @@ void UpdatesSettingsTab::onInstall() QStringLiteral( "#!/usr/bin/env bash\n" "set -u\n" - "OLD_PID=%1\n" - "NEW_LAUNCHER=%2\n" + "OLD_PID=\"$1\"\n" + "NEW_LAUNCHER=\"$2\"\n" "for _ in $(seq 1 200); do\n" " if ! kill -0 \"$OLD_PID\" 2>/dev/null; then break; fi\n" " sleep 0.1\n" "done\n" - "exec \"$NEW_LAUNCHER\"\n") - .arg(QString::number(currentPid), newLauncher); + "exec \"$NEW_LAUNCHER\"\n"); helper.write(script.toUtf8()); helper.close(); QFile::setPermissions( @@ -388,7 +405,9 @@ void UpdatesSettingsTab::onInstall() // Detach the helper so it survives our exit. QProcess::startDetached(QStringLiteral("/usr/bin/env"), - {QStringLiteral("bash"), helperPath}); + {QStringLiteral("bash"), helperPath, + QString::number(currentPid), + newLauncher}); MOBase::log::info( "update installer: spawned helper to restart into {}", @@ -396,7 +415,7 @@ void UpdatesSettingsTab::onInstall() // Give the signal a beat to propagate, then quit cleanly. QTimer::singleShot(250, qApp, - []() { QCoreApplication::quit(); }); + []() { ExitModOrganizer(Exit::Force); }); }); extractor->start(); }); -- cgit v1.3.1