Initial upload of HyprArch releng configuration
This commit is contained in:
15
airootfs/usr/lib/calamares/modules/mobile/CMakeLists.txt
Normal file
15
airootfs/usr/lib/calamares/modules/mobile/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
# SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
calamares_add_plugin( mobile
|
||||
TYPE viewmodule
|
||||
EXPORT_MACRO PLUGINDLLEXPORT_PRO
|
||||
SOURCES
|
||||
Config.cpp
|
||||
MobileQmlViewStep.cpp
|
||||
PartitionJob.cpp
|
||||
UsersJob.cpp
|
||||
RESOURCES
|
||||
mobile.qrc
|
||||
SHARED_LIB
|
||||
)
|
||||
221
airootfs/usr/lib/calamares/modules/mobile/Config.cpp
Normal file
221
airootfs/usr/lib/calamares/modules/mobile/Config.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "Config.h"
|
||||
#include "PartitionJob.h"
|
||||
#include "UsersJob.h"
|
||||
|
||||
#include "ViewManager.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
Config::Config( QObject* parent )
|
||||
: QObject( parent )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Config::setConfigurationMap( const QVariantMap& cfgMap )
|
||||
{
|
||||
using namespace Calamares;
|
||||
|
||||
if ( getBool( cfgMap, "bogus", false ) )
|
||||
{
|
||||
cWarning() << "Configuration key \"bogus\" is still set for *mobile*";
|
||||
}
|
||||
|
||||
m_osName = getString( cfgMap, "osName", "(unknown)" );
|
||||
m_arch = getString( cfgMap, "arch", "(unknown)" );
|
||||
m_device = getString( cfgMap, "device", "(unknown)" );
|
||||
m_userInterface = getString( cfgMap, "userInterface", "(unknown)" );
|
||||
m_version = getString( cfgMap, "version", "(unknown)" );
|
||||
|
||||
m_reservedUsernames = getStringList( cfgMap, "reservedUsernames", QStringList { "adm", "at ", "bin", "colord",
|
||||
"cron", "cyrus", "daemon", "ftp", "games", "geoclue", "guest", "halt", "lightdm", "lp", "mail", "man",
|
||||
"messagebus", "news", "nobody", "ntp", "operator", "polkitd", "postmaster", "pulse", "root", "shutdown",
|
||||
"smmsp", "squid", "sshd", "sync", "uucp", "vpopmail", "xfs" } );
|
||||
|
||||
// ensure m_cmdUsermod matches m_username
|
||||
m_username = getString( cfgMap, "username", "user" );
|
||||
m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true );
|
||||
|
||||
m_builtinVirtualKeyboard = getBool( cfgMap, "builtinVirtualKeyboard", true );
|
||||
|
||||
m_featureSshd = getBool( cfgMap, "featureSshd", true );
|
||||
m_featureFsType = getBool( cfgMap, "featureFsType", false );
|
||||
|
||||
m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" );
|
||||
m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" );
|
||||
m_cmdMount = getString( cfgMap, "cmdMount", "mount" );
|
||||
m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" );
|
||||
m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" );
|
||||
|
||||
m_cmdMkfsRootBtrfs = getString( cfgMap, "cmdMkfsRootBtrfs", "mkfs.btrfs -L 'unknownOS_root'" );
|
||||
m_cmdMkfsRootExt4 = getString( cfgMap, "cmdMkfsRootExt4", "mkfs.ext4 -L 'unknownOS_root'" );
|
||||
m_cmdMkfsRootF2fs = getString( cfgMap, "cmdMkfsRootF2fs", "mkfs.f2fs -l 'unknownOS_root'" );
|
||||
m_fsList = getStringList( cfgMap, "fsModel", QStringList { "ext4", "f2fs", "btrfs" } );
|
||||
m_defaultFs = getString( cfgMap, "defaultFs", "ext4" );
|
||||
m_fsIndex = m_fsList.indexOf( m_defaultFs );
|
||||
m_fsType = m_defaultFs;
|
||||
|
||||
m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" );
|
||||
m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" );
|
||||
m_cmdUsermod = getString( cfgMap, "cmdUsermod", "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user");
|
||||
|
||||
m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" );
|
||||
m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" );
|
||||
m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" );
|
||||
}
|
||||
|
||||
Calamares::JobList
|
||||
Config::createJobs()
|
||||
{
|
||||
QList< Calamares::job_ptr > list;
|
||||
QString cmdSshd = m_isSshEnabled ? m_cmdSshdEnable : m_cmdSshdDisable;
|
||||
|
||||
/* Put users job in queue (should run after unpackfs) */
|
||||
Calamares::Job* j = new UsersJob( m_featureSshd,
|
||||
m_cmdPasswd,
|
||||
m_cmdUsermod,
|
||||
cmdSshd,
|
||||
m_cmdSshdUseradd,
|
||||
m_isSshEnabled,
|
||||
m_username,
|
||||
m_userPassword,
|
||||
m_sshdUsername,
|
||||
m_sshdPassword );
|
||||
list.append( Calamares::job_ptr( j ) );
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void
|
||||
Config::runPartitionJobThenLeave( bool b )
|
||||
{
|
||||
Calamares::ViewManager* v = Calamares::ViewManager::instance();
|
||||
QString cmdMkfsRoot;
|
||||
if ( m_fsType == QStringLiteral( "btrfs" ) )
|
||||
{
|
||||
cmdMkfsRoot = m_cmdMkfsRootBtrfs;
|
||||
}
|
||||
else if ( m_fsType == QStringLiteral( "f2fs" ) )
|
||||
{
|
||||
cmdMkfsRoot = m_cmdMkfsRootF2fs;
|
||||
}
|
||||
else if ( m_fsType == QStringLiteral( "ext4" ) )
|
||||
{
|
||||
cmdMkfsRoot = m_cmdMkfsRootExt4;
|
||||
}
|
||||
else
|
||||
{
|
||||
v->onInstallationFailed( "Unknown filesystem: '" + m_fsType + "'", "" );
|
||||
}
|
||||
/* HACK: run partition job
|
||||
* The "mobile" module has two jobs, the partition job and the users job.
|
||||
* If we added both of them in Config::createJobs(), Calamares would run
|
||||
* them right after each other. But we need the "unpackfs" module to run
|
||||
* inbetween, that's why as workaround, the partition job is started here.
|
||||
* To solve this properly, we would need to place the partition job in an
|
||||
* own module and pass everything via globalstorage. But then we might as
|
||||
* well refactor everything so we can unify the mobile's partition job with
|
||||
* the proper partition job from Calamares. */
|
||||
Calamares::Job* j = new PartitionJob( m_cmdInternalStoragePrepare,
|
||||
m_cmdLuksFormat,
|
||||
m_cmdLuksOpen,
|
||||
cmdMkfsRoot,
|
||||
m_cmdMount,
|
||||
m_targetDeviceRoot,
|
||||
m_targetDeviceRootInternal,
|
||||
m_installFromExternalToInternal,
|
||||
m_isFdeEnabled,
|
||||
m_fdePassword );
|
||||
Calamares::JobResult res = j->exec();
|
||||
|
||||
if ( res )
|
||||
{
|
||||
v->next();
|
||||
}
|
||||
else
|
||||
{
|
||||
v->onInstallationFailed( res.message(), res.details() );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setUsername( const QString& username )
|
||||
{
|
||||
m_username = username;
|
||||
emit usernameChanged( m_username );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setUserPassword( const QString& userPassword )
|
||||
{
|
||||
m_userPassword = userPassword;
|
||||
emit userPasswordChanged( m_userPassword );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setSshdUsername( const QString& sshdUsername )
|
||||
{
|
||||
m_sshdUsername = sshdUsername;
|
||||
emit sshdUsernameChanged( m_sshdUsername );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setSshdPassword( const QString& sshdPassword )
|
||||
{
|
||||
m_sshdPassword = sshdPassword;
|
||||
emit sshdPasswordChanged( m_sshdPassword );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setIsSshEnabled( const bool isSshEnabled )
|
||||
{
|
||||
m_isSshEnabled = isSshEnabled;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFdePassword( const QString& fdePassword )
|
||||
{
|
||||
m_fdePassword = fdePassword;
|
||||
emit fdePasswordChanged( m_fdePassword );
|
||||
}
|
||||
|
||||
void
|
||||
Config::setIsFdeEnabled( const bool isFdeEnabled )
|
||||
{
|
||||
m_isFdeEnabled = isFdeEnabled;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setInstallFromExternalToInternal( const bool val )
|
||||
{
|
||||
m_installFromExternalToInternal = val;
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFsType( int idx )
|
||||
{
|
||||
if ( idx >= 0 && idx < m_fsList.length() )
|
||||
{
|
||||
setFsType( m_fsList[ idx ] );
|
||||
}
|
||||
}
|
||||
void
|
||||
Config::setFsType( const QString& fsType )
|
||||
{
|
||||
if ( fsType != m_fsType )
|
||||
{
|
||||
m_fsType = fsType;
|
||||
emit fsTypeChanged( m_fsType );
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Config::setFsIndex( const int fsIndex )
|
||||
{
|
||||
m_fsIndex = fsIndex;
|
||||
emit fsIndexChanged( m_fsIndex );
|
||||
}
|
||||
207
airootfs/usr/lib/calamares/modules/mobile/Config.h
Normal file
207
airootfs/usr/lib/calamares/modules/mobile/Config.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "Job.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class Config : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
/* installer UI */
|
||||
Q_PROPERTY( bool builtinVirtualKeyboard READ builtinVirtualKeyboard CONSTANT FINAL )
|
||||
|
||||
/* welcome */
|
||||
Q_PROPERTY( QString osName READ osName CONSTANT FINAL )
|
||||
Q_PROPERTY( QString arch READ arch CONSTANT FINAL )
|
||||
Q_PROPERTY( QString device READ device CONSTANT FINAL )
|
||||
Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL )
|
||||
Q_PROPERTY( QString version READ version CONSTANT FINAL )
|
||||
|
||||
/* reserved usernames (user_pass, ssh_credentials )*/
|
||||
Q_PROPERTY( QStringList reservedUsernames READ reservedUsernames CONSTANT FINAL )
|
||||
|
||||
/* default user */
|
||||
Q_PROPERTY( QString username READ username WRITE setUsername NOTIFY usernameChanged )
|
||||
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
|
||||
Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL )
|
||||
|
||||
/* ssh server + credentials */
|
||||
Q_PROPERTY( bool featureSshd READ featureSshd CONSTANT FINAL )
|
||||
Q_PROPERTY( QString sshdUsername READ sshdUsername WRITE setSshdUsername NOTIFY sshdUsernameChanged )
|
||||
Q_PROPERTY( QString sshdPassword READ sshdPassword WRITE setSshdPassword NOTIFY sshdPasswordChanged )
|
||||
Q_PROPERTY( bool isSshEnabled READ isSshEnabled WRITE setIsSshEnabled )
|
||||
|
||||
/* full disk encryption */
|
||||
Q_PROPERTY( QString fdePassword READ fdePassword WRITE setFdePassword NOTIFY fdePasswordChanged )
|
||||
Q_PROPERTY( bool isFdeEnabled READ isFdeEnabled WRITE setIsFdeEnabled )
|
||||
|
||||
/* filesystem selection */
|
||||
Q_PROPERTY( QString fsType READ fsType WRITE setFsType NOTIFY fsTypeChanged )
|
||||
Q_PROPERTY( bool featureFsType READ featureFsType CONSTANT FINAL )
|
||||
Q_PROPERTY( QStringList fsList READ fsList CONSTANT FINAL )
|
||||
Q_PROPERTY( QString defaultFs READ defaultFs CONSTANT FINAL )
|
||||
Q_PROPERTY( int fsIndex READ fsIndex WRITE setFsIndex NOTIFY fsIndexChanged )
|
||||
|
||||
/* partition job */
|
||||
Q_PROPERTY( bool runPartitionJobThenLeave READ runPartitionJobThenLeaveDummy WRITE runPartitionJobThenLeave )
|
||||
Q_PROPERTY( QString cmdInternalStoragePrepare READ cmdInternalStoragePrepare CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdLuksFormat READ cmdLuksFormat CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdLuksOpen READ cmdLuksOpen CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdMount READ cmdMount CONSTANT FINAL )
|
||||
Q_PROPERTY( QString targetDeviceRoot READ targetDeviceRoot CONSTANT FINAL )
|
||||
Q_PROPERTY( QString targetDeviceRootInternal READ targetDeviceRootInternal CONSTANT FINAL )
|
||||
Q_PROPERTY(
|
||||
bool installFromExternalToInternal READ installFromExternalToInternal WRITE setInstallFromExternalToInternal )
|
||||
|
||||
/* users job */
|
||||
Q_PROPERTY( QString cmdSshdEnable READ cmdSshdEnable CONSTANT FINAL )
|
||||
Q_PROPERTY( QString cmdSshdDisable READ cmdSshdDisable CONSTANT FINAL )
|
||||
|
||||
public:
|
||||
Config( QObject* parent = nullptr );
|
||||
void setConfigurationMap( const QVariantMap& );
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
/* installer UI */
|
||||
bool builtinVirtualKeyboard() { return m_builtinVirtualKeyboard; }
|
||||
|
||||
/* welcome */
|
||||
QString osName() const { return m_osName; }
|
||||
QString arch() const { return m_arch; }
|
||||
QString device() const { return m_device; }
|
||||
QString userInterface() const { return m_userInterface; }
|
||||
QString version() const { return m_version; }
|
||||
|
||||
/* reserved usernames (user_pass, ssh_credentials) */
|
||||
QStringList reservedUsernames() const { return m_reservedUsernames; };
|
||||
|
||||
/* user */
|
||||
QString username() const { return m_username; }
|
||||
QString userPassword() const { return m_userPassword; }
|
||||
void setUsername( const QString& username );
|
||||
void setUserPassword( const QString& userPassword );
|
||||
bool userPasswordNumeric() const { return m_userPasswordNumeric; }
|
||||
|
||||
/* ssh server + credentials */
|
||||
bool featureSshd() { return m_featureSshd; }
|
||||
QString sshdUsername() const { return m_sshdUsername; }
|
||||
QString sshdPassword() const { return m_sshdPassword; }
|
||||
bool isSshEnabled() { return m_isSshEnabled; }
|
||||
void setSshdUsername( const QString& sshdUsername );
|
||||
void setSshdPassword( const QString& sshdPassword );
|
||||
void setIsSshEnabled( bool isSshEnabled );
|
||||
|
||||
/* full disk encryption */
|
||||
QString fdePassword() const { return m_fdePassword; }
|
||||
bool isFdeEnabled() { return m_isFdeEnabled; }
|
||||
void setFdePassword( const QString& fdePassword );
|
||||
void setIsFdeEnabled( bool isFdeEnabled );
|
||||
|
||||
/* filesystem selection */
|
||||
bool featureFsType() { return m_featureFsType; };
|
||||
QString fsType() const { return m_fsType; };
|
||||
void setFsType( int idx );
|
||||
void setFsType( const QString& fsType );
|
||||
QStringList fsList() const { return m_fsList; };
|
||||
int fsIndex() const { return m_fsIndex; };
|
||||
void setFsIndex( const int fsIndex );
|
||||
QString defaultFs() const { return m_defaultFs; };
|
||||
|
||||
/* partition job */
|
||||
bool runPartitionJobThenLeaveDummy() { return 0; }
|
||||
void runPartitionJobThenLeave( bool b );
|
||||
QString cmdInternalStoragePrepare() const { return m_cmdInternalStoragePrepare; }
|
||||
QString cmdLuksFormat() const { return m_cmdLuksFormat; }
|
||||
QString cmdLuksOpen() const { return m_cmdLuksOpen; }
|
||||
QString cmdMkfsRootBtrfs() const { return m_cmdMkfsRootBtrfs; }
|
||||
QString cmdMkfsRootExt4() const { return m_cmdMkfsRootExt4; }
|
||||
QString cmdMkfsRootF2fs() const { return m_cmdMkfsRootF2fs; }
|
||||
QString cmdMount() const { return m_cmdMount; }
|
||||
QString targetDeviceRoot() const { return m_targetDeviceRoot; }
|
||||
QString targetDeviceRootInternal() const { return m_targetDeviceRootInternal; }
|
||||
bool installFromExternalToInternal() { return m_installFromExternalToInternal; }
|
||||
void setInstallFromExternalToInternal( const bool val );
|
||||
|
||||
/* users job */
|
||||
QString cmdPasswd() const { return m_cmdPasswd; }
|
||||
QString cmdUsermod() const { return m_cmdUsermod; }
|
||||
QString cmdSshdEnable() const { return m_cmdSshdEnable; }
|
||||
QString cmdSshdDisable() const { return m_cmdSshdDisable; }
|
||||
QString cmdSshdUseradd() const { return m_cmdSshdUseradd; }
|
||||
|
||||
private:
|
||||
/* installer UI */
|
||||
bool m_builtinVirtualKeyboard;
|
||||
|
||||
/* welcome */
|
||||
QString m_osName;
|
||||
QString m_arch;
|
||||
QString m_device;
|
||||
QString m_userInterface;
|
||||
QString m_version;
|
||||
|
||||
/* reserved usernames (user_pass, ssh_credentials) */
|
||||
QStringList m_reservedUsernames;
|
||||
|
||||
/* default user */
|
||||
QString m_username;
|
||||
QString m_userPassword;
|
||||
bool m_userPasswordNumeric;
|
||||
|
||||
/* ssh server + credentials */
|
||||
bool m_featureSshd = false;
|
||||
QString m_sshdUsername;
|
||||
QString m_sshdPassword;
|
||||
bool m_isSshEnabled = false;
|
||||
|
||||
/* full disk encryption */
|
||||
QString m_fdePassword;
|
||||
bool m_isFdeEnabled = false;
|
||||
|
||||
/* filesystem selection */
|
||||
bool m_featureFsType = false;
|
||||
QString m_defaultFs;
|
||||
QString m_fsType;
|
||||
// Index of the currently selected filesystem in UI.
|
||||
int m_fsIndex = -1;
|
||||
QStringList m_fsList;
|
||||
|
||||
/* partition job */
|
||||
QString m_cmdInternalStoragePrepare;
|
||||
QString m_cmdLuksFormat;
|
||||
QString m_cmdLuksOpen;
|
||||
QString m_cmdMkfsRootBtrfs;
|
||||
QString m_cmdMkfsRootExt4;
|
||||
QString m_cmdMkfsRootF2fs;
|
||||
QString m_cmdMount;
|
||||
QString m_targetDeviceRoot;
|
||||
QString m_targetDeviceRootInternal;
|
||||
bool m_installFromExternalToInternal = false;
|
||||
|
||||
/* users job */
|
||||
QString m_cmdPasswd;
|
||||
QString m_cmdUsermod;
|
||||
QString m_cmdSshdEnable;
|
||||
QString m_cmdSshdDisable;
|
||||
QString m_cmdSshdUseradd;
|
||||
|
||||
signals:
|
||||
/* booleans we don't read from QML (like isSshEnabled) don't need a signal */
|
||||
|
||||
/* default user */
|
||||
void userPasswordChanged( QString userPassword );
|
||||
void usernameChanged( QString username );
|
||||
|
||||
/* ssh server + credentials */
|
||||
void sshdUsernameChanged( QString sshdUsername );
|
||||
void sshdPasswordChanged( QString sshdPassword );
|
||||
|
||||
/* full disk encryption */
|
||||
void fdePasswordChanged( QString fdePassword );
|
||||
|
||||
void fsTypeChanged( QString fsType );
|
||||
void fsIndexChanged( int fsIndex );
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "MobileQmlViewStep.h"
|
||||
|
||||
#include "Branding.h"
|
||||
#include "GlobalStorage.h"
|
||||
#include "locale/TranslationsModel.h"
|
||||
#include "modulesystem/ModuleManager.h"
|
||||
#include "utils/Dirs.h"
|
||||
#include "utils/Logger.h"
|
||||
#include "utils/Variant.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DEFINITION( MobileQmlViewStepFactory, registerPlugin< MobileQmlViewStep >(); )
|
||||
|
||||
void
|
||||
MobileQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
|
||||
{
|
||||
m_config->setConfigurationMap( configurationMap );
|
||||
Calamares::QmlViewStep::setConfigurationMap( configurationMap );
|
||||
}
|
||||
|
||||
MobileQmlViewStep::MobileQmlViewStep( QObject* parent )
|
||||
: Calamares::QmlViewStep( parent )
|
||||
, m_config( new Config( this ) )
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MobileQmlViewStep::onLeave()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isNextEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isBackEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isAtBeginning() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MobileQmlViewStep::isAtEnd() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Calamares::JobList
|
||||
MobileQmlViewStep::jobs() const
|
||||
{
|
||||
return m_config->createJobs();
|
||||
}
|
||||
|
||||
QObject*
|
||||
MobileQmlViewStep::getConfig()
|
||||
{
|
||||
return m_config;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#ifndef PARTITION_QMLVIEWSTEP_H
|
||||
#define PARTITION_QMLVIEWSTEP_H
|
||||
#include "Config.h"
|
||||
|
||||
#include "utils/PluginFactory.h"
|
||||
#include "viewpages/QmlViewStep.h"
|
||||
|
||||
#include <DllMacro.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
class PLUGINDLLEXPORT MobileQmlViewStep : public Calamares::QmlViewStep
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MobileQmlViewStep( QObject* parent = nullptr );
|
||||
|
||||
bool isNextEnabled() const override;
|
||||
bool isBackEnabled() const override;
|
||||
bool isAtBeginning() const override;
|
||||
bool isAtEnd() const override;
|
||||
|
||||
Calamares::JobList jobs() const override;
|
||||
|
||||
void setConfigurationMap( const QVariantMap& configurationMap ) override;
|
||||
void onLeave() override;
|
||||
QObject* getConfig() override;
|
||||
|
||||
private:
|
||||
Config* m_config;
|
||||
};
|
||||
|
||||
CALAMARES_PLUGIN_FACTORY_DECLARATION( MobileQmlViewStepFactory )
|
||||
|
||||
#endif // PARTITION_QMLVIEWSTEP_H
|
||||
136
airootfs/usr/lib/calamares/modules/mobile/PartitionJob.cpp
Normal file
136
airootfs/usr/lib/calamares/modules/mobile/PartitionJob.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "PartitionJob.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "utils/System.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
PartitionJob::PartitionJob( const QString& cmdInternalStoragePrepare,
|
||||
const QString& cmdLuksFormat,
|
||||
const QString& cmdLuksOpen,
|
||||
const QString& cmdMkfsRoot,
|
||||
const QString& cmdMount,
|
||||
const QString& targetDeviceRoot,
|
||||
const QString& targetDeviceRootInternal,
|
||||
bool installFromExternalToInternal,
|
||||
bool isFdeEnabled,
|
||||
const QString& password )
|
||||
: Calamares::Job()
|
||||
, m_cmdInternalStoragePrepare( cmdInternalStoragePrepare )
|
||||
, m_cmdLuksFormat( cmdLuksFormat )
|
||||
, m_cmdLuksOpen( cmdLuksOpen )
|
||||
, m_cmdMkfsRoot( cmdMkfsRoot )
|
||||
, m_cmdMount( cmdMount )
|
||||
, m_targetDeviceRoot( targetDeviceRoot )
|
||||
, m_targetDeviceRootInternal( targetDeviceRootInternal )
|
||||
, m_installFromExternalToInternal( installFromExternalToInternal )
|
||||
, m_isFdeEnabled( isFdeEnabled )
|
||||
, m_password( password )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
PartitionJob::prettyName() const
|
||||
{
|
||||
return "Creating and formatting installation partition";
|
||||
}
|
||||
|
||||
/* Fill the "global storage", so the following jobs (like unsquashfs) work.
|
||||
The code is similar to modules/partition/jobs/FillGlobalStorageJob.cpp in
|
||||
Calamares. */
|
||||
void
|
||||
FillGlobalStorage( const QString device, const QString pathMount )
|
||||
{
|
||||
using namespace Calamares;
|
||||
|
||||
GlobalStorage* gs = JobQueue::instance()->globalStorage();
|
||||
QVariantList partitions;
|
||||
QVariantMap partition;
|
||||
|
||||
/* See mapForPartition() in FillGlobalStorageJob.cpp */
|
||||
partition[ "device" ] = device;
|
||||
partition[ "mountPoint" ] = "/";
|
||||
partition[ "claimed" ] = true;
|
||||
|
||||
/* Ignored by calamares modules used in combination with the "mobile"
|
||||
* module, so we can get away with leaving them empty for now. */
|
||||
partition[ "uuid" ] = "";
|
||||
partition[ "fsName" ] = "";
|
||||
partition[ "fs" ] = "";
|
||||
|
||||
partitions << partition;
|
||||
gs->insert( "partitions", partitions );
|
||||
gs->insert( "rootMountPoint", pathMount );
|
||||
}
|
||||
|
||||
Calamares::JobResult
|
||||
PartitionJob::exec()
|
||||
{
|
||||
using namespace Calamares;
|
||||
using namespace Calamares;
|
||||
using namespace std;
|
||||
|
||||
const QString pathMount = "/mnt/install";
|
||||
const QString cryptName = "calamares_crypt";
|
||||
QString cryptDev = "/dev/mapper/" + cryptName;
|
||||
QString passwordStdin = m_password + "\n";
|
||||
QString dev = m_targetDeviceRoot;
|
||||
QList< QPair< QStringList, QString > > commands = {};
|
||||
|
||||
if ( m_installFromExternalToInternal )
|
||||
{
|
||||
dev = m_targetDeviceRootInternal;
|
||||
|
||||
commands.append( {
|
||||
{ { "sh", "-c", m_cmdInternalStoragePrepare }, QString() },
|
||||
} );
|
||||
}
|
||||
|
||||
commands.append( { { { "mkdir", "-p", pathMount }, QString() } } );
|
||||
|
||||
if ( m_isFdeEnabled )
|
||||
{
|
||||
commands.append( {
|
||||
{ { "sh", "-c", m_cmdLuksFormat + " " + dev }, passwordStdin },
|
||||
{ { "sh", "-c", m_cmdLuksOpen + " " + dev + " " + cryptName }, passwordStdin },
|
||||
{ { "sh", "-c", m_cmdMkfsRoot + " " + cryptDev }, QString() },
|
||||
{ { "sh", "-c", m_cmdMount + " " + cryptDev + " " + pathMount }, QString() },
|
||||
} );
|
||||
}
|
||||
else
|
||||
{
|
||||
commands.append( { { { "sh", "-c", m_cmdMkfsRoot + " " + dev }, QString() },
|
||||
{ { "sh", "-c", m_cmdMount + " " + dev + " " + pathMount }, QString() } } );
|
||||
}
|
||||
|
||||
foreach ( auto command, commands )
|
||||
{
|
||||
const QStringList args = command.first;
|
||||
const QString stdInput = command.second;
|
||||
const QString pathRoot = "/";
|
||||
|
||||
ProcessResult res
|
||||
= System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 600 ) );
|
||||
if ( res.getExitCode() )
|
||||
{
|
||||
return JobResult::error( "Command failed:<br><br>"
|
||||
"'"
|
||||
+ args.join( " " )
|
||||
+ "'<br><br>"
|
||||
" with output:<br><br>"
|
||||
"'"
|
||||
+ res.getOutput() + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
FillGlobalStorage( m_isFdeEnabled ? cryptDev : dev, pathMount );
|
||||
return JobResult::ok();
|
||||
}
|
||||
38
airootfs/usr/lib/calamares/modules/mobile/PartitionJob.h
Normal file
38
airootfs/usr/lib/calamares/modules/mobile/PartitionJob.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#pragma once
|
||||
#include "Job.h"
|
||||
|
||||
|
||||
class PartitionJob : public Calamares::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PartitionJob( const QString& cmdInternalStoragePrepare,
|
||||
const QString& cmdLuksFormat,
|
||||
const QString& cmdLuksOpen,
|
||||
const QString& cmdMkfsRoot,
|
||||
const QString& cmdMount,
|
||||
const QString& targetDeviceRoot,
|
||||
const QString& targetDeviceRootInternal,
|
||||
bool installFromExternalToInternal,
|
||||
bool isFdeEnabled,
|
||||
const QString& password );
|
||||
|
||||
QString prettyName() const override;
|
||||
Calamares::JobResult exec() override;
|
||||
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
private:
|
||||
QString m_cmdInternalStoragePrepare;
|
||||
QString m_cmdLuksFormat;
|
||||
QString m_cmdLuksOpen;
|
||||
QString m_cmdMkfsRoot;
|
||||
QString m_cmdMount;
|
||||
QString m_targetDeviceRoot;
|
||||
QString m_targetDeviceRootInternal;
|
||||
bool m_installFromExternalToInternal;
|
||||
bool m_isFdeEnabled;
|
||||
QString m_password;
|
||||
};
|
||||
92
airootfs/usr/lib/calamares/modules/mobile/UsersJob.cpp
Normal file
92
airootfs/usr/lib/calamares/modules/mobile/UsersJob.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#include "UsersJob.h"
|
||||
|
||||
#include "GlobalStorage.h"
|
||||
#include "JobQueue.h"
|
||||
#include "Settings.h"
|
||||
#include "utils/System.h"
|
||||
#include "utils/Logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
|
||||
UsersJob::UsersJob( bool featureSshd,
|
||||
const QString& cmdPasswd,
|
||||
const QString& cmdUsermod,
|
||||
const QString& cmdSshd,
|
||||
const QString& cmdSshdUseradd,
|
||||
bool isSshEnabled,
|
||||
const QString& username,
|
||||
const QString& password,
|
||||
const QString& sshdUsername,
|
||||
const QString& sshdPassword )
|
||||
: Calamares::Job()
|
||||
, m_featureSshd( featureSshd )
|
||||
, m_cmdPasswd( cmdPasswd )
|
||||
, m_cmdUsermod( cmdUsermod )
|
||||
, m_cmdSshd( cmdSshd )
|
||||
, m_cmdSshdUseradd( cmdSshdUseradd )
|
||||
, m_isSshEnabled( isSshEnabled )
|
||||
, m_username( username )
|
||||
, m_password( password )
|
||||
, m_sshdUsername( sshdUsername )
|
||||
, m_sshdPassword( sshdPassword )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
UsersJob::prettyName() const
|
||||
{
|
||||
return "Configuring users";
|
||||
}
|
||||
|
||||
Calamares::JobResult
|
||||
UsersJob::exec()
|
||||
{
|
||||
using namespace Calamares;
|
||||
using namespace Calamares;
|
||||
using namespace std;
|
||||
|
||||
QList< QPair< QStringList, QString > > commands = {
|
||||
{ { "sh", "-c", m_cmdUsermod }, m_username + "\n" }
|
||||
};
|
||||
|
||||
commands.append( { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" } );
|
||||
|
||||
if ( m_featureSshd )
|
||||
{
|
||||
commands.append( { { "sh", "-c", m_cmdSshd }, QString() } );
|
||||
|
||||
if ( m_isSshEnabled )
|
||||
{
|
||||
commands.append( { { "sh", "-c", m_cmdSshdUseradd + " " + m_sshdUsername }, QString() } );
|
||||
commands.append(
|
||||
{ { "sh", "-c", m_cmdPasswd + " " + m_sshdUsername }, m_sshdPassword + "\n" + m_sshdPassword + "\n" } );
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( auto command, commands )
|
||||
{
|
||||
auto location = System::RunLocation::RunInTarget;
|
||||
const QString pathRoot = "/";
|
||||
const QStringList args = command.first;
|
||||
const QString stdInput = command.second;
|
||||
|
||||
ProcessResult res = System::runCommand( location, args, pathRoot, stdInput, chrono::seconds( 30 ) );
|
||||
if ( res.getExitCode() )
|
||||
{
|
||||
return JobResult::error( "Command failed:<br><br>"
|
||||
"'"
|
||||
+ args.join( " " )
|
||||
+ "'<br><br>"
|
||||
" with output:<br><br>"
|
||||
"'"
|
||||
+ res.getOutput() + "'" );
|
||||
}
|
||||
}
|
||||
|
||||
return JobResult::ok();
|
||||
}
|
||||
38
airootfs/usr/lib/calamares/modules/mobile/UsersJob.h
Normal file
38
airootfs/usr/lib/calamares/modules/mobile/UsersJob.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#pragma once
|
||||
#include "Job.h"
|
||||
|
||||
|
||||
class UsersJob : public Calamares::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
UsersJob( bool featureSshd,
|
||||
const QString& cmdPasswd,
|
||||
const QString& cmdUsermod,
|
||||
const QString& cmdSshd,
|
||||
const QString& cmdSshdUseradd,
|
||||
bool isSshEnabled,
|
||||
const QString& username,
|
||||
const QString& password,
|
||||
const QString& sshdUsername,
|
||||
const QString& sshdPassword );
|
||||
|
||||
QString prettyName() const override;
|
||||
Calamares::JobResult exec() override;
|
||||
|
||||
Calamares::JobList createJobs();
|
||||
|
||||
private:
|
||||
bool m_featureSshd;
|
||||
QString m_cmdPasswd;
|
||||
QString m_cmdUsermod;
|
||||
QString m_cmdSshd;
|
||||
QString m_cmdSshdUseradd;
|
||||
bool m_isSshEnabled;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
QString m_sshdUsername;
|
||||
QString m_sshdPassword;
|
||||
};
|
||||
65
airootfs/usr/lib/calamares/modules/mobile/fde_confirm.qml
Normal file
65
airootfs/usr/lib/calamares/modules/mobile/fde_confirm.qml
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "To protect your data in case your device gets stolen," +
|
||||
" it is recommended to enable full disk encryption.<br>" +
|
||||
"<br>" +
|
||||
"If you enable full disk encryption, you will be asked for" +
|
||||
" a password. Without this password, it is not possible to" +
|
||||
" boot your device or access any data on it. Make sure that" +
|
||||
" you don't lose this password!"
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Enable")
|
||||
onClicked: {
|
||||
config.isFdeEnabled = true;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Disable")
|
||||
onClicked: {
|
||||
config.isFdeEnabled = false;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
airootfs/usr/lib/calamares/modules/mobile/fde_pass.qml
Normal file
80
airootfs/usr/lib/calamares/modules/mobile/fde_pass.qml
Normal file
@@ -0,0 +1,80 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
TextField {
|
||||
id: password
|
||||
anchors.top: parent.top
|
||||
placeholderText: qsTr("Password")
|
||||
inputMethodHints: Qt.ImhPreferLowercase
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassword(password, passwordRepeat,
|
||||
errorText)
|
||||
text: config.fdePassword
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
Qt.inputMethod.update(Qt.ImQueryInput);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordRepeat
|
||||
anchors.top: password.bottom
|
||||
placeholderText: qsTr("Password (repeat)")
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassword(password, passwordRepeat,
|
||||
errorText)
|
||||
text: config.fdePassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
anchors.top: passwordRepeat.bottom
|
||||
visible: false
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: errorText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
if (validatePassword(password, passwordRepeat, errorText)) {
|
||||
config.fdePassword = password.text;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
airootfs/usr/lib/calamares/modules/mobile/fs_selection.qml
Normal file
59
airootfs/usr/lib/calamares/modules/mobile/fs_selection.qml
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Undef <calamares@undef.tools>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "Select the filesystem for root partition. If unsure, leave the default."
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: fsTypeCB
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 150
|
||||
height: 30
|
||||
editable: false
|
||||
model: config.fsList
|
||||
/* Save the current state on selection so it is there when the back button is pressed */
|
||||
onActivated: config.fsType = fsTypeCB.currentText;
|
||||
Component.onCompleted: fsTypeCB.currentIndex = find( config.fsType, Qt.MatchContains );
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: fsTypeCB.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
config.fsType = fsTypeCB.currentText;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: (function() {
|
||||
var ret = "Once you hit 'install', the installation will begin." +
|
||||
" It will typically take a few minutes. Do not power off the" +
|
||||
" device until it is done.<br>";
|
||||
|
||||
if (config.installFromExternalToInternal) {
|
||||
ret += "<b>After the installation, your device will shutdown" +
|
||||
" automatically. You must remove the external storage" +
|
||||
" (SD card) before booting again.</b>" +
|
||||
"<br><br>" +
|
||||
"Otherwise, your device will boot into the installer" +
|
||||
" again, and not into the installed system."
|
||||
} else {
|
||||
ret += "Afterwards, it will reboot into the installed system.";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}())
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Install")
|
||||
onClicked: navFinish()
|
||||
}
|
||||
}
|
||||
64
airootfs/usr/lib/calamares/modules/mobile/install_target.qml
Normal file
64
airootfs/usr/lib/calamares/modules/mobile/install_target.qml
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "The installation was started from an external storage medium." +
|
||||
"<br>" +
|
||||
"You can either install to the same medium and overwrite the" +
|
||||
" installer, or install to the internal storage.<br>" +
|
||||
"<br>" +
|
||||
"Where would you like to install " + config.osName + "?"
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Internal (eMMC)")
|
||||
onClicked: {
|
||||
config.installFromExternalToInternal = true;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("External (SD card)")
|
||||
onClicked: {
|
||||
config.installFromExternalToInternal = false;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "Are you sure that you want to overwrite the internal storage?" +
|
||||
"<br><br>" +
|
||||
"<b>All existing data on the device will be lost!</b>"
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Yes")
|
||||
onClicked: {
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("No")
|
||||
onClicked: {
|
||||
navBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
173
airootfs/usr/lib/calamares/modules/mobile/mobile.conf
Normal file
173
airootfs/usr/lib/calamares/modules/mobile/mobile.conf
Normal file
@@ -0,0 +1,173 @@
|
||||
# SPDX-FileCopyrightText: no
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
#
|
||||
# Commented out values are defaults.
|
||||
# All commands are running with 'sh -c'.
|
||||
---
|
||||
# This entry exists only to keep the tests happy, remove it in
|
||||
# any production configuration.
|
||||
bogus: true
|
||||
|
||||
#######
|
||||
### Target OS information
|
||||
#######
|
||||
|
||||
## Operating System Name
|
||||
# osName: "(unknown)"
|
||||
|
||||
## User Interface name (e.g. Plasma Mobile)
|
||||
# userInterface: "(unknown)"
|
||||
|
||||
## User Interface assumes that the password is numeric (as of writing, this is
|
||||
## the case with Plasma Mobile and Phosh)
|
||||
# userPasswordNumeric: true
|
||||
|
||||
## OS version
|
||||
# version: "(unknown)"
|
||||
|
||||
## Default username (for which the password will be set)
|
||||
## Ensure also cmdUsermod command matches the default user, so it can be changed if desired.
|
||||
# username: "user"
|
||||
|
||||
## reserved usernames (for user_pass username prompt and ssh_credentials)
|
||||
# reservedUsernames:
|
||||
# - adm
|
||||
# - at
|
||||
# - bin
|
||||
# - colord
|
||||
# - cron
|
||||
# - cyrus
|
||||
# - daemon
|
||||
# - ftp
|
||||
# - games
|
||||
# - geoclue
|
||||
# - guest
|
||||
# - halt
|
||||
# - lightdm
|
||||
# - lp
|
||||
# - mail
|
||||
# - man
|
||||
# - messagebus
|
||||
# - news
|
||||
# - nobody
|
||||
# - ntp
|
||||
# - operator
|
||||
# - polkitd
|
||||
# - postmaster
|
||||
# - pulse
|
||||
# - root
|
||||
# - shutdown
|
||||
# - smmsp
|
||||
# - squid
|
||||
# - sshd
|
||||
# - sync
|
||||
# - uucp
|
||||
# - vpopmail
|
||||
# - xfs
|
||||
|
||||
#######
|
||||
### Target device information
|
||||
#######
|
||||
|
||||
## Architecture (e.g. aarch64)
|
||||
# arch: "(unknown)"
|
||||
|
||||
## Name of the device (e.g. PinePhone)
|
||||
# device: "(unknown)"
|
||||
|
||||
## Partition that will be formatted and mounted (optionally with FDE) for the
|
||||
## rootfs
|
||||
# targetDeviceRoot: "/dev/unknown"
|
||||
|
||||
## Partition that will be formatted and mounted (optionally with FDE) for the
|
||||
## rootfs, on internal storage. The installer OS must not set this, if it was
|
||||
## booted from the internal storage (this is not checked in the mobile
|
||||
## module!).
|
||||
## If this is set, the user gets asked whether they want to install on internal
|
||||
## or external storage. If the user chose internal storage,
|
||||
## cmdInternalStoragePrepare (see below) runs before this partition gets
|
||||
## formatted (see below). A note is displayed, that the device is powered off
|
||||
## after installation and that the user should remove the external storage
|
||||
## medium. So you need to adjust the installer OS to poweroff in that case, and
|
||||
## not reboot. See postmarketos-ondev.git for reference.
|
||||
# targetDeviceRootInternal: ""
|
||||
|
||||
######
|
||||
### Installer Features
|
||||
######
|
||||
|
||||
## Ask whether sshd should be enabled or not. If enabled, add a dedicated ssh
|
||||
## user with proper username and password and suggest to change to key-based
|
||||
## authentication after installation.
|
||||
# featureSshd: true
|
||||
|
||||
## Ask the user, which filesystem to use.
|
||||
# featureFsType: false
|
||||
## Filesystems that the user can choose from.
|
||||
#fsModel:
|
||||
# - ext4
|
||||
# - f2fs
|
||||
# - btrfs
|
||||
## Default filesystem to display in the dialog. If featureFsType is disabled,
|
||||
## this gets used without asking the user.
|
||||
# defaultFs: ext4
|
||||
|
||||
## Start Qt's virtual keyboard within the mobile module. Disable if you bring
|
||||
## your own virtual keyboard (e.g. svkbd).
|
||||
# builtinVirtualKeyboard: true
|
||||
|
||||
#######
|
||||
### Commands running in the installer OS
|
||||
#######
|
||||
|
||||
## Format the target partition with LUKS
|
||||
## Arguments: <device>
|
||||
## Stdin: password with \n
|
||||
# cmdLuksFormat: "cryptsetup luksFormat --use-random"
|
||||
|
||||
## Open the formatted partition
|
||||
## Arguments: <device> <mapping name>
|
||||
## Stdin: password with \n
|
||||
# cmdLuksOpen: "cryptsetup luksOpen"
|
||||
|
||||
## Format the rootfs with a file system
|
||||
## Arguments: <device>
|
||||
## Btrfs: to allow snapshots to work on the root subvolume, it is recommended that this
|
||||
## command be a script which will create a subvolume and make it default
|
||||
## An example can be found at:
|
||||
## https://gitlab.com/mobian1/calamares-settings-mobian/-/merge_requests/2/diffs#diff-content-dde34f5f1c89e3dea63608c553bbc452dedf428f
|
||||
# cmdMkfsRootBtrfs: "mkfs.btrfs -L 'unknownOS_root'"
|
||||
# cmdMkfsRootExt4: "mkfs.ext4 -L 'unknownOS_root'"
|
||||
# cmdMkfsRootF2fs: "mkfs.f2fs -l 'unknownOS_root'"
|
||||
|
||||
## Mount the partition after formatting with file system
|
||||
## Arguments: <device> <mountpoint>
|
||||
# cmdMount: "mount"
|
||||
|
||||
## When user selects installation from external storage to internal storage
|
||||
## (see targetDeviceRootInternal above), use this command to prepare the
|
||||
## internal storage medium. The command must create a partition table with
|
||||
## two partitions (boot, root) and fill the boot partition. See the
|
||||
## ondev-internal-storage-prepare.sh in postmarketos-ondev as example.
|
||||
# cmdInternalStoragePrepare: "ondev-internal-storage-prepare"
|
||||
|
||||
#######
|
||||
### Commands running in the target OS (chroot)
|
||||
#######
|
||||
|
||||
## Change the username for the default user
|
||||
## Stdin: username with \n
|
||||
# cmdUsermod: "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user"
|
||||
|
||||
## Set the password for default user and sshd user
|
||||
## Arguments: <username>
|
||||
## Stdin: password twice, each time with \n
|
||||
# cmdPasswd: "passwd"
|
||||
|
||||
## Enable or disable sshd
|
||||
# cmdSshdEnable: "systemctl enable sshd.service"
|
||||
# cmdSshdDisable: "systemctl disable sshd.service"
|
||||
|
||||
## Create the user for sshd
|
||||
## Arguments: <username>
|
||||
# cmdSshdUseradd: "useradd -G wheel -m"
|
||||
390
airootfs/usr/lib/calamares/modules/mobile/mobile.qml
Normal file
390
airootfs/usr/lib/calamares/modules/mobile/mobile.qml
Normal file
@@ -0,0 +1,390 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Page
|
||||
{
|
||||
property var screen: "welcome"
|
||||
property var screenPrevious: []
|
||||
property var titles: {
|
||||
"welcome": null, /* titlebar disabled */
|
||||
"install_target": "Installation target",
|
||||
"install_target_confirm": "Warning",
|
||||
"user_pass": "User password",
|
||||
"ssh_confirm": "SSH server",
|
||||
"ssh_credentials": "SSH credentials",
|
||||
"fs_selection": "Root filesystem",
|
||||
"fde_confirm": "Full disk encryption",
|
||||
"fde_pass": "Full disk encryption",
|
||||
"install_confirm": "Ready to install",
|
||||
"wait": null
|
||||
}
|
||||
property var features: [
|
||||
{"name": "welcome",
|
||||
"screens": ["welcome"]},
|
||||
{"name": "installTarget",
|
||||
"screens": ["install_target", "install_target_confirm"]},
|
||||
{"name": "userPassword",
|
||||
"screens": ["user_pass"]},
|
||||
{"name": "sshd",
|
||||
"screens": ["ssh_confirm", "ssh_credentials"]},
|
||||
{"name": "fsType",
|
||||
"screens": ["fs_selection"]},
|
||||
{"name": "fde",
|
||||
"screens": ["fde_confirm", "fde_pass"]},
|
||||
{"name": "installConfirm",
|
||||
"screens": ["install_confirm", "wait"]}
|
||||
]
|
||||
property var featureIdByScreen: (function() {
|
||||
/* Put "features" above into an index of screen name -> feature id:
|
||||
* featureIdByScreen = {"welcome": 0, "user_pass": 1, ...} */
|
||||
var ret = {};
|
||||
for (var i=0; i<features.length; i++) {
|
||||
for (var j=0; j<features[i]["screens"].length; j++) {
|
||||
ret[ features[i]["screens"][j] ] = i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}())
|
||||
/* Only allow characters, that can be typed in with osk-sdl
|
||||
* (src/keyboard.cpp). Details in big comment in validatePassword(). */
|
||||
property var allowed_chars:
|
||||
/* layer 0 */ "abcdefghijklmnopqrstuvwxyz" +
|
||||
/* layer 1 */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||
/* layer 2 */ "1234567890" + "@#$%&-_+()" + ",\"':;!?" +
|
||||
/* layer 3 */ "~`|·√πτ÷×¶" + "©®£€¥^°*{}" + "\\/<>=[]" +
|
||||
/* bottom row */ " ."
|
||||
|
||||
Item {
|
||||
id: appContainer
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: inputPanel.top
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Rectangle {
|
||||
id: mobileNavigation
|
||||
width: parent.width
|
||||
height: 30
|
||||
color: "#e6e4e1"
|
||||
Layout.fillWidth: true
|
||||
|
||||
border.width: 1
|
||||
border.color: "#a7a7a7"
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
spacing: 6
|
||||
|
||||
Button {
|
||||
Layout.leftMargin: 6
|
||||
id: mobileBack
|
||||
text: "<"
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 10
|
||||
implicitHeight: 7
|
||||
border.color: "#c1bab5"
|
||||
border.width: 1
|
||||
radius: 4
|
||||
color: mobileBack.down ? "#dbdbdb" : "#f2f2f2"
|
||||
}
|
||||
|
||||
onClicked: navBack()
|
||||
}
|
||||
Rectangle {
|
||||
implicitHeight: 10
|
||||
Layout.fillWidth: true
|
||||
color: "#e6e4e1"
|
||||
|
||||
Text {
|
||||
id: mobileTitle
|
||||
text: ""
|
||||
color: "#303638"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
color: "#e6e4e1"
|
||||
Layout.rightMargin: 6
|
||||
implicitWidth: 32
|
||||
implicitHeight: 30
|
||||
id: filler
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: load
|
||||
anchors.left: parent.left
|
||||
anchors.top: mobileNavigation.bottom
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
}
|
||||
InputPanel {
|
||||
id: inputPanel
|
||||
y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height
|
||||
visible: config.builtinVirtualKeyboard
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
}
|
||||
|
||||
function skipFeatureInstallTarget() {
|
||||
return config.targetDeviceRootInternal == "";
|
||||
}
|
||||
|
||||
/* Navigation related */
|
||||
function navTo(name, historyPush=true) {
|
||||
console.log("Navigating to screen: " + name);
|
||||
if (historyPush)
|
||||
screenPrevious.push(screen);
|
||||
screen = name;
|
||||
load.source = name + ".qml";
|
||||
mobileNavigation.visible = (titles[name] !== null);
|
||||
mobileTitle.text = "<b>" + titles[name] + "</b>";
|
||||
Qt.inputMethod.hide();
|
||||
}
|
||||
function navFinish() {
|
||||
/* Show a waiting screen and wait a second (so it can render). The big
|
||||
* comment in Config.cpp::runPartitionJobThenLeave() explains why this
|
||||
* is necessary. */
|
||||
navTo("wait");
|
||||
timer.interval = 1000;
|
||||
timer.repeat = false;
|
||||
timer.triggered.connect(function() {
|
||||
/* Trigger Config.cpp::runPartitionJobThenLeave(). (We could expose
|
||||
* the function directly with qmlRegisterSingletonType somehow, but
|
||||
* I haven't seen existing Calamares code do that with the Config
|
||||
* object, so just use the side effect of setting the variable, as
|
||||
* done in existing code of Calamares modules.) */
|
||||
config.runPartitionJobThenLeave = 1
|
||||
});
|
||||
timer.start();
|
||||
}
|
||||
function navNextFeature() {
|
||||
var id;
|
||||
|
||||
/* Skip disabled features */
|
||||
for (id = featureIdByScreen[screen] + 1; id < features.length; id++) {
|
||||
/* First letter uppercase */
|
||||
var name = features[id]["name"];
|
||||
var nameUp = name.charAt(0).toUpperCase() + name.slice(1);
|
||||
|
||||
/* Check config.Feature<Name> */
|
||||
var configOption = "feature" + nameUp;
|
||||
if (config[configOption] === false) {
|
||||
console.log("Skipping feature (disabled in config): " + name);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check skipFeature<Name>() */
|
||||
var funcName = "skipFeature" + nameUp;
|
||||
if (eval("typeof " + funcName) === "function"
|
||||
&& eval(funcName + "()")) {
|
||||
console.log("Skipping feature (skip function): " + name);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
console.log("Navigating to feature: " + features[id]["name"]);
|
||||
return navTo(features[id]["screens"][0]);
|
||||
}
|
||||
function navNext() {
|
||||
var featureId = featureIdByScreen[screen];
|
||||
var featureScreens = features[featureId]["screens"];
|
||||
for (var i = 0; i<featureScreens.length; i++) {
|
||||
/* Seek ahead until i is current screen */
|
||||
if (featureScreens[i] != screen)
|
||||
continue;
|
||||
|
||||
/* Navigate to next screen in same feature */
|
||||
if (i + 1 < featureScreens.length) {
|
||||
var screenNext = featureScreens[i + 1];
|
||||
return navTo(screenNext);
|
||||
}
|
||||
|
||||
/* Screen is last in feature */
|
||||
return navNextFeature();
|
||||
}
|
||||
console.log("ERROR: navNext() failed for screen: " + screen);
|
||||
}
|
||||
function navBack() {
|
||||
if (screenPrevious.length)
|
||||
return navTo(screenPrevious.pop(), false);
|
||||
ViewManager.back();
|
||||
}
|
||||
function onActivate() {
|
||||
navTo(screen, false);
|
||||
}
|
||||
|
||||
/* Input validation: show/clear failures */
|
||||
function validationFailure(errorText, message="") {
|
||||
errorText.text = message;
|
||||
errorText.visible = true;
|
||||
return false;
|
||||
}
|
||||
function validationFailureClear(errorText) {
|
||||
errorText.text = "";
|
||||
errorText.visible = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Input validation: user-screens (fde_pass, user_pass, ssh_credentials) */
|
||||
function validatePin(userPin, userPinRepeat, errorText) {
|
||||
var pin = userPin.text;
|
||||
var repeat = userPinRepeat.text;
|
||||
|
||||
if (pin == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (!pin.match(/^[0-9]*$/))
|
||||
return validationFailure(errorText,
|
||||
"Only digits are allowed.");
|
||||
|
||||
if (pin.length < 5)
|
||||
return validationFailure(errorText,
|
||||
"Too short: needs at least 5 digits.");
|
||||
|
||||
if (repeat == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (repeat != pin)
|
||||
return validationFailure(errorText,
|
||||
"The PINs don't match.");
|
||||
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
function validateUsername(username, errorText, extraReservedUsernames = []) {
|
||||
var name = username.text;
|
||||
var reserved = config.reservedUsernames.concat(extraReservedUsernames);
|
||||
|
||||
/* Validate characters */
|
||||
for (var i = 0; i < name.length; i++) {
|
||||
if (i) {
|
||||
if (!name[i].match(/^[a-z0-9_-]$/))
|
||||
return validationFailure(errorText,
|
||||
"Characters must be lowercase" +
|
||||
" letters, numbers,<br>" +
|
||||
" underscores or minus signs.");
|
||||
} else {
|
||||
if (!name[i].match(/^[a-z_]$/))
|
||||
return validationFailure(errorText,
|
||||
"First character must be a" +
|
||||
" lowercase letter or an" +
|
||||
" underscore.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate against reserved usernames */
|
||||
for (var i = 0; i < reserved.length; i++) {
|
||||
if (name == reserved[i])
|
||||
return validationFailure(errorText, "Username '" +
|
||||
reserved[i] +
|
||||
"' is reserved.");
|
||||
}
|
||||
|
||||
/* Passed */
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
|
||||
function validateSshdUsername(username, errorText) {
|
||||
return validateUsername(username, errorText, [config.username]);
|
||||
}
|
||||
function validateSshdPassword(password, passwordRepeat, errorText) {
|
||||
var pass = password.text;
|
||||
var repeat = passwordRepeat.text;
|
||||
|
||||
if (pass == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (pass.length < 6)
|
||||
return validationFailure(errorText,
|
||||
"Too short: needs at least 6" +
|
||||
" digits/characters.");
|
||||
|
||||
if (repeat == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (pass != repeat)
|
||||
return validationFailure(errorText, "Passwords don't match.");
|
||||
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
function check_chars(input) {
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
if (allowed_chars.indexOf(input[i]) == -1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function allowed_chars_multiline() {
|
||||
/* return allowed_chars split across multiple lines */
|
||||
var step = 20;
|
||||
var ret = "";
|
||||
for (var i = 0; i < allowed_chars.length + step; i += step)
|
||||
ret += allowed_chars.slice(i, i + step) + "\n";
|
||||
return ret.trim();
|
||||
}
|
||||
function validatePassword(password, passwordRepeat, errorText) {
|
||||
var pass = password.text;
|
||||
var repeat = passwordRepeat.text;
|
||||
|
||||
if (pass == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
/* This function gets called for the FDE password and for the user
|
||||
* password. As of writing, all distributions shipping the mobile
|
||||
* module are using osk-sdl to type in the FDE password after the
|
||||
* installation, and another keyboard after booting up, to type in the
|
||||
* user password. The osk-sdl password has the same keys as
|
||||
* squeekboard's default layout, and other keyboards should be able to
|
||||
* type these characters in as well. For now, verify that the password
|
||||
* only contains characters that can be typed in by osk-sdl. If you
|
||||
* need this to be more sophisticated, feel free to submit patches to
|
||||
* make this more configurable. */
|
||||
if (!check_chars(pass))
|
||||
return validationFailure(errorText,
|
||||
"The password must only contain" +
|
||||
" these characters, others can possibly" +
|
||||
" not be typed in after installation:\n" +
|
||||
"\n" +
|
||||
allowed_chars_multiline());
|
||||
|
||||
if (pass.length < 6)
|
||||
return validationFailure(errorText,
|
||||
"Too short: needs at least 6" +
|
||||
" digits/characters.");
|
||||
|
||||
if (repeat == "")
|
||||
return validationFailure(errorText);
|
||||
|
||||
if (pass != repeat)
|
||||
return validationFailure(errorText, "Passwords don't match.");
|
||||
|
||||
return validationFailureClear(errorText);
|
||||
}
|
||||
}
|
||||
21
airootfs/usr/lib/calamares/modules/mobile/mobile.qrc
Normal file
21
airootfs/usr/lib/calamares/modules/mobile/mobile.qrc
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>mobile.qml</file>
|
||||
|
||||
<file>welcome.qml</file>
|
||||
|
||||
<file>install_target.qml</file> <!-- install from external to internal? -->
|
||||
<file>install_target_confirm.qml</file> <!-- overwrite internal storage? -->
|
||||
|
||||
<file>user_pass.qml</file> <!-- default user: username, password -->
|
||||
<file>ssh_confirm.qml</file> <!-- sshd: enable or not? -->
|
||||
<file>ssh_credentials.qml</file> <!-- sshd user: username, password -->
|
||||
<file>fs_selection.qml</file> <!-- filesystem selection -->
|
||||
|
||||
<file>fde_confirm.qml</file> <!-- enable FDE or not? -->
|
||||
<file>fde_pass.qml</file> <!-- FDE password (optional) -->
|
||||
<file>install_confirm.qml</file> <!-- final confirmation before install -->
|
||||
|
||||
<file>wait.qml</file> <!-- please wait while partitioning -->
|
||||
</qresource>
|
||||
</RCC>
|
||||
68
airootfs/usr/lib/calamares/modules/mobile/ssh_confirm.qml
Normal file
68
airootfs/usr/lib/calamares/modules/mobile/ssh_confirm.qml
Normal file
@@ -0,0 +1,68 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 30
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: "If you don't know what SSH is, choose 'disable'.<br>" +
|
||||
"<br>" +
|
||||
"With 'enable', you will be asked for a second username and" +
|
||||
" password. You will be able to login to the SSH server with" +
|
||||
" these credentials via USB (172.16.42.1), Wi-Fi and possibly" +
|
||||
" cellular network. It is recommended to replace the password" +
|
||||
" with an SSH key after the installation.<br>" +
|
||||
"<br>" +
|
||||
"More information:<br>" +
|
||||
"https://postmarketos.org/ssh"
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
id: firstButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 40
|
||||
width: 200
|
||||
|
||||
text: qsTr("Enable")
|
||||
onClicked: {
|
||||
config.isSshEnabled = true;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: firstButton.bottom
|
||||
anchors.topMargin: 40
|
||||
width: 200
|
||||
|
||||
text: qsTr("Disable")
|
||||
onClicked: {
|
||||
config.isSshEnabled = false;
|
||||
navNextFeature();
|
||||
}
|
||||
}
|
||||
}
|
||||
107
airootfs/usr/lib/calamares/modules/mobile/ssh_credentials.qml
Normal file
107
airootfs/usr/lib/calamares/modules/mobile/ssh_credentials.qml
Normal file
@@ -0,0 +1,107 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
TextField {
|
||||
id: username
|
||||
anchors.top: parent.top
|
||||
placeholderText: qsTr("SSH username")
|
||||
inputMethodHints: Qt.ImhPreferLowercase
|
||||
onTextChanged: validateSshdUsername(username, errorTextUsername)
|
||||
text: config.sshdUsername
|
||||
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
Qt.inputMethod.update(Qt.ImQueryInput);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorTextUsername
|
||||
anchors.top: username.bottom
|
||||
visible: false
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: password
|
||||
anchors.top: errorTextUsername.bottom
|
||||
placeholderText: qsTr("SSH password")
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validateSshdPassword(password, passwordRepeat,
|
||||
errorTextPassword)
|
||||
text: config.sshdPassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: passwordRepeat
|
||||
anchors.top: password.bottom
|
||||
placeholderText: qsTr("SSH password (repeat)")
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validateSshdPassword(password, passwordRepeat,
|
||||
errorTextPassword)
|
||||
text: config.sshdPassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorTextPassword
|
||||
anchors.top: passwordRepeat.bottom
|
||||
visible: false
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 50
|
||||
width: 200
|
||||
}
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: errorTextPassword.bottom
|
||||
anchors.topMargin: 40
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
if (validateSshdUsername(username, errorTextUsername) &&
|
||||
validateSshdPassword(password, passwordRepeat,
|
||||
errorTextPassword)) {
|
||||
config.sshdUsername = username.text;
|
||||
config.sshdPassword = password.text;
|
||||
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
143
airootfs/usr/lib/calamares/modules/mobile/user_pass.qml
Normal file
143
airootfs/usr/lib/calamares/modules/mobile/user_pass.qml
Normal file
@@ -0,0 +1,143 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Item {
|
||||
property var passPlaceholder: (config.userPasswordNumeric
|
||||
? "PIN"
|
||||
: "Password")
|
||||
property var hints: (config.userPasswordNumeric
|
||||
? Qt.ImhDigitsOnly
|
||||
: Qt.ImhPreferLowercase)
|
||||
property var validatePassFunc: (config.userPasswordNumeric
|
||||
? validatePin
|
||||
: validatePassword);
|
||||
|
||||
property var validateNameFunc: validateUsername;
|
||||
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
id: usernameDescription
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: (function() {
|
||||
return "Set the username of your user. The default" +
|
||||
" username is \"" + config.username + "\".";
|
||||
}())
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: username
|
||||
anchors.top: usernameDescription.bottom
|
||||
placeholderText: qsTr("Username")
|
||||
onTextChanged: validateNameFunc(username, errorText)
|
||||
text: config.username
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
id: userPassDescription
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: username.bottom
|
||||
anchors.topMargin: 10
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
text: (function() {
|
||||
if (config.userPasswordNumeric) {
|
||||
return "Set the numeric password of your user. The" +
|
||||
" lockscreen will ask for this PIN. This is" +
|
||||
" <i>not</i> the PIN of your SIM card. Make sure to" +
|
||||
" remember it.";
|
||||
} else {
|
||||
return "Set the password of your user. The lockscreen will" +
|
||||
" ask for this password. Make sure to remember it.";
|
||||
}
|
||||
}())
|
||||
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: userPass
|
||||
anchors.top: userPassDescription.bottom
|
||||
placeholderText: qsTr(passPlaceholder)
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
|
||||
text: config.userPassword
|
||||
|
||||
/* Let the virtual keyboard change to digits only */
|
||||
inputMethodHints: hints
|
||||
onActiveFocusChanged: {
|
||||
if(activeFocus) {
|
||||
Qt.inputMethod.update(Qt.ImQueryInput)
|
||||
}
|
||||
}
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: userPassRepeat
|
||||
anchors.top: userPass.bottom
|
||||
placeholderText: qsTr(passPlaceholder + " (repeat)")
|
||||
inputMethodHints: hints
|
||||
echoMode: TextInput.Password
|
||||
onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText)
|
||||
text: config.userPassword
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.top: userPassRepeat.bottom
|
||||
id: errorText
|
||||
visible: false
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: errorText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: {
|
||||
if (validatePassFunc(userPass, userPassRepeat, errorText) && validateNameFunc(username, errorText)) {
|
||||
config.userPassword = userPass.text;
|
||||
config.username = username.text;
|
||||
navNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
airootfs/usr/lib/calamares/modules/mobile/wait.qml
Normal file
45
airootfs/usr/lib/calamares/modules/mobile/wait.qml
Normal file
@@ -0,0 +1,45 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Page
|
||||
{
|
||||
id: fdeWait
|
||||
|
||||
Item {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
height: 50
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
|
||||
}
|
||||
Text {
|
||||
id: waitText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: logo.bottom
|
||||
anchors.topMargin: 50
|
||||
wrapMode: Text.WordWrap
|
||||
text: "Formatting and mounting target partition. This may" +
|
||||
" take up to ten minutes, please be patient."
|
||||
width: 200
|
||||
}
|
||||
}
|
||||
}
|
||||
65
airootfs/usr/lib/calamares/modules/mobile/welcome.qml
Normal file
65
airootfs/usr/lib/calamares/modules/mobile/welcome.qml
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
import io.calamares.core 1.0
|
||||
import io.calamares.ui 1.0
|
||||
|
||||
import QtQuick 2.10
|
||||
import QtQuick.Controls 2.10
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.kde.kirigami 2.7 as Kirigami
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick.Window 2.3
|
||||
import QtQuick.VirtualKeyboard 2.1
|
||||
|
||||
Page
|
||||
{
|
||||
id: welcome
|
||||
|
||||
Item {
|
||||
id: appContainer
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 10
|
||||
height: 50
|
||||
fillMode: Image.PreserveAspectFit
|
||||
source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
|
||||
}
|
||||
Text {
|
||||
id: mainText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: logo.bottom
|
||||
anchors.topMargin: 10
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: "You are about to install<br>" +
|
||||
"<b>" + config.osName +
|
||||
" " + config.version + "</b><br>" +
|
||||
"user interface " +
|
||||
"<b>" + config.userInterface + "</b><br>" +
|
||||
"architecture " +
|
||||
"<b>" + config.arch + "</b><br>" +
|
||||
"on your <br>" +
|
||||
"<b>" + config.device + "</b><br>"
|
||||
width: 200
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: mainText.bottom
|
||||
anchors.topMargin: 10
|
||||
width: 200
|
||||
|
||||
text: qsTr("Continue")
|
||||
onClicked: navNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user