Blob Blame History Raw
From 65c62754c1cd908742ce8305ec19eb08fb358204 Mon Sep 17 00:00:00 2001
From: Kevin Kofler <kevin.kofler@chello.at>
Date: Sun, 25 Sep 2016 03:20:50 +0200
Subject: [PATCH] Add support for LUKS full disk encryption with dracut.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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.

add rd.luks.uuid to GRUB_CMDLINE (fixes unbootable system with dracut… (#265) (V3n3RiX)

* add rd.luks.uuid to GRUB_CMDLINE (fixes unbootable system with dracut --nohostonly, and doesn't affect any other initramfs generators)
* typo

Only write rd.luks.uuid if dracut is present and leave other initramfs generators clean (#266) (V3n3RiX)

* only write rd.luks.uuid line if dracut is present, and leave other initramfs generators alone :D
* add missing = operator

dracut : unlock encrypted swap if exists (#267) (V3n3RiX)

* dracut : unlock encrypted swap if exists
* only check for swap_outer_uuid if dracut exists

[grubcfg] Compare integers to integers, not strings.

target_env_call returns an integer, so do not compare its result to the
string "0".
---
 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 +-
 src/modules/grubcfg/main.py                      |  32 +-
 21 files changed, 1088 insertions(+), 567 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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2014, Teo Mrnjavac <teo@kde.org>
+ *   Copyright 2016, Kevin Kofler <kevin.kofler@chello.at>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
+ *   Copyright 2016, Kevin Kofler <kevin.kofler@chello.at>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CALAMARES_CPPJOB_H
+#define CALAMARES_CPPJOB_H
+
+#include <QObject>
+#include <QVariant>
+
+#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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2015, Teo Mrnjavac <teo@kde.org>
+ *
+ *   Based on KPluginFactory from KCoreAddons, KDE project
+ *   Copyright 2007, Matthias Kretz <kretz@kde.org>
+ *   Copyright 2007, Bernhard Loos <nhuh.put@web.de>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "PluginFactory.h"
+#include "PluginFactory_p.h"
+
+#include <QObjectCleanupHandler>
+#include <QDebug>
+
+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<PluginFactoryPrivate::Plugin> 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<PluginFactoryPrivate::Plugin> 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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2015, Teo Mrnjavac <teo@kde.org>
+ *
+ *   Based on KPluginFactory from KCoreAddons, KDE project
+ *   Copyright 2007, Matthias Kretz <kretz@kde.org>
+ *   Copyright 2007, Bernhard Loos <nhuh.put@web.de>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CALAMARESPLUGINFACTORY_H
+#define CALAMARESPLUGINFACTORY_H
+
+#include "DllMacro.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+
+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 <PluginFactory.h>
+ * #include <plugininterface.h>
+ *
+ * class MyPlugin : public PluginInterface
+ * {
+ * public:
+ *     MyPlugin(QObject *parent, const QVariantList &args)
+ *         : PluginInterface(parent)
+ *     {}
+ * };
+ *
+ * CALAMARES_PLUGIN_FACTORY(MyPluginFactory,
+ *                  registerPlugin<MyPlugin>();
+ *                 )
+ *
+ * #include <myplugin.moc>
+ * \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 <b>not</b> 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.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 <PluginFactory.h>
+ * #include <plugininterface.h>
+ *
+ * class MyPlugin : public PluginInterface
+ * {
+ * public:
+ *     MyPlugin(QObject *parent, const QVariantList &args)
+ *         : PluginInterface(parent)
+ *     {}
+ * };
+ *
+ * CALAMARES_PLUGIN_FACTORY(MyPluginFactory,
+ *                  registerPlugin<MyPlugin>();
+ *                 )
+ * #include <myplugin.moc>
+ * \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<PluginInterface>(parent);
+ *     OtherInterface *p2  = factory->create<OtherInterface>(parent);
+ *     NextInterface *p3   = factory->create<NextInterface>("keyword1", parent);
+ *     NextInterface *p3   = factory->create<NextInterface>("keyword2", parent);
+ * }
+ * \endcode
+ *
+ * \author Matthias Kretz <kretz@kde.org>
+ * \author Bernhard Loos <nhuh.put@web.de>
+ */
+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<typename T>
+    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<typename T>
+    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<class impl>
+    struct InheritanceChecker {
+        CreateInstanceFunction createInstanceFunction(QWidget *)
+        {
+            return &createInstance<impl, QWidget>;
+        }
+        CreateInstanceFunction createInstanceFunction(...)
+        {
+            return &createInstance<impl, QObject>;
+        }
+    };
+
+    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<class T>
+    void registerPlugin(const QString &keyword = QString(),
+                        CreateInstanceFunction instanceFunction
+                        = InheritanceChecker<T>().createInstanceFunction(reinterpret_cast<T *>(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<class impl, class ParentType>
+    static QObject *createInstance(QWidget *parentWidget, QObject *parent)
+    {
+        Q_UNUSED(parentWidget);
+        ParentType *p = 0;
+        if (parent) {
+            p = qobject_cast<ParentType *>(parent);
+            Q_ASSERT(p);
+        }
+        return new impl(p);
+    }
+
+private:
+    void doRegisterPlugin(const QString &keyword, const QMetaObject *metaObject, CreateInstanceFunction instanceFunction);
+};
+
+template<typename T>
+inline T *PluginFactory::create(QObject *parent)
+{
+    QObject *o = create(T::staticMetaObject.className(),
+                        parent && parent->isWidgetType() ? reinterpret_cast<QWidget *>(parent) : 0,
+                        parent,
+                        QString());
+
+    T *t = qobject_cast<T *>(o);
+    if (!t) {
+        delete o;
+    }
+    return t;
+}
+
+template<typename T>
+inline T *PluginFactory::create(const QString &keyword, QObject *parent)
+{
+    QObject *o = create(T::staticMetaObject.className(),
+                        parent && parent->isWidgetType() ? reinterpret_cast<QWidget *>(parent) : 0,
+                        parent,
+                        keyword);
+
+    T *t = qobject_cast<T *>(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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2015, Teo Mrnjavac <teo@kde.org>
+ *
+ *   Based on KPluginFactory from KCoreAddons, KDE project
+ *   Copyright 2007, Matthias Kretz <kretz@kde.org>
+ *   Copyright 2007, Bernhard Loos <nhuh.put@web.de>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CALAMARESPLUGINFACTORY_P_H
+#define CALAMARESPLUGINFACTORY_P_H
+
+#include "PluginFactory.h"
+
+#include <QtCore/QHash>
+
+namespace Calamares
+{
+
+class PluginFactoryPrivate
+{
+    Q_DECLARE_PUBLIC(PluginFactory)
+protected:
+    typedef QPair<const QMetaObject *, PluginFactory::CreateInstanceFunction> Plugin;
+
+    PluginFactoryPrivate()
+        : catalogInitialized( false )
+        , q_ptr( nullptr )
+    {}
+    ~PluginFactoryPrivate() {}
+
+    QHash<QString, Plugin> 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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2014, Teo Mrnjavac <teo@kde.org>
+ *   Copyright 2016, Kevin Kofler <kevin.kofler@chello.at>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "CppJobModule.h"
+
+#include "utils/PluginFactory.h"
+#include "utils/Logger.h"
+#include "CppJob.h"
+
+#include <QDir>
+#include <QPluginLoader>
+
+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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2014, Teo Mrnjavac <teo@kde.org>
+ *   Copyright 2016, Kevin Kofler <kevin.kofler@chello.at>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 - <http://github.com/calamares> ===
- *
- *   Copyright 2015, Teo Mrnjavac <teo@kde.org>
- *
- *   Based on KPluginFactory from KCoreAddons, KDE project
- *   Copyright 2007, Matthias Kretz <kretz@kde.org>
- *   Copyright 2007, Bernhard Loos <nhuh.put@web.de>
- *
- *   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 <http://www.gnu.org/licenses/>.
- */
-
-#include "PluginFactory.h"
-#include "PluginFactory_p.h"
-
-#include <QObjectCleanupHandler>
-#include <QDebug>
-
-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<PluginFactoryPrivate::Plugin> 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<PluginFactoryPrivate::Plugin> 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 - <http://github.com/calamares> ===
- *
- *   Copyright 2015, Teo Mrnjavac <teo@kde.org>
- *
- *   Based on KPluginFactory from KCoreAddons, KDE project
- *   Copyright 2007, Matthias Kretz <kretz@kde.org>
- *   Copyright 2007, Bernhard Loos <nhuh.put@web.de>
- *
- *   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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CALAMARESPLUGINFACTORY_H
-#define CALAMARESPLUGINFACTORY_H
-
-#include "UiDllMacro.h"
-
-#include <QtCore/QObject>
-#include <QtCore/QVariant>
-#include <QtCore/QStringList>
-
-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 <PluginFactory.h>
- * #include <plugininterface.h>
- *
- * class MyPlugin : public PluginInterface
- * {
- * public:
- *     MyPlugin(QObject *parent, const QVariantList &args)
- *         : PluginInterface(parent)
- *     {}
- * };
- *
- * CALAMARES_PLUGIN_FACTORY(MyPluginFactory,
- *                  registerPlugin<MyPlugin>();
- *                 )
- *
- * #include <myplugin.moc>
- * \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 <b>not</b> 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.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 <PluginFactory.h>
- * #include <plugininterface.h>
- *
- * class MyPlugin : public PluginInterface
- * {
- * public:
- *     MyPlugin(QObject *parent, const QVariantList &args)
- *         : PluginInterface(parent)
- *     {}
- * };
- *
- * CALAMARES_PLUGIN_FACTORY(MyPluginFactory,
- *                  registerPlugin<MyPlugin>();
- *                 )
- * #include <myplugin.moc>
- * \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<PluginInterface>(parent);
- *     OtherInterface *p2  = factory->create<OtherInterface>(parent);
- *     NextInterface *p3   = factory->create<NextInterface>("keyword1", parent);
- *     NextInterface *p3   = factory->create<NextInterface>("keyword2", parent);
- * }
- * \endcode
- *
- * \author Matthias Kretz <kretz@kde.org>
- * \author Bernhard Loos <nhuh.put@web.de>
- */
-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<typename T>
-    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<typename T>
-    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<class impl>
-    struct InheritanceChecker {
-        CreateInstanceFunction createInstanceFunction(QWidget *)
-        {
-            return &createInstance<impl, QWidget>;
-        }
-        CreateInstanceFunction createInstanceFunction(...)
-        {
-            return &createInstance<impl, QObject>;
-        }
-    };
-
-    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<class T>
-    void registerPlugin(const QString &keyword = QString(),
-                        CreateInstanceFunction instanceFunction
-                        = InheritanceChecker<T>().createInstanceFunction(reinterpret_cast<T *>(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<class impl, class ParentType>
-    static QObject *createInstance(QWidget *parentWidget, QObject *parent)
-    {
-        Q_UNUSED(parentWidget);
-        ParentType *p = 0;
-        if (parent) {
-            p = qobject_cast<ParentType *>(parent);
-            Q_ASSERT(p);
-        }
-        return new impl(p);
-    }
-
-private:
-    void doRegisterPlugin(const QString &keyword, const QMetaObject *metaObject, CreateInstanceFunction instanceFunction);
-};
-
-template<typename T>
-inline T *PluginFactory::create(QObject *parent)
-{
-    QObject *o = create(T::staticMetaObject.className(),
-                        parent && parent->isWidgetType() ? reinterpret_cast<QWidget *>(parent) : 0,
-                        parent,
-                        QString());
-
-    T *t = qobject_cast<T *>(o);
-    if (!t) {
-        delete o;
-    }
-    return t;
-}
-
-template<typename T>
-inline T *PluginFactory::create(const QString &keyword, QObject *parent)
-{
-    QObject *o = create(T::staticMetaObject.className(),
-                        parent && parent->isWidgetType() ? reinterpret_cast<QWidget *>(parent) : 0,
-                        parent,
-                        keyword);
-
-    T *t = qobject_cast<T *>(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 - <http://github.com/calamares> ===
- *
- *   Copyright 2015, Teo Mrnjavac <teo@kde.org>
- *
- *   Based on KPluginFactory from KCoreAddons, KDE project
- *   Copyright 2007, Matthias Kretz <kretz@kde.org>
- *   Copyright 2007, Bernhard Loos <nhuh.put@web.de>
- *
- *   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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CALAMARESPLUGINFACTORY_P_H
-#define CALAMARESPLUGINFACTORY_P_H
-
-#include "PluginFactory.h"
-
-#include <QtCore/QHash>
-
-namespace Calamares
-{
-
-class PluginFactoryPrivate
-{
-    Q_DECLARE_PUBLIC(PluginFactory)
-protected:
-    typedef QPair<const QMetaObject *, PluginFactory::CreateInstanceFunction> Plugin;
-
-    PluginFactoryPrivate()
-        : catalogInitialized( false )
-        , q_ptr( nullptr )
-    {}
-    ~PluginFactoryPrivate() {}
-
-    QHash<QString, Plugin> 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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2016, Kevin Kofler <kevin.kofler@chello.at>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "DracutLuksCfgJob.h"
+
+#include <QDir>
+#include <QFile>
+#include <QFileInfo>
+#include <QTextStream>
+
+#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<DracutLuksCfgJob>(); )
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 - <http://github.com/calamares> ===
+ *
+ *   Copyright 2016, Kevin Kofler <kevin.kofler@chello.at>
+ *
+ *   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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef DRACUTLUKSCFGJOB_H
+#define DRACUTLUKSCFGJOB_H
+
+#include <QObject>
+#include <QVariantMap>
+
+#include <CppJob.h>
+
+#include <utils/PluginFactory.h>
+
+#include <PluginDllMacro.h>
+
+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()
diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py
index 1cc1f78..55fc697 100644
--- a/src/modules/grubcfg/main.py
+++ b/src/modules/grubcfg/main.py
@@ -36,8 +36,10 @@ def modify_grub_default(partitions, root_mount_point, distributor):
     default_grub = os.path.join(default_dir, "grub")
     distributor_replace = distributor.replace("'", "'\\''")
     plymouth_bin = libcalamares.utils.target_env_call(["sh", "-c", "which plymouth"])
+    dracut_bin = libcalamares.utils.target_env_call(["sh", "-c", "which dracut"])
     use_splash = ""
     swap_uuid = ""
+    swap_outer_uuid = ""
 
     libcalamares.utils.debug("which plymouth exit code: {!s}".format(plymouth_bin))
 
@@ -46,15 +48,26 @@ def modify_grub_default(partitions, root_mount_point, distributor):
 
     cryptdevice_params = []
 
-    for partition in partitions:
-        if partition["fs"] == "linuxswap":
-            swap_uuid = partition["uuid"]
+    if dracut_bin == 0:
+        for partition in partitions:
+            if partition["fs"] == "linuxswap":
+                swap_uuid = partition["uuid"]
 
-        if partition["mountPoint"] == "/" and "luksMapperName" in partition:
-            cryptdevice_params = [
-                "cryptdevice=UUID={!s}:{!s}".format(partition["luksUuid"],
-                                                    partition["luksMapperName"]),
-                "root=/dev/mapper/{!s}".format(partition["luksMapperName"])
+            if partition["fs"] == "linuxswap" and "luksMapperName" in partition:
+                swap_outer_uuid = partition["luksUuid"]
+
+            if partition["mountPoint"] == "/" and "luksMapperName" in partition:
+                cryptdevice_params = ["rd.luks.uuid={!s}".format(partition["luksUuid"])]
+    else:
+        for partition in partitions:
+            if partition["fs"] == "linuxswap":
+                swap_uuid = partition["uuid"]
+
+            if partition["mountPoint"] == "/" and "luksMapperName" in partition:
+                cryptdevice_params = [
+                    "cryptdevice=UUID={!s}:{!s}".format(partition["luksUuid"],
+                                                        partition["luksMapperName"]),
+                    "root=/dev/mapper/{!s}".format(partition["luksMapperName"])
             ]
 
     kernel_params = ["quiet"]
@@ -68,6 +81,9 @@ def modify_grub_default(partitions, root_mount_point, distributor):
     if swap_uuid:
         kernel_params.append("resume=UUID={!s}".format(swap_uuid))
 
+    if dracut_bin == 0 and swap_outer_uuid:
+        kernel_params.append("rd.luks.uuid={!s}".format(swap_outer_uuid))
+
     distributor_line = "GRUB_DISTRIBUTOR='{!s}'".format(distributor_replace)
 
     if not os.path.exists(default_dir):
-- 
2.1.0