19 Commits

Author SHA1 Message Date
Adriaan de Groot
3a525359a5 Changes: pre-release housekeeping 2021-01-04 11:35:51 +01:00
Adriaan de Groot
7716052fbb CMake: behave a little more modern
- This copies in some more settings from Calamares-proper
2021-01-04 11:29:12 +01:00
Adriaan de Groot
5e7c6ef005 Merge pull request #7 from ollieparanoid/sd-to-emmc
[mobile] Offer to install from SD to eMMC
2020-12-24 14:53:02 +01:00
Oliver Smith
83da9b2485 [mobile] Offer to install from SD to eMMC
Make it possible to install from external storage to internal storage,
if the installer was booted from the external storage medium.
2020-12-19 13:16:01 +01:00
Oliver Smith
ceea4db418 [mobile] skipDisabledFeatures: add skip by func
Make it possible to skip features by defining a skip function.
2020-12-19 13:15:59 +01:00
Oliver Smith
86823162c6 [mobile] Config: remove redundant get{Bool,Str}
Use getBool and getString from utils/Variant.h instead. I had
implemented my own versions earlier, because I didn't know about the
existing ones.
2020-12-19 13:15:58 +01:00
Oliver Smith
19ffdc47bb [mobile] *Job: 'const QString&' for args
Related: https://github.com/calamares/calamares-extensions/pull/6#discussion_r545049605
2020-12-19 13:15:57 +01:00
Oliver Smith
0bd415f83e [mobile] Don't ignore partition job errors
Stop the installation if the partition job does not finish successfully
instead of carrying on and failing later on. While at it, elaborate some
more on why it is necessary to call the partition job early.

Related: https://github.com/calamares/calamares/issues/1600
2020-12-19 13:15:55 +01:00
Oliver Smith
028042afe7 [mobile] MobileQmlViewStep: remove unused include 2020-12-19 13:15:54 +01:00
Oliver Smith
59556074cc [mobile] create Users job in jobs()
Don't create the user's job in MobileQmlViewStep::onLeave() anymore.
Instead, have Config::createJobs() and call that from
MobileQmlViewStep::jobs(), as suggested by Adriaan, and that's also how
it's done in the keyboard module in calamares.git. This allows getting
rid of m_jobs, and makes it less awkward to pass parameters to the job.

Related: https://github.com/calamares/calamares-extensions/pull/6#discussion_r545048454
2020-12-19 13:15:53 +01:00
Oliver Smith
a6a9dc6890 [mobile] qml: use generic names for id fields
Rename "enableButton" to "firstButton" and "welcomeText" to "mainText".
2020-12-19 13:15:51 +01:00
Oliver Smith
ed7d63d913 [mobile] mobile.conf: tweak line ending 2020-12-19 13:15:49 +01:00
Oliver Smith
9b1da6ff8c [mobile] Config.h: tweak signal related comments
Fix a typo and make the "booleans we don't read from QML don't need
a signal" message generic with one example, but not with a listing of
all these booleans. Then we won't need to adjust this comment with each
new one.
2020-12-19 13:15:41 +01:00
Oliver Smith
7d3c483b35 [mobile] mobile.qml: fix whitespace error 2020-12-19 13:15:33 +01:00
Adriaan de Groot
0d057478b0 Merge pull request #6 from ollieparanoid/sshd-optional
[mobile] make sshd feature optional and related navigation refactoring
2020-12-17 13:27:34 +01:00
Oliver Smith
7f750cb145 [mobile] Make sshd feature optional
Add featureSshd config key to hide related UI screens and skip related
logic in UsersJob.
2020-12-09 15:39:14 +01:00
Oliver Smith
86e77b975f [mobile] navNextFeature: support skipping features
Skip whole features if a featureSshd (example for feature "sshd") or
smilar config key exists and is set to false.
2020-12-09 15:39:10 +01:00
Oliver Smith
70fa9fa766 [mobile] Refactor screen navigation logic
Organize the screens into features and replace all navTo() calls with
hardcoded screen names with new navNext() and navNextFeature()
functions.
2020-12-09 15:39:05 +01:00
Oliver Smith
c43bd06860 [mobile] navTo: add log message for screen change 2020-12-09 15:39:01 +01:00
22 changed files with 516 additions and 126 deletions

