From 03c7357dbcb70c047c0aac0a2566c50aaaed1168 Mon Sep 17 00:00:00 2001 From: Kevin Kofler Date: Sun, 25 Sep 2016 03:20:50 +0200 Subject: [PATCH] Add support for LUKS full disk encryption with dracut. Add support for C++/Qt batch job plugins These job plugins work similarly to view modules, with the following differences: * These jobs need to link only libcalamares, not libcalamaresui. For this reason, PluginFactory was moved from libcalamaresui to libcalamares. (It depends only on QtCore.) * Instead of deriving from ViewModule, derive from CppJob (which is a subclass of Job). * Like process and Python jobs, a job plugin is a single job, whereas a ViewModule can generate a whole list of jobs. The CppJob and CppJobModule classes are new. In Module::fromDescriptor, the combination type=job, intf=qtplugin is now supported and mapped to CppJobModule. [fstab] Do not omit "/" from crypttab. At least the Debian update-initramfs needs the entry to be there (see pull request #254). Dracut will probably need it too. And logically, it should be there. [fstab] Write configurable options to crypttab (default: luks). fstab.conf: Add a new "crypttabOptions" option that defaults to "luks". Document that for Debian and Debian-based distributions, the setting should be changed to "luks,keyscript=/bin/cat". main.py: Append the options from the above setting to the end of every line in crypttab. At least the "luks" option should always be there, because there may be different encryption types. The Debian initramfs-tools also require the Debian-specific keyscript option and will otherwise ignore the keyfile entirely (see pull request #254). [dracutlukscfg] New module: pre-configuration for dracut+LUKS. Add a dracutlukscfg module to write a /etc/dracut.conf.d/calamares-luks.conf file for LUKS full disk encryption support with Dracut. You should run: * partition * luksbootkeyfile * dracutlukscfg * dracut in that order (not necessarily in immediate sequence). The luksopenswaphook module is not used with Dracut. --- settings.conf | 1 + src/libcalamares/CMakeLists.txt | 2 + src/libcalamares/CppJob.cpp | 45 +++ src/libcalamares/CppJob.h | 51 ++++ src/libcalamares/utils/PluginFactory.cpp | 123 ++++++++ src/libcalamares/utils/PluginFactory.h | 370 +++++++++++++++++++++++ src/libcalamares/utils/PluginFactory_p.h | 54 ++++ src/libcalamaresui/CMakeLists.txt | 3 +- src/libcalamaresui/modulesystem/CppJobModule.cpp | 128 ++++++++ src/libcalamaresui/modulesystem/CppJobModule.h | 53 ++++ src/libcalamaresui/modulesystem/Module.cpp | 7 +- src/libcalamaresui/utils/PluginFactory.cpp | 123 -------- src/libcalamaresui/utils/PluginFactory.h | 370 ----------------------- src/libcalamaresui/utils/PluginFactory_p.h | 54 ---- src/modules/dracutlukscfg/CMakeLists.txt | 9 + src/modules/dracutlukscfg/DracutLuksCfgJob.cpp | 143 +++++++++ src/modules/dracutlukscfg/DracutLuksCfgJob.h | 56 ++++ src/modules/dracutlukscfg/module.desc | 7 + src/modules/fstab/fstab.conf | 3 + src/modules/fstab/main.py | 21 +- 20 files changed, 1064 insertions(+), 559 deletions(-) create mode 100644 src/libcalamares/CppJob.cpp create mode 100644 src/libcalamares/CppJob.h create mode 100644 src/libcalamares/utils/PluginFactory.cpp create mode 100644 src/libcalamares/utils/PluginFactory.h create mode 100644 src/libcalamares/utils/PluginFactory_p.h create mode 100644 src/libcalamaresui/modulesystem/CppJobModule.cpp create mode 100644 src/libcalamaresui/modulesystem/CppJobModule.h delete mode 100644 src/libcalamaresui/utils/PluginFactory.cpp delete mode 100644 src/libcalamaresui/utils/PluginFactory.h delete mode 100644 src/libcalamaresui/utils/PluginFactory_p.h create mode 100644 src/modules/dracutlukscfg/CMakeLists.txt create mode 100644 src/modules/dracutlukscfg/DracutLuksCfgJob.cpp create mode 100644 src/modules/dracutlukscfg/DracutLuksCfgJob.h create mode 100644 src/modules/dracutlukscfg/module.desc diff --git a/settings.conf b/settings.conf index 0721098..d4439a7 100644 --- a/settings.conf +++ b/settings.conf @@ -79,6 +79,7 @@ sequence: - localecfg # - luksbootkeyfile # - luksopenswaphookcfg +# - dracutlukscfg - initcpiocfg - initcpio - users diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index 966ca9c..b1f6c48 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -11,6 +11,7 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../calamares/CalamaresVersion.h.in ${CMAKE_CURRENT_BINARY_DIR}/CalamaresVersion.h ) set( libSources + CppJob.cpp GlobalStorage.cpp Job.cpp JobQueue.cpp @@ -24,6 +25,7 @@ set( libSources utils/CalamaresUtils.cpp utils/CalamaresUtilsSystem.cpp utils/Logger.cpp + utils/PluginFactory.cpp utils/Retranslator.cpp ) diff --git a/src/libcalamares/CppJob.cpp b/src/libcalamares/CppJob.cpp new file mode 100644 index 0000000..1925e39 --- /dev/null +++ b/src/libcalamares/CppJob.cpp @@ -0,0 +1,45 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * Copyright 2016, Kevin Kofler + * + * 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 "CppJob.h" + +namespace Calamares +{ + +CppJob::CppJob( QObject* parent ) + : Job( parent ) +{} + + +CppJob::~CppJob() +{} + + +void +CppJob::setModuleInstanceKey( const QString& instanceKey ) +{ + m_instanceKey = instanceKey; +} + + +void +CppJob::setConfigurationMap( const QVariantMap& configurationMap ) +{} + +} diff --git a/src/libcalamares/CppJob.h b/src/libcalamares/CppJob.h new file mode 100644 index 0000000..a6e6735 --- /dev/null +++ b/src/libcalamares/CppJob.h @@ -0,0 +1,51 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014-2015, Teo Mrnjavac + * Copyright 2016, Kevin Kofler + * + * 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 . + */ + +#ifndef CALAMARES_CPPJOB_H +#define CALAMARES_CPPJOB_H + +#include +#include + +#include "DllMacro.h" +#include "Typedefs.h" +#include "Job.h" + +namespace Calamares +{ + +class DLLEXPORT CppJob : public Job +{ + Q_OBJECT +public: + explicit CppJob( QObject* parent = nullptr ); + virtual ~CppJob(); + + void setModuleInstanceKey( const QString& instanceKey ); + QString moduleInstanceKey() const { return m_instanceKey; } + + virtual void setConfigurationMap( const QVariantMap& configurationMap ); + +protected: + QString m_instanceKey; +}; + +} + +#endif // CALAMARES_CPPJOB_H diff --git a/src/libcalamares/utils/PluginFactory.cpp b/src/libcalamares/utils/PluginFactory.cpp new file mode 100644 index 0000000..30a5bf4 --- /dev/null +++ b/src/libcalamares/utils/PluginFactory.cpp @@ -0,0 +1,123 @@ +/* === This file is part of Calamares - === + * + * Copyright 2015, Teo Mrnjavac + * + * Based on KPluginFactory from KCoreAddons, KDE project + * Copyright 2007, Matthias Kretz + * Copyright 2007, Bernhard Loos + * + * 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 "PluginFactory.h" +#include "PluginFactory_p.h" + +#include +#include + +Q_GLOBAL_STATIC(QObjectCleanupHandler, factorycleanup) + +extern int kLibraryDebugArea(); + +namespace Calamares +{ + +PluginFactory::PluginFactory() + : d_ptr(new PluginFactoryPrivate) +{ + Q_D(PluginFactory); + d->q_ptr = this; + + factorycleanup()->add(this); +} + +PluginFactory::PluginFactory(PluginFactoryPrivate &d) + : d_ptr(&d) +{ + factorycleanup()->add(this); +} + +PluginFactory::~PluginFactory() +{ + delete d_ptr; +} + +void PluginFactory::doRegisterPlugin(const QString &keyword, const QMetaObject *metaObject, CreateInstanceFunction instanceFunction) +{ + Q_D(PluginFactory); + + Q_ASSERT(metaObject); + + // we allow different interfaces to be registered without keyword + if (!keyword.isEmpty()) { + if (d->createInstanceHash.contains(keyword)) { + qWarning() << "A plugin with the keyword" << keyword << "was already registered. A keyword must be unique!"; + } + d->createInstanceHash.insert(keyword, PluginFactoryPrivate::Plugin(metaObject, instanceFunction)); + } else { + const QList clashes(d->createInstanceHash.values(keyword)); + const QMetaObject *superClass = metaObject->superClass(); + if (superClass) { + for (const PluginFactoryPrivate::Plugin &plugin : clashes) { + for (const QMetaObject *otherSuper = plugin.first->superClass(); otherSuper; + otherSuper = otherSuper->superClass()) { + if (superClass == otherSuper) { + qWarning() << "Two plugins with the same interface(" << superClass->className() << ") were registered. Use keywords to identify the plugins."; + } + } + } + } + for (const PluginFactoryPrivate::Plugin &plugin : clashes) { + superClass = plugin.first->superClass(); + if (superClass) { + for (const QMetaObject *otherSuper = metaObject->superClass(); otherSuper; + otherSuper = otherSuper->superClass()) { + if (superClass == otherSuper) { + qWarning() << "Two plugins with the same interface(" << superClass->className() << ") were registered. Use keywords to identify the plugins."; + } + } + } + } + d->createInstanceHash.insertMulti(keyword, PluginFactoryPrivate::Plugin(metaObject, instanceFunction)); + } +} + +QObject *PluginFactory::create(const char *iface, QWidget *parentWidget, QObject *parent, const QString &keyword) +{ + Q_D(PluginFactory); + + QObject *obj = 0; + + const QList candidates(d->createInstanceHash.values(keyword)); + // for !keyword.isEmpty() candidates.count() is 0 or 1 + + for (const PluginFactoryPrivate::Plugin &plugin : candidates) { + for (const QMetaObject *current = plugin.first; current; current = current->superClass()) { + if (0 == qstrcmp(iface, current->className())) { + if (obj) { + qWarning() << "ambiguous interface requested from a DSO containing more than one plugin"; + } + obj = plugin.second(parentWidget, parent); + break; + } + } + } + + if (obj) { + emit objectCreated(obj); + } + return obj; +} + +} diff --git a/src/libcalamares/utils/PluginFactory.h b/src/libcalamares/utils/PluginFactory.h new file mode 100644 index 0000000..c0053ba --- /dev/null +++ b/src/libcalamares/utils/PluginFactory.h @@ -0,0 +1,370 @@ +/* === This file is part of Calamares - === + * + * Copyright 2015, Teo Mrnjavac + * + * Based on KPluginFactory from KCoreAddons, KDE project + * Copyright 2007, Matthias Kretz + * Copyright 2007, Bernhard Loos + * + * 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 . + */ + +#ifndef CALAMARESPLUGINFACTORY_H +#define CALAMARESPLUGINFACTORY_H + +#include "DllMacro.h" + +#include +#include +#include + +namespace Calamares +{ +class PluginFactoryPrivate; +} + +#define CalamaresPluginFactory_iid "io.calamares.PluginFactory" + +#define CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY_SKEL(name, baseFactory, ...) \ + class name : public Calamares::PluginFactory \ + { \ + Q_OBJECT \ + Q_INTERFACES(Calamares::PluginFactory) \ + __VA_ARGS__ \ + public: \ + explicit name(); \ + ~name(); \ + private: \ + void init(); \ + }; + +#define CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, baseFactory) \ + CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY_SKEL(name, baseFactory, Q_PLUGIN_METADATA(IID CalamaresPluginFactory_iid)) + +#define CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, baseFactory, pluginRegistrations) \ + name::name() \ + { \ + pluginRegistrations \ + } \ + name::~name() {} + +#define CALAMARES_PLUGIN_FACTORY_WITH_BASEFACTORY(name, baseFactory, pluginRegistrations) \ + CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, baseFactory) \ + CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, baseFactory, pluginRegistrations) + +#define CALAMARES_PLUGIN_FACTORY_DECLARATION(name) CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, Calamares::PluginFactory) +#define CALAMARES_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations) CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, Calamares::PluginFactory, pluginRegistrations) + +/** + * \relates PluginFactory + * + * Create a PluginFactory subclass and export it as the root plugin object. + * + * \param name The name of the PluginFactory derived class. + * + * \param pluginRegistrations Code to be inserted into the constructor of the + * class. Usually a series of registerPlugin() calls. + * + * Example: + * \code + * #include + * #include + * + * class MyPlugin : public PluginInterface + * { + * public: + * MyPlugin(QObject *parent, const QVariantList &args) + * : PluginInterface(parent) + * {} + * }; + * + * CALAMARES_PLUGIN_FACTORY(MyPluginFactory, + * registerPlugin(); + * ) + * + * #include + * \endcode + * + * \see CALAMARES_PLUGIN_FACTORY_DECLARATION + * \see CALAMARES_PLUGIN_FACTORY_DEFINITION + */ +#define CALAMARES_PLUGIN_FACTORY(name, pluginRegistrations) CALAMARES_PLUGIN_FACTORY_WITH_BASEFACTORY(name, Calamares::PluginFactory, pluginRegistrations) + +/** + * \relates PluginFactory + * + * CALAMARES_PLUGIN_FACTORY_DECLARATION declares the PluginFactory subclass. This macro + * can be used in a header file. + * + * \param name The name of the PluginFactory derived class. + * + * \see CALAMARES_PLUGIN_FACTORY + * \see CALAMARES_PLUGIN_FACTORY_DEFINITION + */ +#define CALAMARES_PLUGIN_FACTORY_DECLARATION(name) CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, Calamares::PluginFactory) + +/** + * \relates PluginFactory + * CALAMARES_PLUGIN_FACTORY_DEFINITION defines the PluginFactory subclass. This macro + * can not be used in a header file. + * + * \param name The name of the PluginFactory derived class. + * + * \param pluginRegistrations Code to be inserted into the constructor of the + * class. Usually a series of registerPlugin() calls. + * + * \see CALAMARES_PLUGIN_FACTORY + * \see CALAMARES_PLUGIN_FACTORY_DECLARATION + */ +#define CALAMARES_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations) CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, Calamares::PluginFactory, pluginRegistrations) + +namespace Calamares +{ + +/** + * \class PluginFactory PluginFactory.h + * + * PluginFactory provides a convenient way to provide factory-style plugins. + * Qt plugins provide a singleton object, but a common pattern is for plugins + * to generate as many objects of a particular type as the application requires. + * By using PluginFactory, you can avoid implementing the factory pattern + * yourself. + * + * PluginFactory also allows plugins to provide multiple different object + * types, indexed by keywords. + * + * The objects created by PluginFactory must inherit QObject, and must have a + * standard constructor pattern: + * \li if the object is a KPart::Part, it must be of the form + * \code + * T(QWidget *parentWidget, QObject *parent, const QVariantList &args) + * \endcode + * \li if it is a QWidget, it must be of the form + * \code + * T(QWidget *parent, const QVariantList &args) + * \endcode + * \li otherwise it must be of the form + * \code + * T(QObject *parent, const QVariantList &args) + * \endcode + * + * You should typically use either CALAMARES_PLUGIN_FACTORY() or + * CALAMARES_PLUGIN_FACTORY_WITH_JSON() in your plugin code to create the factory. The + * typical pattern is + * + * \code + * #include + * #include + * + * class MyPlugin : public PluginInterface + * { + * public: + * MyPlugin(QObject *parent, const QVariantList &args) + * : PluginInterface(parent) + * {} + * }; + * + * CALAMARES_PLUGIN_FACTORY(MyPluginFactory, + * registerPlugin(); + * ) + * #include + * \endcode + * + * If you want to load a library use KPluginLoader. + * The application that wants to instantiate plugin classes can do the following: + * \code + * PluginFactory *factory = KPluginLoader("libraryname").factory(); + * if (factory) { + * PluginInterface *p1 = factory->create(parent); + * OtherInterface *p2 = factory->create(parent); + * NextInterface *p3 = factory->create("keyword1", parent); + * NextInterface *p3 = factory->create("keyword2", parent); + * } + * \endcode + * + * \author Matthias Kretz + * \author Bernhard Loos + */ +class DLLEXPORT PluginFactory : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(PluginFactory) +public: + /** + * This constructor creates a factory for a plugin. + */ + explicit PluginFactory(); + + /** + * This destroys the PluginFactory. + */ + virtual ~PluginFactory(); + + /** + * Use this method to create an object. It will try to create an object which inherits + * \p T. If it has multiple choices, you will get a fatal error (kFatal()), so be careful + * to request a unique interface or use keywords. + * + * \tparam T The interface for which an object should be created. The object will inherit \p T. + * \param parent The parent of the object. If \p parent is a widget type, it will also passed + * to the parentWidget argument of the CreateInstanceFunction for the object. + * \returns A pointer to the created object is returned, or 0 if an error occurred. + */ + template + T *create(QObject *parent = 0); + + /** + * Use this method to create an object. It will try to create an object which inherits + * \p T and was registered with \p keyword. + * + * \tparam T The interface for which an object should be created. The object will inherit \p T. + * \param keyword The keyword of the object. + * \param parent The parent of the object. If \p parent is a widget type, it will also passed + * to the parentWidget argument of the CreateInstanceFunction for the object. + * \returns A pointer to the created object is returned, or 0 if an error occurred. + */ + template + T *create(const QString &keyword, QObject *parent = 0); + +Q_SIGNALS: + void objectCreated(QObject *object); + +protected: + /** + * Function pointer type to a function that instantiates a plugin. + */ + typedef QObject *(*CreateInstanceFunction)(QWidget *, QObject *); + + /** + * This is used to detect the arguments need for the constructor of plugin classes. + * You can inherit it, if you want to add new classes and still keep support for the old ones. + */ + template + struct InheritanceChecker { + CreateInstanceFunction createInstanceFunction(QWidget *) + { + return &createInstance; + } + CreateInstanceFunction createInstanceFunction(...) + { + return &createInstance; + } + }; + + explicit PluginFactory(PluginFactoryPrivate &dd); + + /** + * Registers a plugin with the factory. Call this function from the constructor of the + * PluginFactory subclass to make the create function able to instantiate the plugin when asked + * for an interface the plugin implements. + * + * \tparam T the name of the plugin class + * + * \param keyword An optional keyword as unique identifier for the plugin. This allows you to + * put more than one plugin with the same interface into the same library using the same + * factory. X-KDE-PluginKeyword is a convenient way to specify the keyword in a desktop file. + * + * \param instanceFunction A function pointer to a function that creates an instance of the + * plugin. The default function that will be used depends on the type of interface. If the + * interface inherits from + * \li \c KParts::Part the function will call + * \code + * new T(QWidget *parentWidget, QObject *parent) + * \endcode + * \li \c QWidget the function will call + * \code + * new T(QWidget *parent) + * \endcode + * \li else the function will call + * \code + * new T(QObject *parent) + * \endcode + */ + template + void registerPlugin(const QString &keyword = QString(), + CreateInstanceFunction instanceFunction + = InheritanceChecker().createInstanceFunction(reinterpret_cast(0))) + { + doRegisterPlugin(keyword, &T::staticMetaObject, instanceFunction); + } + + PluginFactoryPrivate *const d_ptr; + + /** + * This function is called when the factory asked to create an Object. + * + * You may reimplement it to provide a very flexible factory. This is especially useful to + * provide generic factories for plugins implemeted using a scripting language. + * + * \param iface The staticMetaObject::className() string identifying the plugin interface that + * was requested. E.g. for KCModule plugins this string will be "KCModule". + * \param parentWidget Only used if the requested plugin is a KPart. + * \param parent The parent object for the plugin object. + * \param args A plugin specific list of arbitrary arguments. + * \param keyword A string that uniquely identifies the plugin. If a KService is used this + * keyword is read from the X-KDE-PluginKeyword entry in the .desktop file. + */ + virtual QObject *create(const char *iface, QWidget *parentWidget, QObject *parent, const QString &keyword); + + template + static QObject *createInstance(QWidget *parentWidget, QObject *parent) + { + Q_UNUSED(parentWidget); + ParentType *p = 0; + if (parent) { + p = qobject_cast(parent); + Q_ASSERT(p); + } + return new impl(p); + } + +private: + void doRegisterPlugin(const QString &keyword, const QMetaObject *metaObject, CreateInstanceFunction instanceFunction); +}; + +template +inline T *PluginFactory::create(QObject *parent) +{ + QObject *o = create(T::staticMetaObject.className(), + parent && parent->isWidgetType() ? reinterpret_cast(parent) : 0, + parent, + QString()); + + T *t = qobject_cast(o); + if (!t) { + delete o; + } + return t; +} + +template +inline T *PluginFactory::create(const QString &keyword, QObject *parent) +{ + QObject *o = create(T::staticMetaObject.className(), + parent && parent->isWidgetType() ? reinterpret_cast(parent) : 0, + parent, + keyword); + + T *t = qobject_cast(o); + if (!t) { + delete o; + } + return t; +} + +} + +Q_DECLARE_INTERFACE(Calamares::PluginFactory, CalamaresPluginFactory_iid) + +#endif // CALAMARESPLUGINFACTORY_H diff --git a/src/libcalamares/utils/PluginFactory_p.h b/src/libcalamares/utils/PluginFactory_p.h new file mode 100644 index 0000000..a0b4a1c --- /dev/null +++ b/src/libcalamares/utils/PluginFactory_p.h @@ -0,0 +1,54 @@ +/* === This file is part of Calamares - === + * + * Copyright 2015, Teo Mrnjavac + * + * Based on KPluginFactory from KCoreAddons, KDE project + * Copyright 2007, Matthias Kretz + * Copyright 2007, Bernhard Loos + * + * 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 . + */ + +#ifndef CALAMARESPLUGINFACTORY_P_H +#define CALAMARESPLUGINFACTORY_P_H + +#include "PluginFactory.h" + +#include + +namespace Calamares +{ + +class PluginFactoryPrivate +{ + Q_DECLARE_PUBLIC(PluginFactory) +protected: + typedef QPair Plugin; + + PluginFactoryPrivate() + : catalogInitialized( false ) + , q_ptr( nullptr ) + {} + ~PluginFactoryPrivate() {} + + QHash createInstanceHash; + QString catalogName; + bool catalogInitialized; + + PluginFactory *q_ptr; +}; + +} + +#endif // CALAMARESPLUGINFACTORY_P_H diff --git a/src/libcalamaresui/CMakeLists.txt b/src/libcalamaresui/CMakeLists.txt index 939d05a..478034b 100644 --- a/src/libcalamaresui/CMakeLists.txt +++ b/src/libcalamaresui/CMakeLists.txt @@ -1,6 +1,7 @@ set( CALAMARESUI_LIBRARY_TARGET calamaresui ) list( APPEND ${CALAMARESUI_LIBRARY_TARGET}_SOURCES + modulesystem/CppJobModule.cpp modulesystem/Module.cpp modulesystem/ModuleManager.cpp modulesystem/ProcessJobModule.cpp @@ -14,8 +15,6 @@ list( APPEND ${CALAMARESUI_LIBRARY_TARGET}_SOURCES utils/qjsonmodel.cpp utils/qjsonitem.cpp - utils/PluginFactory.cpp - viewpages/AbstractPage.cpp viewpages/ViewStep.cpp diff --git a/src/libcalamaresui/modulesystem/CppJobModule.cpp b/src/libcalamaresui/modulesystem/CppJobModule.cpp new file mode 100644 index 0000000..15e41c2 --- /dev/null +++ b/src/libcalamaresui/modulesystem/CppJobModule.cpp @@ -0,0 +1,128 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * Copyright 2016, Kevin Kofler + * + * 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 "CppJobModule.h" + +#include "utils/PluginFactory.h" +#include "utils/Logger.h" +#include "CppJob.h" + +#include +#include + +namespace Calamares { + + +Module::Type +CppJobModule::type() const +{ + return Job; +} + + +Module::Interface +CppJobModule::interface() const +{ + return QtPlugin; +} + + +void +CppJobModule::loadSelf() +{ + if ( m_loader ) + { + PluginFactory* pf = qobject_cast< PluginFactory* >( m_loader->instance() ); + if ( !pf ) + { + cDebug() << Q_FUNC_INFO << m_loader->errorString(); + return; + } + + CppJob *cppJob = pf->create< Calamares::CppJob >(); + if ( !cppJob ) + { + cDebug() << Q_FUNC_INFO << m_loader->errorString(); + return; + } +// cDebug() << "CppJobModule loading self for instance" << instanceKey() +// << "\nCppJobModule at address" << this +// << "\nCalamares::PluginFactory at address" << pf +// << "\nCppJob at address" << cppJob; + + cppJob->setModuleInstanceKey( instanceKey() ); + cppJob->setConfigurationMap( m_configurationMap ); + m_job = Calamares::job_ptr( static_cast< Calamares::Job * >( cppJob ) ); + m_loaded = true; + cDebug() << "CppJobModule" << instanceKey() << "loading complete."; + } +} + + +QList< job_ptr > +CppJobModule::jobs() const +{ + return QList< job_ptr >() << m_job; +} + + +void +CppJobModule::initFrom( const QVariantMap& moduleDescriptor ) +{ + Module::initFrom( moduleDescriptor ); + QDir directory( location() ); + QString load; + if ( !moduleDescriptor.value( "load" ).toString().isEmpty() ) + { + load = moduleDescriptor.value( "load" ).toString(); + load = directory.absoluteFilePath( load ); + } + // If a load path is not specified, we look for a plugin to load in the directory. + if ( load.isEmpty() || !QLibrary::isLibrary( load ) ) + { + const QStringList ls = directory.entryList( QStringList{ "*.so" } ); + if ( !ls.isEmpty() ) + { + for ( QString entry : ls ) + { + entry = directory.absoluteFilePath( entry ); + if ( QLibrary::isLibrary( entry ) ) + { + load = entry; + break; + } + } + } + } + + m_loader = new QPluginLoader( load ); +} + +CppJobModule::CppJobModule() + : Module() + , m_loader( nullptr ) +{ +} + +CppJobModule::~CppJobModule() +{ + delete m_loader; +} + +} // namespace Calamares diff --git a/src/libcalamaresui/modulesystem/CppJobModule.h b/src/libcalamaresui/modulesystem/CppJobModule.h new file mode 100644 index 0000000..127614e --- /dev/null +++ b/src/libcalamaresui/modulesystem/CppJobModule.h @@ -0,0 +1,53 @@ +/* === This file is part of Calamares - === + * + * Copyright 2014, Teo Mrnjavac + * Copyright 2016, Kevin Kofler + * + * 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 . + */ + +#ifndef CALAMARES_CPPJOBMODULE_H +#define CALAMARES_CPPJOBMODULE_H + +#include "UiDllMacro.h" +#include "Module.h" + +class QPluginLoader; + +namespace Calamares { + +class UIDLLEXPORT CppJobModule : public Module +{ +public: + Type type() const override; + Interface interface() const override; + + void loadSelf() override; + QList< job_ptr > jobs() const override; + +protected: + void initFrom( const QVariantMap& moduleDescriptor ) override; + +private: + friend class Module; //so only the superclass can instantiate + explicit CppJobModule(); + virtual ~CppJobModule(); + + QPluginLoader* m_loader; + job_ptr m_job; +}; + +} // namespace Calamares + +#endif // CALAMARES_CPPJOBMODULE_H diff --git a/src/libcalamaresui/modulesystem/Module.cpp b/src/libcalamaresui/modulesystem/Module.cpp index 7809754..2a2fe77 100644 --- a/src/libcalamaresui/modulesystem/Module.cpp +++ b/src/libcalamaresui/modulesystem/Module.cpp @@ -19,6 +19,7 @@ #include "Module.h" #include "ProcessJobModule.h" +#include "CppJobModule.h" #include "ViewModule.h" #include "utils/CalamaresUtils.h" #include "utils/YamlUtils.h" @@ -76,7 +77,11 @@ Module::fromDescriptor( const QVariantMap& moduleDescriptor, } else if ( typeString == "job" ) { - if ( intfString == "process" ) + if ( intfString == "qtplugin" ) + { + m = new CppJobModule(); + } + else if ( intfString == "process" ) { m = new ProcessJobModule(); } diff --git a/src/libcalamaresui/utils/PluginFactory.cpp b/src/libcalamaresui/utils/PluginFactory.cpp deleted file mode 100644 index 30a5bf4..0000000 --- a/src/libcalamaresui/utils/PluginFactory.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* === This file is part of Calamares - === - * - * Copyright 2015, Teo Mrnjavac - * - * Based on KPluginFactory from KCoreAddons, KDE project - * Copyright 2007, Matthias Kretz - * Copyright 2007, Bernhard Loos - * - * 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 "PluginFactory.h" -#include "PluginFactory_p.h" - -#include -#include - -Q_GLOBAL_STATIC(QObjectCleanupHandler, factorycleanup) - -extern int kLibraryDebugArea(); - -namespace Calamares -{ - -PluginFactory::PluginFactory() - : d_ptr(new PluginFactoryPrivate) -{ - Q_D(PluginFactory); - d->q_ptr = this; - - factorycleanup()->add(this); -} - -PluginFactory::PluginFactory(PluginFactoryPrivate &d) - : d_ptr(&d) -{ - factorycleanup()->add(this); -} - -PluginFactory::~PluginFactory() -{ - delete d_ptr; -} - -void PluginFactory::doRegisterPlugin(const QString &keyword, const QMetaObject *metaObject, CreateInstanceFunction instanceFunction) -{ - Q_D(PluginFactory); - - Q_ASSERT(metaObject); - - // we allow different interfaces to be registered without keyword - if (!keyword.isEmpty()) { - if (d->createInstanceHash.contains(keyword)) { - qWarning() << "A plugin with the keyword" << keyword << "was already registered. A keyword must be unique!"; - } - d->createInstanceHash.insert(keyword, PluginFactoryPrivate::Plugin(metaObject, instanceFunction)); - } else { - const QList clashes(d->createInstanceHash.values(keyword)); - const QMetaObject *superClass = metaObject->superClass(); - if (superClass) { - for (const PluginFactoryPrivate::Plugin &plugin : clashes) { - for (const QMetaObject *otherSuper = plugin.first->superClass(); otherSuper; - otherSuper = otherSuper->superClass()) { - if (superClass == otherSuper) { - qWarning() << "Two plugins with the same interface(" << superClass->className() << ") were registered. Use keywords to identify the plugins."; - } - } - } - } - for (const PluginFactoryPrivate::Plugin &plugin : clashes) { - superClass = plugin.first->superClass(); - if (superClass) { - for (const QMetaObject *otherSuper = metaObject->superClass(); otherSuper; - otherSuper = otherSuper->superClass()) { - if (superClass == otherSuper) { - qWarning() << "Two plugins with the same interface(" << superClass->className() << ") were registered. Use keywords to identify the plugins."; - } - } - } - } - d->createInstanceHash.insertMulti(keyword, PluginFactoryPrivate::Plugin(metaObject, instanceFunction)); - } -} - -QObject *PluginFactory::create(const char *iface, QWidget *parentWidget, QObject *parent, const QString &keyword) -{ - Q_D(PluginFactory); - - QObject *obj = 0; - - const QList candidates(d->createInstanceHash.values(keyword)); - // for !keyword.isEmpty() candidates.count() is 0 or 1 - - for (const PluginFactoryPrivate::Plugin &plugin : candidates) { - for (const QMetaObject *current = plugin.first; current; current = current->superClass()) { - if (0 == qstrcmp(iface, current->className())) { - if (obj) { - qWarning() << "ambiguous interface requested from a DSO containing more than one plugin"; - } - obj = plugin.second(parentWidget, parent); - break; - } - } - } - - if (obj) { - emit objectCreated(obj); - } - return obj; -} - -} diff --git a/src/libcalamaresui/utils/PluginFactory.h b/src/libcalamaresui/utils/PluginFactory.h deleted file mode 100644 index a77d959..0000000 --- a/src/libcalamaresui/utils/PluginFactory.h +++ /dev/null @@ -1,370 +0,0 @@ -/* === This file is part of Calamares - === - * - * Copyright 2015, Teo Mrnjavac - * - * Based on KPluginFactory from KCoreAddons, KDE project - * Copyright 2007, Matthias Kretz - * Copyright 2007, Bernhard Loos - * - * 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 . - */ - -#ifndef CALAMARESPLUGINFACTORY_H -#define CALAMARESPLUGINFACTORY_H - -#include "UiDllMacro.h" - -#include -#include -#include - -namespace Calamares -{ -class PluginFactoryPrivate; -} - -#define CalamaresPluginFactory_iid "io.calamares.PluginFactory" - -#define CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY_SKEL(name, baseFactory, ...) \ - class name : public Calamares::PluginFactory \ - { \ - Q_OBJECT \ - Q_INTERFACES(Calamares::PluginFactory) \ - __VA_ARGS__ \ - public: \ - explicit name(); \ - ~name(); \ - private: \ - void init(); \ - }; - -#define CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, baseFactory) \ - CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY_SKEL(name, baseFactory, Q_PLUGIN_METADATA(IID CalamaresPluginFactory_iid)) - -#define CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, baseFactory, pluginRegistrations) \ - name::name() \ - { \ - pluginRegistrations \ - } \ - name::~name() {} - -#define CALAMARES_PLUGIN_FACTORY_WITH_BASEFACTORY(name, baseFactory, pluginRegistrations) \ - CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, baseFactory) \ - CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, baseFactory, pluginRegistrations) - -#define CALAMARES_PLUGIN_FACTORY_DECLARATION(name) CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, Calamares::PluginFactory) -#define CALAMARES_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations) CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, Calamares::PluginFactory, pluginRegistrations) - -/** - * \relates PluginFactory - * - * Create a PluginFactory subclass and export it as the root plugin object. - * - * \param name The name of the PluginFactory derived class. - * - * \param pluginRegistrations Code to be inserted into the constructor of the - * class. Usually a series of registerPlugin() calls. - * - * Example: - * \code - * #include - * #include - * - * class MyPlugin : public PluginInterface - * { - * public: - * MyPlugin(QObject *parent, const QVariantList &args) - * : PluginInterface(parent) - * {} - * }; - * - * CALAMARES_PLUGIN_FACTORY(MyPluginFactory, - * registerPlugin(); - * ) - * - * #include - * \endcode - * - * \see CALAMARES_PLUGIN_FACTORY_DECLARATION - * \see CALAMARES_PLUGIN_FACTORY_DEFINITION - */ -#define CALAMARES_PLUGIN_FACTORY(name, pluginRegistrations) CALAMARES_PLUGIN_FACTORY_WITH_BASEFACTORY(name, Calamares::PluginFactory, pluginRegistrations) - -/** - * \relates PluginFactory - * - * CALAMARES_PLUGIN_FACTORY_DECLARATION declares the PluginFactory subclass. This macro - * can be used in a header file. - * - * \param name The name of the PluginFactory derived class. - * - * \see CALAMARES_PLUGIN_FACTORY - * \see CALAMARES_PLUGIN_FACTORY_DEFINITION - */ -#define CALAMARES_PLUGIN_FACTORY_DECLARATION(name) CALAMARES_PLUGIN_FACTORY_DECLARATION_WITH_BASEFACTORY(name, Calamares::PluginFactory) - -/** - * \relates PluginFactory - * CALAMARES_PLUGIN_FACTORY_DEFINITION defines the PluginFactory subclass. This macro - * can not be used in a header file. - * - * \param name The name of the PluginFactory derived class. - * - * \param pluginRegistrations Code to be inserted into the constructor of the - * class. Usually a series of registerPlugin() calls. - * - * \see CALAMARES_PLUGIN_FACTORY - * \see CALAMARES_PLUGIN_FACTORY_DECLARATION - */ -#define CALAMARES_PLUGIN_FACTORY_DEFINITION(name, pluginRegistrations) CALAMARES_PLUGIN_FACTORY_DEFINITION_WITH_BASEFACTORY(name, Calamares::PluginFactory, pluginRegistrations) - -namespace Calamares -{ - -/** - * \class PluginFactory PluginFactory.h - * - * PluginFactory provides a convenient way to provide factory-style plugins. - * Qt plugins provide a singleton object, but a common pattern is for plugins - * to generate as many objects of a particular type as the application requires. - * By using PluginFactory, you can avoid implementing the factory pattern - * yourself. - * - * PluginFactory also allows plugins to provide multiple different object - * types, indexed by keywords. - * - * The objects created by PluginFactory must inherit QObject, and must have a - * standard constructor pattern: - * \li if the object is a KPart::Part, it must be of the form - * \code - * T(QWidget *parentWidget, QObject *parent, const QVariantList &args) - * \endcode - * \li if it is a QWidget, it must be of the form - * \code - * T(QWidget *parent, const QVariantList &args) - * \endcode - * \li otherwise it must be of the form - * \code - * T(QObject *parent, const QVariantList &args) - * \endcode - * - * You should typically use either CALAMARES_PLUGIN_FACTORY() or - * CALAMARES_PLUGIN_FACTORY_WITH_JSON() in your plugin code to create the factory. The - * typical pattern is - * - * \code - * #include - * #include - * - * class MyPlugin : public PluginInterface - * { - * public: - * MyPlugin(QObject *parent, const QVariantList &args) - * : PluginInterface(parent) - * {} - * }; - * - * CALAMARES_PLUGIN_FACTORY(MyPluginFactory, - * registerPlugin(); - * ) - * #include - * \endcode - * - * If you want to load a library use KPluginLoader. - * The application that wants to instantiate plugin classes can do the following: - * \code - * PluginFactory *factory = KPluginLoader("libraryname").factory(); - * if (factory) { - * PluginInterface *p1 = factory->create(parent); - * OtherInterface *p2 = factory->create(parent); - * NextInterface *p3 = factory->create("keyword1", parent); - * NextInterface *p3 = factory->create("keyword2", parent); - * } - * \endcode - * - * \author Matthias Kretz - * \author Bernhard Loos - */ -class UIDLLEXPORT PluginFactory : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(PluginFactory) -public: - /** - * This constructor creates a factory for a plugin. - */ - explicit PluginFactory(); - - /** - * This destroys the PluginFactory. - */ - virtual ~PluginFactory(); - - /** - * Use this method to create an object. It will try to create an object which inherits - * \p T. If it has multiple choices, you will get a fatal error (kFatal()), so be careful - * to request a unique interface or use keywords. - * - * \tparam T The interface for which an object should be created. The object will inherit \p T. - * \param parent The parent of the object. If \p parent is a widget type, it will also passed - * to the parentWidget argument of the CreateInstanceFunction for the object. - * \returns A pointer to the created object is returned, or 0 if an error occurred. - */ - template - T *create(QObject *parent = 0); - - /** - * Use this method to create an object. It will try to create an object which inherits - * \p T and was registered with \p keyword. - * - * \tparam T The interface for which an object should be created. The object will inherit \p T. - * \param keyword The keyword of the object. - * \param parent The parent of the object. If \p parent is a widget type, it will also passed - * to the parentWidget argument of the CreateInstanceFunction for the object. - * \returns A pointer to the created object is returned, or 0 if an error occurred. - */ - template - T *create(const QString &keyword, QObject *parent = 0); - -Q_SIGNALS: - void objectCreated(QObject *object); - -protected: - /** - * Function pointer type to a function that instantiates a plugin. - */ - typedef QObject *(*CreateInstanceFunction)(QWidget *, QObject *); - - /** - * This is used to detect the arguments need for the constructor of plugin classes. - * You can inherit it, if you want to add new classes and still keep support for the old ones. - */ - template - struct InheritanceChecker { - CreateInstanceFunction createInstanceFunction(QWidget *) - { - return &createInstance; - } - CreateInstanceFunction createInstanceFunction(...) - { - return &createInstance; - } - }; - - explicit PluginFactory(PluginFactoryPrivate &dd); - - /** - * Registers a plugin with the factory. Call this function from the constructor of the - * PluginFactory subclass to make the create function able to instantiate the plugin when asked - * for an interface the plugin implements. - * - * \tparam T the name of the plugin class - * - * \param keyword An optional keyword as unique identifier for the plugin. This allows you to - * put more than one plugin with the same interface into the same library using the same - * factory. X-KDE-PluginKeyword is a convenient way to specify the keyword in a desktop file. - * - * \param instanceFunction A function pointer to a function that creates an instance of the - * plugin. The default function that will be used depends on the type of interface. If the - * interface inherits from - * \li \c KParts::Part the function will call - * \code - * new T(QWidget *parentWidget, QObject *parent) - * \endcode - * \li \c QWidget the function will call - * \code - * new T(QWidget *parent) - * \endcode - * \li else the function will call - * \code - * new T(QObject *parent) - * \endcode - */ - template - void registerPlugin(const QString &keyword = QString(), - CreateInstanceFunction instanceFunction - = InheritanceChecker().createInstanceFunction(reinterpret_cast(0))) - { - doRegisterPlugin(keyword, &T::staticMetaObject, instanceFunction); - } - - PluginFactoryPrivate *const d_ptr; - - /** - * This function is called when the factory asked to create an Object. - * - * You may reimplement it to provide a very flexible factory. This is especially useful to - * provide generic factories for plugins implemeted using a scripting language. - * - * \param iface The staticMetaObject::className() string identifying the plugin interface that - * was requested. E.g. for KCModule plugins this string will be "KCModule". - * \param parentWidget Only used if the requested plugin is a KPart. - * \param parent The parent object for the plugin object. - * \param args A plugin specific list of arbitrary arguments. - * \param keyword A string that uniquely identifies the plugin. If a KService is used this - * keyword is read from the X-KDE-PluginKeyword entry in the .desktop file. - */ - virtual QObject *create(const char *iface, QWidget *parentWidget, QObject *parent, const QString &keyword); - - template - static QObject *createInstance(QWidget *parentWidget, QObject *parent) - { - Q_UNUSED(parentWidget); - ParentType *p = 0; - if (parent) { - p = qobject_cast(parent); - Q_ASSERT(p); - } - return new impl(p); - } - -private: - void doRegisterPlugin(const QString &keyword, const QMetaObject *metaObject, CreateInstanceFunction instanceFunction); -}; - -template -inline T *PluginFactory::create(QObject *parent) -{ - QObject *o = create(T::staticMetaObject.className(), - parent && parent->isWidgetType() ? reinterpret_cast(parent) : 0, - parent, - QString()); - - T *t = qobject_cast(o); - if (!t) { - delete o; - } - return t; -} - -template -inline T *PluginFactory::create(const QString &keyword, QObject *parent) -{ - QObject *o = create(T::staticMetaObject.className(), - parent && parent->isWidgetType() ? reinterpret_cast(parent) : 0, - parent, - keyword); - - T *t = qobject_cast(o); - if (!t) { - delete o; - } - return t; -} - -} - -Q_DECLARE_INTERFACE(Calamares::PluginFactory, CalamaresPluginFactory_iid) - -#endif // CALAMARESPLUGINFACTORY_H diff --git a/src/libcalamaresui/utils/PluginFactory_p.h b/src/libcalamaresui/utils/PluginFactory_p.h deleted file mode 100644 index a0b4a1c..0000000 --- a/src/libcalamaresui/utils/PluginFactory_p.h +++ /dev/null @@ -1,54 +0,0 @@ -/* === This file is part of Calamares - === - * - * Copyright 2015, Teo Mrnjavac - * - * Based on KPluginFactory from KCoreAddons, KDE project - * Copyright 2007, Matthias Kretz - * Copyright 2007, Bernhard Loos - * - * 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 . - */ - -#ifndef CALAMARESPLUGINFACTORY_P_H -#define CALAMARESPLUGINFACTORY_P_H - -#include "PluginFactory.h" - -#include - -namespace Calamares -{ - -class PluginFactoryPrivate -{ - Q_DECLARE_PUBLIC(PluginFactory) -protected: - typedef QPair Plugin; - - PluginFactoryPrivate() - : catalogInitialized( false ) - , q_ptr( nullptr ) - {} - ~PluginFactoryPrivate() {} - - QHash createInstanceHash; - QString catalogName; - bool catalogInitialized; - - PluginFactory *q_ptr; -}; - -} - -#endif // CALAMARESPLUGINFACTORY_P_H diff --git a/src/modules/dracutlukscfg/CMakeLists.txt b/src/modules/dracutlukscfg/CMakeLists.txt new file mode 100644 index 0000000..a239521 --- /dev/null +++ b/src/modules/dracutlukscfg/CMakeLists.txt @@ -0,0 +1,9 @@ +calamares_add_plugin( dracutlukscfg + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + DracutLuksCfgJob.cpp + LINK_LIBRARIES + calamares + SHARED_LIB +) diff --git a/src/modules/dracutlukscfg/DracutLuksCfgJob.cpp b/src/modules/dracutlukscfg/DracutLuksCfgJob.cpp new file mode 100644 index 0000000..273ff98 --- /dev/null +++ b/src/modules/dracutlukscfg/DracutLuksCfgJob.cpp @@ -0,0 +1,143 @@ +/* === This file is part of Calamares - === + * + * Copyright 2016, Kevin Kofler + * + * 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 "DracutLuksCfgJob.h" + +#include +#include +#include +#include + +#include "CalamaresVersion.h" +#include "JobQueue.h" +#include "GlobalStorage.h" + +#include "utils/Logger.h" + +// static +const QString DracutLuksCfgJob::CONFIG_FILE = QStringLiteral( "/etc/dracut.conf.d/calamares-luks.conf" ); + +// static +const char *DracutLuksCfgJob::CONFIG_FILE_CONTENTS = + "# Configuration file automatically written by the Calamares system installer\n" + "# (This file is written once at install time and should be safe to edit.)\n" + "# Enables support for LUKS full disk encryption with single sign on from GRUB.\n" + "\n" + "# force installing /etc/crypttab even if hostonly=\"no\", install the keyfile\n" + "install_items+=\" /etc/crypttab /crypto_keyfile.bin \"\n"; + +// static +const QString DracutLuksCfgJob::CONFIG_FILE_SWAPLINE = QStringLiteral( "# enable automatic resume from swap\nadd_device+=\" /dev/disk/by-uuid/%1 \"\n" ); + +// static +QString +DracutLuksCfgJob::rootMountPoint() +{ + Calamares::GlobalStorage *globalStorage = Calamares::JobQueue::instance()->globalStorage(); + return globalStorage->value( QStringLiteral( "rootMountPoint" ) ).toString(); +} + +// static +QVariantList +DracutLuksCfgJob::partitions() +{ + Calamares::GlobalStorage *globalStorage = Calamares::JobQueue::instance()->globalStorage(); + return globalStorage->value( QStringLiteral( "partitions" ) ).toList(); +} + +// static +bool +DracutLuksCfgJob::isRootEncrypted() +{ + const QVariantList partitions = DracutLuksCfgJob::partitions(); + for ( const QVariant &partition : partitions ) + { + QVariantMap partitionMap = partition.toMap(); + QString mountPoint = partitionMap.value( QStringLiteral( "mountPoint" ) ).toString(); + if ( mountPoint == QStringLiteral( "/" ) ) + return partitionMap.contains( QStringLiteral( "luksMapperName" ) ); + } + return false; +} + +// static +QString +DracutLuksCfgJob::swapOuterUuid() +{ + const QVariantList partitions = DracutLuksCfgJob::partitions(); + for ( const QVariant &partition : partitions ) + { + QVariantMap partitionMap = partition.toMap(); + QString fsType = partitionMap.value( QStringLiteral( "fs" ) ).toString(); + if ( fsType == QStringLiteral( "linuxswap" ) && partitionMap.contains( QStringLiteral( "luksMapperName" ) ) ) + return partitionMap.value( QStringLiteral( "luksUuid" ) ).toString(); + } + return QString(); +} + +DracutLuksCfgJob::DracutLuksCfgJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + + +DracutLuksCfgJob::~DracutLuksCfgJob() +{ +} + + +QString +DracutLuksCfgJob::prettyName() const +{ + if ( isRootEncrypted() ) + return tr( "Write LUKS configuration for Dracut to %1" ).arg( CONFIG_FILE ); + else + return tr( "Skip writing LUKS configuration for Dracut: \"/\" partition is not encrypted" ); +} + + +Calamares::JobResult +DracutLuksCfgJob::exec() +{ + if ( isRootEncrypted() ) + { + const QString realConfigFilePath = rootMountPoint() + CONFIG_FILE; + cDebug() << "[DRACUTLUKSCFG]: Writing" << realConfigFilePath; + QDir( QStringLiteral( "/" ) ).mkpath( QFileInfo( realConfigFilePath ).absolutePath() ); + QFile configFile( realConfigFilePath ); + if ( ! configFile.open( QIODevice::WriteOnly | QIODevice::Text ) ) + { + cDebug() << "[DRACUTLUKSCFG]: Failed to open" << realConfigFilePath; + return Calamares::JobResult::error( tr( "Failed to open %1" ).arg( realConfigFilePath ) ); + } + QTextStream outStream( &configFile ); + outStream << CONFIG_FILE_CONTENTS; + const QString swapOuterUuid = DracutLuksCfgJob::swapOuterUuid(); + if ( ! swapOuterUuid.isEmpty() ) + { + cDebug() << "[DRACUTLUKSCFG]: Swap outer UUID" << swapOuterUuid; + outStream << CONFIG_FILE_SWAPLINE.arg( swapOuterUuid ).toLatin1(); + } + cDebug() << "[DRACUTLUKSCFG]: Wrote config to" << realConfigFilePath; + } else + cDebug() << "[DRACUTLUKSCFG]: / not encrypted, skipping"; + + return Calamares::JobResult::ok(); +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( DracutLuksCfgJobFactory, registerPlugin(); ) diff --git a/src/modules/dracutlukscfg/DracutLuksCfgJob.h b/src/modules/dracutlukscfg/DracutLuksCfgJob.h new file mode 100644 index 0000000..bfedb85 --- /dev/null +++ b/src/modules/dracutlukscfg/DracutLuksCfgJob.h @@ -0,0 +1,56 @@ +/* === This file is part of Calamares - === + * + * Copyright 2016, Kevin Kofler + * + * 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 . + */ + +#ifndef DRACUTLUKSCFGJOB_H +#define DRACUTLUKSCFGJOB_H + +#include +#include + +#include + +#include + +#include + +class PLUGINDLLEXPORT DracutLuksCfgJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit DracutLuksCfgJob( QObject* parent = nullptr ); + virtual ~DracutLuksCfgJob(); + + QString prettyName() const override; + + Calamares::JobResult exec() override; + +private: + static const QString CONFIG_FILE; + static const char *CONFIG_FILE_CONTENTS; + static const QString CONFIG_FILE_SWAPLINE; + + static QString rootMountPoint(); + static QVariantList partitions(); + static bool isRootEncrypted(); + static QString swapOuterUuid(); +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( DracutLuksCfgJobFactory ) + +#endif // DRACUTLUKSCFGJOB_H diff --git a/src/modules/dracutlukscfg/module.desc b/src/modules/dracutlukscfg/module.desc new file mode 100644 index 0000000..10d9b78 --- /dev/null +++ b/src/modules/dracutlukscfg/module.desc @@ -0,0 +1,7 @@ +# Module metadata file for dracutlukscfg job +# Syntax is YAML 1.2 +--- +type: "job" +name: "dracutlukscfg" +interface: "qtplugin" +load: "libcalamares_job_dracutlukscfg.so" diff --git a/src/modules/fstab/fstab.conf b/src/modules/fstab/fstab.conf index 7dbf529..c3dbfc3 100644 --- a/src/modules/fstab/fstab.conf +++ b/src/modules/fstab/fstab.conf @@ -8,3 +8,6 @@ ssdExtraMountOptions: xfs: discard swap: discard btrfs: discard,compress=lzo +crypttabOptions: luks +# For Debian and Debian-based distributions, change the above line to: +# crypttabOptions: luks,keyscript=/bin/cat diff --git a/src/modules/fstab/main.py b/src/modules/fstab/main.py index 65f483b..05b094f 100644 --- a/src/modules/fstab/main.py +++ b/src/modules/fstab/main.py @@ -102,11 +102,13 @@ class FstabGenerator(object): :param mount_options: :param ssd_extra_mount_options: """ - def __init__(self, partitions, root_mount_point, mount_options, ssd_extra_mount_options): + def __init__(self, partitions, root_mount_point, mount_options, + ssd_extra_mount_options, crypttab_options): self.partitions = partitions self.root_mount_point = root_mount_point self.mount_options = mount_options self.ssd_extra_mount_options = ssd_extra_mount_options + self.crypttab_options = crypttab_options self.ssd_disks = set() self.root_is_ssd = False @@ -152,21 +154,20 @@ class FstabGenerator(object): if not mapper_name or not luks_uuid: return None - if mount_point == "/": - return None - return dict( name=mapper_name, device="UUID=" + luks_uuid, password="/crypto_keyfile.bin", + options=self.crypttab_options, ) def print_crypttab_line(self, dct, file=None): """ Prints line to '/etc/crypttab' file. """ - line = "{:21} {:<45} {}".format(dct["name"], - dct["device"], - dct["password"], - ) + line = "{:21} {:<45} {} {}".format(dct["name"], + dct["device"], + dct["password"], + dct["options"], + ) print(line, file=file) @@ -258,9 +259,11 @@ def run(): root_mount_point = global_storage.value("rootMountPoint") mount_options = conf["mountOptions"] ssd_extra_mount_options = conf.get("ssdExtraMountOptions", {}) + crypttab_options = conf.get("crypttabOptions", "luks") generator = FstabGenerator(partitions, root_mount_point, mount_options, - ssd_extra_mount_options) + ssd_extra_mount_options, + crypttab_options) return generator.run() -- 2.1.0