From 5aa6e4b9692351c228702042771b75fdf5ddae22 Mon Sep 17 00:00:00 2001 From: Nik Reist Date: Sun, 29 Jun 2025 22:44:19 -0400 Subject: [PATCH] Remove elevated privileges requirement, rely on udev rules, update CMakeLists, simplify download folder loading. --- src/CMakeLists.txt | 7 +- src/linux/platform_lin.cpp | 76 ----------- src/linux/platform_lin_suprogram.cpp | 155 ---------------------- src/linux/platform_lin_suprogram.h | 73 ---------- src/linux/udev_rules/71-usb-storage.rules | 1 + src/main.cpp | 2 + src/mainapplication.cpp | 17 +-- 7 files changed, 14 insertions(+), 317 deletions(-) delete mode 100644 src/linux/platform_lin_suprogram.cpp delete mode 100644 src/linux/platform_lin_suprogram.h create mode 100644 src/linux/udev_rules/71-usb-storage.rules diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8c0ea3..9e39723 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,14 +63,12 @@ endif() if(UNIX AND NOT APPLE) set(OMI_HEADERS ${OMI_HEADERS} - linux/platform_lin_suprogram.h linux/usbdevicemonitor_lin_p.h ) set(OMI_SOURCES ${OMI_SOURCES} linux/externalprogressbar_lin.cpp linux/platform_lin.cpp - linux/platform_lin_suprogram.cpp linux/usbdevicemonitor_lin.cpp ) @@ -96,6 +94,11 @@ if(UNIX AND NOT APPLE) RENAME om-imagewriter.svg ) + + install(FILES ${CMAKE_SOURCE_DIR}/src/linux/udev_rules/71-usb-storage.rules + DESTINATION + lib/udev/rules.d + ) endif() qt_add_executable(om-imagewriter diff --git a/src/linux/platform_lin.cpp b/src/linux/platform_lin.cpp index 1f83eef..9c558f0 100644 --- a/src/linux/platform_lin.cpp +++ b/src/linux/platform_lin.cpp @@ -5,7 +5,6 @@ #include #include -#include "platform_lin_suprogram.h" #include "../mainapplication.h" #include "../usbdevice.h" @@ -110,78 +109,3 @@ bool platformEnumFlashDevices(AddFlashDeviceCallbackProc callback, void* cbParam return true; } -bool ensureElevated() -{ - // If we already have root privileges do nothing - uid_t uid = getuid(); - if (uid == 0) - return true; - - // Search for known GUI su-applications. - // The list is priority-ordered. If there are native su-applications present, - // using the first such program. Otherwise, using just the first program that is present. - QList suPrograms = { new XdgSu(), new BeeSu(), new KdeSu(), new GkSu() }; - SuProgram* suProgram = NULL; - for (int i = 0; i < suPrograms.size(); ++i) - { - // Skip missing su-apps - if (!suPrograms[i]->isPresent()) - continue; - - if (suPrograms[i]->isNative()) - { - // If we found a native su-application - using it as preferred and stop searching - suProgram = suPrograms[i]; - break; - } - else - { - // If not native, and no other su-application was found - using it, but continue searching, - // in case a native app will appear down the list - if (suProgram == NULL) - suProgram = suPrograms[i]; - } - } - if (suProgram == NULL) - { - QMessageBox::critical( - NULL, - ApplicationTitle, - "" + QObject::tr("Error!") + " " + QObject::tr("No appropriate su-application found!") + "
" + - QObject::tr("Please, restart the program with root privileges."), - QMessageBox::Ok - ); - return false; - } - - // Prepare the list of arguments and restart ourselves using the su-application found - QStringList args; - // First comes our own executable - args << mApp->applicationFilePath(); - // We need to explicitly pass language and initial directory so that the new instance - // inherited the current user's parameters rather than root's - QString argLang = mApp->getLocale(); - if (!argLang.isEmpty()) - args << "--lang=" + argLang; - QString argDir = mApp->getInitialDir(); - if (!argDir.isEmpty()) - args << "--dir=" + argDir; - // Finally, if image file was supplied, append it as well - QString argImage = mApp->getInitialImage(); - if (!argImage.isEmpty()) - args << argImage; - // And now try to take off with all this garbage - suProgram->restartAsRoot(args); - - // Something went wrong, we should have never returned! Cleanup and return error - for (int i = 0; i < suPrograms.size(); ++i) - delete suPrograms[i]; - QMessageBox::critical( - NULL, - ApplicationTitle, - "" + QObject::tr("Error!") + " " + QObject::tr("Failed to restart with root privileges! (Error code: %1)").arg(errno) + "
" + - QObject::tr("Please, restart the program with root privileges."), - QMessageBox::Ok - ); - return false; -} diff --git a/src/linux/platform_lin_suprogram.cpp b/src/linux/platform_lin_suprogram.cpp deleted file mode 100644 index 7e4dddd..0000000 --- a/src/linux/platform_lin_suprogram.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include - -#include "../common.h" -#include "platform_lin_suprogram.h" - -// Copied from private Qt implementation at qtbase/src/platformsupport/services/genericunix/qgenericunixservices.cpp -static QString detectDesktopEnvironment() -{ - const QString xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP"); - if (!xdgCurrentDesktop.isEmpty()) - return xdgCurrentDesktop.toUpper(); // KDE, GNOME, UNITY, LXDE, MATE, XFCE... - - // Classic fallbacks - if (!qEnvironmentVariableIsEmpty("KDE_FULL_SESSION")) - return "KDE"; - if (!qEnvironmentVariableIsEmpty("GNOME_DESKTOP_SESSION_ID")) - return "GNOME"; - - // Fallback to checking $DESKTOP_SESSION (unreliable) - const QString desktopSession = qgetenv("DESKTOP_SESSION"); - if (desktopSession == "gnome") - return "GNOME"; - if (desktopSession == "xfce") - return "XFCE"; - - return ""; -} - -SuProgram::SuProgram(QString binaryName, QStringList suArgs, bool splitArgs) - : m_binaryName(binaryName) - , m_suArgs(suArgs) - , m_splitArgs(splitArgs) -{ - m_binaryPath = QStandardPaths::findExecutable(m_binaryName); -} - -XdgSu::XdgSu() - : SuProgram("xdg-su", {"-c"}, false) -{ -} - -KdeSu::KdeSu() - : SuProgram("kdesu", {"-c"}, false) -{ - // Extract from man: - // Since is no longer installed in <$(kde4-config --prefix)/bin> but in - // and therefore not in your , you have to use <$(kde4-config --path libexec)kdesu> to launch . - // Now, what we do: first, simple PATH check is performed in SuProgram constructor. Then we try to find kdesu - // according to the KDE documentation and, if found, overwrite the path with the new one. - QProcess kdeConfig; - kdeConfig.start("kde4-config", {"--path", "libexec"}, QIODevice::ReadOnly); - kdeConfig.waitForFinished(3000); - QString kdesuPath = kdeConfig.readAllStandardOutput().trimmed(); - if (!kdesuPath.isEmpty()) - { - kdesuPath += (kdesuPath.endsWith('/') ? "" : "/"); - kdesuPath += "kdesu"; - if (QFile::exists(kdesuPath) && QFile::permissions(kdesuPath).testFlag(QFileDevice::ExeUser)) - m_binaryPath = kdesuPath; - } -} - -GkSu::GkSu() - : SuProgram("gksu", {}, false) -{ -} - -BeeSu::BeeSu() - : SuProgram("beesu", {}, true) -{ -} - - -bool SuProgram::isPresent() const -{ - return (!m_binaryPath.isEmpty()); -} - - -bool XdgSu::isNative() const -{ - // xdg-su is considered a universal tool - return true; -} - -bool KdeSu::isNative() const -{ - // kdesu is native only in KDE - const QString desktopEnvironment = detectDesktopEnvironment(); - return (desktopEnvironment == "KDE"); -} - -bool GkSu::isNative() const -{ - // gksu is native for GTK-based DEs; however, for Qt-based DEs (like LXQt) there is no alternative either - const QString desktopEnvironment = detectDesktopEnvironment(); - return (!desktopEnvironment.isEmpty() && (desktopEnvironment != "KDE")); -} - -bool BeeSu::isNative() const -{ - // beesu is developed for Fedora/RedHat, let's consider it native there - QFile redhatRelease("/etc/redhat-release"); - if (!redhatRelease.open(QIODevice::ReadOnly | QIODevice::Text)) - return false; - QString redhatInfo = redhatRelease.readAll(); - return redhatInfo.contains("Red Hat", Qt::CaseInsensitive) || redhatInfo.contains("Fedora", Qt::CaseInsensitive); -} - - -void SuProgram::restartAsRoot(const QStringList& args) -{ - if (m_binaryPath.isEmpty()) - return; - - int i; - - // For execv() we need the list of char* arguments; using QByteArrays as temporary storage - // Store QByteArray objects explicitly to make sure they live long enough, so that their data()'s were valid until execv() call - QList argsBA; - - // First comes the application being started (su-application) with all its arguments (if any) - argsBA << m_binaryPath.toUtf8(); - for (i = 0; i < m_suArgs.size(); ++i) - argsBA << m_suArgs[i].toUtf8(); - // Now append the passed arguments - // Depending on the su-application, we may need to either pas them separately, or space-join them into one single argument word - if (m_splitArgs) - { - for (i = 0; i < args.size(); ++i) - argsBA << args[i].toUtf8(); - } - else - { - QString joined = '\'' + args[0] + '\''; - for (i = 1; i < args.size(); ++i) - joined += " '" + args[i] + "'"; - argsBA << joined.toUtf8(); - } - - // Convert arguments into char*'s and append NULL element - int argsNum = argsBA.size(); - char** argsBin = new char*[argsNum + 1]; - for (i = 0; i < argsNum; ++i) - argsBin[i] = argsBA[i].data(); - argsBin[argsNum] = NULL; - - // Replace ourselves with su-application - execv(argsBin[0], argsBin); - - // Something went wrong, we should have never returned! Cleaning up - delete[] argsBin; -} diff --git a/src/linux/platform_lin_suprogram.h b/src/linux/platform_lin_suprogram.h deleted file mode 100644 index 191ef90..0000000 --- a/src/linux/platform_lin_suprogram.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef SUPROGRAM_H -#define SUPROGRAM_H - -#include -#include - -// Auxiliary set of classes for handling different kinds of su-applications - -// Base class -class SuProgram -{ -protected: - // Name of the su-application - const QString m_binaryName; - // Full path to the executable (if present) - QString m_binaryPath; - // Additional arguments - const QStringList m_suArgs; - // Whether it accepts target command line as separate arguments or single argument - const bool m_splitArgs; - - // Constructor - SuProgram(QString binaryName, QStringList suArgs, bool splitArgs); - -public: - // Destructor - virtual ~SuProgram() {} - // Check if the program is present in the system; by default searching in PATH is used - virtual bool isPresent() const; - // Check whether the su-application is native to the current desktop environment - virtual bool isNative() const = 0; - // Restarts the current application with the specified arguments using the GUI su program - // Returns only if error occurred (execv() is used) - virtual void restartAsRoot(const QStringList& args); -}; - - -// Derivative classes for specific su-applications - -class XdgSu : public SuProgram -{ -public: - XdgSu(); - virtual ~XdgSu() {} - virtual bool isNative() const; -}; - -class KdeSu : public SuProgram -{ -public: - KdeSu(); - virtual ~KdeSu() {} - virtual bool isNative() const; -}; - -class GkSu : public SuProgram -{ -public: - GkSu(); - virtual ~GkSu() {} - virtual bool isNative() const; -}; - -class BeeSu : public SuProgram -{ -public: - BeeSu(); - virtual ~BeeSu() {} - virtual bool isNative() const; -}; - - -#endif // SUPROGRAM_H diff --git a/src/linux/udev_rules/71-usb-storage.rules b/src/linux/udev_rules/71-usb-storage.rules new file mode 100644 index 0000000..8edb476 --- /dev/null +++ b/src/linux/udev_rules/71-usb-storage.rules @@ -0,0 +1 @@ +KERNEL=="sd*", SUBSYSTEMS=="usb", MODE="0660", TAG+="uaccess" diff --git a/src/main.cpp b/src/main.cpp index 18be804..3bae412 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -38,8 +38,10 @@ int main(int argc, char *argv[]) appTranslator.load(langName, QCoreApplication::applicationDirPath() + "/lang"); a.installTranslator(&appTranslator); +#if defined(Q_OS_MAC) || defined(Q_OS_WIN32) if (!ensureElevated()) return 1; +#endif #if defined(Q_OS_WIN32) // CoInitialize() seems to be called by Qt automatically, so only set security attributes diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp index 833e009..b1b1ef6 100644 --- a/src/mainapplication.cpp +++ b/src/mainapplication.cpp @@ -24,19 +24,14 @@ QString MainApplication::getInitialDir() // win:restricted // win:admin // mac:restricted - // linux:restricted - // linux:root - // linux: translated dir names + // linux: should be using udev rules with MODE="0660", TAG+="uaccess" // win: redefined paths - if (m_Options.isSet("dir")) + if (m_Options.isSet("dir")) { return m_Options.value("dir"); - - // Otherwise get the standard system Downloads location - QStringList downloadDirs = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation); - if (downloadDirs.size() > 0) - return downloadDirs.at(0); - else - return ""; + } else { + // Otherwise get the standard system Downloads location + return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + } } // Returns the fila path passed to the application as command-line parameter