28
CHANGES Normal file
View File

@@ -0,0 +1,28 @@
<!-- SPDX-FileCopyrightText: no
SPDX-License-Identifier: CC0-1.0
-->
This is the changelog for Calamares-Extensions. For each release, the major
changes and contributors are listed. Note that Calamares-Extensions does not
have a historical changelog -- this log starts with version 1.0.0.
# 1.1.0 (2021-01-04) #
This release contains contributions from (alphabetically by first name):
- Oliver Smith
Changes and new modules in this release:
- *mobile* module bugfixes
- *mobile* module SSH daemon can be disabled
# 1.0.0 (2020-12-05) #
This release contains contributions from (alphabetically by first name):
- Oliver Smith
- ShalokShalom
Initial release of Calamares-Extensions as such, with one new module
- *mobile*, for PostmarketOS initial configuration

View File

@@ -26,13 +26,33 @@
#
cmake_minimum_required(VERSION 3.3 FATAL_ERROR)
project(calamares-extensions
VERSION 1.0.0
VERSION 1.1.0
LANGUAGES CXX
)
find_package(Calamares 3.2.26 REQUIRED)
find_package(YAMLCPP REQUIRED)
find_package(YAMLCPP REQUIRED) # Needed to untangle some dependencies before Calamares 3.2.36
### CMAKE SETUP
#
# Enable IN_LIST
if( POLICY CMP0057 )
cmake_policy( SET CMP0057 NEW )
endif()
# Let ``AUTOMOC`` and ``AUTOUIC`` process ``GENERATED`` files.
if( POLICY CMP0071 )
cmake_policy( SET CMP0071 NEW )
endif()
# Recognize more macros to trigger automoc
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.10.0")
list(APPEND CMAKE_AUTOMOC_MACRO_NAMES
"K_PLUGIN_FACTORY_WITH_JSON"
"K_EXPORT_PLASMA_DATAENGINE_WITH_JSON"
"K_EXPORT_PLASMA_RUNNER"
)
endif()
### BRANDING
#
@@ -67,6 +87,9 @@ calamares_add_module_subdirectory( modules/slowpython ) # Python job
# which builds a list of explanations; show that list.
calamares_explain_skipped_modules( ${SKIPPED_MODULES} )
### RELEASE SUPPORT
#
#
set( CALAMARES_VERSION ${calamares-extensions_VERSION_MAJOR}.${calamares-extensions_VERSION_MINOR}.${calamares-extensions_VERSION_PATCH} )
# In rare cases we have hotfix-releases with a tweak
if( calamares-extensions_VERSION_TWEAK )

View File

