calamares/src/libcalamares/Settings.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

484 lines
14 KiB
C++
Raw Normal View History

/* === This file is part of Calamares - <https://calamares.io> ===
*
[libcalamares] Update SPDX identifiers. Update CppJob.h Update CalamaresConfig.h.in Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.cpp Update CalamaresConfig.h.in Update CppJob.cpp Update CppJob.h Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.h Update ProcessJob.cpp Update ProcessJob.h Update PythonHelper.cpp Update PythonJob.cpp Update PythonJob.h Update PythonHelper.h Update PythonJobApi.cpp Update PythonJobApi.h Update Settings.cpp Update Settings.h Update GeoIPJSON.cpp Update GeoIPJSON.h Update GeoIPTests.cpp Update GeoIPTests.h Update GeoIPXML.cpp Update GeoIPXML.h Update Handler.cpp Update Handler.h Update Interface.cpp Update Interface.h Update test_geoip.cpp Update CountryData_p.cpp Update Label.cpp Update Label.h Update LabelModel.cpp Update LabelModel.h Update CountryData_p.cpp Update CountryData_p.cpp Update Lookup.cpp Update Lookup.h Update Tests.cpp Update Tests.h Update TimeZone.cpp Update TimeZone.h Update TranslatableConfiguration.cpp Update TranslatableConfiguration.h Update ZoneData_p.cxxtr Update cldr-extractor.py Update zone-extractor.py Update Actions.h Update Actions.h Update Descriptor.h Update InstanceKey.cpp Update Module.cpp Update Module.h Update Requirement.cpp Update RequirementsChecker.h Update RequirementsModel.cpp Update RequirementsModel.h Update Tests.cpp Update Manager.cpp Update Manager.h Update Tests.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update KPMTests.cpp Update FileSystem.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update Mount.cpp Update Mount.h Update PartitionIterator.cpp Update PartitionIterator.h Update PartitionIterator.h Update PartitionQuery.cpp Update PartitionQuery.h Update PartitionSize.cpp Update PartitionSize.h Update Sync.cpp Update Sync.h Update Tests.cpp Update Tests.h Update BoostPython.h Update CalamaresUtilsSystem.cpp Update CalamaresUtilsSystem.h Update CommandList.cpp Update CommandList.h Update Dirs.cpp Update Dirs.h Update Entropy.cpp Update Entropy.h Update Entropy.cpp Update Logger.cpp Update Logger.h Update NamedEnum.h Update NamedSuffix.h Update PluginFactory.cpp Update PluginFactory.h Update RAII.h Update RAII.h Update Retranslator.cpp Update Retranslator.h Update String.cpp Update String.h Update TestPaths.cpp Update Tests.cpp Update Tests.h Update UMask.cpp Update UMask.h Update Units.h Update Variant.cpp Update Variant.h Update Yaml.cpp Update Yaml.h Update moc-warnings.h
2020-05-30 16:15:03 +02:00
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2019 Gabriel Craciunescu <crazy@frugalware.org>
* SPDX-FileCopyrightText: 2019 Dominic Hayes <ferenosdev@outlook.com>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
[libcalamares] Update SPDX identifiers. Update CppJob.h Update CalamaresConfig.h.in Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.cpp Update CalamaresConfig.h.in Update CppJob.cpp Update CppJob.h Update DllMacro.h Update GlobalStorage.cpp Update GlobalStorage.h Update Job.cpp Update Job.h Update JobExample.cpp Update JobExample.h Update JobQueue.h Update ProcessJob.cpp Update ProcessJob.h Update PythonHelper.cpp Update PythonJob.cpp Update PythonJob.h Update PythonHelper.h Update PythonJobApi.cpp Update PythonJobApi.h Update Settings.cpp Update Settings.h Update GeoIPJSON.cpp Update GeoIPJSON.h Update GeoIPTests.cpp Update GeoIPTests.h Update GeoIPXML.cpp Update GeoIPXML.h Update Handler.cpp Update Handler.h Update Interface.cpp Update Interface.h Update test_geoip.cpp Update CountryData_p.cpp Update Label.cpp Update Label.h Update LabelModel.cpp Update LabelModel.h Update CountryData_p.cpp Update CountryData_p.cpp Update Lookup.cpp Update Lookup.h Update Tests.cpp Update Tests.h Update TimeZone.cpp Update TimeZone.h Update TranslatableConfiguration.cpp Update TranslatableConfiguration.h Update ZoneData_p.cxxtr Update cldr-extractor.py Update zone-extractor.py Update Actions.h Update Actions.h Update Descriptor.h Update InstanceKey.cpp Update Module.cpp Update Module.h Update Requirement.cpp Update RequirementsChecker.h Update RequirementsModel.cpp Update RequirementsModel.h Update Tests.cpp Update Manager.cpp Update Manager.h Update Tests.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update KPMTests.cpp Update FileSystem.cpp Update FileSystem.cpp Update FileSystem.h Update KPMManager.cpp Update KPMManager.h Update Mount.cpp Update Mount.h Update PartitionIterator.cpp Update PartitionIterator.h Update PartitionIterator.h Update PartitionQuery.cpp Update PartitionQuery.h Update PartitionSize.cpp Update PartitionSize.h Update Sync.cpp Update Sync.h Update Tests.cpp Update Tests.h Update BoostPython.h Update CalamaresUtilsSystem.cpp Update CalamaresUtilsSystem.h Update CommandList.cpp Update CommandList.h Update Dirs.cpp Update Dirs.h Update Entropy.cpp Update Entropy.h Update Entropy.cpp Update Logger.cpp Update Logger.h Update NamedEnum.h Update NamedSuffix.h Update PluginFactory.cpp Update PluginFactory.h Update RAII.h Update RAII.h Update Retranslator.cpp Update Retranslator.h Update String.cpp Update String.h Update TestPaths.cpp Update Tests.cpp Update Tests.h Update UMask.cpp Update UMask.h Update Units.h Update Variant.cpp Update Variant.h Update Yaml.cpp Update Yaml.h Update moc-warnings.h
2020-05-30 16:15:03 +02:00
*
*/
#include "Settings.h"
#include "CalamaresConfig.h"
#include "compat/Variant.h"
#include "utils/Dirs.h"
2014-06-12 18:47:50 +02:00
#include "utils/Logger.h"
#include "utils/Yaml.h"
2014-06-12 18:47:50 +02:00
#include <QDir>
#include <QFile>
#include <QPair>
static bool
hasValue( const YAML::Node& v )
{
return v.IsDefined() && !v.IsNull();
}
/** @brief Helper function to grab a QString out of the config, and to warn if not present. */
static QString
requireString( const ::YAML::Node& config, const char* key )
{
auto v = config[ key ];
if ( hasValue( v ) )
{
return QString::fromStdString( v.as< std::string >() );
}
else
{
cWarning() << Logger::SubEntry << "Required settings.conf key" << key << "is missing.";
return QString();
}
}
/** @brief Helper function to grab a bool out of the config, and to warn if not present. */
static bool
requireBool( const ::YAML::Node& config, const char* key, bool d )
{
auto v = config[ key ];
if ( hasValue( v ) )
{
return v.as< bool >();
}
else
{
cWarning() << Logger::SubEntry << "Required settings.conf key" << key << "is missing.";
return d;
}
}
2014-06-12 18:47:50 +02:00
namespace Calamares
{
InstanceDescription::InstanceDescription( const Calamares::ModuleSystem::InstanceKey& key )
: m_instanceKey( key )
, m_weight( -1 )
{
if ( !isValid() )
{
m_weight = 0;
}
else
{
m_configFileName = key.module() + QStringLiteral( ".conf" );
}
}
InstanceDescription
InstanceDescription::fromSettings( const QVariantMap& m )
{
InstanceDescription r(
Calamares::ModuleSystem::InstanceKey( m.value( "module" ).toString(), m.value( "id" ).toString() ) );
if ( r.isValid() )
{
if ( m.value( "weight" ).isValid() )
{
int w = qBound( 1, m.value( "weight" ).toInt(), 100 );
r.m_weight = w;
}
QString c = m.value( "config" ).toString();
if ( !c.isEmpty() )
{
r.m_configFileName = c;
}
}
return r;
}
Settings* Settings::s_instance = nullptr;
2014-06-12 18:47:50 +02:00
Settings*
Settings::instance()
{
if ( !s_instance )
{
cWarning() << "Getting nullptr Settings instance.";
}
2014-06-12 18:47:50 +02:00
return s_instance;
}
static void
interpretModulesSearch( const bool debugMode, const QStringList& rawPaths, QStringList& output )
{
for ( const auto& path : rawPaths )
{
if ( path == "local" )
{
// If we're running in debug mode, we assume we might also be
// running from the build dir, so we add a maximum priority
// module search path in the build dir.
if ( debugMode )
{
QString buildDirModules
= QDir::current().absolutePath() + QDir::separator() + "src" + QDir::separator() + "modules";
if ( QDir( buildDirModules ).exists() )
{
output.append( buildDirModules );
}
}
// Install path is set in CalamaresAddPlugin.cmake
output.append( Calamares::systemLibDir().absolutePath() + QDir::separator() + "calamares"
+ QDir::separator() + "modules" );
}
else
{
QDir d( path );
if ( d.exists() && d.isReadable() )
{
output.append( d.absolutePath() );
}
else
{
cDebug() << Logger::SubEntry << "module-search entry non-existent" << path;
}
}
}
}
static void
interpretInstances( const ::YAML::Node& node, Settings::InstanceDescriptionList& customInstances )
{
// Parse the custom instances section
if ( node )
{
QVariant instancesV = Calamares::YAML::toVariant( node ).toList();
if ( typeOf( instancesV ) == ListVariantType )
{
const auto instances = instancesV.toList();
for ( const QVariant& instancesVListItem : instances )
{
if ( typeOf( instancesVListItem ) != MapVariantType )
{
continue;
}
auto description = InstanceDescription::fromSettings( instancesVListItem.toMap() );
if ( !description.isValid() )
{
cWarning() << "Invalid entry in *instances*" << instancesVListItem;
}
// Append it **anyway**, since this will bail out after Settings is constructed
customInstances.append( description );
}
}
}
}
static void
interpretSequence( const ::YAML::Node& node, Settings::ModuleSequence& moduleSequence )
{
// Parse the modules sequence section
if ( node )
{
QVariant sequenceV = Calamares::YAML::toVariant( node );
if ( typeOf( sequenceV ) != ListVariantType )
{
throw ::YAML::Exception( ::YAML::Mark(), "sequence key does not have a list-value" );
}
const auto sequence = sequenceV.toList();
for ( const QVariant& sequenceVListItem : sequence )
{
if ( typeOf( sequenceVListItem ) != MapVariantType )
{
continue;
}
QString thisActionS = sequenceVListItem.toMap().firstKey();
ModuleSystem::Action thisAction;
if ( thisActionS == "show" )
{
thisAction = ModuleSystem::Action::Show;
}
else if ( thisActionS == "exec" )
{
thisAction = ModuleSystem::Action::Exec;
}
else
{
cDebug() << "Unknown action in *sequence*" << thisActionS;
continue;
}
QStringList thisActionRoster = sequenceVListItem.toMap().value( thisActionS ).toStringList();
Calamares::ModuleSystem::InstanceKeyList roster;
roster.reserve( thisActionRoster.count() );
for ( const auto& s : thisActionRoster )
{
auto instanceKey = Calamares::ModuleSystem::InstanceKey::fromString( s );
if ( !instanceKey.isValid() )
{
cWarning() << "Invalid instance in *sequence*" << s;
}
roster.append( instanceKey );
}
moduleSequence.append( qMakePair( thisAction, roster ) );
}
}
else
{
throw ::YAML::Exception( ::YAML::Mark(), "sequence key is missing" );
}
}
Settings::Settings( bool debugMode )
: QObject()
, m_debug( debugMode )
, m_doChroot( true )
, m_promptInstall( false )
, m_disableCancel( false )
, m_disableCancelDuringExec( false )
{
cWarning() << "Using bogus Calamares settings in"
<< ( debugMode ? QStringLiteral( "debug" ) : QStringLiteral( "regular" ) ) << "mode";
s_instance = this;
}
Settings::Settings( const QString& settingsFilePath, bool debugMode )
: QObject()
, m_settingsPath( settingsFilePath )
, m_debug( debugMode )
, m_doChroot( true )
2017-06-21 13:22:08 +02:00
, m_promptInstall( false )
, m_disableCancel( false )
, m_disableCancelDuringExec( false )
{
2014-08-06 17:23:40 +02:00
cDebug() << "Using Calamares settings file at" << settingsFilePath;
QFile file( settingsFilePath );
if ( file.exists() && file.open( QFile::ReadOnly | QFile::Text ) )
2014-06-12 18:47:50 +02:00
{
setConfiguration( file.readAll(), file.fileName() );
2014-06-12 18:47:50 +02:00
}
else
{
cWarning() << "Cannot read settings file" << file.fileName();
2014-06-12 18:47:50 +02:00
}
s_instance = this;
}
bool
Settings::isModuleEnabled( const QString& module ) const
2021-10-23 20:57:24 +02:00
{
// Iterate over the list of modules searching for a match
2021-10-28 21:46:38 +02:00
for ( const auto& moduleInstance : qAsConst( m_moduleInstances ) )
2021-10-23 20:57:24 +02:00
{
if ( moduleInstance.key().module() == module )
2021-10-28 21:46:38 +02:00
{
2021-10-23 20:57:24 +02:00
return true;
2021-10-28 21:46:38 +02:00
}
2021-10-23 20:57:24 +02:00
}
return false;
}
void
Settings::reconcileInstancesAndSequence()
{
// Since moduleFinder captures targetKey by reference, we can
// update targetKey to change what the finder lambda looks for.
Calamares::ModuleSystem::InstanceKey targetKey;
2021-10-28 21:46:38 +02:00
auto moduleFinder = [ &targetKey ]( const InstanceDescription& d ) { return d.isValid() && d.key() == targetKey; };
// Check the sequence against the existing instances (which so far are only custom)
for ( const auto& step : m_modulesSequence )
{
for ( const auto& instanceKey : step.second )
{
targetKey = instanceKey;
const auto it = std::find_if( m_moduleInstances.constBegin(), m_moduleInstances.constEnd(), moduleFinder );
if ( it == m_moduleInstances.constEnd() )
{
if ( instanceKey.isCustom() )
{
cWarning() << "Custom instance key" << instanceKey << "is not listed in the *instances*";
}
m_moduleInstances.append( InstanceDescription( instanceKey ) );
}
}
}
}
void
Settings::setConfiguration( const QByteArray& ba, const QString& explainName )
{
try
{
// Not using Calamares::YAML:: convenience methods because we **want** the exception here
auto config = ::YAML::Load( ba.constData() );
Q_ASSERT( config.IsMap() );
interpretModulesSearch(
debugMode(), Calamares::YAML::toStringList( config[ "modules-search" ] ), m_modulesSearchPaths );
interpretInstances( config[ "instances" ], m_moduleInstances );
interpretSequence( config[ "sequence" ], m_modulesSequence );
m_brandingComponentName = requireString( config, "branding" );
m_promptInstall = requireBool( config, "prompt-install", false );
m_doChroot = !requireBool( config, "dont-chroot", false );
m_isSetupMode = requireBool( config, "oem-setup", !m_doChroot );
m_disableCancel = requireBool( config, "disable-cancel", false );
m_disableCancelDuringExec = requireBool( config, "disable-cancel-during-exec", false );
2020-12-06 00:02:18 +01:00
m_hideBackAndNextDuringExec = requireBool( config, "hide-back-and-next-during-exec", false );
m_quitAtEnd = requireBool( config, "quit-at-end", false );
reconcileInstancesAndSequence();
}
catch ( ::YAML::Exception& e )
{
Calamares::YAML::explainException( e, ba, explainName );
}
}
2014-06-12 18:47:50 +02:00
QStringList
Settings::modulesSearchPaths() const
2014-06-12 18:47:50 +02:00
{
return m_modulesSearchPaths;
}
Settings::InstanceDescriptionList
Settings::moduleInstances() const
2014-06-12 18:47:50 +02:00
{
return m_moduleInstances;
}
2018-06-26 14:41:16 +02:00
Settings::ModuleSequence
Settings::modulesSequence() const
{
return m_modulesSequence;
2014-06-12 18:47:50 +02:00
}
QString
Settings::brandingComponentName() const
{
return m_brandingComponentName;
}
static QStringList
settingsFileCandidates( bool assumeBuilddir )
{
static const char settings[] = "settings.conf";
QStringList settingsPaths;
if ( Calamares::isAppDataDirOverridden() )
{
settingsPaths << Calamares::appDataDir().absoluteFilePath( settings );
}
else
{
if ( assumeBuilddir )
{
settingsPaths << QDir::current().absoluteFilePath( settings );
}
if ( Calamares::haveExtraDirs() )
{
for ( auto s : Calamares::extraConfigDirs() )
{
settingsPaths << ( s + settings );
}
}
settingsPaths << CMAKE_INSTALL_FULL_SYSCONFDIR "/calamares/settings.conf"; // String concat
settingsPaths << Calamares::appDataDir().absoluteFilePath( settings );
}
return settingsPaths;
}
Settings*
Settings::init( bool debugMode )
{
if ( s_instance )
{
cWarning() << "Calamares::Settings already created";
return s_instance;
}
QStringList settingsFileCandidatesByPriority = settingsFileCandidates( debugMode );
QFileInfo settingsFile;
bool found = false;
foreach ( const QString& path, settingsFileCandidatesByPriority )
{
QFileInfo pathFi( path );
if ( pathFi.exists() && pathFi.isReadable() )
{
settingsFile = pathFi;
found = true;
break;
}
}
if ( !found || !settingsFile.exists() || !settingsFile.isReadable() )
{
cError() << "Cowardly refusing to continue startup without settings."
<< Logger::DebugList( settingsFileCandidatesByPriority );
if ( Calamares::isAppDataDirOverridden() )
{
cError() << "FATAL: explicitly configured application data directory is missing settings.conf";
}
else
{
cError() << "FATAL: none of the expected configuration file paths exist.";
}
::exit( EXIT_FAILURE );
}
auto* settings = new Calamares::Settings( settingsFile.absoluteFilePath(), debugMode ); // Creates singleton
if ( settings->modulesSequence().count() < 1 )
{
cError() << "FATAL: no sequence set.";
::exit( EXIT_FAILURE );
}
return settings;
}
Settings*
Settings::init( const QString& path )
{
if ( s_instance )
{
cWarning() << "Calamares::Settings already created";
return s_instance;
}
return new Calamares::Settings( path, true );
}
bool
Settings::isValid() const
{
if ( brandingComponentName().isEmpty() )
{
cWarning() << "No branding component is set";
return false;
}
const auto invalidDescriptor = []( const InstanceDescription& d ) { return !d.isValid(); };
const auto invalidDescriptorIt
= std::find_if( m_moduleInstances.constBegin(), m_moduleInstances.constEnd(), invalidDescriptor );
if ( invalidDescriptorIt != m_moduleInstances.constEnd() )
{
cWarning() << "Invalid module instance in *instances* or *sequence*";
return false;
}
return true;
}
} // namespace Calamares