/* === This file is part of Calamares - === * * Copyright 2014-2015, Teo Mrnjavac * Copyright 2017-2018, Adriaan de Groot * * Calamares is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Calamares is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Calamares. If not, see . */ #include "Module.h" #include "CalamaresConfig.h" #include "CppJobModule.h" #include "ProcessJobModule.h" #include "Settings.h" #include "ViewModule.h" #include "utils/Dirs.h" #include "utils/Logger.h" #include "utils/Yaml.h" #ifdef WITH_PYTHON #include "PythonJobModule.h" #endif #ifdef WITH_PYTHONQT #include "PythonQtViewModule.h" #endif #include #include #include #include static const char EMERGENCY[] = "emergency"; namespace Calamares { Module::~Module() {} Module* Module::fromDescriptor( const QVariantMap& moduleDescriptor, const QString& instanceId, const QString& configFileName, const QString& moduleDirectory ) { std::unique_ptr m; QString typeString = moduleDescriptor.value( "type" ).toString(); QString intfString = moduleDescriptor.value( "interface" ).toString(); if ( typeString.isEmpty() || intfString.isEmpty() ) { cError() << "Bad module descriptor format" << instanceId; return nullptr; } if ( ( typeString == "view" ) || ( typeString == "viewmodule" ) ) { if ( intfString == "qtplugin" ) m.reset( new ViewModule() ); else if ( intfString == "pythonqt" ) { #ifdef WITH_PYTHONQT m.reset( new PythonQtViewModule() ); #else cError() << "PythonQt view modules are not supported in this version of Calamares."; #endif } else cError() << "Bad interface" << intfString << "for module type" << typeString; } else if ( typeString == "job" ) { if ( intfString == "qtplugin" ) m.reset( new CppJobModule() ); else if ( intfString == "process" ) m.reset( new ProcessJobModule() ); else if ( intfString == "python" ) { #ifdef WITH_PYTHON m.reset( new PythonJobModule() ); #else cError() << "Python modules are not supported in this version of Calamares."; #endif } else cError() << "Bad interface" << intfString << "for module type" << typeString; } else cError() << "Bad module type" << typeString; if ( !m ) { cError() << "Bad module type (" << typeString << ") or interface string (" << intfString << ") for module " << instanceId; return nullptr; } QDir moduleDir( moduleDirectory ); if ( moduleDir.exists() && moduleDir.isReadable() ) m->m_directory = moduleDir.absolutePath(); else { cError() << "Bad module directory" << moduleDirectory << "for" << instanceId; return nullptr; } m->m_instanceId = instanceId; m->initFrom( moduleDescriptor ); try { m->loadConfigurationFile( configFileName ); } catch ( YAML::Exception& e ) { cError() << "YAML parser error " << e.what(); return nullptr; } return m.release(); } static QStringList moduleConfigurationCandidates( bool assumeBuildDir, const QString& moduleName, const QString& configFileName ) { QStringList paths; if ( CalamaresUtils::isAppDataDirOverridden() ) paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) ); else { // If an absolute path is given, in debug mode, look for it // first. The case contains('/'), below, will add the absolute // path a second time, though. if ( assumeBuildDir && configFileName.startsWith( '/' ) ) paths << configFileName; if ( assumeBuildDir ) paths << QDir().absoluteFilePath(QString( "src/modules/%1/%2" ).arg( moduleName ).arg( configFileName ) ); if ( assumeBuildDir && configFileName.contains( '/' ) ) paths << QDir().absoluteFilePath( configFileName ); if ( CalamaresUtils::haveExtraDirs() ) for ( auto s : CalamaresUtils::extraConfigDirs() ) paths << ( s + QString( "modules/%1" ).arg( configFileName ) ); paths << QString( "/etc/calamares/modules/%1" ).arg( configFileName ); paths << CalamaresUtils::appDataDir().absoluteFilePath( QString( "modules/%1" ).arg( configFileName ) ); } return paths; } void Module::loadConfigurationFile( const QString& configFileName ) //throws YAML::Exception { QStringList configCandidates = moduleConfigurationCandidates( Settings::instance()->debugMode(), m_name, configFileName ); for ( const QString& path : configCandidates ) { QFile configFile( path ); if ( configFile.exists() && configFile.open( QFile::ReadOnly | QFile::Text ) ) { QByteArray ba = configFile.readAll(); YAML::Node doc = YAML::Load( ba.constData() ); if ( doc.IsNull() ) { cDebug() << "Found empty module configuration" << path; // Special case: empty config files are valid, // but aren't a map. return; } if ( !doc.IsMap() ) { cWarning() << "Bad module configuration format" << path; return; } cDebug() << "Loaded module configuration" << path; m_configurationMap = CalamaresUtils::yamlMapToVariant( doc ).toMap(); m_emergency = m_maybe_emergency && m_configurationMap.contains( EMERGENCY ) && m_configurationMap[ EMERGENCY ].toBool(); return; } } cDebug() << "No config file found in" << Logger::DebugList( configCandidates ); } QString Module::name() const { return m_name; } QString Module::instanceId() const { return m_instanceId; } QString Module::instanceKey() const { return QString( "%1@%2" ).arg( m_name ).arg( m_instanceId ); } QString Module::location() const { return m_directory; } QString Module::typeString() const { switch ( type() ) { case Type::Job: return "Job Module"; case Type::View: return "View Module"; } return QString(); } QString Module::interfaceString() const { switch ( interface() ) { case Interface::Process: return "External process"; case Interface::Python: return "Python (Boost.Python)"; case Interface::PythonQt: return "Python (experimental)"; case Interface::QtPlugin: return "Qt Plugin"; } return QString(); } QVariantMap Module::configurationMap() { return m_configurationMap; } Module::Module() : m_loaded( false ) {} void Module::initFrom( const QVariantMap& moduleDescriptor ) { m_name = moduleDescriptor.value( "name" ).toString(); if ( moduleDescriptor.contains( EMERGENCY ) ) m_maybe_emergency = moduleDescriptor[ EMERGENCY ].toBool(); } RequirementsList Module::checkRequirements() { return RequirementsList(); } } //ns