@@ -1,6 +1,12 @@
/* 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/Variant.h"
#include <QVariant>
Config::Config( QObject* parent )
@@ -8,37 +14,88 @@ Config::Config( QObject* parent )
{
}
QString
cfgStr( const QVariantMap& cfgMap, QString key, QString defaultStr )
{
QString ret = cfgMap.value( key ).toString();
if ( ret.isEmpty() )
{
return defaultStr;
}
return ret;
}
void
Config::setConfigurationMap( const QVariantMap& cfgMap )
{
m_osName = cfgStr( cfgMap, "osName", "(unknown)" );
m_arch = cfgStr( cfgMap, "arch", "(unknown)" );
m_device = cfgStr( cfgMap, "device", "(unknown)" );
m_userInterface = cfgStr( cfgMap, "userInterface", "(unknown)" );
m_version = cfgStr( cfgMap, "version", "(unknown)" );
m_username = cfgStr( cfgMap, "username", "user" );
using namespace CalamaresUtils;
m_cmdLuksFormat = cfgStr( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" );
m_cmdLuksOpen = cfgStr( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" );
m_cmdMkfsRoot = cfgStr( cfgMap, "cmdMkfsRoot", "mkfs.ext4 -L 'unknownOS_root'" );
m_cmdMount = cfgStr( cfgMap, "cmdMount", "mount" );
m_targetDeviceRoot = cfgStr( cfgMap, "targetDeviceRoot", "/dev/unknown" );
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_username = getString( cfgMap, "username", "user" );
m_cmdPasswd = cfgStr( cfgMap, "cmdPasswd", "passwd" );
m_cmdSshdEnable = cfgStr( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" );
m_cmdSshdDisable = cfgStr( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" );
m_cmdSshdUseradd = cfgStr( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" );
m_featureSshd = getBool( cfgMap, "featureSshd", true );
m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" );
m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" );
m_cmdMkfsRoot = getString( cfgMap, "cmdMkfsRoot", "mkfs.ext4 -L 'unknownOS_root'" );
m_cmdMount = getString( cfgMap, "cmdMount", "mount" );
m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" );
m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" );
m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" );
m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" );
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,
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 )
{
/* 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,
m_cmdMkfsRoot,
m_cmdMount,
m_targetDeviceRoot,
m_targetDeviceRootInternal,
m_installFromExternalToInternal,
m_isFdeEnabled,
m_fdePassword );
Calamares::JobResult res = j->exec();
Calamares::ViewManager* v = Calamares::ViewManager::instance();
if ( res )
{
v->next();
}
else
{
v->onInstallationFailed( res.message(), res.details() );
}
}
void
@@ -80,3 +137,9 @@ Config::setIsFdeEnabled( const bool isFdeEnabled )
{
m_isFdeEnabled = isFdeEnabled;
}
void
Config::setInstallFromExternalToInternal( const bool val )
{
m_installFromExternalToInternal = val;
}

View File

@@ -1,6 +1,9 @@
/* 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>
@@ -19,6 +22,7 @@ class Config : public QObject
Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged )
/* 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 )
@@ -28,11 +32,16 @@ class Config : public QObject
Q_PROPERTY( bool isFdeEnabled READ isFdeEnabled WRITE setIsFdeEnabled )
/* 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 cmdMkfsRoot READ cmdMkfsRoot 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 )
@@ -41,6 +50,7 @@ class Config : public QObject
public:
Config( QObject* parent = nullptr );
void setConfigurationMap( const QVariantMap& );
Calamares::JobList createJobs();
/* welcome */
QString osName() const { return m_osName; }
@@ -55,6 +65,7 @@ public:
void setUserPassword( const QString& userPassword );
/* ssh server + credetials */
bool featureSshd() { return m_featureSshd; }
QString sshdUsername() const { return m_sshdUsername; }
QString sshdPassword() const { return m_sshdPassword; }
bool isSshEnabled() { return m_isSshEnabled; }
@@ -69,11 +80,17 @@ public:
void setIsFdeEnabled( bool isFdeEnabled );
/* 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 cmdMkfsRoot() const { return m_cmdMkfsRoot; }
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; }
@@ -94,6 +111,7 @@ private:
QString m_userPassword;
/* ssh server + credetials */
bool m_featureSshd;
QString m_sshdUsername;
QString m_sshdPassword;
bool m_isSshEnabled;
@@ -103,11 +121,14 @@ private:
bool m_isFdeEnabled = false;
/* partition job */
QString m_cmdInternalStoragePrepare;
QString m_cmdLuksFormat;
QString m_cmdLuksOpen;
QString m_cmdMkfsRoot;
QString m_cmdMount;
QString m_targetDeviceRoot;
QString m_targetDeviceRootInternal;
bool m_installFromExternalToInternal;
/* users job */
QString m_cmdPasswd;
@@ -116,13 +137,14 @@ private:
QString m_cmdSshdUseradd;
signals:
/* booleans we don't read from QML (like isSshEnabled) don't need a signal */
/* default user */
void userPasswordChanged( QString userPassword );
/* ssh server + credetials */
/* ssh server + credentials */
void sshdUsernameChanged( QString sshdUsername );
void sshdPasswordChanged( QString sshdPassword );
/* isSshEnabled doesn't need a signal, we don't read it from QML */
/* full disk encryption */
void fdePasswordChanged( QString fdePassword );

