Merge branch 'calamares' of https://github.com/calamares/calamares into development

This commit is contained in:
Philip Mueller 2024-10-09 11:33:13 +07:00
commit df37ae610b
27 changed files with 493 additions and 146 deletions

View File

@ -10,13 +10,21 @@ the history of the 3.2 series (2018-05 - 2022-08).
# 3.3.10 (unreleased)
This release contains contributions from (alphabetically by given name):
- Nobody yet
- Adriaan de Groot
- Evan James
- Neal Gompa
## Core ##
- Nothing yet
## Modules ##
- Nothing yet
- *keyboard* Repaired summary messages with strange formatting. (#2364)
- *keyboard* Can update KDE Plasma configuration in Wayland. (thanks Neal, #2264)
- *locale* Repaired summary messages with strange formatting. (#2364)
- *umount* Correctly unmounts the root filesystem of the target. (thanks Evan)
- *users* Supports a new `home_permissions` setting to override the
distro's `useradd` configuration of the umask. (#2362)
- *welcome* Follows system styling colors (e.g. Dark Mode).
# 3.3.9 (2024-08-12)

View File

@ -5,7 +5,9 @@
# There is no docker image for EndeavoudOS, and the live ISO
# for Cassini Nova is KF5 / Qt5 based, but we can build there.
# It even has most of the build-deps already installed.
pacman -Syu --noconfirm git cmake ninja jq
pacman -S --noconfirm gcc yaml-cpp icu
pacman -S --noconfirm extra-cmake-modules
pacman -Syu --noconfirm jq
pacman -S --noconfirm git cmake ninja jq || exit 1
pacman -S --noconfirm gcc yaml-cpp icu || exit 1
pacman -S --noconfirm extra-cmake-modules || exit 1
pacman -S --noconfirm python-jsonschema || exit 1

View File

@ -378,9 +378,9 @@
<location filename="../src/libcalamares/modulesystem/RequirementsChecker.cpp" line="124"/>
<source>Waiting for %n module(s)</source>
<comment>@status</comment>
<translation type="unfinished">
<numerusform/>
<numerusform/>
<translation>
<numerusform>Waiting for %n module<EFBFBD>~@<EFBFBD></numerusform>
<numerusform>Waiting for %n modules<EFBFBD>~@<EFBFBD></numerusform>
</translation>
</message>
<message numerus="yes">
@ -388,8 +388,8 @@
<source>(%n second(s))</source>
<comment>@status</comment>
<translation type="unfinished">
<numerusform/>
<numerusform/>
<numerusform>(%n second)</numerusform>
<numerusform>(%n seconds)</numerusform>
</translation>
</message>
<message>
@ -965,31 +965,31 @@ The installer will quit and all changes will be lost.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/keyboard/Config.cpp" line="506"/>
<source>Keyboard model has been set to %1&lt;br/&gt;.</source>
<location filename="../src/modules/keyboard/Config.cpp" line="504"/>
<source>Keyboard model has been set to %1.</source>
<comment>@label, %1 is keyboard model, as in Apple Magic Keyboard</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/keyboard/Config.cpp" line="513"/>
<location filename="../src/modules/keyboard/Config.cpp" line="512"/>
<source>Keyboard layout has been set to %1/%2.</source>
<comment>@label, %1 is layout, %2 is layout variant</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/locale/Config.cpp" line="379"/>
<source>Set timezone to %1/%2</source>
<location filename="../src/modules/locale/Config.cpp" line="381"/>
<source>Set timezone to %1.</source>
<comment>@action</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/locale/Config.cpp" line="416"/>
<location filename="../src/modules/locale/Config.cpp" line="418"/>
<source>The system language will be set to %1.</source>
<comment>@info</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/locale/Config.cpp" line="423"/>
<location filename="../src/modules/locale/Config.cpp" line="425"/>
<source>The numbers and dates locale will be set to %1.</source>
<comment>@info</comment>
<translation type="unfinished"/>
@ -2281,7 +2281,7 @@ The installer will quit and all changes will be lost.</source>
<context>
<name>LocaleTests</name>
<message>
<location filename="../src/libcalamares/locale/Tests.cpp" line="266"/>
<location filename="../src/libcalamares/locale/Tests.cpp" line="272"/>
<source>Quit</source>
<translation type="unfinished"/>
</message>
@ -3246,174 +3246,179 @@ The installer will quit and all changes will be lost.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="143"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="144"/>
<source>Install %1 &lt;strong&gt;alongside&lt;/strong&gt; another operating system</source>
<comment>@label</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="147"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="148"/>
<source>&lt;strong&gt;Erase&lt;/strong&gt; disk and install %1</source>
<comment>@label</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="151"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="152"/>
<source>&lt;strong&gt;Replace&lt;/strong&gt; a partition with %1</source>
<comment>@label</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="156"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="157"/>
<source>&lt;strong&gt;Manual&lt;/strong&gt; partitioning</source>
<comment>@label</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="178"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="179"/>
<source>Install %1 &lt;strong&gt;alongside&lt;/strong&gt; another operating system on disk &lt;strong&gt;%2&lt;/strong&gt; (%3)</source>
<comment>@info</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="187"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="188"/>
<source>&lt;strong&gt;Erase&lt;/strong&gt; disk &lt;strong&gt;%2&lt;/strong&gt; (%3) and install %1</source>
<comment>@info</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="194"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="195"/>
<source>&lt;strong&gt;Replace&lt;/strong&gt; a partition on disk &lt;strong&gt;%2&lt;/strong&gt; (%3) with %1</source>
<comment>@info</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="203"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="204"/>
<source>&lt;strong&gt;Manual&lt;/strong&gt; partitioning on disk &lt;strong&gt;%1&lt;/strong&gt; (%2)</source>
<comment>@info</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="213"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="214"/>
<source>Disk &lt;strong&gt;%1&lt;/strong&gt; (%2)</source>
<comment>@info</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="263"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="239"/>
<source>Create a swap file.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="268"/>
<source>Unsafe partition actions are enabled.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="266"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="271"/>
<source>Partitioning is configured to &lt;b&gt;always&lt;/b&gt; fail.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="269"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="274"/>
<source>No partitions will be changed.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="309"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="314"/>
<source>Current:</source>
<comment>@label</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="327"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="332"/>
<source>After:</source>
<comment>@label</comment>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="550"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="555"/>
<source>An EFI system partition is necessary to start %1.&lt;br/&gt;&lt;br/&gt;To configure an EFI system partition, go back and select or create a suitable filesystem.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="556"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="561"/>
<source>An EFI system partition is necessary to start %1.&lt;br/&gt;&lt;br/&gt;The EFI system partition does not meet recommendations. It is recommended to go back and select or create a suitable filesystem.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="564"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="569"/>
<source>The filesystem must be mounted on &lt;strong&gt;%1&lt;/strong&gt;.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="565"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="570"/>
<source>The filesystem must have type FAT32.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="566"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="571"/>
<source>The filesystem must have flag &lt;strong&gt;%1&lt;/strong&gt; set.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="574"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="576"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="579"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="581"/>
<source>The filesystem must be at least %1 MiB in size.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="578"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="583"/>
<source>The minimum recommended size for the filesystem is %1 MiB.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="580"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="585"/>
<source>You can continue without setting up an EFI system partition but your system may fail to start.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="582"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="587"/>
<source>You can continue with this EFI system partition configuration but your system may fail to start.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="594"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="599"/>
<source>No EFI system partition configured</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="602"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="607"/>
<source>EFI system partition configured incorrectly</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="622"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="627"/>
<source>EFI system partition recommendation</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="641"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="646"/>
<source>Option to use GPT on BIOS</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="642"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="647"/>
<source>A GPT partition table is the best option for all systems. This installer supports such a setup for BIOS systems too.&lt;br/&gt;&lt;br/&gt;To configure a GPT partition table on BIOS, (if not done so already) go back and set the partition table to GPT, next create a 8 MB unformatted partition with the &lt;strong&gt;%2&lt;/strong&gt; flag enabled.&lt;br/&gt;&lt;br/&gt;An unformatted 8 MB partition is necessary to start %1 on a BIOS system with GPT.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="664"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="669"/>
<source>Boot partition not encrypted</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="665"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="670"/>
<source>A separate boot partition was set up together with an encrypted root partition, but the boot partition is not encrypted.&lt;br/&gt;&lt;br/&gt;There are security concerns with this kind of setup, because important system files are kept on an unencrypted partition.&lt;br/&gt;You may continue if you wish, but filesystem unlocking will happen later during system startup.&lt;br/&gt;To encrypt the boot partition, go back and recreate it, selecting &lt;strong&gt;Encrypt&lt;/strong&gt; in the partition creation window.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="757"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="762"/>
<source>has at least one disk device available.</source>
<translation type="unfinished"/>
</message>
<message>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="758"/>
<location filename="../src/modules/partition/PartitionViewStep.cpp" line="763"/>
<source>There are no partitions to install on.</source>
<translation type="unfinished"/>
</message>

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-03 22:44+0200\n"
"POT-Creation-Date: 2024-09-06 14:34+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -230,24 +230,24 @@ msgid_plural "Removing %(num)d packages."
msgstr[0] ""
msgstr[1] ""
#: src/modules/packages/main.py:769 src/modules/packages/main.py:781
#: src/modules/packages/main.py:809
#: src/modules/packages/main.py:775 src/modules/packages/main.py:787
#: src/modules/packages/main.py:815
msgid "Package Manager error"
msgstr ""
#: src/modules/packages/main.py:770
#: src/modules/packages/main.py:776
msgid ""
"The package manager could not prepare updates. The command <pre>{!s}</pre> "
"returned error code {!s}."
msgstr ""
#: src/modules/packages/main.py:782
#: src/modules/packages/main.py:788
msgid ""
"The package manager could not update the system. The command <pre>{!s}</pre> "
"returned error code {!s}."
msgstr ""
#: src/modules/packages/main.py:810
#: src/modules/packages/main.py:816
msgid ""
"The package manager could not make changes to the installed system. The "
"command <pre>{!s}</pre> returned error code {!s}."

View File

@ -236,7 +236,7 @@ if(KPMcore_FOUND)
calamares_add_test(libcalamarespartitionkpmtest SOURCES partition/KPMTests.cpp LIBRARIES calamares::kpmcore)
endif()
calamares_add_test(libcalamaresutilstest SOURCES utils/Tests.cpp utils/Runner.cpp)
calamares_add_test(libcalamaresutilstest SOURCES utils/Tests.cpp utils/Permissions.cpp utils/Runner.cpp)
calamares_add_test(libcalamaresutilspathstest SOURCES utils/TestPaths.cpp)

View File

@ -60,9 +60,12 @@ public:
TimeZoneData( const TimeZoneData& ) = delete;
TimeZoneData( TimeZoneData&& ) = delete;
///@brief Returns a translated, human-readable form of region/zone (e.g. "America/New York")
QString translated() const override;
///@brief Returns the region key (e.g. "Europe") with no translation and no human-readable tweaks
QString region() const { return m_region; }
///@brief Returns the zone key (e.g. "New_York") with no translation and no human-readable tweaks
QString zone() const { return key(); }
QString country() const { return m_country; }

View File

@ -50,9 +50,8 @@ Permissions::parsePermissions( QString const& p )
return;
}
bool ok;
int octal = segments[ 2 ].toInt( &ok, 8 );
if ( !ok || octal == 0 )
const auto octal = parseFileMode( segments[ 2 ] );
if ( octal <= 0 )
{
m_valid = false;
return;
@ -120,4 +119,24 @@ Permissions::apply( const QString& path, const Calamares::Permissions& p )
return r;
}
int
parseFileMode( const QString& mode )
{
bool ok;
int octal = mode.toInt( &ok, 8 );
if ( !ok )
{
return -1;
}
if ( 0777 < octal )
{
return -1;
}
if ( octal < 0 )
{
return -1;
}
return octal;
}
} // namespace Calamares

View File

@ -16,8 +16,10 @@ namespace Calamares
{
/**
* @brief The Permissions class takes a QString @p in the form of
* <user>:<group>:<permissions>, checks it for validity, and makes the three
* @brief Represents a <user>:<group>:<file-mode>
*
* The Permissions class takes a QString @p in the form of
* <user>:<group>:<file-mode>, checks it for validity, and makes the three
* components available indivdually.
*/
class DLLEXPORT Permissions
@ -27,8 +29,9 @@ public:
/** @brief Constructor
*
* Splits the string @p at the colon (":") into separate elements for
* <user>, <group>, and <value> (permissions), where <value> is interpreted
* as an **octal** integer. That is, "root:wheel:755" will give
* <user>, <group>, and <file-mode> (permissions), where <file-mode> is any
* value that can be parsed by parseFileMode() . One valid form
* is an **octal** integer. That is, "root:wheel:755" will give
* you an integer value of four-hundred-ninety-three (493),
* corresponding to the UNIX file permissions rwxr-xr-x,
* as one would expect from chmod and other command-line utilities.
@ -90,6 +93,17 @@ private:
bool m_valid;
};
/**
* @brief Parses a file-mode and returns it as an integer
*
* Returns -1 on error.
*
* Valid forms of @p mode are:
* - octal representation, with an optional leading 0 and at most three
* octal digits (e.g. 0755 or 644)
*/
DLLEXPORT int parseFileMode( const QString& mode );
} // namespace Calamares
#endif // LIBCALAMARES_PERMISSIONS_H

View File

@ -63,6 +63,7 @@ private Q_SLOTS:
/** @section Test that all the UMask objects work correctly. */
void testUmask();
void testPermissions();
/** @section Tests the entropy functions. */
void testEntropy();
@ -571,6 +572,24 @@ LibCalamaresTests::testUmask()
QCOMPARE( Calamares::setUMask( m ), mode_t( 022 ) );
}
void
LibCalamaresTests::testPermissions()
{
for ( int i = 0; i <= 0777; ++i )
{
const QString repr = QString::number( i, 8 );
QCOMPARE( Calamares::parseFileMode( repr ), i );
QCOMPARE( Calamares::parseFileMode( QChar( '0' ) + repr ), i );
QCOMPARE( Calamares::parseFileMode( QStringLiteral( " %1\n" ).arg( repr ) ), i );
}
// Failures
QCOMPARE( Calamares::parseFileMode( QStringLiteral( "1024" ) ), -1 );
QCOMPARE( Calamares::parseFileMode( QStringLiteral( "rwxr-----" ) ), -1 );
QCOMPARE( Calamares::parseFileMode( QStringLiteral( "o644" ) ), -1 );
QCOMPARE( Calamares::parseFileMode( QStringLiteral( "O_WRONLY" ) ), -1 );
}
void
LibCalamaresTests::testEntropy()
{

View File

@ -11,19 +11,37 @@
#include "WaitingWidget.h"
#include "utils/Gui.h"
#include "utils/Logger.h"
#include <QBoxLayout>
#include <QEvent>
#include <QLabel>
#include <QTimer>
static void
colorSpinner( WaitingSpinnerWidget* spinner )
{
const auto color = spinner->palette().text().color();
spinner->setTextColor( color );
spinner->setColor( color );
}
static void
styleSpinner( WaitingSpinnerWidget* spinner, int size )
{
spinner->setFixedSize( size, size );
spinner->setInnerRadius( size / 2 );
spinner->setLineLength( size / 2 );
spinner->setLineWidth( size / 8 );
colorSpinner( spinner );
}
WaitingWidget::WaitingWidget( const QString& text, QWidget* parent )
: WaitingSpinnerWidget( parent, false, false )
{
int spnrSize = Calamares::defaultFontHeight() * 4;
setFixedSize( spnrSize, spnrSize );
setInnerRadius( spnrSize / 2 );
setLineLength( spnrSize / 2 );
setLineWidth( spnrSize / 8 );
styleSpinner( this, spnrSize );
setAlignment( Qt::AlignmentFlag::AlignBottom );
setText( text );
start();
@ -31,6 +49,16 @@ WaitingWidget::WaitingWidget( const QString& text, QWidget* parent )
WaitingWidget::~WaitingWidget() {}
void
WaitingWidget::changeEvent( QEvent* event )
{
if ( event->type() == QEvent::PaletteChange )
{
colorSpinner( this );
}
WaitingSpinnerWidget::changeEvent( event );
}
struct CountdownWaitingWidget::Private
{
std::chrono::seconds duration;
@ -52,13 +80,8 @@ CountdownWaitingWidget::CountdownWaitingWidget( std::chrono::seconds duration, Q
{
// Set up the label first for sizing
const int labelHeight = qBound( 16, Calamares::defaultFontHeight() * 3 / 2, 64 );
// Set up the spinner
setFixedSize( labelHeight, labelHeight );
styleSpinner( this, labelHeight );
setRevolutionsPerSecond( 1.0 / double( duration.count() ) );
setInnerRadius( labelHeight / 2 );
setLineLength( labelHeight / 2 );
setLineWidth( labelHeight / 8 );
setAlignment( Qt::AlignmentFlag::AlignVCenter );
// Last because it updates the text
@ -117,3 +140,13 @@ CountdownWaitingWidget::tick()
timeout();
}
}
void
CountdownWaitingWidget::changeEvent( QEvent* event )
{
if ( event->type() == QEvent::PaletteChange )
{
colorSpinner( this );
}
WaitingSpinnerWidget::changeEvent( event );
}

View File

@ -32,6 +32,9 @@ public:
/// Create a WaitingWidget with initial @p text label.
explicit WaitingWidget( const QString& text, QWidget* parent = nullptr );
~WaitingWidget() override;
protected:
void changeEvent( QEvent* event ) override;
};
/** @brief A spinner and a countdown inside it
@ -64,6 +67,9 @@ Q_SIGNALS:
protected Q_SLOTS:
void tick();
protected:
void changeEvent( QEvent* event ) override;
private:
struct Private;
std::unique_ptr< Private > d;

View File

@ -11,7 +11,6 @@
#include "Config.h"
#include "SetKeyboardLayoutJob.h"
#include "keyboardwidget/keyboardpreview.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
@ -27,6 +26,7 @@
#include <QGuiApplication>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>
#include <QTimer>
#include <QDBusConnection>
@ -36,7 +36,7 @@
/* Returns stringlist with suitable setxkbmap command-line arguments
* to set the given @p model.
*/
static inline QStringList
static QStringList
xkbmap_model_args( const QString& model )
{
QStringList r { "-model", model };
@ -46,7 +46,7 @@ xkbmap_model_args( const QString& model )
/* Returns stringlist with suitable setxkbmap command-line arguments
* to set the given @p layout and @p variant.
*/
static inline QStringList
static QStringList
xkbmap_layout_args( const QString& layout, const QString& variant )
{
QStringList r { "-layout", layout };
@ -57,10 +57,10 @@ xkbmap_layout_args( const QString& layout, const QString& variant )
return r;
}
static inline QStringList
xkbmap_layout_args( const QStringList& layouts,
static QStringList
xkbmap_layout_args_with_group_switch( const QStringList& layouts,
const QStringList& variants,
const QString& switchOption = "grp:alt_shift_toggle" )
const QString& switchOption )
{
if ( layouts.size() != variants.size() )
{
@ -161,24 +161,16 @@ Config::Config( QObject* parent )
, m_keyboardVariantsModel( new KeyboardVariantsModel( this ) )
, m_KeyboardGroupSwitcherModel( new KeyboardGroupsSwitchersModel( this ) )
{
m_setxkbmapTimer.setSingleShot( true );
m_applyTimer.setSingleShot( true );
connect( &m_applyTimer, &QTimer::timeout, this, &Config::apply );
// Connect signals and slots
connect( m_keyboardModelsModel,
&KeyboardModelsModel::currentIndexChanged,
[ & ]( int index )
{
// Set Xorg keyboard model
m_selectedModel = m_keyboardModelsModel->key( index );
if ( m_useLocale1 )
{
locale1Apply();
}
else
{
QProcess::execute( "setxkbmap", xkbmap_model_args( m_selectedModel ) );
}
emit prettyStatusChanged();
somethingChanged();
} );
connect( m_keyboardLayoutsModel,
@ -195,16 +187,14 @@ Config::Config( QObject* parent )
[ & ]( int index )
{
m_selectedVariant = m_keyboardVariantsModel->key( index );
xkbChanged();
emit prettyStatusChanged();
somethingChanged();
} );
connect( m_KeyboardGroupSwitcherModel,
&KeyboardGroupsSwitchersModel::currentIndexChanged,
[ & ]( int index )
{
m_selectedGroup = m_KeyboardGroupSwitcherModel->key( index );
xkbChanged();
emit prettyStatusChanged();
somethingChanged();
} );
// If the user picks something explicitly -- not a consequence of
@ -224,30 +214,36 @@ Config::Config( QObject* parent )
}
void
Config::xkbChanged()
Config::somethingChanged()
{
// Set Xorg keyboard layout + variant
if ( m_setxkbmapTimer.isActive() )
if ( m_applyTimer.isActive() )
{
m_setxkbmapTimer.stop();
m_setxkbmapTimer.disconnect( this );
m_applyTimer.stop();
}
if ( m_useLocale1 )
{
connect( &m_setxkbmapTimer, &QTimer::timeout, this, &Config::locale1Apply );
}
else
{
connect( &m_setxkbmapTimer, &QTimer::timeout, this, &Config::xkbApply );
}
m_setxkbmapTimer.start( QApplication::keyboardInputInterval() );
m_applyTimer.start( QApplication::keyboardInputInterval() );
emit prettyStatusChanged();
}
void
Config::locale1Apply()
Config::apply()
{
if ( m_configureXkb )
{
applyXkb();
}
if ( m_configureLocale1 )
{
applyLocale1();
}
if ( m_configureKWin )
{
applyKWin();
}
// Writing /etc/ files is not needed "live"
}
void
Config::applyLocale1()
{
m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout );
@ -283,10 +279,11 @@ Config::locale1Apply()
}
void
Config::xkbApply()
Config::applyXkb()
{
m_additionalLayoutInfo = getAdditionalLayoutInfo( m_selectedLayout );
QStringList basicArguments = xkbmap_model_args( m_selectedModel );
if ( !m_additionalLayoutInfo.additionalLayout.isEmpty() )
{
if ( !m_selectedGroup.isEmpty() )
@ -303,10 +300,11 @@ Config::xkbApply()
m_additionalLayoutInfo.groupSwitcher = "grp:alt_shift_toggle";
}
QProcess::execute( "setxkbmap",
xkbmap_layout_args( { m_additionalLayoutInfo.additionalLayout, m_selectedLayout },
basicArguments.append(
xkbmap_layout_args_with_group_switch( { m_additionalLayoutInfo.additionalLayout, m_selectedLayout },
{ m_additionalLayoutInfo.additionalVariant, m_selectedVariant },
m_additionalLayoutInfo.groupSwitcher ) );
QProcess::execute( "setxkbmap", basicArguments );
cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant << "(added "
<< m_additionalLayoutInfo.additionalLayout << "-" << m_additionalLayoutInfo.additionalVariant
@ -314,12 +312,92 @@ Config::xkbApply()
}
else
{
QProcess::execute( "setxkbmap", xkbmap_layout_args( m_selectedLayout, m_selectedVariant ) );
basicArguments.append( xkbmap_layout_args( m_selectedLayout, m_selectedVariant ) );
QProcess::execute( "setxkbmap", basicArguments );
cDebug() << "xkbmap selection changed to: " << m_selectedLayout << '-' << m_selectedVariant;
}
m_setxkbmapTimer.disconnect( this );
m_applyTimer.stop();
}
// In a config-file's list of lines, replace lines <key>=<something> by <key>=<value>
static void
replaceKey( QStringList& content, const QString& key, const QString& value )
{
for ( int i = 0; i < content.length(); ++i )
{
if ( content.at( i ).startsWith( key ) )
{
content[ i ] = key + value;
}
}
}
static bool
rewriteKWin( const QString& path, const QString& model, const QString& layouts, const QString& variants )
{
if ( !QFile::exists( path ) )
{
return false;
}
QFile config( path );
if ( !config.open( QIODevice::ReadOnly ) )
{
return false;
}
QStringList content = []( QFile& f )
{
QTextStream s( &f );
return s.readAll().split( '\n' );
}( config );
config.close();
if ( !config.open( QIODevice::WriteOnly ) )
{
return false;
}
replaceKey( content, QStringLiteral( "Model=" ), model );
replaceKey( content, QStringLiteral( "LayoutList=" ), layouts );
replaceKey( content, QStringLiteral( "VariantList=" ), variants );
config.write( content.join( '\n' ).toUtf8() );
config.close();
return true;
}
void
Config::applyKWin()
{
const auto paths = QStandardPaths::standardLocations( QStandardPaths::ConfigLocation );
auto join = [ &additional = m_additionalLayoutInfo.additionalLayout ]( const QString& s1, const QString& s2 )
{ return additional.isEmpty() ? s1 : QStringLiteral( "%1,%2" ).arg( s1, s2 ); };
const QString layouts = join( m_selectedLayout, m_additionalLayoutInfo.additionalLayout );
const QString variants = join( m_selectedVariant, m_additionalLayoutInfo.additionalVariant );
bool updated = false;
for ( const auto& path : paths )
{
const QString candidate = path + QStringLiteral( "/kxkbrc" );
if ( rewriteKWin( candidate, m_selectedModel, layouts, variants ) )
{
updated = true;
break;
}
}
if ( updated )
{
auto kwin = QDBusMessage::createSignal(
QStringLiteral( "/Layouts" ), QStringLiteral( "org.kde.keyboard" ), QStringLiteral( "reloadConfig" ) );
QDBusConnection::sessionBus().send( kwin );
}
}
KeyboardModelsModel*
Config::keyboardModels() const
{
@ -455,7 +533,7 @@ Config::detectCurrentKeyboardLayout()
QString currentVariant;
QString currentModel;
if ( m_useLocale1 )
if ( m_configureLocale1 )
{
getCurrentKeyboardLayoutLocale1( currentLayout, currentVariant, currentModel );
}
@ -501,10 +579,10 @@ Config::detectCurrentKeyboardLayout()
QString
Config::prettyStatus() const
{
QString status;
status
+= tr( "Keyboard model has been set to %1<br/>.", "@label, %1 is keyboard model, as in Apple Magic Keyboard" )
QString status
= tr( "Keyboard model has been set to %1.", "@label, %1 is keyboard model, as in Apple Magic Keyboard" )
.arg( m_keyboardModelsModel->label( m_keyboardModelsModel->currentIndex() ) );
status += QStringLiteral( "<br/>" );
QString layout = m_keyboardLayoutsModel->item( m_keyboardLayoutsModel->currentIndex() ).second.description;
QString variant = m_keyboardVariantsModel->currentIndex() >= 0
@ -527,8 +605,8 @@ Config::createJobs()
m_additionalLayoutInfo,
m_xOrgConfFileName,
m_convertedKeymapPath,
m_writeEtcDefaultKeyboard,
m_useLocale1 );
m_configureEtcDefaultKeyboard,
m_configureLocale1 );
list.append( Calamares::job_ptr( j ) );
return list;
@ -718,8 +796,13 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_xOrgConfFileName = xorgConfDefault;
}
m_convertedKeymapPath = getString( configurationMap, "convertedKeymapPath" );
m_writeEtcDefaultKeyboard = getBool( configurationMap, "writeEtcDefaultKeyboard", true );
m_useLocale1 = getBool( configurationMap, "useLocale1", !isX11 );
m_configureEtcDefaultKeyboard = getBool( configurationMap, "writeEtcDefaultKeyboard", true );
m_configureLocale1 = getBool( configurationMap, "useLocale1", !isX11 );
bool bogus = false;
const auto configureItems = getSubMap( configurationMap, "configure", bogus );
m_configureKWin = getBool( configureItems, "kwin", false );
m_guessLayout = getBool( configurationMap, "guessLayout", true );
}

View File

@ -86,14 +86,17 @@ private:
* keyboard layout. This introduces a slight delay between selecting
* a keyboard, and applying it to the system -- so that if you
* scroll through or down-arrow through the list of keyboards,
* you don't get buried under xkbset processes.
* you don't get buried under updates which might take some time.
*
* xkbChanged() is called when the selection changes, and triggers
* a delayed call to xkbApply() which does the actual work.
* somethingChanged() is called when the selection changes, and triggers
* a delayed call to apply() which does the actual work by calling the
* relevant apply*() functions.
*/
void xkbChanged();
void xkbApply();
void locale1Apply();
void somethingChanged();
void apply();
void applyLocale1();
void applyXkb();
void applyKWin();
void getCurrentKeyboardLayoutXkb( QString& currentLayout, QString& currentVariant, QString& currentModel );
void getCurrentKeyboardLayoutLocale1( QString& currentLayout, QString& currentVariant, QString& currentModel );
@ -111,13 +114,15 @@ private:
// Layout (and corresponding info) added if current one doesn't support ASCII (e.g. Russian or Japanese)
AdditionalLayoutInfo m_additionalLayoutInfo;
QTimer m_setxkbmapTimer;
QTimer m_applyTimer;
// From configuration
QString m_xOrgConfFileName;
QString m_convertedKeymapPath;
bool m_writeEtcDefaultKeyboard = true;
bool m_useLocale1 = false;
bool m_configureXkb = true;
bool m_configureEtcDefaultKeyboard = true;
bool m_configureLocale1 = false;
bool m_configureKWin = false;
bool m_guessLayout = false;
// The state determines whether we guess settings or preserve them:

View File

@ -31,3 +31,15 @@ convertedKeymapPath: "/lib/kbd/keymaps/xkb"
# Guess the default layout from the user locale. If false, keeps the current
# OS keyboard layout as the default (useful if the layout is pre-configured).
#guessLayout: true
# Things that should be configured.
configure:
# Configure KWin (KDE Plasma) directly by editing the
# configuration file and informing KWin over DBus. This is
# useful in a system that uses Wayland but does **not** connect
# locale1 with KWin.
#
# Systems that use KDE Plasma Wayland and locale1 can instead start the
# compositor KWin with command-line argument `--locale1`. That
# argument makes this configuration option unnecessary.
kwin: false

View File

@ -11,4 +11,9 @@ properties:
writeEtcDefaultKeyboard: { type: boolean, default: true }
useLocale1: { type: boolean, default: false }
guessLayout: { type: boolean, default: true }
configure:
additionalProperties: false
type: object
properties:
kwin: { type: boolean, default: false }
required: [ xOrgConfFileName, convertedKeymapPath ]

View File

@ -376,9 +376,11 @@ Config::setLCLocaleExplicitly( const QString& locale )
QString
Config::currentLocationStatus() const
{
return tr( "Set timezone to %1/%2", "@action" )
.arg( m_currentLocation ? m_currentLocation->region() : QString(),
m_currentLocation ? m_currentLocation->zone() : QString() );
if ( m_currentLocation )
{
return tr( "Set timezone to %1.", "@action" ).arg( currentTimezoneName());
}
return QString();
}
QString

View File

@ -78,6 +78,18 @@ unmountTargetMounts( const QString& rootMountPoint )
.arg( m.device, m.mountPoint ) );
}
}
// Last we unmount the root
if ( Calamares::Partition::unmount( rootMountPoint, { "-lv" } ) )
{
return Calamares::JobResult::error(
QCoreApplication::translate( UmountJob::staticMetaObject.className(),
"Could not unmount the root of the target system." ),
QCoreApplication::translate( UmountJob::staticMetaObject.className(),
"The device mounted at '%1' could not be unmounted." )
.arg( rootMountPoint ) );
}
return Calamares::JobResult::ok();
}
@ -139,6 +151,7 @@ UmountJob::exec()
return r;
}
}
// For ZFS systems, export the pools
{
auto r = exportZFSPools();

View File

@ -19,6 +19,7 @@
#include "JobQueue.h"
#include "compat/Variant.h"
#include "utils/Logger.h"
#include "utils/Permissions.h"
#include "utils/String.h"
#include "utils/StringExpander.h"
#include "utils/Variant.h"
@ -946,6 +947,21 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
m_forbiddenLoginNames = Calamares::getStringList( userSettings, "forbidden_names" );
m_forbiddenLoginNames << alwaysForbiddenLoginNames();
tidy( m_forbiddenLoginNames );
const auto permissionKey = QStringLiteral( "home_permissions" );
if ( userSettings.contains( permissionKey ) )
{
const auto value = Calamares::getString( userSettings, permissionKey );
m_homeDirPermissions = Calamares::parseFileMode( value );
if ( m_homeDirPermissions < 0 )
{
cWarning() << "Setting for" << permissionKey << '(' << value << userSettings[permissionKey] << ") is invalid.";
}
}
else
{
m_homeDirPermissions = -1;
}
}
setAutoLoginGroup( either< QString, const QString& >(

View File

@ -259,6 +259,9 @@ public:
const QStringList& forbiddenLoginNames() const;
const QStringList& forbiddenHostNames() const;
int homePermissions() const { return m_homeDirPermissions; }
int homeUMask() const { return m_homeDirPermissions >= 0 ? ( ( ~m_homeDirPermissions ) & 0777 ) : -1; }
public Q_SLOTS:
/** @brief Sets the user's shell if possible
*
@ -368,6 +371,8 @@ private:
QStringList m_forbiddenLoginNames;
PasswordCheckList m_passwordChecks;
int m_homeDirPermissions = -1;
};
#endif

View File

@ -46,7 +46,7 @@ CreateUserJob::prettyStatusMessage() const
}
static Calamares::JobResult
createUser( const QString& loginName, const QString& fullName, const QString& shell )
createUser( const QString& loginName, const QString& fullName, const QString& shell, int umask )
{
QStringList useraddCommand;
#ifdef __FreeBSD__
@ -67,6 +67,10 @@ createUser( const QString& loginName, const QString& fullName, const QString& sh
useraddCommand << "-s" << shell;
}
useraddCommand << "-c" << fullName;
if ( umask >= 0 )
{
useraddCommand << "-K" << ( QStringLiteral( "UMASK=" ) + QString::number( umask, 8 ) );
}
useraddCommand << loginName;
#endif
@ -136,7 +140,8 @@ CreateUserJob::exec()
m_status = tr( "Creating user %1…", "@status" ).arg( m_config->loginName() );
emit progress( 0.5 );
auto useraddResult = createUser( m_config->loginName(), m_config->fullName(), m_config->userShell() );
auto useraddResult
= createUser( m_config->loginName(), m_config->fullName(), m_config->userShell(), m_config->homeUMask() );
if ( !useraddResult )
{
return useraddResult;

View File

@ -56,6 +56,8 @@ private Q_SLOTS:
void testUserYAML_data();
void testUserYAML();
void testUserUmask_data();
void testUserUmask();
};
UserTests::UserTests() {}
@ -511,6 +513,56 @@ UserTests::testUserYAML()
QCOMPARE( c.userShell(), shell );
}
void
UserTests::testUserUmask_data()
{
QTest::addColumn< QString >( "filename" );
QTest::addColumn< int >( "permission" );
QTest::addColumn< int >( "umask" );
QTest::newRow( "good " ) << "tests/8a-issue-2362.conf" << 0700 << 0077;
QTest::newRow( "open " ) << "tests/8b-issue-2362.conf" << 0755 << 0022;
QTest::newRow( "weird" ) << "tests/8c-issue-2362.conf" << 0126 << 0651;
}
void
UserTests::testUserUmask()
{
static constexpr int no_permissions = -1;
const QString old_shell = QStringLiteral( "/bin/ls" );
const QString new_shell = QStringLiteral( "/usr/bin/new" );
const QStringList forbidden { QStringLiteral( "me" ), QStringLiteral( "myself" ), QStringLiteral( "moi" ) };
Config c;
c.setUserShell( old_shell );
QCOMPARE( c.homePermissions(), no_permissions );
QCOMPARE( c.homeUMask(), no_permissions );
QFETCH( QString, filename );
QFETCH( int, permission );
QFETCH( int, umask );
QCOMPARE( permission & umask, 0 );
QCOMPARE( permission | umask, 0777 );
QFileInfo fi( QString( "%1/%2" ).arg( BUILD_AS_TEST, filename ) );
QVERIFY( fi.exists() );
bool ok = false;
const auto map = Calamares::YAML::load( fi, &ok );
QVERIFY( ok );
QVERIFY( map.count() > 0 );
QCOMPARE( c.userShell(), old_shell );
c.setConfigurationMap( map );
QCOMPARE( c.userShell(), new_shell );
QCOMPARE( c.homePermissions(), permission );
QCOMPARE( c.homeUMask(), umask );
QCOMPARE( c.forbiddenLoginNames(), forbidden );
}
QTEST_GUILESS_MAIN( UserTests )
#include "utils/moc-warnings.h"

View File

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
user:
shell: /usr/bin/new
forbidden_names:
- me
- myself
- moi
home_permissions: "700"

View File

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
user:
shell: /usr/bin/new
forbidden_names:
- me
- myself
- moi
home_permissions: "755"

View File

@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
#
---
user:
shell: /usr/bin/new
forbidden_names:
- me
- myself
- moi
home_permissions: "126"

View File

@ -214,9 +214,15 @@ allowWeakPasswordsDefault: false
# contains "root" and "nobody", but may be extended to list other special
# names for a given distro (eg. "video", or "mysql" might not be a valid
# end-user login name).
# - *home_permissions* Home directory of the user is given **approximately**
# this set of permissions. Write the permissions in octal. If not set,
# there is no default and no permission-setting is done (uses defaults of
# `useradd` in the target). A umask is computed from these permissions
# and passed to `useradd`. Take care to quote the numeric value.
user:
shell: /bin/bash
forbidden_names: [ root ]
home_permissions: "0700"
# Hostname settings

View File

@ -13,6 +13,7 @@ properties:
# User shell, should be path to /bin/sh or so
shell: { type: string }
forbidden_names: { type: array, items: { type: string } }
home_permissions: { type: string }
# Group settings
defaultGroups:
type: array

View File

@ -35,7 +35,7 @@ paintRequirement( QPainter* painter, const QStyleOptionViewItem& option, const Q
Calamares::ImageType statusImage = Calamares::StatusOk;
painter->setPen( Qt::black );
painter->setPen( option.palette.text().color() );
if ( index.data( Calamares::RequirementsModel::Satisfied ).toBool() )
{
painter->fillRect( textRect, option.palette.window().color() );