Merge branch 'calamares' of https://github.com/calamares/calamares into development
2
.github/workflows/nightly-opensuse.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: calamares/actions/generic-checkout@v5
|
uses: calamares/actions/generic-checkout@v5
|
||||||
- name: "install dependencies"
|
- name: "install dependencies"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./ci/deps-opensuse.sh
|
run: ./ci/deps-opensuse-qt6.sh
|
||||||
- name: "build"
|
- name: "build"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: ./ci/build.sh
|
run: ./ci/build.sh
|
||||||
|
37
CHANGES-3.3
@ -7,6 +7,43 @@ contributors are listed. Note that Calamares does not have a historical
|
|||||||
changelog -- this log starts with version 3.3.0. See CHANGES-3.2 for
|
changelog -- this log starts with version 3.3.0. See CHANGES-3.2 for
|
||||||
the history of the 3.2 series (2018-05 - 2022-08).
|
the history of the 3.2 series (2018-05 - 2022-08).
|
||||||
|
|
||||||
|
# 3.3.7 (unreleased)
|
||||||
|
|
||||||
|
This release contains contributions from (alphabetically by first name):
|
||||||
|
- Nobody, yet
|
||||||
|
|
||||||
|
## Core ##
|
||||||
|
|
||||||
|
## Modules ##
|
||||||
|
|
||||||
|
|
||||||
|
# 3.3.6 (2024-04-16)
|
||||||
|
|
||||||
|
This release contains contributions from (alphabetically by first name):
|
||||||
|
- Adriaan de Groot
|
||||||
|
- Anke Boersma
|
||||||
|
- Eugene Sam
|
||||||
|
- Evan James
|
||||||
|
- Harald Sitter
|
||||||
|
- Mike Stemle
|
||||||
|
- Peter Jung
|
||||||
|
- Simon Quigley
|
||||||
|
|
||||||
|
## Core ##
|
||||||
|
- Various Qt6-related fixes.
|
||||||
|
- Calamares now prevents sleep and suspend while the installation is
|
||||||
|
running, so that unattended installs do not accidentally fall asleep.
|
||||||
|
|
||||||
|
## Modules ##
|
||||||
|
- *bootloader* Adds "splash" to kernel parameters if plymouth is present.
|
||||||
|
(thanks Eugene)
|
||||||
|
- *locale* Now picks the correct timezone for Dubai, Muscat, Tehran.
|
||||||
|
- *plymouthcfg* Use plymouth-set-default-theme to avoid issues with
|
||||||
|
configuration. (thanks Peter)
|
||||||
|
- *users* module now supports enrolling in Active Directory, if enabled.
|
||||||
|
(thanks Simon)
|
||||||
|
|
||||||
|
|
||||||
# 3.3.5 (2024-03-03)
|
# 3.3.5 (2024-03-03)
|
||||||
|
|
||||||
This release contains contributions from (alphabetically by first name):
|
This release contains contributions from (alphabetically by first name):
|
||||||
|
@ -47,8 +47,8 @@
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
|
||||||
|
|
||||||
set(CALAMARES_VERSION 3.3.5)
|
set(CALAMARES_VERSION 3.3.7)
|
||||||
set(CALAMARES_RELEASE_MODE ON) # Set to ON during a release
|
set(CALAMARES_RELEASE_MODE OFF) # Set to ON during a release
|
||||||
|
|
||||||
if(CMAKE_SCRIPT_MODE_FILE)
|
if(CMAKE_SCRIPT_MODE_FILE)
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake)
|
include(${CMAKE_CURRENT_LIST_DIR}/CMakeModules/ExtendedVersion.cmake)
|
||||||
|
@ -154,10 +154,15 @@ dependencies for the image (in this example, for openSUSE and Qt6).
|
|||||||
- `./ci/deps-opensuse-qt6.sh`
|
- `./ci/deps-opensuse-qt6.sh`
|
||||||
|
|
||||||
Then run CMake (add any CMake options you like at the end) and ninja.
|
Then run CMake (add any CMake options you like at the end) and ninja.
|
||||||
There is a script `ci/build.sh` that does this, too (without options).
|
|
||||||
- `cmake -S /src -B /build -G Ninja`
|
- `cmake -S /src -B /build -G Ninja`
|
||||||
- `ninja -C /build`
|
- `ninja -C /build`
|
||||||
|
|
||||||
|
There is a script `ci/build.sh` that does the CMake an ninja steps.
|
||||||
|
- If you set `CMAKE_ARGS` in the environment those extra CMake options are used.
|
||||||
|
- If you add an argument to the script command which names a workflow
|
||||||
|
(e.g. "nightly-opensuse-qt6") then `CMAKE_ARGS` are extracted from that
|
||||||
|
workflow and used for the build.
|
||||||
|
|
||||||
### Running in Docker
|
### Running in Docker
|
||||||
|
|
||||||
To run Calamares inside the container, or e.g. `loadmodule` to test
|
To run Calamares inside the container, or e.g. `loadmodule` to test
|
||||||
|
35
ci/build.sh
@ -5,6 +5,41 @@
|
|||||||
# - BUILDDIR (e.g. /build)
|
# - BUILDDIR (e.g. /build)
|
||||||
# - CMAKE_ARGS (e.g. "-DWITH_QT6=ON -DCMAKE_BUILD_TYPE=Debug")
|
# - CMAKE_ARGS (e.g. "-DWITH_QT6=ON -DCMAKE_BUILD_TYPE=Debug")
|
||||||
#
|
#
|
||||||
|
# If SRCDIR is not set, it is assumed to be the directory above
|
||||||
|
# wherever this script is being run from (this script is in ci/).
|
||||||
|
#
|
||||||
|
# If BUILDDIR is not set, and /build exists (e.g. in the recommended
|
||||||
|
# Docker setup) then /build is used.
|
||||||
|
#
|
||||||
|
# If CMAKE_ARGS is not set, but the script is given an argument
|
||||||
|
# that exists as a workflow (e.g. "nightly-opensuse-qt6" or
|
||||||
|
# "nightly-debian.yml") and yq is installed, then the CMAKE_ARGS
|
||||||
|
# are extracted from that workflow file.
|
||||||
|
#
|
||||||
|
# Summary, pick one:
|
||||||
|
# - set environment variables, run "build.sh"
|
||||||
|
# - set no variables, run "build.sh <workflow-name>"
|
||||||
|
|
||||||
|
if test -z "$SRCDIR" ; then
|
||||||
|
_d=$(dirname "$0" )
|
||||||
|
_d=$(dirname "$_d" )
|
||||||
|
test -f "$_d/CMakeLists.txt" && SRCDIR="$_d"
|
||||||
|
fi
|
||||||
|
if test -z "$BUILDDIR" ; then
|
||||||
|
test -d "/build" && BUILDDIR=/build
|
||||||
|
fi
|
||||||
|
if test -z "$CMAKE_ARGS" -a -n "$1" ; then
|
||||||
|
_d="$SRCDIR/.github/workflows/$1"
|
||||||
|
test -f "$_d" || _d="$SRCDIR/.github/workflows/$1.yml"
|
||||||
|
test -f "$_d" || { echo "! No workflow $1" ; exit 1 ; }
|
||||||
|
|
||||||
|
if test -x "$(which yq)" ; then
|
||||||
|
CMAKE_ARGS=$(yq ".env.CMAKE_ARGS" "$_d")
|
||||||
|
else
|
||||||
|
CMAKE_ARGS=$(python3 -c 'import yaml ; f=open("'$_d'","r"); print(yaml.safe_load(f)["env"]["CMAKE_ARGS"]);')
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
# Sanity check
|
# Sanity check
|
||||||
test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; }
|
test -n "$BUILDDIR" || { echo "! \$BUILDDIR not set" ; exit 1 ; }
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
yum install -y bison flex git make cmake gcc-c++ ninja-build
|
yum install -y bison flex git make cmake gcc-c++ ninja-build
|
||||||
yum install -y yaml-cpp-devel libpwquality-devel parted-devel python-devel gettext gettext-devel
|
yum install -y yaml-cpp-devel libpwquality-devel parted-devel python-devel gettext gettext-devel python3-pyyaml
|
||||||
yum install -y libicu-devel libatasmart-devel
|
yum install -y libicu-devel libatasmart-devel
|
||||||
yum install -y boost-devel
|
yum install -y boost-devel
|
||||||
# Qt6/KF6 dependencies
|
# Qt6/KF6 dependencies
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
yum install -y bison flex git make cmake gcc-c++ ninja-build
|
yum install -y bison flex git make cmake gcc-c++ ninja-build
|
||||||
yum install -y yaml-cpp-devel libpwquality-devel parted-devel python-devel gettext gettext-devel
|
yum install -y yaml-cpp-devel libpwquality-devel parted-devel python-devel gettext gettext-devel python3-pyyaml
|
||||||
yum install -y libicu-devel libatasmart-devel
|
yum install -y libicu-devel libatasmart-devel
|
||||||
# Qt6/KF6 dependencies
|
# Qt6/KF6 dependencies
|
||||||
yum install -y qt6-qtbase-devel qt6-linguist qt6-qtbase-private-devel qt6-qtdeclarative-devel qt6-qtsvg-devel qt6-qttools-devel
|
yum install -y qt6-qtbase-devel qt6-linguist qt6-qtbase-private-devel qt6-qtdeclarative-devel qt6-qtsvg-devel qt6-qttools-devel
|
||||||
|
@ -8,7 +8,7 @@ zypper --non-interactive addrepo -f -G https://download.opensuse.org/repositorie
|
|||||||
|
|
||||||
zypper --non-interactive refresh
|
zypper --non-interactive refresh
|
||||||
zypper --non-interactive up
|
zypper --non-interactive up
|
||||||
zypper --non-interactive in git-core jq curl ninja
|
zypper --non-interactive in git-core jq yq curl ninja
|
||||||
# From deploycala.py
|
# From deploycala.py
|
||||||
zypper --non-interactive in bison flex git make cmake gcc-c++
|
zypper --non-interactive in bison flex git make cmake gcc-c++
|
||||||
zypper --non-interactive in yaml-cpp-devel libpwquality-devel parted-devel python3-devel
|
zypper --non-interactive in yaml-cpp-devel libpwquality-devel parted-devel python3-devel
|
||||||
@ -18,5 +18,5 @@ zypper --non-interactive in kf6-extra-cmake-modules
|
|||||||
zypper --non-interactive in "qt6-declarative-devel" "cmake(Qt6Concurrent)" "cmake(Qt6Gui)" "cmake(Qt6Network)" "cmake(Qt6Svg)" "cmake(Qt6Linguist)"
|
zypper --non-interactive in "qt6-declarative-devel" "cmake(Qt6Concurrent)" "cmake(Qt6Gui)" "cmake(Qt6Network)" "cmake(Qt6Svg)" "cmake(Qt6Linguist)"
|
||||||
zypper --non-interactive in "cmake(KF6CoreAddons)" "cmake(KF6DBusAddons)" "cmake(KF6Crash)"
|
zypper --non-interactive in "cmake(KF6CoreAddons)" "cmake(KF6DBusAddons)" "cmake(KF6Crash)"
|
||||||
zypper --non-interactive in "cmake(KF6Parts)" # Also installs KF5 things
|
zypper --non-interactive in "cmake(KF6Parts)" # Also installs KF5 things
|
||||||
zypper --non-interactive in "cmake(PolkitQt6-1)"
|
zypper --non-interactive in "cmake(PolkitQt6-1)" appstream-qt6-devel
|
||||||
true
|
true
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -455,11 +456,11 @@ libcalamares.utils.debug('pre-script for testing purposes injected')
|
|||||||
int
|
int
|
||||||
main( int argc, char* argv[] )
|
main( int argc, char* argv[] )
|
||||||
{
|
{
|
||||||
QCoreApplication* aw = createApplication( argc, argv );
|
QCoreApplication* application = createApplication( argc, argv );
|
||||||
|
|
||||||
Logger::setupLogLevel( Logger::LOGVERBOSE );
|
Logger::setupLogLevel( Logger::LOGVERBOSE );
|
||||||
|
|
||||||
ModuleConfig module = handle_args( *aw );
|
ModuleConfig module = handle_args( *application );
|
||||||
if ( module.moduleName().isEmpty() )
|
if ( module.moduleName().isEmpty() )
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -469,7 +470,7 @@ main( int argc, char* argv[] )
|
|||||||
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
|
std::unique_ptr< Calamares::JobQueue > jobqueue_p( new Calamares::JobQueue( nullptr ) );
|
||||||
std::unique_ptr< Calamares::System > system_p( new Calamares::System( settings_p->doChroot() ) );
|
std::unique_ptr< Calamares::System > system_p( new Calamares::System( settings_p->doChroot() ) );
|
||||||
|
|
||||||
QMainWindow* mw = nullptr;
|
QMainWindow* mainWindow = nullptr;
|
||||||
|
|
||||||
auto* gs = jobqueue_p->globalStorage();
|
auto* gs = jobqueue_p->globalStorage();
|
||||||
if ( !module.globalConfigFile().isEmpty() )
|
if ( !module.globalConfigFile().isEmpty() )
|
||||||
@ -513,21 +514,21 @@ main( int argc, char* argv[] )
|
|||||||
// tries to create the widget **which won't be used anyway**.
|
// tries to create the widget **which won't be used anyway**.
|
||||||
//
|
//
|
||||||
// To avoid that crash, re-create the QApplication, now with GUI
|
// To avoid that crash, re-create the QApplication, now with GUI
|
||||||
if ( !qobject_cast< QApplication* >( aw ) )
|
if ( !qobject_cast< QApplication* >( application ) )
|
||||||
{
|
{
|
||||||
auto* replace_app = new QApplication( argc, argv );
|
auto* replace_app = new QApplication( argc, argv );
|
||||||
replace_app->setQuitOnLastWindowClosed( true );
|
replace_app->setQuitOnLastWindowClosed( true );
|
||||||
aw = replace_app;
|
application = replace_app;
|
||||||
}
|
}
|
||||||
mw = module.m_ui ? new QMainWindow() : nullptr;
|
mainWindow = module.m_ui ? new QMainWindow() : nullptr;
|
||||||
if ( mw )
|
if ( mainWindow )
|
||||||
{
|
{
|
||||||
mw->installEventFilter( Calamares::Retranslator::instance() );
|
mainWindow->installEventFilter( Calamares::Retranslator::instance() );
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)new Calamares::Branding( module.m_branding );
|
(void)new Calamares::Branding( module.m_branding );
|
||||||
auto* modulemanager = new Calamares::ModuleManager( QStringList(), nullptr );
|
auto* modulemanager = new Calamares::ModuleManager( QStringList(), nullptr );
|
||||||
(void)Calamares::ViewManager::instance( mw );
|
(void)Calamares::ViewManager::instance( mainWindow );
|
||||||
modulemanager->addModule( m );
|
modulemanager->addModule( m );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,16 +543,16 @@ main( int argc, char* argv[] )
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( mw )
|
if ( mainWindow )
|
||||||
{
|
{
|
||||||
auto* vm = Calamares::ViewManager::instance();
|
auto* vm = Calamares::ViewManager::instance();
|
||||||
vm->onInitComplete();
|
vm->onInitComplete();
|
||||||
QWidget* w = vm->currentStep()->widget();
|
QWidget* w = vm->currentStep()->widget();
|
||||||
w->setParent( mw );
|
w->setParent( mainWindow );
|
||||||
mw->setCentralWidget( w );
|
mainWindow->setCentralWidget( w );
|
||||||
w->show();
|
w->show();
|
||||||
mw->show();
|
mainWindow->show();
|
||||||
return aw->exec();
|
return application->exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
using TR = Logger::DebugRow< const char*, const QString >;
|
using TR = Logger::DebugRow< const char*, const QString >;
|
||||||
@ -559,30 +560,10 @@ main( int argc, char* argv[] )
|
|||||||
cDebug() << Logger::SubEntry << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() )
|
cDebug() << Logger::SubEntry << "Module metadata" << TR( "name", m->name() ) << TR( "type", m->typeString() )
|
||||||
<< TR( "interface", m->interfaceString() );
|
<< TR( "interface", m->interfaceString() );
|
||||||
|
|
||||||
Calamares::JobList jobList = m->jobs();
|
Calamares::JobQueue::instance()->enqueue(100, m->jobs());
|
||||||
unsigned int failure_count = 0;
|
|
||||||
unsigned int count = 1;
|
|
||||||
for ( const auto& p : jobList )
|
|
||||||
{
|
|
||||||
// This doesn't get a SubEntry because the jobs may log a bunch of
|
|
||||||
// things; print the function-header to make clear that we're back in main.
|
|
||||||
cDebug() << "Job #" << count << "name" << p->prettyName();
|
|
||||||
Calamares::JobResult r = p->exec();
|
|
||||||
if ( !r )
|
|
||||||
{
|
|
||||||
cError() << "Job #" << count << "failed" << TR( "summary", r.message() ) << TR( "details", r.details() );
|
|
||||||
if ( r.errorCode() > 0 )
|
|
||||||
{
|
|
||||||
++failure_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( aw )
|
QObject::connect(Calamares::JobQueue::instance(), &Calamares::JobQueue::finished, [application]() { QTimer::singleShot(std::chrono::seconds(3), application, &QApplication::quit); });
|
||||||
{
|
QTimer::singleShot(0, []() { Calamares::JobQueue::instance()->start(); });
|
||||||
delete aw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return failure_count ? 1 : 0;
|
return application->exec();
|
||||||
}
|
}
|
||||||
|
@ -109,12 +109,23 @@ public:
|
|||||||
* which of the jobs is "heavy" and which is not.
|
* which of the jobs is "heavy" and which is not.
|
||||||
*/
|
*/
|
||||||
virtual int getJobWeight() const;
|
virtual int getJobWeight() const;
|
||||||
|
|
||||||
/** @brief The human-readable name of this job
|
/** @brief The human-readable name of this job
|
||||||
*
|
*
|
||||||
* This should be a very short statement of what the job does.
|
* This should be a very short statement of what the job does.
|
||||||
* For status and state information, see prettyStatusMessage().
|
* For status and state information, see prettyStatusMessage().
|
||||||
|
*
|
||||||
|
* The job's name may be similar to the status message, but this is
|
||||||
|
* a name, and should not be an active verb phrase. The translation
|
||||||
|
* should use context @c \@label .
|
||||||
|
*
|
||||||
|
* The name of the job is used as a **fallback** when the status
|
||||||
|
* or descriptions are empty. If a job has no implementation of
|
||||||
|
* those methods, it is OK to use other contexts, but it may look
|
||||||
|
* strange in some places in the UI.
|
||||||
*/
|
*/
|
||||||
virtual QString prettyName() const = 0;
|
virtual QString prettyName() const = 0;
|
||||||
|
|
||||||
/** @brief a longer human-readable description of what the job will do
|
/** @brief a longer human-readable description of what the job will do
|
||||||
*
|
*
|
||||||
* This **may** be used by view steps to fill in the summary
|
* This **may** be used by view steps to fill in the summary
|
||||||
@ -122,15 +133,23 @@ public:
|
|||||||
* module does so.
|
* module does so.
|
||||||
*
|
*
|
||||||
* The default implementation returns an empty string.
|
* The default implementation returns an empty string.
|
||||||
|
*
|
||||||
|
* The translation should use context @c \@title .
|
||||||
*/
|
*/
|
||||||
virtual QString prettyDescription() const;
|
virtual QString prettyDescription() const;
|
||||||
|
|
||||||
/** @brief A human-readable status for progress reporting
|
/** @brief A human-readable status for progress reporting
|
||||||
*
|
*
|
||||||
* This is called from the JobQueue when progress is made, and should
|
* This is called from the JobQueue when progress is made, and should
|
||||||
* return a not-too-long description of the job's status. This
|
* return a not-too-long description of the job's status. This
|
||||||
* is made visible in the progress bar of the execution view step.
|
* is made visible in the progress bar of the execution view step.
|
||||||
|
*
|
||||||
|
* The job's status should say **what** the job is doing. It should be in
|
||||||
|
* present active tense. Typically the translation uses tr() context
|
||||||
|
* @c \@status . See prettyName() for examples.
|
||||||
*/
|
*/
|
||||||
virtual QString prettyStatusMessage() const;
|
virtual QString prettyStatusMessage() const;
|
||||||
|
|
||||||
virtual JobResult exec() = 0;
|
virtual JobResult exec() = 0;
|
||||||
|
|
||||||
bool isEmergency() const { return m_emergency; }
|
bool isEmergency() const { return m_emergency; }
|
||||||
|
@ -16,13 +16,174 @@
|
|||||||
#include "compat/Mutex.h"
|
#include "compat/Mutex.h"
|
||||||
#include "utils/Logger.h"
|
#include "utils/Logger.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusMessage>
|
||||||
|
#include <QDBusPendingCall>
|
||||||
|
#include <QDBusPendingReply>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// This power-management code is largely cribbed from KDE Discover,
|
||||||
|
// https://invent.kde.org/plasma/discover/-/blob/master/discover/PowerManagementInterface.cpp
|
||||||
|
//
|
||||||
|
// Upstream license text says:
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2019 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Class to manage sleep / suspend on inactivity
|
||||||
|
*
|
||||||
|
* Create an object of this class on the heap. Call inhibitSleep()
|
||||||
|
* to (try to) stop system sleep / suspend. Call uninhibitSleep()
|
||||||
|
* when the object is no longer needed. The object self-deletes
|
||||||
|
* after uninhibitSleep() completes.
|
||||||
|
*/
|
||||||
|
class PowerManagementInterface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
PowerManagementInterface( QObject* parent = nullptr );
|
||||||
|
~PowerManagementInterface() override;
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void inhibitSleep();
|
||||||
|
void uninhibitSleep();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void hostSleepInhibitChanged();
|
||||||
|
void inhibitDBusCallFinished( QDBusPendingCallWatcher* aWatcher );
|
||||||
|
void uninhibitDBusCallFinished( QDBusPendingCallWatcher* aWatcher );
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint m_inhibitSleepCookie = 0;
|
||||||
|
bool m_inhibitedSleep = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
PowerManagementInterface::PowerManagementInterface( QObject* parent )
|
||||||
|
: QObject( parent )
|
||||||
|
{
|
||||||
|
auto sessionBus = QDBusConnection::sessionBus();
|
||||||
|
|
||||||
|
sessionBus.connect( QStringLiteral( "org.freedesktop.PowerManagement.Inhibit" ),
|
||||||
|
QStringLiteral( "/org/freedesktop/PowerManagement/Inhibit" ),
|
||||||
|
QStringLiteral( "org.freedesktop.PowerManagement.Inhibit" ),
|
||||||
|
QStringLiteral( "HasInhibitChanged" ),
|
||||||
|
this,
|
||||||
|
SLOT( hostSleepInhibitChanged() ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerManagementInterface::~PowerManagementInterface() = default;
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerManagementInterface::hostSleepInhibitChanged()
|
||||||
|
{
|
||||||
|
// We don't actually care
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerManagementInterface::inhibitDBusCallFinished( QDBusPendingCallWatcher* aWatcher )
|
||||||
|
{
|
||||||
|
QDBusPendingReply< uint > reply = *aWatcher;
|
||||||
|
if ( reply.isError() )
|
||||||
|
{
|
||||||
|
cError() << "Could not inhibit sleep:" << reply.error();
|
||||||
|
// m_inhibitedSleep = false; // unchanged
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_inhibitSleepCookie = reply.argumentAt< 0 >();
|
||||||
|
m_inhibitedSleep = true;
|
||||||
|
cDebug() << "Sleep inhibited, cookie" << m_inhibitSleepCookie;
|
||||||
|
}
|
||||||
|
aWatcher->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerManagementInterface::uninhibitDBusCallFinished( QDBusPendingCallWatcher* aWatcher )
|
||||||
|
{
|
||||||
|
QDBusPendingReply<> reply = *aWatcher;
|
||||||
|
if ( reply.isError() )
|
||||||
|
{
|
||||||
|
cError() << "Could not uninhibit sleep:" << reply.error();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_inhibitedSleep = false;
|
||||||
|
m_inhibitSleepCookie = 0;
|
||||||
|
cDebug() << "Sleep uninhibited.";
|
||||||
|
}
|
||||||
|
aWatcher->deleteLater();
|
||||||
|
this->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerManagementInterface::inhibitSleep()
|
||||||
|
{
|
||||||
|
if ( m_inhibitedSleep )
|
||||||
|
{
|
||||||
|
cDebug() << "Sleep is already inhibited.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sessionBus = QDBusConnection::sessionBus();
|
||||||
|
auto inhibitCall = QDBusMessage::createMethodCall( QStringLiteral( "org.freedesktop.PowerManagement.Inhibit" ),
|
||||||
|
QStringLiteral( "/org/freedesktop/PowerManagement/Inhibit" ),
|
||||||
|
QStringLiteral( "org.freedesktop.PowerManagement.Inhibit" ),
|
||||||
|
QStringLiteral( "Inhibit" ) );
|
||||||
|
inhibitCall.setArguments(
|
||||||
|
{ { tr( "Calamares" ) }, { tr( "Installation in progress", "@status" ) } } );
|
||||||
|
|
||||||
|
auto asyncReply = sessionBus.asyncCall( inhibitCall );
|
||||||
|
auto* replyWatcher = new QDBusPendingCallWatcher( asyncReply, this );
|
||||||
|
QObject::connect(
|
||||||
|
replyWatcher, &QDBusPendingCallWatcher::finished, this, &PowerManagementInterface::inhibitDBusCallFinished );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PowerManagementInterface::uninhibitSleep()
|
||||||
|
{
|
||||||
|
if ( !m_inhibitedSleep )
|
||||||
|
{
|
||||||
|
cDebug() << "Sleep was never inhibited.";
|
||||||
|
this->deleteLater();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sessionBus = QDBusConnection::sessionBus();
|
||||||
|
auto uninhibitCall = QDBusMessage::createMethodCall( QStringLiteral( "org.freedesktop.PowerManagement.Inhibit" ),
|
||||||
|
QStringLiteral( "/org/freedesktop/PowerManagement/Inhibit" ),
|
||||||
|
QStringLiteral( "org.freedesktop.PowerManagement.Inhibit" ),
|
||||||
|
QStringLiteral( "UnInhibit" ) );
|
||||||
|
uninhibitCall.setArguments( { { m_inhibitSleepCookie } } );
|
||||||
|
|
||||||
|
auto asyncReply = sessionBus.asyncCall( uninhibitCall );
|
||||||
|
auto replyWatcher = new QDBusPendingCallWatcher( asyncReply, this );
|
||||||
|
QObject::connect(
|
||||||
|
replyWatcher, &QDBusPendingCallWatcher::finished, this, &PowerManagementInterface::uninhibitDBusCallFinished );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace Calamares
|
namespace Calamares
|
||||||
{
|
{
|
||||||
|
SleepInhibitor::SleepInhibitor()
|
||||||
|
{
|
||||||
|
// Create a PowerManagementInterface object with intentionally no parent
|
||||||
|
// so it is not destroyed along with this. Instead, when this
|
||||||
|
// is destroyed, **start** the uninhibit-sleep call which will (later)
|
||||||
|
// destroy the PowerManagementInterface object.
|
||||||
|
auto* p = new PowerManagementInterface( nullptr );
|
||||||
|
p->inhibitSleep();
|
||||||
|
connect( this, &QObject::destroyed, p, &PowerManagementInterface::uninhibitSleep );
|
||||||
|
}
|
||||||
|
|
||||||
|
SleepInhibitor::~SleepInhibitor() = default;
|
||||||
|
|
||||||
struct WeightedJob
|
struct WeightedJob
|
||||||
{
|
{
|
||||||
@ -188,6 +349,13 @@ private:
|
|||||||
// starts the job, or if the job itself reports 0.0) be more
|
// starts the job, or if the job itself reports 0.0) be more
|
||||||
// accepting in what gets reported: jobs with no status fall
|
// accepting in what gets reported: jobs with no status fall
|
||||||
// back to description and name, whichever is non-empty.
|
// back to description and name, whichever is non-empty.
|
||||||
|
//
|
||||||
|
// Later calls (e.g. when percentage > 0) use the status unchanged.
|
||||||
|
// It may be empty, but the ExecutionViewStep knows about empty
|
||||||
|
// status messages and does not update the text in that case.
|
||||||
|
//
|
||||||
|
// This means that a Job can implement just prettyName() and get
|
||||||
|
// a reasonable "status" message which will update only once.
|
||||||
if ( percentage == 0.0 && message.isEmpty() )
|
if ( percentage == 0.0 && message.isEmpty() )
|
||||||
{
|
{
|
||||||
message = jobitem.job->prettyDescription();
|
message = jobitem.job->prettyDescription();
|
||||||
@ -267,6 +435,10 @@ JobQueue::start()
|
|||||||
m_thread->finalize();
|
m_thread->finalize();
|
||||||
m_finished = false;
|
m_finished = false;
|
||||||
m_thread->start();
|
m_thread->start();
|
||||||
|
|
||||||
|
auto* inhibitor = new PowerManagementInterface( this );
|
||||||
|
inhibitor->inhibitSleep();
|
||||||
|
connect( this, &JobQueue::finished, inhibitor, &PowerManagementInterface::uninhibitSleep );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,15 @@ namespace Calamares
|
|||||||
class GlobalStorage;
|
class GlobalStorage;
|
||||||
class JobThread;
|
class JobThread;
|
||||||
|
|
||||||
|
///@brief RAII class to suppress sleep / suspend during its lifetime
|
||||||
|
class DLLEXPORT SleepInhibitor : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SleepInhibitor();
|
||||||
|
~SleepInhibitor() override;
|
||||||
|
};
|
||||||
|
|
||||||
class DLLEXPORT JobQueue : public QObject
|
class DLLEXPORT JobQueue : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -25,4 +25,8 @@
|
|||||||
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
||||||
#pragma clang diagnostic ignored "-Wredundant-parens"
|
#pragma clang diagnostic ignored "-Wredundant-parens"
|
||||||
#pragma clang diagnostic ignored "-Wreserved-identifier"
|
#pragma clang diagnostic ignored "-Wreserved-identifier"
|
||||||
|
|
||||||
|
#if __clang_major__ >= 17
|
||||||
|
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -125,23 +125,28 @@ def is_zfs_root(partition):
|
|||||||
return partition["mountPoint"] == "/" and partition["fs"] == "zfs"
|
return partition["mountPoint"] == "/" and partition["fs"] == "zfs"
|
||||||
|
|
||||||
|
|
||||||
|
def have_program_in_target(program : str):
|
||||||
|
"""Returns @c True if @p program is in path in the target"""
|
||||||
|
return libcalamares.utils.target_env_call(["/usr/bin/which", program]) == 0
|
||||||
|
|
||||||
|
|
||||||
def get_kernel_params(uuid):
|
def get_kernel_params(uuid):
|
||||||
|
# Configured kernel parameters (default "quiet"), if plymouth installed, add splash
|
||||||
|
# screen parameter and then "rw".
|
||||||
kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"])
|
kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"])
|
||||||
|
if have_program_in_target("plymouth"):
|
||||||
|
kernel_params.append("splash")
|
||||||
kernel_params.append("rw")
|
kernel_params.append("rw")
|
||||||
|
|
||||||
|
use_systemd_naming = have_program_in_target("dracut") or (libcalamares.utils.target_env_call(["/usr/bin/grep", "-q", "^HOOKS.*systemd", "/etc/mkinitcpio.conf"]) == 0)
|
||||||
|
|
||||||
partitions = libcalamares.globalstorage.value("partitions")
|
partitions = libcalamares.globalstorage.value("partitions")
|
||||||
|
|
||||||
|
cryptdevice_params = []
|
||||||
swap_uuid = ""
|
swap_uuid = ""
|
||||||
swap_outer_mappername = None
|
swap_outer_mappername = None
|
||||||
swap_outer_uuid = None
|
swap_outer_uuid = None
|
||||||
|
|
||||||
cryptdevice_params = []
|
|
||||||
|
|
||||||
has_dracut = libcalamares.utils.target_env_call(["sh", "-c", "which dracut"]) == 0
|
|
||||||
uses_systemd_hook = libcalamares.utils.target_env_call(["sh", "-c",
|
|
||||||
"grep -q \"^HOOKS.*systemd\" /etc/mkinitcpio.conf"]) == 0
|
|
||||||
use_systemd_naming = has_dracut or uses_systemd_hook
|
|
||||||
|
|
||||||
|
|
||||||
# Take over swap settings:
|
# Take over swap settings:
|
||||||
# - unencrypted swap partition sets swap_uuid
|
# - unencrypted swap partition sets swap_uuid
|
||||||
# - encrypted root sets cryptdevice_params
|
# - encrypted root sets cryptdevice_params
|
||||||
|
@ -100,7 +100,7 @@ hostCPU_FreeBSD()
|
|||||||
|
|
||||||
#if defined( Q_OS_LINUX )
|
#if defined( Q_OS_LINUX )
|
||||||
static QString
|
static QString
|
||||||
hostCPUmatchARM( const QString& s )
|
hostCPUmatchARM( const QString& )
|
||||||
{
|
{
|
||||||
/* The "CPU implementer" line is for ARM CPUs in general.
|
/* The "CPU implementer" line is for ARM CPUs in general.
|
||||||
*
|
*
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,6 +1,6 @@
|
|||||||
/* === This file is part of Calamares - <https://calamares.io> ===
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
*
|
*
|
||||||
* SPDX-FileCopyrightText: 2020 - 2022 Anke Boersma <demm@kaosx.us>
|
* SPDX-FileCopyrightText: 2020 - 2024 Anke Boersma <demm@kaosx.us>
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*
|
*
|
||||||
* Calamares is Free Software: see the License-Identifier above.
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
@ -112,7 +112,7 @@ Column {
|
|||||||
|
|
||||||
Plugin {
|
Plugin {
|
||||||
id: mapPlugin
|
id: mapPlugin
|
||||||
preferred: ["osm", "esri"] // "esri", "here", "itemsoverlay", "mapbox", "mapboxgl", "osm"
|
name: ["osm"]
|
||||||
}
|
}
|
||||||
|
|
||||||
Map {
|
Map {
|
||||||
@ -177,6 +177,30 @@ Column {
|
|||||||
getTzOffline();
|
getTzOffline();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WheelHandler {
|
||||||
|
id: wheel
|
||||||
|
acceptedDevices: Qt.platform.pluginName === "cocoa" || Qt.platform.pluginName === "wayland"
|
||||||
|
? PointerDevice.Mouse | PointerDevice.TouchPad
|
||||||
|
: PointerDevice.Mouse
|
||||||
|
rotationScale: 1/120
|
||||||
|
property: "zoomLevel"
|
||||||
|
}
|
||||||
|
DragHandler {
|
||||||
|
id: drag
|
||||||
|
target: null
|
||||||
|
onTranslationChanged: (delta) => map.pan(-delta.x, -delta.y)
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
enabled: map.zoomLevel < map.maximumZoomLevel
|
||||||
|
sequence: StandardKey.ZoomIn
|
||||||
|
onActivated: map.zoomLevel = Math.round(map.zoomLevel + 1)
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
enabled: map.zoomLevel > map.minimumZoomLevel
|
||||||
|
sequence: StandardKey.ZoomOut
|
||||||
|
onActivated: map.zoomLevel = Math.round(map.zoomLevel - 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
|
@ -223,7 +223,7 @@ PartitionViewStep::prettyStatus() const
|
|||||||
const QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo();
|
const QList< PartitionCoreModule::SummaryInfo > list = m_core->createSummaryInfo();
|
||||||
|
|
||||||
cDebug() << "Summary for Partition" << list.length() << choice;
|
cDebug() << "Summary for Partition" << list.length() << choice;
|
||||||
auto joinDiskInfo = [ choice = choice ]( QString& s, const PartitionCoreModule::SummaryInfo& i )
|
auto joinDiskInfo = [ choice ]( QString& s, const PartitionCoreModule::SummaryInfo& i )
|
||||||
{ return s + diskDescription( 1, i, choice ); };
|
{ return s + diskDescription( 1, i, choice ); };
|
||||||
const QString diskInfoLabel = std::accumulate( list.begin(), list.end(), QString(), joinDiskInfo );
|
const QString diskInfoLabel = std::accumulate( list.begin(), list.end(), QString(), joinDiskInfo );
|
||||||
const QString jobsLabel = jobDescriptions( jobs() ).join( QStringLiteral( "<br/>" ) );
|
const QString jobsLabel = jobDescriptions( jobs() ).join( QStringLiteral( "<br/>" ) );
|
||||||
@ -497,11 +497,12 @@ shouldWarnForNotEncryptedBoot( const Config* config, const PartitionCoreModule*
|
|||||||
Partition* root_p = core->findPartitionByMountPoint( "/" );
|
Partition* root_p = core->findPartitionByMountPoint( "/" );
|
||||||
Partition* boot_p = core->findPartitionByMountPoint( "/boot" );
|
Partition* boot_p = core->findPartitionByMountPoint( "/boot" );
|
||||||
|
|
||||||
if ( root_p and boot_p )
|
if ( root_p && boot_p )
|
||||||
{
|
{
|
||||||
if ( ( root_p->fileSystem().type() == FileSystem::Luks && boot_p->fileSystem().type() != FileSystem::Luks )
|
const auto encryptionMismatch
|
||||||
|| ( root_p->fileSystem().type() == FileSystem::Luks2
|
= [ root_t = root_p->fileSystem().type(), boot_t = boot_p->fileSystem().type() ]( FileSystem::Type t )
|
||||||
&& boot_p->fileSystem().type() != FileSystem::Luks2 ) )
|
{ return root_t == t && boot_t != t; };
|
||||||
|
if ( encryptionMismatch( FileSystem::Luks ) || encryptionMismatch( FileSystem::Luks2 ) )
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -581,12 +581,7 @@ efiFilesystemMinimumSize()
|
|||||||
uefisys_part_sizeB = v > 0 ? v : 0;
|
uefisys_part_sizeB = v > 0 ? v : 0;
|
||||||
}
|
}
|
||||||
// There is a lower limit of what can be configured
|
// There is a lower limit of what can be configured
|
||||||
if ( uefisys_part_sizeB < efiSpecificationHardMinimumSize )
|
return std::max( uefisys_part_sizeB, efiSpecificationHardMinimumSize );
|
||||||
{
|
|
||||||
uefisys_part_sizeB = efiSpecificationHardMinimumSize;
|
|
||||||
}
|
|
||||||
return uefisys_part_sizeB;
|
|
||||||
return efiSpecificationHardMinimumSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
|
@ -248,9 +248,9 @@ defaultFileSystemType: "ext4"
|
|||||||
# for root that uses 100% of the space and uses the filesystem defined by
|
# for root that uses 100% of the space and uses the filesystem defined by
|
||||||
# defaultFileSystemType.
|
# defaultFileSystemType.
|
||||||
#
|
#
|
||||||
# Note: the EFI system partition is prepend automatically to the layout if
|
# Note: the EFI system partition is prepended automatically to the layout if
|
||||||
# needed; the swap partition is appended to the layout if enabled (small of
|
# needed; the swap partition is appended to the layout if enabled (selections
|
||||||
# suspend).
|
# "small" or "suspend" in *userSwapChoices*).
|
||||||
#
|
#
|
||||||
# Otherwise, the partition layout is defined as follow:
|
# Otherwise, the partition layout is defined as follow:
|
||||||
#
|
#
|
||||||
|
@ -48,9 +48,7 @@ class PlymouthController:
|
|||||||
|
|
||||||
def setTheme(self):
|
def setTheme(self):
|
||||||
plymouth_theme = libcalamares.job.configuration["plymouth_theme"]
|
plymouth_theme = libcalamares.job.configuration["plymouth_theme"]
|
||||||
target_env_call(["sed", "-e", 's|^.*Theme=.*|Theme=' +
|
target_env_call(["plymouth-set-default-theme", plymouth_theme])
|
||||||
plymouth_theme + '|', "-i",
|
|
||||||
"/etc/plymouth/plymouthd.conf"])
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if detect_plymouth():
|
if detect_plymouth():
|
||||||
|
85
src/modules/users/ActiveDirectoryJob.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Simon Quigley <tsimonq2@ubuntu.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ActiveDirectoryJob.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include "GlobalStorage.h"
|
||||||
|
#include "JobQueue.h"
|
||||||
|
#include "utils/Logger.h"
|
||||||
|
#include "utils/Permissions.h"
|
||||||
|
#include "utils/System.h"
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
|
ActiveDirectoryJob::ActiveDirectoryJob( const QString& adminLogin,
|
||||||
|
const QString& adminPassword,
|
||||||
|
const QString& domain,
|
||||||
|
const QString& ip )
|
||||||
|
: Calamares::Job()
|
||||||
|
, m_adminLogin( adminLogin )
|
||||||
|
, m_adminPassword( adminPassword )
|
||||||
|
, m_domain( domain )
|
||||||
|
, m_ip( ip )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
ActiveDirectoryJob::prettyName() const
|
||||||
|
{
|
||||||
|
return tr( "Enroll system in Active Directory", "@label" );
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
ActiveDirectoryJob::prettyStatusMessage() const
|
||||||
|
{
|
||||||
|
return tr( "Enrolling system in Active Directory…", "@status" );
|
||||||
|
}
|
||||||
|
|
||||||
|
Calamares::JobResult
|
||||||
|
ActiveDirectoryJob::exec()
|
||||||
|
{
|
||||||
|
if ( !m_ip.isEmpty() )
|
||||||
|
{
|
||||||
|
const QString hostsFilePath = Calamares::System::instance()->targetPath( QStringLiteral( "/etc/hosts" ) );
|
||||||
|
;
|
||||||
|
QFile hostsFile( hostsFilePath );
|
||||||
|
if ( hostsFile.open( QIODevice::Append | QIODevice::Text ) )
|
||||||
|
{
|
||||||
|
QTextStream out( &hostsFile );
|
||||||
|
out << m_ip << " " << m_domain << "\n";
|
||||||
|
hostsFile.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Calamares::JobResult::error( "Failed to open /etc/hosts for writing." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString installPath = Calamares::System::instance()->targetPath( QStringLiteral( "/" ) );
|
||||||
|
auto r = Calamares::System::instance()->runCommand(
|
||||||
|
Calamares::System::RunLocation::RunInHost,
|
||||||
|
{ "realm", "join", m_domain, "-U", m_adminLogin, "--install=" + installPath, "--verbose" },
|
||||||
|
QString(),
|
||||||
|
m_adminPassword,
|
||||||
|
std::chrono::seconds( 30 ) );
|
||||||
|
|
||||||
|
|
||||||
|
if ( r.getExitCode() == 0 )
|
||||||
|
{
|
||||||
|
return Calamares::JobResult::ok();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Calamares::JobResult::error( QString( "Failed to join realm: %1" ).arg( r.getOutput() ) );
|
||||||
|
}
|
||||||
|
}
|
34
src/modules/users/ActiveDirectoryJob.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* === This file is part of Calamares - <https://calamares.io> ===
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Simon Quigley <tsimonq2@ubuntu.com>
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* Calamares is Free Software: see the License-Identifier above.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ACTIVEDIRECTORYJOB_H
|
||||||
|
#define ACTIVEDIRECTORYJOB_H
|
||||||
|
|
||||||
|
#include "Job.h"
|
||||||
|
|
||||||
|
class ActiveDirectoryJob : public Calamares::Job
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ActiveDirectoryJob( const QString& adminLogin,
|
||||||
|
const QString& adminPassword,
|
||||||
|
const QString& domain,
|
||||||
|
const QString& ip );
|
||||||
|
QString prettyName() const override;
|
||||||
|
QString prettyStatusMessage() const override;
|
||||||
|
Calamares::JobResult exec() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_adminLogin; // Admin credentials to do the enrollment
|
||||||
|
QString m_adminPassword;
|
||||||
|
QString m_domain;
|
||||||
|
QString m_ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ACTIVEDIRECTORYJOB_H */
|
@ -55,6 +55,7 @@ include_directories(${PROJECT_BINARY_DIR}/src/libcalamaresui)
|
|||||||
|
|
||||||
set(_users_src
|
set(_users_src
|
||||||
# Jobs
|
# Jobs
|
||||||
|
ActiveDirectoryJob.cpp
|
||||||
CreateUserJob.cpp
|
CreateUserJob.cpp
|
||||||
MiscJobs.cpp
|
MiscJobs.cpp
|
||||||
SetPasswordJob.cpp
|
SetPasswordJob.cpp
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include "ActiveDirectoryJob.h"
|
||||||
#include "CreateUserJob.h"
|
#include "CreateUserJob.h"
|
||||||
#include "MiscJobs.h"
|
#include "MiscJobs.h"
|
||||||
#include "SetHostNameJob.h"
|
#include "SetHostNameJob.h"
|
||||||
@ -444,8 +445,6 @@ makeHostnameSuggestion( const QString& templateString, const QStringList& fullNa
|
|||||||
|
|
||||||
QString hostnameSuggestion = d.expand( templateString );
|
QString hostnameSuggestion = d.expand( templateString );
|
||||||
|
|
||||||
// RegExp for valid hostnames; if the suggestion produces a valid name, return it
|
|
||||||
static const QRegularExpression HOSTNAME_RX( "^[a-zA-Z0-9][-a-zA-Z0-9_]*$" );
|
|
||||||
return hostnameSuggestion.indexOf( HOSTNAME_RX ) != -1 ? hostnameSuggestion : QString();
|
return hostnameSuggestion.indexOf( HOSTNAME_RX ) != -1 ? hostnameSuggestion : QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -656,6 +655,48 @@ Config::setRootPasswordSecondary( const QString& s )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setActiveDirectoryUsed( bool used )
|
||||||
|
{
|
||||||
|
m_activeDirectoryUsed = used;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Config::getActiveDirectoryEnabled() const
|
||||||
|
{
|
||||||
|
return m_activeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Config::getActiveDirectoryUsed() const
|
||||||
|
{
|
||||||
|
return m_activeDirectoryUsed && m_activeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setActiveDirectoryAdminUsername( const QString& s )
|
||||||
|
{
|
||||||
|
m_activeDirectoryAdminUsername = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setActiveDirectoryAdminPassword( const QString& s )
|
||||||
|
{
|
||||||
|
m_activeDirectoryAdminPassword = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setActiveDirectoryDomain( const QString& s )
|
||||||
|
{
|
||||||
|
m_activeDirectoryDomain = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::setActiveDirectoryIP( const QString& s )
|
||||||
|
{
|
||||||
|
m_activeDirectoryIP = s;
|
||||||
|
}
|
||||||
|
|
||||||
QString
|
QString
|
||||||
Config::rootPassword() const
|
Config::rootPassword() const
|
||||||
{
|
{
|
||||||
@ -913,6 +954,9 @@ Config::setConfigurationMap( const QVariantMap& configurationMap )
|
|||||||
m_sudoStyle = Calamares::getBool( configurationMap, "sudoersConfigureWithGroup", false ) ? SudoStyle::UserAndGroup
|
m_sudoStyle = Calamares::getBool( configurationMap, "sudoersConfigureWithGroup", false ) ? SudoStyle::UserAndGroup
|
||||||
: SudoStyle::UserOnly;
|
: SudoStyle::UserOnly;
|
||||||
|
|
||||||
|
// Handle Active Directory enablement
|
||||||
|
m_activeDirectory = Calamares::getBool( configurationMap, "allowActiveDirectory", false );
|
||||||
|
|
||||||
// Handle *hostname* key and subkeys and legacy settings
|
// Handle *hostname* key and subkeys and legacy settings
|
||||||
{
|
{
|
||||||
bool ok = false; // Ignored
|
bool ok = false; // Ignored
|
||||||
@ -990,6 +1034,15 @@ Config::createJobs() const
|
|||||||
jobs.append( Calamares::job_ptr( j ) );
|
jobs.append( Calamares::job_ptr( j ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( getActiveDirectoryUsed() )
|
||||||
|
{
|
||||||
|
j = new ActiveDirectoryJob( m_activeDirectoryAdminUsername,
|
||||||
|
m_activeDirectoryAdminPassword,
|
||||||
|
m_activeDirectoryDomain,
|
||||||
|
m_activeDirectoryIP );
|
||||||
|
jobs.append( Calamares::job_ptr( j ) );
|
||||||
|
}
|
||||||
|
|
||||||
j = new SetupGroupsJob( this );
|
j = new SetupGroupsJob( this );
|
||||||
jobs.append( Calamares::job_ptr( j ) );
|
jobs.append( Calamares::job_ptr( j ) );
|
||||||
|
|
||||||
|
@ -226,6 +226,10 @@ public:
|
|||||||
bool permitWeakPasswords() const { return m_permitWeakPasswords; }
|
bool permitWeakPasswords() const { return m_permitWeakPasswords; }
|
||||||
/// Current setting for "require strong password"?
|
/// Current setting for "require strong password"?
|
||||||
bool requireStrongPasswords() const { return m_requireStrongPasswords; }
|
bool requireStrongPasswords() const { return m_requireStrongPasswords; }
|
||||||
|
/// Is Active Directory enabled in the config file?
|
||||||
|
bool getActiveDirectoryEnabled() const;
|
||||||
|
/// Is it both enabled and activated by user choice (checkbox)?
|
||||||
|
bool getActiveDirectoryUsed() const;
|
||||||
|
|
||||||
const QList< GroupDescription >& defaultGroups() const { return m_defaultGroups; }
|
const QList< GroupDescription >& defaultGroups() const { return m_defaultGroups; }
|
||||||
/** @brief the names of all the groups for the current user
|
/** @brief the names of all the groups for the current user
|
||||||
@ -292,6 +296,12 @@ public Q_SLOTS:
|
|||||||
void setRootPassword( const QString& );
|
void setRootPassword( const QString& );
|
||||||
void setRootPasswordSecondary( const QString& );
|
void setRootPasswordSecondary( const QString& );
|
||||||
|
|
||||||
|
void setActiveDirectoryUsed( bool used );
|
||||||
|
void setActiveDirectoryAdminUsername( const QString& );
|
||||||
|
void setActiveDirectoryAdminPassword( const QString& );
|
||||||
|
void setActiveDirectoryDomain( const QString& );
|
||||||
|
void setActiveDirectoryIP( const QString& );
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void userShellChanged( const QString& );
|
void userShellChanged( const QString& );
|
||||||
void autoLoginGroupChanged( const QString& );
|
void autoLoginGroupChanged( const QString& );
|
||||||
@ -343,6 +353,13 @@ private:
|
|||||||
|
|
||||||
bool m_isReady = false; ///< Used to reduce readyChanged signals
|
bool m_isReady = false; ///< Used to reduce readyChanged signals
|
||||||
|
|
||||||
|
bool m_activeDirectory = false;
|
||||||
|
bool m_activeDirectoryUsed = false;
|
||||||
|
QString m_activeDirectoryAdminUsername;
|
||||||
|
QString m_activeDirectoryAdminPassword;
|
||||||
|
QString m_activeDirectoryDomain;
|
||||||
|
QString m_activeDirectoryIP;
|
||||||
|
|
||||||
HostNameAction m_hostnameAction = HostNameAction::EtcHostname;
|
HostNameAction m_hostnameAction = HostNameAction::EtcHostname;
|
||||||
bool m_writeEtcHosts = false;
|
bool m_writeEtcHosts = false;
|
||||||
QString m_hostnameTemplate;
|
QString m_hostnameTemplate;
|
||||||
|
@ -162,6 +162,16 @@ UsersPage::UsersPage( Config* config, QWidget* parent )
|
|||||||
config, &Config::requireStrongPasswordsChanged, ui->checkBoxRequireStrongPassword, &QCheckBox::setChecked );
|
config, &Config::requireStrongPasswordsChanged, ui->checkBoxRequireStrongPassword, &QCheckBox::setChecked );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Active Directory is not checked or enabled by default
|
||||||
|
ui->useADCheckbox->setVisible( m_config->getActiveDirectoryEnabled() );
|
||||||
|
onActiveDirectoryToggled( false );
|
||||||
|
|
||||||
|
connect( ui->useADCheckbox, &QCheckBox::toggled, this, &UsersPage::onActiveDirectoryToggled );
|
||||||
|
connect( ui->domainField, &QLineEdit::textChanged, config, &Config::setActiveDirectoryDomain );
|
||||||
|
connect( ui->domainAdminField, &QLineEdit::textChanged, config, &Config::setActiveDirectoryAdminUsername );
|
||||||
|
connect( ui->domainPasswordField, &QLineEdit::textChanged, config, &Config::setActiveDirectoryAdminPassword );
|
||||||
|
connect( ui->ipAddressField, &QLineEdit::textChanged, config, &Config::setActiveDirectoryIP );
|
||||||
|
|
||||||
CALAMARES_RETRANSLATE_SLOT( &UsersPage::retranslate );
|
CALAMARES_RETRANSLATE_SLOT( &UsersPage::retranslate );
|
||||||
|
|
||||||
onReuseUserPasswordChanged( m_config->reuseUserPasswordForRoot() );
|
onReuseUserPasswordChanged( m_config->reuseUserPasswordForRoot() );
|
||||||
@ -283,3 +293,18 @@ UsersPage::onReuseUserPasswordChanged( const int checked )
|
|||||||
ui->textBoxRootPassword->setVisible( visible );
|
ui->textBoxRootPassword->setVisible( visible );
|
||||||
ui->textBoxVerifiedRootPassword->setVisible( visible );
|
ui->textBoxVerifiedRootPassword->setVisible( visible );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
UsersPage::onActiveDirectoryToggled( bool checked )
|
||||||
|
{
|
||||||
|
ui->domainLabel->setVisible( checked );
|
||||||
|
ui->domainField->setVisible( checked );
|
||||||
|
ui->domainAdminLabel->setVisible( checked );
|
||||||
|
ui->domainAdminField->setVisible( checked );
|
||||||
|
ui->domainPasswordField->setVisible( checked );
|
||||||
|
ui->domainPasswordLabel->setVisible( checked );
|
||||||
|
ui->ipAddressField->setVisible( checked );
|
||||||
|
ui->ipAddressLabel->setVisible( checked );
|
||||||
|
|
||||||
|
m_config->setActiveDirectoryUsed( checked );
|
||||||
|
}
|
||||||
|
@ -44,6 +44,8 @@ protected slots:
|
|||||||
void reportUserPasswordStatus( int, const QString& );
|
void reportUserPasswordStatus( int, const QString& );
|
||||||
void reportRootPasswordStatus( int, const QString& );
|
void reportRootPasswordStatus( int, const QString& );
|
||||||
|
|
||||||
|
void onActiveDirectoryToggled( bool checked );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void retranslate();
|
void retranslate();
|
||||||
|
|
||||||
|
@ -603,6 +603,93 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>6</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="useADCheckbox">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use Active Directory</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="domainLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Domain:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="domainField"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="domainAdminLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Domain Administrator:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="domainAdminField"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="domainPasswordLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Password:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="domainPasswordField">
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="ipAddressLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>IP Address (optional):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="ipAddressField"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_7">
|
<spacer name="verticalSpacer_7">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -265,6 +265,12 @@ hostname:
|
|||||||
template: "derp-${cpu}"
|
template: "derp-${cpu}"
|
||||||
forbidden_names: [ localhost ]
|
forbidden_names: [ localhost ]
|
||||||
|
|
||||||
|
# Enable Active Directory enrollment support (opt-in)
|
||||||
|
#
|
||||||
|
# This uses realmd to enroll the machine in an Active Directory server
|
||||||
|
# It requires realmd as a runtime dependency of Calamares, if enabled
|
||||||
|
allowActiveDirectory: false
|
||||||
|
|
||||||
presets:
|
presets:
|
||||||
fullName:
|
fullName:
|
||||||
# value: "OEM User"
|
# value: "OEM User"
|
||||||
|
@ -52,6 +52,7 @@ properties:
|
|||||||
writeHostsFile: { type: boolean, default: true }
|
writeHostsFile: { type: boolean, default: true }
|
||||||
template: { type: string, default: "${first}-${product}" }
|
template: { type: string, default: "${first}-${product}" }
|
||||||
forbidden_names: { type: array, items: { type: string } }
|
forbidden_names: { type: array, items: { type: string } }
|
||||||
|
allowActiveDirectory: { type: boolean, default: false }
|
||||||
|
|
||||||
# Presets
|
# Presets
|
||||||
#
|
#
|
||||||
|