View File

@@ -1,11 +1,8 @@
/* SPDX-FileCopyrightText: 2020 Oliver Smith <ollieparanoid@postmarketos.org>
* SPDX-License-Identifier: GPL-3.0-or-later */
#include "MobileQmlViewStep.h"
#include "PartitionJob.h"
#include "UsersJob.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "locale/LabelModel.h"
#include "utils/Dirs.h"
@@ -35,34 +32,7 @@ MobileQmlViewStep::MobileQmlViewStep( QObject* parent )
void
MobileQmlViewStep::onLeave()
{
Calamares::Job *partition, *users;
/* HACK: run partition job now */
partition = new PartitionJob( m_config->cmdLuksFormat(),
m_config->cmdLuksOpen(),
m_config->cmdMkfsRoot(),
m_config->cmdMount(),
m_config->targetDeviceRoot(),
m_config->isFdeEnabled(),
m_config->fdePassword() );
Calamares::JobResult res = partition->exec();
if ( !res )
{
cError() << "PARTITION JOB FAILED: " << res.message();
}
/* Put users job in queue (should run after unpackfs) */
m_jobs.clear();
QString cmdSshd = m_config->isSshEnabled() ? m_config->cmdSshdEnable() : m_config->cmdSshdDisable();
users = new UsersJob( m_config->cmdPasswd(),
cmdSshd,
m_config->cmdSshdUseradd(),
m_config->isSshEnabled(),
m_config->username(),
m_config->userPassword(),
m_config->sshdUsername(),
m_config->sshdPassword() );
m_jobs.append( Calamares::job_ptr( users ) );
return;
}
bool
@@ -95,7 +65,7 @@ MobileQmlViewStep::isAtEnd() const
Calamares::JobList
MobileQmlViewStep::jobs() const
{
return m_jobs;
return m_config->createJobs();
}
QObject*

View File

@@ -32,7 +32,6 @@ public:
private:
Config* m_config;
QList< Calamares::job_ptr > m_jobs;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( MobileQmlViewStepFactory )

View File

