diff --git a/CHANGES-3.3 b/CHANGES-3.3 index 6378c7be678827dad61218dafadc6ac92903774e..de7c712a9a7df1ef4d6f2c0b21c29ce6f22fa0a9 100644 --- a/CHANGES-3.3 +++ b/CHANGES-3.3 @@ -7,10 +7,23 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.3.0. See CHANGES-3.2 for the history of the 3.2 series (2018-05 - 2022-08). -# 3.3.14 (unreleased) +# 3.3.15 (unreleased) + +This release contains contributions from (alphabetically by given name): + - Nobody yet! + +## Core ## + - Nothing yet! + +## Modules ## + - Nothing yet! + + +# 3.3.14 (2024-02-20) This release contains contributions from (alphabetically by given name): - Adriaan de Groot + - Evan James - TNE - vincent PENVERN @@ -19,13 +32,21 @@ This release contains contributions from (alphabetically by given name): consistent. At least one valid Python program would work with the Boost::Python bindings, but not the pybind11 bindings. A memory-corruption problem in the Boost::Python bindings was resolved. + - Steps in the UI now have a hook to undo any changes they have made + to the live system, if the user cancels the installation. ## Modules ## + - *keyboard* module undoes changes to the keyboard layout if the + user cancels the installation (returning the system to whatever + layout was in use when Calamares started). (#2377, #2431) + - *locale* module undoes changes to the timezone. (#2377, #2431) - *partition* module stores a global storage value in luksPassphrase, for later modules that need to manipulate the encrypted partition. (thanks vincent, #2424) - *partition* module no longer clear (unmounts) a Ventoy device. (thanks TNE, #2427) + - *partition* module has configurable exceptions for the clear-mounts + (unmount) job, which always includes Ventoy. # 3.3.13 (2024-12-31) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c534af0409847bbb366cfc62643b8810dd43911..1f3d3e02ff3fbc1739b40ef64e261803c5c50d08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ cmake_minimum_required(VERSION 3.16 FATAL_ERROR) -set(CALAMARES_VERSION 3.3.14) +set(CALAMARES_VERSION 3.3.15) set(CALAMARES_RELEASE_MODE OFF) # Set to ON during a release if(CMAKE_SCRIPT_MODE_FILE) diff --git a/src/calamares/CalamaresWindow.cpp b/src/calamares/CalamaresWindow.cpp index e1892e7642c000abb8328c42dfd052257de5340b..bad04809ebc2865ed1e31546e921b3ab837f6ee5 100644 --- a/src/calamares/CalamaresWindow.cpp +++ b/src/calamares/CalamaresWindow.cpp @@ -534,7 +534,13 @@ CalamaresWindow::ensureSize( QSize size ) void CalamaresWindow::closeEvent( QCloseEvent* event ) { - if ( ( !m_viewManager ) || m_viewManager->confirmCancelInstallation() ) + if ( m_viewManager ) + { + m_viewManager->quit(); + // If it didn't actually exit, eat the event to ignore close + event->ignore(); + } + else { event->accept(); #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) @@ -543,8 +549,4 @@ CalamaresWindow::closeEvent( QCloseEvent* event ) QApplication::exit( EXIT_SUCCESS ); #endif } - else - { - event->ignore(); - } } diff --git a/src/libcalamares/geoip/Interface.h b/src/libcalamares/geoip/Interface.h index 09c28c1f0317bfcf4f522a005227524a91bedddc..c144cc3eeb9874233170de08e12f02dcf4e452d3 100644 --- a/src/libcalamares/geoip/Interface.h +++ b/src/libcalamares/geoip/Interface.h @@ -60,6 +60,8 @@ public: return std::tie( lhs.m_region, lhs.m_zone ) == std::tie( rhs.m_region, rhs.m_zone ); } + QString asString() const { return isValid() ? region() + QChar( '/' ) + zone() : QString(); } + private: QString m_region; QString m_zone; @@ -68,13 +70,13 @@ private: inline QDebug& operator<<( QDebug&& s, const RegionZonePair& tz ) { - return s << tz.region() << '/' << tz.zone(); + return s << tz.asString(); } inline QDebug& operator<<( QDebug& s, const RegionZonePair& tz ) { - return s << tz.region() << '/' << tz.zone(); + return s << tz.asString(); } /** @brief Splits a region/zone string into a pair. diff --git a/src/libcalamaresui/ViewManager.cpp b/src/libcalamaresui/ViewManager.cpp index 2fd7f45c1418feb81fc954bb49d676fe6e036f79..0e8c4531c6229a6dd3c93e706b552cd5696a419a 100644 --- a/src/libcalamaresui/ViewManager.cpp +++ b/src/libcalamaresui/ViewManager.cpp @@ -489,17 +489,31 @@ ViewManager::back() void ViewManager::quit() { - if ( confirmCancelInstallation() ) + const auto r = confirmCancelInstallation(); + if ( r == Confirmation::Continue ) { + return; + } + + if ( r == Confirmation::CancelInstallation ) + { + // Cancel view steps in reverse + for ( int i = m_currentStep; i >= 0; --i ) + { + auto* step = m_steps.at( i ); + cDebug() << "Cancelling view step" << step->moduleInstanceKey(); + step->onCancel(); + } + } + #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - QApplication::quit(); + QApplication::quit(); #else - QApplication::exit( EXIT_SUCCESS ); + QApplication::exit( EXIT_SUCCESS ); #endif - } } -bool +ViewManager::Confirmation ViewManager::confirmCancelInstallation() { const auto* const settings = Calamares::Settings::instance(); @@ -507,17 +521,17 @@ ViewManager::confirmCancelInstallation() // When we're at the very end, then it's always OK to exit. if ( isAtVeryEnd( m_steps, m_currentStep ) ) { - return true; + return Confirmation::EndOfInstallation; } // Not at the very end, cancel/quit might be disabled if ( settings->disableCancel() ) { - return false; + return Confirmation::Continue; } if ( settings->disableCancelDuringExec() && stepIsExecute( m_steps, m_currentStep ) ) { - return false; + return Confirmation::Continue; } // Otherwise, confirm cancel/quit. @@ -530,7 +544,7 @@ ViewManager::confirmCancelInstallation() mb.setDefaultButton( QMessageBox::No ); Calamares::fixButtonLabels( &mb ); int response = mb.exec(); - return response == QMessageBox::Yes; + return ( response == QMessageBox::Yes ) ? Confirmation::CancelInstallation : Confirmation::Continue; } void diff --git a/src/libcalamaresui/ViewManager.h b/src/libcalamaresui/ViewManager.h index 5a449a1536a67dac3f20be9a908ae286d8085a49..62d2b9f032d8bbfa17de470be50beb6cbecebf89 100644 --- a/src/libcalamaresui/ViewManager.h +++ b/src/libcalamaresui/ViewManager.h @@ -101,14 +101,21 @@ public: */ int currentStepIndex() const; + enum class Confirmation + { + Continue, // User rejects cancel / close question + CancelInstallation, // User accepts cancel / close question + EndOfInstallation, // There was no question because the installation was already done + }; + /** * @brief Called when "Cancel" is clicked; asks for confirmation. * Other means of closing Calamares also call this method, e.g. alt-F4. * At the end of installation, no confirmation is asked. * - * @return @c true if the user confirms closing the window. + * @return a Confirmation value, @c Unasked if the installation is complete */ - bool confirmCancelInstallation(); + Confirmation confirmCancelInstallation(); Qt::Orientations panelSides() const { return m_panelSides; } void setPanelSides( Qt::Orientations panelSides ) { m_panelSides = panelSides; } diff --git a/src/libcalamaresui/viewpages/ViewStep.cpp b/src/libcalamaresui/viewpages/ViewStep.cpp index 417f5413c9a11ba57814ff74ae033d23fa877ab8..4a172a990503aa98cc4fd84e6bde87dfafb83d17 100644 --- a/src/libcalamaresui/viewpages/ViewStep.cpp +++ b/src/libcalamaresui/viewpages/ViewStep.cpp @@ -45,6 +45,11 @@ ViewStep::onLeave() { } +void +ViewStep::onCancel() +{ +} + void ViewStep::next() { diff --git a/src/libcalamaresui/viewpages/ViewStep.h b/src/libcalamaresui/viewpages/ViewStep.h index 22e72e5fd53ef17d874d6595fa156d981c42ef30..1a52ef6304887fe3f2fc86ef248b6bbee29c392b 100644 --- a/src/libcalamaresui/viewpages/ViewStep.h +++ b/src/libcalamaresui/viewpages/ViewStep.h @@ -168,6 +168,15 @@ public: */ virtual RequirementsList checkRequirements(); + /** + * @brief Called when the user cancels the installation + * + * View steps are expected to leave the system unchanged when + * the installation is cancelled. The default implementation + * does nothing. + */ + virtual void onCancel(); + signals: /// @brief Tells the viewmanager to enable the *next* button according to @p status void nextStatusChanged( bool status ); diff --git a/src/modules/keyboard/AdditionalLayoutInfo.h b/src/modules/keyboard/AdditionalLayoutInfo.h index 61e854d3b9ad2ea35430d9918827d6a9232b591c..f606bfec9f518b141a97406b20dffedfd540b875 100644 --- a/src/modules/keyboard/AdditionalLayoutInfo.h +++ b/src/modules/keyboard/AdditionalLayoutInfo.h @@ -12,6 +12,14 @@ #include <QString> +struct BasicLayoutInfo +{ + QString selectedLayout; + QString selectedModel; + QString selectedVariant; + QString selectedGroup; +}; + struct AdditionalLayoutInfo { QString additionalLayout; diff --git a/src/modules/keyboard/Config.cpp b/src/modules/keyboard/Config.cpp index 54ee7649cd4a6bf6770717075081c47b6f30f799..47c81a347b2483f80f8f8bbb8c3168510f2c8527 100644 --- a/src/modules/keyboard/Config.cpp +++ b/src/modules/keyboard/Config.cpp @@ -169,7 +169,7 @@ Config::Config( QObject* parent ) &KeyboardModelsModel::currentIndexChanged, [ & ]( int index ) { - m_selectedModel = m_keyboardModelsModel->key( index ); + m_current.selectedModel = m_keyboardModelsModel->key( index ); somethingChanged(); } ); @@ -177,7 +177,7 @@ Config::Config( QObject* parent ) &KeyboardLayoutModel::currentIndexChanged, [ & ]( int index ) { - m_selectedLayout = m_keyboardLayoutsModel->item( index ).first; + m_current.selectedLayout = m_keyboardLayoutsModel->item( index ).first; updateVariants( QPersistentModelIndex( m_keyboardLayoutsModel->index( index ) ) ); emit prettyStatusChanged(); } ); @@ -186,14 +186,14 @@ Config::Config( QObject* parent ) &KeyboardVariantsModel::currentIndexChanged, [ & ]( int index ) { - m_selectedVariant = m_keyboardVariantsModel->key( index ); + m_current.selectedVariant = m_keyboardVariantsModel->key( index ); somethingChanged(); } ); connect( m_KeyboardGroupSwitcherModel, &KeyboardGroupsSwitchersModel::currentIndexChanged, [ & ]( int index ) { - m_selectedGroup = m_KeyboardGroupSwitcherModel->key( index ); + m_current.selectedGroup = m_KeyboardGroupSwitcherModel->key( index ); somethingChanged(); } ); @@ -207,10 +207,10 @@ Config::Config( QObject* parent ) this, &Config::selectionChange ); - m_selectedModel = m_keyboardModelsModel->key( m_keyboardModelsModel->currentIndex() ); - m_selectedLayout = m_keyboardLayoutsModel->item( m_keyboardLayoutsModel->currentIndex() ).first; - m_selectedVariant = m_keyboardVariantsModel->key( m_keyboardVariantsModel->currentIndex() ); - m_selectedGroup = m_KeyboardGroupSwitcherModel->key( m_KeyboardGroupSwitcherModel->currentIndex() ); + m_current.selectedModel = m_keyboardModelsModel->key( m_keyboardModelsModel->currentIndex() ); + m_current.selectedLayout = m_keyboardLayoutsModel->item( m_keyboardLayoutsModel->currentIndex() ).first; + m_current.selectedVariant = m_keyboardVariantsModel->key( m_keyboardVariantsModel->currentIndex() ); + m_current.selectedGroup = m_KeyboardGroupSwitcherModel->key( m_KeyboardGroupSwitcherModel->currentIndex() ); } void @@ -224,38 +224,56 @@ Config::somethingChanged() emit prettyStatusChanged(); } -void -Config::apply() +static void +applyXkb( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra ) { - if ( m_configureXkb ) - { - applyXkb(); - } - if ( m_configureLocale1 ) + QStringList basicArguments = xkbmap_model_args( settings.selectedModel ); + if ( !extra.additionalLayout.isEmpty() ) { - applyLocale1(); + if ( !settings.selectedGroup.isEmpty() ) + { + extra.groupSwitcher = "grp:" + settings.selectedGroup; + } + + if ( extra.groupSwitcher.isEmpty() ) + { + extra.groupSwitcher = xkbmap_query_grp_option(); + } + if ( extra.groupSwitcher.isEmpty() ) + { + extra.groupSwitcher = "grp:alt_shift_toggle"; + } + + basicArguments.append( + xkbmap_layout_args_with_group_switch( { extra.additionalLayout, settings.selectedLayout }, + { extra.additionalVariant, settings.selectedVariant }, + extra.groupSwitcher ) ); + QProcess::execute( "setxkbmap", basicArguments ); + + cDebug() << "xkbmap selection changed to: " << settings.selectedLayout << '-' << settings.selectedVariant + << "(added " << extra.additionalLayout << "-" << extra.additionalVariant + << " since current layout is not ASCII-capable)"; } - if ( m_configureKWin ) + else { - applyKWin(); + basicArguments.append( xkbmap_layout_args( settings.selectedLayout, settings.selectedVariant ) ); + QProcess::execute( "setxkbmap", basicArguments ); + cDebug() << "xkbmap selection changed to: " << settings.selectedLayout << '-' << settings.selectedVariant; } - // Writing /etc/ files is not needed "live" } -void -Config::applyLocale1() +static void +applyLocale1( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra ) { - m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout ); - - QString layout = m_selectedLayout; - QString variant = m_selectedVariant; + QString layout = settings.selectedLayout; + QString variant = settings.selectedVariant; QString option; - if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() ) + if ( !extra.additionalLayout.isEmpty() ) { - layout = m_additionalLayoutInfo.additionalLayout + "," + layout; - variant = m_additionalLayoutInfo.additionalVariant + "," + variant; - option = m_additionalLayoutInfo.groupSwitcher; + layout = extra.additionalLayout + "," + layout; + variant = extra.additionalVariant + "," + variant; + option = extra.groupSwitcher; } QDBusInterface locale1( "org.freedesktop.locale1", @@ -270,7 +288,8 @@ Config::applyLocale1() // Using convert=true, this also updates the VConsole config { - QDBusReply< void > r = locale1.call( "SetX11Keyboard", layout, m_selectedModel, variant, option, true, false ); + QDBusReply< void > r + = locale1.call( "SetX11Keyboard", layout, settings.selectedModel, variant, option, true, false ); if ( !r.isValid() ) { cWarning() << "Could not set keyboard config through org.freedesktop.locale1.X11Keyboard." << r.error(); @@ -278,47 +297,6 @@ Config::applyLocale1() } } -void -Config::applyXkb() -{ - m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout ); - - QStringList basicArguments = xkbmap_model_args( m_selectedModel ); - if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() ) - { - if ( !m_selectedGroup.isEmpty() ) - { - m_additionalLayoutInfo.groupSwitcher = "grp:" + m_selectedGroup; - } - - if ( m_additionalLayoutInfo.groupSwitcher.isEmpty() ) - { - m_additionalLayoutInfo.groupSwitcher = xkbmap_query_grp_option(); - } - if ( m_additionalLayoutInfo.groupSwitcher.isEmpty() ) - { - m_additionalLayoutInfo.groupSwitcher = "grp:alt_shift_toggle"; - } - - basicArguments.append( - xkbmap_layout_args_with_group_switch( { m_additionalLayoutInfo.additionalLayout, m_selectedLayout }, - { m_additionalLayoutInfo.additionalVariant, m_selectedVariant }, - m_additionalLayoutInfo.groupSwitcher ) ); - QProcess::execute( "setxkbmap", basicArguments ); - - cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant << "(added " - << m_additionalLayoutInfo.additionalLayout << "-" << m_additionalLayoutInfo.additionalVariant - << " since current layout is not ASCII-capable)"; - } - else - { - basicArguments.append( xkbmap_layout_args( m_selectedLayout, m_selectedVariant ) ); - QProcess::execute( "setxkbmap", basicArguments ); - cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant; - } - m_applyTimer.stop(); -} - // In a config-file's list of lines, replace lines <key>=<something> by <key>=<value> static void replaceKey( QStringList& content, const QString& key, const QString& value ) @@ -368,21 +346,21 @@ rewriteKWin( const QString& path, const QString& model, const QString& layouts, } void -Config::applyKWin() +applyKWin( const BasicLayoutInfo& settings, AdditionalLayoutInfo& extra ) { const auto paths = QStandardPaths::standardLocations( QStandardPaths::ConfigLocation ); - auto join = [ &additional = m_additionalLayoutInfo.additionalLayout ]( const QString& s1, const QString& s2 ) + auto join = [ &additional = extra.additionalLayout ]( const QString& s1, const QString& s2 ) { return additional.isEmpty() ? s1 : QStringLiteral( "%1,%2" ).arg( s1, s2 ); }; - const QString layouts = join( m_selectedLayout, m_additionalLayoutInfo.additionalLayout ); - const QString variants = join( m_selectedVariant, m_additionalLayoutInfo.additionalVariant ); + const QString layouts = join( settings.selectedLayout, extra.additionalLayout ); + const QString variants = join( settings.selectedVariant, extra.additionalVariant ); bool updated = false; for ( const auto& path : paths ) { const QString candidate = path + QStringLiteral( "/kxkbrc" ); - if ( rewriteKWin( candidate, m_selectedModel, layouts, variants ) ) + if ( rewriteKWin( candidate, settings.selectedModel, layouts, variants ) ) { updated = true; break; @@ -397,6 +375,25 @@ Config::applyKWin() } } +void +Config::apply() +{ + m_additionalLayoutInfo = getAdditionalLayoutInfo( m_current.selectedLayout ); + if ( m_configureXkb ) + { + applyXkb( m_current, m_additionalLayoutInfo ); + } + if ( m_configureLocale1 ) + { + applyLocale1( m_current, m_additionalLayoutInfo ); + } + if ( m_configureKWin ) + { + applyKWin( m_current, m_additionalLayoutInfo ); + } + m_applyTimer.stop(); + // Writing /etc/ files is not needed "live" +} KeyboardModelsModel* Config::keyboardModels() const @@ -574,6 +571,26 @@ Config::detectCurrentKeyboardLayout() break; } } + // The models have updated the m_current settings, copy them + m_original = m_current; +} + +void +Config::cancel() +{ + const auto extra = getAdditionalLayoutInfo( m_original.selectedLayout ); + if ( m_configureXkb ) + { + applyXkb( m_original, m_additionalLayoutInfo ); + } + if ( m_configureLocale1 ) + { + applyLocale1( m_original, m_additionalLayoutInfo ); + } + if ( m_configureKWin ) + { + applyKWin( m_original, m_additionalLayoutInfo ); + } } QString @@ -599,9 +616,9 @@ Config::createJobs() { QList< Calamares::job_ptr > list; - Calamares::Job* j = new SetKeyboardLayoutJob( m_selectedModel, - m_selectedLayout, - m_selectedVariant, + Calamares::Job* j = new SetKeyboardLayoutJob( m_current.selectedModel, + m_current.selectedLayout, + m_current.selectedVariant, m_additionalLayoutInfo, m_xOrgConfFileName, m_convertedKeymapPath, @@ -748,10 +765,10 @@ void Config::finalize() { Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); - if ( !m_selectedLayout.isEmpty() ) + if ( !m_current.selectedLayout.isEmpty() ) { - gs->insert( "keyboardLayout", m_selectedLayout ); - gs->insert( "keyboardVariant", m_selectedVariant ); //empty means default variant + gs->insert( "keyboardLayout", m_current.selectedLayout ); + gs->insert( "keyboardVariant", m_current.selectedVariant ); //empty means default variant if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() ) { diff --git a/src/modules/keyboard/Config.h b/src/modules/keyboard/Config.h index e2a8c4f0e3ac2c9e4ae02e8ffa2575e1261a843d..d48c734bea7b0f004a5ec8a36152a1a80784b07d 100644 --- a/src/modules/keyboard/Config.h +++ b/src/modules/keyboard/Config.h @@ -44,6 +44,9 @@ public: /// @brief When leaving the page, write to GS void finalize(); + /// @brief Restore the system to whatever layout was in use when detectCurrentKeyboardLayout() was called + void cancel(); + static AdditionalLayoutInfo getAdditionalLayoutInfo( const QString& layout ); /* A model is a physical configuration of a keyboard, e.g. 105-key PC @@ -94,9 +97,6 @@ private: */ void somethingChanged(); void apply(); - void applyLocale1(); - void applyXkb(); - void applyKWin(); void getCurrentKeyboardLayoutXkb( QString& currentLayout, QString& currentVariant, QString& currentModel ); void getCurrentKeyboardLayoutLocale1( QString& currentLayout, QString& currentVariant, QString& currentModel ); @@ -106,10 +106,8 @@ private: KeyboardVariantsModel* m_keyboardVariantsModel; KeyboardGroupsSwitchersModel* m_KeyboardGroupSwitcherModel; - QString m_selectedLayout; - QString m_selectedModel; - QString m_selectedVariant; - QString m_selectedGroup; + BasicLayoutInfo m_current; + BasicLayoutInfo m_original; // Layout (and corresponding info) added if current one doesn't support ASCII (e.g. Russian or Japanese) AdditionalLayoutInfo m_additionalLayoutInfo; diff --git a/src/modules/keyboard/KeyboardViewStep.cpp b/src/modules/keyboard/KeyboardViewStep.cpp index 3eaf4dcc5d956d6c62dcd933444d95180475ba8d..92df2d0bcca405849231d6d7a79068b87acda5d9 100644 --- a/src/modules/keyboard/KeyboardViewStep.cpp +++ b/src/modules/keyboard/KeyboardViewStep.cpp @@ -104,6 +104,11 @@ KeyboardViewStep::onLeave() m_config->finalize(); } +void +KeyboardViewStep::onCancel() +{ + m_config->cancel(); +} void KeyboardViewStep::setConfigurationMap( const QVariantMap& configurationMap ) diff --git a/src/modules/keyboard/KeyboardViewStep.h b/src/modules/keyboard/KeyboardViewStep.h index 902b888fd3785e12ea9cc35046c4b4b9cb24b2c5..96fd4c897ea4830b9f5e5335b7a2e45552b15db0 100644 --- a/src/modules/keyboard/KeyboardViewStep.h +++ b/src/modules/keyboard/KeyboardViewStep.h @@ -43,6 +43,7 @@ public: void onActivate() override; void onLeave() override; + void onCancel() override; void setConfigurationMap( const QVariantMap& configurationMap ) override; diff --git a/src/modules/keyboardq/KeyboardQmlViewStep.cpp b/src/modules/keyboardq/KeyboardQmlViewStep.cpp index f63d03b81db281962175749639cf88871ee78bcd..1acdd6ec60cee55af2aa111347ba86b62e72a2bd 100644 --- a/src/modules/keyboardq/KeyboardQmlViewStep.cpp +++ b/src/modules/keyboardq/KeyboardQmlViewStep.cpp @@ -80,6 +80,12 @@ KeyboardQmlViewStep::onLeave() m_config->finalize(); } +void +KeyboardQmlViewStep::onCancel() +{ + m_config->cancel(); +} + QObject* KeyboardQmlViewStep::getConfig() { diff --git a/src/modules/keyboardq/KeyboardQmlViewStep.h b/src/modules/keyboardq/KeyboardQmlViewStep.h index eb31c3d595c09204304f7d6da872a488ce33838c..540e753aab06ff5bc68074cdac4c76b6bbc30a71 100644 --- a/src/modules/keyboardq/KeyboardQmlViewStep.h +++ b/src/modules/keyboardq/KeyboardQmlViewStep.h @@ -39,6 +39,7 @@ public: void onActivate() override; void onLeave() override; + void onCancel() override; void setConfigurationMap( const QVariantMap& configurationMap ) override; QObject* getConfig() override; diff --git a/src/modules/locale/Config.cpp b/src/modules/locale/Config.cpp index b2ef0e890b81262458c4511ce317cf7721a339a7..3e27a52279ede5c759329ad3376382cb19ea4dab 100644 --- a/src/modules/locale/Config.cpp +++ b/src/modules/locale/Config.cpp @@ -378,7 +378,7 @@ Config::currentLocationStatus() const { if ( m_currentLocation ) { - return tr( "Set timezone to %1.", "@action" ).arg( currentTimezoneName()); + return tr( "Set timezone to %1.", "@action" ).arg( currentTimezoneName() ); } return QString(); } @@ -513,6 +513,8 @@ getGeoIP( const QVariantMap& configurationMap, std::unique_ptr< Calamares::GeoIP void Config::setConfigurationMap( const QVariantMap& configurationMap ) { + m_originalTimezone = Calamares::GeoIP::splitTZString( QTimeZone::systemTimeZoneId() ); + getLocaleGenLines( configurationMap, m_localeGenLines ); getAdjustLiveTimezone( configurationMap, m_adjustLiveTimezone ); getStartingTimezone( configurationMap, m_startingTimezone ); @@ -588,3 +590,12 @@ Config::completeGeoIP() m_geoipWatcher.reset(); m_geoip.reset(); } + +void +Config::cancel() +{ + if ( m_adjustLiveTimezone && m_originalTimezone.isValid() ) + { + QProcess::execute( "timedatectl", { "set-timezone", m_originalTimezone.asString() } ); + } +} diff --git a/src/modules/locale/Config.h b/src/modules/locale/Config.h index a26d25a9c9727e6926e072e4e6cfe0b4d667e075..fb1fae8621c278ae7a79695349dcb731b75cf588 100644 --- a/src/modules/locale/Config.h +++ b/src/modules/locale/Config.h @@ -83,10 +83,12 @@ public: const Calamares::Locale::TimeZoneData* currentLocation() const { return m_currentLocation; } - /// Special case, set location from starting timezone if not already set void setCurrentLocation(); + /// Restores original timezone, if any + void cancel(); + private: Calamares::Locale::TimeZoneData* currentLocation_c() const { @@ -176,6 +178,9 @@ private: */ Calamares::GeoIP::RegionZonePair m_startingTimezone; + /// @brief The timezone set in the system when Calamares started (not from config) + Calamares::GeoIP::RegionZonePair m_originalTimezone; + /** @brief Handler for GeoIP lookup (if configured) * * The GeoIP lookup needs to be started at some suitable time, diff --git a/src/modules/locale/LocaleViewStep.cpp b/src/modules/locale/LocaleViewStep.cpp index c40e3ae51872113634caacd6df42787e15889f73..87290a8514bfc8fea64d93569917d92376d26b9c 100644 --- a/src/modules/locale/LocaleViewStep.cpp +++ b/src/modules/locale/LocaleViewStep.cpp @@ -130,6 +130,12 @@ LocaleViewStep::onLeave() m_config->finalizeGlobalStorage(); } +void +LocaleViewStep::onCancel() +{ + m_config->cancel(); +} + void LocaleViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { diff --git a/src/modules/locale/LocaleViewStep.h b/src/modules/locale/LocaleViewStep.h index 12b05f9f8b0462fc9b1a12c51dc6d08a3fb2e54d..6244219476de6b633eb30405218cf7a337be746c 100644 --- a/src/modules/locale/LocaleViewStep.h +++ b/src/modules/locale/LocaleViewStep.h @@ -44,6 +44,7 @@ public: void onActivate() override; void onLeave() override; + void onCancel() override; void setConfigurationMap( const QVariantMap& configurationMap ) override; diff --git a/src/modules/localeq/LocaleQmlViewStep.cpp b/src/modules/localeq/LocaleQmlViewStep.cpp index 6863bb78adfad107314005eb718d2fe33a0a8b03..37f131c9543bed8d6569ad33d9ae379c387c4049 100644 --- a/src/modules/localeq/LocaleQmlViewStep.cpp +++ b/src/modules/localeq/LocaleQmlViewStep.cpp @@ -83,6 +83,12 @@ LocaleQmlViewStep::onLeave() m_config->finalizeGlobalStorage(); } +void +LocaleQmlViewStep::onCancel() +{ + m_config->cancel(); +} + void LocaleQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) { diff --git a/src/modules/localeq/LocaleQmlViewStep.h b/src/modules/localeq/LocaleQmlViewStep.h index ca70ca5d95308b3d76983c5ba05c33d6033e7710..a7b20ba0470e3cc8026342dbe1fb521a3646c1b0 100644 --- a/src/modules/localeq/LocaleQmlViewStep.h +++ b/src/modules/localeq/LocaleQmlViewStep.h @@ -34,8 +34,9 @@ public: bool isAtBeginning() const override; bool isAtEnd() const override; - virtual void onActivate() override; - virtual void onLeave() override; + void onActivate() override; + void onLeave() override; + void onCancel() override; Calamares::JobList jobs() const override; diff --git a/src/modules/partition/Config.cpp b/src/modules/partition/Config.cpp index 085c451791e96d10c34e286b200007d33c2c039f..b2f88789a5ef92682cb8c1767bf7931b9333db84 100644 --- a/src/modules/partition/Config.cpp +++ b/src/modules/partition/Config.cpp @@ -451,9 +451,11 @@ Config::setConfigurationMap( const QVariantMap& configurationMap ) { bool bogus = true; const auto lvmConfiguration = Calamares::getSubMap( configurationMap, "lvm", bogus ); - m_isLVMEnabled = Calamares::getBool( lvmConfiguration, "enable", true); + m_isLVMEnabled = Calamares::getBool( lvmConfiguration, "enable", true ); } + m_essentialMounts= Calamares::getStringList( configurationMap, "essentialMounts" ); + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); gs->insert( "armInstall", Calamares::getBool( configurationMap, "armInstall", false ) ); fillGSConfigurationEFI( gs, configurationMap ); diff --git a/src/modules/partition/Config.h b/src/modules/partition/Config.h index 13da58ac44c445d482af2db68ae1977fd515b92b..d2dfec64d16157b067a06c34dc2bb21a1e89f015 100644 --- a/src/modules/partition/Config.h +++ b/src/modules/partition/Config.h @@ -40,6 +40,8 @@ class Config : public QObject Q_PROPERTY( bool lvmEnabled READ isLVMEnabled CONSTANT FINAL ) + Q_PROPERTY( QStringList essentialMounts READ essentialMounts CONSTANT FINAL ) + public: Config( QObject* parent ); ~Config() override = default; @@ -178,6 +180,14 @@ public: bool isLVMEnabled() const { return m_isLVMEnabled; } + /** @brief A list of names that can follow /dev/mapper/ that must not be closed + * + * These names (if any) are skipped by the ClearMountsJob. + * The names may contain a trailing '*' which acts as a wildcard. + * In any other position, '*' is interpreted literally. + */ + QStringList essentialMounts() const { return m_essentialMounts; } + public Q_SLOTS: void setInstallChoice( int ); ///< Translates a button ID or so to InstallChoice void setInstallChoice( InstallChoice ); @@ -213,6 +223,7 @@ private: bool m_preCheckEncryption = false; bool m_showNotEncryptedBootMessage = true; bool m_isLVMEnabled = true; + QStringList m_essentialMounts; }; /** @brief Given a set of swap choices, return a sensible value from it. diff --git a/src/modules/partition/core/PartitionCoreModule.cpp b/src/modules/partition/core/PartitionCoreModule.cpp index c2fd61db9aebf956d2c593edcfd8c77ef03d58c8..d92c66f72dccdd2837f76573a03103679c7f2e54 100644 --- a/src/modules/partition/core/PartitionCoreModule.cpp +++ b/src/modules/partition/core/PartitionCoreModule.cpp @@ -610,7 +610,7 @@ PartitionCoreModule::setPartitionFlags( Device* device, Partition* partition, Pa STATICTEST QStringList findEssentialLVs( const QList< PartitionCoreModule::DeviceInfo* >& infos ) { - QStringList doNotClose; + QStringList essentialLV; cDebug() << "Checking LVM use on" << infos.count() << "devices"; for ( const auto* info : infos ) { @@ -635,12 +635,12 @@ findEssentialLVs( const QList< PartitionCoreModule::DeviceInfo* >& infos ) cDebug() << Logger::SubEntry << partPath << "is an essential LV filesystem=" << partition->fileSystem().type(); QString lvName = partPath.right( partPath.length() - devicePath.length() ); - doNotClose.append( info->device->name() + '-' + lvName ); + essentialLV.append( info->device->name() + '-' + lvName ); } } } } - return doNotClose; + return essentialLV; } Calamares::JobList @@ -670,14 +670,14 @@ PartitionCoreModule::jobs( const Config* config ) const #ifdef DEBUG_PARTITION_SKIP cWarning() << "Partitioning actions are skipped."; #else - const QStringList doNotClose = findEssentialLVs( m_deviceInfos ); + const QStringList essentialMounts = findEssentialLVs( m_deviceInfos ) + config->essentialMounts(); for ( const auto* info : m_deviceInfos ) { if ( info->isDirty() ) { auto* job = new ClearMountsJob( info->device.data() ); - job->setMapperExceptions( doNotClose ); + job->setMapperExceptions( essentialMounts ); lst << Calamares::job_ptr( job ); } } diff --git a/src/modules/partition/jobs/ClearMountsJob.cpp b/src/modules/partition/jobs/ClearMountsJob.cpp index 2fbafb5dc754d40dccbb18aa512c67d6a3712287..b4ebfebf490d3759ad51fcef42d3b0402c58bff5 100644 --- a/src/modules/partition/jobs/ClearMountsJob.cpp +++ b/src/modules/partition/jobs/ClearMountsJob.cpp @@ -123,6 +123,23 @@ isSpecial( const QString& baseName ) return specialForFedora || specialMapperControl || specialVentoy; } +static inline bool +matchesExceptions( const QStringList& mapperExceptions, const QString& basename ) +{ + for ( const auto& e : mapperExceptions ) + { + if ( basename == e ) + { + return true; + } + if ( e.endsWith( '*' ) && basename.startsWith( e.left( e.length() - 1 ) ) ) + { + return true; + } + } + return false; +} + /** @brief Returns a list of unneeded crypto devices * * These are the crypto devices to unmount and close; some are "needed" @@ -139,7 +156,7 @@ getCryptoDevices( const QStringList& mapperExceptions ) for ( const QFileInfo& fi : fiList ) { QString baseName = fi.baseName(); - if ( isSpecial( baseName ) || mapperExceptions.contains( baseName ) ) + if ( isSpecial( baseName ) || matchesExceptions( mapperExceptions, baseName ) ) { continue; } diff --git a/src/modules/partition/partition.conf b/src/modules/partition/partition.conf index 2f56df71582a368bcf64d3f69a6a718f1a3b0b45..4c78f58140fa65cb84893371545ff998f5038fc5 100644 --- a/src/modules/partition/partition.conf +++ b/src/modules/partition/partition.conf @@ -266,6 +266,13 @@ defaultFileSystemType: "ext4" # even if the directory is part of a filesystem on a # different mountpoint. Defaults to false. +# The ClearMounts job unmounts / unmaps things before partitioning. +# Some special entries under /dev/mapper are excepted from this process. +# The example lists the three hard-coded exceptions which always apply +# (they don't need to be listed here). Add other names or wildcards (with +# a trailing '*') to this list if the live-ISO has additional mounts. +essentialMounts: [ "live-*", "control", "ventoy" ] + # Show/hide LUKS related functionality in automated partitioning modes. # Disable this if you choose not to deploy early unlocking support in GRUB2 # and/or your distribution's initramfs solution. diff --git a/src/modules/partition/partition.schema.yaml b/src/modules/partition/partition.schema.yaml index 4bd2fa4ae0f6e94d3e759f9c4b205377ad0e2bdf..07763aef4ca6879f6a203047cdf9f39743091020 100644 --- a/src/modules/partition/partition.schema.yaml +++ b/src/modules/partition/partition.schema.yaml @@ -42,6 +42,7 @@ properties: luksGeneration: { type: string, enum: [luks1, luks2] } # Also allows "luks" as alias of "luks1" enableLuksAutomatedPartitioning: { type: boolean, default: false } preCheckEncryption: { type: boolean, default: false } + essentialMounts: { type: array, items: { type: string } } # List of names under /dev/mapper not to close allowManualPartitioning: { type: boolean, default: true } showNotEncryptedBootMessage: { type: boolean, default: true }