@@ -12,19 +12,25 @@
#include <QFileInfo>
PartitionJob::PartitionJob( QString cmdLuksFormat,
QString cmdLuksOpen,
QString cmdMkfsRoot,
QString cmdMount,
QString targetDeviceRoot,
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 )
{
@@ -77,10 +83,18 @@ PartitionJob::exec()
QString cryptDev = "/dev/mapper/" + cryptName;
QString passwordStdin = m_password + "\n";
QString dev = m_targetDeviceRoot;
QList< QPair< QStringList, QString > > commands = {};
QList< QPair< QStringList, QString > > commands = {
{ { "mkdir", "-p", pathMount }, QString() },
};
if ( m_installFromExternalToInternal )
{
dev = m_targetDeviceRootInternal;
commands.append( {
{ { "sh", "-c", m_cmdInternalStoragePrepare }, QString() },
} );
}
commands.append( { { { "mkdir", "-p", pathMount }, QString() } } );
if ( m_isFdeEnabled )
{

View File

@@ -8,11 +8,14 @@ class PartitionJob : public Calamares::Job
{
Q_OBJECT
public:
PartitionJob( QString cmdLuksFormat,
QString cmdLuksOpen,
QString cmdMkfsRoot,
QString cmdMount,
QString targetDeviceRoot,
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 );
@@ -22,11 +25,14 @@ public:
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;
};

View File

@@ -12,15 +12,17 @@
#include <QFileInfo>
UsersJob::UsersJob( QString cmdPasswd,
QString cmdSshd,
QString cmdSshdUseradd,
UsersJob::UsersJob( bool featureSshd,
const QString& cmdPasswd,
const QString& cmdSshd,
const QString& cmdSshdUseradd,
bool isSshEnabled,
QString username,
QString password,
QString sshdUsername,
QString sshdPassword )
const QString& username,
const QString& password,
const QString& sshdUsername,
const QString& sshdPassword )
: Calamares::Job()
, m_featureSshd( featureSshd )
, m_cmdPasswd( cmdPasswd )
, m_cmdSshd( cmdSshd )
, m_cmdSshdUseradd( cmdSshdUseradd )
@@ -48,14 +50,18 @@ UsersJob::exec()
QList< QPair< QStringList, QString > > commands = {
{ { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" },
{ { "sh", "-c", m_cmdSshd }, QString() },
};
if ( m_isSshEnabled )
if ( m_featureSshd )
{
commands.append( { { "sh", "-c", m_cmdSshdUseradd + " " + m_sshdUsername }, QString() } );
commands.append(
{ { "sh", "-c", m_cmdPasswd + " " + m_sshdUsername }, m_sshdPassword + "\n" + m_sshdPassword + "\n" } );
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 )

View File

@@ -8,14 +8,15 @@ class UsersJob : public Calamares::Job
{
Q_OBJECT
public:
UsersJob( QString cmdPasswd,
QString cmdSshd,
QString cmdSshdUseradd,
UsersJob( bool featureSshd,
const QString& cmdPasswd,
const QString& cmdSshd,
const QString& cmdSshdUseradd,
bool isSshEnabled,
QString username,
QString password,
QString sshdUsername,
QString sshdPassword );
const QString& username,
const QString& password,
const QString& sshdUsername,
const QString& sshdPassword );
QString prettyName() const override;
Calamares::JobResult exec() override;
@@ -23,6 +24,7 @@ public:
Calamares::JobList createJobs();
private:
bool m_featureSshd;
QString m_cmdPasswd;
QString m_cmdSshd;
QString m_cmdSshdUseradd;

View File

@@ -88,7 +88,7 @@ Item {
onClicked: {
if (validatePin(userPin, userPinRepeat, errorText)) {
config.userPassword = userPin.text;
navTo("ssh_confirm");
navNext();
}
}
}

View File

@@ -19,7 +19,7 @@ Item {
height: parent.height
Text {
id: welcomeText
id: mainText
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 30
@@ -37,29 +37,29 @@ Item {
}
Button {
id: enableButton
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: welcomeText.bottom
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Enable")
onClicked: {
config.isFdeEnabled = true;
navTo("fde_pass");
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: enableButton.bottom
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Disable")
onClicked: {
config.isFdeEnabled = false;
navTo("install_confirm");
navNextFeature();
}
}
}

View File

@@ -73,7 +73,7 @@ Item {
onClicked: {
if (validatePassword(password, passwordRepeat, errorText)) {
config.fdePassword = password.text;
navTo("install_confirm");
navNext();
}
}
}

View File

@@ -19,24 +19,38 @@ Item {
height: parent.height
Text {
id: welcomeText
id: mainText
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 30
wrapMode: Text.WordWrap
text: "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. Afterwards, it will reboot into the" +
" installed system."
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><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: 500
}
Button {
id: enableButton
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: welcomeText.bottom
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500

View 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: 30
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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Internal (eMMC)")
onClicked: {
config.installFromExternalToInternal = true;
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("External (SD card)")
onClicked: {
config.installFromExternalToInternal = false;
navNextFeature();
}
}
}

View File

@@ -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: 30
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: 500
}
Button {
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Yes")
onClicked: {
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("No")
onClicked: {
navBack();
}
}
}

View File

@@ -27,9 +27,32 @@
## Name of the device (e.g. PinePhone)
# device: "(unknown)"
## Partition that will be formatted and mounted (optionally with FDE) for the rootfs
## 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
#######
### Commands running in the installer OS
#######
@@ -52,6 +75,13 @@
## 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)
#######

View File

@@ -17,6 +17,8 @@ Page
property var screenPrevious: []
property var titles: {
"welcome": null, /* titlebar disabled */
"install_target": "Installation target",
"install_target_confirm": "Warning",
"default_pin": "Lockscreen PIN",
"ssh_confirm": "SSH server",
"ssh_credentials": "SSH credentials",
@@ -25,6 +27,31 @@ Page
"install_confirm": "Ready to install",
"wait": null
}
property var features: [
{"name": "welcome",
"screens": ["welcome"]},
{"name": "installTarget",
"screens": ["install_target", "install_target_confirm"]},
{"name": "userPin",
"screens": ["default_pin"]},
{"name": "sshd",
"screens": ["ssh_confirm", "ssh_credentials"]},
{"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, "default_pin": 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 the initramfs on-screen keyboard
* (osk-sdl: see src/keyboard.cpp). FIXME: make configurable, but keep this as default? */
property var allowed_chars:
@@ -120,8 +147,13 @@ Page
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;
@@ -131,15 +163,71 @@ Page
Qt.inputMethod.hide();
}
function navFinish() {
/* Show a waiting screen and wait a second (so it can render), then let
* MobileQmlViewStep.cpp::onLeave() create the (encrypted) partition
* and mount it. We can't have this as proper job due to ondev#18. */
/* 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(ViewManager.next);
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 = featureIdByScreen[screen] + 1;
/* Skip disabled features */
do {
/* 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);
id += 1;
continue;
}
/* Check skipFeature<Name>() */
var funcName = "skipFeature" + nameUp;
if (eval("typeof " + funcName) === "function"
&& eval(funcName + "()")) {
console.log("Skipping feature (skip function): " + name);
id += 1;
continue;
}
} while(false);
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);
@@ -183,7 +271,7 @@ Page
if (repeat != pin)
return validationFailure(errorText,
"The PINs don't match.");
return validationFailureClear(errorText);
}
function validateSshdUsername(username, errorText) {

View File

@@ -4,6 +4,9 @@
<file>welcome.qml</file>
<file>install_target.qml</file> <!-- install from external to internal? -->
<file>install_target_confirm.qml</file> <!-- overwrite internal storage? -->
<file>default_pin.qml</file> <!-- default user: pin -->
<file>ssh_confirm.qml</file> <!-- sshd: enable or not? -->
<file>ssh_credentials.qml</file> <!-- sshd user: username, password -->

View File

@@ -19,7 +19,7 @@ Item {
height: parent.height
Text {
id: welcomeText
id: mainText
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 30
@@ -40,29 +40,29 @@ Item {
}
Button {
id: enableButton
id: firstButton
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: welcomeText.bottom
anchors.top: mainText.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Enable")
onClicked: {
config.isSshEnabled = true;
navTo("ssh_credentials");
navNext();
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: enableButton.bottom
anchors.top: firstButton.bottom
anchors.topMargin: 40
width: 500
text: qsTr("Disable")
onClicked: {
config.isSshEnabled = false;
navTo("fde_confirm");
navNextFeature();
}
}
}

View File

@@ -100,7 +100,7 @@ Item {
config.sshdUsername = username.text;
config.sshdPassword = password.text;
navTo("fde_confirm");
navNext();
}
}
}

View File

@@ -34,7 +34,7 @@ Page
source: "file:///usr/share/calamares/branding/default-mobile/logo.png"
}
Text {
id: welcomeText
id: mainText
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: logo.bottom
anchors.topMargin: 50
@@ -53,12 +53,12 @@ Page
Button {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: welcomeText.bottom
anchors.top: mainText.bottom
anchors.topMargin: 50
width: 500
text: qsTr("Continue")
onClicked: navTo("default_pin")
onClicked: navNext()
}
}
}