commit 7df61351c037e6edd8b7173c1ec353a7ac58f64e Author: Mitsuba100 Date: Tue Mar 3 20:31:33 2026 +0000 Initial upload of HyprArch releng configuration diff --git a/airootfs/etc/calamares/branding/default/banner.png b/airootfs/etc/calamares/branding/default/banner.png new file mode 100644 index 0000000..d1baeee Binary files /dev/null and b/airootfs/etc/calamares/branding/default/banner.png differ diff --git a/airootfs/etc/calamares/branding/default/banner.png.license b/airootfs/etc/calamares/branding/default/banner.png.license new file mode 100644 index 0000000..38aa361 --- /dev/null +++ b/airootfs/etc/calamares/branding/default/banner.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2020 Adriaan de Groot +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/airootfs/etc/calamares/branding/default/branding.desc b/airootfs/etc/calamares/branding/default/branding.desc new file mode 100644 index 0000000..38b309f --- /dev/null +++ b/airootfs/etc/calamares/branding/default/branding.desc @@ -0,0 +1,239 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Product branding information. This influences some global +# user-visible aspects of Calamares, such as the product +# name, window behavior, and the slideshow during installation. +# +# Additional styling can be done using the stylesheet.qss +# file, also in the branding directory. +--- +componentName: default + + +### WELCOME / OVERALL WORDING +# +# These settings affect some overall phrasing and looks, +# which are most visible in the welcome page. + +# This selects between different welcome texts. When false, uses +# the traditional "Welcome to the %1 installer.", and when true, +# uses "Welcome to the Calamares installer for %1." This allows +# to distinguish this installer from other installers for the +# same distribution. +welcomeStyleCalamares: true + +# Should the welcome image (productWelcome, below) be scaled +# up beyond its natural size? If false, the image does not grow +# with the window but remains the same size throughout (this +# may have surprising effects on HiDPI monitors). +welcomeExpandingLogo: true + +### WINDOW CONFIGURATION +# +# The settings here affect the placement of the Calamares +# window through hints to the window manager and initial +# sizing of the Calamares window. + +# Size and expansion policy for Calamares. +# - "normal" or unset, expand as needed, use *windowSize* +# - "fullscreen", start as large as possible, ignore *windowSize* +# - "noexpand", don't expand automatically, use *windowSize* +windowExpanding: normal + +# Size of Calamares window, expressed as w,h. Both w and h +# may be either pixels (suffix px) or font-units (suffix em). +# e.g. "800px,600px" +# "60em,480px" +# This setting is ignored if "fullscreen" is selected for +# *windowExpanding*, above. If not set, use constants defined +# in CalamaresUtilsGui, 800x520. +windowSize: 800px,520px + +# Placement of Calamares window. Either "center" or "free". +# Whether "center" actually works does depend on the window +# manager in use (and only makes sense if you're not using +# *windowExpanding* set to "fullscreen"). +windowPlacement: center + +### PANELS CONFIGURATION +# +# Calamares has a main content area, and two panels (navigation +# and progress / sidebar). The panels can be controlled individually, +# or switched off. If both panels are switched off, the layout of +# the main content area loses its margins, on the assumption that +# you're doing something special. + +# Kind of sidebar (panel on the left, showing progress). +# - "widget" or unset, use traditional sidebar (logo, items) +# - "none", hide it entirely +# - "qml", use calamares-sidebar.qml from branding folder +# In addition, you **may** specify a side, separated by a comma, +# from the kind. Valid sides are: +# - "left" (if not specified, uses this) +# - "right" +# - "top" +# - "bottom" +# For instance, "widget,right" is valid; so is "qml", which defaults +# to putting the sidebar on the left. Also valid is "qml,top". +# While "widget,top" is valid, the widgets code is **not** flexible +# and results will be terrible. +sidebar: widget + +# Kind of navigation (button panel on the bottom). +# - "widget" or unset, use traditional navigation +# - "none", hide it entirely +# - "qml", use calamares-navigation.qml from branding folder +# In addition, you **may** specify a side, separated by a comma, +# from the kind. The same sides are valid as for *sidebar*, +# except the default is *bottom*. +navigation: widget + + +### STRINGS, IMAGES AND COLORS +# +# This section contains the "branding proper" of names +# and images, rather than global-look settings. + +# These are strings shown to the user in the user interface. +# There is no provision for translating them -- since they +# are names, the string is included as-is. +# +# The four Url strings are the Urls used by the buttons in +# the welcome screen, and are not shown to the user. Clicking +# on the "Support" button, for instance, opens the link supportUrl. +# If a Url is empty, the corresponding button is not shown. +# +# bootloaderEntryName is how this installation / distro is named +# in the boot loader (e.g. in the GRUB menu). +# +# These strings support substitution from /etc/os-release +# if KDE Frameworks 5.58 are available at build-time. When +# enabled, ${varname} is replaced by the equivalent value +# from os-release. All the supported var-names are in all-caps, +# and are listed on the FreeDesktop.org site, +# https://www.freedesktop.org/software/systemd/man/os-release.html +# Note that ANSI_COLOR and CPE_NAME don't make sense here, and +# are not supported (the rest are). Remember to quote the string +# if it contains substitutions, or you'll get YAML exceptions. +# +# The *Url* entries are used on the welcome page, and they +# are visible as buttons there if the corresponding *show* keys +# are set to "true" (they can also be overridden). +strings: + productName: "${NAME}" + shortProductName: Generic + version: 2026.3 + shortVersion: 2023.3 + versionedName: HyprArch 03.26 "Droplet" + shortVersionedName: HyprArch 03.36 + bootloaderEntryName: HyprArch + productUrl: https://hyprarch.stuple.net/ + supportUrl: https://hyprarch.stuple.net/wiki + knownIssuesUrl: https://hyprarch.stuple.net/issues + releaseNotesUrl: https://hyprarch.stuple.net/news/ + donateUrl: https://docs.codeberg.org/improving-codeberg/donate/ + +# These images are loaded from the branding module directory. +# +# productBanner is an optional image, which if present, will be shown +# on the welcome page of the application, above the welcome text. +# It is intended to have a width much greater than height. +# It is displayed at 64px height (also on HiDPI). +# Recommended size is 64px tall, and up to 460px wide. +# productIcon is used as the window icon, and will (usually) be used +# by the window manager to represent the application. This image +# should be square, and may be displayed by the window manager +# as small as 16x16 (but possibly larger). +# productLogo is used as the logo at the top of the left-hand column +# which shows the steps to be taken. The image should be square, +# and is displayed at 80x80 pixels (also on HiDPI). +# productWallpaper is an optional image, which if present, will replace +# the normal solid background on every page of the application. +# It can be any size and proportion, +# and will be tiled to fit the entire window. +# For a non-tiled wallpaper, the size should be the same as +# the overall window, see *windowSize* above (800x520). +# productWelcome is shown on the welcome page of the application in +# the middle of the window, below the welcome text. It can be +# any size and proportion, and will be scaled to fit inside +# the window. Use `welcomeExpandingLogo` to make it non-scaled. +# Recommended size is 320x150. +# +# These filenames can also use substitutions from os-release (see above). +images: + # productBanner: "banner.png" + productIcon: "squid.png" + productLogo: "squid.png" + # productWallpaper: "wallpaper.png" + productWelcome: "languages.png" + +# Colors for text and background components. +# +# - SidebarBackground is the background of the sidebar +# - SidebarText is the (foreground) text color +# - SidebarBackgroundCurrent sets the background of the current step. +# Optional, and defaults to the application palette. +# - SidebarTextCurrent is the text color of the current step. +# +# These colors can **also** be set through the stylesheet, if the +# branding component also ships a stylesheet.qss. Then they are +# the corresponding CSS attributes of #sidebarApp. +style: + SidebarBackground: "#292F34" + SidebarText: "#FFFFFF" + SidebarTextCurrent: "#292F34" + SidebarBackgroundCurrent: "#D35400" + +### SLIDESHOW +# +# The slideshow is displayed during execution steps (e.g. when the +# installer is actually writing to disk and doing other slow things). + +# The slideshow can be a QML file (recommended) which can display +# arbitrary things -- text, images, animations, or even play a game -- +# during the execution step. The QML **is** abruptly stopped when the +# execution step is done, though, so maybe a game isn't a great idea. +# +# The slideshow can also be a sequence of images (not recommended unless +# you don't want QML at all in your Calamares). The images are displayed +# at a rate of 1 every 2 seconds during the execution step. +# +# To configure a QML file, list a single filename: +# slideshow: "show.qml" +# To configure images, like the filenames (here, as an inline list): +# slideshow: [ "/etc/calamares/slideshow/0.png", "/etc/logo.png" ] +slideshow: "show.qml" + +# There are two available APIs for a QML slideshow: +# - 1 (the default) loads the entire slideshow when the installation- +# slideshow page is shown and starts the QML then. The QML +# is never stopped (after installation is done, times etc. +# continue to fire). +# - 2 loads the slideshow on startup and calls onActivate() and +# onLeave() in the root object. After the installation is done, +# the show is stopped (first by calling onLeave(), then destroying +# the QML components). +# +# An image slideshow does not need to have the API defined. +slideshowAPI: 2 + + +# These options are to customize online uploading of logs to pastebins: +# - type : Defines the kind of pastebin service to be used. Currently +# it accepts two values: +# - none : disables the pastebin functionality +# - fiche : use fiche pastebin server +# - url : Defines the address of pastebin service to be used. +# Takes string as input. Important bits are the host and port, +# the scheme is not used. +# - sizeLimit : Defines maximum size limit (in KiB) of log file to be pasted. +# The option must be set, to have the log option work. +# Takes integer as input. If < 0, no limit will be forced, +# else only last (approximately) 'n' KiB of log file will be pasted. +# Please note that upload size may be slightly over the limit (due +# to last minute logging), so provide a suitable value. +uploadServer : + type : "fiche" + url : "http://termbin.com:9999" + sizeLimit : -1 diff --git a/airootfs/etc/calamares/branding/default/lang/calamares-default_ar.qm b/airootfs/etc/calamares/branding/default/lang/calamares-default_ar.qm new file mode 100644 index 0000000..23eaa2a Binary files /dev/null and b/airootfs/etc/calamares/branding/default/lang/calamares-default_ar.qm differ diff --git a/airootfs/etc/calamares/branding/default/lang/calamares-default_en.qm b/airootfs/etc/calamares/branding/default/lang/calamares-default_en.qm new file mode 100644 index 0000000..ce6afd2 Binary files /dev/null and b/airootfs/etc/calamares/branding/default/lang/calamares-default_en.qm differ diff --git a/airootfs/etc/calamares/branding/default/lang/calamares-default_eo.qm b/airootfs/etc/calamares/branding/default/lang/calamares-default_eo.qm new file mode 100644 index 0000000..4d85aa1 Binary files /dev/null and b/airootfs/etc/calamares/branding/default/lang/calamares-default_eo.qm differ diff --git a/airootfs/etc/calamares/branding/default/lang/calamares-default_fr.qm b/airootfs/etc/calamares/branding/default/lang/calamares-default_fr.qm new file mode 100644 index 0000000..ae8e52e Binary files /dev/null and b/airootfs/etc/calamares/branding/default/lang/calamares-default_fr.qm differ diff --git a/airootfs/etc/calamares/branding/default/lang/calamares-default_nl.qm b/airootfs/etc/calamares/branding/default/lang/calamares-default_nl.qm new file mode 100644 index 0000000..5e26cc8 Binary files /dev/null and b/airootfs/etc/calamares/branding/default/lang/calamares-default_nl.qm differ diff --git a/airootfs/etc/calamares/branding/default/languages.png b/airootfs/etc/calamares/branding/default/languages.png new file mode 100644 index 0000000..5331652 Binary files /dev/null and b/airootfs/etc/calamares/branding/default/languages.png differ diff --git a/airootfs/etc/calamares/branding/default/languages.png.license b/airootfs/etc/calamares/branding/default/languages.png.license new file mode 100644 index 0000000..ea82645 --- /dev/null +++ b/airootfs/etc/calamares/branding/default/languages.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2015 Teo Mrnjavac +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/airootfs/etc/calamares/branding/default/show.qml b/airootfs/etc/calamares/branding/default/show.qml new file mode 100644 index 0000000..f4c50e6 --- /dev/null +++ b/airootfs/etc/calamares/branding/default/show.qml @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +import QtQuick 2.0; +import calamares.slideshow 1.0; + +Presentation +{ + id: presentation + + function nextSlide() { + console.log("QML Component (default slideshow) Next slide"); + presentation.goToNextSlide(); + } + + Timer { + id: advanceTimer + interval: 1000 + running: presentation.activatedInCalamares + repeat: true + onTriggered: nextSlide() + } + + Slide { + + Image { + id: background + source: "squid.png" + width: 200; height: 200 + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + } + Text { + anchors.horizontalCenter: background.horizontalCenter + anchors.top: background.bottom + text: "This is a customizable QML slideshow.
"+ + "Distributions should provide their own slideshow and list it in
"+ + "their custom branding.desc file.
"+ + "To create a Calamares presentation in QML, import calamares.slideshow,
"+ + "define a Presentation element with as many Slide elements as needed." + wrapMode: Text.WordWrap + width: presentation.width + horizontalAlignment: Text.Center + } + } + + Slide { + centeredText: qsTr("This is a second Slide element.") + } + + Slide { + centeredText: qsTr("This is a third Slide element.") + } + + // When this slideshow is loaded as a V1 slideshow, only + // activatedInCalamares is set, which starts the timer (see above). + // + // In V2, also the onActivate() and onLeave() methods are called. + // These example functions log a message (and re-start the slides + // from the first). + function onActivate() { + console.log("QML Component (default slideshow) activated"); + presentation.currentSlide = 0; + } + + function onLeave() { + console.log("QML Component (default slideshow) deactivated"); + } + +} diff --git a/airootfs/etc/calamares/branding/default/squid.png b/airootfs/etc/calamares/branding/default/squid.png new file mode 100644 index 0000000..452e445 Binary files /dev/null and b/airootfs/etc/calamares/branding/default/squid.png differ diff --git a/airootfs/etc/calamares/branding/default/squid.png.license b/airootfs/etc/calamares/branding/default/squid.png.license new file mode 100644 index 0000000..cc08e1f --- /dev/null +++ b/airootfs/etc/calamares/branding/default/squid.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2014 Teo Mrnjavac +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/airootfs/etc/calamares/branding/default/stylesheet.qss b/airootfs/etc/calamares/branding/default/stylesheet.qss new file mode 100644 index 0000000..5c36738 --- /dev/null +++ b/airootfs/etc/calamares/branding/default/stylesheet.qss @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: no + * SPDX-License-Identifier: CC0-1.0 + */ + +/* +A branding component can ship a stylesheet (like this one) +which is applied to parts of the Calamares user-interface. +In principle, all parts can be styled through CSS. +Missing parts should be filed as issues. + +The IDs are based on the object names in the C++ code. +You can use the Debug Dialog to find out object names: + - Open the debug dialog + - Choose tab *Tools* + - Click *Widget Tree* button +The list of object names is printed in the log. + +Documentation for styling Qt Widgets through a stylesheet +can be found at + https://doc.qt.io/qt-5/stylesheet-examples.html + https://doc.qt.io/qt-5/stylesheet-reference.html +In Calamares, styling widget classes is supported (e.g. +using `QComboBox` as a selector). + +This example stylesheet has all the actual styling commented out. +The examples are not exhaustive. + +*/ + +/*** Generic Widgets. + * + * You can style **all** widgets of a given class by selecting + * the class name. Some widgets have specialized sub-selectors. + */ + +/* +QPushButton { background-color: green; } +*/ + +/*** Main application window. + * + * The main application window has the sidebar, which in turn + * contains a logo and a list of items -- note that the list + * can **not** be styled, since it has its own custom C++ + * delegate code. + */ + +/* +#mainApp { } +#sidebarApp { } +#logoApp { } +*/ + +/*** Welcome module. + * + * There are plenty of parts, but the buttons are the most interesting + * ones (donate, release notes, ...). The little icon image can be + * styled through *qproperty-icon*, which is a little obscure. + * URLs can reference the QRC paths of the Calamares application + * or loaded via plugins or within the filesystem. There is no + * comprehensive list of available icons, though. + */ + +/* +QPushButton#aboutButton { qproperty-icon: url(:/data/images/release.svg); } +#donateButton, +#supportButton, +#releaseNotesButton, +#knownIssuesButton { qproperty-icon: url(:/data/images/help.svg); } +*/ + +/*** Partitioning module. + * + * Many moving parts, which you will need to experiment with. + */ + +/* +#bootInfoIcon { } +#bootInfoLable { } +#deviceInfoIcon { } +#defineInfoLabel { } +#scrollAreaWidgetContents { } +#partitionBarView { } +*/ + +/*** Licensing module. + * + * The licensing module paints individual widgets for each of + * the licenses. The item can be collapsed or expanded. + */ + +/* +#licenseItem { } +#licenseItemFullText { } +*/ diff --git a/airootfs/etc/calamares/modules/bootloader.conf b/airootfs/etc/calamares/modules/bootloader.conf new file mode 100644 index 0000000..2105f27 --- /dev/null +++ b/airootfs/etc/calamares/modules/bootloader.conf @@ -0,0 +1,86 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Bootloader configuration. The bootloader is installed to allow +# the system to start (and pick one of the installed operating +# systems to run). +# +# Take note that Debian-derivatives that use unmodified GRUB EFI packages +# should specifically set *efiBootloaderId* to "debian" because that is +# hard-coded in `grubx64.efi`. +--- +# A variable from global storage which overrides the value of efiBootLoader +#efiBootLoaderVar: "packagechooser_bootloader" + +# Define which bootloader you want to use for EFI installations +# Possible options are 'grub', 'sb-shim', 'refind` and 'systemd-boot'. +efiBootLoader: "grub" + +# systemd-boot configuration files settings + +# kernelSearchPath is the path relative to the root of the install to search for kernels +# A kernel is identified by finding files which match regular expression, kernelPattern +kernelSearchPath: "/usr/lib/modules" +kernelPattern: "^vmlinuz.*" + +# loaderEntries is an array of options to add to loader.conf for systemd-boot +# please note that the "default" option is added programmatically +loaderEntries: + - "timeout 5" + - "console-mode keep" + +# systemd-boot and refind support custom kernel params +kernelParams: [ "quiet" ] + +# A list of kernel names that refind should accept as kernels +#refindKernelList: [ "linux","linux-lts","linux-zen","linux-hardened" ] + +# GRUB 2 binary names and boot directory +# Some distributions (e.g. Fedora) use grub2-* (resp. /boot/grub2/) names. +# These names are also used when using sb-shim, since that needs some +# GRUB functionality (notably grub-probe) to work. As needed, you may use +# complete paths like `/usr/bin/efibootmgr` for the executables. +# +grubInstall: "grub-install" +grubMkconfig: "grub-mkconfig" +grubCfg: "/boot/grub/grub.cfg" +grubProbe: "grub-probe" +efiBootMgr: "efibootmgr" + +# Optionally set the bootloader ID to use for EFI. This is passed to +# grub-install --bootloader-id. +# +# If not set here, the value from bootloaderEntryName from branding.desc +# is used, with problematic characters (space and slash) replaced. +# +# The ID is also used as a directory name within the EFI environment, +# and the bootloader is copied from /boot/efi/EFI// . When +# setting the option here, keep in mind that the name is sanitized +# (problematic characters, see above, are replaced). +# +# There are some special words possible at the end of *efiBootloaderId*: +# ${SERIAL} can be used to obtain a uniquely-numbered suffix +# that is added to the Id (yielding, e.g., `dirname1` or `dirname72`) +# ${RANDOM} can be used to obtain a unique 4-digit hex suffix +# ${PHRASE} can be used to obtain a unique 1-to-3-word suffix +# from a dictionary of space-themed words +# These words must be at the **end** of the *efiBootloaderId* value. +# There must also be at most one of them. If there is none, no suffix- +# processing is done and the *efiBootloaderId* is used unchanged. +# +# NOTE: Debian derivatives that use the unmodified Debian GRUB EFI +# packages may need to set this to "debian" because that is +# hard-coded in `grubx64.efi`. +# +# efiBootloaderId: "dirname" + +# Optionally install a copy of the GRUB EFI bootloader as the EFI +# fallback loader (either bootia32.efi or bootx64.efi depending on +# the system). This may be needed on certain systems (Intel DH87MC +# seems to be the only one). If you set this to false, take care +# to add another module to optionally install the fallback on those +# boards that need it. +installEFIFallback: true + +# Optionally install both BIOS and UEFI GRUB bootloaders. +installHybridGRUB: false diff --git a/airootfs/etc/calamares/modules/contextualprocess.conf b/airootfs/etc/calamares/modules/contextualprocess.conf new file mode 100644 index 0000000..e5c1c1d --- /dev/null +++ b/airootfs/etc/calamares/modules/contextualprocess.conf @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the contextual process job. +# +# Contextual processes are based on **global** configuration values. +# When a given global value (string) equals a given value, then +# the associated command is executed. +# +# Configuration consists of keys for global variable names (except +# *dontChroot* and *timeout*), and the sub-keys are strings to compare +# to the variable's value. If the variable has that particular value, the +# corresponding value (script) is executed. The top-level keys *dontChroot* +# and *timeout* are not global variable names. They have +# meaning just like in shellprocess.conf, that is they +# determine **where** the command runs and how long it has. +# +# The variable **may** contain dots, in which case the dot is used +# to select into maps inside global storage, e.g. +# +# - *firmwareType* is a simple global name +# - *branding.bootloader* is the *bootloader* value in the *branding* map +# +# Only a few global storage entries have well-defined sub-maps; +# branding is one of them, and *filesystem_use* is another. Note that +# variable names with dots **must** be quoted, or you will get a YAML error. +# +# +# You can check for an empty value with "". +# +# As a special case, the value-check "*" matches any value, but **only** +# if no other value-check matches. Use it as an *else* form for value- +# checks. Take care to put the asterisk in quotes. The value-check "*" +# **also** matches a literal asterisk as value; a confusing corner case +# is checking for an asterisk **and** having a wildcard match with +# different commands. This is currently not possible. +# +# Global configuration variables are not checked in a deterministic +# order, so do not rely on commands from one variable-check to +# always happen before (or after) checks on another +# variable. Similarly, the value-equality checks are not +# done in a deterministic order, but all of the value-checks +# for a given variable happen together. As a special case, the +# value-check for "*" (the *else* case) happens after all of the +# other value-checks, and only matches if none of the others do. +# +# The values after a value sub-keys are the same kinds of values +# as can be given to the *script* key in the shellprocess module. +# See shellprocess.conf for documentation on valid values and how +# variables are expanded in those commands. +--- +dontChroot: false +firmwareType: + efi: + - "-pkg remove efi-firmware" + - command: "-mkinitramfsrd -abgn" + timeout: 120 # This is slow + bios: "-pkg remove bios-firmware" + "": "/bin/false no-firmware-type-set" + "*": "/bin/false some-other-firmware-value" +"branding.shortVersion": + "2020.2": "/bin/false february" + "2019.4": "/bin/true april" diff --git a/airootfs/etc/calamares/modules/displaymanager.conf b/airootfs/etc/calamares/modules/displaymanager.conf new file mode 100644 index 0000000..d0a6a35 --- /dev/null +++ b/airootfs/etc/calamares/modules/displaymanager.conf @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configure one or more display managers (e.g. SDDM) +# with a "best effort" approach. +# +# This module also sets up autologin, if the feature is enabled in +# globalstorage (where it would come from the users page). +--- +# The DM module attempts to set up all the DMs found in this list, in the +# precise order listed. The displaymanagers list can also be set in +# globalstorage, and in that case it overrides the setting here. +# +# If *sysconfigSetup* is set to *true* (see below, only relevant for +# openSUSE derivatives) then this list is ignored and only sysconfig +# is attempted. You can also list "sysconfig" in this list instead. +# +displaymanagers: + - slim + - sddm + - lightdm + - gdm + - mdm + - lxdm + - greetd + +# Enable the following settings to force a desktop environment +# in your displaymanager configuration file. This will attempt +# to configure the given DE (without checking if it is installed). +# The DM configuration for each potential DM may **or may not** +# support configuring a default DE, so the keys are mandatory +# but their interpretation is up to the DM configuration. +# +# Subkeys of *defaultDesktopEnvironment* are (all mandatory): +# - *executable* a full path to an executable +# - *desktopFile* a .desktop filename +# +# If this is **not** set, then Calamares will look for installed +# DE's and pick the first one it finds that is actually installed. +# +# If this **is** set, and the *executable* key doesn't point to +# an installed file, then the .desktop file's TryExec key is +# used instead. +# + +#defaultDesktopEnvironment: +# executable: "startkde" +# desktopFile: "plasma" + +#If true, try to ensure that the user, group, /var directory etc. for the +#display manager are set up correctly. This is normally done by the distribution +#packages, and best left to them. Therefore, it is disabled by default. +basicSetup: false + +# If true, setup autologin for openSUSE. This only makes sense on openSUSE +# derivatives or other systems where /etc/sysconfig/displaymanager exists. +# +# The preferred way to pick sysconfig is to just list it in the +# *displaymanagers* list (as the only one). +# +sysconfigSetup: false + +# Some DMs have specific settings. These can be customized here. +# +# greetd has configurable user and group; the user and group is created if it +# does not exist, and the user is set as default-session user. +# +# Some greeters for greetd (e.g gtkgreet or regreet) have support for a user's GTK CSS style to change appearance. +# +# lightdm has a list of greeters to look for, preferring them in order if +# they are installed (if not, picks the alphabetically first greeter that is installed). +# +greetd: + greeter_user: "tom_bombadil" + greeter_group: "wheel" + greeter_css_location: "/etc/greetd/style.css" +lightdm: + preferred_greeters: ["lightdm-greeter.desktop", "slick-greeter.desktop"] +sddm: + configuration_file: "/etc/sddm.conf" diff --git a/airootfs/etc/calamares/modules/finished.conf b/airootfs/etc/calamares/modules/finished.conf new file mode 100644 index 0000000..7abfb36 --- /dev/null +++ b/airootfs/etc/calamares/modules/finished.conf @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the "finished" page, which is usually shown only at +# the end of the installation (successful or not). +--- +# DEPRECATED +# +# The finished page can hold a "restart system now" checkbox. +# If this is false, no checkbox is shown and the system is not restarted +# when Calamares exits. +# restartNowEnabled: true + +# DEPRECATED +# +# Initial state of the checkbox "restart now". Only relevant when the +# checkbox is shown by restartNowEnabled. +# restartNowChecked: false + +# Behavior of the "restart system now" button. +# +# There are four usable values: +# - never +# Does not show the button and does not restart. +# This matches the old behavior with restartNowEnabled=false. +# - user-unchecked +# Shows the button, defaults to unchecked, restarts if it is checked. +# This matches the old behavior with restartNowEnabled=true and restartNowChecked=false. +# - user-checked +# Shows the button, defaults to checked, restarts if it is checked. +# This matches the old behavior with restartNowEnabled=true and restartNowChecked=true. +# - always +# Shows the button, checked, but the user cannot change it. +# This is new behavior. +# +# The three combinations of legacy values are still supported. +restartNowMode: user-unchecked + +# If the checkbox is shown, and the checkbox is checked, then when +# Calamares exits from the finished-page it will run this command. +# If not set, falls back to "shutdown -r now". +restartNowCommand: "systemctl -i reboot" + +# When the last page is (successfully) reached, send a DBus notification +# to the desktop that the installation is done. This works only if the +# user as whom Calamares is run, can reach the regular desktop session bus. +notifyOnFinished: false diff --git a/airootfs/etc/calamares/modules/finishedq.conf b/airootfs/etc/calamares/modules/finishedq.conf new file mode 100644 index 0000000..ee226c3 --- /dev/null +++ b/airootfs/etc/calamares/modules/finishedq.conf @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the "finishedq" page, which is usually shown only at +# the end of the installation (successful or not). +# +# See the documentation for the "finished" module for a full explanation +# of the configuration options; the description here applies primarily +# to the use that the QML makes of them. +--- +# Behavior of the "restart system now" button. +# +# The example QML for this module offers a "Restart Now" button, +# which the user can click on. It calls directly to the restart +# function. If the user closes the installer in some other way, +# (the "Done" button or close-window) a restart **might** happen: +# +# - never +# Do not restart (this will also block the "Restart Now" button, +# so it is not very useful) +# - user-unchecked +# Do not restart on other ways of closing the window. No checkbox +# is shown in the example QML, so there is no way for the user to +# express a choice -- except by clicking the "Restart Now" button. +# - user-checked +# Do restart on other ways of closing the window. This makes close +# and "Restart Now" do the same thing. No checkbox is shown by the QML, +# so the machine will **always** restart. +# - always +# Same as above. +# +# For the **specific** example QML included with this module, only +# *user-unchecked* really makes sense. +restartNowMode: user-unchecked +restartNowCommand: "systemctl -i reboot" +notifyOnFinished: false diff --git a/airootfs/etc/calamares/modules/flatpakinfo/CMakeLists.txt b/airootfs/etc/calamares/modules/flatpakinfo/CMakeLists.txt new file mode 100644 index 0000000..5ecba15 --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/CMakeLists.txt @@ -0,0 +1,17 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +calamares_add_plugin(flatpakInfo + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + FlatpakInfoJob.h + ItemFlatpak.h + PackagePool.h + FlatpakInfoJob.cpp + ItemFlatpak.cpp + PackagePool.cpp + SHARED_LIB +) diff --git a/airootfs/etc/calamares/modules/flatpakinfo/FlatpakInfoJob.cpp b/airootfs/etc/calamares/modules/flatpakinfo/FlatpakInfoJob.cpp new file mode 100644 index 0000000..9b492db --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/FlatpakInfoJob.cpp @@ -0,0 +1,63 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "FlatpakInfoJob.h" + +#include "utils/Runner.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" + +#include + +#include + +#include "ItemFlatpak.h" +#include "PackagePool.h" + +FlatpakInfoJob::FlatpakInfoJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + +FlatpakInfoJob::~FlatpakInfoJob() +{ + ItemFlatpak_freeMem(); +} + +QString +FlatpakInfoJob::prettyName() const +{ + return tr( "Fill netinstall with flatpak packages" ); +} + + +Calamares::JobResult +FlatpakInfoJob::exec() +{ + QVariantList partitions; + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + + + downloadPackagesInfo(); + serializePackagesInfo(); + + return Calamares::JobResult::ok(); +} + + +void +FlatpakInfoJob::setConfigurationMap( const QVariantMap& map ) +{ +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( FlatpakInfoJobFactory, registerPlugin< FlatpakInfoJob >(); ) diff --git a/airootfs/etc/calamares/modules/flatpakinfo/FlatpakInfoJob.h b/airootfs/etc/calamares/modules/flatpakinfo/FlatpakInfoJob.h new file mode 100644 index 0000000..1371343 --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/FlatpakInfoJob.h @@ -0,0 +1,43 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef FLATPAKINFOJOB_H +#define FLATPAKINFOJOB_H + +#include +#include +#include + +#include "CppJob.h" + +#include "utils/PluginFactory.h" + +#include "DllMacro.h" + +/** @brief Create zpools and zfs datasets + * + */ +class PLUGINDLLEXPORT FlatpakInfoJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit FlatpakInfoJob( QObject* parent = nullptr ); + ~FlatpakInfoJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( FlatpakInfoJobFactory ) + +#endif // ZFSJOB_H diff --git a/airootfs/etc/calamares/modules/flatpakinfo/ItemFlatpak.cpp b/airootfs/etc/calamares/modules/flatpakinfo/ItemFlatpak.cpp new file mode 100644 index 0000000..540c2e2 --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/ItemFlatpak.cpp @@ -0,0 +1,66 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* Qt */ +#include + +/* CPP */ +#include +#include + +/* Calamares */ +#include "utils/Runner.h" + +/* Module */ +#include "ItemFlatpak.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +PackageItem +fromFlatpak( const QVariantMap& itemMap, InstalledList &installed ) +{ + // check if it is installed + PackageItem item( Calamares::getString( itemMap, "appstream" ) ); + item.setInstalled( false ); + + item.setInstalled( installed.contains( Calamares::getString( itemMap, "appstream" ) ) ); + + return item; +} + +InstalledList::InstalledList() +{ + long long int prev_pos; + long long int pos = 0; + QString line; + auto process = Calamares::System::instance()->targetEnvCommand( + QStringList { QString::fromLatin1( "flatpak" ), + QString::fromLatin1( "list" ), + QString::fromLatin1( "--app" ), + QString::fromLatin1( "--columns=application" ) } ); + auto outputStr = process.second; + + do { + prev_pos = pos; + + pos = outputStr.indexOf('\n', prev_pos); + QString line = outputStr.mid(prev_pos, pos); + installed.append(line); + + /* Increase by 1 to not stuck on newline */ + ++pos; + + /* QString::indexOf returns -1 since no occurences. 0 = -1 + 1.*/ + } while (0 != pos); +} + +InstalledList::~InstalledList() +{ + installed.clear(); +} diff --git a/airootfs/etc/calamares/modules/flatpakinfo/ItemFlatpak.h b/airootfs/etc/calamares/modules/flatpakinfo/ItemFlatpak.h new file mode 100644 index 0000000..e69de29 diff --git a/airootfs/etc/calamares/modules/flatpakinfo/PackagePool.cpp b/airootfs/etc/calamares/modules/flatpakinfo/PackagePool.cpp new file mode 100644 index 0000000..dca9a5e --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/PackagePool.cpp @@ -0,0 +1,110 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "utils/Logger.h" +#include "utils/Variant.h" +#include "ItemFlatpak.h" +#include "PackagePool.h" + +#include "utils/System.h" + +void PackagePool::downloadPackagesInfo(InstalledList &list) +{ + QHash addedPackages; + QString line; + auto process = Calamares::System::instance()->targetEnvCommand( QStringList { QString::fromStdString( "flatpak" ), QString::fromStdString( "remotes" ), QString::fromStdString( "--columns=name" ) }); + auto outputStr = process.second; + QTextStream output(&outputStr); + + while (output.readLineInto(&line)) + { + QString line2; + auto process2 = Calamares::System::instance()->targetEnvCommand( + QStringList { QString::fromStdString( "flatpak" ), + QString::fromStdString( "remote-ls" ), + QString::fromStdString( "--app" ), + QString::fromStdString( "--columns=application" ), + line } ); + auto output2Str = process2.second; + QTextStream output2( &output2Str ); + + while ( output2.readLineInto( &line2 ) ) + { + if ( line2 == "" ) + { + continue; + } + QVariantMap itemMap; + + if ( addedPackages.contains( line2 ) ) + { + continue; + } + + addedPackages.insert( line2, true ); + + itemMap.insert( "appstream", QVariant( line2 ) ); + itemMap.insert( "id", QVariant( line2 ) ); + + PackageItem item = fromFlatpak( itemMap, list ); + packages.append( item ); + } + } + + serializePackagesInfo(); +} + +void PackagePool::serializePackagesInfo() +{ + QList changedValue; + auto* gs = Calamares::JobQueue::instance()->globalStorage(); + + // If an earlier packagechooser instance added this data to global storage, combine them + if ( gs->contains( "netinstallAdd" ) ) + { + auto selectedOrig = gs->value( "netinstallAdd" ); + + changedValue = selectedOrig.toList(); + for (auto current: packages) + { + QStringList selfInstall; + QVariantMap newValue; + newValue.insert("name", current.getAppStreamId()); + + if (current.getInstalled()) + { + newValue.insert("selected", true); + newValue.insert("immutable", true); + newValue.insert("description", "[Already installed; cannot be uninstalled]"); + } + else + { + newValue.insert("selected", false); + } + selfInstall.append(current.getAppStreamId()); + newValue.insert("packages", selfInstall); + changedValue.append(newValue); + } + + gs->remove( "netinstallAdd" ); + } + gs->insert( "netinstallAdd", changedValue ); +} diff --git a/airootfs/etc/calamares/modules/flatpakinfo/PackagePool.h b/airootfs/etc/calamares/modules/flatpakinfo/PackagePool.h new file mode 100644 index 0000000..e69de29 diff --git a/airootfs/etc/calamares/modules/flatpakinfo/flatpakInfo.conf b/airootfs/etc/calamares/modules/flatpakinfo/flatpakInfo.conf new file mode 100644 index 0000000..93b2349 --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/flatpakInfo.conf @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The flatpakinfo module will collect package list from configured flatpak repositories +# +# +# +--- diff --git a/airootfs/etc/calamares/modules/flatpakinfo/module.desc b/airootfs/etc/calamares/modules/flatpakinfo/module.desc new file mode 100644 index 0000000..b5ac4cb --- /dev/null +++ b/airootfs/etc/calamares/modules/flatpakinfo/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "flatpakinfo" +interface: "qtplugin" +load: "libcalamares_job_flatpakInfo.so" diff --git a/airootfs/etc/calamares/modules/freebsddisk/CMakeLists.txt b/airootfs/etc/calamares/modules/freebsddisk/CMakeLists.txt new file mode 100644 index 0000000..4739482 --- /dev/null +++ b/airootfs/etc/calamares/modules/freebsddisk/CMakeLists.txt @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +if( NOT Calamares_WITH_QML ) + calamares_skip_module( "freebsddisk (QML is not supported in this build)" ) + return() +endif() + +calamares_add_plugin( freebsddisk + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + FreeBSDDiskViewStep.cpp + RESOURCES + freebsddisk.qrc + SHARED_LIB +) diff --git a/airootfs/etc/calamares/modules/freebsddisk/FreeBSDDiskViewStep.cpp b/airootfs/etc/calamares/modules/freebsddisk/FreeBSDDiskViewStep.cpp new file mode 100644 index 0000000..11d4a85 --- /dev/null +++ b/airootfs/etc/calamares/modules/freebsddisk/FreeBSDDiskViewStep.cpp @@ -0,0 +1,42 @@ +/* === This file is part of Calamares - === + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSES/GPL-3.0 + */ + +#include "FreeBSDDiskViewStep.h" + +FreeBSDDiskViewStep::FreeBSDDiskViewStep( QObject* parent ) + : Calamares::QmlViewStep( parent ) +{ +} + +FreeBSDDiskViewStep::~FreeBSDDiskViewStep() {} + +QString +FreeBSDDiskViewStep::prettyName() const +{ + return tr( "Disk Setup" ); +} + +void +FreeBSDDiskViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDDiskViewStepFactory, registerPlugin< FreeBSDDiskViewStep >(); ) diff --git a/airootfs/etc/calamares/modules/freebsddisk/FreeBSDDiskViewStep.h b/airootfs/etc/calamares/modules/freebsddisk/FreeBSDDiskViewStep.h new file mode 100644 index 0000000..ba19a01 --- /dev/null +++ b/airootfs/etc/calamares/modules/freebsddisk/FreeBSDDiskViewStep.h @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSES/GPL-3.0 + */ + + +#ifndef FREEBSDDISKVIEWSTEP_H +#define FREEBSDDISKVIEWSTEP_H + +#include "DllMacro.h" +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" + +class PLUGINDLLEXPORT FreeBSDDiskViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + FreeBSDDiskViewStep( QObject* parent = nullptr ); + virtual ~FreeBSDDiskViewStep() override; + + QString prettyName() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDDiskViewStepFactory ) + +#endif diff --git a/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.conf b/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.conf new file mode 100644 index 0000000..c0462bd --- /dev/null +++ b/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.conf @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The *freebsddisk* module can be used to pick a disk +# as an installer step. This module supports ZFSroot +# on one whole disk, and UFSroot on one whole disk. +# +--- +qmlSearch: both + diff --git a/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.qml b/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.qml new file mode 100644 index 0000000..57b2fba --- /dev/null +++ b/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.qml @@ -0,0 +1,35 @@ +/* === This file is part of Calamares - === + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSES/GPL-3.0 + */ + +import io.calamares.ui 1.0 + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.1 + +Item { + Text { + anchors.top: parent.top + anchors.topMargin: 10 + text: "Select a disk on which to install FreeBSD." + } +} diff --git a/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.qrc b/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.qrc new file mode 100644 index 0000000..3f87d28 --- /dev/null +++ b/airootfs/etc/calamares/modules/freebsddisk/freebsddisk.qrc @@ -0,0 +1,5 @@ + + + freebsddisk.qml + + diff --git a/airootfs/etc/calamares/modules/fsresizer.conf b/airootfs/etc/calamares/modules/fsresizer.conf new file mode 100644 index 0000000..e58c398 --- /dev/null +++ b/airootfs/etc/calamares/modules/fsresizer.conf @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Module that resizes a single FS to fill the entire (rest) of +# a device. This is used in OEM situations where an image is +# flashed onto an SD card (or similar) and used to boot a device, +# after which the FS should expand to fill the SD card. +# +# Example: a distro produces a 6GiB large image that is +# written to an 8GiB SD card; the FS should expand to take +# advantage of the unused 2GiB. The FS should expand much +# more if the same image is written to a 16GiB card. +--- + +# Which FS needs to be grown? Choose one way to identify it: +# - *fs* names a mount point which should already be mounted +# in the system. +# - *dev* names a device +fs: / +# dev: /dev/mmcblk0p1 + +# How much of the total remaining space should the FS use? +# The only sensible amount is "all of it". The value is +# in percent, so set it to 100. Perhaps a fixed size is +# needed (that would be weird though, since you don't know +# how big the card is), use MiB as suffix in that case. +# If missing, then it's assumed to be 0, and no resizing +# will happen. +# +# Percentages apply to **available space**. +size: 100% + +# Resizing might not be worth it, though. Set the minimum +# that it must grow; if it cannot grow that much, the +# resizing is skipped. Can be in percentage or absolute +# size, as above. If missing, then it's assumed to be 0, +# which means resizing is always worthwhile. +# +# If *atleast* is not zero, then the setting *required*, +# below, becomes relevant. +# +# Percentages apply to **total device size**. +#atleast: 1000MiB + +# When *atleast* is not zero, then the resize may be +# recommended (the default) or **required**. If the +# resize is required and cannot be carried out (because +# there's not enough space), then that is a fatal +# error for the installer. By default, resize is only +# recommended and it is not an error for no resize to be +# carried out. +required: false diff --git a/airootfs/etc/calamares/modules/fstab.conf b/airootfs/etc/calamares/modules/fstab.conf new file mode 100644 index 0000000..5c5c566 --- /dev/null +++ b/airootfs/etc/calamares/modules/fstab.conf @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Creates /etc/fstab and /etc/crypttab in the target system. +# Also creates mount points for all the filesystems. +# +# When creating fstab entries for a filesystem, this module +# uses the options previously defined in the mount module +--- + +# Additional options added to each line in /etc/crypttab +crypttabOptions: luks +# For Debian and Debian-based distributions, change the above line to: +# crypttabOptions: luks,keyscript=/bin/cat + +# Options for handling /tmp in /etc/fstab +# Currently default (required) and ssd are supported +# The corresponding string can contain the following variables: +# tmpfs: true or tmpfs: false to either mount /tmp as tmpfs or not +# options: "" +# +# Example: +#tmpOptions: +# default: +# tmpfs: false +# options: "" +# ssd: +# tmpfs: true +# options: "defaults,noatime,mode=1777" +# +tmpOptions: + default: + tmpfs: false + options: "" + ssd: + tmpfs: true + options: "defaults,noatime,mode=1777" diff --git a/airootfs/etc/calamares/modules/grubcfg.conf b/airootfs/etc/calamares/modules/grubcfg.conf new file mode 100644 index 0000000..2212d27 --- /dev/null +++ b/airootfs/etc/calamares/modules/grubcfg.conf @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Create, overwrite or update /etc/default/grub in the target system. +# +# Write lines to /etc/default/grub (in the target system) based +# on calculated values and the values set in the *defaults* key +# in this configuration file. +# +# Calculated values are: +# - GRUB_DISTRIBUTOR, branding module, *bootloaderEntryName* (this +# string is sanitized, and see also setting *keep_distributor*) +# - GRUB_ENABLE_CRYPTODISK, based on the presence of filesystems +# that use LUKS +# - GRUB_CMDLINE_LINUX_DEFAULT, adding LUKS setup and plymouth +# support to the kernel. + +--- +# If set to true, always creates /etc/default/grub from scratch even if the file +# already existed. If set to false, edits the existing file instead. +overwrite: false + +# If set to true, prefer to write files in /etc/default/grub.d/ +# rather than the single file /etc/default/grub. If this is set, +# Calamares will write /etc/default/grub.d/00calamares.cfg instead. +prefer_grub_d: false + +# If set to true, an **existing** setting for GRUB_DISTRIBUTOR is +# kept, not updated to the *bootloaderEntryName* from the branding file. +# Use this if the GRUB_DISTRIBUTOR setting in the file is "smart" in +# some way (e.g. uses shell-command substitution). +keep_distributor: false + +# The default kernel params that should always be applied. +# This is an array of strings. If it is unset, the default is +# `["quiet"]`. To avoid the default, explicitly set this key +# to an empty list, `[]`. +kernel_params: [ "quiet" ] + +# Default entries to write to /etc/default/grub if it does not exist yet or if +# we are overwriting it. +# +defaults: + GRUB_TIMEOUT: 5 + GRUB_DEFAULT: "saved" + GRUB_DISABLE_SUBMENU: true + GRUB_TERMINAL_OUTPUT: "console" + GRUB_DISABLE_RECOVERY: true + +# Set to true to force defaults to be used even when not overwriting +always_use_defaults: false diff --git a/airootfs/etc/calamares/modules/initcpio.conf b/airootfs/etc/calamares/modules/initcpio.conf new file mode 100644 index 0000000..d2a1268 --- /dev/null +++ b/airootfs/etc/calamares/modules/initcpio.conf @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Run mkinitcpio(8) with the given preset value +--- +# This key defines the kernel to be loaded. +# It can have the following values: +# - the name of a single mkinitcpio preset +# - empty or unset +# - the literal string "all" +# +# If kernel is set to "all" or empty/unset then mkinitpio is called for all +# kernels. Otherwise it is called with a single preset with the value +# contained in kernel. +# +kernel: linux + +# Set this to true to turn off mitigations for lax file +# permissions on initramfs (which, in turn, can compromise +# your LUKS encryption keys, CVS-2019-13179). +# +# If your initramfs are stored in the EFI partition or another non-POSIX +# filesystem, this has no effect as the file permissions cannot be changed. +# In this case, ensure the partition is mounted securely. +# +be_unsafe: false diff --git a/airootfs/etc/calamares/modules/initcpiocfg.conf b/airootfs/etc/calamares/modules/initcpiocfg.conf new file mode 100644 index 0000000..a660393 --- /dev/null +++ b/airootfs/etc/calamares/modules/initcpiocfg.conf @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The initcpiocfg module is responsible for the configuration of mkinitcpio.conf. Typically this +# module is used in conjunction with the initcpio module to generate the boot image when using mkinitcpio +--- +# +# Determines if the systemd versions of the hooks should be used. This is false by default. +# +# Please note that using the systemd hooks result in no access to the emergency recovery shell +useSystemdHook: false + +# +# Modifications to the standard list of hooks. +# +# There are three subkeys: +# - prepend, which puts hooks at the beginning of the +# list of hooks, in the order specified here, +# - append, which adds hooks at the end of the list of +# hooks, in the order specified here, +# - remove, which removes hooks from the list of hooks, +# wherever they may be. +# +# The example configuration here yields bogus, , bogus +# initially, and then removes that hook again. +# +hooks: + prepend: [ bogus ] + append: [ bogus ] + remove: [ bogus ] + +# +# In some cases, you may want to use a different source +# file than /etc/mkinitcpio.conf , e.g. because the live system +# does not match the target in a useful way. If unset or +# empty, defaults to /etc/mkinitcpio.conf +# +source: "/etc/mkinitcpio.conf" diff --git a/airootfs/etc/calamares/modules/initramfs.conf b/airootfs/etc/calamares/modules/initramfs.conf new file mode 100644 index 0000000..82cf94b --- /dev/null +++ b/airootfs/etc/calamares/modules/initramfs.conf @@ -0,0 +1,3 @@ +--- +kernel: all +be_quiet: false diff --git a/airootfs/etc/calamares/modules/keyboard.conf b/airootfs/etc/calamares/modules/keyboard.conf new file mode 100644 index 0000000..b4850b2 --- /dev/null +++ b/airootfs/etc/calamares/modules/keyboard.conf @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# NOTE: you must have ckbcomp installed and runnable +# on the live system, for keyboard layout previews. +--- +# The name of the file to write X11 keyboard settings to +# The default value is the name used by upstream systemd-localed. +# Relative paths are assumed to be relative to /etc/X11/xorg.conf.d +xOrgConfFileName: "/etc/X11/xorg.conf.d/00-keyboard.conf" + +# The path to search for keymaps converted from X11 to kbd format. +# Common paths for this are: +# - /lib/kbd/keymaps/xkb +# - /usr/share/kbd/keymaps/xkb +# Leave this empty if the setting does not make sense on your distribution. +# +convertedKeymapPath: "/lib/kbd/keymaps/xkb" + +# Write keymap configuration to /etc/default/keyboard, usually +# found on Debian-related systems. +# Defaults to true if nothing is set. +#writeEtcDefaultKeyboard: true + +# Use the Locale1 service instead of directly managing configuration files. +# This is the modern mechanism for configuring the systemwide keyboard layout, +# and works on Wayland compositors to set the current layout. +# Defaults to false on X11 and true otherwise. +#useLocale1: true + +# 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 + # Configure keyboard when using Wayland with Gnome on Ubuntu 24.10+ + gnome: false diff --git a/airootfs/etc/calamares/modules/keyboardq.conf b/airootfs/etc/calamares/modules/keyboardq.conf new file mode 100644 index 0000000..d122f30 --- /dev/null +++ b/airootfs/etc/calamares/modules/keyboardq.conf @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# NOTE: you must have ckbcomp installed and runnable +# on the live system, for keyboard layout previews. +--- +# The name of the file to write X11 keyboard settings to +# The default value is the name used by upstream systemd-localed. +# Relative paths are assumed to be relative to /etc/X11/xorg.conf.d +xOrgConfFileName: "/etc/X11/xorg.conf.d/00-keyboard.conf" + +# The path to search for keymaps converted from X11 to kbd format +# Leave this empty if the setting does not make sense on your distribution. +convertedKeymapPath: "/lib/kbd/keymaps/xkb" + +# Write keymap configuration to /etc/default/keyboard, usually +# found on Debian-related systems. +# Defaults to true if nothing is set. +#writeEtcDefaultKeyboard: true diff --git a/airootfs/etc/calamares/modules/license.conf b/airootfs/etc/calamares/modules/license.conf new file mode 100644 index 0000000..e32d499 --- /dev/null +++ b/airootfs/etc/calamares/modules/license.conf @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration file for License viewmodule, Calamares +# Syntax is YAML 1.2 +--- +# Define a list of licenses which may / must be accepted before continuing. +# +# Each entry in this list has the following keys: +# - id Entry identifier, must be unique. Not user visible. YAML: string. +# - name Pretty name for the software product, user visible and untranslatable. YAML: string. +# - vendor Pretty name for the software vendor, user visible and untranslatable. YAML: string, optional, default is empty. +# - type Package type identifier for presentation, not user visible but affects user visible strings. YAML: string. +# values: driver, gpudriver, browserplugin, codec, package, software; optional, default is software. +# - required If set to true, the user cannot proceed without accepting this license. YAML: boolean, optional, default is false. +# - url A URL for the license; a remote URL is not shown in Calamares, but a link +# to the URL is provided, which opens in the default web browser. A local +# URL (i.e. file:///) assumes that the contents are HTML or plain text, and +# displays the license in-line. YAML: string, mandatory. +# - expand A boolean value only relevant for **local** URLs. If true, +# the license text is displayed in "expanded" form by +# default, rather than requiring the user to first open it up. +# YAML: boolean, optional, default is false. +entries: +- id: nvidia + name: Nvidia + vendor: Nvidia Corporation + type: driver + url: http://developer.download.nvidia.com/cg/Cg_3.0/license.pdf + required: false +- id: amd + name: Catalyst + vendor: "Advanced Micro Devices, Inc." + type: gpudriver + url: http://support.amd.com/en-us/download/eula + required: false +- id: flashplugin + name: Adobe Flash + vendor: Adobe Systems Incorporated + type: browserplugin + url: http://www.adobe.com/products/eulas/pdfs/PlatformClients_PC_WWEULA_Combined_20100108_1657.pdf + required: true +# This example uses a file: link. This example uses a relative link, which +# is relative to where you run Calamares. Assuming you run it from build/ +# as part of your testing, you'll get the LICENSE text for Calamares +# (which is the text of the GPLv3, not proprietary at all). +- id: mine_mine + name: Calamares Proprietary License + vendor: Calamares, Inc. + type: software + required: true + url: file:../LICENSE + expand: true diff --git a/airootfs/etc/calamares/modules/locale.conf b/airootfs/etc/calamares/modules/locale.conf new file mode 100644 index 0000000..4463f7a --- /dev/null +++ b/airootfs/etc/calamares/modules/locale.conf @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +# These settings are used to set your default system time zone. +# Time zones are usually located under /usr/share/zoneinfo and +# provided by the 'tzdata' package of your Distribution. +# +# Distributions using systemd can list available +# time zones by using the timedatectl command. +# timedatectl list-timezones +# +# The starting timezone (e.g. the pin-on-the-map) when entering +# the locale page can be set through keys *region* and *zone*. +# If either is not set, defaults to America/New_York. +# +# Note that useSystemTimezone and GeoIP settings can change the +# starting time zone. +# +region: "America" +zone: "New_York" + +# Instead of using *region* and *zone* specified above, +# you can use the system's notion of the timezone, instead. +# This can help if your system is automatically configured with +# a sensible TZ rather than chasing a fixed default. +# +# The default is false. +# +# useSystemTimezone: true + +# Should changing the system location (e.g. clicking around on the timezone +# map) immediately reflect the changed timezone in the live system? +# By default, installers (with a target system) do, and setup (e.g. OEM +# configuration) does not, but you can switch it on here (or off, if +# you think it's annoying in the installer). +# +# Note that not all systems support live adjustment. +# +# adjustLiveTimezone: true + +# System locales are detected in the following order: +# +# - /usr/share/i18n/SUPPORTED +# - localeGenPath (defaults to /etc/locale.gen if not set) +# - `locale -a` output +# +# Enable only when your Distribution is using a +# custom path for locale.gen +# +#localeGenPath: "/etc/locale.gen" + +# GeoIP based Language settings: Leave commented out to disable GeoIP. +# +# GeoIP needs a working Internet connection. +# This can be managed from `welcome.conf` by adding +# internet to the list of required conditions. (The welcome +# module can also do its own GeoIP lookups, independently +# of the lookup done here. The lookup in the welcome module +# is used to establish language; this one is for timezone). +# +# The configuration is in three parts: +# - a *style*, which can be "json" or "xml" depending on the +# kind of data returned by the service, and +# - a *url* where the data is retrieved, and +# - an optional *selector* +# to pick the right field out of the returned data (e.g. field +# name in JSON or element name in XML). +# +# The default selector (when the setting is blank) is picked to +# work with existing JSON providers (which use "time_zone") and +# Ubiquity's XML providers (which use "TimeZone"). +# +# If the service configured via *url* uses +# a different attribute name (e.g. "timezone") in JSON or a +# different element tag (e.g. "") in XML, set the +# selector to the name or tag to be used. +# +# In JSON: +# - if the string contains "." characters, this is used as a +# multi-level selector, e.g. "a.b" will select the timezone +# from data "{a: {b: "Europe/Amsterdam" } }". +# - each part of the string split by "." characters is used as +# a key into the JSON data. +# In XML: +# - all elements with the named tag (e.g. all TimeZone) elements +# from the document are checked; the first one with non-empty +# text value is used. +# Special case: +# - the *style* "fixed" is also supported. This ignores the data +# returned from the URL (but the URL must still be valid!) +# and just returns the value of the *selector*. +# +# An HTTP(S) request is made to *url*. The request should return +# valid data in a suitable format, depending on *style*; +# generally this includes a string value with the timezone +# in / format. For services that return data which +# does not follow the conventions of "suitable data" described +# below, *selector* may be used to pick different data. +# +# Suitable JSON data looks like +# ``` +# {"time_zone":"America/New_York"} +# ``` +# Suitable XML data looks like +# ``` +# Europe/Brussels +# ``` +# +# To accommodate providers of GeoIP timezone data with peculiar timezone +# naming conventions, the following cleanups are performed automatically: +# - backslashes are removed +# - spaces are replaced with _ +# +# To disable GeoIP checking, either comment-out the entire geoip section, +# or set the *style* key to an unsupported format (e.g. `none`). +# Also, note the analogous feature in src/modules/welcome/welcome.conf. +# +geoip: + style: "json" + url: "https://geoip.kde.org/v1/calamares" + selector: "" # leave blank for the default + +# For testing purposes, you could use *fixed* style, to see how Calamares +# behaves in a particular zone: +# +# geoip: +# style: "fixed" +# url: "https://geoip.kde.org/v1/calamares" # Still needs to be valid! +# selector: "America/Vancouver" # this is the selected zone +# diff --git a/airootfs/etc/calamares/modules/localeq.conf b/airootfs/etc/calamares/modules/localeq.conf new file mode 100644 index 0000000..bb2a7e8 --- /dev/null +++ b/airootfs/etc/calamares/modules/localeq.conf @@ -0,0 +1,100 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +# This settings are used to set your default system time zone. +# Time zones are usually located under /usr/share/zoneinfo and +# provided by the 'tzdata' package of your Distribution. +# +# Distributions using systemd can list available +# time zones by using the timedatectl command. +# timedatectl list-timezones +# +# The starting timezone (e.g. the pin-on-the-map) when entering +# the locale page can be set through keys *region* and *zone*. +# If either is not set, defaults to America/New_York. +# +region: "America" +zone: "New_York" + + +# System locales are detected in the following order: +# +# - /usr/share/i18n/SUPPORTED +# - localeGenPath (defaults to /etc/locale.gen if not set) +# - 'locale -a' output +# +# Enable only when your Distribution is using an +# custom path for locale.gen +# +#localeGenPath: "PATH_TO/locale.gen" + +# GeoIP based Language settings: Leave commented out to disable GeoIP. +# +# GeoIP needs a working Internet connection. +# This can be managed from `welcome.conf` by adding +# internet to the list of required conditions. +# +# The configuration +# is in three parts: a *style*, which can be "json" or "xml" +# depending on the kind of data returned by the service, and +# a *url* where the data is retrieved, and an optional *selector* +# to pick the right field out of the returned data (e.g. field +# name in JSON or element name in XML). +# +# The default selector (when the setting is blank) is picked to +# work with existing JSON providers (which use "time_zone") and +# Ubiquity's XML providers (which use "TimeZone"). +# +# If the service configured via *url* uses +# a different attribute name (e.g. "timezone") in JSON or a +# different element tag (e.g. "") in XML, set this +# string to the name or tag to be used. +# +# In JSON: +# - if the string contains "." characters, this is used as a +# multi-level selector, e.g. "a.b" will select the timezone +# from data "{a: {b: "Europe/Amsterdam" } }". +# - each part of the string split by "." characters is used as +# a key into the JSON data. +# In XML: +# - all elements with the named tag (e.g. all TimeZone) elements +# from the document are checked; the first one with non-empty +# text value is used. +# +# +# An HTTP(S) request is made to *url*. The request should return +# valid data in a suitable format, depending on *style*; +# generally this includes a string value with the timezone +# in / format. For services that return data which +# does not follow the conventions of "suitable data" described +# below, *selector* may be used to pick different data. +# +# Note that this example URL works, but the service is shutting +# down in June 2018. +# +# Suitable JSON data looks like +# ``` +# {"time_zone":"America/New_York"} +# ``` +# Suitable XML data looks like +# ``` +# Europe/Brussels +# ``` +# +# To accommodate providers of GeoIP timezone data with peculiar timezone +# naming conventions, the following cleanups are performed automatically: +# - backslashes are removed +# - spaces are replaced with _ +# +# Legacy settings "geoipStyle", "geoipUrl" and "geoipSelector" +# in the top-level are still supported, but I'd advise against. +# +# To disable GeoIP checking, either comment-out the entire geoip section, +# or set the *style* key to an unsupported format (e.g. `none`). +# Also, note the analogous feature in src/modules/welcome/welcome.conf. +# +geoip: + style: "json" + url: "https://geoip.kde.org/v1/calamares" + selector: "" # leave blank for the default diff --git a/airootfs/etc/calamares/modules/luksbootkeyfile.conf b/airootfs/etc/calamares/modules/luksbootkeyfile.conf new file mode 100644 index 0000000..477d0e3 --- /dev/null +++ b/airootfs/etc/calamares/modules/luksbootkeyfile.conf @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Luksbootkeyfile configuration. A key file is created for the +# LUKS encrypted devices. +--- +# Set Password-Based Key Derivation Function (PBKDF) algorithm +# for LUKS keyslot. +# +# There are three usable specific values: pbkdf2, argon2i or argon2id. +# There is one value equivalent to not setting it: default +# +# When not set (or explicitly set to "default"), the cryptsetup default is used +luks2Hash: default diff --git a/airootfs/etc/calamares/modules/luksopenswaphookcfg.conf b/airootfs/etc/calamares/modules/luksopenswaphookcfg.conf new file mode 100644 index 0000000..f1f03bb --- /dev/null +++ b/airootfs/etc/calamares/modules/luksopenswaphookcfg.conf @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Writes an openswap configuration with LUKS settings to the given path +--- +# Path of the configuration file to write (in the target system) +configFilePath: /etc/openswap.conf diff --git a/airootfs/etc/calamares/modules/machineid.conf b/airootfs/etc/calamares/modules/machineid.conf new file mode 100644 index 0000000..6a45234 --- /dev/null +++ b/airootfs/etc/calamares/modules/machineid.conf @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Machine-ID and other random data on the target system. +# +# This module can create a number of "random" things on the target: +# - a systemd machine-id file (hence the name of the Calamares module) +# with a random UUID. +# - a dbus machine-id file (or, optionally, link to the one from systemd) +# - an entropy file +# +--- +# Whether to create /etc/machine-id for systemd. +# The default is *false*. +systemd: true +# If systemd is true, the kind of /etc/machine-id to create in the target +# - uuid (default) generates a UUID +# - systemd alias of uuid +# - blank creates the file but leaves it empty at 0 bytes +# - none alias of blank (use `systemd: false` if you don't want one at all) +# - literal-uninitialized creates the file and writes the string "uninitialized\n" +systemd-style: uuid + +# Whether to create /var/lib/dbus/machine-id for D-Bus. +# The default is *false*. +dbus: true +# Whether /var/lib/dbus/machine-id should be a symlink to /etc/machine-id +# (ignored if dbus is false, or if there is no /etc/machine-id to point to). +# The default is *false*. +dbus-symlink: true + +# Copy entropy from the host? If this is set to *true*, then +# any entropy file listed below will be copied from the host +# if it exists. Non-existent files will be generated from +# /dev/urandom . The default is *false*. +entropy-copy: false +# Which files to write (paths in the target). Each of these files is +# either generated from /dev/urandom or copied from the host, depending +# on the setting for *entropy-copy*, above. +entropy-files: + - /var/lib/urandom/random-seed + - /var/lib/systemd/random-seed + +# Whether to create an entropy file /var/lib/urandom/random-seed +# +# DEPRECATED: list the file in entropy-files instead. If this key +# exists and is set to *true*, a warning is printed and Calamares +# behaves as if `/var/lib/urandom/random-seed` is listed in *entropy-files*. +# +# entropy: false + +# Whether to create a symlink for D-Bus +# +# DEPRECATED: set *dbus-symlink* with the same meaning instead. +# +# symlink: false diff --git a/airootfs/etc/calamares/modules/mobile/CMakeLists.txt b/airootfs/etc/calamares/modules/mobile/CMakeLists.txt new file mode 100644 index 0000000..68815e3 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2020 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later + +calamares_add_plugin( mobile + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + Config.cpp + MobileQmlViewStep.cpp + PartitionJob.cpp + UsersJob.cpp + RESOURCES + mobile.qrc + SHARED_LIB +) diff --git a/airootfs/etc/calamares/modules/mobile/Config.cpp b/airootfs/etc/calamares/modules/mobile/Config.cpp new file mode 100644 index 0000000..93bbfd8 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/Config.cpp @@ -0,0 +1,221 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "Config.h" +#include "PartitionJob.h" +#include "UsersJob.h" + +#include "ViewManager.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include + +Config::Config( QObject* parent ) + : QObject( parent ) +{ +} + +void +Config::setConfigurationMap( const QVariantMap& cfgMap ) +{ + using namespace Calamares; + + if ( getBool( cfgMap, "bogus", false ) ) + { + cWarning() << "Configuration key \"bogus\" is still set for *mobile*"; + } + + m_osName = getString( cfgMap, "osName", "(unknown)" ); + m_arch = getString( cfgMap, "arch", "(unknown)" ); + m_device = getString( cfgMap, "device", "(unknown)" ); + m_userInterface = getString( cfgMap, "userInterface", "(unknown)" ); + m_version = getString( cfgMap, "version", "(unknown)" ); + + m_reservedUsernames = getStringList( cfgMap, "reservedUsernames", QStringList { "adm", "at ", "bin", "colord", + "cron", "cyrus", "daemon", "ftp", "games", "geoclue", "guest", "halt", "lightdm", "lp", "mail", "man", + "messagebus", "news", "nobody", "ntp", "operator", "polkitd", "postmaster", "pulse", "root", "shutdown", + "smmsp", "squid", "sshd", "sync", "uucp", "vpopmail", "xfs" } ); + + // ensure m_cmdUsermod matches m_username + m_username = getString( cfgMap, "username", "user" ); + m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true ); + + m_builtinVirtualKeyboard = getBool( cfgMap, "builtinVirtualKeyboard", true ); + + m_featureSshd = getBool( cfgMap, "featureSshd", true ); + m_featureFsType = getBool( cfgMap, "featureFsType", false ); + + m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" ); + m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" ); + m_cmdMount = getString( cfgMap, "cmdMount", "mount" ); + m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" ); + m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" ); + + m_cmdMkfsRootBtrfs = getString( cfgMap, "cmdMkfsRootBtrfs", "mkfs.btrfs -L 'unknownOS_root'" ); + m_cmdMkfsRootExt4 = getString( cfgMap, "cmdMkfsRootExt4", "mkfs.ext4 -L 'unknownOS_root'" ); + m_cmdMkfsRootF2fs = getString( cfgMap, "cmdMkfsRootF2fs", "mkfs.f2fs -l 'unknownOS_root'" ); + m_fsList = getStringList( cfgMap, "fsModel", QStringList { "ext4", "f2fs", "btrfs" } ); + m_defaultFs = getString( cfgMap, "defaultFs", "ext4" ); + m_fsIndex = m_fsList.indexOf( m_defaultFs ); + m_fsType = m_defaultFs; + + m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" ); + m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" ); + m_cmdUsermod = getString( cfgMap, "cmdUsermod", "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user"); + + m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" ); + m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" ); + m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" ); +} + +Calamares::JobList +Config::createJobs() +{ + QList< Calamares::job_ptr > list; + QString cmdSshd = m_isSshEnabled ? m_cmdSshdEnable : m_cmdSshdDisable; + + /* Put users job in queue (should run after unpackfs) */ + Calamares::Job* j = new UsersJob( m_featureSshd, + m_cmdPasswd, + m_cmdUsermod, + cmdSshd, + m_cmdSshdUseradd, + m_isSshEnabled, + m_username, + m_userPassword, + m_sshdUsername, + m_sshdPassword ); + list.append( Calamares::job_ptr( j ) ); + + return list; +} + +void +Config::runPartitionJobThenLeave( bool b ) +{ + Calamares::ViewManager* v = Calamares::ViewManager::instance(); + QString cmdMkfsRoot; + if ( m_fsType == QStringLiteral( "btrfs" ) ) + { + cmdMkfsRoot = m_cmdMkfsRootBtrfs; + } + else if ( m_fsType == QStringLiteral( "f2fs" ) ) + { + cmdMkfsRoot = m_cmdMkfsRootF2fs; + } + else if ( m_fsType == QStringLiteral( "ext4" ) ) + { + cmdMkfsRoot = m_cmdMkfsRootExt4; + } + else + { + v->onInstallationFailed( "Unknown filesystem: '" + m_fsType + "'", "" ); + } + /* HACK: run partition job + * The "mobile" module has two jobs, the partition job and the users job. + * If we added both of them in Config::createJobs(), Calamares would run + * them right after each other. But we need the "unpackfs" module to run + * inbetween, that's why as workaround, the partition job is started here. + * To solve this properly, we would need to place the partition job in an + * own module and pass everything via globalstorage. But then we might as + * well refactor everything so we can unify the mobile's partition job with + * the proper partition job from Calamares. */ + Calamares::Job* j = new PartitionJob( m_cmdInternalStoragePrepare, + m_cmdLuksFormat, + m_cmdLuksOpen, + cmdMkfsRoot, + m_cmdMount, + m_targetDeviceRoot, + m_targetDeviceRootInternal, + m_installFromExternalToInternal, + m_isFdeEnabled, + m_fdePassword ); + Calamares::JobResult res = j->exec(); + + if ( res ) + { + v->next(); + } + else + { + v->onInstallationFailed( res.message(), res.details() ); + } +} + +void +Config::setUsername( const QString& username ) +{ + m_username = username; + emit usernameChanged( m_username ); +} + +void +Config::setUserPassword( const QString& userPassword ) +{ + m_userPassword = userPassword; + emit userPasswordChanged( m_userPassword ); +} + +void +Config::setSshdUsername( const QString& sshdUsername ) +{ + m_sshdUsername = sshdUsername; + emit sshdUsernameChanged( m_sshdUsername ); +} + +void +Config::setSshdPassword( const QString& sshdPassword ) +{ + m_sshdPassword = sshdPassword; + emit sshdPasswordChanged( m_sshdPassword ); +} + +void +Config::setIsSshEnabled( const bool isSshEnabled ) +{ + m_isSshEnabled = isSshEnabled; +} + +void +Config::setFdePassword( const QString& fdePassword ) +{ + m_fdePassword = fdePassword; + emit fdePasswordChanged( m_fdePassword ); +} + +void +Config::setIsFdeEnabled( const bool isFdeEnabled ) +{ + m_isFdeEnabled = isFdeEnabled; +} + +void +Config::setInstallFromExternalToInternal( const bool val ) +{ + m_installFromExternalToInternal = val; +} + +void +Config::setFsType( int idx ) +{ + if ( idx >= 0 && idx < m_fsList.length() ) + { + setFsType( m_fsList[ idx ] ); + } +} +void +Config::setFsType( const QString& fsType ) +{ + if ( fsType != m_fsType ) + { + m_fsType = fsType; + emit fsTypeChanged( m_fsType ); + } +} + +void +Config::setFsIndex( const int fsIndex ) +{ + m_fsIndex = fsIndex; + emit fsIndexChanged( m_fsIndex ); +} diff --git a/airootfs/etc/calamares/modules/mobile/Config.h b/airootfs/etc/calamares/modules/mobile/Config.h new file mode 100644 index 0000000..3d77561 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/Config.h @@ -0,0 +1,207 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#pragma once + +#include "Job.h" + +#include +#include + +class Config : public QObject +{ + Q_OBJECT + /* installer UI */ + Q_PROPERTY( bool builtinVirtualKeyboard READ builtinVirtualKeyboard CONSTANT FINAL ) + + /* welcome */ + Q_PROPERTY( QString osName READ osName CONSTANT FINAL ) + Q_PROPERTY( QString arch READ arch CONSTANT FINAL ) + Q_PROPERTY( QString device READ device CONSTANT FINAL ) + Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL ) + Q_PROPERTY( QString version READ version CONSTANT FINAL ) + + /* reserved usernames (user_pass, ssh_credentials )*/ + Q_PROPERTY( QStringList reservedUsernames READ reservedUsernames CONSTANT FINAL ) + + /* default user */ + Q_PROPERTY( QString username READ username WRITE setUsername NOTIFY usernameChanged ) + Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged ) + Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL ) + + /* ssh server + credentials */ + Q_PROPERTY( bool featureSshd READ featureSshd CONSTANT FINAL ) + Q_PROPERTY( QString sshdUsername READ sshdUsername WRITE setSshdUsername NOTIFY sshdUsernameChanged ) + Q_PROPERTY( QString sshdPassword READ sshdPassword WRITE setSshdPassword NOTIFY sshdPasswordChanged ) + Q_PROPERTY( bool isSshEnabled READ isSshEnabled WRITE setIsSshEnabled ) + + /* full disk encryption */ + Q_PROPERTY( QString fdePassword READ fdePassword WRITE setFdePassword NOTIFY fdePasswordChanged ) + Q_PROPERTY( bool isFdeEnabled READ isFdeEnabled WRITE setIsFdeEnabled ) + + /* filesystem selection */ + Q_PROPERTY( QString fsType READ fsType WRITE setFsType NOTIFY fsTypeChanged ) + Q_PROPERTY( bool featureFsType READ featureFsType CONSTANT FINAL ) + Q_PROPERTY( QStringList fsList READ fsList CONSTANT FINAL ) + Q_PROPERTY( QString defaultFs READ defaultFs CONSTANT FINAL ) + Q_PROPERTY( int fsIndex READ fsIndex WRITE setFsIndex NOTIFY fsIndexChanged ) + + /* partition job */ + Q_PROPERTY( bool runPartitionJobThenLeave READ runPartitionJobThenLeaveDummy WRITE runPartitionJobThenLeave ) + Q_PROPERTY( QString cmdInternalStoragePrepare READ cmdInternalStoragePrepare CONSTANT FINAL ) + Q_PROPERTY( QString cmdLuksFormat READ cmdLuksFormat CONSTANT FINAL ) + Q_PROPERTY( QString cmdLuksOpen READ cmdLuksOpen CONSTANT FINAL ) + Q_PROPERTY( QString cmdMount READ cmdMount CONSTANT FINAL ) + Q_PROPERTY( QString targetDeviceRoot READ targetDeviceRoot CONSTANT FINAL ) + Q_PROPERTY( QString targetDeviceRootInternal READ targetDeviceRootInternal CONSTANT FINAL ) + Q_PROPERTY( + bool installFromExternalToInternal READ installFromExternalToInternal WRITE setInstallFromExternalToInternal ) + + /* users job */ + Q_PROPERTY( QString cmdSshdEnable READ cmdSshdEnable CONSTANT FINAL ) + Q_PROPERTY( QString cmdSshdDisable READ cmdSshdDisable CONSTANT FINAL ) + +public: + Config( QObject* parent = nullptr ); + void setConfigurationMap( const QVariantMap& ); + Calamares::JobList createJobs(); + + /* installer UI */ + bool builtinVirtualKeyboard() { return m_builtinVirtualKeyboard; } + + /* welcome */ + QString osName() const { return m_osName; } + QString arch() const { return m_arch; } + QString device() const { return m_device; } + QString userInterface() const { return m_userInterface; } + QString version() const { return m_version; } + + /* reserved usernames (user_pass, ssh_credentials) */ + QStringList reservedUsernames() const { return m_reservedUsernames; }; + + /* user */ + QString username() const { return m_username; } + QString userPassword() const { return m_userPassword; } + void setUsername( const QString& username ); + void setUserPassword( const QString& userPassword ); + bool userPasswordNumeric() const { return m_userPasswordNumeric; } + + /* ssh server + credentials */ + bool featureSshd() { return m_featureSshd; } + QString sshdUsername() const { return m_sshdUsername; } + QString sshdPassword() const { return m_sshdPassword; } + bool isSshEnabled() { return m_isSshEnabled; } + void setSshdUsername( const QString& sshdUsername ); + void setSshdPassword( const QString& sshdPassword ); + void setIsSshEnabled( bool isSshEnabled ); + + /* full disk encryption */ + QString fdePassword() const { return m_fdePassword; } + bool isFdeEnabled() { return m_isFdeEnabled; } + void setFdePassword( const QString& fdePassword ); + void setIsFdeEnabled( bool isFdeEnabled ); + + /* filesystem selection */ + bool featureFsType() { return m_featureFsType; }; + QString fsType() const { return m_fsType; }; + void setFsType( int idx ); + void setFsType( const QString& fsType ); + QStringList fsList() const { return m_fsList; }; + int fsIndex() const { return m_fsIndex; }; + void setFsIndex( const int fsIndex ); + QString defaultFs() const { return m_defaultFs; }; + + /* partition job */ + bool runPartitionJobThenLeaveDummy() { return 0; } + void runPartitionJobThenLeave( bool b ); + QString cmdInternalStoragePrepare() const { return m_cmdInternalStoragePrepare; } + QString cmdLuksFormat() const { return m_cmdLuksFormat; } + QString cmdLuksOpen() const { return m_cmdLuksOpen; } + QString cmdMkfsRootBtrfs() const { return m_cmdMkfsRootBtrfs; } + QString cmdMkfsRootExt4() const { return m_cmdMkfsRootExt4; } + QString cmdMkfsRootF2fs() const { return m_cmdMkfsRootF2fs; } + QString cmdMount() const { return m_cmdMount; } + QString targetDeviceRoot() const { return m_targetDeviceRoot; } + QString targetDeviceRootInternal() const { return m_targetDeviceRootInternal; } + bool installFromExternalToInternal() { return m_installFromExternalToInternal; } + void setInstallFromExternalToInternal( const bool val ); + + /* users job */ + QString cmdPasswd() const { return m_cmdPasswd; } + QString cmdUsermod() const { return m_cmdUsermod; } + QString cmdSshdEnable() const { return m_cmdSshdEnable; } + QString cmdSshdDisable() const { return m_cmdSshdDisable; } + QString cmdSshdUseradd() const { return m_cmdSshdUseradd; } + +private: + /* installer UI */ + bool m_builtinVirtualKeyboard; + + /* welcome */ + QString m_osName; + QString m_arch; + QString m_device; + QString m_userInterface; + QString m_version; + + /* reserved usernames (user_pass, ssh_credentials) */ + QStringList m_reservedUsernames; + + /* default user */ + QString m_username; + QString m_userPassword; + bool m_userPasswordNumeric; + + /* ssh server + credentials */ + bool m_featureSshd = false; + QString m_sshdUsername; + QString m_sshdPassword; + bool m_isSshEnabled = false; + + /* full disk encryption */ + QString m_fdePassword; + bool m_isFdeEnabled = false; + + /* filesystem selection */ + bool m_featureFsType = false; + QString m_defaultFs; + QString m_fsType; + // Index of the currently selected filesystem in UI. + int m_fsIndex = -1; + QStringList m_fsList; + + /* partition job */ + QString m_cmdInternalStoragePrepare; + QString m_cmdLuksFormat; + QString m_cmdLuksOpen; + QString m_cmdMkfsRootBtrfs; + QString m_cmdMkfsRootExt4; + QString m_cmdMkfsRootF2fs; + QString m_cmdMount; + QString m_targetDeviceRoot; + QString m_targetDeviceRootInternal; + bool m_installFromExternalToInternal = false; + + /* users job */ + QString m_cmdPasswd; + QString m_cmdUsermod; + QString m_cmdSshdEnable; + QString m_cmdSshdDisable; + QString m_cmdSshdUseradd; + +signals: + /* booleans we don't read from QML (like isSshEnabled) don't need a signal */ + + /* default user */ + void userPasswordChanged( QString userPassword ); + void usernameChanged( QString username ); + + /* ssh server + credentials */ + void sshdUsernameChanged( QString sshdUsername ); + void sshdPasswordChanged( QString sshdPassword ); + + /* full disk encryption */ + void fdePasswordChanged( QString fdePassword ); + + void fsTypeChanged( QString fsType ); + void fsIndexChanged( int fsIndex ); +}; diff --git a/airootfs/etc/calamares/modules/mobile/MobileQmlViewStep.cpp b/airootfs/etc/calamares/modules/mobile/MobileQmlViewStep.cpp new file mode 100644 index 0000000..a4ac96f --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/MobileQmlViewStep.cpp @@ -0,0 +1,73 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "MobileQmlViewStep.h" + +#include "Branding.h" +#include "GlobalStorage.h" +#include "locale/TranslationsModel.h" +#include "modulesystem/ModuleManager.h" +#include "utils/Dirs.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include + +CALAMARES_PLUGIN_FACTORY_DEFINITION( MobileQmlViewStepFactory, registerPlugin< MobileQmlViewStep >(); ) + +void +MobileQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + m_config->setConfigurationMap( configurationMap ); + Calamares::QmlViewStep::setConfigurationMap( configurationMap ); +} + +MobileQmlViewStep::MobileQmlViewStep( QObject* parent ) + : Calamares::QmlViewStep( parent ) + , m_config( new Config( this ) ) +{ +} + +void +MobileQmlViewStep::onLeave() +{ + return; +} + +bool +MobileQmlViewStep::isNextEnabled() const +{ + return false; +} + +bool +MobileQmlViewStep::isBackEnabled() const +{ + return false; +} + + +bool +MobileQmlViewStep::isAtBeginning() const +{ + return true; +} + + +bool +MobileQmlViewStep::isAtEnd() const +{ + return true; +} + + +Calamares::JobList +MobileQmlViewStep::jobs() const +{ + return m_config->createJobs(); +} + +QObject* +MobileQmlViewStep::getConfig() +{ + return m_config; +} diff --git a/airootfs/etc/calamares/modules/mobile/MobileQmlViewStep.h b/airootfs/etc/calamares/modules/mobile/MobileQmlViewStep.h new file mode 100644 index 0000000..a595680 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/MobileQmlViewStep.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef PARTITION_QMLVIEWSTEP_H +#define PARTITION_QMLVIEWSTEP_H +#include "Config.h" + +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" + +#include + +#include +#include + +class PLUGINDLLEXPORT MobileQmlViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + explicit MobileQmlViewStep( QObject* parent = nullptr ); + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + bool isAtBeginning() const override; + bool isAtEnd() const override; + + Calamares::JobList jobs() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + void onLeave() override; + QObject* getConfig() override; + +private: + Config* m_config; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( MobileQmlViewStepFactory ) + +#endif // PARTITION_QMLVIEWSTEP_H diff --git a/airootfs/etc/calamares/modules/mobile/PartitionJob.cpp b/airootfs/etc/calamares/modules/mobile/PartitionJob.cpp new file mode 100644 index 0000000..ecb6ad9 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/PartitionJob.cpp @@ -0,0 +1,136 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "PartitionJob.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" +#include "utils/System.h" +#include "utils/Logger.h" + +#include +#include + + +PartitionJob::PartitionJob( const QString& cmdInternalStoragePrepare, + const QString& cmdLuksFormat, + const QString& cmdLuksOpen, + const QString& cmdMkfsRoot, + const QString& cmdMount, + const QString& targetDeviceRoot, + const QString& targetDeviceRootInternal, + bool installFromExternalToInternal, + bool isFdeEnabled, + const QString& password ) + : Calamares::Job() + , m_cmdInternalStoragePrepare( cmdInternalStoragePrepare ) + , m_cmdLuksFormat( cmdLuksFormat ) + , m_cmdLuksOpen( cmdLuksOpen ) + , m_cmdMkfsRoot( cmdMkfsRoot ) + , m_cmdMount( cmdMount ) + , m_targetDeviceRoot( targetDeviceRoot ) + , m_targetDeviceRootInternal( targetDeviceRootInternal ) + , m_installFromExternalToInternal( installFromExternalToInternal ) + , m_isFdeEnabled( isFdeEnabled ) + , m_password( password ) +{ +} + + +QString +PartitionJob::prettyName() const +{ + return "Creating and formatting installation partition"; +} + +/* Fill the "global storage", so the following jobs (like unsquashfs) work. + The code is similar to modules/partition/jobs/FillGlobalStorageJob.cpp in + Calamares. */ +void +FillGlobalStorage( const QString device, const QString pathMount ) +{ + using namespace Calamares; + + GlobalStorage* gs = JobQueue::instance()->globalStorage(); + QVariantList partitions; + QVariantMap partition; + + /* See mapForPartition() in FillGlobalStorageJob.cpp */ + partition[ "device" ] = device; + partition[ "mountPoint" ] = "/"; + partition[ "claimed" ] = true; + + /* Ignored by calamares modules used in combination with the "mobile" + * module, so we can get away with leaving them empty for now. */ + partition[ "uuid" ] = ""; + partition[ "fsName" ] = ""; + partition[ "fs" ] = ""; + + partitions << partition; + gs->insert( "partitions", partitions ); + gs->insert( "rootMountPoint", pathMount ); +} + +Calamares::JobResult +PartitionJob::exec() +{ + using namespace Calamares; + using namespace Calamares; + using namespace std; + + const QString pathMount = "/mnt/install"; + const QString cryptName = "calamares_crypt"; + QString cryptDev = "/dev/mapper/" + cryptName; + QString passwordStdin = m_password + "\n"; + QString dev = m_targetDeviceRoot; + QList< QPair< QStringList, QString > > commands = {}; + + if ( m_installFromExternalToInternal ) + { + dev = m_targetDeviceRootInternal; + + commands.append( { + { { "sh", "-c", m_cmdInternalStoragePrepare }, QString() }, + } ); + } + + commands.append( { { { "mkdir", "-p", pathMount }, QString() } } ); + + if ( m_isFdeEnabled ) + { + commands.append( { + { { "sh", "-c", m_cmdLuksFormat + " " + dev }, passwordStdin }, + { { "sh", "-c", m_cmdLuksOpen + " " + dev + " " + cryptName }, passwordStdin }, + { { "sh", "-c", m_cmdMkfsRoot + " " + cryptDev }, QString() }, + { { "sh", "-c", m_cmdMount + " " + cryptDev + " " + pathMount }, QString() }, + } ); + } + else + { + commands.append( { { { "sh", "-c", m_cmdMkfsRoot + " " + dev }, QString() }, + { { "sh", "-c", m_cmdMount + " " + dev + " " + pathMount }, QString() } } ); + } + + foreach ( auto command, commands ) + { + const QStringList args = command.first; + const QString stdInput = command.second; + const QString pathRoot = "/"; + + ProcessResult res + = System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 600 ) ); + if ( res.getExitCode() ) + { + return JobResult::error( "Command failed:

" + "'" + + args.join( " " ) + + "'

" + " with output:

" + "'" + + res.getOutput() + "'" ); + } + } + + FillGlobalStorage( m_isFdeEnabled ? cryptDev : dev, pathMount ); + return JobResult::ok(); +} diff --git a/airootfs/etc/calamares/modules/mobile/PartitionJob.h b/airootfs/etc/calamares/modules/mobile/PartitionJob.h new file mode 100644 index 0000000..e265abd --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/PartitionJob.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#pragma once +#include "Job.h" + + +class PartitionJob : public Calamares::Job +{ + Q_OBJECT +public: + PartitionJob( const QString& cmdInternalStoragePrepare, + const QString& cmdLuksFormat, + const QString& cmdLuksOpen, + const QString& cmdMkfsRoot, + const QString& cmdMount, + const QString& targetDeviceRoot, + const QString& targetDeviceRootInternal, + bool installFromExternalToInternal, + bool isFdeEnabled, + const QString& password ); + + QString prettyName() const override; + Calamares::JobResult exec() override; + + Calamares::JobList createJobs(); + +private: + QString m_cmdInternalStoragePrepare; + QString m_cmdLuksFormat; + QString m_cmdLuksOpen; + QString m_cmdMkfsRoot; + QString m_cmdMount; + QString m_targetDeviceRoot; + QString m_targetDeviceRootInternal; + bool m_installFromExternalToInternal; + bool m_isFdeEnabled; + QString m_password; +}; diff --git a/airootfs/etc/calamares/modules/mobile/UsersJob.cpp b/airootfs/etc/calamares/modules/mobile/UsersJob.cpp new file mode 100644 index 0000000..7d637d6 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/UsersJob.cpp @@ -0,0 +1,92 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "UsersJob.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" +#include "utils/System.h" +#include "utils/Logger.h" + +#include +#include + + +UsersJob::UsersJob( bool featureSshd, + const QString& cmdPasswd, + const QString& cmdUsermod, + const QString& cmdSshd, + const QString& cmdSshdUseradd, + bool isSshEnabled, + const QString& username, + const QString& password, + const QString& sshdUsername, + const QString& sshdPassword ) + : Calamares::Job() + , m_featureSshd( featureSshd ) + , m_cmdPasswd( cmdPasswd ) + , m_cmdUsermod( cmdUsermod ) + , m_cmdSshd( cmdSshd ) + , m_cmdSshdUseradd( cmdSshdUseradd ) + , m_isSshEnabled( isSshEnabled ) + , m_username( username ) + , m_password( password ) + , m_sshdUsername( sshdUsername ) + , m_sshdPassword( sshdPassword ) +{ +} + + +QString +UsersJob::prettyName() const +{ + return "Configuring users"; +} + +Calamares::JobResult +UsersJob::exec() +{ + using namespace Calamares; + using namespace Calamares; + using namespace std; + + QList< QPair< QStringList, QString > > commands = { + { { "sh", "-c", m_cmdUsermod }, m_username + "\n" } + }; + + commands.append( { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" } ); + + if ( m_featureSshd ) + { + commands.append( { { "sh", "-c", m_cmdSshd }, QString() } ); + + if ( m_isSshEnabled ) + { + commands.append( { { "sh", "-c", m_cmdSshdUseradd + " " + m_sshdUsername }, QString() } ); + commands.append( + { { "sh", "-c", m_cmdPasswd + " " + m_sshdUsername }, m_sshdPassword + "\n" + m_sshdPassword + "\n" } ); + } + } + + foreach ( auto command, commands ) + { + auto location = System::RunLocation::RunInTarget; + const QString pathRoot = "/"; + const QStringList args = command.first; + const QString stdInput = command.second; + + ProcessResult res = System::runCommand( location, args, pathRoot, stdInput, chrono::seconds( 30 ) ); + if ( res.getExitCode() ) + { + return JobResult::error( "Command failed:

" + "'" + + args.join( " " ) + + "'

" + " with output:

" + "'" + + res.getOutput() + "'" ); + } + } + + return JobResult::ok(); +} diff --git a/airootfs/etc/calamares/modules/mobile/UsersJob.h b/airootfs/etc/calamares/modules/mobile/UsersJob.h new file mode 100644 index 0000000..8c4b285 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/UsersJob.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#pragma once +#include "Job.h" + + +class UsersJob : public Calamares::Job +{ + Q_OBJECT +public: + UsersJob( bool featureSshd, + const QString& cmdPasswd, + const QString& cmdUsermod, + const QString& cmdSshd, + const QString& cmdSshdUseradd, + bool isSshEnabled, + const QString& username, + const QString& password, + const QString& sshdUsername, + const QString& sshdPassword ); + + QString prettyName() const override; + Calamares::JobResult exec() override; + + Calamares::JobList createJobs(); + +private: + bool m_featureSshd; + QString m_cmdPasswd; + QString m_cmdUsermod; + QString m_cmdSshd; + QString m_cmdSshdUseradd; + bool m_isSshEnabled; + QString m_username; + QString m_password; + QString m_sshdUsername; + QString m_sshdPassword; +}; diff --git a/airootfs/etc/calamares/modules/mobile/fde_confirm.qml b/airootfs/etc/calamares/modules/mobile/fde_confirm.qml new file mode 100644 index 0000000..94ceac2 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/fde_confirm.qml @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "To protect your data in case your device gets stolen," + + " it is recommended to enable full disk encryption.
" + + "
" + + "If you enable full disk encryption, you will be asked for" + + " a password. Without this password, it is not possible to" + + " boot your device or access any data on it. Make sure that" + + " you don't lose this password!" + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Enable") + onClicked: { + config.isFdeEnabled = true; + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Disable") + onClicked: { + config.isFdeEnabled = false; + navNextFeature(); + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/fde_pass.qml b/airootfs/etc/calamares/modules/mobile/fde_pass.qml new file mode 100644 index 0000000..9806897 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/fde_pass.qml @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + TextField { + id: password + anchors.top: parent.top + placeholderText: qsTr("Password") + inputMethodHints: Qt.ImhPreferLowercase + echoMode: TextInput.Password + onTextChanged: validatePassword(password, passwordRepeat, + errorText) + text: config.fdePassword + + onActiveFocusChanged: { + if(activeFocus) { + Qt.inputMethod.update(Qt.ImQueryInput); + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + TextField { + id: passwordRepeat + anchors.top: password.bottom + placeholderText: qsTr("Password (repeat)") + echoMode: TextInput.Password + onTextChanged: validatePassword(password, passwordRepeat, + errorText) + text: config.fdePassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Text { + id: errorText + anchors.top: passwordRepeat.bottom + visible: false + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + wrapMode: Text.WordWrap + } + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: errorText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: { + if (validatePassword(password, passwordRepeat, errorText)) { + config.fdePassword = password.text; + navNext(); + } + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/fs_selection.qml b/airootfs/etc/calamares/modules/mobile/fs_selection.qml new file mode 100644 index 0000000..5924b85 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/fs_selection.qml @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2020 Undef + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "Select the filesystem for root partition. If unsure, leave the default." + + width: 200 + } + + ComboBox { + id: fsTypeCB + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 150 + height: 30 + editable: false + model: config.fsList + /* Save the current state on selection so it is there when the back button is pressed */ + onActivated: config.fsType = fsTypeCB.currentText; + Component.onCompleted: fsTypeCB.currentIndex = find( config.fsType, Qt.MatchContains ); + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: fsTypeCB.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: { + config.fsType = fsTypeCB.currentText; + navNextFeature(); + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/install_confirm.qml b/airootfs/etc/calamares/modules/mobile/install_confirm.qml new file mode 100644 index 0000000..2e2bdd8 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/install_confirm.qml @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: (function() { + var ret = "Once you hit 'install', the installation will begin." + + " It will typically take a few minutes. Do not power off the" + + " device until it is done.
"; + + if (config.installFromExternalToInternal) { + ret += "After the installation, your device will shutdown" + + " automatically. You must remove the external storage" + + " (SD card) before booting again." + + "

" + + "Otherwise, your device will boot into the installer" + + " again, and not into the installed system." + } else { + ret += "Afterwards, it will reboot into the installed system."; + } + + return ret; + }()) + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Install") + onClicked: navFinish() + } +} diff --git a/airootfs/etc/calamares/modules/mobile/install_target.qml b/airootfs/etc/calamares/modules/mobile/install_target.qml new file mode 100644 index 0000000..e15c4ff --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/install_target.qml @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "The installation was started from an external storage medium." + + "
" + + "You can either install to the same medium and overwrite the" + + " installer, or install to the internal storage.
" + + "
" + + "Where would you like to install " + config.osName + "?" + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Internal (eMMC)") + onClicked: { + config.installFromExternalToInternal = true; + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("External (SD card)") + onClicked: { + config.installFromExternalToInternal = false; + navNextFeature(); + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/install_target_confirm.qml b/airootfs/etc/calamares/modules/mobile/install_target_confirm.qml new file mode 100644 index 0000000..4064329 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/install_target_confirm.qml @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "Are you sure that you want to overwrite the internal storage?" + + "

" + + "All existing data on the device will be lost!" + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Yes") + onClicked: { + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("No") + onClicked: { + navBack(); + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/mobile.conf b/airootfs/etc/calamares/modules/mobile/mobile.conf new file mode 100644 index 0000000..f2a1a9a --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/mobile.conf @@ -0,0 +1,173 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Commented out values are defaults. +# All commands are running with 'sh -c'. +--- +# This entry exists only to keep the tests happy, remove it in +# any production configuration. +bogus: true + +####### +### Target OS information +####### + +## Operating System Name +# osName: "(unknown)" + +## User Interface name (e.g. Plasma Mobile) +# userInterface: "(unknown)" + +## User Interface assumes that the password is numeric (as of writing, this is +## the case with Plasma Mobile and Phosh) +# userPasswordNumeric: true + +## OS version +# version: "(unknown)" + +## Default username (for which the password will be set) +## Ensure also cmdUsermod command matches the default user, so it can be changed if desired. +# username: "user" + +## reserved usernames (for user_pass username prompt and ssh_credentials) +# reservedUsernames: +# - adm +# - at +# - bin +# - colord +# - cron +# - cyrus +# - daemon +# - ftp +# - games +# - geoclue +# - guest +# - halt +# - lightdm +# - lp +# - mail +# - man +# - messagebus +# - news +# - nobody +# - ntp +# - operator +# - polkitd +# - postmaster +# - pulse +# - root +# - shutdown +# - smmsp +# - squid +# - sshd +# - sync +# - uucp +# - vpopmail +# - xfs + +####### +### Target device information +####### + +## Architecture (e.g. aarch64) +# arch: "(unknown)" + +## Name of the device (e.g. PinePhone) +# device: "(unknown)" + +## Partition that will be formatted and mounted (optionally with FDE) for the +## rootfs +# targetDeviceRoot: "/dev/unknown" + +## Partition that will be formatted and mounted (optionally with FDE) for the +## rootfs, on internal storage. The installer OS must not set this, if it was +## booted from the internal storage (this is not checked in the mobile +## module!). +## If this is set, the user gets asked whether they want to install on internal +## or external storage. If the user chose internal storage, +## cmdInternalStoragePrepare (see below) runs before this partition gets +## formatted (see below). A note is displayed, that the device is powered off +## after installation and that the user should remove the external storage +## medium. So you need to adjust the installer OS to poweroff in that case, and +## not reboot. See postmarketos-ondev.git for reference. +# targetDeviceRootInternal: "" + +###### +### Installer Features +###### + +## Ask whether sshd should be enabled or not. If enabled, add a dedicated ssh +## user with proper username and password and suggest to change to key-based +## authentication after installation. +# featureSshd: true + +## Ask the user, which filesystem to use. +# featureFsType: false +## Filesystems that the user can choose from. +#fsModel: +# - ext4 +# - f2fs +# - btrfs +## Default filesystem to display in the dialog. If featureFsType is disabled, +## this gets used without asking the user. +# defaultFs: ext4 + +## Start Qt's virtual keyboard within the mobile module. Disable if you bring +## your own virtual keyboard (e.g. svkbd). +# builtinVirtualKeyboard: true + +####### +### Commands running in the installer OS +####### + +## Format the target partition with LUKS +## Arguments: +## Stdin: password with \n +# cmdLuksFormat: "cryptsetup luksFormat --use-random" + +## Open the formatted partition +## Arguments: +## Stdin: password with \n +# cmdLuksOpen: "cryptsetup luksOpen" + +## Format the rootfs with a file system +## Arguments: +## Btrfs: to allow snapshots to work on the root subvolume, it is recommended that this +## command be a script which will create a subvolume and make it default +## An example can be found at: +## https://gitlab.com/mobian1/calamares-settings-mobian/-/merge_requests/2/diffs#diff-content-dde34f5f1c89e3dea63608c553bbc452dedf428f +# cmdMkfsRootBtrfs: "mkfs.btrfs -L 'unknownOS_root'" +# cmdMkfsRootExt4: "mkfs.ext4 -L 'unknownOS_root'" +# cmdMkfsRootF2fs: "mkfs.f2fs -l 'unknownOS_root'" + +## Mount the partition after formatting with file system +## Arguments: +# cmdMount: "mount" + +## When user selects installation from external storage to internal storage +## (see targetDeviceRootInternal above), use this command to prepare the +## internal storage medium. The command must create a partition table with +## two partitions (boot, root) and fill the boot partition. See the +## ondev-internal-storage-prepare.sh in postmarketos-ondev as example. +# cmdInternalStoragePrepare: "ondev-internal-storage-prepare" + +####### +### Commands running in the target OS (chroot) +####### + +## Change the username for the default user +## Stdin: username with \n +# cmdUsermod: "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user" + +## Set the password for default user and sshd user +## Arguments: +## Stdin: password twice, each time with \n +# cmdPasswd: "passwd" + +## Enable or disable sshd +# cmdSshdEnable: "systemctl enable sshd.service" +# cmdSshdDisable: "systemctl disable sshd.service" + +## Create the user for sshd +## Arguments: +# cmdSshdUseradd: "useradd -G wheel -m" diff --git a/airootfs/etc/calamares/modules/mobile/mobile.qml b/airootfs/etc/calamares/modules/mobile/mobile.qml new file mode 100644 index 0000000..18ef706 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/mobile.qml @@ -0,0 +1,390 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Page +{ + property var screen: "welcome" + property var screenPrevious: [] + property var titles: { + "welcome": null, /* titlebar disabled */ + "install_target": "Installation target", + "install_target_confirm": "Warning", + "user_pass": "User password", + "ssh_confirm": "SSH server", + "ssh_credentials": "SSH credentials", + "fs_selection": "Root filesystem", + "fde_confirm": "Full disk encryption", + "fde_pass": "Full disk encryption", + "install_confirm": "Ready to install", + "wait": null + } + property var features: [ + {"name": "welcome", + "screens": ["welcome"]}, + {"name": "installTarget", + "screens": ["install_target", "install_target_confirm"]}, + {"name": "userPassword", + "screens": ["user_pass"]}, + {"name": "sshd", + "screens": ["ssh_confirm", "ssh_credentials"]}, + {"name": "fsType", + "screens": ["fs_selection"]}, + {"name": "fde", + "screens": ["fde_confirm", "fde_pass"]}, + {"name": "installConfirm", + "screens": ["install_confirm", "wait"]} + ] + property var featureIdByScreen: (function() { + /* Put "features" above into an index of screen name -> feature id: + * featureIdByScreen = {"welcome": 0, "user_pass": 1, ...} */ + var ret = {}; + for (var i=0; i=[]" + + /* bottom row */ " ." + + Item { + id: appContainer + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: inputPanel.top + Item { + width: parent.width + height: parent.height + + Rectangle { + id: mobileNavigation + width: parent.width + height: 30 + color: "#e6e4e1" + Layout.fillWidth: true + + border.width: 1 + border.color: "#a7a7a7" + + anchors.left: parent.left + anchors.right: parent.right + + RowLayout { + width: parent.width + height: parent.height + spacing: 6 + + Button { + Layout.leftMargin: 6 + id: mobileBack + text: "<" + + background: Rectangle { + implicitWidth: 10 + implicitHeight: 7 + border.color: "#c1bab5" + border.width: 1 + radius: 4 + color: mobileBack.down ? "#dbdbdb" : "#f2f2f2" + } + + onClicked: navBack() + } + Rectangle { + implicitHeight: 10 + Layout.fillWidth: true + color: "#e6e4e1" + + Text { + id: mobileTitle + text: "" + color: "#303638" + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + Rectangle { + color: "#e6e4e1" + Layout.rightMargin: 6 + implicitWidth: 32 + implicitHeight: 30 + id: filler + } + } + } + + Loader { + id: load + anchors.left: parent.left + anchors.top: mobileNavigation.bottom + anchors.right: parent.right + } + } + } + InputPanel { + id: inputPanel + y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height + visible: config.builtinVirtualKeyboard + anchors.left: parent.left + anchors.right: parent.right + } + + Timer { + id: timer + } + + function skipFeatureInstallTarget() { + return config.targetDeviceRootInternal == ""; + } + + /* Navigation related */ + function navTo(name, historyPush=true) { + console.log("Navigating to screen: " + name); + if (historyPush) + screenPrevious.push(screen); + screen = name; + load.source = name + ".qml"; + mobileNavigation.visible = (titles[name] !== null); + mobileTitle.text = "" + titles[name] + ""; + Qt.inputMethod.hide(); + } + function navFinish() { + /* Show a waiting screen and wait a second (so it can render). The big + * comment in Config.cpp::runPartitionJobThenLeave() explains why this + * is necessary. */ + navTo("wait"); + timer.interval = 1000; + timer.repeat = false; + timer.triggered.connect(function() { + /* Trigger Config.cpp::runPartitionJobThenLeave(). (We could expose + * the function directly with qmlRegisterSingletonType somehow, but + * I haven't seen existing Calamares code do that with the Config + * object, so just use the side effect of setting the variable, as + * done in existing code of Calamares modules.) */ + config.runPartitionJobThenLeave = 1 + }); + timer.start(); + } + function navNextFeature() { + var id; + + /* Skip disabled features */ + for (id = featureIdByScreen[screen] + 1; id < features.length; id++) { + /* First letter uppercase */ + var name = features[id]["name"]; + var nameUp = name.charAt(0).toUpperCase() + name.slice(1); + + /* Check config.Feature */ + var configOption = "feature" + nameUp; + if (config[configOption] === false) { + console.log("Skipping feature (disabled in config): " + name); + continue; + } + + /* Check skipFeature() */ + var funcName = "skipFeature" + nameUp; + if (eval("typeof " + funcName) === "function" + && eval(funcName + "()")) { + console.log("Skipping feature (skip function): " + name); + continue; + } + + break; + } + + console.log("Navigating to feature: " + features[id]["name"]); + return navTo(features[id]["screens"][0]); + } + function navNext() { + var featureId = featureIdByScreen[screen]; + var featureScreens = features[featureId]["screens"]; + for (var i = 0; i" + + " underscores or minus signs."); + } else { + if (!name[i].match(/^[a-z_]$/)) + return validationFailure(errorText, + "First character must be a" + + " lowercase letter or an" + + " underscore."); + } + } + + /* Validate against reserved usernames */ + for (var i = 0; i < reserved.length; i++) { + if (name == reserved[i]) + return validationFailure(errorText, "Username '" + + reserved[i] + + "' is reserved."); + } + + /* Passed */ + return validationFailureClear(errorText); + } + + function validateSshdUsername(username, errorText) { + return validateUsername(username, errorText, [config.username]); + } + function validateSshdPassword(password, passwordRepeat, errorText) { + var pass = password.text; + var repeat = passwordRepeat.text; + + if (pass == "") + return validationFailure(errorText); + + if (pass.length < 6) + return validationFailure(errorText, + "Too short: needs at least 6" + + " digits/characters."); + + if (repeat == "") + return validationFailure(errorText); + + if (pass != repeat) + return validationFailure(errorText, "Passwords don't match."); + + return validationFailureClear(errorText); + } + function check_chars(input) { + for (var i = 0; i < input.length; i++) { + if (allowed_chars.indexOf(input[i]) == -1) + return false; + } + return true; + } + function allowed_chars_multiline() { + /* return allowed_chars split across multiple lines */ + var step = 20; + var ret = ""; + for (var i = 0; i < allowed_chars.length + step; i += step) + ret += allowed_chars.slice(i, i + step) + "\n"; + return ret.trim(); + } + function validatePassword(password, passwordRepeat, errorText) { + var pass = password.text; + var repeat = passwordRepeat.text; + + if (pass == "") + return validationFailure(errorText); + + /* This function gets called for the FDE password and for the user + * password. As of writing, all distributions shipping the mobile + * module are using osk-sdl to type in the FDE password after the + * installation, and another keyboard after booting up, to type in the + * user password. The osk-sdl password has the same keys as + * squeekboard's default layout, and other keyboards should be able to + * type these characters in as well. For now, verify that the password + * only contains characters that can be typed in by osk-sdl. If you + * need this to be more sophisticated, feel free to submit patches to + * make this more configurable. */ + if (!check_chars(pass)) + return validationFailure(errorText, + "The password must only contain" + + " these characters, others can possibly" + + " not be typed in after installation:\n" + + "\n" + + allowed_chars_multiline()); + + if (pass.length < 6) + return validationFailure(errorText, + "Too short: needs at least 6" + + " digits/characters."); + + if (repeat == "") + return validationFailure(errorText); + + if (pass != repeat) + return validationFailure(errorText, "Passwords don't match."); + + return validationFailureClear(errorText); + } +} diff --git a/airootfs/etc/calamares/modules/mobile/mobile.qrc b/airootfs/etc/calamares/modules/mobile/mobile.qrc new file mode 100644 index 0000000..d8ef887 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/mobile.qrc @@ -0,0 +1,21 @@ + + + mobile.qml + + welcome.qml + + install_target.qml + install_target_confirm.qml + + user_pass.qml + ssh_confirm.qml + ssh_credentials.qml + fs_selection.qml + + fde_confirm.qml + fde_pass.qml + install_confirm.qml + + wait.qml + + diff --git a/airootfs/etc/calamares/modules/mobile/ssh_confirm.qml b/airootfs/etc/calamares/modules/mobile/ssh_confirm.qml new file mode 100644 index 0000000..8bf9e0f --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/ssh_confirm.qml @@ -0,0 +1,68 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 30 + wrapMode: Text.WordWrap + + text: "If you don't know what SSH is, choose 'disable'.
" + + "
" + + "With 'enable', you will be asked for a second username and" + + " password. You will be able to login to the SSH server with" + + " these credentials via USB (172.16.42.1), Wi-Fi and possibly" + + " cellular network. It is recommended to replace the password" + + " with an SSH key after the installation.
" + + "
" + + "More information:
" + + "https://postmarketos.org/ssh" + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 40 + width: 200 + + text: qsTr("Enable") + onClicked: { + config.isSshEnabled = true; + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 40 + width: 200 + + text: qsTr("Disable") + onClicked: { + config.isSshEnabled = false; + navNextFeature(); + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/ssh_credentials.qml b/airootfs/etc/calamares/modules/mobile/ssh_credentials.qml new file mode 100644 index 0000000..bfb54c7 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/ssh_credentials.qml @@ -0,0 +1,107 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + TextField { + id: username + anchors.top: parent.top + placeholderText: qsTr("SSH username") + inputMethodHints: Qt.ImhPreferLowercase + onTextChanged: validateSshdUsername(username, errorTextUsername) + text: config.sshdUsername + + onActiveFocusChanged: { + if(activeFocus) { + Qt.inputMethod.update(Qt.ImQueryInput); + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + Text { + id: errorTextUsername + anchors.top: username.bottom + visible: false + wrapMode: Text.WordWrap + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + TextField { + id: password + anchors.top: errorTextUsername.bottom + placeholderText: qsTr("SSH password") + echoMode: TextInput.Password + onTextChanged: validateSshdPassword(password, passwordRepeat, + errorTextPassword) + text: config.sshdPassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + TextField { + id: passwordRepeat + anchors.top: password.bottom + placeholderText: qsTr("SSH password (repeat)") + echoMode: TextInput.Password + onTextChanged: validateSshdPassword(password, passwordRepeat, + errorTextPassword) + text: config.sshdPassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + Text { + id: errorTextPassword + anchors.top: passwordRepeat.bottom + visible: false + wrapMode: Text.WordWrap + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: errorTextPassword.bottom + anchors.topMargin: 40 + width: 200 + + text: qsTr("Continue") + onClicked: { + if (validateSshdUsername(username, errorTextUsername) && + validateSshdPassword(password, passwordRepeat, + errorTextPassword)) { + config.sshdUsername = username.text; + config.sshdPassword = password.text; + + navNext(); + } + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/user_pass.qml b/airootfs/etc/calamares/modules/mobile/user_pass.qml new file mode 100644 index 0000000..9449579 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/user_pass.qml @@ -0,0 +1,143 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + property var passPlaceholder: (config.userPasswordNumeric + ? "PIN" + : "Password") + property var hints: (config.userPasswordNumeric + ? Qt.ImhDigitsOnly + : Qt.ImhPreferLowercase) + property var validatePassFunc: (config.userPasswordNumeric + ? validatePin + : validatePassword); + + property var validateNameFunc: validateUsername; + + + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: usernameDescription + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: (function() { + return "Set the username of your user. The default" + + " username is \"" + config.username + "\"."; + }()) + + width: 200 + } + + TextField { + id: username + anchors.top: usernameDescription.bottom + placeholderText: qsTr("Username") + onTextChanged: validateNameFunc(username, errorText) + text: config.username + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Text { + id: userPassDescription + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: username.bottom + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: (function() { + if (config.userPasswordNumeric) { + return "Set the numeric password of your user. The" + + " lockscreen will ask for this PIN. This is" + + " not the PIN of your SIM card. Make sure to" + + " remember it."; + } else { + return "Set the password of your user. The lockscreen will" + + " ask for this password. Make sure to remember it."; + } + }()) + + width: 200 + } + + TextField { + id: userPass + anchors.top: userPassDescription.bottom + placeholderText: qsTr(passPlaceholder) + echoMode: TextInput.Password + onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText) + text: config.userPassword + + /* Let the virtual keyboard change to digits only */ + inputMethodHints: hints + onActiveFocusChanged: { + if(activeFocus) { + Qt.inputMethod.update(Qt.ImQueryInput) + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + TextField { + id: userPassRepeat + anchors.top: userPass.bottom + placeholderText: qsTr(passPlaceholder + " (repeat)") + inputMethodHints: hints + echoMode: TextInput.Password + onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText) + text: config.userPassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Text { + anchors.top: userPassRepeat.bottom + id: errorText + visible: false + wrapMode: Text.WordWrap + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: errorText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: { + if (validatePassFunc(userPass, userPassRepeat, errorText) && validateNameFunc(username, errorText)) { + config.userPassword = userPass.text; + config.username = username.text; + navNext(); + } + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/wait.qml b/airootfs/etc/calamares/modules/mobile/wait.qml new file mode 100644 index 0000000..cd50e25 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/wait.qml @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Page +{ + id: fdeWait + + Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Image { + id: logo + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + height: 50 + fillMode: Image.PreserveAspectFit + source: "file:///usr/share/calamares/branding/default-mobile/logo.png" + } + Text { + id: waitText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logo.bottom + anchors.topMargin: 50 + wrapMode: Text.WordWrap + text: "Formatting and mounting target partition. This may" + + " take up to ten minutes, please be patient." + width: 200 + } + } +} diff --git a/airootfs/etc/calamares/modules/mobile/welcome.qml b/airootfs/etc/calamares/modules/mobile/welcome.qml new file mode 100644 index 0000000..67fcce8 --- /dev/null +++ b/airootfs/etc/calamares/modules/mobile/welcome.qml @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Page +{ + id: welcome + + Item { + id: appContainer + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + Item { + width: parent.width + height: parent.height + + Image { + id: logo + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + height: 50 + fillMode: Image.PreserveAspectFit + source: "file:///usr/share/calamares/branding/default-mobile/logo.png" + } + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logo.bottom + anchors.topMargin: 10 + horizontalAlignment: Text.AlignRight + text: "You are about to install
" + + "" + config.osName + + " " + config.version + "
" + + "user interface " + + "" + config.userInterface + "
" + + "architecture " + + "" + config.arch + "
" + + "on your
" + + "" + config.device + "
" + width: 200 + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: navNext() + } + } + } +} diff --git a/airootfs/etc/calamares/modules/mount.conf b/airootfs/etc/calamares/modules/mount.conf new file mode 100644 index 0000000..da95395 --- /dev/null +++ b/airootfs/etc/calamares/modules/mount.conf @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Mount filesystems in the target (generally, before treating the +# target as a usable chroot / "live" system). Filesystems are +# automatically mounted from the partitioning module. Filesystems +# listed here are **extra**. The filesystems listed in *extraMounts* +# are mounted in all target systems. +--- +# Extra filesystems to mount. The key's value is a list of entries; each +# entry has five keys: +# - device The device node to mount +# - fs (optional) The filesystem type to use +# - mountPoint Where to mount the filesystem +# - options (optional) An array of options to pass to mount +# - efi (optional) A boolean that when true is only mounted for UEFI installs +# +# The device is not mounted if the mountPoint is unset or if the fs is +# set to unformatted. +# +extraMounts: + - device: proc + fs: proc + mountPoint: /proc + - device: sys + fs: sysfs + mountPoint: /sys + - device: /dev + mountPoint: /dev + options: [ bind ] + - device: tmpfs + fs: tmpfs + mountPoint: /run + - device: /run/udev + mountPoint: /run/udev + options: [ bind ] + - device: efivarfs + fs: efivarfs + mountPoint: /sys/firmware/efi/efivars + efi: true + +# Btrfs subvolumes to create if root filesystem is on btrfs volume. +# If *mountpoint* is mounted already to another partition, it is ignored. +# Separate subvolume for swapfile is handled separately and automatically. +# +# It is possible to prevent subvolume creation -- this is likely only relevant +# for the root (/) subvolume -- by giving an empty string as a subvolume +# name. In this case no subvolume will be created. +# +btrfsSubvolumes: + - mountPoint: / + subvolume: /@ + # As an alternative: + # + # subvolume: "" + - mountPoint: /home + subvolume: /@home + - mountPoint: /var/cache + subvolume: /@cache + - mountPoint: /var/log + subvolume: /@log + +# The name of the btrfs subvolume holding the swapfile. This only used when +# a swapfile is selected and the root filesystem is btrfs +# +btrfsSwapSubvol: /@swap + +# The mount options used to mount each filesystem. +# +# filesystem contains the name of the filesystem or on of three special +# values, "default", efi" and "btrfs_swap". The logic is applied in this manner: +# - If the partition is the EFI partition, the "efi" entry will be used +# - If the fs is btrfs and the subvolume is for the swapfile, +# the "btrfs_swap" entry is used +# - If the filesystem is an exact match for filesystem, that entry is used +# - If no match is found in the above, the default entry is used +# - If there is no match and no default entry, "defaults" is used +# - If the mountOptions key is not present, "defaults" is used +# +# Each filesystem entry contains 3 keys, all of which are optional +# options - An array of mount options that is used on all disk types +# ssdOptions - An array of mount options combined with options for ssds +# hddOptions - An array of mount options combined with options for hdds +# If combining these options results in an empty array, "defaults" is used +# +# Example 1 +# In this example, there are specific options for ext4 and btrfs filesystems, +# the EFI partition and the subvolume holding the btrfs swapfile. All other +# filesystems use the default entry. For the btrfs filesystem, there are +# additional options specific to hdds and ssds +# +# mountOptions: +# - filesystem: default +# options: [ defaults ] +# - filesystem: efi +# options: [ defaults, umask=0077 ] +# - filesystem: ext4 +# options: [ defaults ] +# - filesystem: btrfs +# options: [ defaults, compress=zstd:1 ] +# ssdOptions: [ discard=async ] +# hddOptions: [ autodefrag ] +# - filesystem: btrfs_swap +# options: [ defaults, noatime ] +# +# Example 2 +# In this example there is a single default used by all filesystems +# +# mountOptions: +# - filesystem: default +# options: [ defaults ] +# +mountOptions: + - filesystem: default + options: [ defaults ] + - filesystem: efi + options: [ defaults, umask=0077 ] + - filesystem: btrfs + options: [ defaults, compress=zstd:1 ] + - filesystem: btrfs_swap + options: [ defaults, noatime ] + + + + diff --git a/airootfs/etc/calamares/modules/netinstall.conf b/airootfs/etc/calamares/modules/netinstall.conf new file mode 100644 index 0000000..f185fc1 --- /dev/null +++ b/airootfs/etc/calamares/modules/netinstall.conf @@ -0,0 +1,347 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +### Netinstall module +# +# The netinstall module allows distribution maintainers to ship minimal ISOs +# with only a basic set of preinstalled packages. At installation time, the +# user is presented with the choice to install groups of packages from a +# predefined list. +# +# Calamares will then use the *packages* module to install the packages. +# Without a *packages* module in the exec phase somewhere **after** +# this netinstall, nothing will actually get installed. The packages +# module must be correctly configured **and** the package manager must +# be runnable from within the installed system at the point where it +# is invoked, otherwise you'll get nothing. +# +# There are two basic deployment schemes: +# - static package lists; the packages do not change for this release. +# In this case, the package list file may be on the ISO-image itself +# as a separate file, **or** included in this configuration file. +# Either will do; separate file is easier to update independently +# of the Calamares configuration, while merged configurations use +# fewer files overall and are closer to self-documenting. +# - online package lists; the package list is fetched from a remote +# URL and handled otherwise like a static list. This can be useful +# if the package list needs updating during the lifetime of an ISO- +# image, e.g. packages are added or renamed. +# +# There is only one required key for this module, *groupsUrl*. +# +# This module supports multiple instances through the *label* key, +# which allows you to distinguish them in the UI. +--- +# The *groupsUrl* determines where the data for the netinstall groups-and- +# packages comes from. The value of the key may be: +# +# - a single string (this is treated as a list with just that string in it) +# - a list of strings +# +# Each string is treated as a URL (see below for special cases. The +# list is examined **in order** and each URL is tried in turn. The +# first URL to load successfully -- even if it yields 0 packages -- +# ends the process. This allows using a network URL and a (fallback) +# local URL for package lists, or for using multiple mirrors of +# netinstall data. +# +# The URL must point to a YAML file that follows the format described +# below at the key *groups* -- except for the special case URL "local". +# Note that the contents of the groups file is the **important** +# part of the configuration of this module. It specifies what +# groups and packages the user may select (and so what commands are to +# be run to install them). +# +# The format of the groups file is the same as the format of the +# *groups* key described below, **except** that a stand-alone +# groups file does not have to have the top-level *groups* key. +# (It **may** have one, though, for instance when you copy +# this configuration file to `netinstall.yaml` and key *groups* +# must have a list-of-groups as value; if the file does not have +# a top-level key *groups*, then the file must contain only a list of groups. +# +# Each item in the list *groupsUrl* may be: +# - A remote URL like `http://example.org/netinstall.php` +# - A local file URL like `file:///usr/share/calamares/netinstall.yaml` +# - The special-case literal string `local` +# +# Non-special case URLs are loaded as YAML; if the load succeeds, then +# they are interpreted like the *groups* key below. The special case +# `local` loads the data directly from **this** file. +# +groupsUrl: local + +# Alternate form: +# groupsUrl: [ local ] + +# Net-based package list, with fallback to local file +# groupsUrl: +# - http://example.com/calamares/netinstall.yaml +# - file:///etc/calamares/modules/netinstall.yaml + + + +# If the installation can proceed without netinstall (e.g. the Live CD +# can create a working installed system, but netinstall is preferred +# to bring it up-to-date or extend functionality) leave this set to +# false (the default). If set to true, the netinstall data is required. +# +# This only has an effect if the netinstall data cannot be retrieved, +# or is corrupt: having "required" set, means the install cannot proceed. +# For local or file: type *groupsUrl* settings, this setting is not +# really meaningful. +required: false + +# To support multiple instances of this module, +# some strings are configurable and translatable here. +# Sub-keys under *label* are used for the user interface. +# - *sidebar* This is the name of the module in the progress-tree / sidebar +# in Calamares. +# - *title* This is displayed above the list of packages. +# If no *sidebar* values are provided, defaults to "Package selection" +# and existing translations. If no *title* values are provided, no string +# is displayed. +# +# Translations are handled through `[ll]` notation, much like in +# `.desktop` files. The string associated with `key[ll]` is used for +# *key* when when the language *ll* (language-code, like *nl* or *en_GB* +# or *ja*) is used. +# +# The following strings are **already** known to Calamares and can be +# listed here in *untranslated* form (e.g. as value of *sidebar*) +# without bothering with the translations: they are picked up from +# the regular translation framework: +# - "Package selection" +# - "Office software" +# - "Office package" +# - "Browser software" +# - "Browser package" +# - "Web browser" +# - "Kernel" +# - "Services" +# - "Login" +# - "Desktop" +# - "Applications" +# - "Communication" +# - "Development" +# - "Office" +# - "Multimedia" +# - "Internet" +# - "Theming" +# - "Gaming" +# - "Utilities" +# Other strings should follow the translations format. +label: + sidebar: "Package selection" + # sidebar[nl]: "Pakketkeuze" + # sidebar[en_GB]: "Package choice" + # sidebar[ja]: "知りません" # "I don't know" + # title: "Office Package" + # title[nl]: "Kantoorsoftware" + +# If, and only if, *groupsUrl* is set to the literal string `local`, +# groups data is read from this file. The value of *groups* must be +# a list. Each item in the list is a group (of packages, or subgroups, +# or both). A standalone groups file contains just the list, +# (without the top-level *groups* key, or just the top-level *groups* +# key and with the list as its value, like in this file). +# +# Using `local` is recommended only for small static package lists. +# Here it is used for documentation purposes. +# +# +### Groups Format +# +# Each item in the list describes one group. The following keys are +# required for each group: +# +# - *name* of the group; short and human-readable. Shown in the first +# column of the UI. +# - *description* of the group; longer and human-readable. Shown in the +# second column of the UI. This is one of the things that visually +# distinguishes groups (with descriptions) from packages (without). +# - *packages*, a list of packages that belong to this group. +# The items of the *packages* list are actual package names +# as passed to the package manager (e.g. `qt5-creator-dev`). +# This list may be empty (e.g. if your group contains only +# subgroups). This key isn't **really** required, either -- +# one of *subgroups* or *packages* is. +# +# The following keys are **optional** for a group: +# +# - *hidden*: if true, do not show the group on the page. Defaults to false. +# - *selected*: if true, display the group as selected. Defaults to the +# parent group's value, if there is a parent group; top-level groups +# are set to true by default. +# - *critical*: if true, make the installation process fail if installing +# any of the packages in the group fails. Otherwise, just log a warning. +# Defaults to false. If not set in a subgroup (see below), inherits from +# the parent group. +# - *immutable*: if true, the state of the group (and all its subgroups) +# cannot be changed; no packages can be selected or deselected. No +# checkboxes are show for the group. Setting *immutable* to true +# really only makes sense in combination with *selected* set to true, +# so that the packages will be installed. (Setting a group to immutable +# can be seen as removing it from the user-interface.) +# - *noncheckable*: if true, the entire group cannot be selected or +# deselected by a single click. This does not affect any subgroups +# or child packages +# - *expanded*: if true, the group is shown in an expanded form (that is, +# not-collapsed) in the treeview on start. This only affects the user- +# interface. Only top-level groups are show expanded-initially. +# - *subgroups*: if present this follows the same structure as the top level +# groups, allowing sub-groups of packages to an arbitary depth. +# - *pre-install*: an optional command to run within the new system before +# the group's packages are installed. It will run before **each** package in +# the group is installed. +# - *post-install*: an optional command to run within the new system after +# the group's packages are installed. It will run after **each** package in +# the group is installed. +# +# If you set both *hidden* and *selected* for a top-level group, you are +# creating a "default" group of packages which will always be installed +# in the user's system. Hidden selected subgroups are installed if their +# parent is selected. Setting *hidden* to true without *selected*, or with +# *selected* set to false, is kind of pointless and will generate a warning. +# +# The *pre-install* and *post-install* commands are **not** passed to +# a shell; see the **packages** module configuration (i.e. `packages.conf`) +# for details. To use a full shell pipeline, call the shell explicitly. +# +# Non-critical groups are installed by calling the package manager +# individually, once for each package (and ignoring errors), while +# critical packages are installed in one single call to the package +# manager (and errors cause the installation to terminate). +# +# +# +# The *groups* key below contains some common patterns for packages +# and sub-groups, with documentation. + + +groups: + # This group is hidden, so the name and description are not really + # important. Since it is selected, these packages will be installed. + # It's non-critical, so they are installed one-by-one. + # + # This is a good approach for something you want up-to-date installed + # in the target system every time. + - name: "Default" + description: "Default group" + hidden: true + selected: true + critical: false + packages: + - base + - chakra-live-skel + # The Shells group contains only subgroups, no packages itself. + # The *critical* value is set for the subgroups that do not + # override it; *selected* is set to false but because one of + # the subgroups sets *selected* to true, the overall state of + # **this** group is partly-selected. + # + # Each of the sub-groups lists a bunch of packages that can + # be individually selected, so a user can pick (for instance) + # just one of the ZSH packages if they like. + - name: "Shells" + description: "Shells" + hidden: false + selected: false + critical: true + subgroups: + - name: "Bash" + description: "Bourne Again Shell" + selected: true + packages: + - bash + - bash-completion + - name: "Zsh" + description: "Zee shell, boss" + packages: + - zsh + - zsh-completion + - zsh-extensions + # The kernel group has no checkbox, because it is immutable. + # It can be (manually) expanded, and the packages inside it + # will be shown, also without checkboxes. This is a way to + # inform users that something will always be installed, + # sort of like a hidden+selected group but visible. + - name: "Kernel" + description: "Kernel bits" + hidden: false + selected: true + critical: true + immutable: true + packages: + - kernel + - kernel-debugsym + - kernel-nvidia + # *selected* defaults to true for top-level + - name: Communications + description: "Communications Software" + packages: + - ruqola + - konversation + - nheko + - quaternion + # Setting *selected* is supported. Here we also show off "rich" + # packages: ones with a package-name (for the package-manager) + # and a description (for the human). + - name: Editors + description: "Editing" + selected: false + packages: + - vi + - emacs + - nano + - name: kate-git + description: Kate (unstable) + - name: kate + description: KDE's text editor + # The "bare" package names can be intimidating, so you can use subgroups + # to provide human-readable names while hiding the packages themselves. + # This also allows you you group related packages -- suppose you feel + # that KDevelop should be installed always with PHP and Python support, + # but that support is split into multiple packages. + # + # So this subgroup (IDE) contains subgroups, one for each "package" + # we want to install. Each of those subgroups (Emacs, KDevelop) + # in turn contains **one** bogus subgroup, which then has the list + # of relevant packages. This extra-level-of-subgrouping allows us + # to list packages, while giving human-readable names. + # + # The name of the internal subgroup doesn't matter -- it is hidden + # from the user -- so we can give them all bogus names and + # descriptions, even the same name. Here, we use "Bogus". You + # can re-use the subgroup name, it doesn't really matter. + # + # Each internal subgroup is set to *hidden*, so it does not show up + # as an entry in the list, and it is set to *selected*, + # so that if you select its parent subgroup, the packages from + # the subgroup are selected with it and get installed. + - name: IDE + description: "Development Environment" + selected: false + subgroups: + - name: Emacs + description: LISP environment and editor + subgroups: + - name: Bogus + description: Bogus + hidden: true + selected: true + packages: + - emacs + - name: KDevelop + description: KDE's C++, PHP and Python environment + subgroups: + - name: Bogus + description: Bogus + hidden: true + selected: true + packages: + - kdevelop + - kdevelop-dev + - kdev-php + - kdev-python + diff --git a/airootfs/etc/calamares/modules/notesqml.conf b/airootfs/etc/calamares/modules/notesqml.conf new file mode 100644 index 0000000..c65f988 --- /dev/null +++ b/airootfs/etc/calamares/modules/notesqml.conf @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The *notesqml* module can be used to display a QML file +# as an installer step. This is most useful for release-notes +# and similar somewhat-static content, but if you want to you +# can put SameGame in there as well. +# +# While the module compiles a QML file into a QRC for inclusion +# into the shared library, normal use will configure it with +# an external file, either from Calamares AppData directory or +# from the branding directory. +# +# --- +# +# QML modules can search for the QML inside the Qt resources +# (QRC) which are compiled into the module, or in the branding +# setup for Calamares, (or both of them, with branding taking +# precedence). This allows the module to ship a default UI and +# branding to optionally introduce a replacement file. +# +# Generally, leave the search method set to "both" because if +# you don't want to brand the UI, just don't ship a branding +# QML file for it. +# +# To support instanced QML modules, searches in the branding +# directory look for the full notesqml@instanceid name as well. +--- +# Search mode. Valid values are "both", "qrc" and "branding" +qmlSearch: both + +# Name of the QML file. If not set, uses the name of the instance +# of the module (e.g. if you list this module in `settings.conf` +# in the *instances* section, you get *id*, otherwise it would +# normally be "notesqml"). +# qmlFilename: notesqml + +# This is the name of the module in the progress-tree / sidebar +# in Calamares. To support multiple instances of the QML module, +# the name is configurable and translatable here. +qmlLabel: + notes: "Release Notes" + notes[nl]: "Opmerkingen" diff --git a/airootfs/etc/calamares/modules/oemid.conf b/airootfs/etc/calamares/modules/oemid.conf new file mode 100644 index 0000000..4fb14d9 --- /dev/null +++ b/airootfs/etc/calamares/modules/oemid.conf @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# This is an OEM setup (phase-0) configuration file. +--- +# The batch-identifier is written to /var/log/installer/oem-id. +# This value is put into the text box as the **suggested** +# OEM ID. If ${DATE} is included in the identifier, then +# that is replaced by the current date in yyyy-MM-dd (ISO) format. +# +# It is ok for the identifier to be empty. +# +# The identifier is written to the file as UTF-8 (this will be no +# different from ASCII, for most inputs) and followed by a newline. +# If the identifier is empty, only a newline is written. +batch-identifier: neon-${DATE} diff --git a/airootfs/etc/calamares/modules/openrcdmcryptcfg.conf b/airootfs/etc/calamares/modules/openrcdmcryptcfg.conf new file mode 100644 index 0000000..911a4ef --- /dev/null +++ b/airootfs/etc/calamares/modules/openrcdmcryptcfg.conf @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +configFilePath: /etc/conf.d/dmcrypt diff --git a/airootfs/etc/calamares/modules/os-freebsd/CMakeLists.txt b/airootfs/etc/calamares/modules/os-freebsd/CMakeLists.txt new file mode 100644 index 0000000..d666700 --- /dev/null +++ b/airootfs/etc/calamares/modules/os-freebsd/CMakeLists.txt @@ -0,0 +1,20 @@ +# The OS-FreeBSD module does "all the things" in a FreeBSD installation. +# Since the other modules -- users, fstab, grub, pretty much all of them +# -- are Linux-specific, it doesn't make much sense to fork each of them +# or provide alternatives, so instead we have one module that completes +# a FreeBSD installation based on the GlobalStorage values set by +# Calamares viewmodules. +# +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# License-Filename: LICENSE +# + +calamares_add_plugin( os-freebsd + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + FreeBSDJob.cpp + SHARED_LIB + NO_CONFIG +) diff --git a/airootfs/etc/calamares/modules/os-freebsd/FreeBSDJob.cpp b/airootfs/etc/calamares/modules/os-freebsd/FreeBSDJob.cpp new file mode 100644 index 0000000..8e845e8 --- /dev/null +++ b/airootfs/etc/calamares/modules/os-freebsd/FreeBSDJob.cpp @@ -0,0 +1,58 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSE + */ + +#include "FreeBSDJob.h" + +#include "CalamaresVersion.h" +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "utils/Logger.h" + +#include +#include +#include + + +FreeBSDJob::FreeBSDJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + + +FreeBSDJob::~FreeBSDJob() {} + + +QString +FreeBSDJob::prettyName() const +{ + return tr( "FreeBSD Installation Job" ); +} + +Calamares::JobResult +FreeBSDJob::exec() +{ + emit progress( 0.1 ); + cDebug() << "[FREEBSD]"; + + Calamares::JobQueue::instance()->globalStorage()->debugDump(); + emit progress( 0.5 ); + + QThread::sleep( 3 ); + emit progress( 1.0 ); + + return Calamares::JobResult::ok(); +} + + +void +FreeBSDJob::setConfigurationMap( const QVariantMap& configurationMap ) +{ + // TODO: actually fetch something from that configuration + m_configurationMap = configurationMap; +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDJobFactory, registerPlugin< FreeBSDJob >(); ) diff --git a/airootfs/etc/calamares/modules/os-freebsd/FreeBSDJob.h b/airootfs/etc/calamares/modules/os-freebsd/FreeBSDJob.h new file mode 100644 index 0000000..5f39f08 --- /dev/null +++ b/airootfs/etc/calamares/modules/os-freebsd/FreeBSDJob.h @@ -0,0 +1,39 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSE + */ + +#ifndef FREEBSDJOB_H +#define FREEBSDJOB_H + +#include "CppJob.h" +#include "DllMacro.h" +#include "utils/PluginFactory.h" + +#include +#include + + +class PLUGINDLLEXPORT FreeBSDJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit FreeBSDJob( QObject* parent = nullptr ); + virtual ~FreeBSDJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private: + QVariantMap m_configurationMap; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDJobFactory ) + +#endif // FREEBSDJOB_H diff --git a/airootfs/etc/calamares/modules/os-nixos/main.py b/airootfs/etc/calamares/modules/os-nixos/main.py new file mode 100644 index 0000000..469c613 --- /dev/null +++ b/airootfs/etc/calamares/modules/os-nixos/main.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# Calamares is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Calamares is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Calamares. If not, see . +# +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +=== NixOS Configuration + +NixOS has its own "do all the things" configuration file which +declaratively handles what things need to be done in the target +system, and it has an existing tool to "execute" that declarative +specification. This module takes configuration values set by +Calamares viewmodules (e.g. the users module) and puts +them into the configuration file in the target system, +and then runs the necessary NixOS specific tools. +""" + +import libcalamares +import os +from time import gmtime, strftime, sleep + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +# The following long **long** string is the entire text of +# a nix-configuration file. It is cribbed from, and adapted from, +# the sample file in https://github.com/itamar567/dotnix . +# +# We are going to substitute values into this text. However, +# Python's .format() function wants parens { } around variable +# names, and Nix's config file wants to use parens { } for block +# structure. So we have a compromise format here: +# +# - Write the config file as you would normally, +# - Write @@variable@@ instead of {variable} +# +# Some minor trickery later will massage this and substitute variables. +# +configuration_nix_sample = """# Nix configuration file +{ config, pkgs, ... }: + +{ + imports = + [ # Include the results of the hardware scan. + ./hardware-configuration.nix + ./command-not-found/command-not-found.nix + ]; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # Use Zen Kernel + boot.kernelPackages = pkgs.linuxPackages_zen; + + networking.hostName = "@@hostname@@"; # Define your hostname. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + + # Set your time zone. + time.timeZone = "@@timezone@@"; + + # The global useDHCP flag is deprecated, therefore explicitly set to false here. + networking.useDHCP = false; + networking.interfaces.enp42s0.useDHCP = true; + + # Select internationalisation properties. + i18n.defaultLocale = "en_US.UTF-8"; + + # Configure X11 + services.xserver = { + enable = true; + windowManager.i3 = { + enable = true; + package = pkgs.i3-gaps; + }; + + # Set i3 to the default session in the display manager + displayManager.defaultSession = "none+i3"; + }; + + # SSH fix + programs.ssh.askPassword = pkgs.lib.mkForce ""; + + # Enable CUPS to print documents. + services.printing.enable = true; + + # Enable sound. + sound.enable = true; + hardware.pulseaudio.enable = true; + + # Define a user account. Don't forget to set a password with ‘passwd’. + users.users.username = { + isNormalUser = true; + extraGroups = [ "wheel" "libvirtd" ]; + }; + + # Disable password for sudo + security.sudo.extraRules= [{ + groups = [ "wheel" ]; + commands = [{ + command = "ALL" ; + options= [ "NOPASSWD" ]; + }]; + }]; + + # Set ZSH as the default shell + users.defaultUserShell = pkgs.zsh; + + # clean /tmp on boot + boot.cleanTmpDir=true; + + # Config packages + nixpkgs.config = { + allowUnfree = true; + + chromium = { + enableWideVine = true; + }; + }; + + # Automatically upgrade the system + # This service is a modified version of https://github.com/NixOS/nixpkgs/blob/nixos-21.05/nixos/modules/tasks/auto-upgrade.nix#L122 + systemd = { + services.nixos-upgrade = { + description = "NixOS Upgrade"; + + # We use --upgrade, so we need internet access + wants = [ "network-online.target" ]; + + restartIfChanged = false; + unitConfig.X-StopOnRemoval = false; + + serviceConfig.Type = "oneshot"; + + environment = config.nix.envVars // { + inherit (config.environment.sessionVariables) NIX_PATH; + HOME = "/root"; + } // config.networking.proxy.envVars; + + path = with pkgs; [ + coreutils + gnutar + xz.bin + gzip + gitMinimal + config.nix.package.out + ]; + + script = let + nixos-rebuild = + "${config.system.build.nixos-rebuild}/bin/nixos-rebuild"; + in '' + ${nixos-rebuild} boot --upgrade + booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})" + built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})" + if [ "$booted" = "$built" ]; then + ${nixos-rebuild} switch + fi + ''; + }; + + # To start the service at boot, we will use a systemd timer + timers.nixos-upgrade = { + wantedBy = [ "timers.target" ]; + partOf = [ "nixos-upgrade.service" ]; + timerConfig.OnBootSec = "5s"; + }; + }; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + programs.mtr.enable = true; + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + }; + + # Enable the OpenSSH daemon. + services.openssh.enable = true; + + # Open ports in the firewall. + networking.firewall.enable = false; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "20.09"; # Did you read the comment? +} +""" + + +def pretty_name(): + return _("NixOS Configuration.") + + +def catenate(d, key, *values): + """ + Sets @p d[key] to the string-concatenation of @p values + if none of the values are None. + + This can be used to set keys conditionally based on + the values being found. + """ + if [v for v in values if v is None]: + return + + d[key] = "".join(values) + + +def run(): + """NixOS Configuration.""" + + gs = libcalamares.globalstorage + text = configuration_nix_sample + + # Collect variables to substitute into the main text + variables = dict() + catenate(variables, "hostname", gs.value("hostname")) + catenate(variables, "timezone", gs.value("locationRegion"), "/", gs.value("locationZone")) + + # Check that all variables are used + for key in variables.keys(): + pattern = "@@{key}@@".format(key=key) + if not pattern in text: + libcalamares.utils.warning("Variable '{key}' is not used.".format(key=key)) + + # Check that all patterns exist + import re + variable_pattern = re.compile("@@\w+@@") + for match in variable_pattern.finditer(text): + variable_name = text[match.start()+2:match.end()-2] + if not variable_name in variables: + libcalamares.utils.warning("Variable '{key}' is used but not defined.".format(key=variable_name)) + + # Do the substitutions + for key in variables.keys(): + pattern = "@@{key}@@".format(key=key) + text = text.replace(pattern, str(variables[key])) + + # Write the result to a temp-file, then run the main tool. + # There is no progress reporting from the tool, so it's going + # to seem like the module is hanging (see issue #1740). + configuration_filename = "/tmp/configuration.nix" + with open(configuration_filename, "w") as f: + f.write(text) + + libcalamares.job.setprogress(0.1) + libcalamares.utils.check_target_env_call(["nix", configuration_filename]) + + # To indicate an error, return a tuple of: + # (message, detailed-error-message) + return None diff --git a/airootfs/etc/calamares/modules/os-nixos/module.desc b/airootfs/etc/calamares/modules/os-nixos/module.desc new file mode 100644 index 0000000..c6f3568 --- /dev/null +++ b/airootfs/etc/calamares/modules/os-nixos/module.desc @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The NixOS module writes a nix-configuration file and then calls +# the Nix configuration tool to do the actual work of building +# the target system. +--- +type: "job" +name: "os-nixos" +interface: "python" +script: "main.py" diff --git a/airootfs/etc/calamares/modules/os-nixos/tests/1.global b/airootfs/etc/calamares/modules/os-nixos/tests/1.global new file mode 100644 index 0000000..b68dc4e --- /dev/null +++ b/airootfs/etc/calamares/modules/os-nixos/tests/1.global @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +hostname: my-nix-host +locationRegion: Asia +locationZone: Kolkata diff --git a/airootfs/etc/calamares/modules/packages.conf b/airootfs/etc/calamares/modules/packages.conf new file mode 100644 index 0000000..b9777f6 --- /dev/null +++ b/airootfs/etc/calamares/modules/packages.conf @@ -0,0 +1,214 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The configuration for the package manager starts with the +# *backend* key, which picks one of the backends to use. +# In `main.py` there is a base class `PackageManager`. +# Implementations must subclass that and set a (class-level) +# property *backend* to the name of the backend (e.g. "dummy"). +# That property is used to match against the *backend* key here. +# +# You will have to add such a class for your package manager. +# It is fairly simple Python code. The API is described in the +# abstract methods in class `PackageManager`. Mostly, the only +# trick is to figure out the correct commands to use, and in particular, +# whether additional switches are required or not. Some package managers +# have more installer-friendly defaults than others, e.g., DNF requires +# passing --disablerepo=* -C to allow removing packages without Internet +# connectivity, and it also returns an error exit code if the package did +# not exist to begin with. +--- +# +# Which package manager to use, options are: +# - apk - Alpine Linux package manager +# - apt - APT frontend for DEB and RPM +# - dnf - DNF, the new RPM frontend +# - dnf5 - DNF5, the newer new RPM frontend +# - entropy - Sabayon package manager (is being deprecated) +# - luet - Sabayon package manager (next-gen) +# - packagekit - PackageKit CLI tool +# - pacman - Pacman +# - pamac - Manjaro package manager +# - portage - Gentoo package manager +# - yum - Yum RPM frontend +# - zypp - Zypp RPM frontend +# +# Not actually a package manager, but suitable for testing: +# - dummy - Dummy manager, only logs +# +backend: dummy + +# +# Often package installation needs an internet connection. +# Since you may allow system installation without a connection +# and want to offer OPTIONAL package installation, it's +# possible to have no internet, yet have this packages module +# enabled in settings. +# +# You can skip the whole module when there is no internet +# by setting "skip_if_no_internet" to true. +# +# You can run a package-manager specific update procedure +# before installing packages (for instance, to update the +# list of packages and dependencies); this is done only if there +# is an internet connection. +# +# Set "update_db" to 'true' for refreshing the database on the +# target system. On target installations, which got installed by +# unsquashing, a full system update may be needed. Otherwise +# post-installing additional packages may result in conflicts. +# Therefore set also "update_system" to 'true'. +# +skip_if_no_internet: false +update_db: true +update_system: false + +# pacman specific options +# +# *num_retries* should be a positive integer which specifies the +# number of times the call to pacman will be retried in the event of a +# failure. If it is missing, it will be set to 0. +# +# *disable_download_timeout* is a boolean that, when true, includes +# the flag --disable-download-timeout on calls to pacman. When missing, +# false is assumed. +# +# *needed_only* is a boolean that includes the pacman argument --needed +# when set to true. If missing, false is assumed. +pacman: + num_retries: 0 + disable_download_timeout: false + needed_only: false + +# +# List of maps with package operations such as install or remove. +# Distro developers can provide a list of packages to remove +# from the installed system (for instance packages meant only +# for the live system). +# +# A job implementing a distro specific logic to determine other +# packages that need to be installed or removed can run before +# this one. Distro developers may want to install locale packages +# or remove drivers not needed on the installed system. +# Such a job would populate a list of dictionaries in the global +# storage called "packageOperations" and that list is processed +# after the static list in the job configuration (i.e. the list +# that is in this configuration file). +# +# Allowed package operations are: +# - *install*, *try_install*: will call the package manager to +# install one or more packages. The install target will +# abort the whole installation if package-installation +# fails, while try_install carries on. Packages may be +# listed as (localized) names, or as (localized) package-data. +# See below for the description of the format. +# - *localInstall*: this is used to call the package manager +# to install a package from a path-to-a-package. This is +# useful if you have a static package archive on the install media. +# The *pacman* package manager is the only one to specially support +# this operation (all others treat this the same as *install*). +# - *remove*, *try_remove*: will call the package manager to +# remove one or more packages. The remove target will +# abort the whole installation if package-removal fails, +# while try_remove carries on. Packages may be listed as +# (localized) names. +# One additional key is recognized, to help netinstall out: +# - *source*: ignored, does get logged +# Any other key is ignored, and logged as a warning. +# +# There are two formats for naming packages: as a name or as package-data, +# which is an object notation providing package-name, as well as pre- and +# post-install scripts. +# +# Here are both formats, for installing vi. The first one just names the +# package for vi (using the naming of the installed package manager), while +# the second contains three data-items; the pre-script is run before invoking +# the package manager, and the post-script runs once it is done. +# +# - install +# - vi +# - package: vi +# pre-script: touch /tmp/installing-vi +# post-script: rm -f /tmp/installing-vi +# +# The pre- and post-scripts are optional, but you cannot leave both out +# if you do use the *package* key: using "package: vi" with neither script +# option will trick Calamares into trying to install a package named +# "package: vi", which is unlikely to work. +# +# The pre- and post-scripts are **not** executed by a shell unless you +# explicitly invoke `/bin/sh` in them. The command-lines are passed +# to exec(), which does not understand shell syntax. In other words: +# +# pre-script: ls | wc -l +# +# Will fail, because `|` is passed as a command-line argument to ls, +# as are `wc`, and `-l`. No shell pipeline is set up, and ls is likely +# to complain. Invoke the shell explicitly: +# +# pre-script: /bin/sh -c \"ls | wc -l\" +# +# The above note on shell-expansion applies to versions up-to-and-including +# Calamares 3.2.12, but will change in future. +# +# Any package name may be localized; this is used to install localization +# packages for software based on the selected system locale. By including +# the string `LOCALE` in the package name, the following happens: +# +# - if the system locale is English (any variety), then the package is not +# installed at all, +# - otherwise `$LOCALE` or `${LOCALE}` is replaced by the 'lower-cased' BCP47 +# name of the 'language' part of the selected system locale (not the +# country/region/dialect part), e.g. selecting "nl_BE" will use "nl" +# here. +# +# Take care that just plain `LOCALE` will not be replaced, so `foo-LOCALE` will +# be left unchanged, while `foo-$LOCALE` will be changed. However, `foo-LOCALE` +# **will** be removed from the list of packages (i.e. not installed), if +# English is selected. If a non-English locale is selected, then `foo-LOCALE` +# will be installed, unchanged (no language-name-substitution occurs). +# +# The following installs localizations for vi, if they are relevant; if +# there is no localization, installation continues normally. +# +# - install +# - vi-$LOCALE +# - package: vi-${LOCALE} +# pre-script: touch /tmp/installing-vi +# post-script: rm -f /tmp/installing-vi +# +# When installing packages, Calamares will invoke the package manager +# with a list of package names if it can; package-data prevents this because +# of the scripts that need to run. In other words, this: +# +# - install: +# - vi +# - binutils +# - package: wget +# pre-script: touch /tmp/installing-wget +# +# This will invoke the package manager three times, once for each package, +# because not all of them are simple package names. You can speed up the +# process if you have only a few pre-scripts, by using multiple install targets: +# +# - install: +# - vi +# - binutils +# - install: +# - package: wget +# pre-script: touch /tmp/installing-wget +# +# This will call the package manager once with the package-names "vi" and +# "binutils", and then a second time for "wget". When installing large numbers +# of packages, this can lead to a considerable time savings. +# +operations: + - install: + - vi + - vi-${LOCALE} + - wget + - binutils + - remove: + - vi + - wget + - binutils diff --git a/airootfs/etc/calamares/modules/partition.conf b/airootfs/etc/calamares/modules/partition.conf new file mode 100644 index 0000000..e77c5d0 --- /dev/null +++ b/airootfs/etc/calamares/modules/partition.conf @@ -0,0 +1,392 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# + +# Options for EFI system partition. +# +# - *mountPoint* +# This setting specifies the mount point of the EFI system partition. Some +# distributions (Fedora, Debian, Manjaro, etc.) use /boot/efi, others (KaOS, +# etc.) use just /boot. +# +# Defaults to "/boot/efi", may be empty (but weird effects ensue) +# - *recommendedSize* +# This optional setting specifies the size of the EFI system partition. +# If nothing is specified, the default size of 300MiB will be used. +# When writing quantities here, M is treated as MiB, and if you really +# want one-million (10^6) bytes, use MB. +# - *minimumSize* +# This optional setting specifies the absolute minimum size of the EFI +# system partition. If nothing is specified, the *recommendedSize* +# is used instead. +# - *label* +# This optional setting specifies the name of the EFI system partition (see +# PARTLABEL; gpt only; requires KPMCore >= 4.2.0). +# If nothing is specified, the partition name is left unset. +# +# Going below the *recommended* size is allowed, but the user will +# get a warning that it might not work. Going below the *minimum* +# size is not allowed and the user will be told it will not work. +# +# Both quantities must be at least 32MiB, this is enforced by the EFI +# spec. If minimum is not specified, it defaults to the recommended +# size. Distros that allow more user latitude can set the minimum lower. +efi: + mountPoint: "/boot/efi" + recommendedSize: 300MiB + minimumSize: 32MiB + label: "EFI" + +# Deprecated alias of efi.mountPoint +# efiSystemPartition: "/boot/efi" + +# Deprecated alias of efi.recommendedSize +# efiSystemPartitionSize: 300MiB + +# Deprecated alias of efi.label +# efiSystemPartitionName: EFI + +# In autogenerated partitioning, allow the user to select a swap size? +# If there is exactly one choice, no UI is presented, and the user +# cannot make a choice -- this setting is used. If there is more than +# one choice, a UI is presented. +# +# Legacy settings *neverCreateSwap* and *ensureSuspendToDisk* correspond +# to values of *userSwapChoices* as follows: +# - *neverCreateSwap* is true, means [none] +# - *neverCreateSwap* is false, *ensureSuspendToDisk* is false, [small] +# - *neverCreateSwap* is false, *ensureSuspendToDisk* is true, [suspend] +# +# Autogenerated swap sizes are as follows: +# - *suspend*: Swap is always at least total memory size, +# and up to 4GiB RAM follows the rule-of-thumb 2 * memory; +# from 4GiB to 8 GiB it stays steady at 8GiB, and over 8 GiB memory +# swap is the size of main memory. +# - *small*: Follows the rules above, but Swap is at +# most 8GiB, and no more than 10% of available disk. +# In both cases, a fudge factor (usually 10% extra) is applied so that there +# is some space for administrative overhead (e.g. 8 GiB swap will allocate +# 8.8GiB on disk in the end). +# +# If *file* is enabled here, make sure to have the *fstab* module +# as well (later in the exec phase) so that the swap file is +# actually created. +userSwapChoices: + - none # Create no swap, use no swap + - small # Up to 4GB + - suspend # At least main memory size + # - reuse # Re-use existing swap, but don't create any (unsupported right now) + - file # To swap file instead of partition + +# This optional setting specifies the name of the swap partition (see +# PARTLABEL; gpt only; requires KPMCore >= 4.2.0). +# If nothing is specified, the partition name is left unset. +# swapPartitionName: swap + +# LEGACY SETTINGS (these will generate a warning) +# ensureSuspendToDisk: true +# neverCreateSwap: false + +# This setting specifies the LUKS generation (i.e LUKS1, LUKS2) used internally by +# cryptsetup when creating an encrypted partition. +# +# This option is set to luks1 by default, as grub doesn't support LUKS2 + Argon2id +# currently. On the other hand grub does support LUKS2 with PBKDF2 and could therefore be +# also set to luks2. Also there are some patches for grub and Argon2. +# See: https://aur.archlinux.org/packages/grub-improved-luks2-git +# +# Choices: luks1, luks2 (in addition, "luks" means "luks1") +# +# The default is luks1 +# +luksGeneration: luks1 + +# This setting determines if encryption should be allowed when using zfs. This +# setting has no effect unless zfs support is provided. +# +# This setting is to handle the fact that some bootloaders(such as grub) do not +# support zfs encryption. +# +# The default is true +# +# allowZfsEncryption: true + +# Correctly draw nested (e.g. logical) partitions as such. +drawNestedPartitions: false + +# Show/hide partition labels on manual partitioning page. +alwaysShowPartitionLabels: true + +# Allow manual partitioning. +# +# When set to false, this option hides the "Manual partitioning" button, +# limiting the user's choice to "Erase", "Replace" or "Alongside". +# This can be useful when using a custom partition layout we don't want +# the user to modify. +# +# If nothing is specified, manual partitioning is enabled. +#allowManualPartitioning: true + +# Show not encrypted boot partition warning. +# +# When set to false, this option does not show the +# "Boot partition not encrypted" warning when encrypting the +# root partition but not /boot partition. +# +# If nothing is specified, the warning is shown. +#showNotEncryptedBootMessage: true + +# Initial selection on the Choice page +# +# There are four radio buttons (in principle: erase, replace, alongside, manual), +# and you can pick which of them, if any, is initially selected. For most +# installers, "none" is the right choice: it makes the user pick something specific, +# rather than accidentally being able to click past an important choice (in particular, +# "erase" is a dangerous choice). +# +# The default is "none" +# +initialPartitioningChoice: none +# +# Similarly, some of the installation choices may offer a choice of swap; +# the available choices depend on *userSwapChoices*, above, and this +# setting can be used to pick a specific one. +# +# The default is "none" (no swap) if that is one of the enabled options, otherwise +# one of the items from the options. +initialSwapChoice: none + +# armInstall +# +# Leaves 16MB empty at the start of a drive when partitioning +# where usually the u-boot loader goes +# +# armInstall: false + +# Default partition table type, used when a "erase" disk is made. +# +# When erasing a disk, a new partition table is created on disk. +# In other cases, e.g. Replace and Alongside, as well as when using +# manual partitioning, this partition table exists already on disk +# and it is left unmodified. +# +# Possible values: gpt, msdos (or other names defined by KPMcore). +# Names are case-sensitive. +# +# If nothing is specified, Calamares defaults to "gpt" if system is +# efi or "msdos" otherwise. +# +# defaultPartitionTableType: msdos + +# Specify whether to create a partition table layout suitable for a hybrid +# (BIOS + EFI) bootloader installation. This will prepend both bios-boot and +# EFI system partitions to the partition layout, regardless of whether the +# booted system uses BIOS or EFI firmware. Defaults to false. +# +# createHybridBootloaderLayout: false + +# Requirement for partition table type +# +# Restrict the installation on disks that match the type of partition +# tables that are specified. +# +# Possible values: msdos, gpt (or other names defined by KPMcore). +# Names are case-sensitive. +# +# If nothing is specified, Calamares defaults to both "msdos" and "gpt". +# +# requiredPartitionTableType: gpt +# requiredPartitionTableType: +# - msdos +# - gpt + +# Default filesystem type, used when a "new" partition is made. +# +# When replacing a partition, the new filesystem type will be from the +# defaultFileSystemType value. In other cases, e.g. Erase and Alongside, +# as well as when using manual partitioning and creating a new +# partition, this filesystem type is pre-selected. Note that +# editing a partition in manual-creation mode will not automatically +# change the filesystem type to this default value -- it is not +# creating a new partition. +# +# Suggested values: ext2, ext3, ext4, reiser, xfs, jfs, btrfs +# If nothing is specified, Calamares defaults to "ext4". +# +# Names are case-sensitive and defined by KPMCore. +defaultFileSystemType: "ext4" + +# Selectable filesystem type, used when "erase" is done. +# +# When erasing the disk, the *defaultFileSystemType* is used (see +# above), but it is also possible to give users a choice: +# list suitable filesystems here. A drop-down is provided +# to pick which is the filesystems will be used. +# +# The value *defaultFileSystemType* is added to this list (with a warning) +# if not present; the default pick is the *defaultFileSystemType*. +# +# If not specified at all, uses *defaultFileSystemType* without a +# warning (this matches traditional no-choice-available behavior best). +# availableFileSystemTypes: ["ext4","f2fs"] + +# Per-directory filesystem restrictions. +# +# This optional setting specifies what filesystems the user can and cannot use +# for various directories and mountpoints when using manual partitioning. +# +# If nothing is specified, the only restriction enforced by default is that +# the EFI system partition must use the fat32 filesystem. +# +# Otherwise, the filesystem restrictions are defined as follow: +# +# directoryFilesystemRestrictions: +# - directory: "any" +# allowedFilesystemTypes: ["all"] +# - directory: "/" +# allowedFilesystemTypes: ["ext4","xfs","btrfs","jfs","f2fs"] +# - mountpoint: "efi" +# allowedFilesystemTypes: ["fat32"] +# onlyWhenMountpoint: true +# +# There can be any number of mountpoints listed, each entry having the +# following attributes: +# - mountpoint: mountpoint's full path +# or +# "any" to specify a global whitelist that applies to all +# mountpoints +# or +# "efi" to specify a whitelist specific to the EFI system +# partition, wherever that partition is located +# - allowedFilesystemTypes: the list of all filesystems valid for this +# mountpoint. If the list contains exactly one +# element, and that element is the special value +# "any", all filesystem types recognized by +# Calamares will be allowed. +# - onlyWhenMountpoint: Whether the restriction should apply only when the +# specified directory is a mountpoint. When set to +# true, Calamares will only enforce the listed +# restrictions when the user makes a separate partition +# for this directory and assigns the mountpoint +# accordingly. When set to false, Calamares will +# ensure this directory uses the specified filesystem +# even if the directory is part of a filesystem on a +# different mountpoint. Defaults to false. + +# The ClearMounts job unmounts / unmaps things before partitioning. +# Some special entries under /dev/mapper are excepted from this process. +# The example lists the three hard-coded exceptions which always apply +# (they don't need to be listed here). Add other names or wildcards (with +# a trailing '*') to this list if the live-ISO has additional mounts. +essentialMounts: [ "live-*", "control", "ventoy" ] + +# Show/hide LUKS related functionality in automated partitioning modes. +# Disable this if you choose not to deploy early unlocking support in GRUB2 +# and/or your distribution's initramfs solution. +# +# BIG FAT WARNING: +# +# This option is unsupported, as it cuts out a crucial security feature. +# Disabling LUKS and shipping Calamares without a correctly configured GRUB2 +# and initramfs is considered suboptimal use of the Calamares software. The +# Calamares team will not provide user support for any potential issue that +# may arise as a consequence of setting this option to false. +# It is strongly recommended that system integrators put in the work to support +# LUKS unlocking support in GRUB2 and initramfs/dracut/mkinitcpio/etc. +# For more information on setting up GRUB2 for Calamares with LUKS, see +# the Calamares website at https://calamares.io/docs/partitions/#luks . +# +# If nothing is specified, LUKS is enabled in automated modes. +#enableLuksAutomatedPartitioning: true + +# When enableLuksAutomatedPartitioning is true, this option will pre-check +# encryption checkbox. This option is only usefull to help people to not forget +# to cypher their disk when installing in enterprise (for example). +#preCheckEncryption: false + +# LVM support +# +# There is only one sub-key available, *enable* (defaults to true) +# which can be used to show (default) or hide the LVM buttons in the partitioning module. +lvm: + enable: true + +# Partition layout. +# +# This optional setting specifies a custom partition layout. +# +# If nothing is specified, the default partition layout is a single partition +# for root that uses 100% of the space and uses the filesystem defined by +# defaultFileSystemType. +# +# Note: the EFI system partition is prepended automatically to the layout if +# needed; the swap partition is appended to the layout if enabled (selections +# "small" or "suspend" in *userSwapChoices*). +# +# Otherwise, the partition layout is defined as follow: +# +# partitionLayout: +# - name: "rootfs" +# type: "4f68bce3-e8cd-4db1-96e7-fbcaf984b709" +# filesystem: "ext4" +# noEncrypt: false +# mountPoint: "/" +# size: 20% +# minSize: 500M +# maxSize: 10G +# attributes: 0xffff000000000003 +# - name: "home" +# type: "933ac7e1-2eb4-4f13-b844-0e14e2aef915" +# filesystem: "ext4" +# noEncrypt: false +# mountPoint: "/home" +# size: 3G +# minSize: 1.5G +# features: +# 64bit: false +# casefold: true +# - name: "data" +# filesystem: "fat32" +# mountPoint: "/data" +# features: +# sector-size: 4096 +# sectors-per-cluster: 128 +# size: 100% +# +# There can be any number of partitions, each entry having the following attributes: +# - name: filesystem label +# and +# partition name (gpt only; since KPMCore 4.2.0) +# - uuid: partition uuid (optional parameter; gpt only; requires KPMCore >= 4.2.0) +# - type: partition type (optional parameter; gpt only; requires KPMCore >= 4.2.0) +# - attributes: partition attributes (optional parameter; gpt only; requires KPMCore >= 4.2.0) +# - filesystem: filesystem type (optional parameter) +# - if not set at all, treat as "unformatted" +# - if "unformatted", no filesystem will be created +# - if "unknown" (or an unknown FS name, like "elephant") then the +# default filesystem type, or the user's choice, will be applied instead +# of "unknown" (e.g. the user might pick ext4, or xfs). +# - noEncrypt: whether this partition is exempt from encryption if enabled (optional parameter; default is false) +# - mountPoint: partition mount point (optional parameter; not mounted if unset) +# - size: partition size in bytes (append 'K', 'M' or 'G' for KiB, MiB or GiB) +# or +# % of the available drive space if a '%' is appended to the value +# - minSize: minimum partition size (optional parameter) +# - maxSize: maximum partition size (optional parameter) +# - features: filesystem features (optional parameter; requires KPMCore >= 4.2.0) +# name: boolean or integer or string + +# Checking for available storage +# +# This overlaps with the setting of the same name in the welcome module's +# requirements section. If nothing is set by the welcome module, this +# value is used instead. It is still a problem if there is no required +# size set at all, and the replace and resize options will not be offered +# if no required size is set. +# +# The value is in Gibibytes (GiB). +# +# BIG FAT WARNING: except for OEM-phase-0 use, you should be using +# the welcome module, **and** configure this value in +# `welcome.conf`, not here. +# requiredStorage: 3.5 diff --git a/airootfs/etc/calamares/modules/plasmalnf.conf b/airootfs/etc/calamares/modules/plasmalnf.conf new file mode 100644 index 0000000..105f247 --- /dev/null +++ b/airootfs/etc/calamares/modules/plasmalnf.conf @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The Plasma Look-and-Feel module allows selecting a Plasma +# Look-and-Feel in the live- or host-system and switches the +# host Plasma session immediately to the chosen LnF; it +# can also write a LnF configuration to the target user / on +# the target system. +# +# This module should be used once in a view section (to get +# the UI) and once in the exec section (to apply the selection +# to the target user). It should come **after** the user module +# in exec, so that the target user has been created alrady. +--- +# Full path to the Plasma look-and-feel tool (CLI program +# for querying and applying Plasma themes). If this is not +# set, no LNF setting will happen. +lnftool: "/usr/bin/lookandfeeltool" + +# For systems where the user Calamares runs as (usually root, +# via either sudo or pkexec) has a clean environment, set this +# to the originating username; the lnftool will be run through +# "sudo -H -u " instead of directly. +# +# liveuser: "live" + +# If *showAll* is true, then all installed themes are shown in the +# UI for selection, even if they are not listed in *themes* (below). +# This allows selection of all themes even while not all of them are +# listed in *themes* -- which is useful to show screenshots for those +# you do have a screenshot for. If *themes* is empty or missing, +# the value of *showAll* is treated as `true`. +showAll: false + +# You can limit the list of Plasma look-and-feel themes by listing ids +# here. If this key is not present, all of the installed themes are listed. +# If the key is present, only installed themes that are **also** included +# in the list are shown (could be none!). See the *showAll* key, above, +# to change that. +# +# Themes may be listed by id, (e.g. fluffy-bunny, below) or as a theme +# and an image (e.g. breeze) which will be used to show a screenshot. +# Themes with no image set at all get a "missing screenshot" image; if the +# image file is not found, they get a color swatch based on the image name. +# +# The image may be an absolute path. If it is a relative path, though, +# it is searched in the current directory and in the branding directory +# (i.e. relative to the directory where your branding.desc lives). +# +# Valid forms of entries in the *themes* key: +# - A single string (unquoted), which is the theme id +# - A pair of *theme* and *image* keys, e.g. +# ``` +# - theme: fluffy-bunny.desktop +# image: "fluffy-screenshot.png" +# ``` +# +# The image screenshot is resized to 12x8 the current font size, with +# a minimum of 120x80 pixels. This allows the screenshot to scale up +# on HiDPI displays where the fonts are larger (in pixels). +themes: + - org.kde.fuzzy-pig.desktop + - theme: org.kde.breeze.desktop + image: "breeze.png" + - theme: org.kde.breezedark.desktop + image: "breeze-dark.png" + - org.kde.fluffy-bunny.desktop + +# You can pre-select one of the themes; it is not applied +# immediately, but its radio-button is switched on to indicate +# that that is the theme (that is most likely) currently in use. +# Do this only on Live images where you are reasonably sure +# that the user is not going to change the theme out from under +# themselves before running the installer. +# +# If this key is present, its value should be the id of the theme +# which should be pre-selected. If absent, empty, or the pre-selected +# theme is not found on the live system, no theme will be pre-selected. +# +# As a special setting, use "*", to try to find the currently- +# selected theme by reading the Plasma configuration. This requires +# KF5::Config at build- and run-time. +preselect: "*" diff --git a/airootfs/etc/calamares/modules/plymouthcfg.conf b/airootfs/etc/calamares/modules/plymouthcfg.conf new file mode 100644 index 0000000..ebe51d1 --- /dev/null +++ b/airootfs/etc/calamares/modules/plymouthcfg.conf @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Plymouth Configuration Module +# +# This module can be used to setup the default plymouth theme to +# be used with your distribution +# +# You should only use this module if the plymouth package is installed +# on the build configurations of your distribution & the plymouth +# theme you want to configure is installed as well. If the unpacked +# filesystem configures a plymouth theme already, there is no need +# to change it here. +--- + + +# Leave this commented if you want to use the default theme +# shipped with your distribution configurations. Make sure that +# the theme exists in the themes directory of plymouth path. +# Debian / Ubuntu comes with themes "joy", "script", "softwaves", +# possibly others. Look in /usr/share/plymouth/themes for more. +# +# Specifying a non-existent theme will leave the plymouth +# configuration set to that theme. It is up to plymouth to +# deal with that. + +plymouth_theme: spinfinity + + + + diff --git a/airootfs/etc/calamares/modules/preservefiles.conf b/airootfs/etc/calamares/modules/preservefiles.conf new file mode 100644 index 0000000..75584f5 --- /dev/null +++ b/airootfs/etc/calamares/modules/preservefiles.conf @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the preserve-files job +# +# The *files* key contains a list of files to preserve. Each element of +# the list should have one of these forms: +# +# - an absolute path (probably within the host system). This will be preserved +# as the same path within the target system (chroot). If, globally, +# *dontChroot* is true, then these items will be ignored (since the +# destination is the same as the source). +# - a map with a *dest* key. The *dest* value is a path interpreted in the +# target system (if the global *dontChroot* is true, then the host is the +# target as well). Relative paths are not recommended. There are two +# ways to select the source data for the file: +# - *from*, which must have one of the values, below; it is used to +# preserve files whose pathname is known to Calamares internally. +# - *src*, to refer to a path interpreted in the host system. Relative +# paths are not recommended, and are interpreted relative to where +# Calamares is being run. +# Exactly one of the two source keys (either *from* or *src*) must be set. +# +# Special values for the key *from* are: +# - *log*, for the complete log file (up to the moment the preservefiles +# module is run), +# - *config*, for a JSON dump of the contents of global storage. +# Note that this may contain sensitive information, and should be +# given restrictive permissions. +# +# A map with a *dest* key can have these additional fields: +# - *perm*, is a colon-separated tuple of :: +# where is in octal (e.g. 4777 for wide-open, 0400 for read-only +# by owner). If set, the file's ownership and permissions are set to +# those values within the target system; if not set, no permissions +# are changed. +# - *optional*, is a boolean; if this is set to `true` then failure to +# preserve the file will **not** be counted as a failure of the +# module, and installation will proceed. Set this for files that might +# not exist in the host system (e.g. nvidia configuration files that +# are created in some boot scenarios and not in others). +# +# The target path (*dest*) is modified by expanding variables in `${}`: +# - `ROOT` is replaced by the path to the target root (may be /). +# There is never any reason to use this, since the *dest* is already +# interpreted in the target system. +# - `USER` is replaced by the username entered by on the user +# page (may be empty, for instance if no user page is enabled) +# +# +# +files: + - from: log + dest: /var/log/Calamares.log + perm: root:wheel:600 + - from: log + dest: /home/${USER}/installation.log + optional: true + - from: config + dest: /var/log/Calamares-install.json + perm: root:wheel:600 +# - src: /var/log/nvidia.conf +# dest: /var/log/Calamares-nvidia.conf +# optional: true + +# The *perm* key contains a default value to apply to all files listed +# above that do not have a *perm* key of their own. If not set, +# root:root:0400 (highly restrictive) is used. +# +# perm: "root:root:0400" diff --git a/airootfs/etc/calamares/modules/rawfs.conf b/airootfs/etc/calamares/modules/rawfs.conf new file mode 100644 index 0000000..bbc3690 --- /dev/null +++ b/airootfs/etc/calamares/modules/rawfs.conf @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the rawfs module: raw filesystem copy to a block device + +--- + +# To apply a custom partition layout, it has to be defined as a list of targets. +# +# For each target, the following attributes must be defined: +# * mountPoint: The mount point of the destination device on the installed system +# The corresponding block device will automatically be identified and used as the +# destination for the operation +# * source: The source filesystem; it can be the mount point of a locally (on the +# live system) mounted filesystem, a path to a disk image, or a block device +# * resize (optional): Expand the destination filesystem to fill the whole +# partition at the end of the operation; this works only with ext filesystems +# for now + +targets: + - mountPoint: / + source: / + - mountPoint: /home + source: /images/home.img + resize: true + - mountPoint: /data + source: /dev/mmcblk0p3 + +# To support testing, set the *bogus* key to true. No actual work is done, but the +# module's logic is exercised. + +# bogus: false diff --git a/airootfs/etc/calamares/modules/refind/main.py b/airootfs/etc/calamares/modules/refind/main.py new file mode 100644 index 0000000..0239aaf --- /dev/null +++ b/airootfs/etc/calamares/modules/refind/main.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2021 Anke Boersma +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares + +import os +import subprocess + +from libcalamares.utils import check_target_env_call + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Install rEFInd.") + + +def get_uuid(): + partitions = libcalamares.globalstorage.value("partitions") + for partition in partitions: + if partition["mountPoint"] == "/": + libcalamares.utils.debug(partition["uuid"]) + return partition["uuid"] + return None + + +def update_conf(uuid, conf_path): + """ + Updates the created rEFInd configuration file based on given parameters. + """ + partitions = libcalamares.globalstorage.value("partitions") + + kernel_params = ["quiet", "systemd.show_status=0"] + swap = None # Partition UUID + swap_luks = None # LUKS name + cryptdevice_params = [] + btrfs_params = "" + + for partition in partitions: + if partition["fs"] == "linuxswap" and not "luksMapperName" in partition: + swap = partition["uuid"] + + if partition["fs"] == "linuxswap" and "luksMapperName" in partition: + swap_luks = partition["luksMapperName"] + + if partition["mountPoint"] == "/" and "luksMapperName" in partition: + cryptdevice_params = [ + "cryptdevice=UUID={!s}:{!s}".format(partition["luksUuid"], + partition["luksMapperName"]), + "root=/dev/mapper/{!s}".format(partition["luksMapperName"]), + "resume=/dev/mapper/{!s}".format(partition["luksMapperName"]) + ] + + # rEFInd with a BTRFS root filesystem needs to be told + # about the root subvolume. + if partition["mountPoint"] == "/" and partition["fs"] == "btrfs": + btrfs_params = "rootflags=subvol=@" + + if cryptdevice_params: + kernel_params.extend(cryptdevice_params) + else: + kernel_params.append("root=UUID={!s}".format(uuid)) + + if swap: + kernel_params.append("resume=UUID={!s}".format(swap)) + if swap_luks: + kernel_params.append("resume=/dev/mapper/{!s}".format(swap_luks)) + if btrfs_params: + kernel_params.append(btrfs_params) + + with open(conf_path, "r") as refind_file: + filedata = [x.strip() for x in refind_file.readlines()] + + with open(conf_path, 'w') as refind_file: + for line in filedata: + if line.startswith('"Boot with standard options"'): + line = '"Boot with standard options" "rw {!s}"'.format(" ".join(kernel_params)) + refind_file.write(line + "\n") + + +def efi_partitions(efi_boot_path): + """ + The (one) partition mounted on @p efi_boot_path, or an empty list. + """ + return [p for p in libcalamares.globalstorage.value("partitions") if p["mountPoint"] == efi_boot_path] + + +def install_refind(): + install_path = libcalamares.globalstorage.value("rootMountPoint") + uuid = get_uuid() + conf_path = os.path.join(install_path, "boot/refind_linux.conf") + + # TODO: some distro's use /boot/efi , so maybe this needs to + # become configurable (that depends on what rEFInd likes). + efi_boot_path = "/boot" + + # Might not have a /boot configured in the system at all; warn and don't operate + if not efi_partitions(efi_boot_path): + libcalamares.utils.warning("No partition mounted on {!s}".format(efi_boot_path)) + # This isn't returned as an error, but the installation + # probably won't boot because no bootloader was installed. + return None + + subprocess.call( + ["refind-install", "--root", "{!s}".format(install_path)]) + update_conf(uuid, conf_path) + + +def run(): + """ + Optional entry for when providing bootloader choices. + Values taken from a packagechooser instance. + Module won't run, if value not present. + """ + bootchoice = libcalamares.globalstorage.value("packagechooser_bootchoice") + + if bootchoice == "refind": + return install_refind() diff --git a/airootfs/etc/calamares/modules/refind/module.desc b/airootfs/etc/calamares/modules/refind/module.desc new file mode 100644 index 0000000..342d60c --- /dev/null +++ b/airootfs/etc/calamares/modules/refind/module.desc @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "refind" +interface: "python" +script: "main.py" +noconfig: true +# The partition module sets up the needed paths in +# global storage, which is used to decide how to install. +requiredModules: [ "partition" ] diff --git a/airootfs/etc/calamares/modules/refind/refind.conf.in b/airootfs/etc/calamares/modules/refind/refind.conf.in new file mode 100644 index 0000000..ad587c3 --- /dev/null +++ b/airootfs/etc/calamares/modules/refind/refind.conf.in @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +## This file should be present in the same directory as the EFISTUB kernel and initramfs files +## More info at http://www.rodsbooks.com/refind/linux.html , http://www.rodsbooks.com/efi-bootloaders/efistub.html +## File is not needed when rEFInd is installed with the `refind-install` option, it will be created automatically. + +#"Boot with defaults" "root=PARTUUID=XXXXXXXX rootfstype=XXXX rw add_efi_memmap" +#"Boot to terminal" "root=PARTUUID=XXXXXXXX rootfstype=XXXX rw add_efi_memmap systemd.unit=multi-user.target" + + diff --git a/airootfs/etc/calamares/modules/removeuser.conf b/airootfs/etc/calamares/modules/removeuser.conf new file mode 100644 index 0000000..cc086e7 --- /dev/null +++ b/airootfs/etc/calamares/modules/removeuser.conf @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Removes a single user (with userdel) from the system. +# This is typically used in OEM setups or if the live user +# spills into the target system. +# +# The module never fails; if userdel fails, this is logged +# but the module still reports success and installation / setup +# continues as normal. +--- +# Username in the target system to be removed. +username: live diff --git a/airootfs/etc/calamares/modules/services-systemd.conf b/airootfs/etc/calamares/modules/services-systemd.conf new file mode 100644 index 0000000..330a94c --- /dev/null +++ b/airootfs/etc/calamares/modules/services-systemd.conf @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Systemd units manipulation. +# +# This module can perform actions using systemd units, +# (for example, enabling, disabling, or masking services, sockets, paths, etc.) +--- + +# There is one key for this module: *units*. Its value is a list of entries. +# Each entry has three keys: +# - *name* is the (string) name of the systemd unit that is being changed. +# Use quotes. You can use any valid systemd unit here (for example, +# "NetworkManager.service", "cups.socket", "lightdm", "gdm", etc.) +# - *action* is the (string) action that you want to perform over the unit +# (for example, "enable", "disable", "mask", "unmask", etc.). Please +# ensure that the action can actually run under chroot (otherwise it is +# pointless) +# - *mandatory* is a boolean option, which states whether the change +# must be done successfully. If systemd reports an error while changing +# a mandatory entry, the installation will fail. When mandatory is false, +# errors for that systemd unit are ignored. If mandatory +# is not specified, the default is false. +# +# The order of operations is the same as the order in which entries +# appear in the list + +# # This example enables NetworkManager.service (and fails if it can't), +# # disables cups.socket (and ignores failure). Then it enables the +# # graphical target (e.g. so that SDDM runs for login), and +# # finally masks pacman-init (an ArchLinux-only service). +# # +# units: +# - name: "NetworkManager.service" +# action: "enable" +# mandatory: true +# +# - name: "cups.socket" +# action: "disable" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified +# +# - name: "graphical.target" +# action: "enable" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified +# +# - name: "pacman-init.service" +# action: "mask" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified + +# By default, no changes are made. +units: [] diff --git a/airootfs/etc/calamares/modules/shellprocess.conf b/airootfs/etc/calamares/modules/shellprocess.conf new file mode 100644 index 0000000..709d2a8 --- /dev/null +++ b/airootfs/etc/calamares/modules/shellprocess.conf @@ -0,0 +1,146 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the shell process job. +# +# Executes a list of commands found under the key *script*. +# If the top-level key *dontChroot* is true, then the commands +# are executed in the context of the live system, otherwise +# in the context of the target system. In all of the commands, +# the following variable expansions will take place: +# - `ROOT` is replaced by the root mount point of the **target** +# system from the point of view of the command (when run in the target +# system, e.g. when *dontChroot* is false, that will be `/`). +# - `USER` is replaced by the username, set on the user page. +# - `LANG` is replaced by the language chosen for the user-interface +# of Calamares, set on the welcome page. This may not reflect the +# chosen system language from the locale page. +# +# As a special case, variables of the form `gs[key]` where `key` is +# a dotted-keys string and `gs` is literally the letters `g` and `s`, +# use **any** value from Global Storage. For example, +# +# gs[branding.bootloader] +# +# This variable refers to the GS value stored in `bootloader` in the +# `branding` map. Examine the Debug window for information about the +# keys stored in GS. Only strings and integers are exposed this way, +# lists and other data types do not set any variable this way. +# +# Variables are written as `${var}`, e.g. `${ROOT}`. +# Write `$$` to get a shell-escaped `\$` in the shell command. +# It is not possible to get an un-escaped `$` in the shell command +# (either the command will fail because of undefined variables, or +# you get a shell-escaped `\$`). +# +# The (global) timeout for the command list can be set with +# the *timeout* key. The value is a time in seconds, default +# is 30 seconds if not set. The timeout **must** be tuned, either +# globally or per-command (see below in the description of *script*), +# to the load or expected running-time of the command. +# +# - Setting a timeout of 30 for a `touch` command is probably exessive +# - Setting a timeout of 1 for a `touch` command might be low, +# on a slow disk where touch needs to be loaded from CDROM +# - Setting a timeout of 30 for a 1GB download is definitely low +# - Setting a timeout of 3600 for a 1GB download is going to leave +# the user in uncertainty for a loooong time. +# +# The (global) verbosity of a command can be set to `true` or `false`. +# When set to `true`, command output is logged one line at a time. +# Otherwise the output is logged when the command completes. +# Line-at-a-time logging is appropriate for commands that take +# a long time to complete and produce their own (progress) output. +# +# If a command starts with "-" (a single minus sign), then the +# return value of the command following the - is ignored; otherwise, +# a failing command will abort the installation. This is much like +# make's use of - in a command. +# +# The value of *script* may be: +# - a single string; this is one command that is executed. +# - a single object (see below). +# - a list of items; these are executed one at a time, by +# separate shells (/bin/sh -c is invoked for each command). +# Each list item may be: +# - a single string; this is one command that is executed. +# - a single object, specifying a key *command* and (optionally) +# a key *timeout* to set the timeout for this specific +# command differently from the global setting. An optional +# key *environment* is a list of strings to put into the +# environment of the command. An optional key *verbose* +# overrides the global *verbose* setting in this file. +# +# Using a single object is not generally useful because the same effect +# can be obtained with a single string and a global timeout, except +# when the command needs environment-settings. When there are +# multiple commands to execute, one of them might have +# a different timeout than the others. +# +# The environment strings should all be "KEY='some value'" strings, +# as if they can be typed into the shell. Quoting the environment +# strings with "" in YAML is recommended. Adding the '' quotes ensures +# that the value will not be interpreted by the shell. Writing +# environment strings is the same as placing `export KEY='some value' ;` +# in front of the *command*. +# +# Calamares variable expansion is **also** done on the environment strings. +# Write `$$` to get a literal `$` in the shell command. +# +# To change the description of the job, set the *name* entries in *i18n*. +--- +# Set to true to run in host, rather than target system +dontChroot: false + +# Tune this for the commands you're actually running, or +# use the list-of-items form of commands to tune the timeout +# for each command individually. +timeout: 10 + +# This will copy the output from the command into the Calamares +# log file. No processing is done beyond log-each-line-separately, +# so this can introduce weirdness in the log if the script +# outputs e.g. escape codes. +# +# The default is `false`. This can also be set for each +# command individually. +verbose: false + +# Script may be a single string (because false returns an error exit +# code, this will trigger a failure in the installation): +# +# script: "/usr/bin/false" + +# Script may be a list of strings (because false returns an error exit +# code, **but** the command starts with a "-", the error exit is +# ignored and installation continues): +# +# script: +# - "-/usr/bin/false" +# - "/bin/ls" +# - "/usr/bin/true" + +# Script may be a list of items +# - if the touch command fails, it is ignored +# - there is nothing special about the invocation of true +# - the slowloris command has a different timeout from the other commands +# - the echo command logs its output line-by-line +script: + - "-touch ${ROOT}/tmp/thingy" + - "/usr/bin/true" + - command: "/usr/local/bin/slowloris" + timeout: 3600 + - command: "echo -e '\e[33;2mred\e[33;0m' ; echo second line" + verbose: true + +# You can change the description of the job (as it is displayed in the +# progress bar during installation) by defining an *i18n* key, which +# has a *name* field and optionally, translations as *name[lang]*. +# +# Without a translation here, the default name from the source code +# is used, "Shell Processes Job". +# +# i18n: +# name: "Shell process" +# name[nl]: "Schelpenpad" +# name[en_GB]: "Just a moment" diff --git a/airootfs/etc/calamares/modules/slowpython/main.py b/airootfs/etc/calamares/modules/slowpython/main.py new file mode 100644 index 0000000..d41f316 --- /dev/null +++ b/airootfs/etc/calamares/modules/slowpython/main.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# Calamares is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Calamares is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Calamares. If not, see . +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# License-Filename: LICENSES/GPL-3.0 + +""" +The slowpython module is slow. +""" + +import libcalamares +from time import sleep + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Slow python job.") + +status = _("Slow python step {}/10").format(0) + +def pretty_status_message(): + return status + +def run(): + """Slow python job.""" + try: + timeout = int(libcalamares.job.timeout) + except: + timeout = 30 + + if not (3 <= timeout <= 600): + timeout = 30 + + libcalamares.utils.debug("Slow python job for {} seconds".format(timeout)) + + global status + step = timeout / 10.0 + for x in range(11): + status = _("Slow python step {}/10").format(x) + libcalamares.job.setprogress(x / 10.0) + sleep(step) + return None diff --git a/airootfs/etc/calamares/modules/slowpython/module.desc b/airootfs/etc/calamares/modules/slowpython/module.desc new file mode 100644 index 0000000..5320003 --- /dev/null +++ b/airootfs/etc/calamares/modules/slowpython/module.desc @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The slowpython module is .. just slow. It can be used +# in testing to allow the slideshow time to run. +--- +type: "job" +interface: "python" +name: "slowpython" +script: "main.py" diff --git a/airootfs/etc/calamares/modules/slowpython/slowpython.conf b/airootfs/etc/calamares/modules/slowpython/slowpython.conf new file mode 100644 index 0000000..d366768 --- /dev/null +++ b/airootfs/etc/calamares/modules/slowpython/slowpython.conf @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# This is an example module for Python Job Modules. +# +# The slowpython module is just slow. It does produce +# progress output, in 10 steps during the timeout period. +--- + +# Number of seconds this module will take to run +timeout: 30 diff --git a/airootfs/etc/calamares/modules/tracking.conf b/airootfs/etc/calamares/modules/tracking.conf new file mode 100644 index 0000000..fdbd355 --- /dev/null +++ b/airootfs/etc/calamares/modules/tracking.conf @@ -0,0 +1,105 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Settings for various kinds of tracking that Distributions can +# enable. Distributions looking at tracking should be aware of +# the privacy (and hence communications) impact of that tracking, +# and are advised to consult the Mozilla and KDE policies on +# privacy and user tracking. +# +# There are three areas of tracking (-configuration) supported +# by Calamares It is up to individual Distributions to create +# suitable backends / configuration scripts for each. The +# different areas are: +# +# install: This is "phone home" functionality at the end of the +# install process. When enabled, it contacts the given +# URL. The URL can contain the special token $MACHINE, +# which is replaced by the machine-id of the installed +# system (if available, blank otherwise). +# +# machine: This enables machine-level tracking on a (semi-) +# continuous basis. It is meant to keep track of installed +# systems and their continued use / updating. +# +# user: This area enables user-level tracking, along the lines +# of the KDE User Telemetry Policy. It enables specific +# collection of data at a user- and application-level, +# possibly including actions done in an application. +# For the KDE environment, this enables user tracking +# with the appropriate framework, and the KDE User Telemetry +# policy applies. +# +# Each area has a key *enabled*. If the area is enabled, it is shown to +# the user. This defaults to false, which means no tracking would be +# configured or enabled by Calamares. +# +# Each area has a key *policy*, which is a Url to be opened when +# the user clicks on the corresponding Help button for an explanation +# of the details of that particular kind of tracking. If no policy +# is set, that tracking style is disabled. The example policy links +# go to Calamares' generic user manual (which is a terrible idea +# for a distribution: you have GDPR obligations under most of these +# tracking styles, so do your homework). +# +# Each area may have other configuration keys, depending on the +# area and how it needs to be configured. +# +# Globally, there are two other keys: +# +# policy: (optional) url about tracking settings for this distro. +# default: (optional) level to enable by default +# +--- +# This is the global policy; it is displayed as a link on the page. +# If blank or commented out, no link is displayed on the tracking +# page. You **must** provide policy links per-area as well. +policy: "https://calamares.io/docs/tracking#policy" + +# This is the default area to enable for tracking. If commented out, +# empty, or otherwise invalid, "none" is used, so no tracking by default. +# Setting an area here also checks the areas before it (install, machine, +# then user) by default -- subject to those areas being enabled at all. +# default: user + +# The install area has one specific configuration key: +# url: this URL (remember to include the protocol, and prefer https) +# is fetched (with a GET request, and the data discarded) at +# the end of the installation process. The following tokens +# are replaced in the url (possibly by blank strings, or by 0). +# - $CPU (cpu make and model) +# - $MEMORY (amount of main memory available) +# - $DISK (total amount of disk attached) +# Typically these are used as GET parameters, as in the example. +# +# Note that phone-home only works if the system has an internet +# connection; it is a good idea to require internet in the welcome +# module then. +install: + enabled: false + policy: "https://calamares.io/docs/tracking#policy" + url: "https://example.com/install.php?c=$CPU&m=$MEMORY" + +# The machine area has one specific configuration key: +# style: This string specifies what kind of tracking configuration +# needs to be done. See below for valid styles. +# +# Available styles: +# - *updatemanager* replaces the literal string "${MACHINE_ID}" with the contents of +# /etc/machine-id, in lines starting with "URI" in the file /etc/update-manager/meta-release +machine: + enabled: false + style: updatemanager + policy: "https://calamares.io/docs/tracking#policy" + +# The user area has one specific configuration key: +# style: This string specifies what kind of tracking configuration +# needs to be done. See below for valid styles. +# +# Available styles: +# - *kuserfeedback* sets up KUserFeedback tracking (applicable to the KDE +# Plasma Desktop) for each KUserFeedback area listed in *areas*. +user: + enabled: false + style: kuserfeedback + areas: [ PlasmaUserFeedback ] diff --git a/airootfs/etc/calamares/modules/umount.conf b/airootfs/etc/calamares/modules/umount.conf new file mode 100644 index 0000000..5842c87 --- /dev/null +++ b/airootfs/etc/calamares/modules/umount.conf @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +### Umount Module +# +# This module represents the last part of the installation, the unmounting +# of partitions used for the install. After this, there is no regular way +# to modify the target system anymore. +# + +--- +# Setting emergency to true will make it so this module is still run +# when a prior module fails +emergency: true diff --git a/airootfs/etc/calamares/modules/unpackfs.conf b/airootfs/etc/calamares/modules/unpackfs.conf new file mode 100644 index 0000000..cfd2f0b --- /dev/null +++ b/airootfs/etc/calamares/modules/unpackfs.conf @@ -0,0 +1,121 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Unsquash / unpack a filesystem. Multiple sources are supported, and +# they may be squashed or plain filesystems. +# +# Configuration: +# +# from globalstorage: rootMountPoint +# from job.configuration: the path to where to mount the source image(s) +# for copying an ordered list of unpack mappings for image file <-> +# target dir relative to rootMountPoint. + +--- +# Each list item is unpacked, in order, to the target system. +# +# Each list item has the following **mandatory** attributes: +# - *source* path relative to the live / intstalling system to the image +# - *sourcefs* the type of the source files; valid entries are +# - `ext4` (copies the filesystem contents) +# - `squashfs` (unsquashes) +# - `file` (copies a file or directory) +# - (may be others if mount supports it) +# - *destination* path relative to rootMountPoint (so in the target +# system) where this filesystem is unpacked. It may be an +# empty string, which effectively is / (the root) of the target +# system. +# +# Each list item **optionally** can include the following attributes: +# - *exclude* is a list of values that is expanded into --exclude +# arguments for rsync (each entry in exclude gets its own --exclude). +# - *excludeFile* is a single file that is passed to rsync as an +# --exclude-file argument. This should be a full pathname +# inside the **host** filesystem. +# - *weight* is useful when the entries take wildly different +# times to unpack (e.g. with a squashfs, and one single file) +# and the total weight of this module should be distributed +# differently between the entries. (This is only relevant when +# there is more than one entry; by default all the entries +# have the same weight, 1) +# +# EXAMPLES +# +# Usually you list a filesystem image to unpack; you can use +# squashfs or an ext4 image. An empty destination is equivalent to "/", +# the root of the target system. The destination directory must exist +# in the target system. +# +# - source: "/path/to/filesystem.sqfs" +# sourcefs: "squashfs" +# destination: "" +# +# Multiple entries are unpacked in-order; if there is more than one +# item then only the first must exist beforehand -- it's ok to +# create directories with one unsquash and then to use those +# directories as a target from a second unsquash. +# +# - source: "/path/to/another/filesystem.img" +# sourcefs: "ext4" +# destination: "" +# - source: "/path/to/another/filesystem2.img" +# sourcefs: "ext4" +# destination: "/usr/lib/extra" +# +# You can list filesystem source paths relative to the Calamares run +# directory, if you use -d (this is only useful for testing, though). +# +# - source: ./example.sqfs +# sourcefs: squashfs +# destination: "" +# +# You can list individual files (copied one-by-one), or directories +# (the files inside this directory are copied directly to the destination, +# so no "dummycpp/" subdirectory is created in this example). +# Do note that the target directory must exist already (e.g. from +# extracting some other filesystem). +# +# - source: ../CHANGES +# sourcefs: file +# destination: "/tmp/derp" +# - source: ../src/modules/dummycpp +# sourcefs: file +# destination: "/tmp/derp" +# +# The *destination* and *source* are handed off to rsync, so the semantics +# of trailing slashes apply. In order to *rename* a file as it is +# copied, specify one single file (e.g. CHANGES) and a full pathname +# for its destination name, as in the example below. +# +# It is also possible to dynamically (conditionally) unpack a source by passing a boolean +# value for *condition*. This may be true or false (constant) or name a globalstorage +# value. Use '.' to separate parts of a globalstorage name if it is nested. +# +# This is used in e.g. stacked squashfses, where the user can select a specific +# install type. The default value of *condition* is true. +# +# - source: ./example.minimal.sqfs +# sourcefs: squashfs +# destination: "" +# condition: false +# - source: ./example.standard.sqfs +# sourcefs: squashfs +# destination: "" +# condition: exampleGlobalStorageVariable.subkey +# +# You may also wish to include optional squashfses, which may not exist at certain times +# depending on your image tooling. If an optional squashfs is not found, it is simply +# skipped. +# +# - source: ./example.standard.sqfs +# sourcefs: squashfs +# destination: "" +# - source: ./example.extras.sqfs +# sourcefs: squashfs +# destination: "" +# optional: true + +unpack: + - source: "/run/archiso/bootmnt/arch/x86_64/airootfs.sfs" + sourcefs: "squashfs" + destination: "" diff --git a/airootfs/etc/calamares/modules/unpackfsc.conf b/airootfs/etc/calamares/modules/unpackfsc.conf new file mode 100644 index 0000000..b8780ac --- /dev/null +++ b/airootfs/etc/calamares/modules/unpackfsc.conf @@ -0,0 +1,74 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Unpack a filesystem. Supported ways to "pack" the filesystem are: +# - erofs +# Enhanced Read-Only File System is a relatively new filesystem, introduced +# in 2019 with Linux 5.4, which is optimised for runtime performance. EROFS +# has compression options similar to that of SquashFS, however, in +# direct head to head comparison, as of 2025, the EROFS compression +# algorithms are not as performant; it takes longer to pack a filesystem and +# the compression size is not quite as good. EROFS is under active +# development. +# . +# Wikipedia: https://en.wikipedia.org/wiki/EROFS +# Upstream documentation: https://erofs.docs.kernel.org/en/latest/ +# . +# erofs-utils contains mkfs.erofs, fsck.erofs and dump.erofs utilities +# which are required to pack, inspect and extract erofs files. +# - fsarchiver in *savedir/restdir* mode (directories, not block devices) +# - squashfs +# SquashFS is a compressed read-only file system for Linux that was +# introduced in 2002. It supports deduplication and has mature and +# performant compression algorithms. +# . +# Wikipedia: https://en.wikipedia.org/wiki/SquashFS +# Upstream documentation: https://github.com/plougher/squashfs-tools +# . +# squashfs-tools contains mksquashfs and unsquashfs utilities which are +# required to pack and extract squashfs files. +# +# Configuration: +# +# from globalstorage: rootMountPoint +# from job configuration: the item to unpack +# + +--- +# This module is configured a lot like the items in the *unpackfs* +# module, but with only **one** item. Use multiple instances for +# unpacking more than one filesystem. +# +# There are the following **mandatory** keys: +# - *source* path relative to the live / intstalling system to the image +# - *sourcefs* the type of the source files; valid entries are +# - `none` (this entry is ignored; kind of useless) +# - `erofs` +# - `fsarchiver` +# Aliases of this are `fsarchive`, `fsa` and `fsa-dir`. Uses +# fsarchiver in "restdir" mode. +# - `fsarchiver-block` +# Aliases of this are `fsa-block` and `fsa-fs`. Uses fsarchiver +# in "restfs" mode. +# - `squashfs` +# Aliases of this are `squash` and `unsquash`. +# - `tar` +# - *destination* path relative to rootMountPoint (so in the target +# system) where this filesystem is unpacked. It may be an +# empty string, which effectively is / (the root) of the target +# system. +# +# +# There are the following **optional** keys: +# - *condition* sets a dynamic condition on unpacking the item in +# this job. This may be true or false (constant) or name a globalstorage +# value. Use '.' to separate parts of a globalstorage name if it is nested. +# Remember to quote names. +# +# A condition is used in e.g. stacked squashfses, where the user can select +# a specific install type. The default value of *condition* is true. + +source: /data/rootfs.fsa +sourcefs: fsarchiver +destination: "/" +# condition: true diff --git a/airootfs/etc/calamares/modules/users.conf b/airootfs/etc/calamares/modules/users.conf new file mode 100644 index 0000000..0a9adf5 --- /dev/null +++ b/airootfs/etc/calamares/modules/users.conf @@ -0,0 +1,312 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the one-user-system user module. +# +# Besides these settings, the users module also places the following +# keys into the Global Storage area, based on user input in the view step. +# +# - hostname +# - username +# - password (obscured) +# - autologinUser (if enabled, set to username) +# +# These Global Storage keys are set when the configuration for this module +# is read and when they are modified in the UI. +--- +### GROUPS CONFIGURATION +# +# The system has groups of uses. Some special groups must be +# created during installation. Optionally, there are special +# groups for users who can use sudo and for supporting autologin. + +# Used as default groups for the created user. +# Adjust to your Distribution defaults. +# +# Each entry in the *defaultGroups* list is either: +# - a string, naming a group; this is a **non**-system group +# which does not need to exist in the target system; if it +# does not exist, it will be created. +# - an entry with subkeys *name*, *must_exist* and *system*; +# if the group *must_exist* and does not, an error is thrown +# and the installation fails. +# +# The group is created if it does not exist, and it is +# created as a system group (GID < 1000) or user group +# (GID >= 1000) depending on the value of *system*. +defaultGroups: + - name: users + must_exist: true + system: true + - lp + - video + - network + - storage + - name: wheel + must_exist: false + system: true + - audio + - name: nopasswdlogin + must_exist: false + system: true + +# When *sudoersGroup* is set to a non-empty string, Calamares creates a +# sudoers file for the user. This file is located at: +# `/etc/sudoers.d/10-installer` +# Remember to add the (value of) *sudoersGroup* to *defaultGroups*. +# +# If your Distribution already sets up a group of sudoers in its packaging, +# remove this setting (delete or comment out the line below). Otherwise, +# the setting will be duplicated in the `/etc/sudoers.d/10-installer` file, +# potentially confusing users. +sudoersGroup: wheel + +# Some Distributions require a 'autologin' group for the user. +# Autologin causes a user to become automatically logged in to +# the desktop environment on boot. +# Disable when your Distribution does not require such a group. +# +# Remember to add the (value of) *autologinGroup* to *defaultGroups*. +autologinGroup: autologin + +# See also *user.nopasswd_group* for another group that is optionally added to the user + +### ROOT AND SUDO +# +# Some distributions have a root user enabled for login. Others +# rely entirely on sudo or similar mechanisms to raise privileges. + +# If set to `false` (the default), writes a sudoers file with `ALL=(ALL)` +# so that commands can be run as any user. If set to `true`, writes +# `ALL=(ALL:ALL)` so that any user and any group can be chosen. +sudoersConfigureWithGroup: false + +# Setting this to false, causes the root account to be disabled. +# When disabled, hides the "Use the same password for administrator" +# checkbox. Also hides the "Choose a password" and associated text-inputs. +setRootPassword: true + +# You can control the initial state for the 'reuse password for root' +# checkbox here. Possible values are: +# - true to check or +# - false to uncheck +# +# When checked, the user password is used for the root account too. +# +# NOTE: *doReusePassword* requires *setRootPassword* to be enabled. +doReusePassword: true + + +### PASSWORDS AND LOGIN +# +# Autologin is convenient for single-user systems, but depends on +# the location of the machine if it is practical. "Password strength" +# measures measures might improve security by enforcing hard-to-guess +# passwords, or might encourage a post-it-under-the-keyboard approach. +# Distributions are free to steer their users to one kind of password +# or another. Weak(er) passwords may be allowed, may cause a warning, +# or may be forbidden entirely. + +# Autologin choice can be display to the user. +# Possible values are: +# - true to display it +# - false to hide it +# By default, this value is set to true. +displayAutologin: true + +# You can control the initial state for the 'autologin checkbox' here. +# Possible values are: +# - true to check or +# - false to uncheck +# These set the **initial** state of the checkbox. +doAutologin: true + +# These are optional password-requirements that a distro can enforce +# on the user. The values given in this sample file set only very weak +# validation settings. +# +# Calamares itself supports two checks: +# - minLength +# - maxLength +# In this sample file, the values are set to -1 which means "no +# minimum", "no maximum". This allows any password at all. +# No effort is done to ensure that the checks are consistent +# (e.g. specifying a maximum length less than the minimum length +# will annoy users). +# +# Calamares supports password checking through libpwquality. +# The libpwquality check relies on the (optional) libpwquality library. +# The value for libpwquality is a list of configuration statements like +# those found in pwquality.conf. The statements are handed off to the +# libpwquality parser for evaluation. The check is ignored if +# libpwquality is not available at build time (generates a warning in +# the log). The Calamares password check rejects passwords with a +# score of < 40 with the given libpwquality settings. +# +# (additional checks may be implemented in CheckPWQuality.cpp and +# wired into UsersPage.cpp) +# +# To disable all password validations: +# - comment out the relevant 'passwordRequirements' keys below, +# or set minLength and maxLength to -1. +# - disable libpwquality at build-time. +# To allow all passwords, but provide warnings: +# - set both 'allowWeakPasswords' and 'allowWeakPasswordsDefault' to true. +# (That will show the box *Allow weak passwords* in the user- +# interface, and check it by default). +# - configure password-checking however you wish. +# To require specific password characteristics: +# - set 'allowWeakPasswords' to false (the default) +# - configure password-checking, e.g. with NIST settings + + +# These are very weak -- actually, none at all -- requirements +passwordRequirements: + minLength: -1 # Password at least this many characters + maxLength: -1 # Password at most this many characters + libpwquality: + - minlen=0 + - minclass=0 + +# These are "you must have a password, any password" -- requirements +# +# passwordRequirements: +# minLength: 1 + +# These are requirements the try to follow the suggestions from +# https://pages.nist.gov/800-63-3/sp800-63b.html , "Digital Identity Guidelines". +# Note that requiring long and complex passwords has its own cost, +# because the user has to come up with one at install time. +# Setting 'allowWeakPasswords' to false and 'doAutologin' to false +# will require a strong password and prevent (graphical) login +# without the password. It is likely to be annoying for casual users. +# +# passwordRequirements: +# minLength: 8 +# maxLength: 64 +# libpwquality: +# - minlen=8 +# - maxrepeat=3 +# - maxsequence=3 +# - usersubstr=4 +# - badwords=linux + +# You can control the visibility of the 'strong passwords' checkbox here. +# Possible values are: +# - true to show or +# - false to hide (default) +# the checkbox. This checkbox allows the user to choose to disable +# password-strength-checks. By default the box is **hidden**, so +# that you have to pick a password that satisfies the checks. +allowWeakPasswords: false +# You can control the initial state for the 'strong passwords' checkbox here. +# Possible values are: +# - true to uncheck or +# - false to check (default) +# the checkbox by default. Since the box is labeled to enforce strong +# passwords, in order to **allow** weak ones by default, the box needs +# to be unchecked. +allowWeakPasswordsDefault: false + + +# User settings +# +# The user can enter a username, but there are some other +# hidden settings for the user which are configurable in Calamares. +# +# Key *user* has the following sub-keys: +# +# - *shell* Shell to be used for the regular user of the target system. +# There are three possible kinds of settings: +# - unset (i.e. commented out, the default), act as if set to /bin/bash +# - empty (explicit), don't pass shell information to useradd at all +# and rely on a correct configuration file in /etc/default/useradd +# - set, non-empty, use that path as shell. No validation is done +# that the shell actually exists or is executable. +# - *forbidden_names* Login names that may not be used. This list always +# 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. 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`. +# +# You may write permissions as: +# - write "NNN" (three octal digits) or +# - write "oNNN" (small 'o' and three octal digits) or +# - write "rwxrwxrwx" (like the output of ls, with a - for unset bits) +# The following permissions mean the same thing: "o750", "rwxr-x---" . +# - *nopasswd_group* If set, **and** the user sets no password, then +# the user is added to this group as well. Whether "no password" is +# allowed depends on other settings in this module; distributions that +# require a specific group for "no password" login should set this +# **and** configure the group in the *defaultGroups* section, above. +# The default is unset; this example configuration is non-default. +user: + shell: /bin/bash + forbidden_names: [ root ] + home_permissions: "o700" + nopasswd_group: "nopasswdlogin" + + +# Hostname settings +# +# The user can enter a hostname; this is configured into the system +# in some way. There are settings for how a hostname is guessed (as +# a default / suggestion) and where (or how) the hostname is set in +# the target system. +# +# Key *hostname* has the following sub-keys: +# +# - *location* How the hostname is set in the target system: +# - *None*, to not set the hostname at all +# - *EtcFile*, to write to `/etc/hostname` directly +# - *Etc*, identical to above +# - *Hostnamed*, to use systemd hostnamed(1) over DBus +# - *Transient*, to remove `/etc/hostname` from the target +# The default is *EtcFile*. Setting this to *None* or *Transient* will +# hide the hostname field. +# - *writeHostsFile* Should /etc/hosts be written with a hostname for +# this machine (also adds localhost and some ipv6 standard entries). +# Defaults to *true*. +# - *template* Is a simple template for making a suggestion for the +# hostname, based on user data. The default is "${first}-${product}". +# This is used only if the hostname field is shown. KMacroExpander is +# used; write `${key}` where `key` is one of the following: +# - *first* User's first name (whatever is first in the User Name field, +# which is first-in-order but not necessarily a "first name" as in +# "given name" or "name by which you call someone"; beware of western bias) +# - *name* All the text in the User Name field. +# - *login* The login name (which may be suggested based on User Name) +# - *product* The hardware product, based on DMI data +# - *product2* The product as described by Qt +# - *cpu* CPU name +# - *host* Current hostname (which may be a transient hostname) +# Literal text in the template is preserved. Calamares tries to map +# `${key}` values to something that will fit in a hostname, but does not +# apply the same to literal text in the template. Do not use invalid +# characters in the literal text, or no suggeston will be done. +# - *forbidden_names* lists hostnames that may not be used. This list +# always contains "localhost", but may list others that are unsuitable +# or broken in special ways. +hostname: + location: EtcFile + writeHostsFile: true + template: "derp-${cpu}" + 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: + fullName: + # value: "OEM User" + editable: true + loginName: + # value: "oem" + editable: true diff --git a/airootfs/etc/calamares/modules/usersq.conf b/airootfs/etc/calamares/modules/usersq.conf new file mode 100644 index 0000000..ea171bd --- /dev/null +++ b/airootfs/etc/calamares/modules/usersq.conf @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# For documentation see Users Module users.conf +# +--- +# Used as default groups for the created user. +# Adjust to your Distribution defaults. +defaultGroups: + - users + - lp + - video + - network + - storage + - wheel + - audio + - lpadmin + +autologinGroup: autologin + +doAutologin: true + +sudoersGroup: wheel + +setRootPassword: true + +doReusePassword: true + +passwordRequirements: + minLength: -1 + maxLength: -1 + libpwquality: + - minlen=0 + - minclass=0 + +allowWeakPasswords: false + +allowWeakPasswordsDefault: false + +userShell: /bin/bash + +setHostname: EtcFile + +writeHostsFile: true diff --git a/airootfs/etc/calamares/modules/welcome.conf b/airootfs/etc/calamares/modules/welcome.conf new file mode 100644 index 0000000..29cd905 --- /dev/null +++ b/airootfs/etc/calamares/modules/welcome.conf @@ -0,0 +1,138 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the welcome module. The welcome page +# displays some information from the branding file. +# Which parts it displays can be configured through +# the show* variables. +# +# In addition to displaying the welcome page, this module +# can check requirements for installation. +--- +# Display settings for various buttons on the welcome page. +# The URLs themselves come from `branding.desc`. Each button +# is show if the corresponding *show* setting +# here is "true". If the setting is "false", the button is hidden. +# Empty or not-set is interpreted as "false". +# +# TODO:3.3 Remove the URL fallback here; URLs only in `branding.desc` +# +# The setting can also be a full URL which will then be used +# instead of the one from the branding file. +showSupportUrl: true +showKnownIssuesUrl: true +showReleaseNotesUrl: false +# TODO:3.3 Move to branding, keep only a bool here +showDonateUrl: https://kde.org/community/donations/ + +# Requirements checking. These are general, generic, things +# that are checked. They may not match with the actual requirements +# imposed by other modules in the system. +requirements: + # Amount of available disk, in GiB. Floating-point is allowed here. + # Note that this does not account for *usable* disk, so it is possible + # to satisfy this requirement, yet have no space to install to. + requiredStorage: 5.5 + + # Amount of available RAM, in GiB. Floating-point is allowed here. + requiredRam: 1.0 + + # To check for internet connectivity, Calamares does a HTTP GET + # on this URL; on success (e.g. HTTP code 200) internet is OK. + # Use a privacy-respecting URL here, preferably in your own + # distribution domain. + # + # The URL is only used if "internet" is in the *check* list below. + internetCheckUrl: http://example.com + # + # This may be a single URL, or a list or URLs, in which case the + # URLs will be checked one-by-one; if any of them returns data, + # internet is assumed to be OK. This can be used to check via + # a number of places, where some domains may be down or blocked. + # + # To use a list of URLs, just use YAML list syntax (e.g. + # + # internetCheckUrl: + # - http://www.kde.org + # - http://www.freebsd.org + # + # or short-form + # + # internetCheckUrl: [ http://www.kde.org, http://www.freebsd.org ] + + # List conditions to check. Each listed condition will be + # probed in some way, and yields true or false according to + # the host system satisfying the condition. + # + # This sample file lists all the conditions that are known. + # + # Note that the last three checks are for testing-purposes only, + # and shouldn't be used in production (they are only available + # when building Calamares in development mode). There are five + # special checks: + # - *false* is a check that is always false (unsatisfied) + # - *true* is a check that is always true (satisfied) + # - *slow-false* takes 3 seconds, and then is false; use this one to + # show off the waiting-spinner before the first results come in + # - *slow-true* takes 3 seconds, and then is true + # - *snark* is a check that is only satisfied once it has been checked + # at least three times ("what I tell you three times is true"). + # Keep in mind that "true" and "false" are YAML keywords for + # boolean values, so should be quoted. + check: + - storage + - ram + - power + - internet + - root + - screen + - "false" + - slow-true + - snark + # List conditions that **must** be satisfied (from the list + # of conditions, above) for installation to proceed. + # If any of these conditions are not met, the user cannot + # continue past the welcome page. + required: + # - storage + - ram + # - root + +# GeoIP checking +# +# This can be used to pre-select a language based on the country +# the user is currently in. It *assumes* that there's internet +# connectivity, though. Configuration is like in the locale module, +# but remember to use a URL that returns full data **and** to +# use a selector that will pick the country, not the timezone. +# +# To disable GeoIP checking, either comment-out the entire geoip section, +# or set the *style* key to an unsupported format (e.g. `none`). +# Also, note the analogous feature in `src/modules/locale/locale.conf`, +# which is where you will find complete documentation. +# +# For testing, the *style* may be set to `fixed`, any URL that +# returns data (e.g. `http://example.com`) and then *selector* +# sets the data that is actually returned (e.g. "DE" to simulate +# the machine being in Germany). +# +# NOTE: the *selector* must pick the country code from the GeoIP +# data. Timezone, city, or other data will not be recognized. +# +geoip: + style: "none" + url: "https://geoip.kde.org/v1/ubiquity" # extended XML format + selector: "CountryCode" # blank uses default, which is wrong + +# User interface +# +# The "select language" icon is an international standard, but it +# might not theme very well with your desktop environment. +# Fill in an icon name (following FreeDesktop standards) to +# use that named icon instead of the usual one. +# +# Leave blank or unset to use the international standard. +# +# Known icons in this space are "set-language" and "config-language". +# +# languageIcon: set-language diff --git a/airootfs/etc/calamares/modules/welcomeq.conf b/airootfs/etc/calamares/modules/welcomeq.conf new file mode 100644 index 0000000..2efc514 --- /dev/null +++ b/airootfs/etc/calamares/modules/welcomeq.conf @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the welcomeq module. +# +# The configuration for welcomeq is exactly the same +# as the welcome module, with the one exception of +# *qmlSearch* which governs QML loading. +# +# No documentation is given here: look in the welcome module. +--- +# Setting for QML loading: use QRC, branding, or both sources of files +qmlSearch: both + +# Everythin below here is documented in `welcome.conf` +showSupportUrl: true +showKnownIssuesUrl: true +showReleaseNotesUrl: true +# showDonateUrl: https://kde.org/community/donations/ + +requirements: + requiredStorage: 5.5 + requiredRam: 1.0 + internetCheckUrl: http://google.com + check: + - storage + - ram + - power + - internet + - root + - screen + required: + - ram + +geoip: + style: "none" + url: "https://geoip.kde.org/v1/ubiquity" # extended XML format + selector: "CountryCode" # blank uses default, which is wrong + +#languageIcon: languages diff --git a/airootfs/etc/calamares/modules/zfs.conf b/airootfs/etc/calamares/modules/zfs.conf new file mode 100644 index 0000000..e5a0aa3 --- /dev/null +++ b/airootfs/etc/calamares/modules/zfs.conf @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The zfs module creates the zfs pools and datasets +# +# +# +--- +# The name to be used for the zpool +poolName: zpcala + +# A list of options that will be passed to zpool create +# +# Encryption options should generally not be added here since they will be added by +# selecting the encrypt disk option in the partition module +poolOptions: "-f -o ashift=12 -O mountpoint=none -O acltype=posixacl -O relatime=on" + +# A list of options that will be passed to zfs create when creating each dataset +# Do not include "canmount" or "mountpoint" as those are set below in the datasets array +datasetOptions: "-o compression=lz4 -o atime=off -o xattr=sa" + +# An array of datasets that will be created on the zpool mounted at / +# +# This default configuration is commonly used when support for booting more than one distro +# out of a single zpool is desired. If you decide to keep this default configuration, +# you should replace "distro" with an identifier that represents your distro. +datasets: + - dsName: ROOT + mountpoint: none + canMount: off + - dsName: ROOT/distro + mountpoint: none + canMount: off + - dsName: ROOT/distro/root + mountpoint: / + canMount: noauto + - dsName: ROOT/distro/home + mountpoint: /home + canMount: on + - dsName: ROOT/distro/varcache + mountpoint: /var/cache + canMount: on + - dsName: ROOT/distro/varlog + mountpoint: /var/log + canMount: on diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/BackButton.qml b/airootfs/etc/calamares/qml/calamares/slideshow/BackButton.qml new file mode 100644 index 0000000..4e420e0 --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/BackButton.qml @@ -0,0 +1,15 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +NavButton { + id: backButton + anchors.left: parent.left + visible: parent.currentSlide > 0 + isForward: false +} diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/ForwardButton.qml b/airootfs/etc/calamares/qml/calamares/slideshow/ForwardButton.qml new file mode 100644 index 0000000..7838fab --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/ForwardButton.qml @@ -0,0 +1,14 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +NavButton { + id: forwardButton + anchors.right: parent.right + visible: parent.currentSlide + 1 < parent.slides.length; +} diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/NavButton.qml b/airootfs/etc/calamares/qml/calamares/slideshow/NavButton.qml new file mode 100644 index 0000000..bdb2f40 --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/NavButton.qml @@ -0,0 +1,59 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* This is a navigation (arrow) button that fades in on hover, and + * which calls forward / backward navigation on the presentation it + * is in. It should be a child item of the presentation (not of a + * single slide). Use the ForwardButton or BackButton for a pre- + * configured instance that interacts with the presentation. + */ + +import QtQuick 2.5; + +Image { + id: fade + + property bool isForward : true + + width: 100 + height: 100 + anchors.verticalCenter: parent.verticalCenter + opacity: 0.3 + + OpacityAnimator { + id: fadeIn + target: fade + from: fade.opacity + to: 1.0 + duration: 500 + running: false + } + + OpacityAnimator { + id: fadeOut + target: fade + from: fade.opacity + to: 0.3 + duration: 250 + running: false + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: { fadeOut.running = false; fadeIn.running = true } + onExited: { fadeIn.running = false ; fadeOut.running = true } + onClicked: { + if (isForward) + fade.parent.goToNextSlide() + else + fade.parent.goToPreviousSlide() + } + } +} diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/Presentation.qml b/airootfs/etc/calamares/qml/calamares/slideshow/Presentation.qml new file mode 100644 index 0000000..7abc14f --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/Presentation.qml @@ -0,0 +1,238 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-FileCopyrightText: 2016 The Qt Company Ltd. + * SPDX-License-Identifier: LGPL-2.1-only + * + * 2017, Adriaan de Groot + * - added looping, keys-instead-of-shortcut + * 2018, Adriaan de Groot + * - make looping a property, drop the 'c' fade-key + * - drop navigation through entering a slide number + * (this and the 'c' key make sense in a *presentation* + * slideshow, not in a passive slideshow like Calamares) + * - remove quit key + * 2019, Adriaan de Groot + * - Support "V2" loading + * - Disable shortcuts until the content is visible in Calamares + * 2020, Adriaan de Groot + * - Updated to SPDX headers + */ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML Presentation System. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Window 2.0 + +Item { + id: root + + property variant slides: [] + property int currentSlide: 0 + + property bool loopSlides: true + + property bool showNotes: false; + property bool allowDelay: true; + property alias mouseNavigation: mouseArea.enabled + property bool arrowNavigation: true + property bool keyShortcutsEnabled: true + + property color titleColor: textColor; + property color textColor: "black" + property string fontFamily: "Helvetica" + property string codeFontFamily: "Courier New" + + // This is set by the C++ part of Calamares when the slideshow + // becomes visible. You can connect it to a timer, or whatever + // else needs to start only when the slideshow becomes visible. + // + // It is used in this example also to keep the keyboard shortcuts + // enabled only while the slideshow is active. + property bool activatedInCalamares: false + + // Private API + property int _lastShownSlide: 0 + + Component.onCompleted: { + var slideCount = 0; + var slides = []; + for (var i=0; i 0) + root.slides[root.currentSlide].visible = true; + } + + function switchSlides(from, to, forward) { + from.visible = false + to.visible = true + return true + } + + onCurrentSlideChanged: { + switchSlides(root.slides[_lastShownSlide], root.slides[currentSlide], currentSlide > _lastShownSlide) + _lastShownSlide = currentSlide + // Always keep focus on the slideshow + root.focus = true + } + + function goToNextSlide() { + if (root.slides[currentSlide].delayPoints) { + if (root.slides[currentSlide]._advance()) + return; + } + if (currentSlide + 1 < root.slides.length) + ++currentSlide; + else if (loopSlides) + currentSlide = 0; // Loop at the end + } + + function goToPreviousSlide() { + if (currentSlide - 1 >= 0) + --currentSlide; + else if (loopSlides) + currentSlide = root.slides.length - 1 + } + + focus: true // Keep focus + + // Navigation through key events, too + Keys.onSpacePressed: goToNextSlide() + Keys.onRightPressed: goToNextSlide() + Keys.onLeftPressed: goToPreviousSlide() + + // navigate with arrow keys + Shortcut { sequence: StandardKey.MoveToNextLine; enabled: root.activatedInCalamares && root .arrowNavigation; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousLine; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToPreviousSlide() } + Shortcut { sequence: StandardKey.MoveToNextChar; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousChar; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToPreviousSlide() } + + // presentation-specific single-key shortcuts (which interfere with normal typing) + Shortcut { sequence: " "; enabled: root.activatedInCalamares && root.keyShortcutsEnabled; onActivated: goToNextSlide() } + + // standard shortcuts + Shortcut { sequence: StandardKey.MoveToNextPage; enabled: root.activatedInCalamares; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousPage; enabled: root.activatedInCalamares; onActivated: goToPreviousSlide() } + + MouseArea { + id: mouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if (mouse.button == Qt.RightButton) + goToPreviousSlide() + else + goToNextSlide() + } + onPressAndHold: goToPreviousSlide(); //A back mechanism for touch only devices + } + + Window { + id: notesWindow; + width: 400 + height: 300 + + title: "QML Presentation: Notes" + visible: root.showNotes + + Flickable { + anchors.fill: parent + contentWidth: parent.width + contentHeight: textContainer.height + + Item { + id: textContainer + width: parent.width + height: notesText.height + 2 * notesText.padding + + Text { + id: notesText + + anchors.margins: 16 + + font.pixelSize: 16 + wrapMode: Text.WordWrap + + property string notes: root.slides[root.currentSlide].notes; + + onNotesChanged: { + var result = ""; + + var lines = notes.split("\n"); + var beginNewLine = false + for (var i=0; i 0) + result += " "; + result += line; + } + } + + if (result.length == 0) { + font.italic = true; + text = "no notes.." + } else { + font.italic = false; + text = result; + } + } + } + } + } + } +} diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/Slide.qml b/airootfs/etc/calamares/qml/calamares/slideshow/Slide.qml new file mode 100644 index 0000000..9cb9e73 --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/Slide.qml @@ -0,0 +1,206 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2012 Digia Plc and/or its subsidiary(-ies). + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QML Presentation System. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.5 + +Item { + /* + Slides can only be instantiated as a direct child of a Presentation {} as they rely on + several properties there. + */ + + id: slide + + property bool isSlide: true; + + property bool delayPoints: false; + property int _pointCounter: 0; + function _advance() { + if (!parent.allowDelay) + return false; + + _pointCounter = _pointCounter + 1; + if (_pointCounter < content.length) + return true; + _pointCounter = 0; + return false; + } + + property string title; + property variant content: [] + property string centeredText + property string writeInText; + property string notes; + + property real fontSize: parent.height * 0.05 + property real fontScale: 1 + + property real baseFontSize: fontSize * fontScale + property real titleFontSize: fontSize * 1.2 * fontScale + property real bulletSpacing: 1 + + property real contentWidth: width + + // Define the slide to be the "content area" + x: parent.width * 0.05 + y: parent.height * 0.2 + width: parent.width * 0.9 + height: parent.height * 0.7 + + property real masterWidth: parent.width + property real masterHeight: parent.height + + property color titleColor: parent.titleColor; + property color textColor: parent.textColor; + property string fontFamily: parent.fontFamily; + property int textFormat: Text.PlainText + + visible: false + + Text { + id: titleText + font.pixelSize: titleFontSize + text: title; + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.top + anchors.bottomMargin: parent.fontSize * 1.5 + font.bold: true; + font.family: slide.fontFamily + color: slide.titleColor + horizontalAlignment: Text.Center + z: 1 + } + + Text { + id: centeredId + width: parent.width + anchors.centerIn: parent + anchors.verticalCenterOffset: - parent.y / 3 + text: centeredText + horizontalAlignment: Text.Center + font.pixelSize: baseFontSize + font.family: slide.fontFamily + color: slide.textColor + wrapMode: Text.Wrap + } + + Text { + id: writeInTextId + property int length; + font.family: slide.fontFamily + font.pixelSize: baseFontSize + color: slide.textColor + + anchors.fill: parent; + wrapMode: Text.Wrap + + text: slide.writeInText.substring(0, length); + + NumberAnimation on length { + from: 0; + to: slide.writeInText.length; + duration: slide.writeInText.length * 30; + running: slide.visible && parent.visible && slide.writeInText.length > 0 + } + + visible: slide.writeInText != undefined; + } + + + Column { + id: contentId + anchors.fill: parent + + Repeater { + model: content.length + + Row { + id: row + + function decideIndentLevel(s) { return s.charAt(0) == " " ? 1 + decideIndentLevel(s.substring(1)) : 0 } + property int indentLevel: decideIndentLevel(content[index]) + property int nextIndentLevel: index < content.length - 1 ? decideIndentLevel(content[index+1]) : 0 + property real indentFactor: (10 - row.indentLevel * 2) / 10; + + height: text.height + (nextIndentLevel == 0 ? 1 : 0.3) * slide.baseFontSize * slide.bulletSpacing + x: slide.baseFontSize * indentLevel + visible: (!slide.parent.allowDelay || !delayPoints) || index <= _pointCounter + + Rectangle { + id: dot + anchors.baseline: text.baseline + anchors.baselineOffset: -text.font.pixelSize / 2 + width: text.font.pixelSize / 3 + height: text.font.pixelSize / 3 + color: slide.textColor + radius: width / 2 + opacity: text.text.length == 0 ? 0 : 1 + } + + Item { + id: space + width: dot.width * 1.5 + height: 1 + } + + Text { + id: text + width: slide.contentWidth - parent.x - dot.width - space.width + font.pixelSize: baseFontSize * row.indentFactor + text: content[index] + textFormat: slide.textFormat + wrapMode: Text.WordWrap + color: slide.textColor + horizontalAlignment: Text.AlignLeft + font.family: slide.fontFamily + } + } + } + } + +} diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/SlideCounter.qml b/airootfs/etc/calamares/qml/calamares/slideshow/SlideCounter.qml new file mode 100644 index 0000000..d5b2de7 --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/SlideCounter.qml @@ -0,0 +1,29 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* This control just shows a (non-translated) count of the slides + * in the slideshow in the format "n / total". + */ + +import QtQuick 2.5; + +Rectangle { + id: slideCounter + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 100 + height: 50 + + Text { + id: slideCounterText + anchors.centerIn: parent + //: slide counter, %1 of %2 (numeric) + text: qsTr("%L1 / %L2").arg(parent.parent.currentSlide + 1).arg(parent.parent.slides.length) + } +} diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/qmldir b/airootfs/etc/calamares/qml/calamares/slideshow/qmldir new file mode 100644 index 0000000..7b964b8 --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/qmldir @@ -0,0 +1,10 @@ +module calamares.slideshow + +Presentation 1.0 Presentation.qml +Slide 1.0 Slide.qml + +NavButton 1.0 NavButton.qml +ForwardButton 1.0 ForwardButton.qml +BackButton 1.0 BackButton.qml + +SlideCounter 1.0 SlideCounter.qml diff --git a/airootfs/etc/calamares/qml/calamares/slideshow/qmldir.license b/airootfs/etc/calamares/qml/calamares/slideshow/qmldir.license new file mode 100644 index 0000000..d2da9cf --- /dev/null +++ b/airootfs/etc/calamares/qml/calamares/slideshow/qmldir.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: no +SPDX-License-Identifier: CC0-1.0 diff --git a/airootfs/etc/calamares/settings.conf b/airootfs/etc/calamares/settings.conf new file mode 100644 index 0000000..d0e0860 --- /dev/null +++ b/airootfs/etc/calamares/settings.conf @@ -0,0 +1,236 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration file for Calamares +# +# This is the top-level configuration file for Calamares. +# It specifies what modules will be used, as well as some +# overall characteristics -- is this a setup program, or +# an installer. More specific configuration is devolved +# to the branding file (for the UI) and the individual +# module configuration files (for functionality). +--- +# Modules can be job modules (with different interfaces) and QtWidgets view +# modules. They could all be placed in a number of different paths. +# "modules-search" is a list of strings, each of these can either be a full +# path to a directory or the keyword "local". +# +# "local" means: +# - modules in $LIBDIR/calamares/modules, with +# - settings in SHARE/calamares/modules or /etc/calamares/modules. +# In debug-mode (e.g. calamares -d) "local" also adds some paths +# that make sense from inside the build-directory, so that you +# can build-and-run with the latest modules immediately. +# +# Strings other than "local" are taken as paths and interpreted +# relative to wherever Calamares is started. It is therefore **strongly** +# recommended to use only absolute paths here. This is mostly useful +# if your distro has forks of standard Calamares modules, but also +# uses some form of upstream packaging which might overwrite those +# forked modules -- then you can keep modules somewhere outside of +# the "regular" module tree. +# +# +# YAML: list of strings. +modules-search: [ local ] + +# Instances section. This section is optional, and it defines custom instances +# for modules of any kind. An instance entry has these keys: +# - *module* name, which matches the module name from the module descriptor +# (usually the name of the directory under `src/modules/`, but third- +# party modules may diverge. +# - *id* (optional) an identifier to distinguish this instance from +# all the others. If none is given, the name of the module is used. +# Together, the module and id form an instance key (see below). +# - *config* (optional) a filename for the configuration. If none is +# given, *module*`.conf` is used (e.g. `welcome.conf` for the welcome +# module) +# - *weight* (optional) In the *exec* phase of the sequence, progress +# is reported as jobs are completed. The jobs from a single module +# together contribute the full weight of that module. The overall +# progress (0 .. 100%) is divided up according to the weight of each +# module. Give modules that take a lot of time to complete, a larger +# weight to keep the overall progress moving along steadily. This +# weight overrides a weight given in the module descriptor. If no weight +# is given, uses the value from the module descriptor, or 1 if there +# isn't one there either. +# +# The primary goal of this mechanism is to allow loading multiple instances +# of the same module, with different configuration. If you don't need this, +# the instances section can safely be left empty. +# +# Module name plus instance name makes an instance key, e.g. +# "packagechooserq@licenseq", where "packagechooserq" is the module name (for the packagechooserq +# viewmodule) and "licenseq" is the instance name. In the *sequence* +# section below, use instance-keys to name instances (instead of just +# a module name, for modules which have only a single instance). +# +# Every module implicitly has an instance with the instance name equal +# to its module name, e.g. "welcome@welcome". In the *sequence* section, +# mentioning a module without a full instance key (e.g. "welcome") +# means that implicit module. +# +# An instance may specify its configuration file (e.g. `webview-home.conf`). +# The implicit instances all have configuration files named `.conf`. +# This (implict) way matches the source examples, where the welcome +# module contains an example `welcome.conf`. Specify a *config* for +# any module (also implicit instances) to change which file is used. +# +# For more information on running module instances, run Calamares in debug +# mode and check the Modules page in the Debug information interface. +# +# A module that is often used with instances is shellprocess, which will +# run shell commands specified in the configuration file. By configuring +# more than one instance of the module, multiple shell sessions can be run +# during install. +# +# YAML: list of maps of string:string key-value pairs. +#instances: +#- id: licenseq +# module: packagechooserq +# config: licenseq.conf + +# Sequence section. This section describes the sequence of modules, both +# viewmodules and jobmodules, as they should appear and/or run. +# +# A jobmodule instance key (or name) can only appear in an exec phase, whereas +# a viewmodule instance key (or name) can appear in both exec and show phases. +# There is no limit to the number of show or exec phases. However, the same +# module instance key should not appear more than once per phase, and +# deployers should take notice that the global storage structure is persistent +# throughout the application lifetime, possibly influencing behavior across +# phases. A show phase defines a sequence of viewmodules (and therefore +# pages). These viewmodules can offer up jobs for the execution queue. +# +# An exec phase displays a progress page (with brandable slideshow). This +# progress page iterates over the modules listed in the *immediately +# preceding* show phase, and enqueues their jobs, as well as any other jobs +# from jobmodules, in the order defined in the current exec phase. +# +# It then executes the job queue and clears it. If a viewmodule offers up a +# job for execution, but the module name (or instance key) isn't listed in the +# immediately following exec phase, this job will not be executed. +# +# YAML: list of lists of strings. +sequence: +- show: + - welcome +# - notesqml +# - packagechooserq@licenseq + - locale + - keyboard + - partition + - users +# - tracking + - summary +- exec: +# - dummycpp +# - dummyprocess +# - dummypython + - partition +# - zfs + - mount + - unpackfs + - machineid + - locale + - keyboard + - localecfg +# - luksbootkeyfile +# - luksopenswaphookcfg +# - dracutlukscfg + - fstab +# - plymouthcfg +# - zfshostid + - initcpiocfg + - initcpio + - users + - displaymanager + - networkcfg + - hwclock + - services-systemd +# - dracut + - initramfs +# - grubcfg + - bootloader + - umount +- show: + - finished + +# A branding component is a directory, either in SHARE/calamares/branding or +# in /etc/calamares/branding (the latter takes precedence). The directory must +# contain a YAML file branding.desc which may reference additional resources +# (such as images) as paths relative to the current directory. +# +# A branding component can also ship a QML slideshow for execution pages, +# along with translation files. +# +# Only the name of the branding component (directory) should be specified +# here, Calamares then takes care of finding it and loading the contents. +# +# YAML: string. +branding: default + +# If this is set to true, Calamares will show an "Are you sure?" prompt right +# before each execution phase, i.e. at points of no return. If this is set to +# false, no prompt is shown. Default is false, but Calamares will complain if +# this is not explicitly set. +# +# YAML: boolean. +prompt-install: false + +# If this is set to true, Calamares will execute all target environment +# commands in the current environment, without chroot. This setting should +# only be used when setting up Calamares as a post-install configuration tool, +# as opposed to a full operating system installer. +# +# Some official Calamares modules are not expected to function with this +# setting. (e.g. partitioning seems like a bad idea, since that is expected to +# have been done already) +# +# Default is false (for a normal installer), but Calamares will complain if +# this is not explicitly set. +# +# YAML: boolean. +dont-chroot: false + +# If this is set to true, Calamares refers to itself as a "setup program" +# rather than an "installer". Defaults to the value of dont-chroot, but +# Calamares will complain if this is not explicitly set. +oem-setup: false + +# If this is set to true, the "Cancel" button will be disabled entirely. +# The button is also hidden from view. +# +# This can be useful if when e.g. Calamares is used as a post-install +# configuration tool and you require the user to go through all the +# configuration steps. +# +# Default is false, but Calamares will complain if this is not explicitly set. +# +# YAML: boolean. +disable-cancel: false + +# If this is set to true, the "Cancel" button will be disabled once +# you start the 'Installation', meaning there won't be a way to cancel +# the Installation until it has finished or installation has failed. +# +# Default is false, but Calamares will complain if this is not explicitly set. +# +# YAML: boolean. +disable-cancel-during-exec: false + +# If this is set to true, the "Next" and "Back" button will be hidden once +# you start the 'Installation'. +# +# Default is false, but Calamares will complain if this is not explicitly set. +# +# YAML: boolean. +hide-back-and-next-during-exec: false + +# If this is set to true, then once the end of the sequence has +# been reached, the quit (done) button is clicked automatically +# and Calamares will close. Default is false: the user will see +# that the end of installation has been reached, and that things are ok. +# +# +quit-at-end: false diff --git a/airootfs/etc/hostname b/airootfs/etc/hostname new file mode 100644 index 0000000..e23fe64 --- /dev/null +++ b/airootfs/etc/hostname @@ -0,0 +1 @@ +live diff --git a/airootfs/etc/locale.conf b/airootfs/etc/locale.conf new file mode 100644 index 0000000..f9c983c --- /dev/null +++ b/airootfs/etc/locale.conf @@ -0,0 +1 @@ +LANG=C.UTF-8 diff --git a/airootfs/etc/localtime b/airootfs/etc/localtime new file mode 120000 index 0000000..0e35b57 --- /dev/null +++ b/airootfs/etc/localtime @@ -0,0 +1 @@ +/usr/share/zoneinfo/UTC \ No newline at end of file diff --git a/airootfs/etc/mkinitcpio.conf.d/archiso.conf b/airootfs/etc/mkinitcpio.conf.d/archiso.conf new file mode 100644 index 0000000..5c008e5 --- /dev/null +++ b/airootfs/etc/mkinitcpio.conf.d/archiso.conf @@ -0,0 +1,3 @@ +HOOKS=(base udev microcode modconf kms memdisk archiso archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs block filesystems keyboard) +COMPRESSION="xz" +COMPRESSION_OPTIONS=(-9e) diff --git a/airootfs/etc/modprobe.d/broadcom-wl.conf b/airootfs/etc/modprobe.d/broadcom-wl.conf new file mode 100644 index 0000000..0eae70c --- /dev/null +++ b/airootfs/etc/modprobe.d/broadcom-wl.conf @@ -0,0 +1,7 @@ +# The broadcom-wl package requires some modules to be disabled in order to use +# wl. Since the ISO image needs to cover many hardware cases, this file +# overrides the default blacklist in /usr/lib/modprobe.d/ +# +# If you need to use wl, you may need to delete this file, then `rmmod` any +# already-loaded modules that are now blacklisted before proceeding to modprobe +# wl itself. diff --git a/airootfs/etc/motd b/airootfs/etc/motd new file mode 100644 index 0000000..4d9eda1 --- /dev/null +++ b/airootfs/etc/motd @@ -0,0 +1,11 @@ +To install Arch Linux follow the installation guide: +https://wiki.archlinux.org/title/Installation_guide + +For Wi-Fi, authenticate to the wireless network using the iwctl utility. +For mobile broadband (WWAN) modems, connect with the mmcli utility. +Ethernet, WLAN and WWAN interfaces using DHCP should work automatically. + +After connecting to the internet, the installation guide can be accessed +via the convenience script Installation_guide. + +                                           diff --git a/airootfs/etc/os-release b/airootfs/etc/os-release new file mode 100644 index 0000000..f4ff08b --- /dev/null +++ b/airootfs/etc/os-release @@ -0,0 +1,9 @@ +NAME="HyprArch" +PRETTY_NAME="HyprArch" +ID=hyprarch +BUILD_ID=rolling +ANSI_COLOR="38;2;23;147;209" +HOME_URL="https://hyprarch.stuple.net/" +SUPPORT_URL="https://hyprarch.stuple.net/support" +BUG_REPORT_URL="https://hyprarch.stuple.net/issues" +LOGO=archlinux diff --git a/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook b/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook new file mode 100644 index 0000000..e3167cc --- /dev/null +++ b/airootfs/etc/pacman.d/hooks/uncomment-mirrors.hook @@ -0,0 +1,13 @@ +# remove from airootfs! +[Trigger] +Operation = Install +Operation = Upgrade +Type = Package +Target = pacman-mirrorlist + +[Action] +Description = Uncommenting HTTPS mirrors in /etc/pacman.d/mirrorlist... +When = PostTransaction +Depends = pacman-mirrorlist +Depends = sed +Exec = /usr/bin/sed -E -i 's/#(Server = https:)/\1/g' /etc/pacman.d/mirrorlist diff --git a/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook b/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook new file mode 100644 index 0000000..8dfb943 --- /dev/null +++ b/airootfs/etc/pacman.d/hooks/zzzz99-remove-custom-hooks-from-airootfs.hook @@ -0,0 +1,18 @@ +# remove from airootfs! +# As a workaround for https://bugs.archlinux.org/task/49347 , remove pacman hooks specific to the ISO build process. +# If not, they would be used when pacstrap is run in the live environment. + +[Trigger] +Operation = Install +Operation = Upgrade +Operation = Remove +Type = Package +Target = * + +[Action] +Description = Work around FS#49347 by removing custom pacman hooks that are only required during ISO build... +When = PostTransaction +Depends = sh +Depends = coreutils +Depends = grep +Exec = /bin/sh -c "rm -- $(grep -Frl 'remove from airootfs' /etc/pacman.d/hooks/)" diff --git a/airootfs/etc/pam.d/sddm b/airootfs/etc/pam.d/sddm new file mode 100644 index 0000000..df11003 --- /dev/null +++ b/airootfs/etc/pam.d/sddm @@ -0,0 +1,15 @@ +#%PAM-1.0 + +auth include system-login +-auth optional pam_gnome_keyring.so +-auth optional pam_kwallet5.so + +account include system-login + +password include system-login +-password optional pam_gnome_keyring.so use_authtok + +session optional pam_keyinit.so force revoke +session include system-login +-session optional pam_gnome_keyring.so auto_start +-session optional pam_kwallet5.so auto_start diff --git a/airootfs/etc/passwd b/airootfs/etc/passwd new file mode 100644 index 0000000..2807d5d --- /dev/null +++ b/airootfs/etc/passwd @@ -0,0 +1 @@ +root:x:0:0:root:/root:/usr/bin/zsh diff --git a/airootfs/etc/profile b/airootfs/etc/profile new file mode 100644 index 0000000..a7b57e1 --- /dev/null +++ b/airootfs/etc/profile @@ -0,0 +1,4 @@ +# Auto-start Hyprland on TTY1 +if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then + exec Hyprland +fi diff --git a/airootfs/etc/resolv.conf b/airootfs/etc/resolv.conf new file mode 120000 index 0000000..3639662 --- /dev/null +++ b/airootfs/etc/resolv.conf @@ -0,0 +1 @@ +/run/systemd/resolve/stub-resolv.conf \ No newline at end of file diff --git a/airootfs/etc/sddm.conf.d/autologin.conf b/airootfs/etc/sddm.conf.d/autologin.conf new file mode 100644 index 0000000..b6458db --- /dev/null +++ b/airootfs/etc/sddm.conf.d/autologin.conf @@ -0,0 +1,3 @@ +[Autologin] +User=root +Session=hyprland diff --git a/airootfs/etc/shadow b/airootfs/etc/shadow new file mode 100644 index 0000000..7edfd69 --- /dev/null +++ b/airootfs/etc/shadow @@ -0,0 +1 @@ +root::14871:::::: diff --git a/airootfs/etc/skel/.config/hypr/hyprland.conf b/airootfs/etc/skel/.config/hypr/hyprland.conf new file mode 100644 index 0000000..9bec2f6 --- /dev/null +++ b/airootfs/etc/skel/.config/hypr/hyprland.conf @@ -0,0 +1,186 @@ +# This is an example Hyprland config file. +# +# Refer to the wiki for more information. + +# +# Please note not all available settings / options are set here. +# For a full list, see the wiki +# + +# See https://wiki.hyprland.org/Configuring/Monitors/ +monitor=,preferred,auto,auto + + +# See https://wiki.hyprland.org/Configuring/Keywords/ for more + +# Execute your favorite apps at launch +# exec-once = waybar & hyprpaper & firefox + +# Source a file (multi-file configs) +# source = ~/.config/hypr/myColors.conf + +# Set programs that you use +$terminal = kitty +$fileManager = dolphin +$menu = wofi --show drun + +# Some default env vars. +env = XCURSOR_SIZE,24 +env = QT_QPA_PLATFORMTHEME,qt5ct # change to qt6ct if you have that + +# For all categories, see https://wiki.hyprland.org/Configuring/Variables/ +input { + kb_layout = us + kb_variant = + kb_model = + kb_options = + kb_rules = + + follow_mouse = 1 + + touchpad { + natural_scroll = false + } + + sensitivity = 0 # -1.0 - 1.0, 0 means no modification. +} + +general { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + + gaps_in = 5 + gaps_out = 20 + border_size = 2 + col.active_border = rgba(33ccffee) rgba(00ff99ee) 45deg + col.inactive_border = rgba(595959aa) + + layout = dwindle + + # Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on + allow_tearing = false +} + +decoration { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + + rounding = 10 + + blur { + enabled = true + size = 3 + passes = 1 + + vibrancy = 0.1696 + } + + drop_shadow = true + shadow_range = 4 + shadow_render_power = 3 + col.shadow = rgba(1a1a1aee) +} + +animations { + enabled = true + + # Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more + + bezier = myBezier, 0.05, 0.9, 0.1, 1.05 + + animation = windows, 1, 7, myBezier + animation = windowsOut, 1, 7, default, popin 80% + animation = border, 1, 10, default + animation = borderangle, 1, 8, default + animation = fade, 1, 7, default + animation = workspaces, 1, 6, default +} + +dwindle { + # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more + pseudotile = true # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below + preserve_split = true # you probably want this +} + +master { + # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more + new_is_master = true +} + +gestures { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + workspace_swipe = false +} + +misc { + # See https://wiki.hyprland.org/Configuring/Variables/ for more + force_default_wallpaper = -1 # Set to 0 or 1 to disable the anime mascot wallpapers +} + +# Example per-device config +# See https://wiki.hyprland.org/Configuring/Keywords/#per-device-input-configs for more +device { + name = epic-mouse-v1 + sensitivity = -0.5 +} + +# Example windowrule v1 +# windowrule = float, ^(kitty)$ +# Example windowrule v2 +# windowrulev2 = float,class:^(kitty)$,title:^(kitty)$ +# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more +windowrulev2 = suppressevent maximize, class:.* # You'll probably like this. + + +# See https://wiki.hyprland.org/Configuring/Keywords/ for more +$mainMod = SUPER + +# Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more +bind = $mainMod, Q, exec, $terminal +bind = $mainMod, C, killactive, +bind = $mainMod, M, exit, +bind = $mainMod, E, exec, $fileManager +bind = $mainMod, V, togglefloating, +bind = $mainMod, R, exec, $menu +bind = $mainMod, P, pseudo, # dwindle +bind = $mainMod, J, togglesplit, # dwindle + +# Move focus with mainMod + arrow keys +bind = $mainMod, left, movefocus, l +bind = $mainMod, right, movefocus, r +bind = $mainMod, up, movefocus, u +bind = $mainMod, down, movefocus, d + +# Switch workspaces with mainMod + [0-9] +bind = $mainMod, 1, workspace, 1 +bind = $mainMod, 2, workspace, 2 +bind = $mainMod, 3, workspace, 3 +bind = $mainMod, 4, workspace, 4 +bind = $mainMod, 5, workspace, 5 +bind = $mainMod, 6, workspace, 6 +bind = $mainMod, 7, workspace, 7 +bind = $mainMod, 8, workspace, 8 +bind = $mainMod, 9, workspace, 9 +bind = $mainMod, 0, workspace, 10 + +# Move active window to a workspace with mainMod + SHIFT + [0-9] +bind = $mainMod SHIFT, 1, movetoworkspace, 1 +bind = $mainMod SHIFT, 2, movetoworkspace, 2 +bind = $mainMod SHIFT, 3, movetoworkspace, 3 +bind = $mainMod SHIFT, 4, movetoworkspace, 4 +bind = $mainMod SHIFT, 5, movetoworkspace, 5 +bind = $mainMod SHIFT, 6, movetoworkspace, 6 +bind = $mainMod SHIFT, 7, movetoworkspace, 7 +bind = $mainMod SHIFT, 8, movetoworkspace, 8 +bind = $mainMod SHIFT, 9, movetoworkspace, 9 +bind = $mainMod SHIFT, 0, movetoworkspace, 10 + +# Example special workspace (scratchpad) +bind = $mainMod, S, togglespecialworkspace, magic +bind = $mainMod SHIFT, S, movetoworkspace, special:magic + +# Scroll through existing workspaces with mainMod + scroll +bind = $mainMod, mouse_down, workspace, e+1 +bind = $mainMod, mouse_up, workspace, e-1 + +# Move/resize windows with mainMod + LMB/RMB and dragging +bindm = $mainMod, mouse:272, movewindow +bindm = $mainMod, mouse:273, resizewindow diff --git a/airootfs/etc/skel/Desktop/install-HyprArch.desktop b/airootfs/etc/skel/Desktop/install-HyprArch.desktop new file mode 100644 index 0000000..f93e464 --- /dev/null +++ b/airootfs/etc/skel/Desktop/install-HyprArch.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=Install Arch Linux +Exec=sudo calamares +Icon=calamares +Terminal=false diff --git a/airootfs/etc/ssh/sshd_config.d/10-archiso.conf b/airootfs/etc/ssh/sshd_config.d/10-archiso.conf new file mode 100644 index 0000000..6ea7b41 --- /dev/null +++ b/airootfs/etc/ssh/sshd_config.d/10-archiso.conf @@ -0,0 +1,3 @@ +# Allow root login using password authentication +PasswordAuthentication yes +PermitRootLogin yes diff --git a/airootfs/etc/systemd/journald.conf.d/volatile-storage.conf b/airootfs/etc/systemd/journald.conf.d/volatile-storage.conf new file mode 100644 index 0000000..b69850d --- /dev/null +++ b/airootfs/etc/systemd/journald.conf.d/volatile-storage.conf @@ -0,0 +1,2 @@ +[Journal] +Storage=volatile diff --git a/airootfs/etc/systemd/logind.conf.d/do-not-suspend.conf b/airootfs/etc/systemd/logind.conf.d/do-not-suspend.conf new file mode 100644 index 0000000..f3ecb39 --- /dev/null +++ b/airootfs/etc/systemd/logind.conf.d/do-not-suspend.conf @@ -0,0 +1,4 @@ +[Login] +HandleSuspendKey=ignore +HandleHibernateKey=ignore +HandleLidSwitch=ignore diff --git a/airootfs/etc/systemd/network/20-ethernet.network b/airootfs/etc/systemd/network/20-ethernet.network new file mode 100644 index 0000000..d3a3271 --- /dev/null +++ b/airootfs/etc/systemd/network/20-ethernet.network @@ -0,0 +1,24 @@ +[Match] +# Matching with "Type=ether" causes issues with containers because it also matches virtual Ethernet interfaces (veth*). +# See https://bugs.archlinux.org/task/70892 +# Instead match by globbing the network interface name. +Name=en* +Name=eth* + +[Link] +RequiredForOnline=routable + +[Network] +DHCP=yes +MulticastDNS=yes + +# systemd-networkd does not set per-interface-type default route metrics +# https://github.com/systemd/systemd/issues/17698 +# Explicitly set route metric, so that Ethernet is preferred over Wi-Fi and Wi-Fi is preferred over mobile broadband. +# Use values from NetworkManager. From nm_device_get_route_metric_default in +# https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/src/core/devices/nm-device.c +[DHCPv4] +RouteMetric=100 + +[IPv6AcceptRA] +RouteMetric=100 diff --git a/airootfs/etc/systemd/network/20-wlan.network b/airootfs/etc/systemd/network/20-wlan.network new file mode 100644 index 0000000..8b70a95 --- /dev/null +++ b/airootfs/etc/systemd/network/20-wlan.network @@ -0,0 +1,20 @@ +[Match] +Name=wl* + +[Link] +RequiredForOnline=routable + +[Network] +DHCP=yes +MulticastDNS=yes + +# systemd-networkd does not set per-interface-type default route metrics +# https://github.com/systemd/systemd/issues/17698 +# Explicitly set route metric, so that Ethernet is preferred over Wi-Fi and Wi-Fi is preferred over mobile broadband. +# Use values from NetworkManager. From nm_device_get_route_metric_default in +# https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/src/core/devices/nm-device.c +[DHCPv4] +RouteMetric=600 + +[IPv6AcceptRA] +RouteMetric=600 diff --git a/airootfs/etc/systemd/network/20-wwan.network b/airootfs/etc/systemd/network/20-wwan.network new file mode 100644 index 0000000..6e1c8dd --- /dev/null +++ b/airootfs/etc/systemd/network/20-wwan.network @@ -0,0 +1,19 @@ +[Match] +Name=ww* + +[Link] +RequiredForOnline=routable + +[Network] +DHCP=yes + +# systemd-networkd does not set per-interface-type default route metrics +# https://github.com/systemd/systemd/issues/17698 +# Explicitly set route metric, so that Ethernet is preferred over Wi-Fi and Wi-Fi is preferred over mobile broadband. +# Use values from NetworkManager. From nm_device_get_route_metric_default in +# https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/main/src/core/devices/nm-device.c +[DHCPv4] +RouteMetric=700 + +[IPv6AcceptRA] +RouteMetric=700 diff --git a/airootfs/etc/systemd/networkd.conf.d/ipv6-privacy-extensions.conf b/airootfs/etc/systemd/networkd.conf.d/ipv6-privacy-extensions.conf new file mode 100644 index 0000000..0e9ceb4 --- /dev/null +++ b/airootfs/etc/systemd/networkd.conf.d/ipv6-privacy-extensions.conf @@ -0,0 +1,2 @@ +[Network] +IPv6PrivacyExtensions=yes diff --git a/airootfs/etc/systemd/resolved.conf.d/archiso.conf b/airootfs/etc/systemd/resolved.conf.d/archiso.conf new file mode 100644 index 0000000..636f3bd --- /dev/null +++ b/airootfs/etc/systemd/resolved.conf.d/archiso.conf @@ -0,0 +1,4 @@ +# Default systemd-resolved configuration for archiso + +[Resolve] +MulticastDNS=yes diff --git a/airootfs/etc/systemd/system-generators/systemd-gpt-auto-generator b/airootfs/etc/systemd/system-generators/systemd-gpt-auto-generator new file mode 120000 index 0000000..dc1dc0c --- /dev/null +++ b/airootfs/etc/systemd/system-generators/systemd-gpt-auto-generator @@ -0,0 +1 @@ +/dev/null \ No newline at end of file diff --git a/airootfs/etc/systemd/system/choose-mirror.service b/airootfs/etc/systemd/system/choose-mirror.service new file mode 100644 index 0000000..b6a3562 --- /dev/null +++ b/airootfs/etc/systemd/system/choose-mirror.service @@ -0,0 +1,10 @@ +[Unit] +Description=Choose mirror from the kernel command line +ConditionKernelCommandLine=mirror + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/choose-mirror + +[Install] +WantedBy=multi-user.target diff --git a/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service new file mode 120000 index 0000000..ebc50f0 --- /dev/null +++ b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-config.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-config.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service new file mode 120000 index 0000000..80fa3c8 --- /dev/null +++ b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-final.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-final.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service new file mode 120000 index 0000000..dd8e9f1 --- /dev/null +++ b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-local.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-init-local.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-main.service b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-main.service new file mode 120000 index 0000000..875ff73 --- /dev/null +++ b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-main.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-init-main.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-network.service b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-network.service new file mode 120000 index 0000000..ae77734 --- /dev/null +++ b/airootfs/etc/systemd/system/cloud-init.target.wants/cloud-init-network.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/cloud-init-network.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/dbus-org.freedesktop.ModemManager1.service b/airootfs/etc/systemd/system/dbus-org.freedesktop.ModemManager1.service new file mode 120000 index 0000000..dcf7c8e --- /dev/null +++ b/airootfs/etc/systemd/system/dbus-org.freedesktop.ModemManager1.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/ModemManager.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/dbus-org.freedesktop.network1.service b/airootfs/etc/systemd/system/dbus-org.freedesktop.network1.service new file mode 120000 index 0000000..4c158e6 --- /dev/null +++ b/airootfs/etc/systemd/system/dbus-org.freedesktop.network1.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/dbus-org.freedesktop.resolve1.service b/airootfs/etc/systemd/system/dbus-org.freedesktop.resolve1.service new file mode 120000 index 0000000..4f6ae34 --- /dev/null +++ b/airootfs/etc/systemd/system/dbus-org.freedesktop.resolve1.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-resolved.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/dbus-org.freedesktop.timesync1.service b/airootfs/etc/systemd/system/dbus-org.freedesktop.timesync1.service new file mode 120000 index 0000000..cd00411 --- /dev/null +++ b/airootfs/etc/systemd/system/dbus-org.freedesktop.timesync1.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-timesyncd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/display-manager.service b/airootfs/etc/systemd/system/display-manager.service new file mode 120000 index 0000000..56f1df2 --- /dev/null +++ b/airootfs/etc/systemd/system/display-manager.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/sddm.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/etc-pacman.d-gnupg.mount b/airootfs/etc/systemd/system/etc-pacman.d-gnupg.mount new file mode 100644 index 0000000..038961e --- /dev/null +++ b/airootfs/etc/systemd/system/etc-pacman.d-gnupg.mount @@ -0,0 +1,8 @@ +[Unit] +Description=Temporary /etc/pacman.d/gnupg directory + +[Mount] +What=tmpfs +Where=/etc/pacman.d/gnupg +Type=tmpfs +Options=mode=0755,noswap diff --git a/airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf b/airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf new file mode 100644 index 0000000..fc7465e --- /dev/null +++ b/airootfs/etc/systemd/system/getty@tty1.service.d/autologin.conf @@ -0,0 +1,3 @@ +[Service] +ExecStart= +ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin root %I $TERM diff --git a/airootfs/etc/systemd/system/livecd-alsa-unmuter.service b/airootfs/etc/systemd/system/livecd-alsa-unmuter.service new file mode 100644 index 0000000..03db4b9 --- /dev/null +++ b/airootfs/etc/systemd/system/livecd-alsa-unmuter.service @@ -0,0 +1,13 @@ +[Unit] +Description=Unmute All Sound Card Controls For Use With The Live Arch Environment +# This needs to run after the audio device becomes available. +Wants=systemd-udev-settle.service +After=systemd-udev-settle.service sound.target +ConditionKernelCommandLine=accessibility=on + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/livecd-sound -u + +[Install] +WantedBy=sound.target diff --git a/airootfs/etc/systemd/system/livecd-talk.service b/airootfs/etc/systemd/system/livecd-talk.service new file mode 100644 index 0000000..b38df22 --- /dev/null +++ b/airootfs/etc/systemd/system/livecd-talk.service @@ -0,0 +1,20 @@ +[Unit] +Description=Screen reader service +After=livecd-alsa-unmuter.service +Before=getty@tty1.service +ConditionKernelCommandLine=accessibility=on + +[Service] +Type=oneshot +TTYPath=/dev/tty13 +ExecStartPre=/usr/bin/chvt 13 +ExecStart=/usr/local/bin/livecd-sound -p +ExecStartPost=/usr/bin/chvt 1 +ExecStartPost=systemctl start espeakup.service +StandardInput=tty +TTYVHangup=yes +TTYVTDisallocate=yes +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/ModemManager.service b/airootfs/etc/systemd/system/multi-user.target.wants/ModemManager.service new file mode 120000 index 0000000..dcf7c8e --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/ModemManager.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/ModemManager.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/choose-mirror.service b/airootfs/etc/systemd/system/multi-user.target.wants/choose-mirror.service new file mode 120000 index 0000000..2d8d256 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/choose-mirror.service @@ -0,0 +1 @@ +../choose-mirror.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/hv_fcopy_daemon.service b/airootfs/etc/systemd/system/multi-user.target.wants/hv_fcopy_daemon.service new file mode 120000 index 0000000..20ac7b2 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/hv_fcopy_daemon.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/hv_fcopy_daemon.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/hv_kvp_daemon.service b/airootfs/etc/systemd/system/multi-user.target.wants/hv_kvp_daemon.service new file mode 120000 index 0000000..a7eac4a --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/hv_kvp_daemon.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/hv_kvp_daemon.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/hv_vss_daemon.service b/airootfs/etc/systemd/system/multi-user.target.wants/hv_vss_daemon.service new file mode 120000 index 0000000..eae19ef --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/hv_vss_daemon.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/hv_vss_daemon.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/iwd.service b/airootfs/etc/systemd/system/multi-user.target.wants/iwd.service new file mode 120000 index 0000000..3625abd --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/iwd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/iwd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/livecd-talk.service b/airootfs/etc/systemd/system/multi-user.target.wants/livecd-talk.service new file mode 120000 index 0000000..b917481 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/livecd-talk.service @@ -0,0 +1 @@ +/etc/systemd/system/livecd-talk.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/pacman-init.service b/airootfs/etc/systemd/system/multi-user.target.wants/pacman-init.service new file mode 120000 index 0000000..d09eec6 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/pacman-init.service @@ -0,0 +1 @@ +../pacman-init.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service b/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service new file mode 120000 index 0000000..d21ebd9 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/sshd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/sshd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service b/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service new file mode 120000 index 0000000..4c158e6 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/systemd-networkd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service b/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service new file mode 120000 index 0000000..4f6ae34 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/systemd-resolved.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-resolved.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/vboxservice.service b/airootfs/etc/systemd/system/multi-user.target.wants/vboxservice.service new file mode 120000 index 0000000..cb2d560 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/vboxservice.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/vboxservice.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/vmtoolsd.service b/airootfs/etc/systemd/system/multi-user.target.wants/vmtoolsd.service new file mode 120000 index 0000000..e0a11a7 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/vmtoolsd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/vmtoolsd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/multi-user.target.wants/vmware-vmblock-fuse.service b/airootfs/etc/systemd/system/multi-user.target.wants/vmware-vmblock-fuse.service new file mode 120000 index 0000000..173f306 --- /dev/null +++ b/airootfs/etc/systemd/system/multi-user.target.wants/vmware-vmblock-fuse.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/vmware-vmblock-fuse.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service b/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service new file mode 120000 index 0000000..7d6ad92 --- /dev/null +++ b/airootfs/etc/systemd/system/network-online.target.wants/systemd-networkd-wait-online.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd-wait-online.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/pacman-init.service b/airootfs/etc/systemd/system/pacman-init.service new file mode 100644 index 0000000..b824884 --- /dev/null +++ b/airootfs/etc/systemd/system/pacman-init.service @@ -0,0 +1,15 @@ +[Unit] +Description=Initializes Pacman keyring +Requires=etc-pacman.d-gnupg.mount +After=etc-pacman.d-gnupg.mount time-sync.target +BindsTo=etc-pacman.d-gnupg.mount +Before=archlinux-keyring-wkd-sync.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/pacman-key --init +ExecStart=/usr/bin/pacman-key --populate + +[Install] +WantedBy=multi-user.target diff --git a/airootfs/etc/systemd/system/sockets.target.wants/pcscd.socket b/airootfs/etc/systemd/system/sockets.target.wants/pcscd.socket new file mode 120000 index 0000000..3897c63 --- /dev/null +++ b/airootfs/etc/systemd/system/sockets.target.wants/pcscd.socket @@ -0,0 +1 @@ +/usr/lib/systemd/system/pcscd.socket \ No newline at end of file diff --git a/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket b/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket new file mode 120000 index 0000000..51942c8 --- /dev/null +++ b/airootfs/etc/systemd/system/sockets.target.wants/systemd-networkd.socket @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-networkd.socket \ No newline at end of file diff --git a/airootfs/etc/systemd/system/sound.target.wants/livecd-alsa-unmuter.service b/airootfs/etc/systemd/system/sound.target.wants/livecd-alsa-unmuter.service new file mode 120000 index 0000000..98c0fc8 --- /dev/null +++ b/airootfs/etc/systemd/system/sound.target.wants/livecd-alsa-unmuter.service @@ -0,0 +1 @@ +../livecd-alsa-unmuter.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/sysinit.target.wants/systemd-time-wait-sync.service b/airootfs/etc/systemd/system/sysinit.target.wants/systemd-time-wait-sync.service new file mode 120000 index 0000000..cabf28b --- /dev/null +++ b/airootfs/etc/systemd/system/sysinit.target.wants/systemd-time-wait-sync.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-time-wait-sync.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service b/airootfs/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service new file mode 120000 index 0000000..cd00411 --- /dev/null +++ b/airootfs/etc/systemd/system/sysinit.target.wants/systemd-timesyncd.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-timesyncd.service \ No newline at end of file diff --git a/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf b/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf new file mode 100644 index 0000000..c9f9bce --- /dev/null +++ b/airootfs/etc/systemd/system/systemd-networkd-wait-online.service.d/wait-for-only-one-interface.conf @@ -0,0 +1,6 @@ +# Allow systemd-networkd-wait-online to succeed with one interface, otherwise, if multiple network interfaces exist, +# network-online.target gets needlessly delayed. +# See https://wiki.archlinux.org/title/systemd-networkd#systemd-networkd-wait-online +[Service] +ExecStart= +ExecStart=/usr/lib/systemd/systemd-networkd-wait-online --any diff --git a/airootfs/root/.automated_script.sh b/airootfs/root/.automated_script.sh new file mode 100755 index 0000000..f7f3ced --- /dev/null +++ b/airootfs/root/.automated_script.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +script_cmdline() { + local param + for param in $(/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit +bind = $mainMod, E, exec, $fileManager +bind = $mainMod, V, togglefloating, +bind = $mainMod, R, exec, $menu +bind = $mainMod, P, pseudo, # dwindle +bind = $mainMod, J, layoutmsg, togglesplit # dwindle + +# Move focus with mainMod + arrow keys +bind = $mainMod, left, movefocus, l +bind = $mainMod, right, movefocus, r +bind = $mainMod, up, movefocus, u +bind = $mainMod, down, movefocus, d + +# Switch workspaces with mainMod + [0-9] +bind = $mainMod, 1, workspace, 1 +bind = $mainMod, 2, workspace, 2 +bind = $mainMod, 3, workspace, 3 +bind = $mainMod, 4, workspace, 4 +bind = $mainMod, 5, workspace, 5 +bind = $mainMod, 6, workspace, 6 +bind = $mainMod, 7, workspace, 7 +bind = $mainMod, 8, workspace, 8 +bind = $mainMod, 9, workspace, 9 +bind = $mainMod, 0, workspace, 10 + +# Move active window to a workspace with mainMod + SHIFT + [0-9] +bind = $mainMod SHIFT, 1, movetoworkspace, 1 +bind = $mainMod SHIFT, 2, movetoworkspace, 2 +bind = $mainMod SHIFT, 3, movetoworkspace, 3 +bind = $mainMod SHIFT, 4, movetoworkspace, 4 +bind = $mainMod SHIFT, 5, movetoworkspace, 5 +bind = $mainMod SHIFT, 6, movetoworkspace, 6 +bind = $mainMod SHIFT, 7, movetoworkspace, 7 +bind = $mainMod SHIFT, 8, movetoworkspace, 8 +bind = $mainMod SHIFT, 9, movetoworkspace, 9 +bind = $mainMod SHIFT, 0, movetoworkspace, 10 + +# Example special workspace (scratchpad) +bind = $mainMod, S, togglespecialworkspace, magic +bind = $mainMod SHIFT, S, movetoworkspace, special:magic + +# Scroll through existing workspaces with mainMod + scroll +bind = $mainMod, mouse_down, workspace, e+1 +bind = $mainMod, mouse_up, workspace, e-1 + +# Move/resize windows with mainMod + LMB/RMB and dragging +bindm = $mainMod, mouse:272, movewindow +bindm = $mainMod, mouse:273, resizewindow + +# Laptop multimedia keys for volume and LCD brightness +bindel = ,XF86AudioRaiseVolume, exec, wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ +bindel = ,XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- +bindel = ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle +bindel = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle +bindel = ,XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+ +bindel = ,XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%- + +# Requires playerctl +bindl = , XF86AudioNext, exec, playerctl next +bindl = , XF86AudioPause, exec, playerctl play-pause +bindl = , XF86AudioPlay, exec, playerctl play-pause +bindl = , XF86AudioPrev, exec, playerctl previous + +############################## +### WINDOWS AND WORKSPACES ### +############################## + +# See https://wiki.hypr.land/Configuring/Window-Rules/ for more +# See https://wiki.hypr.land/Configuring/Workspace-Rules/ for workspace rules + +# Example windowrules that are useful + +windowrule { + # Ignore maximize requests from all apps. You'll probably like this. + name = suppress-maximize-events + match:class = .* + + suppress_event = maximize +} + +windowrule { + # Fix some dragging issues with XWayland + name = fix-xwayland-drags + match:class = ^$ + match:title = ^$ + match:xwayland = true + match:float = true + match:fullscreen = false + match:pin = false + + no_focus = true +} + +# Hyprland-run windowrule +windowrule { + name = move-hyprland-run + + match:class = hyprland-run + + move = 20 monitor_h-120 + float = yes +} diff --git a/airootfs/root/.gnupg/scdaemon.conf b/airootfs/root/.gnupg/scdaemon.conf new file mode 100755 index 0000000..e1f3d1f --- /dev/null +++ b/airootfs/root/.gnupg/scdaemon.conf @@ -0,0 +1,4 @@ +disable-ccid +disable-pinpad +pcsc-driver /usr/lib/libpcsclite.so +pcsc-shared diff --git a/airootfs/root/.zlogin b/airootfs/root/.zlogin new file mode 100755 index 0000000..bf6bc8f --- /dev/null +++ b/airootfs/root/.zlogin @@ -0,0 +1,6 @@ +# fix for screen readers +if grep -Fqa 'accessibility=' /proc/cmdline &> /dev/null; then + setopt SINGLE_LINE_ZLE +fi + +~/.automated_script.sh diff --git a/airootfs/root/.zprofile b/airootfs/root/.zprofile new file mode 100644 index 0000000..35cc157 --- /dev/null +++ b/airootfs/root/.zprofile @@ -0,0 +1,9 @@ +if [[ -z $DISPLAY ]] && [[ $(tty) = /dev/tty1 ]]; then + # 1. Auto-install the package from your local repo + # --noconfirm ensures it doesn't wait for you to press 'Y' + pacman -Sy + pacman -U --noconfirm /root/local_repo/calamares-*.pkg.tar.zst + + # 2. Start the Plasma Wayland session + exec dbus-run-session startplasma-wayland +fi diff --git a/airootfs/root/Desktop/install-HyprArch.desktop b/airootfs/root/Desktop/install-HyprArch.desktop new file mode 100644 index 0000000..d9ab450 --- /dev/null +++ b/airootfs/root/Desktop/install-HyprArch.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=Install HyprArch Linux +Exec=sudo calamares +Icon=calamares +Terminal=false diff --git a/airootfs/root/install-HyprArch.desktop b/airootfs/root/install-HyprArch.desktop new file mode 100644 index 0000000..f93e464 --- /dev/null +++ b/airootfs/root/install-HyprArch.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Type=Application +Name=Install Arch Linux +Exec=sudo calamares +Icon=calamares +Terminal=false diff --git a/airootfs/root/local_repo/calamares-3.4.0-1-x86_64.pkg.tar.zst b/airootfs/root/local_repo/calamares-3.4.0-1-x86_64.pkg.tar.zst new file mode 100644 index 0000000..bd3b825 Binary files /dev/null and b/airootfs/root/local_repo/calamares-3.4.0-1-x86_64.pkg.tar.zst differ diff --git a/airootfs/root/local_repo/localrepo.db b/airootfs/root/local_repo/localrepo.db new file mode 100644 index 0000000..07683da Binary files /dev/null and b/airootfs/root/local_repo/localrepo.db differ diff --git a/airootfs/root/local_repo/localrepo.db.tar.gz b/airootfs/root/local_repo/localrepo.db.tar.gz new file mode 100644 index 0000000..07683da Binary files /dev/null and b/airootfs/root/local_repo/localrepo.db.tar.gz differ diff --git a/airootfs/root/local_repo/localrepo.files b/airootfs/root/local_repo/localrepo.files new file mode 100644 index 0000000..954c0c1 Binary files /dev/null and b/airootfs/root/local_repo/localrepo.files differ diff --git a/airootfs/root/local_repo/localrepo.files.tar.gz b/airootfs/root/local_repo/localrepo.files.tar.gz new file mode 100644 index 0000000..954c0c1 Binary files /dev/null and b/airootfs/root/local_repo/localrepo.files.tar.gz differ diff --git a/airootfs/usr/bin/calamares b/airootfs/usr/bin/calamares new file mode 100755 index 0000000..c12c08c Binary files /dev/null and b/airootfs/usr/bin/calamares differ diff --git a/airootfs/usr/include/libcalamares/Branding.h b/airootfs/usr/include/libcalamares/Branding.h new file mode 100644 index 0000000..3fffa02 --- /dev/null +++ b/airootfs/usr/include/libcalamares/Branding.h @@ -0,0 +1,326 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-FileCopyrightText: 2018 Raul Rodrigo Segura (raurodse) + * SPDX-FileCopyrightText: 2019 Camilo Higuita + * SPDX-FileCopyrightText: 2021 Anubhav Choudhary + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef BRANDING_H +#define BRANDING_H + +#include "CalamaresConfig.h" +#include "DllMacro.h" +#include "utils/NamedSuffix.h" + +#include +#include +#include +#include +#include +#include + +namespace YAML +{ +class Node; +} // namespace YAML + +namespace Calamares +{ + +class GlobalStorage; + +class UIDLLEXPORT Branding : public QObject +{ + Q_OBJECT +public: + /** + * Descriptive strings in the configuration file. use + * e.g. *Branding::ProductName to get the string value for + * the product name. + */ + + enum StringEntry + { + ProductName, + Version, + ShortVersion, + VersionedName, + ShortVersionedName, + ShortProductName, + BootloaderEntryName, + ProductUrl, + SupportUrl, + KnownIssuesUrl, + ReleaseNotesUrl, + DonateUrl + }; + Q_ENUM( StringEntry ) + + enum ImageEntry : short + { + ProductBanner, + ProductIcon, + ProductLogo, + ProductWallpaper, + ProductWelcome + }; + Q_ENUM( ImageEntry ) + + /** @brief Names of style entries, for use in code + * + * These names are mapped to names in the branding.desc file through + * an internal table s_styleEntryStrings, which defines which names + * in `branding.desc` key *style* are used for which entry. + */ + enum StyleEntry : short + { + SidebarBackground, + SidebarText, + SidebarTextCurrent, + SidebarBackgroundCurrent, + }; + Q_ENUM( StyleEntry ) + + /** @brief Supported log-upload servers. + * + * 'None' is here as a fallback. + */ + enum UploadServerType : short + { + None, + Fiche + }; + Q_ENUM( UploadServerType ) + + /** @brief Setting for how much the main window may expand. */ + enum class WindowExpansion + { + Normal, + Fullscreen, + Fixed + }; + Q_ENUM( WindowExpansion ) + /** @brief Setting for the main window size. + * + * The units are pixels (Pixies) or something-based-on-fontsize (Fonties), which + * we suffix as "em", e.g. "600px" or "32em". + */ + enum class WindowDimensionUnit + { + None, + Pixies, + Fonties + }; + Q_ENUM( WindowDimensionUnit ) + class WindowDimension : public NamedSuffix< WindowDimensionUnit, WindowDimensionUnit::None > + { + public: + static const NamedEnumTable< WindowDimensionUnit >& suffixes(); + bool isValid() const; + + using NamedSuffix::NamedSuffix; + WindowDimension( const QString& s ) + : NamedSuffix( suffixes(), s ) + { + } + }; + /** @brief Placement of main window. + */ + enum class WindowPlacement + { + Center, + Free + }; + Q_ENUM( WindowPlacement ) + ///@brief What kind of panel (sidebar, navigation) to use in the main window + enum class PanelFlavor + { + None, + Widget +#ifdef WITH_QML + , + Qml +#endif + }; + Q_ENUM( PanelFlavor ) + ///@brief Where to place a panel (sidebar, navigation) + enum class PanelSide + { + None, + Left, + Right, + Top, + Bottom + }; + Q_ENUM( PanelSide ) + + static Branding* instance(); + + explicit Branding( const QString& brandingFilePath, QObject* parent = nullptr, qreal devicePixelRatio = 1.0 ); + + /** @brief Complete path of the branding descriptor file. */ + QString descriptorPath() const { return m_descriptorPath; } + /** @brief The component name found in the descriptor file. + * + * The component name always matches the last directory name in the path. + */ + QString componentName() const { return m_componentName; } + /** @brief The directory holding all of the branding assets. */ + QString componentDirectory() const; + /** @brief The directory where branding translations live. + * + * This is componentDir + "/lang". + */ + QString translationsDirectory() const { return m_translationsPathPrefix; } + + /** @brief Path to the slideshow QML file, if any. (API == 1 or 2)*/ + QString slideshowPath() const { return m_slideshowPath; } + /// @brief List of pathnames of slideshow images, if any. (API == -1) + QStringList slideshowImages() const { return m_slideshowFilenames; } + /** @brief Which slideshow API to use for the slideshow? + * + * - 2 For QML-based slideshows loaded asynchronously (current) + * - 1 For QML-based slideshows, loaded when shown (legacy) + * - -1 For oldschool image-slideshows. + */ + int slideshowAPI() const { return m_slideshowAPI; } + + QPixmap image( Branding::ImageEntry imageEntry, const QSize& size ) const; + + /** @brief Look up an image in the branding directory or as an icon + * + * The @p name is checked in the branding directory: if it is an image + * file, return the pixmap from that file, at the requested size. + * If it isn't a file, look it up as an icon name in the current theme. + * May return a null pixmap if nothing is found. + */ + QPixmap image( const QString& name, const QSize& size ) const; + + /** @brief Look up image with alternate names + * + * Calls image() for each name in the @p list and returns the first + * one that is non-null. May return a null pixmap if nothing is found. + */ + QPixmap image( const QStringList& list, const QSize& size ) const; + + /** @brief Stylesheet to apply for this branding. May be empty. + * + * The file is loaded every time this function is called, so + * it may be quite expensive -- although normally it will be + * called only once, on startup. (Or from the debug window) + */ + QString stylesheet() const; + + bool welcomeStyleCalamares() const { return m_welcomeStyleCalamares; } + bool welcomeExpandingLogo() const { return m_welcomeExpandingLogo; } + bool windowMaximize() const { return m_windowExpansion == WindowExpansion::Fullscreen; } + bool windowExpands() const { return m_windowExpansion != WindowExpansion::Fixed; } + QPair< WindowDimension, WindowDimension > windowSize() const + { + return QPair< WindowDimension, WindowDimension >( m_windowWidth, m_windowHeight ); + } + bool windowPlacementCentered() const { return m_windowPlacement == WindowPlacement::Center; } + + ///@brief Which sidebar flavor is configured + PanelFlavor sidebarFlavor() const { return m_sidebarFlavor; } + ///@brief Which navigation flavor is configured + PanelFlavor navigationFlavor() const { return m_navigationFlavor; } + + /** @brief Upload server configuration + * + * This object has 3 items : the type (which may be none, in which case the URL + * is irrelevant and usually empty), the URL for the upload and the size limit of upload + * in bytes (for configuration value < 0, it serves -1, which stands for having no limit). + */ + struct UploadServerInfo + { + UploadServerType type; + QUrl url; + qint64 size; + + operator bool() const { return type != Calamares::Branding::UploadServerType::None && size != 0; } + }; + UploadServerInfo uploadServer() const { return m_uploadServer; } + + /** + * Creates a map called "branding" in the global storage, and inserts an + * entry for each of the branding strings. This makes the branding + * information accessible to the Python modules. + */ + void setGlobals( GlobalStorage* globalStorage ) const; + +public slots: + QString string( StringEntry stringEntry ) const; + QString versionedName() const { return string( VersionedName ); } + QString productName() const { return string( ProductName ); } + QString shortProductName() const { return string( ShortProductName ); } + QString shortVersionedName() const { return string( ShortVersionedName ); } + + /** @brief Map an enum-value to the entry from the *style* key. + * + * e.g. StyleEntry::SidebarTextCurrent maps to the corresponding + * *style* entry, which is (confusingly) named "sidebarTextSelect" + * in the branding file. + */ + QString styleString( StyleEntry styleEntry ) const; + QString imagePath( ImageEntry imageEntry ) const; + + PanelSide sidebarSide() const { return m_sidebarSide; } + PanelSide navigationSide() const { return m_navigationSide; } + +private: + static Branding* s_instance; + + static const QStringList s_stringEntryStrings; + static const QStringList s_imageEntryStrings; + static const QStringList s_uploadServerStrings; + + QString m_descriptorPath; // Path to descriptor (e.g. "/etc/calamares/default/branding.desc") + QString m_componentName; // Matches last part of full path to containing directory + QMap< QString, QString > m_strings; + QMap< QString, QString > m_images; + QMap< QString, QString > m_style; + UploadServerInfo m_uploadServer; + + /* The slideshow can be done in one of two ways: + * - as a sequence of images + * - as a QML file + * The slideshow: setting selects which one is used. If it is + * a list (of filenames) then it is a sequence of images, and otherwise + * it is a QML file which is run. (For QML, the slideshow API is + * important). + */ + QStringList m_slideshowFilenames; + QString m_slideshowPath; + int m_slideshowAPI; + QString m_translationsPathPrefix; + + /** @brief Initialize the simple settings below */ + void initSimpleSettings( const YAML::Node& doc ); + ///@brief Initialize the slideshow settings, above + void initSlideshowSettings( const YAML::Node& doc ); + + bool m_welcomeStyleCalamares; + bool m_welcomeExpandingLogo; + + WindowExpansion m_windowExpansion; + WindowDimension m_windowHeight, m_windowWidth; + WindowPlacement m_windowPlacement; + + PanelFlavor m_sidebarFlavor = PanelFlavor::Widget; + PanelFlavor m_navigationFlavor = PanelFlavor::Widget; + PanelSide m_sidebarSide = PanelSide::Left; + PanelSide m_navigationSide = PanelSide::Bottom; + + qreal m_devicePixelRatio; +}; + +} // namespace Calamares + +#endif // BRANDING_H diff --git a/airootfs/usr/include/libcalamares/CalamaresAbout.h b/airootfs/usr/include/libcalamares/CalamaresAbout.h new file mode 100644 index 0000000..88d4999 --- /dev/null +++ b/airootfs/usr/include/libcalamares/CalamaresAbout.h @@ -0,0 +1,31 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_CALAMARESABOUT_H +#define CALAMARES_CALAMARESABOUT_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ +/** @brief Returns an about string for the application + * + * The about string includes a header-statement, a list of maintainer + * addresses, and a thank-you to Blue Systems. There is on %-substitution + * left, where you can fill in the name of the product (e.g. to say + * "Calamares for Netrunner" or ".. for Manjaro"). + */ +DLLEXPORT const QString aboutStringUntranslated(); +/// @brief As above, but translated in the current Calamares language +DLLEXPORT const QString aboutString(); +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/CalamaresConfig.h b/airootfs/usr/include/libcalamares/CalamaresConfig.h new file mode 100644 index 0000000..ff22b9b --- /dev/null +++ b/airootfs/usr/include/libcalamares/CalamaresConfig.h @@ -0,0 +1,34 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ +#ifndef CALAMARESCONFIG_H +#define CALAMARESCONFIG_H + +#define CMAKE_INSTALL_PREFIX "/usr" +#define CMAKE_INSTALL_FULL_LIBEXECDIR "/usr/libexec" +#define CMAKE_INSTALL_LIBDIR "lib" +#define CMAKE_INSTALL_FULL_LIBDIR "/usr/lib" +#define CMAKE_INSTALL_FULL_DATADIR "/usr/share/calamares" +#define CMAKE_INSTALL_FULL_SYSCONFDIR "/etc" +#define CMAKE_BUILD_TYPE "Release" + +/* + * These are feature-settings that affect consumers of Calamares + * libraries as well; without Python-support in the libs, for instance, + * there's no point in having a Python plugin. + * + * This list should match the one in CalamaresConfig.cmake + * which is the CMake-time side of the same configuration. + */ +#define WITH_PYTHON +#define WITH_PYBIND11 +/* #undef WITH_BOOST_PYTHON */ +#define WITH_QML +#define WITH_QT6 + +#endif // CALAMARESCONFIG_H diff --git a/airootfs/usr/include/libcalamares/CalamaresVersion.h b/airootfs/usr/include/libcalamares/CalamaresVersion.h new file mode 100644 index 0000000..59c35f8 --- /dev/null +++ b/airootfs/usr/include/libcalamares/CalamaresVersion.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: no +// SPDX-License-Identifier: CC0-1.0 +#ifndef CALAMARES_VERSION_H +#define CALAMARES_VERSION_H + +#define CALAMARES_ORGANIZATION_NAME "Calamares" +#define CALAMARES_ORGANIZATION_DOMAIN "calamares.io" +#define CALAMARES_APPLICATION_NAME "Calamares" +#define CALAMARES_VERSION "3.4.0" +#define CALAMARES_VERSION_SHORT "3.4.0" + +#define CALAMARES_VERSION_MAJOR "3" +#define CALAMARES_VERSION_MINOR "4" +/* #undef CALAMARES_VERSION_PATCH */ +/* #undef CALAMARES_VERSION_RC */ + +#endif // CALAMARES_VERSION_H diff --git a/airootfs/usr/include/libcalamares/CppJob.h b/airootfs/usr/include/libcalamares/CppJob.h new file mode 100644 index 0000000..f906a0d --- /dev/null +++ b/airootfs/usr/include/libcalamares/CppJob.h @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2016 Kevin Kofler + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_CPPJOB_H +#define CALAMARES_CPPJOB_H + +#include "DllMacro.h" +#include "Job.h" + +#include "modulesystem/InstanceKey.h" + +#include +#include + +namespace Calamares +{ + +class DLLEXPORT CppJob : public Job +{ + Q_OBJECT +public: + explicit CppJob( QObject* parent = nullptr ); + ~CppJob() override; + + void setModuleInstanceKey( const Calamares::ModuleSystem::InstanceKey& instanceKey ); + Calamares::ModuleSystem::InstanceKey moduleInstanceKey() const { return m_instanceKey; } + + virtual void setConfigurationMap( const QVariantMap& configurationMap ); + +protected: + Calamares::ModuleSystem::InstanceKey m_instanceKey; +}; + +} // namespace Calamares + +#endif // CALAMARES_CPPJOB_H diff --git a/airootfs/usr/include/libcalamares/DllMacro.h b/airootfs/usr/include/libcalamares/DllMacro.h new file mode 100644 index 0000000..f144d46 --- /dev/null +++ b/airootfs/usr/include/libcalamares/DllMacro.h @@ -0,0 +1,68 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef DLLMACRO_H +#define DLLMACRO_H + +#ifndef CALAMARES_EXPORT +#define CALAMARES_EXPORT __attribute__( ( visibility( "default" ) ) ) +#endif + +/* + * Mark symbols exported from Calamares non-GUI library with DLLEXPORT. + * These are the public API of libcalamares. + */ +#ifndef DLLEXPORT +#if defined( DLLEXPORT_PRO ) +#define DLLEXPORT CALAMARES_EXPORT +#else +#define DLLEXPORT +#endif +#endif + +/* + * Mark symbols exported from Calamares GUI library with DLLEXPORT. + * These are the public API of libcalamaresui. + */ +#ifndef UIDLLEXPORT +#if defined( UIDLLEXPORT_PRO ) +#define UIDLLEXPORT CALAMARES_EXPORT +#else +#define UIDLLEXPORT +#endif +#endif + +/* + * Mark symbols exported from Calamares C++ plugins with PLUGINDLLEXPORT. + * These are the public API of the libraries (generally, the plugin + * entry point) + */ +#ifndef PLUGINDLLEXPORT +#if defined( PLUGINDLLEXPORT_PRO ) +#define PLUGINDLLEXPORT CALAMARES_EXPORT +#else +#define PLUGINDLLEXPORT +#endif +#endif + +/* + * For functions that should be static in production but also need to + * be tested, use STATICTEST as linkage specifier. When built as part + * of a test, the function will be given normal linkage. + */ +#ifndef STATICTEST +#if defined( BUILD_AS_TEST ) +#define STATICTEST +#else +#define STATICTEST static +#endif +#endif + +#endif diff --git a/airootfs/usr/include/libcalamares/GlobalStorage.h b/airootfs/usr/include/libcalamares/GlobalStorage.h new file mode 100644 index 0000000..37ea332 --- /dev/null +++ b/airootfs/usr/include/libcalamares/GlobalStorage.h @@ -0,0 +1,192 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef CALAMARES_GLOBALSTORAGE_H +#define CALAMARES_GLOBALSTORAGE_H + +#include "DllMacro.h" + +#include +#include +#include +#include + +namespace Calamares +{ + +/** @brief Storage for data that passes between Calamares modules. + * + * The Global Storage is global to the Calamares JobQueue and + * everything that depends on that: all of its modules use the + * same instance of the JobQueue, and so of the Global Storage. + * + * GS is used to pass data between modules; there is only convention + * about what keys are used, and individual modules should document + * what they put in to GS or what they expect to find in it. + * + * GS behaves as a basic key-value store, with a QVariantMap behind + * it. Any QVariant can be put into the storage, and the signal + * changed() is emitted when any data is modified. + * + * In general, see QVariantMap (possibly after calling data()) for details. + * + * This class is thread-safe -- most accesses go through JobQueue, which + * handles threading itself, but because modules load in parallel and can + * have asynchronous tasks like GeoIP lookups, the storage itself also + * has locking. All methods are thread-safe, use data() to make a snapshot + * copy for use outside of the thread-safe API. + */ +class DLLEXPORT GlobalStorage : public QObject +{ + Q_OBJECT +public: + /** @brief Create a GS object + * + * **Generally** there is only one GS object (hence, "global") which + * is owned by the JobQueue instance (which is a singleton). However, + * it is possible to create more GS objects. + */ + explicit GlobalStorage( QObject* parent = nullptr ); + + /** @brief Insert a key and value into the store + * + * The @p value is added to the store with key @p key. If @p key + * already exists in the store, its existing value is overwritten. + * The changed() signal is emitted regardless. + */ + void insert( const QString& key, const QVariant& value ); + /** @brief Removes a key and its value + * + * The @p key is removed from the store. If the @p key does not + * exist, nothing happens. changed() is emitted regardless. + * + * @return the number of keys remaining + */ + int remove( const QString& key ); + + /// @brief Clears all keys in this GS object + void clear(); + + /** @brief dump keys and values to the debug log + * + * All the keys and their values are written to the debug log. + * See save() for caveats: this can leak sensitive information. + */ + void debugDump() const; + + /** @brief write as JSON to the given filename + * + * The file named @p filename is overwritten with a JSON representation + * of the entire global storage (this may be structured, for instance + * if maps or lists have been inserted). + * + * No tidying, sanitization, or censoring is done -- for instance, + * the user module sets a slightly-obscured password in global storage, + * and this JSON file will contain that password in-the-only-slightly- + * obscured form. + */ + bool saveJson( const QString& filename ) const; + + /** @brief Adds the keys from the given JSON file + * + * No tidying, sanitization, or censoring is done. + * The JSON file is read and each key is added as a value to + * the global storage. The storage is not cleared first: existing + * keys will remain; keys that also occur in the JSON file are overwritten. + */ + bool loadJson( const QString& filename ); + + /** @brief write as YAML to the given filename + * + * See also save(), above. + */ + bool saveYaml( const QString& filename ) const; + + /** @brief reads settings from the given filename + * + * See also load(), above. + */ + bool loadYaml( const QString& filename ); + + /** @brief Make a complete copy of the data + * + * Provides a snapshot of the data at a given time. + */ + QVariantMap data() const { return m; } + +public Q_SLOTS: + /** @brief Does the store contain the given key? + * + * This can distinguish an explicitly-inserted QVariant() from + * a no-value-exists QVariant. See value() for details. + */ + bool contains( const QString& key ) const; + /** @brief The number of keys in the store + * + * This should be unsigned, but the underlying QMap uses signed as well. + * Equal to keys().length(), in theory. + */ + int count() const; + /** @brief The keys in the store + * + * This makes a copy of all the keys currently in the store, which + * could be used for iterating over all the values in the store. + */ + QStringList keys() const; + /** @brief Gets a value from the store + * + * If a value has been previously inserted, returns that value. + * If @p key does not exist in the store, returns a QVariant() + * (an invalid QVariant, which boolean-converts to false). Since + * QVariant() van also be inserted explicitly, use contains() + * to check for the presence of a key if you need that. + */ + QVariant value( const QString& key ) const; + +signals: + /** @brief Emitted any time the store changes + * + * Also emitted sometimes when the store does not change, e.g. + * when removing a non-existent key or inserting a value that + * is already present. + */ + void changed(); + +private: + class ReadLock; + class WriteLock; + QVariantMap m; + mutable QMutex m_mutex; +}; + + +/** @brief Gets a value from the store + * + * When @p nestedKey contains no '.' characters, equivalent + * to `gs->value(nestedKey)`. Otherwise recursively looks up + * the '.'-separated parts of @p nestedKey in successive sub-maps + * of the store, returning the value in the innermost one. + * + * Example: `lookup(gs, "branding.name")` finds the value of the + * 'name' key in the 'branding' submap of the store. + * + * Sets @p ok to @c true if a value was found. Returns the value + * as a variant. If no value is found (e.g. the key is missing + * or some prefix submap is missing) sets @p ok to @c false + * and returns an invalid QVariant. + * + * @see GlobalStorage::value + */ +DLLEXPORT QVariant lookup( const GlobalStorage* gs, const QString& nestedKey, bool& ok ); + +} // namespace Calamares + +#endif // CALAMARES_GLOBALSTORAGE_H diff --git a/airootfs/usr/include/libcalamares/Job.h b/airootfs/usr/include/libcalamares/Job.h new file mode 100644 index 0000000..931029a --- /dev/null +++ b/airootfs/usr/include/libcalamares/Job.h @@ -0,0 +1,175 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_JOB_H +#define CALAMARES_JOB_H + +#include "DllMacro.h" + +#include +#include +#include + +namespace Calamares +{ + +class DLLEXPORT JobResult +{ +public: + /** @brief Distinguish classes of errors + * + * All "ok result" have errorCode 0 (NoError). + * Errors returned from job execution have values < 0. + * Errors before job execution, or not returned by the job execution + * itself, have values > 0. + */ + enum + { + NoError = 0, + GenericError = -1, + PythonUncaughtException = 1, + InvalidConfiguration = 2, + MissingRequirements = 3, + }; + + // Can't copy, but you can keep a temporary + JobResult( const JobResult& rhs ) = delete; + JobResult( JobResult&& rhs ); + + virtual ~JobResult() {} + + /** @brief Is this JobResult a success? + * + * Equivalent to errorCode() == 0, see succeeded(). + */ + virtual operator bool() const; + + virtual QString message() const; + virtual void setMessage( const QString& message ); + + virtual QString details() const; + virtual void setDetails( const QString& details ); + + int errorCode() const { return m_number; } + /** @brief Is this JobResult a success? + * + * Equivalent to errorCode() == 0. + */ + bool succeeded() const { return this->operator bool(); } + + /// @brief an "ok status" result + static JobResult ok(); + /** @brief an "error" result resulting from the execution of the job + * + * The error code is set to GenericError. + */ + static JobResult error( const QString& message, const QString& details = QString() ); + /** @brief an "internal error" meaning the job itself has a problem (usually for python) + * + * Pass in a suitable error code; using 0 (which would normally mean "ok") instead + * gives you a GenericError code. + */ + static JobResult internalError( const QString& message, const QString& details, int errorCode ); + +protected: + explicit JobResult( const QString& message, const QString& details, int errorCode ); + +private: + QString m_message; + QString m_details; + int m_number; +}; + +class DLLEXPORT Job : public QObject +{ + Q_OBJECT +public: + explicit Job( QObject* parent = nullptr ); + ~Job() override; + + /** @brief The job's (relative) weight. + * + * The default implementation returns 1, which gives all jobs + * the same weight, so they advance the overall progress the same + * amount. This is nonsense, since some jobs take much longer than + * others; it's up to the individual jobs to say something about + * how much work is (relatively) done. + * + * Since jobs are caused by **modules** from the sequence, the + * overall weight of the module is taken into account: its weight + * is divided among the jobs based on each jobs relative weight. + * This can be used in a module that runs a bunch of jobs to indicate + * which of the jobs is "heavy" and which is not. + */ + virtual int getJobWeight() const; + + /** @brief The human-readable name of this job + * + * This should be a very short statement of what the job does. + * 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; + + /** @brief a longer human-readable description of what the job will do + * + * This **may** be used by view steps to fill in the summary + * messages for the summary page; at present, only the *partition* + * module does so. + * + * The default implementation returns an empty string. + * + * The translation should use context @c \@title . + */ + virtual QString prettyDescription() const; + + /** @brief A human-readable status for progress reporting + * + * This is called from the JobQueue when progress is made, and should + * return a not-too-long description of the job's status. This + * 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 JobResult exec() = 0; + + bool isEmergency() const { return m_emergency; } + void setEmergency( bool e ) { m_emergency = e; } + +signals: + /** @brief Signals that the job has made progress + * + * The parameter @p percent should be between 0 (0%) and 1 (100%). + * Values outside of this range will be clamped. + */ + void progress( qreal percent ); + +private: + bool m_emergency = false; +}; + +using job_ptr = QSharedPointer< Job >; +using JobList = QList< job_ptr >; + +} // namespace Calamares + +#endif // CALAMARES_JOB_H diff --git a/airootfs/usr/include/libcalamares/JobExample.h b/airootfs/usr/include/libcalamares/JobExample.h new file mode 100644 index 0000000..e3506fe --- /dev/null +++ b/airootfs/usr/include/libcalamares/JobExample.h @@ -0,0 +1,69 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_JOB_EXAMPLE_H +#define CALAMARES_JOB_EXAMPLE_H + +#include "Job.h" + +namespace Calamares +{ + +/** @brief A Job with a name + * + * This includes a default implementation of prettyName(), + * but is only used as a base for FailJob and GoodJob, + * which are support / bogus classes. + */ +class DLLEXPORT NamedJob : public Job +{ + Q_OBJECT +public: + explicit NamedJob( const QString& name, QObject* parent = nullptr ) + : Job( parent ) + , m_name( name ) + { + } + + virtual QString prettyName() const override; + +protected: + const QString m_name; +}; + +/// @brief Job does nothing, always succeeds +class DLLEXPORT GoodJob : public NamedJob +{ + Q_OBJECT +public: + explicit GoodJob( const QString& name, QObject* parent = nullptr ) + : NamedJob( name, parent ) + { + } + + virtual JobResult exec() override; +}; + + +/// @brief Job does nothing, always fails +class DLLEXPORT FailJob : public NamedJob +{ + Q_OBJECT +public: + explicit FailJob( const QString& name, QObject* parent = nullptr ) + : NamedJob( name, parent ) + { + } + + virtual JobResult exec() override; +}; + +} // namespace Calamares + +#endif // CALAMARES_JOB_EXAMPLE_H diff --git a/airootfs/usr/include/libcalamares/JobQueue.h b/airootfs/usr/include/libcalamares/JobQueue.h new file mode 100644 index 0000000..593bb06 --- /dev/null +++ b/airootfs/usr/include/libcalamares/JobQueue.h @@ -0,0 +1,122 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_JOBQUEUE_H +#define CALAMARES_JOBQUEUE_H + +#include "DllMacro.h" +#include "Job.h" + +#include + +namespace Calamares +{ +class GlobalStorage; +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 +{ + Q_OBJECT +public: + explicit JobQueue( QObject* parent = nullptr ); + ~JobQueue() override; + + /** @brief Returns the most-recently-created instance. + * + * It is possible for instance() to be @c nullptr, since you must + * call the constructor explicitly first. + */ + static JobQueue* instance(); + /* @brief Returns the GlobalStorage object for the instance. + * + * It is possible for instanceGlobalStorage() to be @c nullptr, + * since there might not be an instance to begin with. + */ + static GlobalStorage* instanceGlobalStorage() + { + auto* jq = instance(); + return jq ? jq->globalStorage() : nullptr; + } + + GlobalStorage* globalStorage() const; + + /** @brief Queues up jobs from a single module source + * + * The total weight of the jobs is spread out to fill the weight + * of the module. + */ + void enqueue( int moduleWeight, const JobList& jobs ); + /** @brief Starts all the jobs that are enqueued. + * + * After this, isRunning() returns @c true until + * finished() is emitted. + */ + void start(); + + bool isRunning() const { return !m_finished; } + +signals: + /** @brief Report progress of the whole queue, with a status message + * + * The @p percent is a value between 0.0 and 1.0 (100%) of the + * overall queue progress (not of the current job), while + * @p prettyName is the status message from the job -- often + * just the name of the job, but some jobs include more information. + */ + void progress( qreal percent, const QString& prettyName ); + /** @brief Indicate that the queue is empty, after calling start() + * + * Emitted when the queue empties. The queue may also emit + * failed(), if something went wrong, but finished() is always + * the last one. + */ + void finished(); + /** @brief A job in the queue failed. + * + * Contains the (already-translated) text from the job describing + * the failure. + */ + void failed( const QString& message, const QString& details ); + + /** @brief Reports the names of jobs in the queue. + * + * When jobs are added via enqueue(), or when the queue otherwise + * changes, the **names** of the jobs are reported. This is + * primarily for debugging purposes. + */ + void queueChanged( const QStringList& jobNames ); + +public Q_SLOTS: + /** @brief Implementation detail + * + * This is a private implementation detail for the job thread, + * which should not be called by other core. + */ + void finish(); + +private: + static JobQueue* s_instance; + + JobThread* m_thread; + GlobalStorage* m_storage; + bool m_finished = true; ///< Initially, not running +}; + +} // namespace Calamares + +#endif // CALAMARES_JOBQUEUE_H diff --git a/airootfs/usr/include/libcalamares/ProcessJob.h b/airootfs/usr/include/libcalamares/ProcessJob.h new file mode 100644 index 0000000..2e44ba3 --- /dev/null +++ b/airootfs/usr/include/libcalamares/ProcessJob.h @@ -0,0 +1,46 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_PROCESSJOB_H +#define CALAMARES_PROCESSJOB_H + +#include "DllMacro.h" +#include "Job.h" + +#include + +namespace Calamares +{ + +class DLLEXPORT ProcessJob : public Job +{ + Q_OBJECT +public: + explicit ProcessJob( const QString& command, + const QString& workingPath, + bool runInChroot = false, + std::chrono::seconds secondsTimeout = std::chrono::seconds( 30 ), + QObject* parent = nullptr ); + ~ProcessJob() override; + + QString prettyName() const override; + QString prettyStatusMessage() const override; + JobResult exec() override; + +private: + QString m_command; + QString m_workingPath; + bool m_runInChroot; + std::chrono::seconds m_timeoutSec; +}; + +} // namespace Calamares + +#endif // CALAMARES_PROCESSJOB_H diff --git a/airootfs/usr/include/libcalamares/Settings.h b/airootfs/usr/include/libcalamares/Settings.h new file mode 100644 index 0000000..3ae35c2 --- /dev/null +++ b/airootfs/usr/include/libcalamares/Settings.h @@ -0,0 +1,205 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2019 Gabriel Craciunescu + * SPDX-FileCopyrightText: 2019 Dominic Hayes + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef SETTINGS_H +#define SETTINGS_H + +#include "DllMacro.h" +#include "modulesystem/Actions.h" +#include "modulesystem/InstanceKey.h" + +#include +#include + + +namespace Calamares +{ + +/** @brief Description of an instance as named in `settings.conf` + * + * An instance is an intended-step-in-sequence; it is not yet + * a loaded module. The instances have config-files and weights + * which are used by the module manager when loading modules + * and creating jobs. + */ +class DLLEXPORT InstanceDescription +{ + using InstanceKey = Calamares::ModuleSystem::InstanceKey; + +public: + /** @brief An invalid InstanceDescription + * + * Use `fromSettings()` to populate an InstanceDescription and + * check its validity. + */ + InstanceDescription() = default; + + /** @brief An InstanceDescription with no special settings. + * + * Regardless of @p key being custom, sets weight to 1 and + * the configuration file to @c key.module() (plus the ".conf" + * extension). + * + * To InstanceDescription is custom if the key is. + */ + InstanceDescription( const InstanceKey& key ); + + static InstanceDescription fromSettings( const QVariantMap& ); + + bool isValid() const { return m_instanceKey.isValid(); } + + const InstanceKey& key() const { return m_instanceKey; } + QString configFileName() const { return m_configFileName; } + bool isCustom() const { return m_instanceKey.isCustom(); } + int weight() const { return m_weight < 0 ? 1 : m_weight; } + bool explicitWeight() const { return m_weight > 0; } + +private: + InstanceKey m_instanceKey; + QString m_configFileName; + int m_weight = 0; +}; + +class DLLEXPORT Settings : public QObject +{ + Q_OBJECT +#ifdef BUILD_AS_TEST +public: +#endif + explicit Settings( bool debugMode ); + explicit Settings( const QString& settingsFilePath, bool debugMode ); + + void setConfiguration( const QByteArray& configData, const QString& explainName ); + void reconcileInstancesAndSequence(); + +public: + static Settings* instance(); + /// @brief Find a settings.conf, following @p debugMode + static Settings* init( bool debugMode ); + /// @brief Explicif filename, debug is always true (for testing) + static Settings* init( const QString& filename ); + + /// @brief Get the path this settings was created for (may be empty) + QString path() const { return m_settingsPath; } + + QStringList modulesSearchPaths() const; + + using InstanceDescriptionList = QList< InstanceDescription >; + /** @brief All the module instances used + * + * Each module-instance mentioned in `settings.conf` has an entry + * in the moduleInstances list -- both custom entries that are + * in the *instances* section, and each module mentioned in the + * *sequence*. + */ + InstanceDescriptionList moduleInstances() const; + + using ModuleSequence = QList< QPair< ModuleSystem::Action, Calamares::ModuleSystem::InstanceKeyList > >; + /** @brief Representation of *sequence* of execution + * + * Each "section" of the *sequence* key in `settings.conf` gets an + * entry here, stating what kind of action is taken and which modules + * take part (in order). Each entry in the list is an instance key + * which can be found in the moduleInstances() list. + */ + ModuleSequence modulesSequence() const; + + QString brandingComponentName() const; + + /** @brief Are the settings consistent and valid? + * + * Checks that at least branding is set, and that the instances + * and sequence are valid. + */ + bool isValid() const; + + /** @brief Is this a debugging run? + * + * Returns true if Calamares is in debug mode. In debug mode, + * modules and settings are loaded from more locations, to help + * development and debugging. + */ + bool debugMode() const { return m_debug; } + + /** @brief Distinguish "install" from "OEM" modes. + * + * Returns true in "install" mode, which is where actions happen + * in a chroot -- the target system, which exists separately from + * the source system. In "OEM" mode, returns false and most actions + * apply to the *current* (host) system. + */ + bool doChroot() const { return m_doChroot; } + + /** @brief Global setting of prompt-before-install. + * + * Returns true when the configuration is such that the user + * should be prompted one-last-time before any action is taken + * that really affects the machine. + */ + bool showPromptBeforeExecution() const { return m_promptInstall; } + + /** @brief Distinguish between "install" and "setup" modes. + * + * This influences user-visible strings, for instance using the + * word "setup" instead of "install" where relevant. + */ + bool isSetupMode() const { return m_isSetupMode; } + + /** @brief Returns whether the named module is enabled + * + * Returns true if @p module is enabled in settings.conf. Be aware that it + * only tests for a specific module name so if a QML and non-QML version + * of the same module exists, it must be specified explicitly + * + * @p module is a module name or module key e.g. packagechooser) and not a + * full module key+id (e.g. packagechooser@packagechooser) + * + */ + bool isModuleEnabled( const QString& module ) const; + + /** @brief Global setting of disable-cancel: can't cancel ever. */ + bool disableCancel() const { return m_disableCancel; } + + /** @brief Temporary setting of disable-cancel: can't cancel during exec. */ + bool disableCancelDuringExec() const { return m_disableCancelDuringExec; } + + bool hideBackAndNextDuringExec() const { return m_hideBackAndNextDuringExec; } + + /** @brief Is quit-at-end set? (Quit automatically when done) */ + bool quitAtEnd() const { return m_quitAtEnd; } + +private: + static Settings* s_instance; + QString m_settingsPath; + + QStringList m_modulesSearchPaths; + + InstanceDescriptionList m_moduleInstances; + ModuleSequence m_modulesSequence; + + QString m_brandingComponentName; + + // bools are initialized here according to default setting + bool m_debug; + bool m_doChroot = true; + bool m_isSetupMode = false; + bool m_promptInstall = false; + bool m_disableCancel = false; + bool m_disableCancelDuringExec = false; + bool m_hideBackAndNextDuringExec = false; + bool m_quitAtEnd = false; +}; + +} // namespace Calamares + +#endif // SETTINGS_H diff --git a/airootfs/usr/include/libcalamares/ViewManager.h b/airootfs/usr/include/libcalamares/ViewManager.h new file mode 100644 index 0000000..62d2b9f --- /dev/null +++ b/airootfs/usr/include/libcalamares/ViewManager.h @@ -0,0 +1,296 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef VIEWMANAGER_H +#define VIEWMANAGER_H + +#include "DllMacro.h" +#include "viewpages/ViewStep.h" + +#include +#include +#include +#include + +namespace Calamares +{ +/** + * @brief The ViewManager class handles progression through view pages. + * @note Singleton object, only use through ViewManager::instance(). + */ +class UIDLLEXPORT ViewManager : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY( int currentStepIndex READ currentStepIndex NOTIFY currentStepChanged FINAL ) + + Q_PROPERTY( bool nextEnabled READ nextEnabled NOTIFY nextEnabledChanged FINAL ) + Q_PROPERTY( QString nextLabel READ nextLabel NOTIFY nextLabelChanged FINAL ) + Q_PROPERTY( QString nextIcon READ nextIcon NOTIFY nextIconChanged FINAL ) + + Q_PROPERTY( bool backEnabled READ backEnabled NOTIFY backEnabledChanged FINAL ) + Q_PROPERTY( QString backLabel READ backLabel NOTIFY backLabelChanged FINAL ) + Q_PROPERTY( QString backIcon READ backIcon NOTIFY backIconChanged FINAL ) + + Q_PROPERTY( bool quitEnabled READ quitEnabled NOTIFY quitEnabledChanged FINAL ) + Q_PROPERTY( QString quitLabel READ quitLabel NOTIFY quitLabelChanged FINAL ) + Q_PROPERTY( QString quitIcon READ quitIcon NOTIFY quitIconChanged FINAL ) + Q_PROPERTY( QString quitTooltip READ quitTooltip NOTIFY quitTooltipChanged FINAL ) + + Q_PROPERTY( bool quitVisible READ quitVisible NOTIFY quitVisibleChanged FINAL ) + + Q_PROPERTY( bool backAndNextVisible READ backAndNextVisible NOTIFY backAndNextVisibleChanged FINAL ) + + ///@brief Sides on which the ViewManager has side-panels + Q_PROPERTY( Qt::Orientations panelSides READ panelSides WRITE setPanelSides MEMBER m_panelSides ) + + // Global properties, where ViewManager proxies to Settings + Q_PROPERTY( bool isDebugMode READ isDebugMode CONSTANT FINAL ) + Q_PROPERTY( bool isChrootMode READ isChrootMode CONSTANT FINAL ) + Q_PROPERTY( bool isSetupMode READ isSetupMode CONSTANT FINAL ) + Q_PROPERTY( QString logFilePath READ logFilePath CONSTANT FINAL ) + +public: + /** + * @brief instance access to the ViewManager singleton. + * @return pointer to the singleton instance. + */ + static ViewManager* instance(); + static ViewManager* instance( QObject* parent ); + + /** + * @brief centralWidget always returns the central widget in the Calamares main + * window. + * @return a pointer to the active QWidget (usually a wizard page provided by a + * view module). + */ + QWidget* centralWidget(); + + /** + * @brief addViewStep appends a view step to the roster. + * @param step a pointer to the ViewStep object to add. + * @note a ViewStep is the active instance of a view module, it aggregates one + * or more view pages, plus zero or more jobs which may be created at runtime. + */ + void addViewStep( ViewStep* step ); + + /** + * @brief viewSteps returns the list of currently present view steps. + * @return the ViewStepList. + * This should only return an empty list before startup is complete. + */ + ViewStepList viewSteps() const; + + /** + * @brief currentStep returns the currently active ViewStep, i.e. the ViewStep + * which owns the currently visible view page. + * @return the active ViewStep. Do not confuse this with centralWidget(). + * @see ViewStep::centralWidget + */ + ViewStep* currentStep() const; + + /** + * @brief currentStepIndex returns the index of the currently active ViewStep. + * @return the index. + */ + int currentStepIndex() const; + + enum class Confirmation + { + Continue, // User rejects cancel / close question + CancelInstallation, // User accepts cancel / close question + EndOfInstallation, // There was no question because the installation was already done + }; + + /** + * @brief Called when "Cancel" is clicked; asks for confirmation. + * Other means of closing Calamares also call this method, e.g. alt-F4. + * At the end of installation, no confirmation is asked. + * + * @return a Confirmation value, @c Unasked if the installation is complete + */ + Confirmation confirmCancelInstallation(); + + Qt::Orientations panelSides() const { return m_panelSides; } + void setPanelSides( Qt::Orientations panelSides ) { m_panelSides = panelSides; } + +public Q_SLOTS: + /** + * @brief next moves forward to the next page of the current ViewStep (if any), + * or to the first page of the next ViewStep if the current ViewStep doesn't + * have any more pages. + */ + void next(); + bool nextEnabled() const + { + return m_nextEnabled; ///< Is the next-button to be enabled + } + QString nextLabel() const + { + return m_nextLabel; ///< What should be displayed on the next-button + } + QString nextIcon() const + { + return m_nextIcon; ///< Name of the icon to show + } + + /** + * @brief back moves backward to the previous page of the current ViewStep (if any), + * or to the last page of the previous ViewStep if the current ViewStep doesn't + * have any pages before the current one. + */ + void back(); + bool backEnabled() const + { + return m_backEnabled; ///< Is the back-button to be enabled + } + QString backLabel() const + { + return m_backLabel; ///< What should be displayed on the back-button + } + QString backIcon() const + { + return m_backIcon; ///< Name of the icon to show + } + + bool backAndNextVisible() const + { + return m_backAndNextVisible; ///< Is the quit-button to be enabled + } + /** + * @brief Probably quit + * + * Asks for confirmation if necessary. Terminates the application. + */ + void quit(); + bool quitEnabled() const + { + return m_quitEnabled; ///< Is the quit-button to be enabled + } + QString quitLabel() const + { + return m_quitLabel; ///< What should be displayed on the quit-button + } + QString quitIcon() const + { + return m_quitIcon; ///< Name of the icon to show + } + bool quitVisible() const + { + return m_quitVisible; ///< Should the quit-button be visible + } + QString quitTooltip() const { return m_quitTooltip; } + + /** + * @brief onInstallationFailed displays an error message when a fatal failure + * happens in a ViewStep. + * @param message the error string. + * @param details the details string. + */ + void onInstallationFailed( const QString& message, const QString& details ); + + /** @brief Replaces the stack with a view step stating that initialization failed. + * + * @param modules a list of failed modules. + */ + void onInitFailed( const QStringList& modules ); + + /** @brief Tell the manager that initialization / loading is complete. + * + * Call this at least once, to tell the manager to activate the first page. + */ + void onInitComplete(); + + /// @brief Connected to ViewStep::nextStatusChanged for all steps + void updateNextStatus( bool enabled ); + + /// @brief Proxy to Settings::debugMode() default @c false + bool isDebugMode() const; + /// @brief Proxy to Settings::doChroot() default @c true + bool isChrootMode() const; + /// @brief Proxy to Settings::isSetupMode() default @c false + bool isSetupMode() const; + /// @brief Proxy to Logger::logFile(), default empty + QString logFilePath() const; + +signals: + void currentStepChanged(); + void ensureSize( QSize size ) const; // See ViewStep::ensureSize() + void cancelEnabled( bool enabled ) const; + + void nextEnabledChanged( bool ) const; + void nextLabelChanged( QString ) const; + void nextIconChanged( QString ) const; + + void backEnabledChanged( bool ) const; + void backLabelChanged( QString ) const; + void backIconChanged( QString ) const; + void backAndNextVisibleChanged( bool ) const; + + void quitEnabledChanged( bool ) const; + void quitLabelChanged( QString ) const; + void quitIconChanged( QString ) const; + void quitVisibleChanged( bool ) const; + void quitTooltipChanged( QString ) const; + +private: + explicit ViewManager( QObject* parent = nullptr ); + ~ViewManager() override; + + void insertViewStep( int before, ViewStep* step ); + void updateButtonLabels(); + void updateCancelEnabled( bool enabled ); + void updateBackAndNextVisibility( bool visible ); + + inline bool currentStepValid() const { return ( 0 <= m_currentStep ) && ( m_currentStep < m_steps.length() ); } + + static ViewManager* s_instance; + + ViewStepList m_steps; + int m_currentStep; + + QWidget* m_widget; + QStackedWidget* m_stack; + + bool m_nextEnabled = false; + QString m_nextLabel; + QString m_nextIcon; ///< Name of icon to show on button + + bool m_backEnabled = false; + QString m_backLabel; + QString m_backIcon; + + bool m_backAndNextVisible = true; + + bool m_quitEnabled = false; + QString m_quitLabel; + QString m_quitIcon; + QString m_quitTooltip; + bool m_quitVisible = true; + + Qt::Orientations m_panelSides; + +public: + /** @section Model + * + * These are the methods and enums used for the as-a-model part + * of the ViewManager. + */ + enum Role + { + ProgressTreeItemCurrentIndex = Qt::UserRole + 13 ///< Index (row) of the current step + }; + + QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + int rowCount( const QModelIndex& parent = QModelIndex() ) const override; +}; + +} // namespace Calamares + +#endif // VIEWMANAGER_H diff --git a/airootfs/usr/include/libcalamares/compat/CheckBox.h b/airootfs/usr/include/libcalamares/compat/CheckBox.h new file mode 100644 index 0000000..261ed46 --- /dev/null +++ b/airootfs/usr/include/libcalamares/compat/CheckBox.h @@ -0,0 +1,30 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2024 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_COMPAT_XML_H +#define CALAMARES_COMPAT_XML_H + +#include + +namespace Calamares +{ + +#if QT_VERSION < QT_VERSION_CHECK( 6, 7, 0 ) +using checkBoxStateType = int; +const auto checkBoxStateChangedSignal = &QCheckBox::stateChanged; +constexpr checkBoxStateType checkBoxUncheckedValue = 0; +#else +using checkBoxStateType = Qt::CheckState; +const auto checkBoxStateChangedSignal = &QCheckBox::checkStateChanged; +constexpr checkBoxStateType checkBoxUncheckedValue = Qt::Unchecked; +#endif + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/compat/Mutex.h b/airootfs/usr/include/libcalamares/compat/Mutex.h new file mode 100644 index 0000000..36a1473 --- /dev/null +++ b/airootfs/usr/include/libcalamares/compat/Mutex.h @@ -0,0 +1,30 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_COMPAT_MUTEX_H +#define CALAMARES_COMPAT_MUTEX_H + +#include + +namespace Calamares +{ + +/* + * In Qt5, QMutexLocker is a class and operates implicitly on + * QMutex but in Qt6 it is a template and needs a specialization. + */ +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) +using MutexLocker = QMutexLocker; +#else +using MutexLocker = QMutexLocker< QMutex >; +#endif + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/compat/Size.h b/airootfs/usr/include/libcalamares/compat/Size.h new file mode 100644 index 0000000..5e217e4 --- /dev/null +++ b/airootfs/usr/include/libcalamares/compat/Size.h @@ -0,0 +1,23 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2024 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_COMPAT_SIZE_H +#define CALAMARES_COMPAT_SIZE_H + +#include + +namespace Calamares +{ +/* Compatibility of size types (e.g. qsizetype, STL sizes, int) between Qt5 and Qt6 */ + +using NumberForTr = int; + +} + +#endif diff --git a/airootfs/usr/include/libcalamares/compat/Variant.h b/airootfs/usr/include/libcalamares/compat/Variant.h new file mode 100644 index 0000000..dab4e85 --- /dev/null +++ b/airootfs/usr/include/libcalamares/compat/Variant.h @@ -0,0 +1,57 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_COMPAT_VARIANT_H +#define CALAMARES_COMPAT_VARIANT_H + +#include + +namespace Calamares +{ +/* Compatibility of QVariant between Qt5 and Qt6 */ +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) +const auto typeOf = []( const QVariant& v ) { return v.type(); }; +const auto ListVariantType = QVariant::List; +const auto MapVariantType = QVariant::Map; +const auto HashVariantType = QVariant::Hash; +const auto StringVariantType = QVariant::String; +const auto CharVariantType = QVariant::Char; +const auto StringListVariantType = QVariant::StringList; +const auto BoolVariantType = QVariant::Bool; +const auto IntVariantType = QVariant::Int; +const auto UIntVariantType = QVariant::UInt; +const auto LongLongVariantType = QVariant::LongLong; +const auto ULongLongVariantType = QVariant::ULongLong; +const auto DoubleVariantType = QVariant::Double; +#else +const auto typeOf = []( const QVariant& v ) { return v.typeId(); }; +const auto ListVariantType = QMetaType::Type::QVariantList; +const auto MapVariantType = QMetaType::Type::QVariantMap; +const auto HashVariantType = QMetaType::Type::QVariantHash; +const auto StringVariantType = QMetaType::Type::QString; +const auto CharVariantType = QMetaType::Type::Char; +const auto StringListVariantType = QMetaType::Type::QStringList; +const auto BoolVariantType = QMetaType::Type::Bool; +const auto IntVariantType = QMetaType::Type::Int; +const auto UIntVariantType = QMetaType::Type::UInt; +const auto LongLongVariantType = QMetaType::Type::LongLong; +const auto ULongLongVariantType = QMetaType::Type::ULongLong; +const auto DoubleVariantType = QMetaType::Type::Double; +#endif + +inline bool +isIntegerVariantType( const QVariant& v ) +{ + const auto t = typeOf( v ); + return t == IntVariantType || t == UIntVariantType || t == LongLongVariantType || t == ULongLongVariantType; +} + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/compat/Xml.h b/airootfs/usr/include/libcalamares/compat/Xml.h new file mode 100644 index 0000000..75b775a --- /dev/null +++ b/airootfs/usr/include/libcalamares/compat/Xml.h @@ -0,0 +1,42 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2024 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_COMPAT_XML_H +#define CALAMARES_COMPAT_XML_H + +#include + +namespace Calamares +{ +#if QT_VERSION < QT_VERSION_CHECK( 6, 6, 0 ) +struct ParseResult +{ + QString errorMessage; + int errorLine = -1; + int errorColumn = -1; +}; + +[[nodiscard]] inline ParseResult +setXmlContent( QDomDocument& doc, const QByteArray& ba ) +{ + ParseResult p; + const bool r = doc.setContent( ba, &p.errorMessage, &p.errorLine, &p.errorColumn ); + return r ? ParseResult {} : p; +} +#else +[[nodiscard]] inline QDomDocument::ParseResult +setXmlContent( QDomDocument& doc, const QByteArray& ba ) +{ + return doc.setContent( ba ); +} +#endif + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/geoip/GeoIPFixed.h b/airootfs/usr/include/libcalamares/geoip/GeoIPFixed.h new file mode 100644 index 0000000..b180630 --- /dev/null +++ b/airootfs/usr/include/libcalamares/geoip/GeoIPFixed.h @@ -0,0 +1,43 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef GEOIP_GEOIPFIXED_H +#define GEOIP_GEOIPFIXED_H + +#include "Interface.h" + +namespace Calamares +{ +namespace GeoIP +{ +/** @brief GeoIP with a fixed return value + * + * The data is ignored entirely and the attribute value is returned unchanged. + * Note that you still need to provide a usable URL for a successful GeoIP + * lookup -- the URL's data is just ignored. + * + * @note This class is an implementation detail. + */ +class GeoIPFixed : public Interface +{ +public: + /** @brief Configure the value to return from rawReply() + * + * An empty string, which would not be a valid zone name, is + * translated to "Europe/Amsterdam". + */ + explicit GeoIPFixed( const QString& value = QString() ); + + virtual RegionZonePair processReply( const QByteArray& ) override; + virtual QString rawReply( const QByteArray& ) override; +}; + +} // namespace GeoIP +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/geoip/GeoIPJSON.h b/airootfs/usr/include/libcalamares/geoip/GeoIPJSON.h new file mode 100644 index 0000000..3c226ff --- /dev/null +++ b/airootfs/usr/include/libcalamares/geoip/GeoIPJSON.h @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef GEOIP_GEOIPJSON_H +#define GEOIP_GEOIPJSON_H + +#include "Interface.h" + +namespace Calamares +{ +namespace GeoIP +{ +/** @brief GeoIP lookup for services that return JSON. + * + * This is the original implementation of GeoIP lookup, + * (e.g. using the FreeGeoIP.net service), or similar. + * + * The data is assumed to be in JSON format with a time_zone attribute. + * + * @note This class is an implementation detail. + */ +class GeoIPJSON : public Interface +{ +public: + /** @brief Configure the attribute name which is selected. + * + * If an empty string is passed in (not a valid attribute name), + * then "time_zone" is used. + */ + explicit GeoIPJSON( const QString& attribute = QString() ); + + virtual RegionZonePair processReply( const QByteArray& ) override; + virtual QString rawReply( const QByteArray& ) override; +}; + +} // namespace GeoIP +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/geoip/GeoIPTests.h b/airootfs/usr/include/libcalamares/geoip/GeoIPTests.h new file mode 100644 index 0000000..4d36edb --- /dev/null +++ b/airootfs/usr/include/libcalamares/geoip/GeoIPTests.h @@ -0,0 +1,37 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef GEOIPTESTS_H +#define GEOIPTESTS_H + +#include + +class GeoIPTests : public QObject +{ + Q_OBJECT +public: + GeoIPTests(); + ~GeoIPTests() override; + +private Q_SLOTS: + void initTestCase(); + void testFixed(); + void testJSON(); + void testJSONalt(); + void testJSONbad(); + void testXML(); + void testXML2(); + void testXMLalt(); + void testXMLbad(); + void testSplitTZ(); + + void testGet(); +}; + +#endif diff --git a/airootfs/usr/include/libcalamares/geoip/GeoIPXML.h b/airootfs/usr/include/libcalamares/geoip/GeoIPXML.h new file mode 100644 index 0000000..313b931 --- /dev/null +++ b/airootfs/usr/include/libcalamares/geoip/GeoIPXML.h @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef GEOIP_GEOIPXML_H +#define GEOIP_GEOIPXML_H + +#include "Interface.h" + +namespace Calamares +{ +namespace GeoIP +{ +/** @brief GeoIP lookup with XML data + * + * The data is assumed to be in XML format with a + * + * element, which contains the text (string) for the region/zone. This + * format is expected by, e.g. the Ubiquity installer. + * + * @note This class is an implementation detail. + */ +class GeoIPXML : public Interface +{ +public: + /** @brief Configure the element tag which is selected. + * + * If an empty string is passed in (not a valid element tag), + * then "TimeZone" is used. + */ + explicit GeoIPXML( const QString& element = QString() ); + + virtual RegionZonePair processReply( const QByteArray& ) override; + virtual QString rawReply( const QByteArray& ) override; +}; + +} // namespace GeoIP +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/geoip/Handler.h b/airootfs/usr/include/libcalamares/geoip/Handler.h new file mode 100644 index 0000000..4b69612 --- /dev/null +++ b/airootfs/usr/include/libcalamares/geoip/Handler.h @@ -0,0 +1,86 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef GEOIP_HANDLER_H +#define GEOIP_HANDLER_H + +#include "Interface.h" + +#include +#include +#include + +namespace Calamares +{ +namespace GeoIP +{ + +/** @brief Handle one complete GeoIP lookup. + * + * This class handles one complete GeoIP lookup. Create it with + * suitable configuration values, then call get(). This is a + * synchronous API and will return an invalid zone pair on + * error or if the configuration is not understood. For an + * async API, use query(). + */ +class DLLEXPORT Handler +{ +public: + enum class Type + { + None, // No lookup, returns empty string + JSON, // JSON-formatted data, returns extracted field + XML, // XML-formatted data, returns extracted field + Fixed // Returns selector string verbatim + }; + + /** @brief An unconfigured handler; this always returns errors. */ + Handler(); + /** @brief A handler for a specific GeoIP source. + * + * The @p implementation name selects an implementation; currently JSON and XML + * are supported. The @p url is retrieved by query() and then the @p selector + * is used to select something from the data returned by the @url. + */ + Handler( const QString& implementation, const QString& url, const QString& selector ); + + ~Handler(); + + /** @brief Synchronously get the GeoIP result. + * + * If the Handler is valid, then do the actual fetching and interpretation + * of data and return the result. An invalid Handler will return an + * invalid (empty) result. + */ + RegionZonePair get() const; + /// @brief Like get, but don't interpret the contents + QString getRaw() const; + + /** @brief Asynchronously get the GeoIP result. + * + * See get() for the return value. + */ + QFuture< RegionZonePair > query() const; + /// @brief Like query, but don't interpret the contents + QFuture< QString > queryRaw() const; + + bool isValid() const { return m_type != Type::None; } + Type type() const { return m_type; } + QString url() const { return m_url; } + QString selector() const { return m_selector; } + +private: + Type m_type; + const QString m_url; + const QString m_selector; +}; + +} // namespace GeoIP +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/geoip/Interface.h b/airootfs/usr/include/libcalamares/geoip/Interface.h new file mode 100644 index 0000000..c144cc3 --- /dev/null +++ b/airootfs/usr/include/libcalamares/geoip/Interface.h @@ -0,0 +1,127 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef GEOIP_INTERFACE_H +#define GEOIP_INTERFACE_H + +#include "DllMacro.h" + +#include +#include +#include + +#include + +class QByteArray; + +namespace Calamares +{ +namespace GeoIP +{ +/** @brief A Region, Zone pair of strings + * + * A GeoIP lookup returns a timezone, which is represented as a Region, + * Zone pair of strings (e.g. "Europe" and "Amsterdam"). Generally, + * pasting the strings back together with a "/" is the right thing to + * do. The Zone **may** contain a "/" (e.g. "Kentucky/Monticello"). + */ +class DLLEXPORT RegionZonePair +{ +public: + /** @brief Construct from two strings, like qMakePair(). */ + RegionZonePair( const QString& region, const QString& zone ) + : m_region( region ) + , m_zone( zone ) + { + } + + /** @brief Construct from an existing pair. */ + RegionZonePair( const RegionZonePair& p ) + : RegionZonePair( p.m_region, p.m_zone ) + { + } + + /** @brief An invalid zone pair (empty strings). */ + RegionZonePair() = default; + + bool isValid() const { return !m_region.isEmpty(); } + + QString region() const { return m_region; } + QString zone() const { return m_zone; } + + friend bool operator==( const RegionZonePair& lhs, const RegionZonePair& rhs ) noexcept + { + return std::tie( lhs.m_region, lhs.m_zone ) == std::tie( rhs.m_region, rhs.m_zone ); + } + + QString asString() const { return isValid() ? region() + QChar( '/' ) + zone() : QString(); } + +private: + QString m_region; + QString m_zone; +}; + +inline QDebug& +operator<<( QDebug&& s, const RegionZonePair& tz ) +{ + return s << tz.asString(); +} + +inline QDebug& +operator<<( QDebug& s, const RegionZonePair& tz ) +{ + return s << tz.asString(); +} + +/** @brief Splits a region/zone string into a pair. + * + * Cleans up the string by removing backslashes (\\) + * since some providers return silly-escaped names. Replaces + * spaces with _ since some providers return human-readable names. + * Splits on the first / in the resulting string, or returns a + * pair of empty QStrings if it can't. (e.g. America/North Dakota/Beulah + * will return "America", "North_Dakota/Beulah"). + */ +DLLEXPORT RegionZonePair splitTZString( const QString& s ); + +/** + * @brief Interface for GeoIP retrievers. + * + * A GeoIP retriever takes a configured URL (from the config file) + * and can handle the data returned from its interpretation of that + * configured URL, returning a region and zone. + */ +class DLLEXPORT Interface +{ +public: + virtual ~Interface(); + + /** @brief Handle a (successful) request by interpreting the data. + * + * Should return a ( , ) pair, e.g. + * ( "Europe", "Amsterdam" ). This is called **only** if the + * request to the fullUrl was successful; the handler + * is free to read as much, or as little, data as it + * likes. On error, returns a RegionZonePair with empty + * strings (e.g. ( "", "" ) ). + */ + virtual RegionZonePair processReply( const QByteArray& ) = 0; + + /** @brief Get the raw reply data. */ + virtual QString rawReply( const QByteArray& ) = 0; + +protected: + Interface( const QString& element = QString() ); + + QString m_element; // string for selecting from data +}; + +} // namespace GeoIP +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/locale/Global.h b/airootfs/usr/include/libcalamares/locale/Global.h new file mode 100644 index 0000000..6a4047b --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/Global.h @@ -0,0 +1,83 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/** @file GlobalStorage management for Locale settings + * + * The *localeConf* key in Global Storage is semi-structured, + * and there are multiple modules that write to it (and some that + * read from it). Functions in this file provide access to + * that semi-structured data. + */ + +#ifndef LOCALE_GLOBAL_H +#define LOCALE_GLOBAL_H + +#include "DllMacro.h" + +#include +#include +#include + +namespace Calamares +{ +class GlobalStorage; + +namespace Locale +{ + +/** @brief Selector for methods that insert multiple values. + * + * When inserting, use @c Overwrite to remove all keys not in the collection + * of values being inserted; use @c Merge to preserve whatever is + * already in Global Storage but not mentioned in the collection. + */ +enum class InsertMode +{ + Overwrite, + Merge +}; + +/** @brief Insert the given @p values into the *localeConf* map in @p gs + * + * @param gs The Global Storage to write to + * @param values The collection of keys and values to write to @p gs + * @param mode Indicates whether the *localeConf* key is cleared first + * + * The keys in the collection @p values should be first-level keys + * in *localeConf*, e.g. "LANG" or "LC_TIME". No effort is made to + * enforce this. + */ +DLLEXPORT void insertGS( Calamares::GlobalStorage& gs, const QVariantMap& values, InsertMode mode = InsertMode::Merge ); +/** @brief Insert the given @p values into the *localeConf* map in @p gs + * + * Alternate way of providing the keys and values. + */ +DLLEXPORT void +insertGS( Calamares::GlobalStorage& gs, const QMap< QString, QString >& values, InsertMode mode = InsertMode::Merge ); +/** @brief Write a single @p key and @p value to the *localeConf* map + */ +DLLEXPORT void insertGS( Calamares::GlobalStorage& gs, const QString& key, const QString& value ); +/** @brief Remove a single @p key from the *localeConf* map + */ +DLLEXPORT void removeGS( Calamares::GlobalStorage& gs, const QString& key ); +/** @brief Remove the *localeConf* map from Global Storage + */ +DLLEXPORT void clearGS( Calamares::GlobalStorage& gs ); + +/** @brief Gets a value from the *localeConf* map in @p gs + * + * If the key is not set (or doesn't exist), returns QString(). + */ +DLLEXPORT QString readGS( Calamares::GlobalStorage& gs, const QString& key ); + +} // namespace Locale +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/locale/Lookup.h b/airootfs/usr/include/libcalamares/locale/Lookup.h new file mode 100644 index 0000000..d026f93 --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/Lookup.h @@ -0,0 +1,47 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef LOCALE_LOOKUP_H +#define LOCALE_LOOKUP_H + +#include "DllMacro.h" + +#include +#include + +namespace Calamares +{ +namespace Locale +{ +/* All the functions in this file do lookups of locale data + * based on CLDR tables; these are lookups that you can't (easily) + * do with just QLocale (e.g. from 2-letter country code to a likely + * locale). + */ + +/// @brief Map a 2-letter code to a Country, or AnyCountry if not found +DLLEXPORT QLocale::Country countryForCode( const QString& code ); +/** @brief Map a Country to a Language, or AnyLanguage if not found + * + * This is a *likely* language for the given country, based on the + * CLDR tables. For instance, this maps Belgium to Dutch. + */ +DLLEXPORT QLocale::Language languageForCountry( QLocale::Country country ); +/// @brief Map a 2-letter code to a Language, or AnyLanguage if not found +DLLEXPORT QLocale::Language languageForCountry( const QString& code ); + +/// @brief Get both Country and Language for a 2-letter code +DLLEXPORT QPair< QLocale::Country, QLocale::Language > countryData( const QString& code ); +/// @brief Get a likely locale for a 2-letter country code +DLLEXPORT QLocale countryLocale( const QString& code ); +} // namespace Locale +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/locale/TimeZone.h b/airootfs/usr/include/libcalamares/locale/TimeZone.h new file mode 100644 index 0000000..43db892 --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/TimeZone.h @@ -0,0 +1,237 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/** @file Timezone data and models to go with it + * + * The TimeZoneData class holds information from zone.tab, about + * TZ names and locations (latitude and longitude) for geographic + * lookups. + * + * The RegionModel lists the regions of the world (about 12) and + * ZonesModel lists all the timezones; the RegionalZonesModel provides + * a way to restrict the view of timezones to those of a specific region. + * + */ +#ifndef LOCALE_TIMEZONE_H +#define LOCALE_TIMEZONE_H + +#include "DllMacro.h" + +#include "locale/TranslatableString.h" + +#include +#include +#include +#include + +namespace Calamares +{ +namespace Locale +{ +class Private; +class RegionalZonesModel; +class ZonesModel; + +class DLLEXPORT TimeZoneData : public QObject, TranslatableString +{ + friend class RegionalZonesModel; + friend class ZonesModel; + + Q_OBJECT + + Q_PROPERTY( QString region READ region CONSTANT ) + Q_PROPERTY( QString zone READ zone CONSTANT ) + Q_PROPERTY( QString name READ translated CONSTANT ) + Q_PROPERTY( QString countryCode READ country CONSTANT ) + +public: + TimeZoneData( const QString& region, + const QString& zone, + const QString& country, + double latitude, + double longitude ); + 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; } + double latitude() const { return m_latitude; } + double longitude() const { return m_longitude; } + +private: + QString m_region; + QString m_country; + double m_latitude; + double m_longitude; +}; + +/** @brief The list of timezone regions + * + * The regions are a short list of global areas (Africa, America, India ..) + * which contain zones. + */ +class DLLEXPORT RegionsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles + { + NameRole = Qt::DisplayRole, + KeyRole = Qt::UserRole // So that currentData() will get the key + }; + + RegionsModel( QObject* parent = nullptr ); + ~RegionsModel() override; + + int rowCount( const QModelIndex& parent ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + + QHash< int, QByteArray > roleNames() const override; + +public Q_SLOTS: + /** @brief Provides a human-readable version of the region + * + * Returns @p region unchanged if there is no such region + * or no translation for the region's name. + */ + QString translated( const QString& region ) const; + +private: + Private* m_private; +}; + +class DLLEXPORT ZonesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Roles + { + NameRole = Qt::DisplayRole, + KeyRole = Qt::UserRole, // So that currentData() will get the key + RegionRole = Qt::UserRole + 1 + }; + + ZonesModel( QObject* parent = nullptr ); + ~ZonesModel() override; + + int rowCount( const QModelIndex& parent ) const override; + QVariant data( const QModelIndex& index, int role ) const override; + + QHash< int, QByteArray > roleNames() const override; + + /** @brief Iterator for the underlying list of zones + * + * Iterates over all the zones in the model. Operator * may return + * a @c nullptr when the iterator is not valid. Typical usage: + * + * ``` + * for( auto it = model.begin(); it; ++it ) + * { + * const auto* zonedata = *it; + * ... + * } + */ + class Iterator + { + friend class ZonesModel; + Iterator( const Private* m ) + : m_index( 0 ) + , m_p( m ) + { + } + + public: + operator bool() const; + void operator++() { ++m_index; } + const TimeZoneData* operator*() const; + int index() const { return m_index; } + + private: + int m_index; + const Private* m_p; + }; + + Iterator begin() const { return Iterator( m_private ); } + + /** @brief Look up TZ data based on an arbitrary distance function + * + * This is a generic method that can define distance in whatever + * coordinate system is wanted; returns the zone with the smallest + * distance. The @p distanceFunc must return "the distance" for + * each zone. It would be polite to return something non-negative. + * + * Note: not a slot, because the parameter isn't moc-able. + */ + const TimeZoneData* find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const; + +public Q_SLOTS: + /** @brief Look up TZ data based on its name. + * + * Returns @c nullptr if not found. + */ + const TimeZoneData* find( const QString& region, const QString& zone ) const; + + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone to the given lat and lon. This is a + * convenience function for calling find(), below, with a standard + * distance function based on the distance between the given + * location (lat and lon) and each zone's given location. + */ + const TimeZoneData* find( double latitude, double longitude ) const; + + /** @brief Look up TZ data based on the location. + * + * Returns the nearest zone, or New York. This is non-const for QML + * purposes, but the object should be considered const anyway. + */ + QObject* lookup( double latitude, double longitude ) const; + +private: + Private* m_private; +}; + +class DLLEXPORT RegionalZonesModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY( QString region READ region WRITE setRegion NOTIFY regionChanged ) + +public: + RegionalZonesModel( ZonesModel* source, QObject* parent = nullptr ); + ~RegionalZonesModel() override; + + bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const override; + + QString region() const { return m_region; } + +public Q_SLOTS: + void setRegion( const QString& r ); + +signals: + void regionChanged( const QString& ); + +private: + Private* m_private; + QString m_region; +}; + +} // namespace Locale +} // namespace Calamares + +#endif // LOCALE_TIMEZONE_H diff --git a/airootfs/usr/include/libcalamares/locale/TranslatableConfiguration.h b/airootfs/usr/include/libcalamares/locale/TranslatableConfiguration.h new file mode 100644 index 0000000..1bc95b2 --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/TranslatableConfiguration.h @@ -0,0 +1,104 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/** @file Run-time translation of strings from configuration files + * + * The TranslatedString class provides a way of doing run-time + * lookups of human-readable strings, from data provided in + * the configuration files (*.conf) for Calamares. This acts + * like "normal" translation through tr() calls, as far as the + * user-visible part goes. + */ +#ifndef LOCALE_TRANSLATABLECONFIGURATION_H +#define LOCALE_TRANSLATABLECONFIGURATION_H + +#include "DllMacro.h" + +#include +#include +#include + +namespace Calamares +{ +namespace Locale +{ +/** @brief A human-readable string from a configuration file + * + * The configuration files can contain human-readable strings, + * but those need their own translations and are not supported + * by QObject::tr or anything else. + */ +class DLLEXPORT TranslatedString +{ +public: + /** @brief Get all the translations connected to @p key + * + * Gets map[key] as the "untranslated" form, and then all the + * keys of the form [lang] are taken as the translation + * for of the untranslated form. + * + * If @p context is not a nullptr, then that is taken as an + * indication to **also** use the regular QObject::tr() translation + * mechanism for these strings. It is recommended to pass in + * metaObject()->className() as context (from a QObject based class) + * to give the TranslatedString the same context as other calls + * to tr() within that class. + * + * The @p context, if any, should point to static data; it is + * **not** owned by the TranslatedString. + */ + TranslatedString( const QVariantMap& map, const QString& key, const char* context = nullptr ); + /** @brief Not-actually-translated string. + */ + TranslatedString( const QString& string ); + /** @brief Proxy for calling QObject::tr() + * + * This is like the two constructors above, with an empty map an a + * non-null context. It will end up calling tr() with that context. + * + * The @p context, if any, should point to static data; it is + * **not** owned by the TranslatedString. + */ + TranslatedString( const QString& key, const char* context ); + /// @brief Empty string + TranslatedString() + : TranslatedString( QString() ) + { + } + + /** @brief How many strings (translations) are there? + * + * This is always at least 1 (for the untranslated string), + * but may be more than 1 even when isEmpty() is true -- + * if there is no untranslated version, for instance. + */ + int count() const { return m_strings.count(); } + /** @brief Consider this string empty? + * + * Only the state of the untranslated string is considered, + * so count() may be more than 1 even while the string is empty. + */ + bool isEmpty() const { return m_strings[ QString() ].isEmpty(); } + + /// @brief Gets the string in the current locale + QString get() const; + + /// @brief Gets the string from the given locale + QString get( const QLocale& ) const; + +private: + // Maps locale name to human-readable string, "" is English + QMap< QString, QString > m_strings; + const char* m_context = nullptr; +}; +} // namespace Locale +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/locale/TranslatableString.h b/airootfs/usr/include/libcalamares/locale/TranslatableString.h new file mode 100644 index 0000000..438a948 --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/TranslatableString.h @@ -0,0 +1,58 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef LOCALE_TRANSLATABLESTRING_H +#define LOCALE_TRANSLATABLESTRING_H + +#include + +namespace Calamares +{ +namespace Locale +{ + +/** @brief A pair of strings, one human-readable, one a key + * + * Given an identifier-like string (e.g. "New_York"), makes + * a human-readable version of that and keeps a copy of the + * identifier itself. + * + * This explicitly uses const char* instead of just being + * QPair because the human-readable part + * may need to be translated through tr(), and that takes a char* + * C-style strings. + */ +class TranslatableString +{ +public: + /// @brief An empty pair + TranslatableString() {} + /// @brief Given an identifier, create the pair + explicit TranslatableString( const char* s1 ); + explicit TranslatableString( const QString& s ); + TranslatableString( TranslatableString&& t ); + TranslatableString( const TranslatableString& ); + virtual ~TranslatableString(); + + /// @brief Give the localized human-readable form + virtual QString translated() const = 0; + QString key() const { return m_key; } + + bool operator==( const TranslatableString& other ) const { return m_key == other.m_key; } + bool operator<( const TranslatableString& other ) const { return m_key < other.m_key; } + +protected: + char* m_human = nullptr; + QString m_key; +}; + +} // namespace Locale +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/locale/Translation.h b/airootfs/usr/include/libcalamares/locale/Translation.h new file mode 100644 index 0000000..14fb33c --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/Translation.h @@ -0,0 +1,145 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef LOCALE_TRANSLATION_H +#define LOCALE_TRANSLATION_H + +#include "utils/Logger.h" + +#include +#include +#include + +///@brief Define to 1 if the Qt version being used supports Interlingue fully +#if QT_VERSION < QT_VERSION_CHECK( 6, 7, 0 ) +#define CALAMARES_QT_SUPPORT_INTERLINGUE 0 +#else +#define CALAMARES_QT_SUPPORT_INTERLINGUE 1 +#endif + +namespace Calamares +{ +namespace Locale +{ + +/** + * @brief Consistent locale (language + country) naming. + * + * Support class to turn locale names (as used by Calamares's + * translation system) into QLocales, and also into consistent + * human-readable text labels. + * + * This handles special-cases in Calamares translations: + * - `sr@latin` is the name which Qt recognizes as `sr@latn`, + * Serbian written with Latin characters (not Cyrillic). + * - `ca@valencia` is the Catalan dialect spoken in Valencia. + * There is no Qt code for it. + * + * (There are more special cases, not documented here) + */ +class DLLEXPORT Translation : public QObject +{ + Q_OBJECT + +public: + /** @brief Formatting option for label -- add (country) to label. */ + enum class LabelFormat + { + AlwaysWithCountry, + IfNeededWithCountry + }; + + struct Id + { + QString name; + }; + + /** @brief Empty locale. This uses the system-default locale. */ + Translation( QObject* parent = nullptr ); + + /** @brief Construct from a locale name. + * + * The @p localeName should be one that Qt recognizes, e.g. en_US or ar_EY. + * The @p format determines whether the country name is always present + * in the label (human-readable form) or only if needed for disambiguation. + */ + Translation( const Id& localeId, LabelFormat format = LabelFormat::IfNeededWithCountry, QObject* parent = nullptr ); + + /** @brief Define a sorting order. + * + * Locales are sorted by their id, which means the ISO 2-letter code + country. + */ + bool operator<( const Translation& other ) const { return m_localeId < other.m_localeId; } + + /** @brief Is this locale English? + * + * en_US and en (American English) is defined as English. The Queen's + * English -- proper English -- is relegated to non-English status. + */ + bool isEnglish() const { return m_localeId == QLatin1String( "en_US" ) || m_localeId == QLatin1String( "en" ); } + + /** @brief Get the human-readable name for this locale. */ + QString label() const { return m_label; } + /** @brief Get the *English* human-readable name for this locale. */ + QString englishLabel() const { return m_englishLabel; } + + /** @brief Get the Qt locale. */ + QLocale locale() const { return m_locale; } + + /** @brief Gets the Calamares internal name (code) of the locale. + * + * This is a strongly-typed return to avoid it ending up all over + * the place as a QString. + */ + Id id() const { return { m_localeId }; } + + /// @brief Convenience accessor to the language part of the locale + QLocale::Language language() const { return m_locale.language(); } + + /// @brief Convenience accessor to the country part (if any) of the locale + QLocale::Country country() const + { +#if QT_VERSION < QT_VERSION_CHECK( 6, 6, 0 ) + + return m_locale.country(); +#else + return m_locale.territory(); +#endif + } + + /** @brief Get a Qt locale for the given @p localeName + * + * This obeys special cases as described in the class documentation. + */ + static QLocale getLocale( const Id& localeId ); + +private: + QLocale m_locale; + QString m_localeId; // the locale identifier, e.g. "en_GB" + QString m_label; // the native name of the locale + QString m_englishLabel; +}; + +static inline QDebug& +operator<<( QDebug& s, const Translation::Id& id ) +{ + return s << id.name; +} +static inline bool +operator==( const Translation::Id& lhs, const Translation::Id& rhs ) +{ + return lhs.name == rhs.name; +} + +} // namespace Locale +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/locale/TranslationsModel.h b/airootfs/usr/include/libcalamares/locale/TranslationsModel.h new file mode 100644 index 0000000..0fdad03 --- /dev/null +++ b/airootfs/usr/include/libcalamares/locale/TranslationsModel.h @@ -0,0 +1,94 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Camilo Higuita + * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef LOCALE_TRANSLATIONSMODEL_H +#define LOCALE_TRANSLATIONSMODEL_H + +#include "DllMacro.h" +#include "Translation.h" + +#include +#include + +namespace Calamares +{ +namespace Locale +{ + +class DLLEXPORT TranslationsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum + { + LabelRole = Qt::DisplayRole, + EnglishLabelRole = Qt::UserRole + 1 + }; + + TranslationsModel( const QStringList& locales, QObject* parent = nullptr ); + ~TranslationsModel() override; + + int rowCount( const QModelIndex& parent ) const override; + + QVariant data( const QModelIndex& index, int role ) const override; + QHash< int, QByteArray > roleNames() const override; + + /** @brief Gets locale information for entry #n + * + * This is the backing data for the model; if @p row is out-of-range, + * returns a reference to en_US. + */ + const Translation& locale( int row ) const; + + /// @brief Returns all of the locale Ids (e.g. en_US) put into this model. + const QStringList& localeIds() const { return m_localeIds; } + + /** @brief Searches for an item that matches @p predicate + * + * Returns the row number of the first match, or -1 if there isn't one. + */ + int find( std::function< bool( const QLocale& ) > predicate ) const; + int find( std::function< bool( const Translation& ) > predicate ) const; + /// @brief Looks for an item using the same locale, -1 if there isn't one + int find( const QLocale& ) const; + /// @brief Looks for an item that best matches the 2-letter country code + int find( const QString& countryCode ) const; + /// @brief Looks up a translation Id + int find( const Translation::Id& id ) const; + +private: + QVector< Translation* > m_locales; + QStringList m_localeIds; +}; + +/** @brief Returns a model with all available translations. + * + * The translations are set when Calamares is compiled; the list + * of names used can be queried with avalableLanguages(). + * + * This model is a singleton and can be shared. + * + * NOTE: While the model is not typed const, it should be. Do not modify. + */ +DLLEXPORT TranslationsModel* availableTranslations(); + +/** @brief The list of names (e.g. en, pt_BR) of available translations. + * + * The translations are set when Calamares is compiled. + * At CMake-time, the list CALAMARES_TRANSLATION_LANGUAGES + * is used to create the table. + */ +DLLEXPORT const QStringList& availableLanguages(); + +} // namespace Locale +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/Actions.h b/airootfs/usr/include/libcalamares/modulesystem/Actions.h new file mode 100644 index 0000000..f4bbe7e --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/Actions.h @@ -0,0 +1,29 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef MODULESYSTEM_ACTIONS_H +#define MODULESYSTEM_ACTIONS_H + +namespace Calamares +{ +namespace ModuleSystem +{ + +enum class Action : char +{ + Show, + Exec +}; + +} // namespace ModuleSystem +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/Config.h b/airootfs/usr/include/libcalamares/modulesystem/Config.h new file mode 100644 index 0000000..5facae3 --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/Config.h @@ -0,0 +1,147 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_MODULESYSTEM_CONFIG_H +#define CALAMARES_MODULESYSTEM_CONFIG_H + +#include "DllMacro.h" + +#include +#include +#include + +#include + +namespace Calamares +{ +namespace ModuleSystem +{ +/** @brief Base class for Config-objects + * + * This centralizes the things every Config-object should + * do and provides one source of preset-data. A Config-object + * for a module can **optionally** inherit from this class + * to get presets-support. + * + * TODO:3.3 This is not optional + * TODO:3.3 Put consistent i18n for Configurations in here too + */ +class DLLEXPORT Config : public QObject +{ + Q_OBJECT +public: + Config( QObject* parent = nullptr ); + ~Config() override; + + /** @brief Set the configuration from the config file + * + * Subclasses must implement this to load configuration data; + * that subclass **should** also call loadPresets() with the + * same map, to pick up the "presets" key consistently. + */ + virtual void setConfigurationMap( const QVariantMap& ) = 0; + +public Q_SLOTS: + /** @brief Checks if a @p fieldName is editable according to presets + * + * If the field is named as a preset, **and** the field is set + * to not-editable, returns @c false. Otherwise, return @c true. + * Calling this with an unknown field (one for which no presets + * are accepted) will print a warning and return @c true. + * + * @see CONFIG_PREVENT_EDITING + * + * Most setters will call isEditable() to check if the field should + * be editable. Do not count on the setter not being called: the + * UI might not have set the field to fixed / constant / not-editable + * and then you can have the setter called by changes in the UI. + * + * To prevent the UI from changing **and** to make sure that the UI + * reflects the unchanged value (rather than the changed value it + * sent to the Config object), use CONFIG_PREVENT_EDITING, like so: + * + * CONFIG_PREVENT_EDITING( type, "propertyName" ); + * + * The ; is necessary. is the type of the property; for instance + * QString. The name of the property is a (constant) string. The + * macro will return (out of the setter it is used in) if the field + * is not editable, and will send a notification event with the old + * value as soon as the event loop resumes. + */ + bool isEditable( const QString& fieldName ) const; + +protected: + friend class ApplyPresets; + /** @brief "Builder" class for presets + * + * Derived classes should instantiate this (with themselves, + * and the whole configuration map that is passed to + * setConfigurationMap()) and then call .apply() to apply + * the presets specified in the configuration to the **named** + * QObject properties. + */ + class ApplyPresets + { + public: + /** @brief Create a preset-applier for this config + * + * The @p configurationMap should be the one passed in to + * setConfigurationMap() . Presets are extracted from the + * standard key *presets* and can be applied to the configuration + * with apply() or operator<<. + */ + ApplyPresets( Config& c, const QVariantMap& configurationMap ); + ~ApplyPresets(); + + /** @brief Add a preset for the given @p fieldName + * + * This checks for preset-entries in the configuration map that was + * passed in to the constructor. + */ + ApplyPresets& apply( const char* fieldName ); + /** @brief Alternate way of writing apply() + */ + ApplyPresets& operator<<( const char* fieldName ) { return apply( fieldName ); } + + private: + Config& m_c; + bool m_bogus = true; + const QVariantMap m_map; + }; + +private: + class Private; + std::unique_ptr< Private > d; + bool m_unlocked = false; +}; +} // namespace ModuleSystem +} // namespace Calamares + +/// @see Config::isEditable() +// +// This needs to be a macro, because Q_ARG() is a macro that stringifies +// the type name. +#define CONFIG_PREVENT_EDITING( type, fieldName ) \ + do \ + { \ + if ( !isEditable( QStringLiteral( fieldName ) ) ) \ + { \ + auto prop = property( fieldName ); \ + const auto& metaobject = metaObject(); \ + auto metaprop = metaobject->property( metaobject->indexOfProperty( fieldName ) ); \ + if ( metaprop.hasNotifySignal() ) \ + { \ + metaprop.notifySignal().invoke( this, Qt::QueuedConnection, Q_ARG( type, prop.value< type >() ) ); \ + } \ + return; \ + } \ + } while ( 0 ) + + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/CppJobModule.h b/airootfs/usr/include/libcalamares/modulesystem/CppJobModule.h new file mode 100644 index 0000000..b999fd0 --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/CppJobModule.h @@ -0,0 +1,50 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2016 Kevin Kofler + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_CPPJOBMODULE_H +#define CALAMARES_CPPJOBMODULE_H + +#include "DllMacro.h" +#include "modulesystem/Module.h" + +class QPluginLoader; + +namespace Calamares +{ + +class UIDLLEXPORT CppJobModule : public Module +{ +public: + Type type() const override; + Interface interface() const override; + + void loadSelf() override; + JobList jobs() const override; + +protected: + void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override; + +private: + explicit CppJobModule(); + ~CppJobModule() override; + + QPluginLoader* m_loader; + job_ptr m_job; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +}; + +} // namespace Calamares + +#endif // CALAMARES_CPPJOBMODULE_H diff --git a/airootfs/usr/include/libcalamares/modulesystem/Descriptor.h b/airootfs/usr/include/libcalamares/modulesystem/Descriptor.h new file mode 100644 index 0000000..b3eefac --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/Descriptor.h @@ -0,0 +1,148 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef MODULESYSTEM_DESCRIPTOR_H +#define MODULESYSTEM_DESCRIPTOR_H + +#include "DllMacro.h" +#include "utils/NamedEnum.h" + +#include + +namespace Calamares +{ +namespace ModuleSystem +{ +/** + * @brief The Type enum represents the intended functionality of the module + * Every module is either a job module or a view module. + * A job module is a single Calamares job. + * A view module has a UI (one or more view pages) and zero-to-many jobs. + */ +enum class Type +{ + Job, + View +}; +DLLEXPORT const NamedEnumTable< Type >& typeNames(); + +/** + * @brief The Interface enum represents the interface through which the module + * talks to Calamares. + * Not all Type-Interface associations are valid. + */ +enum class Interface +{ + QtPlugin, // Jobs or Views + Python, // Jobs only + Process, // Deprecated interface +}; +DLLEXPORT const NamedEnumTable< Interface >& interfaceNames(); + +/** + * @brief Description of a module (obtained from module.desc) + * + * Provides access to the fields of a descriptor, use type() to + * determine which specialized fields make sense for a given + * descriptor (e.g. a Python module has no shared-library path). + */ +class DLLEXPORT Descriptor +{ +public: + ///@brief an invalid, and empty, descriptor + Descriptor(); + + /** @brief Fills a descriptor from the loaded (YAML) data. + * + * The @p descriptorPath is used only for debug messages, the + * data is only read from @p moduleDesc. + * + */ + static Descriptor fromDescriptorData( const QVariantMap& moduleDesc, const QString& descriptorPath ); + + bool isValid() const { return m_isValid; } + + QString name() const { return m_name; } + Type type() const { return m_type; } + Interface interface() const { return m_interface; } + + bool isEmergency() const { return m_isEmergeny; } + bool hasConfig() const { return m_hasConfig; } // TODO: 3.5 rename to noConfig() to match descriptor key + int weight() const { return m_weight < 1 ? 1 : m_weight; } + bool explicitWeight() const { return m_weight > 0; } + + + /// @brief The directory where the module.desc lives + QString directory() const { return m_directory; } + void setDirectory( const QString& d ) { m_directory = d; } + + const QStringList& requiredModules() const { return m_requiredModules; } + + /** @section C++ Modules + * + * The C++ modules are the most general, and are loaded as + * a shared library after which a suitable factory creates + * objects from them. + */ + + /// @brief Short path to the shared-library; no extension. + QString load() const { return m_interface == Interface::QtPlugin ? m_script : QString(); } + + /** @section Process Job modules + * + * Process Jobs are somewhat deprecated in favor of shellprocess + * and contextualprocess jobs, since those handle multiple configuration + * much more gracefully. + * + * Process Jobs execute one command. + */ + /// @brief The command to execute; passed to the shell + QString command() const { return m_interface == Interface::Process ? m_script : QString(); } + /// @brief Timeout in seconds + int timeout() const { return m_processTimeout; } + /// @brief Run command in chroot? + bool chroot() const { return m_processChroot; } + + /** @section Python Job modules + * + * Python job modules have one specific script to load and run. + */ + QString script() const { return m_interface == Interface::Python ? m_script : QString(); } + +private: + QString m_name; + QString m_directory; + QStringList m_requiredModules; + int m_weight = -1; + Type m_type; + Interface m_interface; + bool m_isValid = false; + bool m_isEmergeny = false; + bool m_hasConfig = true; + + /** @brief The name of the thing to load + * + * - A C++ module loads a shared library (via key *load*), + * - A Python module loads a Python script (via key *script*), + * - A process runs a specific command (via key *command*) + * + * This name-of-the-thing is stored here, regardless of which + * interface is being used. + */ + QString m_script; + + int m_processTimeout = 30; + bool m_processChroot = false; +}; + +} // namespace ModuleSystem +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/InstanceKey.h b/airootfs/usr/include/libcalamares/modulesystem/InstanceKey.h new file mode 100644 index 0000000..5153090 --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/InstanceKey.h @@ -0,0 +1,117 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef MODULESYSTEM_INSTANCEKEY_H +#define MODULESYSTEM_INSTANCEKEY_H + +#include "DllMacro.h" + +#include +#include +#include + +#include +#include + +namespace Calamares +{ +namespace ModuleSystem +{ + +/** @brief A module instance's key (`module@id`) + * + * A module instance is identified by both the module's name + * (a Calamares module, e.g. `users`) and an instance id. + * Usually, the instance id is the same as the module name + * and the whole module instance key is `users@users`, but + * it is possible to use the same module more than once + * and then you distinguish those module instances by their + * secondary id (e.g. `users@one`). + * + * This is supported by the *instances* configuration entry + * in `settings.conf`. + */ +class DLLEXPORT InstanceKey +{ +public: + /// @brief Create an instance key from explicit module and id. + InstanceKey( const QString& module, const QString& id ) + : first( module ) + , second( id ) + { + if ( second.isEmpty() ) + { + second = first; + } + validate(); + } + + /// @brief Create unusual, invalid instance key + InstanceKey() = default; + + /// @brief A valid module has both name and id + bool isValid() const { return !first.isEmpty() && !second.isEmpty(); } + + /// @brief A custom module has a non-default id + bool isCustom() const { return first != second; } + + QString module() const { return first; } + QString id() const { return second; } + + /// @brief Create instance key from stringified version + static InstanceKey fromString( const QString& s ); + + QString toString() const + { + if ( isValid() ) + { + return first + '@' + second; + } + return QString(); + } + + friend bool operator==( const InstanceKey& lhs, const InstanceKey& rhs ) noexcept + { + return std::tie( lhs.first, lhs.second ) == std::tie( rhs.first, rhs.second ); + } + + friend bool operator<( const InstanceKey& lhs, const InstanceKey& rhs ) noexcept + { + return std::tie( lhs.first, lhs.second ) < std::tie( rhs.first, rhs.second ); + } + +private: + /** @brief Check validity and reset module and id if needed. */ + void validate() + { + if ( first.contains( '@' ) || second.contains( '@' ) ) + { + first = QString(); + second = QString(); + } + } + + QString first; + QString second; +}; + +using InstanceKeyList = QList< InstanceKey >; + +DLLEXPORT QDebug& operator<<( QDebug& s, const Calamares::ModuleSystem::InstanceKey& i ); +inline QDebug& +operator<<( QDebug&& s, const Calamares::ModuleSystem::InstanceKey& i ) +{ + return s << i; +} + +} // namespace ModuleSystem +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/Module.h b/airootfs/usr/include/libcalamares/modulesystem/Module.h new file mode 100644 index 0000000..88632cb --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/Module.h @@ -0,0 +1,169 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef CALAMARES_MODULE_H +#define CALAMARES_MODULE_H + +#include "DllMacro.h" +#include "Job.h" + +#include "modulesystem/Descriptor.h" +#include "modulesystem/InstanceKey.h" +#include "modulesystem/Requirement.h" + +#include +#include + + +namespace Calamares +{ +class Module; +Module* moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); + + +/** + * @brief The Module class is a common supertype for Calamares modules. + * It enforces a common interface for all the different types of modules, and it + * takes care of creating an object of the correct type starting from a module + * descriptor structure. + */ +class DLLEXPORT Module +{ +public: + using Type = ModuleSystem::Type; + using Interface = ModuleSystem::Interface; + + virtual ~Module(); + + /** + * @brief name returns the name of this module. + * @return a string with this module's name. + */ + QString name() const { return m_key.module(); } + + /** + * @brief instanceId returns the instance id of this module. + * @return a string with this module's instance id. + */ + QString instanceId() const { return m_key.id(); } + + /** + * @brief instanceKey returns the instance key of this module. + * @return a string with the instance key. + * A module instance's instance key is modulename\@instanceid. + * For instance, "partition\@partition" (default configuration) or + * "locale\@someconfig" (custom configuration) + */ + ModuleSystem::InstanceKey instanceKey() const { return m_key; } + + /** + * @brief location returns the full path of this module's directory. + * @return the path. + */ + QString location() const { return m_directory; } + + /** + * @brief Is this an emergency module? + * + * An emergency module is run even if an error occurs + * which would terminate Calamares earlier in the same + * *exec* block. Emergency modules in later exec blocks + * are not run (in the common case where there is only + * one exec block, this doesn't really matter). + */ + bool isEmergency() const { return m_emergency; } + + /** + * @brief isLoaded reports on the loaded status of a module. + * @return true if the module's loading phase has finished, otherwise false. + */ + bool isLoaded() const { return m_loaded; } + + /** + * @brief configurationMap returns the contents of the configuration file for + * this module instance. + * @return the instance's configuration, already parsed from YAML into a variant map. + */ + QVariantMap configurationMap(); + + /** + * @brief typeString returns a user-visible string for the module's type. + * @return the type string. + */ + QString typeString() const; + + /** + * @brief interface returns a user-visible string for the module's interface. + * @return the interface string. + */ + QString interfaceString() const; + + /** + * @brief loadSelf initialized the module. + * Subclasses must reimplement this depending on the module type and interface. + */ + virtual void loadSelf() = 0; + + /** + * @brief jobs returns any jobs exposed by this module. + * @return a list of jobs (can be empty). + */ + virtual JobList jobs() const = 0; + + /** + * @brief type returns the Type of this module object. + * @return the type enum value. + */ + virtual Type type() const = 0; + + /** + * @brief interface the Interface used by this module. + * @return the interface enum value. + */ + virtual Interface interface() const = 0; + + /** + * @brief Check the requirements of this module. + */ + virtual RequirementsList checkRequirements(); + +protected: + explicit Module(); + + /// @brief For subclasses to read their part of the descriptor + virtual void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) = 0; + /// @brief Generic part of descriptor reading (and instance id) + void initFrom( const ModuleSystem::Descriptor& moduleDescriptor, const QString& id ); + + QVariantMap m_configurationMap; + + bool m_loaded = false; + bool m_emergency = false; // Based on module and local config + bool m_maybe_emergency = false; // Based on the module.desc + +private: + void loadConfigurationFile( const QString& configFileName ); //throws YAML::Exception + + QString m_directory; + ModuleSystem::InstanceKey m_key; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +}; + +} // namespace Calamares + +#endif // CALAMARES_MODULE_H diff --git a/airootfs/usr/include/libcalamares/modulesystem/ModuleFactory.h b/airootfs/usr/include/libcalamares/modulesystem/ModuleFactory.h new file mode 100644 index 0000000..170e27e --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/ModuleFactory.h @@ -0,0 +1,38 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_MODULEFACTORY_H +#define CALAMARES_MODULEFACTORY_H + +#include "DllMacro.h" + +#include "modulesystem/Descriptor.h" +#include "modulesystem/Module.h" + +#include + +namespace Calamares +{ + +/** + * @brief fromDescriptor creates a new Module object of the correct type. + * @param moduleDescriptor a module descriptor, already parsed into a variant map. + * @param instanceId the instance id of the new module instance. + * @param configFileName the name of the configuration file to read. + * @param moduleDirectory the path to the directory with this module's files. + * @return a pointer to an object of a subtype of Module. + */ +UIDLLEXPORT Module* moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +} // namespace Calamares + +#endif // CALAMARES_MODULEFACTORY_H diff --git a/airootfs/usr/include/libcalamares/modulesystem/ModuleManager.h b/airootfs/usr/include/libcalamares/modulesystem/ModuleManager.h new file mode 100644 index 0000000..015f66e --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/ModuleManager.h @@ -0,0 +1,177 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef MODULELOADER_H +#define MODULELOADER_H + +#include "DllMacro.h" + +#include "modulesystem/Descriptor.h" +#include "modulesystem/InstanceKey.h" +#include "modulesystem/Requirement.h" + +#include +#include +#include + +namespace Calamares +{ + +class Module; +class RequirementsModel; + +/** + * @brief The ModuleManager class is a singleton which manages Calamares modules. + * + * It goes through the module search directories and reads module metadata. It then + * constructs objects of type Module, loads them and makes them accessible by their + * instance key. + */ +class UIDLLEXPORT ModuleManager : public QObject +{ + Q_OBJECT +public: + explicit ModuleManager( const QStringList& paths, QObject* parent = nullptr ); + ~ModuleManager() override; + + static ModuleManager* instance(); + + /** + * @brief init goes through the module search directories and gets a list of + * modules available for loading, along with their metadata. + * This information is stored as a map of Module* objects, indexed by name. + */ + void init(); + + /** + * @brief loadedInstanceKeys returns a list of instance keys for the available + * modules. + * @return a QStringList with the instance keys. + */ + QList< ModuleSystem::InstanceKey > loadedInstanceKeys(); + + /** + * @brief moduleDescriptor returns the module descriptor structure for a given module. + * @param name the name of the module for which to return the module descriptor. + * @return the module descriptor, as a variant map already parsed from YAML. + */ + ModuleSystem::Descriptor moduleDescriptor( const QString& name ); + + /** @brief returns the module descriptor structure for the module @p instance + * + * Descriptors are for the module, which may have multiple instances; + * this is the same as moduleDescriptor( instance.module() ). + */ + ModuleSystem::Descriptor moduleDescriptor( const ModuleSystem::InstanceKey& instanceKey ) + { + return moduleDescriptor( instanceKey.module() ); + } + + /** + * @brief moduleInstance returns a Module object for a given instance key. + * @param instanceKey the instance key for a module instance. + * @return a pointer to an object of a subtype of Module. + */ + Module* moduleInstance( const ModuleSystem::InstanceKey& instanceKey ); + + /** + * @brief loadModules does all of the module loading operation. + * When this is done, the signal modulesLoaded is emitted. + * It is recommended to call this from a single-shot QTimer. + */ + void loadModules(); + + /** + * @brief Adds a single module (loaded by some other means) + * + * Returns @c true on success (that is, the module's dependencies + * are satisfied, it wasn't already loaded, ...). + */ + bool addModule( Module* ); + + /** + * @brief Starts asynchronous requirements checking for each module. + * When this is done, the signal requirementsComplete is emitted. + */ + void checkRequirements(); + + ///@brief Gets the model that requirements-checking works on. + RequirementsModel* requirementsModel() { return m_requirementsModel; } + +signals: + /** @brief Emitted when all the module **configuration** has been read + * + * This indicates that all of the module.desc files have been + * loaded; bad ones are silently skipped, so this just indicates + * that the module manager is ready for the next phase (loading). + */ + void initDone(); + /** @brief Emitted when all the modules are loaded successfully + * + * Each module listed in the settings is loaded. Modules are loaded + * only once, even when instantiated multiple times. If all of + * the listed modules are successfully loaded, this signal is + * emitted (otherwise, it isn't, so you need to wait for **both** + * of the signals). + * + * If this is emitted (i.e. all modules have loaded) then the next + * phase, requirements checking, can be started. + */ + void modulesLoaded(); + /** @brief Emitted if any modules failed to load + * + * Modules that failed to load (for any reason) are listed by + * instance key (e.g. "welcome@welcome", "shellprocess@mycustomthing"). + */ + void modulesFailed( QStringList ); + /** @brief Emitted after all requirements have been checked + * + * The bool @p canContinue indicates if all of the **mandatory** requirements + * are satisfied (e.g. whether installation can continue). + */ + void requirementsComplete( bool canContinue ); + +private slots: + void doInit(); + +private: + /** + * Check in a general sense whether the dependencies between + * modules are valid. Returns the number of modules that + * have missing dependencies -- this is **not** a problem + * unless any of those modules are actually used. + * + * Returns 0 on success. + * + * Also modifies m_availableDescriptorsByModuleName to remove + * all the entries that (so that later, when we try to look + * them up, they are not found). + */ + size_t checkDependencies(); + + /** + * Check for this specific module if its required modules have + * already been loaded (i.e. are in sequence before it). + * + * Returns true if the requirements are met. + */ + bool checkModuleDependencies( const Module& ); + + QMap< QString, ModuleSystem::Descriptor > m_availableDescriptorsByModuleName; + QMap< ModuleSystem::InstanceKey, Module* > m_loadedModulesByInstanceKey; + const QStringList m_paths; + RequirementsModel* m_requirementsModel; + + static ModuleManager* s_instance; +}; + +} // namespace Calamares + +#endif // MODULELOADER_H diff --git a/airootfs/usr/include/libcalamares/modulesystem/Preset.h b/airootfs/usr/include/libcalamares/modulesystem/Preset.h new file mode 100644 index 0000000..b768a31 --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/Preset.h @@ -0,0 +1,91 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_MODULESYSTEM_PRESET_H +#define CALAMARES_MODULESYSTEM_PRESET_H + +#include +#include +#include + +namespace Calamares +{ +namespace ModuleSystem +{ +/** @brief The settings for a single field + * + * The settings apply to a single field; **often** this will + * correspond to a single value or property of a Config + * object, but there is no guarantee of a correspondence + * between names here and names in the code. + * + * The value is stored as a string; consumers (e.g. the UI) + * will need to translate the value to whatever is actually + * used (e.g. in the case of an integer field). + * + * By default, presets are still editable. Set that to @c false + * to make the field unchangeable (again, the UI is responsible + * for setting that up). + */ +struct PresetField +{ + QString fieldName; + QVariant value; + bool editable = true; + + bool isValid() const { return !fieldName.isEmpty(); } +}; + +/** @brief All the presets for one UI entity + * + * This is a collection of presets read from a module + * configuration file, one setting per field. + */ +class Presets : public QVector< PresetField > +{ +public: + /** @brief Reads preset entries from the map + * + * The map's keys are used as field name, and each value entry + * should specify an initial value and whether the entry is editable. + * Fields are editable by default. + */ + explicit Presets( const QVariantMap& configurationMap ); + /** @brief Reads preset entries from the @p configurationMap + * + * As above, but only field names that occur in @p recognizedKeys + * are kept; others are discarded. + */ + Presets( const QVariantMap& configurationMap, const QStringList& recognizedKeys ); + + /** @brief Creates an empty presets map + * + * This constructor is primarily intended for use by the ApplyPresets + * helper class, which will reserve suitable space and load + * presets on-demand. + */ + Presets() = default; + + /** @brief Is the given @p fieldName editable? + * + * Fields are editable by default, so if there is no explicit setting, + * returns @c true. + */ + bool isEditable( const QString& fieldName ) const; + + /** @brief Finds the settings for a field @p fieldName + * + * If there is no such field, returns an invalid PresetField. + */ + PresetField find( const QString& fieldName ) const; +}; +} // namespace ModuleSystem +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/ProcessJobModule.h b/airootfs/usr/include/libcalamares/modulesystem/ProcessJobModule.h new file mode 100644 index 0000000..645127d --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/ProcessJobModule.h @@ -0,0 +1,52 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_PROCESSJOBMODULE_H +#define CALAMARES_PROCESSJOBMODULE_H + +#include "DllMacro.h" +#include "modulesystem/Module.h" + +#include + +namespace Calamares +{ + +class UIDLLEXPORT ProcessJobModule : public Module +{ +public: + Type type() const override; + Interface interface() const override; + + void loadSelf() override; + JobList jobs() const override; + +protected: + void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override; + +private: + explicit ProcessJobModule(); + ~ProcessJobModule() override; + + QString m_command; + QString m_workingPath; + std::chrono::seconds m_secondsTimeout; + bool m_runInChroot; + job_ptr m_job; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +}; + +} // namespace Calamares + +#endif // CALAMARES_PROCESSJOBMODULE_H diff --git a/airootfs/usr/include/libcalamares/modulesystem/PythonJobModule.h b/airootfs/usr/include/libcalamares/modulesystem/PythonJobModule.h new file mode 100644 index 0000000..4424cc7 --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/PythonJobModule.h @@ -0,0 +1,47 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_PYTHONJOBMODULE_H +#define CALAMARES_PYTHONJOBMODULE_H + +#include "DllMacro.h" +#include "modulesystem/Module.h" + +namespace Calamares +{ + +class UIDLLEXPORT PythonJobModule : public Module +{ +public: + Type type() const override; + Interface interface() const override; + + void loadSelf() override; + JobList jobs() const override; + +protected: + void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override; + +private: + explicit PythonJobModule(); + ~PythonJobModule() override; + + QString m_scriptFileName; + QString m_workingPath; + job_ptr m_job; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +}; + +} // namespace Calamares + +#endif // CALAMARES_PYTHONJOBMODULE_H diff --git a/airootfs/usr/include/libcalamares/modulesystem/Requirement.h b/airootfs/usr/include/libcalamares/modulesystem/Requirement.h new file mode 100644 index 0000000..dc9fe1d --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/Requirement.h @@ -0,0 +1,61 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_REQUIREMENT_H +#define CALAMARES_REQUIREMENT_H + +#include "DllMacro.h" + +#include +#include +#include + +#include + +namespace Calamares +{ + +/** + * An indication of a requirement, which is checked in preparation + * for system installation. An entry has a name and some explanation functions + * (functions, because they need to respond to translations). + * + * A requirement can be *satisfied* or not. + * A requirement can be optional (i.e. a "good to have") or mandatory. + * + * Requirements which are not satisfied, and also mandatory, will prevent the + * installation from proceeding. + */ +struct RequirementEntry +{ + using TextFunction = std::function< QString() >; + + /// @brief name of this requirement; not shown to user and used as ID + QString name; + + /// @brief Detailed description of this requirement, for use in user-visible lists + TextFunction enumerationText; + + /// @brief User-visible string to show that the requirement is not met, short form + TextFunction negatedText; + + bool satisfied; + bool mandatory; + + /// @brief Convenience to check if this entry should be shown in details dialog + bool hasDetails() const { return !enumerationText().isEmpty(); } +}; + +using RequirementsList = QList< RequirementEntry >; + +} // namespace Calamares + +Q_DECLARE_METATYPE( Calamares::RequirementEntry ) + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/RequirementsChecker.h b/airootfs/usr/include/libcalamares/modulesystem/RequirementsChecker.h new file mode 100644 index 0000000..befa32b --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/RequirementsChecker.h @@ -0,0 +1,73 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef CALAMARES_REQUIREMENTSCHECKER_H +#define CALAMARES_REQUIREMENTSCHECKER_H + +#include "DllMacro.h" +#include "modulesystem/Requirement.h" + +#include +#include +#include +#include + +namespace Calamares +{ + +class Module; +class RequirementsModel; + +/** @brief A manager-class that checks all the module requirements + * + * Asynchronously checks the requirements for each module, and + * emits progress signals as appropriate. + */ +class DLLEXPORT RequirementsChecker : public QObject +{ + Q_OBJECT + +public: + RequirementsChecker( QVector< Module* > modules, RequirementsModel* model, QObject* parent = nullptr ); + ~RequirementsChecker() override; + +public Q_SLOTS: + /// @brief Start checking all the requirements + void run(); + + /// @brief Called when requirements are reported by a module + void addCheckedRequirements( Module* ); + + /// @brief Called when all requirements have been checked + void finished(); + + /// @brief Called periodically while requirements are being checked + void reportProgress(); + +signals: + /// @brief Human-readable progress message + void requirementsProgress( const QString& ); + /// @brief Emitted after requirementsComplete + void done(); + +private: + QVector< Module* > m_modules; + + using Watcher = QFutureWatcher< void >; + QVector< Watcher* > m_watchers; + + RequirementsModel* m_model; + + QTimer* m_progressTimer; + unsigned m_progressTimeouts; +}; + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/RequirementsModel.h b/airootfs/usr/include/libcalamares/modulesystem/RequirementsModel.h new file mode 100644 index 0000000..e6cd068 --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/RequirementsModel.h @@ -0,0 +1,100 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef CALAMARES_REQUIREMENTSMODEL_H +#define CALAMARES_REQUIREMENTSMODEL_H + +#include "Requirement.h" + +#include "DllMacro.h" + +#include +#include + +namespace Calamares +{ +class RequirementsChecker; + +/** @brief System requirements from each module and their checked-status + * + * A Calamares module can have system requirements (e.g. check for + * internet, or amount of RAM, or an available disk) which can + * be stated and checked. + * + * This model collects those requirements, can run the checks, and + * reports on the overall status of those checks. + */ +class DLLEXPORT RequirementsModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY( bool satisfiedRequirements READ satisfiedRequirements NOTIFY satisfiedRequirementsChanged FINAL ) + Q_PROPERTY( bool satisfiedMandatory READ satisfiedMandatory NOTIFY satisfiedMandatoryChanged FINAL ) + Q_PROPERTY( QString progressMessage READ progressMessage NOTIFY progressMessageChanged FINAL ) + +public: + using QAbstractListModel::QAbstractListModel; + + enum Roles : short + { + NegatedText = Qt::DisplayRole, + Details = Qt::ToolTipRole, + Name = Qt::UserRole, + Satisfied, + Mandatory, + HasDetails + }; + // No Q_ENUM because these are exposed through roleNames() + + ///@brief Are all the requirements satisfied? + bool satisfiedRequirements() const { return m_satisfiedRequirements; } + ///@brief Are all the **mandatory** requirements satisfied? + bool satisfiedMandatory() const { return m_satisfiedMandatory; } + ///@brief Message (from an ongoing check) about progress + QString progressMessage() const { return m_progressMessage; } + + + QVariant data( const QModelIndex& index, int role ) const override; + int rowCount( const QModelIndex& ) const override; // TODO 3.4 use qsizetype + int count() const { return static_cast< int >( m_requirements.count() ); } // TODO 3.4 use qsizetype + + ///@brief Debugging tool, describe the checking-state + void describe() const; + + ///@brief Update progress message (called by the checker) + void setProgressMessage( const QString& m ); + + ///@brief Append some requirements; resets the model + void addRequirementsList( const Calamares::RequirementsList& requirements ); + + ///@brief Check the whole list, emit signals satisfied...() + void reCheckList(); + +signals: + void satisfiedRequirementsChanged( bool value ); + void satisfiedMandatoryChanged( bool value ); + void progressMessageChanged( QString message ); + +protected: + QHash< int, QByteArray > roleNames() const override; + + ///@brief Clears the requirements; resets the model + void clear(); + +private: + QString m_progressMessage; + QMutex m_addLock; + RequirementsList m_requirements; + bool m_satisfiedRequirements = false; + bool m_satisfiedMandatory = false; +}; + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/modulesystem/ViewModule.h b/airootfs/usr/include/libcalamares/modulesystem/ViewModule.h new file mode 100644 index 0000000..217611b --- /dev/null +++ b/airootfs/usr/include/libcalamares/modulesystem/ViewModule.h @@ -0,0 +1,53 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARES_VIEWMODULE_H +#define CALAMARES_VIEWMODULE_H + +#include "DllMacro.h" +#include "modulesystem/Module.h" + +class QPluginLoader; + +namespace Calamares +{ + +class ViewStep; + +class UIDLLEXPORT ViewModule : public Module +{ +public: + Type type() const override; + Interface interface() const override; + + void loadSelf() override; + JobList jobs() const override; + + RequirementsList checkRequirements() override; + +protected: + void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override; + +private: + explicit ViewModule(); + ~ViewModule() override; + + QPluginLoader* m_loader; + ViewStep* m_viewStep = nullptr; + + friend Module* Calamares::moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor, + const QString& instanceId, + const QString& configFileName, + const QString& moduleDirectory ); +}; + +} // namespace Calamares + +#endif // CALAMARES_VIEWMODULE_H diff --git a/airootfs/usr/include/libcalamares/network/Manager.h b/airootfs/usr/include/libcalamares/network/Manager.h new file mode 100644 index 0000000..7520388 --- /dev/null +++ b/airootfs/usr/include/libcalamares/network/Manager.h @@ -0,0 +1,166 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARES_NETWORK_MANAGER_H +#define LIBCALAMARES_NETWORK_MANAGER_H + +#include "DllMacro.h" + +#include +#include +#include +#include +#include + +#include +#include + +class QNetworkReply; +class QNetworkRequest; + +namespace Calamares +{ +namespace Network +{ +class DLLEXPORT RequestOptions +{ +public: + using milliseconds = std::chrono::milliseconds; + + enum Flag + { + FollowRedirect = 0x1, + FakeUserAgent = 0x100 + }; + Q_DECLARE_FLAGS( Flags, Flag ) + + RequestOptions() + : m_flags( Flags() ) + , m_timeout( -1 ) + { + } + + RequestOptions( Flags f, milliseconds timeout = milliseconds( -1 ) ) + : m_flags( f ) + , m_timeout( timeout ) + { + } + + void applyToRequest( QNetworkRequest* ) const; + + bool hasTimeout() const { return m_timeout > milliseconds( 0 ); } + auto timeout() const { return m_timeout; } + +private: + Flags m_flags; + milliseconds m_timeout; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( RequestOptions::Flags ); + +struct RequestStatus +{ + enum State + { + Ok, + Timeout, // Timeout exceeded + Failed, // bad Url + HttpError, // some other HTTP error (eg. SSL failed) + Empty // for ping(), response is empty + }; + + RequestStatus( State s = Ok ) + : status( s ) + { + } + operator bool() const { return status == Ok; } + + State status; +}; + +DLLEXPORT QDebug& operator<<( QDebug& s, const RequestStatus& e ); + +class DLLEXPORT Manager : public QObject +{ + Q_OBJECT + Q_PROPERTY( bool hasInternet READ hasInternet NOTIFY hasInternetChanged FINAL ) + Q_PROPERTY( QVector< QUrl > checkInternetUrls READ getCheckInternetUrls WRITE setCheckHasInternetUrl ) + +public: + Manager(); + ~Manager() override; + + /** @brief Checks if the given @p url returns data. + * + * Returns a RequestStatus, which converts to @c true if the ping + * was successful. Other status reasons convert to @c false, + * typically because of no data, a Url error or no network access. + * + * May return Empty if the request was successful but returned + * no data at all. + */ + RequestStatus synchronousPing( const QUrl& url, const RequestOptions& options = RequestOptions() ); + + /** @brief Downloads the data from a given @p url + * + * Returns the data as a QByteArray, or an empty + * array if any error occurred (or no data was returned). + */ + QByteArray synchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() ); + + /** @brief Do a network request asynchronously. + * + * Returns a pointer to the reply-from-the-request. + * This may be a nullptr if an error occurs immediately. + * The caller is responsible for cleaning up the reply (eventually). + */ + QNetworkReply* asynchronousGet( const QUrl& url, const RequestOptions& options = RequestOptions() ); + + /// @brief Set the URL which is used for the general "is there internet" check. + static void setCheckHasInternetUrl( const QUrl& url ); + + /// @brief Adds an (extra) URL to check + static void addCheckHasInternetUrl( const QUrl& url ); + + /// @brief Set a collection of URLs used for the general "is there internet" check. + static void setCheckHasInternetUrl( const QVector< QUrl >& urls ); + + /// @brief What URLs are used to check for internet connectivity? + static QVector< QUrl > getCheckInternetUrls(); + +public Q_SLOTS: + /** @brief Do an explicit check for internet connectivity. + * + * This **may** do a ping to the configured check URL, but can also + * use other mechanisms. + */ + bool checkHasInternet(); + /** @brief Is there internet connectivity? + * + * This returns the result of the last explicit check, or if there + * is other information about the state of the internet connection, + * whatever is known. @c true means you can expect (all) internet + * connectivity to be present. + */ + bool hasInternet(); + +signals: + /** @brief Indicates that internet connectivity status has changed + * + * The value is that returned from hasInternet() -- @c true when there + * is connectivity, @c false otherwise. + */ + void hasInternetChanged( bool ); + +private: + class Private; +}; +} // namespace Network +} // namespace Calamares +#endif // LIBCALAMARES_NETWORK_MANAGER_H diff --git a/airootfs/usr/include/libcalamares/network/Tests.h b/airootfs/usr/include/libcalamares/network/Tests.h new file mode 100644 index 0000000..d72da57 --- /dev/null +++ b/airootfs/usr/include/libcalamares/network/Tests.h @@ -0,0 +1,32 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARES_NETWORK_TESTS_H +#define LIBCALAMARES_NETWORK_TESTS_H + +#include + +class NetworkTests : public QObject +{ + Q_OBJECT +public: + NetworkTests(); + ~NetworkTests() override; + +private Q_SLOTS: + void initTestCase(); + + void testInstance(); + void testPing(); + + void testCheckUrl(); + void testCheckMultiUrl(); +}; + +#endif diff --git a/airootfs/usr/include/libcalamares/packages/Globals.h b/airootfs/usr/include/libcalamares/packages/Globals.h new file mode 100644 index 0000000..ad8d15e --- /dev/null +++ b/airootfs/usr/include/libcalamares/packages/Globals.h @@ -0,0 +1,45 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARES_PACKAGES_GLOBALS_H +#define LIBCALAMARES_PACKAGES_GLOBALS_H + +#include "DllMacro.h" +#include "GlobalStorage.h" +#include "modulesystem/InstanceKey.h" + +namespace Calamares +{ +namespace Packages +{ +/** @brief Sets the install-packages GS keys for the given module + * + * This replaces previously-set install-packages lists for the + * given module by the two new lists. + * + * Returns @c true if anything was changed, @c false otherwise. + */ +DLLEXPORT bool setGSPackageAdditions( Calamares::GlobalStorage* gs, + const Calamares::ModuleSystem::InstanceKey& module, + const QVariantList& installPackages, + const QVariantList& tryInstallPackages ); +/** @brief Sets the install-packages GS keys for the given module + * + * This replaces previously-set install-packages lists. Use this with + * plain lists of package names. It does not support try-install. + */ +DLLEXPORT bool setGSPackageAdditions( Calamares::GlobalStorage* gs, + const Calamares::ModuleSystem::InstanceKey& module, + const QStringList& installPackages ); +// void setGSPackageRemovals( const Calamares::ModuleSystem::InstanceKey& key, const QVariantList& removePackages ); +} // namespace Packages +} // namespace Calamares + + +#endif diff --git a/airootfs/usr/include/libcalamares/partition/AutoMount.h b/airootfs/usr/include/libcalamares/partition/AutoMount.h new file mode 100644 index 0000000..de8bc4c --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/AutoMount.h @@ -0,0 +1,51 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef PARTITION_AUTOMOUNT_H +#define PARTITION_AUTOMOUNT_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ +namespace Partition +{ + +struct AutoMountInfo; + +/** @brief Disable automount + * + * Various subsystems can do "agressive automount", which can get in the + * way of partitioning actions. In particular, Solid can be configured + * to automount every device it sees, and partitioning happens in multiple + * steps (create table, create partition, set partition flags) which are + * blocked if the partition gets mounted partway through the operation. + * + * @param disable set this to false to reverse the sense of the function + * call and force *enabling* automount, instead. + * + * Returns an opaque structure which can be passed to automountRestore() + * to return the system to the previously-configured automount settings. + */ +DLLEXPORT std::shared_ptr< AutoMountInfo > automountDisable( bool disable = true ); + +/** @brief Restore automount settings + * + * Pass the value returned from automountDisable() to restore the + * previous settings. + */ +DLLEXPORT void automountRestore( const std::shared_ptr< AutoMountInfo >& t ); + +} // namespace Partition +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/partition/FileSystem.h b/airootfs/usr/include/libcalamares/partition/FileSystem.h new file mode 100644 index 0000000..36291e7 --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/FileSystem.h @@ -0,0 +1,100 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Aurélien Gâteau + * SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/* + * NOTE: this functionality is only available when Calamares is compiled + * with KPMcore support. + */ + +#ifndef PARTITION_FILESYSTEM_H +#define PARTITION_FILESYSTEM_H + +#include "DllMacro.h" +#include "partition/Global.h" + +#include + +namespace Calamares +{ +namespace Partition +{ +QString DLLEXPORT prettyNameForFileSystemType( FileSystem::Type t ); + +/** @brief Returns a machine-readable identifier for the filesystem type + * + * This identifier is used in filesystem manipulation -- + * e.g. when mounting the filesystem, or in /etc/fstab. It + * is almost always just what KPMCore says it is, with + * the following exceptions: + * - reiserfs is called "reiser" by KPMCore, "reiserfs" by Calamares + */ +QString DLLEXPORT untranslatedFS( FileSystem::Type t ); + +/** @brief Returns the machine-readable identifier for the given @p fs + * + * See notes for untranslatedFS(), above. + */ +static inline QString +untranslatedFS( FileSystem& fs ) +{ + return untranslatedFS( fs.type() ); +} + +/** @brief Returns a machine-readable identifier for the given @p fs + * + * Returns an empty string is the @p fs is not valid (e.g. nullptr). + */ +static inline QString +untranslatedFS( FileSystem* fs ) +{ + return fs ? untranslatedFS( *fs ) : QString(); +} + +static inline QString +userVisibleFS( FileSystem& fs ) +{ + return fs.name(); +} + +static inline QString +userVisibleFS( FileSystem* fs ) +{ + return fs ? userVisibleFS( *fs ) : QString(); +} + +/** @brief Mark a particular filesystem type as used (or not) + * + * See useFilesystemGS(const QString&, bool); this method uses the filesystem type + * enumeration to pick the name. (The other implementation is in `Global.h` + * because it touches Global Storage, but this one needs KPMcore) + */ +inline void +useFilesystemGS( FileSystem::Type filesystem, bool used ) +{ + useFilesystemGS( untranslatedFS( filesystem ), used ); +} + +/* @brief Reads from global storage whether the typesystem type is used + * + * See isFilesystemUsedGS(const QString&). (The other implementation is in `Global.h` + * because it touches Global Storage, but this one needs KPMcore) + */ +inline bool +isFilesystemUsedGS( FileSystem::Type filesystem ) +{ + return isFilesystemUsedGS( untranslatedFS( filesystem ) ); +} + +} // namespace Partition +} // namespace Calamares + +#endif // PARTITION_PARTITIONQUERY_H diff --git a/airootfs/usr/include/libcalamares/partition/Global.h b/airootfs/usr/include/libcalamares/partition/Global.h new file mode 100644 index 0000000..cef1ecb --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/Global.h @@ -0,0 +1,78 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/* + * This is the API for manipulating Global Storage keys related to + * filesystems and partitions. This does **not** depend on KPMcore. + */ + +#ifndef PARTITION_GLOBAL_H +#define PARTITION_GLOBAL_H + +#include "DllMacro.h" +#include "JobQueue.h" + +namespace Calamares +{ +namespace Partition +{ +/** @brief Mark a particular filesystem type as used (or not) + * + * Filesystems are marked used (or not) in the global storage + * key *filesystem_use*. Sub-keys are the filesystem name, + * and the values are boolean; filesystems that are used in + * the target system are marked with @c true. Unused filesystems + * may be unmarked, or may be marked @c false. + * + * The filesystem name should be the untranslated name. Filesystem + * names are **lower**cased when used as keys. + */ +void DLLEXPORT useFilesystemGS( Calamares::GlobalStorage* gs, const QString& filesystemType, bool used ); +/** @brief Reads from global storage whether the filesystem type is used + * + * Reads from the global storage key *filesystem_use* and returns + * the boolean value stored in subkey @p filesystemType. Returns + * @c false if the subkey is not set at all. + * + * The filesystem name should be the untranslated name. Filesystem + * names are **lower**cased when used as keys. + */ +bool DLLEXPORT isFilesystemUsedGS( const Calamares::GlobalStorage* gs, const QString& filesystemType ); + +/** @brief Clears the usage data for filesystems + * + * This removes the internal key *filesystem_use*. + */ +void DLLEXPORT clearFilesystemGS( Calamares::GlobalStorage* gs ); + +/** @brief Convenience function for using "the" Global Storage + * + * @see useFilesystemGS(const QString&, bool) + */ +inline void +useFilesystemGS( const QString& filesystemType, bool used ) +{ + useFilesystemGS( Calamares::JobQueue::instanceGlobalStorage(), filesystemType, used ); +} + +/** @brief Convenience function for using "the" Global Storage + * + * @see isFilesystemUsedGS(const QString&); + */ +inline bool +isFilesystemUsedGS( const QString& filesystemType ) +{ + return isFilesystemUsedGS( Calamares::JobQueue::instanceGlobalStorage(), filesystemType ); +} + +} // namespace Partition +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/partition/KPMHelper.h b/airootfs/usr/include/libcalamares/partition/KPMHelper.h new file mode 100644 index 0000000..0a4aaee --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/KPMHelper.h @@ -0,0 +1,43 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* + * KPMCore header file inclusion. + * + * Includes the system KPMCore headers without warnings (by switching off + * the expected warnings). + */ +#ifndef PARTITION_KPMHELPER_H +#define PARTITION_KPMHELPER_H + +#include + +// The kpmcore headers are not C++17 warning-proof, especially +// with picky compilers like Clang 10. Since we use Clang for the +// find-all-the-warnings case, switch those warnings off for +// the we-can't-change-them system headers. +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG( "-Wdocumentation" ) +QT_WARNING_DISABLE_CLANG( "-Wsuggest-destructor-override" ) +QT_WARNING_DISABLE_CLANG( "-Winconsistent-missing-destructor-override" ) +// Because of __lastType +QT_WARNING_DISABLE_CLANG( "-Wreserved-identifier" ) + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_WARNING_POP + +#endif diff --git a/airootfs/usr/include/libcalamares/partition/KPMManager.h b/airootfs/usr/include/libcalamares/partition/KPMManager.h new file mode 100644 index 0000000..45a403b --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/KPMManager.h @@ -0,0 +1,63 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/* + * NOTE: this functionality is only available when Calamares is compiled + * with KPMcore support. + */ + +#ifndef PARTITION_KPMMANAGER_H +#define PARTITION_KPMMANAGER_H + +#include "DllMacro.h" + +#include + +class CoreBackend; + +namespace Calamares +{ +namespace Partition +{ +/// @brief Handle to KPMCore +class InternalManager; + +/** @brief KPMCore loader and cleanup + * + * A Calamares plugin that uses KPMCore should hold an object of + * this class; its only responsibility is to load KPMCore + * and to cleanly unload it on destruction (with KPMCore 4, + * also to shutdown the privileged helper application). + * + * It loads the default plugin ("parted" with KPMCore 3, "sfdisk" + * with KPMCore 4), but this can be overridden by setting the + * environment variable KPMCORE_BACKEND. Setting it to + * "dummy" will load the dummy plugin instead. + */ +class DLLEXPORT KPMManager +{ +public: + KPMManager(); + ~KPMManager(); + + /// @brief Is KPMCore loaded correctly? + operator bool() const; + + /// @brief Gets the KPMCore backend (e.g. CoreBackendManager::self()->backend() ) + CoreBackend* backend() const; + +private: + std::shared_ptr< InternalManager > m_d; +}; + +} // namespace Partition +} // namespace Calamares + +#endif // PARTITION_KPMMANAGER_H diff --git a/airootfs/usr/include/libcalamares/partition/Mount.h b/airootfs/usr/include/libcalamares/partition/Mount.h new file mode 100644 index 0000000..3781c4f --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/Mount.h @@ -0,0 +1,112 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef PARTITION_MOUNT_H +#define PARTITION_MOUNT_H + +#include "DllMacro.h" + +#include +#include +#include + +#include + +namespace Calamares +{ +namespace Partition +{ + +/** + * Runs the mount utility with the specified parameters. + * @param devicePath the path of the partition to mount. + * @param mountPoint the full path of the target mount point. + * @param filesystemName the name of the filesystem (optional). + * @param options any additional options as passed to mount -o (optional). + * If @p options starts with a dash (-) then it is passed unchanged + * and no -o option is added; this is used in handling --bind mounts. + * @returns the program's exit code, or: + * Crashed = QProcess crash + * FailedToStart = QProcess cannot start + * NoWorkingDirectory = bad arguments + */ +DLLEXPORT int mount( const QString& devicePath, + const QString& mountPoint, + const QString& filesystemName = QString(), + const QString& options = QString() ); + +/** @brief Unmount the given @p path (device or mount point). + * + * Runs umount(8) in the host system. + * + * @returns the program's exit code, or special codes like mount(). + */ +DLLEXPORT int unmount( const QString& path, const QStringList& options = QStringList() ); + + +/** @brief Mount and automatically unmount a device + * + * The TemporaryMount object mounts a filesystem, and is like calling + * the mount() function, above. When the object is destroyed, unmount() + * is called with suitable options to undo the original mount. + */ +class DLLEXPORT TemporaryMount +{ +public: + TemporaryMount( const QString& devicePath, + const QString& filesystemName = QString(), + const QString& options = QString() ); + TemporaryMount( const TemporaryMount& ) = delete; + TemporaryMount& operator=( const TemporaryMount& ) = delete; + ~TemporaryMount(); + + bool isValid() const { return bool( m_d ); } + QString path() const; + +private: + struct Private; + std::unique_ptr< Private > m_d; +}; + + +/** @brief Information about a mount point from /etc/mtab + * + * Entries in /etc/mtab are of the form: + * This struct only stores device and mountpoint. + * + * The main way of getting these structs is to call fromMtab() to read + * an /etc/mtab-like file and storing all of the entries from it. + */ +struct DLLEXPORT MtabInfo +{ + QString device; + QString mountPoint; + + /** @brief Reads an mtab-like file and returns the entries from it + * + * When @p mtabPath is given, that file is read. If the given name is + * empty (e.g. the default) then /etc/mtab is read, instead. + * + * If @p mountPrefix is given, then only entries that have a mount point + * that starts with that prefix are returned. + */ + static QList< MtabInfo > fromMtabFilteredByPrefix( const QString& mountPrefix = QString(), + const QString& mtabPath = QString() ); + /// @brief Predicate to sort MtabInfo objects by device-name + static bool deviceOrder( const MtabInfo& a, const MtabInfo& b ) { return a.device > b.device; } + /// @brief Predicate to sort MtabInfo objects by mount-point + static bool mountPointOrder( const MtabInfo& a, const MtabInfo& b ) { return a.mountPoint > b.mountPoint; } +}; + +} // namespace Partition +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/partition/PartitionIterator.h b/airootfs/usr/include/libcalamares/partition/PartitionIterator.h new file mode 100644 index 0000000..b447ca0 --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/PartitionIterator.h @@ -0,0 +1,69 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Aurélien Gâteau + * SPDX-FileCopyrightText: 2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + + +/* + * NOTE: this functionality is only available when Calamares is compiled + * with KPMcore support. + */ + +#ifndef PARTITION_PARTITIONITERATOR_H +#define PARTITION_PARTITIONITERATOR_H + +#include "DllMacro.h" + +class Device; +class Partition; +class PartitionTable; + +namespace Calamares +{ +namespace Partition +{ + +/** @brief Iterator over KPMCore partitions + * + * A forward-only iterator to go through the partitions of a device, + * independently of whether they are primary, logical or extended. + * + * An iterator can be created from a device (then it refers to the + * partition table of that device) or a partition table. The + * partition table must remain valid throughout iteration. + * + * A nullptr is valid, for an empty iterator. + */ +class DLLEXPORT PartitionIterator +{ +public: + ::Partition* operator*() const; + + void operator++(); + + bool operator==( const PartitionIterator& other ) const; + bool operator!=( const PartitionIterator& other ) const; + + static PartitionIterator begin( ::Device* device ); + static PartitionIterator begin( ::PartitionTable* table ); + static PartitionIterator end( ::Device* device ); + static PartitionIterator end( ::PartitionTable* table ); + +private: + PartitionIterator( ::PartitionTable* table ); + + ::PartitionTable* m_table; + ::Partition* m_current = nullptr; +}; + +} // namespace Partition +} // namespace Calamares + +#endif // PARTITION_PARTITIONITERATOR_H diff --git a/airootfs/usr/include/libcalamares/partition/PartitionQuery.h b/airootfs/usr/include/libcalamares/partition/PartitionQuery.h new file mode 100644 index 0000000..24567be --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/PartitionQuery.h @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Aurélien Gâteau + * SPDX-FileCopyrightText: 2015-2016 Teo Mrnjavac + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/* + * NOTE: this functionality is only available when Calamares is compiled + * with KPMcore support. + */ + +#ifndef PARTITION_PARTITIONQUERY_H +#define PARTITION_PARTITIONQUERY_H + +#include "DllMacro.h" + +#include + +#include + +class Device; +class Partition; +class PartitionTable; + +namespace Calamares +{ +namespace Partition +{ + +using ::Device; +using ::Partition; +using ::PartitionTable; + +/** @brief Get partition table */ +DLLEXPORT const PartitionTable* getPartitionTable( const Partition* partition ); + +/** @brief Is this a free-space area? */ +DLLEXPORT bool isPartitionFreeSpace( const Partition* ); + +/** @brief Is this partition newly-to-be-created? + * + * Returns true if the partition is planned to be created by the installer as + * opposed to already existing on the disk. + */ +DLLEXPORT bool isPartitionNew( const Partition* ); + +/** + * Iterates on all devices and return the first partition which is (already) + * mounted on @p mountPoint. + */ +DLLEXPORT Partition* findPartitionByCurrentMountPoint( const QList< Device* >& devices, const QString& mountPoint ); + +// TODO: add this distinction +// Partition* findPartitionByIntendedMountPoint( const QList< Device* >& devices, const QString& mountPoint ); + +/** + * Iterates on all devices and partitions and returns a pointer to the Partition object + * for the given path, or nullptr if a Partition for the given path cannot be found. + */ +DLLEXPORT Partition* findPartitionByPath( const QList< Device* >& devices, const QString& path ); + +/** + * Iterates on all devices and partitions and returns a list of pointers to the Partition + * objects that satisfy the conditions defined in the criterion function. + */ +DLLEXPORT QList< Partition* > findPartitions( const QList< Device* >& devices, + std::function< bool( Partition* ) > criterionFunction ); +} // namespace Partition +} // namespace Calamares + +#endif // PARTITION_PARTITIONQUERY_H diff --git a/airootfs/usr/include/libcalamares/partition/PartitionSize.h b/airootfs/usr/include/libcalamares/partition/PartitionSize.h new file mode 100644 index 0000000..f7a1aa2 --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/PartitionSize.h @@ -0,0 +1,120 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Collabora Ltd + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef PARTITION_PARTITIONSIZE_H +#define PARTITION_PARTITIONSIZE_H + +#include "DllMacro.h" +#include "utils/NamedSuffix.h" +#include "utils/Units.h" + +// Qt +#include + +namespace Calamares +{ +namespace Partition +{ + +enum class SizeUnit +{ + None, + Percent, + Byte, + KB, + KiB, + MB, + MiB, + GB, + GiB +}; + +/** @brief Partition size expressions + * + * Sizes can be specified in bytes, KiB, MiB, GiB or percent (of + * the available drive space are on). This class handles parsing + * of such strings from the config file. + */ +class DLLEXPORT PartitionSize : public NamedSuffix< SizeUnit, SizeUnit::None > +{ +public: + PartitionSize() + : NamedSuffix() + { + } + PartitionSize( int v, SizeUnit u ) + : NamedSuffix( v, u ) + { + } + PartitionSize( const QString& ); + + bool isValid() const { return ( unit() != SizeUnit::None ) && ( value() > 0 ); } + + bool operator<( const PartitionSize& other ) const; + bool operator>( const PartitionSize& other ) const; + bool operator==( const PartitionSize& other ) const; + + /** @brief Convert the size to the number of sectors @p totalSectors . + * + * Each sector has size @p sectorSize, for converting sizes in Bytes, + * KiB, MiB or GiB to sector counts. + * + * @return the number of sectors needed, or -1 for invalid sizes. + */ + qint64 toSectors( qint64 totalSectors, qint64 sectorSize ) const; + + /** @brief Convert the size to bytes. + * + * The device's sectors count @p totalSectors and sector size + * @p sectoreSize are used to calculated the total size, which + * is then used to calculate the size when using Percent. + * + * @return the size in bytes, or -1 for invalid sizes. + */ + qint64 toBytes( qint64 totalSectors, qint64 sectorSize ) const; + + /** @brief Convert the size to bytes. + * + * Total size @p totalBytes is needed for sizes in Percent. This + * parameter is unused in any other case. + * + * @return the size in bytes, or -1 for invalid sizes. + */ + qint64 toBytes( qint64 totalBytes ) const; + + /** @brief Convert the size to bytes. + * + * This method is only valid for sizes in Bytes, KiB, MiB or GiB. + * It will return -1 in any other case. Note that 0KiB and 0MiB and + * 0GiB are considered **invalid** sizes and return -1. + * + * @return the size in bytes, or -1 if it cannot be calculated. + */ + qint64 toBytes() const; + + /** @brief Are the units comparable? + * + * None units cannot be compared with anything. Percentages can + * be compared with each other, and all the explicit sizes (KiB, ...) + * can be compared with each other. + */ + static constexpr bool unitsComparable( const SizeUnit u1, const SizeUnit u2 ) + { + return !( ( u1 == SizeUnit::None || u2 == SizeUnit::None ) + || ( u1 == SizeUnit::Percent && u2 != SizeUnit::Percent ) + || ( u1 != SizeUnit::Percent && u2 == SizeUnit::Percent ) ); + } +}; + +} // namespace Partition +} // namespace Calamares + +#endif // PARTITION_PARTITIONSIZE_H diff --git a/airootfs/usr/include/libcalamares/partition/Sync.h b/airootfs/usr/include/libcalamares/partition/Sync.h new file mode 100644 index 0000000..c564b94 --- /dev/null +++ b/airootfs/usr/include/libcalamares/partition/Sync.h @@ -0,0 +1,40 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef PARTITION_SYNC_H +#define PARTITION_SYNC_H + +#include "DllMacro.h" + +namespace Calamares +{ +namespace Partition +{ + +/** @brief Run "udevadm settle" or other disk-sync mechanism. + * + * Call this after mounting, unmount, toggling swap, or other functions + * that might cause the disk to be "busy" for other disk-modifying + * actions (in particular, KPMcore actions with the sfdisk backend + * are sensitive, and systemd tends to keep disks busy after a change + * for a while). + */ +DLLEXPORT void sync(); + +/** @brief RAII class for calling sync() */ +struct DLLEXPORT Syncer +{ + ~Syncer() { sync(); } +}; + +} // namespace Partition +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/CommandList.h b/airootfs/usr/include/libcalamares/utils/CommandList.h new file mode 100644 index 0000000..b3d1fc4 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/CommandList.h @@ -0,0 +1,167 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_COMMANDLIST_H +#define UTILS_COMMANDLIST_H + +#include "DllMacro.h" +#include "Job.h" + +#include +#include + +#include +#include +#include + +class KMacroExpanderBase; + +namespace Calamares +{ +/** + * Each command can have an associated timeout in seconds. The timeout + * defaults to 10 seconds. Provide some convenience naming and construction. + */ +class DLLEXPORT CommandLine +{ +public: + static inline constexpr std::chrono::seconds TimeoutNotSet() { return std::chrono::seconds( -1 ); } + + /// An invalid command line + CommandLine() = default; + + CommandLine( const QString& s ) + : m_command( s ) + { + } + + CommandLine( const QString& s, std::chrono::seconds t ) + : m_command( s ) + , m_timeout( t ) + { + } + + CommandLine( const QString& s, const QStringList& env, std::chrono::seconds t ) + : m_command( s ) + , m_environment( env ) + , m_timeout( t ) + { + } + + /** @brief Constructs a CommandLine from a map with keys + * + * Relevant keys are *command*, *environment* and *timeout*. + */ + CommandLine( const QVariantMap& m ); + + QString command() const { return m_command; } + [[nodiscard]] QStringList environment() const { return m_environment; } + std::chrono::seconds timeout() const { return m_timeout; } + bool isVerbose() const { return m_verbose.value_or( false ); } + + bool isValid() const { return !m_command.isEmpty(); } + + /** @brief Returns a copy of this one command, with variables expanded + * + * The given macro-expander is used to expand the command-line. + * This will normally be a Calamares::String::DictionaryExpander + * instance, which handles the ROOT and USER variables. + */ + DLLEXPORT CommandLine expand( KMacroExpanderBase& expander ) const; + + /** @brief As above, with a default macro-expander. + * + * The default macro-expander assumes RunInHost (e.g. ROOT will + * expand to the RootMountPoint set in Global Storage). + */ + DLLEXPORT CommandLine expand() const; + + /** @brief If nothing has set verbosity yet, update to @p verbose */ + void updateVerbose( bool verbose ) + { + if ( !m_verbose.has_value() ) + { + m_verbose = verbose; + } + } + + /** @brief Unconditionally set verbosity (can also reset it to nullopt) */ + void setVerbose( std::optional< bool > v ) { m_verbose = v; } + +private: + QString m_command; + QStringList m_environment; + std::chrono::seconds m_timeout = TimeoutNotSet(); + std::optional< bool > m_verbose; +}; + +/** @brief Abbreviation, used internally. */ +using CommandList_t = QList< CommandLine >; + +/** + * A list of commands; the list may have its own default timeout + * for commands (which is then applied to each individual command + * that doesn't have one of its own). + * + * Documentation for the format of commands can be found in + * `shellprocess.conf`. + */ +class DLLEXPORT CommandList : protected CommandList_t +{ +public: + /** @brief empty command-list with timeout to apply to entries. */ + CommandList( bool doChroot = true, std::chrono::seconds timeout = std::chrono::seconds( 10 ) ); + /** @brief command-list constructed from script-entries in @p v + * + * The global settings @p doChroot and @p timeout can be overridden by + * the individual script-entries. + */ + CommandList( const QVariant& v, bool doChroot = true, std::chrono::seconds timeout = std::chrono::seconds( 10 ) ); + CommandList( int ) = delete; + CommandList( const QVariant&, int ) = delete; + + bool doChroot() const { return m_doChroot; } + std::chrono::seconds defaultTimeout() const { return m_timeout; } + + Calamares::JobResult run(); + + using CommandList_t::at; + using CommandList_t::cbegin; + using CommandList_t::cend; + using CommandList_t::const_iterator; + using CommandList_t::count; + using CommandList_t::isEmpty; + using CommandList_t::push_back; + using CommandList_t::value_type; + + /** @brief Return a copy of this command-list, with variables expanded + * + * Each command-line in the list is expanded with the given @p expander. + * @see CommandLine::expand() for details. + */ + DLLEXPORT CommandList expand( KMacroExpanderBase& expander ) const; + + /** @brief As above, with a default macro-expander. + * + * Each command-line in the list is expanded with that default macro-expander. + * @see CommandLine::expand() for details. + */ + DLLEXPORT CommandList expand() const; + + /** @brief Applies default-value @p verbose to each entry without an explicit setting. */ + DLLEXPORT void updateVerbose( bool verbose ); + +private: + bool m_doChroot; + std::chrono::seconds m_timeout; +}; + +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Dirs.h b/airootfs/usr/include/libcalamares/utils/Dirs.h new file mode 100644 index 0000000..d0edd7a --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Dirs.h @@ -0,0 +1,61 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2013-2016 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Originally from Tomahawk, portions: + * SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser + * SPDX-FileCopyrightText: 2010-2011 Leo Franchi + * SPDX-FileCopyrightText: 2010-2012 Jeff Mitchell + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_DIRS_H +#define UTILS_DIRS_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ +/** + * @brief appDataDir returns the directory with common application data. + * Defaults to CMAKE_INSTALL_FULL_DATADIR (usually /usr/share/calamares). + */ +DLLEXPORT QDir appDataDir(); + +/** + * @brief appLogDir returns the directory for Calamares logs. + * Defaults to QStandardPaths::CacheLocation (usually ~/.cache/Calamares). + */ +DLLEXPORT QDir appLogDir(); + +/** + * @brief systemLibDir returns the system's lib directory. + * Defaults to CMAKE_INSTALL_FULL_LIBDIR (usually /usr/lib64 or /usr/lib). + */ +DLLEXPORT QDir systemLibDir(); + +/** + * Override app data dir. Only for testing purposes. + */ +DLLEXPORT void setAppDataDir( const QDir& dir ); +DLLEXPORT bool isAppDataDirOverridden(); + +/** @brief Setup extra config and data dirs from the XDG variables. + */ +DLLEXPORT void setXdgDirs(); +/** @brief Are any extra directories configured? */ +DLLEXPORT bool haveExtraDirs(); +/** @brief XDG_CONFIG_DIRS, each guaranteed to end with / */ +DLLEXPORT QStringList extraConfigDirs(); +/** @brief XDG_DATA_DIRS, each guaranteed to end with / */ +DLLEXPORT QStringList extraDataDirs(); +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Entropy.h b/airootfs/usr/include/libcalamares/utils/Entropy.h new file mode 100644 index 0000000..a28c95c --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Entropy.h @@ -0,0 +1,46 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_ENTROPY_H +#define UTILS_ENTROPY_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ +/// @brief Which entropy source was actually used for the entropy. +enum class EntropySource +{ + None, ///< Buffer is empty, no random data + URandom, ///< Read from /dev/urandom + Twister ///< Generated by pseudo-random +}; + +/** @brief Fill buffer @p b with exactly @p size random bytes + * + * The array is cleared and resized, then filled with 0xcb + * "just in case", after which it is filled with random + * bytes from a suitable source. Returns which source was used. + */ +DLLEXPORT EntropySource getEntropy( int size, QByteArray& b ); + +/** @brief Fill string @p s with exactly @p size random printable ASCII characters + * + * The characters are picked from a set of 64 (2^6). The string + * contains 6 * size bits of entropy. * Returns which source was used. + * @see getEntropy + */ +DLLEXPORT EntropySource getPrintableEntropy( int size, QString& s ); +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Gui.h b/airootfs/usr/include/libcalamares/utils/Gui.h new file mode 100644 index 0000000..1264bc1 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Gui.h @@ -0,0 +1,102 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef CALAMARESUTILSGUI_H +#define CALAMARESUTILSGUI_H + +#include "DllMacro.h" + +#include +#include +#include + +class QLayout; + +namespace Calamares +{ + +/** + * @brief The ImageType enum lists all common Calamares icons. + * Icons are loaded from SVGs and cached. Each icon has an enum value, through which + * it can be accessed. + * You can forward-declare this as: + * enum ImageType : int; + */ +enum ImageType : int +{ + Yes, + No, + Information, + Fail, + Bugs, + Help, + Release, + Donate, + PartitionDisk, + PartitionPartition, + PartitionAlongside, + PartitionEraseAuto, + PartitionManual, + PartitionReplaceOs, + PartitionTable, + BootEnvironment, + Squid, + StatusOk, // Icons for the requirements checker + StatusWarning, + StatusError +}; + +/** + * @brief The ImageMode enum contains different transformations that can be applied. + * Most of these are currently unused. + */ +enum ImageMode +{ + Original, + CoverInCase, + Grid, + DropShadow, +}; + +/** + * @brief defaultPixmap returns a resized and/or transformed pixmap for a given + * ImageType. + * @param type the ImageType i.e. the enum value for an SVG. + * @param mode the transformation to apply (default: no transformation). + * @param size the target pixmap size (default: original SVG size). + * @return the new pixmap. + */ +UIDLLEXPORT QPixmap defaultPixmap( ImageType type, + ImageMode mode = Calamares::Original, + const QSize& size = QSize( 0, 0 ) ); + +/** + * @brief unmarginLayout recursively walks the QLayout tree and removes all margins. + * @param layout the layout to unmargin. + */ +UIDLLEXPORT void unmarginLayout( QLayout* layout ); + +UIDLLEXPORT void setDefaultFontSize( int points ); +UIDLLEXPORT int defaultFontSize(); // in points +UIDLLEXPORT int defaultFontHeight(); // in pixels, DPI-specific +UIDLLEXPORT QFont largeFont(); +UIDLLEXPORT QSize defaultIconSize(); + +/** + * @brief Size constants for the main Calamares window. + */ +constexpr int windowMinimumWidth = 800; +constexpr int windowMinimumHeight = 520; +constexpr int windowPreferredWidth = 1024; +constexpr int windowPreferredHeight = 520; + +} // namespace Calamares + +#endif // CALAMARESUTILSGUI_H diff --git a/airootfs/usr/include/libcalamares/utils/ImageRegistry.h b/airootfs/usr/include/libcalamares/utils/ImageRegistry.h new file mode 100644 index 0000000..0ced780 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/ImageRegistry.h @@ -0,0 +1,32 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2012 Christian Muehlhaeuser + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef IMAGE_REGISTRY_H +#define IMAGE_REGISTRY_H + +#include + +#include "DllMacro.h" +#include "utils/Gui.h" + +class UIDLLEXPORT ImageRegistry +{ +public: + static ImageRegistry* instance(); + + explicit ImageRegistry(); + + QIcon icon( const QString& image, Calamares::ImageMode mode = Calamares::Original ); + QPixmap pixmap( const QString& image, const QSize& size, Calamares::ImageMode mode = Calamares::Original ); + +private: + qint64 cacheKey( const QSize& size ); + void putInCache( const QString& image, const QSize& size, Calamares::ImageMode mode, const QPixmap& pixmap ); +}; + +#endif // IMAGE_REGISTRY_H diff --git a/airootfs/usr/include/libcalamares/utils/Logger.h b/airootfs/usr/include/libcalamares/utils/Logger.h new file mode 100644 index 0000000..8d3301d --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Logger.h @@ -0,0 +1,404 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_LOGGER_H +#define UTILS_LOGGER_H + +#include "DllMacro.h" + +#include +#include +#include + +#include + +namespace Logger +{ +class Once; + +struct FuncSuppressor +{ + explicit constexpr FuncSuppressor( const char[] ); + const char* m_s; +}; + +struct NoQuote_t +{ +}; +struct Quote_t +{ +}; + +DLLEXPORT extern const FuncSuppressor Continuation; +DLLEXPORT extern const FuncSuppressor SubEntry; +DLLEXPORT extern const NoQuote_t NoQuote; +DLLEXPORT extern const Quote_t Quote; + +enum +{ + LOG_DISABLE = 0, + LOGERROR = 1, + LOGWARNING = 2, + LOGDEBUG = 6, + LOGVERBOSE = 8 +}; + +class DLLEXPORT CDebug : public QDebug +{ +public: + explicit CDebug( unsigned int debugLevel = LOGDEBUG, const char* func = nullptr ); + virtual ~CDebug(); + + friend CDebug& operator<<( CDebug&&, const FuncSuppressor& ); + friend CDebug& operator<<( CDebug&&, const Once& ); + + inline unsigned int level() const { return m_debugLevel; } + +private: + QString m_msg; + unsigned int m_debugLevel; + const char* m_funcinfo = nullptr; +}; + +inline CDebug& +operator<<( CDebug&& s, const FuncSuppressor& f ) +{ + if ( s.m_funcinfo ) + { + s.m_funcinfo = nullptr; + s.m_msg = QString( f.m_s ); + } + return s; +} + +inline QDebug& +operator<<( QDebug& s, const FuncSuppressor& f ) +{ + return s << f.m_s; +} + +inline QDebug& +operator<<( QDebug& s, const NoQuote_t& ) +{ + return s.noquote().nospace(); +} + +inline QDebug& +operator<<( QDebug& s, const Quote_t& ) +{ + return s.quote().space(); +} + +/** + * @brief The full path of the log file. + */ +DLLEXPORT QString logFile(); + +/** + * @brief Start logging to the log file. + * + * Call this (once) to start logging to the log file (usually + * ~/.cache/calamares/session.log ). An existing log file is + * rolled over if it is too large. + */ +DLLEXPORT void setupLogfile(); + +/** + * @brief Set a log level for future logging. + * + * Pass in a value from the LOG* enum, above. Use 0 to + * disable logging. Values greater than LOGVERBOSE are + * limited to LOGVERBOSE, which will log everything. + * + * Practical values are 0, 1, 2, and 6. + */ +DLLEXPORT void setupLogLevel( unsigned int level ); + +/** @brief Return the configured log-level. */ +DLLEXPORT unsigned int logLevel(); + +/** @brief Would the given @p level really be logged? */ +DLLEXPORT bool logLevelEnabled( unsigned int level ); + +/** + * @brief Row-oriented formatted logging. + * + * Use DebugRow to produce multiple rows of 2-column output + * in a debugging statement. For instance, + * cDebug() << DebugRow(1,12) + * << DebugRow(2,24) + * will produce a single timestamped debug line with continuations. + * Each DebugRow produces one line of output, with the two values. + */ +template < typename T, typename U > +struct DebugRow +{ +public: + explicit DebugRow( const T& t, const U& u ) + : first( t ) + , second( u ) + { + } + + const T first; + const U second; +}; + +/** + * @brief List-oriented formatted logging. + * + * Use DebugList to produce multiple rows of output in a debugging + * statement. For instance, + * cDebug() << DebugList( QStringList() << "foo" << "bar" ) + * will produce a single timestamped debug line with continuations. + * Each element of the list of strings will be logged on a separate line. + */ +/* TODO: Calamares 3.3, bump requirements to C++17, and rename + * this to DebugList, dropping the convenience-definition + * below. In C++17, class template argument deduction is + * added, so `DebugList( whatever )` determines the right + * type already (also for QStringList). + */ +template < typename T > +struct DebugListT +{ + using list_t = QList< T >; + + explicit DebugListT( const list_t& l ) + : list( l ) + { + } + + const list_t& list; +}; + +///@brief Convenience for QStringList, needs no template parameters +struct DebugList : public DebugListT< QString > +{ + explicit DebugList( const list_t& l ) + : DebugListT( l ) + { + } +}; + +/** + * @brief Map-oriented formatted logging. + * + * Use DebugMap to produce multiple rows of output in a debugging + * statement from a map. The output is intentionally a bit-yaml-ish. + * cDebug() << DebugMap( map ) + * will produce a single timestamped debug line with continuations. + * The continued lines will have a key (from the map) and a value + * on each line. + */ +struct DebugMap +{ +public: + explicit DebugMap( const QVariantMap& m ) + : map( m ) + { + } + + const QVariantMap& map; +}; + +/** @brief When logging commands, don't log everything. + * + * The command-line arguments to some commands may contain the + * encrypted password set by the user. Don't log that password, + * since the log may get posted to bug reports, or stored in + * the target system. + */ +struct RedactedCommand +{ + RedactedCommand( const QStringList& l ) + : list( l ) + { + } + + const QStringList& list; +}; + +DLLEXPORT QDebug& operator<<( QDebug& s, const RedactedCommand& l ); + +/** @brief When logging "private" identifiers, keep them consistent but private + * + * Send a string to a logger in such a way that each time it is logged, + * it logs the same way, but without revealing the actual contents. + * This can be applied to user names, UUIDs, etc. + */ +struct DLLEXPORT RedactedName +{ + RedactedName( const char* context, const QString& s ); + RedactedName( const QString& context, const QString& s ); + + operator QString() const; + +private: + const uint m_id; + const QString m_context; +}; + +inline QDebug& +operator<<( QDebug& s, const RedactedName& n ) +{ + return s << NoQuote << QString( n ) << Quote; +} + +/** + * @brief Formatted logging of a pointer + * + * Pointers are printed as void-pointer, so just an address (unlike, say, + * QObject pointers which show an address and some metadata) preceded + * by an '@'. This avoids C-style (void*) casts in the code. + * + * Shared pointers are indicated by 'S@' and unique pointers by 'U@'. + */ +struct Pointer +{ +public: + explicit Pointer( const void* p ) + : ptr( p ) + , kind( 0 ) + { + } + + template < typename T > + explicit Pointer( const std::shared_ptr< T >& p ) + : ptr( p.get() ) + , kind( 'S' ) + { + } + + template < typename T > + explicit Pointer( const std::unique_ptr< T >& p ) + : ptr( p.get() ) + , kind( 'U' ) + { + } + + const void* const ptr; + const char kind; +}; + +/** @brief output operator for DebugRow */ +template < typename T, typename U > +inline QDebug& +operator<<( QDebug& s, const DebugRow< T, U >& t ) +{ + s << Continuation << t.first << ':' << ' ' << t.second; + return s; +} + +/** @brief output operator for DebugList, assuming operator<< for T exists */ +template < typename T = QString > +inline QDebug& +operator<<( QDebug& s, const DebugListT< T >& c ) +{ + for ( const auto& i : c.list ) + { + s << Continuation << i; + } + return s; +} + +/** @brief supporting method for outputting a DebugMap */ +DLLEXPORT QString toString( const QVariant& v ); + +/** @brief output operator for DebugMap */ +inline QDebug& +operator<<( QDebug& s, const DebugMap& t ) +{ + for ( auto it = t.map.constBegin(); it != t.map.constEnd(); ++it ) + { + s << Continuation << it.key().toUtf8().constData() << ':' << ' ' << toString( it.value() ).toUtf8().constData(); + } + return s; +} + +inline QDebug& +operator<<( QDebug& s, const Pointer& p ) +{ + s << NoQuote; + if ( p.kind ) + { + s << p.kind; + } + s << '@' << p.ptr << Quote; + return s; +} + +/** @brief Convenience object for supplying SubEntry to a debug stream + * + * In a function with convoluted control paths, it may be unclear + * when to supply SubEntry to a debug stream -- it is convenient + * for the **first** debug statement from a given function to print + * the function header, and all subsequent onces to get SubEntry. + * + * Create an object of type Once and send it (first) to all CDebug + * objects; this will print the function header only once within the + * lifetime of that Once object. + */ +class Once +{ +public: + Once() + : m( true ) + { + } + friend CDebug& operator<<( CDebug&&, const Once& ); + + /** @brief Restore the object to "fresh" state + * + * It may be necessary to allow the Once object to stream the + * function header again -- for instance, after logging an error, + * any following debug log might want to re-introduce the header. + */ + void refresh() { m = true; } + + /** @brief Is this object "fresh"? + * + * Once a Once-object has printed (once) it is no longer fresh. + */ + operator bool() const { return m; } + +private: + mutable bool m = false; +}; + +inline CDebug& +operator<<( CDebug&& s, const Once& o ) +{ + if ( !logLevelEnabled( s.level() ) ) + { + // This won't print, so it's not using the "onceness" + return s; + } + + if ( o.m ) + { + o.m = false; + return s; + } + s.m_funcinfo = nullptr; + s << SubEntry; + return s; +} + +} // namespace Logger + +#define cVerbose() Logger::CDebug( Logger::LOGVERBOSE, Q_FUNC_INFO ) +#define cDebug() Logger::CDebug( Logger::LOGDEBUG, Q_FUNC_INFO ) +#define cWarning() Logger::CDebug( Logger::LOGWARNING, Q_FUNC_INFO ) +#define cError() Logger::CDebug( Logger::LOGERROR, Q_FUNC_INFO ) + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/NamedEnum.h b/airootfs/usr/include/libcalamares/utils/NamedEnum.h new file mode 100644 index 0000000..d93287b --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/NamedEnum.h @@ -0,0 +1,263 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Collabora Ltd + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/** @brief Support for "named" enumerations + * + * When a string needs to be one specific string out of a set of + * alternatives -- one "name" from an enumerated set -- then it + * is useful to have an **enum type** for the enumeration so that + * C++ code can work with the (strong) type of the enum, while + * the string can be used for human-readable interaction. + * The `NamedEnumTable` template provides support for naming + * values of an enum. + */ + +#ifndef UTILS_NAMEDENUM_H +#define UTILS_NAMEDENUM_H + +#include + +#include +#include +#include + +/** @brief Type for collecting parts of a named enum. + * + * The `NamedEnumTable` template provides support for naming + * values of an enum. It supports mapping strings to enum values + * and mapping enum values to strings. + * + * ## Example + * + * Suppose we have code where there are three alternatives; it is + * useful to have a strong type to make the alternatives visible + * in that code, so the compiler can help check: + * + * ``` + * enum class MilkshakeSize { None, Small, Large }; + * ``` + * + * In a switch() statement, the compiler will check that all kinds + * of milkshakes are dealt with; we can pass a MilkshakeSize to + * a function and rest assured that nobody will call that function + * with a silly value, like `1`. + * + * There is no relation between the C++ identifiers used, and + * any I/O related to that enumeration. In other words, + * + * ``` + * std::cout << MilkshakeSize::Small; + * ``` + * + * Will **not** print out "Small", or "small", or 1. It won't even + * compile, because there is no mapping of the enum values to + * something that can be output. + * + * By making a `NamedEnumTable` we can define a mapping + * between strings (names) and enum values, so that we can easily + * output the human-readable name, and also take string input + * and convert it to an enum value. Suppose we have a function + * `milkshakeSizeNames()` that returns a reference to such a table, + * then we can use `find()` to map enums-to-names and names-to-enums. + * + * ``` + * const auto& names = milkshakeSizeNames(); + * MilkshakeSize sz{ MilkshakeSize::Large }; + * std::cout << names.find(sz); // Probably "large" + * + * bool ok; + * sz = names.find( "small", ok ); // Probably MilkshakeSize::Small + * ``` + * + * ## Usage + * + * It is recommended to use a static const declaration for the table; + * typical use will define a function that returns a reference to + * the table, for shared use. + * + * The constructor for a table takes an initializer_list; each element + * of the initializer_list is a **pair** consisting of a name and + * an associated enum value. The names should be QStrings. For each enum + * value that is listed, the canonical name should come **first** in the + * table, so that printing the enum values gives the canonical result. + * + * ``` + * static const NamedEnumTable& milkshakeSizeNames() + * { + * static NamedEnumTable n { // Initializer list for n + * { "large", MilkshakeSize::Large }, // One pair of name-and-value + * { "small", MilkshakeSize::Small }, + * { "big", MilkshakeSize::Large } + * }; + * return n; + * } + * ``` + * + * The function `eNames()`, above, returns a reference to a name table + * for the enum (presumably an enum class) `E`. It is possible to have + * more than one table for an enum, or to make the table locally, + * but **usually** you want one definitive table of names and values. + * The `eNames()` function gives you that definitive table. In Calamres + * code, such functions are usually named after the underlying enum. + * + * Using this particular table, looking up "large" will return `MilkshakeSize::Large`, + * looking up "big" will **also** return `MilkshakeSize::Large`, looking up "derp" + * will return `MilkshakeSize::Large` (because that is the first value in the table) + * but will set the boolean `ok` parameter to false. Conversely, looking + * up `MilkshakeSize::Large` will return "large" (never "big"). + * + * Note that this particular table does **not** name MilkshakeSize::None, + * so it is probably wrong: you can't get a string for that enum + * value, and no string will map to MilkshakeSize::None either. + * In general, tables should cover all of the enum values. + * + * Passing an empty initializer_list to the constructor is supported, + * but will cause UB if the table is ever used for looking up a string. + * + */ +template < typename T > +struct NamedEnumTable +{ + using string_t = QString; + using enum_t = T; + using pair_t = std::pair< string_t, enum_t >; + using type = std::vector< pair_t >; + + type table; + + /** @brief Create a table of named enum values. + * + * Use braced-initialisation for NamedEnum, and remember that the + * elements of the list are **pairs**, e.g. + * + * ``` + * static const NamedEnumTable c{ {"red", Colors::Red } }; + * ``` + */ + NamedEnumTable( const std::initializer_list< pair_t >& v ) + : table( v ) + { + /* static_assert( v.size() > 0 ); */ + } + + /** @brief Find a name @p s in the table. + * + * Searches case-insensitively. + * + * If the name @p s is not found, @p ok is set to @c false and + * the first enum value in the table is returned. Otherwise, + * @p ok is set to @c true and the corresponding value is returned. + * Use the output value of @p ok to determine if the lookup was + * successful: there is otherwise no sensible way to distinguish + * found-the-name-of-the-first-item from not-found. + */ + enum_t find( const string_t& s, bool& ok ) const + { + ok = false; + + for ( const auto& p : table ) + { + if ( 0 == QString::compare( s, p.first, Qt::CaseInsensitive ) ) + { + ok = true; + return p.second; + } + } + + // ok is still false + return table.begin()->second; + } + + /** @brief Find a name @p s in the table. + * + * Searches case-insensitively. + * + * If the name @p s is not found, the value @p d is returned as + * a default. Otherwise the value corresponding to @p s is returned. + * This is a shortcut over find() using a bool to distinguish + * successful and unsuccesful lookups. + */ + enum_t find( const string_t& s, enum_t d ) const + { + bool ok = false; + enum_t e = find( s, ok ); + return ok ? e : d; + } + + /** @brief Find a value @p s in the table and return its name. + * + * If @p s is an enum value in the table, return the corresponding + * name (the first name with that value, if there are aliases) + * and set @p ok to @c true. + * + * If the value @p s is not found, @p ok is set to @c false and + * an empty string is returned. This indicates that the table does + * not cover all of the values * in `enum_t` (and @p s is one + * of them), **or** that the passed-in value of @p s is + * not a legal value, e.g. via a static_cast. + */ + string_t find( enum_t s, bool& ok ) const + { + ok = false; + + for ( const auto& p : table ) + { + if ( s == p.second ) + { + ok = true; + return p.first; + } + } + + // ok is still false + return string_t(); + } + + /** @brief Find a value @p s in the table and return its name. + * + * Returns an empty string if the value @p s is not found (this + * indicates that the table does not cover all of the values + * in `enum_t`, **or** that the passed-in value of @p s is + * not a legal value, e.g. via a static_cast). + */ + string_t find( enum_t s ) const + { + bool ok = false; + return find( s, ok ); + } +}; + +/** @brief Smashes an enum value to its underlying type. + * + * While an enum **class** is not an integral type, and its values can't be + * printed or treated like an integer (like an old-style enum can), + * the underlying type **is** integral. This template function + * returns the value of an enum value, in its underlying type. + * This can be useful for debugging purposes, e.g. + * + * ``` + * MilkshakeSize sz{ MilkshakeSize::None }; + * std::cout << milkshakeSizeNames().find( sz ) << smash( sz ); + * ``` + * + * This will print both the name and the underlying integer for the + * value; assuming the table from the example is used, there is + * no name for MilkshakeSize::None, so it will print an empty string, + * followed by the integral representation -- probably a 0. + */ +template < typename E > +constexpr typename std::underlying_type< E >::type +smash( const E e ) +{ + return static_cast< typename std::underlying_type< E >::type >( e ); +} + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/NamedSuffix.h b/airootfs/usr/include/libcalamares/utils/NamedSuffix.h new file mode 100644 index 0000000..8454bbe --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/NamedSuffix.h @@ -0,0 +1,100 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/** @brief Support for unit-suffixed values. + * + * This combines a value with an (enum) unit indicating what kind + * of value it is, e.g. 10 meters, or 64 pixels. Includes simple + * parsing support for the values written as strings like , + * e.g. "10m" or "64px". + * + * When a suffixed unit value needs validation, define an isValid() + * method; similarly for simple construction from a string (with a fixed + * table of suffixes). Typical use then looks like: + * + * class MyUnit : public NamedSuffix + * { + * public: + * using NamedSuffix::NamedSuffix; // Keep existing constructors + * MyUnit( const QString& s ); + * bool isValid() const; + * } ; + */ + +#ifndef UTILS_NAMEDSUFFIX_H +#define UTILS_NAMEDSUFFIX_H + +#include "NamedEnum.h" + +/** @brief Template that takes the enum type to work with and a special none-enum. */ +template < typename T, T _none > +class NamedSuffix +{ +public: + using unit_t = T; + + static constexpr unit_t none = _none; + + /** @brief Empty value. */ + NamedSuffix() + : m_value( 0 ) + , m_unit( none ) + { + } + + /** @brief Specific value and unit. */ + NamedSuffix( qint64 value, unit_t unit ) + : m_value( value ) + , m_unit( unit ) + { + } + + /** @brief Construct value and unit from string. + * + * This parses the given string @p s by comparing with the suffixes + * in @p table and uses the first matching suffix as the unit. + */ + NamedSuffix( const NamedEnumTable< T >& table, const QString& s ) + : NamedSuffix() + { + for ( const auto& suffix : table.table ) + { + if ( s.endsWith( suffix.first ) ) + { + m_value = s.left( s.length() - suffix.first.length() ).toLongLong(); + m_unit = suffix.second; + break; + } + } + } + + /** @brief Construct value from string. + * + * This is not defined in the template, because it should probably + * delegate to the constructor above with a fixed table. + */ + NamedSuffix( const QString& s ); + + qint64 value() const { return m_value; } + unit_t unit() const { return m_unit; } + + /** @brief Check that a value-unit combination is valid. + * + * This is not defined in the template, because validity (e.g. + * range of acceptable values) depends on the kind of unit. + */ + bool isValid() const; + +protected: + qint64 m_value; + unit_t m_unit; +}; + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Paste.h b/airootfs/usr/include/libcalamares/utils/Paste.h new file mode 100644 index 0000000..9cec18c --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Paste.h @@ -0,0 +1,46 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Bill Auger + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UTILS_PASTE_H +#define UTILS_PASTE_H + +#include "DllMacro.h" + +#include + +class QObject; +class QWidget; + +namespace Calamares +{ +namespace Paste +{ +/** @brief Send the current log file to a pastebin + * + * Returns the (string) URL that the pastebin gives us. + */ +UIDLLEXPORT QString doLogUpload( QObject* parent ); + +/** @brief Send the current log file to a pastebin + * + * As doLogUpload(), but also sets the clipboard and displays + * a message saying it's been done. + */ +UIDLLEXPORT QString doLogUploadUI( QWidget* parent ); + +/** @brief Is paste enabled? + * + * Checks the branding instance if paste can be done. + */ +UIDLLEXPORT bool isEnabled(); +} // namespace Paste + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Permissions.h b/airootfs/usr/include/libcalamares/utils/Permissions.h new file mode 100644 index 0000000..02ffc9c --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Permissions.h @@ -0,0 +1,113 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Scott Harvey + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef LIBCALAMARES_PERMISSIONS_H +#define LIBCALAMARES_PERMISSIONS_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ + +/** + * @brief Represents a :: + * + * The Permissions class takes a QString @p in the form of + * ::, checks it for validity, and makes the three + * components available indivdually. + */ +class DLLEXPORT Permissions +{ + +public: + /** @brief Constructor + * + * Splits the string @p at the colon (":") into separate elements for + * , , and (permissions), where 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. + */ + Permissions( QString const& p ); + + /// @brief Default constructor of an invalid Permissions. + Permissions(); + + /// @brief Was the Permissions object constructed from valid data? + bool isValid() const { return m_valid; } + /// @brief The user (first component, e.g. "root" in "root:wheel:755") + QString username() const { return m_username; } + /// @brief The group (second component, e.g. "wheel" in "root:wheel:755") + QString group() const { return m_group; } + /** @brief The value (file permission) as an integer. + * + * Bear in mind that input is in octal, but integers are just integers; + * naively printing them will get decimal results (e.g. 493 from the + * input of "root:wheel:755"). This is suitable to pass to apply(). + */ + int value() const { return m_value; } + /** @brief The value (file permission) as octal string + * + * This is suitable for passing to chmod-the-program, or for + * recreating the original Permissions string. + */ + QString octal() const { return QString::number( value(), 8 ); } + + /** @brief Sets the file-access @p mode of @p path + * + * Pass a path that is relative (or absolute) in the **host** system. + * + * @return @c true on success + */ + static bool apply( const QString& path, int mode ); + /** @brief Do both chmod and chown on @p path + * + * Note that interpreting user- and group- names for applying the + * permissions can be different between the host system and the target + * system; the target might not have a "live" user, for instance, and + * the host won't have the user-entered username for the installation. + * + * For this call, the names are interpreted in the **host** system. + * Pass a path that is relative (or absolute) in the **host** system. + * + * @return @c true on success of **both** operations + */ + static bool apply( const QString& path, const Permissions& p ); + /// Convenience method for apply(const QString&, const Permissions& ) + bool apply( const QString& path ) const { return apply( path, *this ); } + +private: + void parsePermissions( QString const& p ); + + QString m_username; + QString m_group; + int m_value; + 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). + * - octal representation with a leading 'o' (letter) and at most three + * octal digits (e.g. o755 or o644). Use this in YAML where a string + * of digits would otherwise be interpreted as a (base-10) integer. + * - "rwx" representation with exactly 9 characters like the output of ls. + */ +DLLEXPORT int parseFileMode( const QString& mode ); + +} // namespace Calamares + +#endif // LIBCALAMARES_PERMISSIONS_H diff --git a/airootfs/usr/include/libcalamares/utils/PluginFactory.h b/airootfs/usr/include/libcalamares/utils/PluginFactory.h new file mode 100644 index 0000000..3c975ac --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/PluginFactory.h @@ -0,0 +1,113 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_PLUGINFACTORY_H +#define UTILS_PLUGINFACTORY_H + +#include "DllMacro.h" + +#include + +#define CalamaresPluginFactory_iid "io.calamares.PluginFactory" + +/** @brief Plugin factory for Calamares + * + * A Calamares plugin contains just one kind of plugin -- either + * a job, or a viewstep -- so the factory is straightforward. + * It gets a single CreateInstanceFunction and calls that; + * the function is set when registerPlugin() is called in a subclass. + * + */ +class DLLEXPORT CalamaresPluginFactory : public QObject +{ + Q_OBJECT +public: + explicit CalamaresPluginFactory() {} + ~CalamaresPluginFactory() override; + + typedef QObject* ( *CreateInstanceFunction )( QObject* ); + + template < class T > + T* create( QObject* parent = nullptr ) + { + auto* op = fn ? fn( parent ) : nullptr; + if ( !op ) + { + return nullptr; + } + T* tp = qobject_cast< T* >( op ); + if ( !tp ) + { + delete op; + } + return tp; + } + +protected: + CreateInstanceFunction fn = nullptr; +}; + +/** @brief declare a Calamares Plugin Factory + * + * There should be one declaration -- generally alongside the + * class definition for the Job or ViewStep that the plugin is + * going to provide, in the header -- and one definition -- in + * the corresponding implementation. + */ +#define CALAMARES_PLUGIN_FACTORY_DECLARATION( name ) \ + class name : public CalamaresPluginFactory \ + { \ + Q_OBJECT \ + Q_INTERFACES( CalamaresPluginFactory ) \ + Q_PLUGIN_METADATA( IID CalamaresPluginFactory_iid ) \ + public: \ + explicit name(); \ + ~name() override; \ + template < class T > \ + static QObject* createInstance( QObject* parent ) \ + { \ + return new T( parent ); \ + } \ + template < class T > \ + void registerPlugin() \ + { \ + fn = createInstance< T >; \ + } \ + }; + +/** @brief Define a Calamares Plugin Factory + * + * This should be done exactly once, generally in the translation + * unit containing the definitions for the main class of the plugin, + * either the Job or the ViewStep definitions. + * + * The @p name must match the name used in the declaration, while + * @p pluginRegistrations should be a single call to `registerPlugin()` + * where `T` is the type (subclass of Job or ViewStep) defined by the + * plugin, eg. + * + * ``` + * CALAMARES_PLUGIN_FACTORY_DEFINITION( MyPlugin, registerPlugin() ) + * ``` + * + * Leaving out the `()` will lead to generally-weird compiler warnings. + */ +#define CALAMARES_PLUGIN_FACTORY_DEFINITION( name, pluginRegistrations ) \ + name::name() \ + : CalamaresPluginFactory() \ + { \ + pluginRegistrations; \ + } \ + name::~name() {} + +Q_DECLARE_INTERFACE( CalamaresPluginFactory, CalamaresPluginFactory_iid ) + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Qml.h b/airootfs/usr/include/libcalamares/utils/Qml.h new file mode 100644 index 0000000..6f44bc3 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Qml.h @@ -0,0 +1,90 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef UTILS_QML_H +#define UTILS_QML_H + +#include "DllMacro.h" + +#include "modulesystem/InstanceKey.h" +#include "utils/NamedEnum.h" + +#include + +class QQuickItem; + +namespace Calamares +{ +/// @brief the extra directory where Calamares searches for QML files +UIDLLEXPORT QDir qmlModulesDir(); +/// @brief sets specific directory for searching for QML files +UIDLLEXPORT void setQmlModulesDir( const QDir& dir ); + +/** @brief initialize QML search path with branding directories + * + * Picks a suitable branding directory (from the build-dir in debug mode, + * otherwise based on the branding directory) and adds it to the + * QML modules directory; returns @c false if none is found. + */ +UIDLLEXPORT bool initQmlModulesDir(); + +/** @brief Sets up global Calamares models for QML + * + * This needs to be called at least once to make the global Calamares + * models (Branding, ViewManager, ...) available to QML. + * + * The following objects are made available globally: + * - `io.calamares.ui.Branding` (an object, see Branding.h) + * - `io.calamares.core.ViewManager` (a model, see ViewManager.h) + * - `io.calamares.core.Global` (an object, see GlobalStorage.h) + * Additionally, modules based on QmlViewStep have a context + * property `config` referring to that module's configuration (if any). + */ +UIDLLEXPORT void registerQmlModels(); + +/** @brief Calls the QML method @p method on @p qmlObject + * + * Pass in only the name of the method (e.g. onActivate). This function + * checks if the method exists (with no arguments) before trying to + * call it, so that no warnings are printed due to missing methods. + * + * If there is a return value from the QML method, it is logged (but not otherwise used). + */ +UIDLLEXPORT void callQmlFunction( QQuickItem* qmlObject, const char* method ); + +/** @brief Search modes for loading Qml files. + * + * A QML file could be compiled into QRC, or it could live + * in the branding directory (and, in debug-runs, in + * the current-directory). Modules have some control + * over where the search is done. + */ +enum class QmlSearch +{ + QrcOnly, + BrandingOnly, + Both +}; + +/// @brief Names for the search terms (in config files) +UIDLLEXPORT const NamedEnumTable< QmlSearch >& qmlSearchNames(); + +/** @brief Find a suitable QML file, given the search method and name hints + * + * Returns QString() if nothing is found (which would mean the module + * is badly configured). + */ +UIDLLEXPORT QString searchQmlFile( QmlSearch method, + const QString& configuredName, + const Calamares::ModuleSystem::InstanceKey& i ); +UIDLLEXPORT QString searchQmlFile( QmlSearch method, const QString& fileNameNoSuffix ); + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/QtCompat.h b/airootfs/usr/include/libcalamares/utils/QtCompat.h new file mode 100644 index 0000000..b568fbf --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/QtCompat.h @@ -0,0 +1,30 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/**@file Handle compatibility and deprecations across Qt versions + * + * Since Calamares is supposed to work with Qt 5.15 or Qt 6 or later, it + * covers a lot of changes in the Qt API. + * + * This file adjusts for that by introducing suitable aliases + * and workaround-functions. + * + * For a similar approach for QtCore, see libcalamares/utils/String.h + */ + +#ifndef UTILS_QTCOMPAT_H +#define UTILS_QTCOMPAT_H + +#include + +/* Avoid warnings about QPalette changes */ +constexpr static const auto WindowBackground = QPalette::Window; + +constexpr static const auto WindowText = QPalette::WindowText; + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/RAII.h b/airootfs/usr/include/libcalamares/utils/RAII.h new file mode 100644 index 0000000..8d4a907 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/RAII.h @@ -0,0 +1,112 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_RAII_H +#define UTILS_RAII_H + +#include +#include + +#include +#include + +/** @brief Convenience to zero out and deleteLater of any QObject-derived-class + * + * If, before destruction, preserve is set to @c true, then + * the object is "preserved", and not deleted at all. + */ +template < typename T > +struct cqDeleter +{ + T*& p; + bool preserve = false; + + ~cqDeleter() + { + static_assert( std::is_base_of< QObject, T >::value, "Not a QObject-class" ); + if ( !preserve ) + { + if ( p ) + { + p->deleteLater(); + } + p = nullptr; + } + } +}; + +/// @brief Blocks signals on a QObject until destruction +using cSignalBlocker = QSignalBlocker; + +/** @brief Writes a value on destruction to a pointed-to location. + * + * If the pointer is non-null, write the last-given-value if there + * is one to the pointed-to object. This is called the "then-value". + * + */ +template < typename T > +struct cScopedAssignment +{ + std::optional< T > m_value; + T* m_pointer; + + /** @brief Create a setter with no value set + * + * Until a value is set via operator=(), this pointer-setter + * will do nothing on destruction, leaving the pointed-to + * value unchanged. + */ + cScopedAssignment( T* p ) + : m_pointer( p ) + { + } + /** @brief Create a setter with a then-value already set + * + * This ensures that on destruction, the value @p v will be written; + * it is equivalent to assigning @p v immediately. The pointed-to + * value is **not** changed (until destruction). + */ + cScopedAssignment( T* p, T then ) + : m_value( then ) + , m_pointer( p ) + { + } + /** @brief Create a setter with a then-value and assign a new value now + * + * As above, but also assign @p now to the thing pointed-to. + */ + cScopedAssignment( T* p, T now, T then ) + : m_value( then ) + , m_pointer( p ) + { + if ( p ) + { + *p = now; + } + } + + ~cScopedAssignment() + { + if ( m_pointer && m_value.has_value() ) + { + *m_pointer = m_value.value(); + } + } + + const T& operator=( const T& then ) + { + m_value = then; + return then; + } +}; + +template < typename T > +cScopedAssignment( T p ) -> cScopedAssignment< decltype( *p ) >; +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Retranslator.h b/airootfs/usr/include/libcalamares/utils/Retranslator.h new file mode 100644 index 0000000..0075562 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Retranslator.h @@ -0,0 +1,145 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_RETRANSLATOR_H +#define UTILS_RETRANSLATOR_H + +#include "DllMacro.h" +#include "locale/Translation.h" + +#include +#include + +#include + +class QEvent; +class QLocale; +class QTranslator; + +namespace Calamares +{ +/** @brief changes the application language. + * @param locale the new locale (names as defined by Calamares). + * @param brandingTranslationsPrefix the branding path prefix, from Calamares::Branding. + */ +DLLEXPORT void installTranslator( const Calamares::Locale::Translation::Id& locale, + const QString& brandingTranslationsPrefix ); + +/** @brief Initializes the translations with the current system settings + */ +DLLEXPORT void installTranslator(); + +/** @brief The name of the (locale of the) most recently installed translator + * + * May return something different from the locale.name() of the + * QLocale passed in, because Calamares will munge some names and + * may remap translations. + */ +DLLEXPORT Calamares::Locale::Translation::Id translatorLocaleName(); + +/** @brief Loads translations into the given @p translator + * + * This function is not intended for general use: it is for those special + * cases where modules need their own translator / translations for data + * that is locale to the module. Tries to load a .qm from "sensible" + * locations, which are the same ones that installTranslator() would use. + * Takes local-translations into account. + * + * Note that @p prefix should end with an underscore '_' -- this function + * does not introduce one by itself. + * + * @returns @c true on success + */ +DLLEXPORT bool +loadTranslator( const Calamares::Locale::Translation::Id& locale, const QString& prefix, QTranslator* translator ); + +/** @brief Set @p allow to true to load translations from current dir. + * + * If false, (or never called) the translations are loaded only from + * system locations (the AppData dir) and from QRC (compiled in). + * Enable local translations to test translations stored in the + * current directory. + */ +DLLEXPORT void setAllowLocalTranslation( bool allow ); + +/** @brief Handles change-of-language events + * + * There is one single Retranslator object. Use `instance()` to get it. + * The top-level widget of the application should call + * `installEventFilter( Retranslator::instance() )` + * to set up event-handling for translation events. The Retranslator + * will emit signal `languageChanged()` if there is such an event. + * + * Normal consumers should not have to use the Retranslator directly, + * but use the macros `CALAMARES_RETRANSLATE*` to set things up + * in code -- the macros will connect to the Retranslator's signals. + */ +class DLLEXPORT Retranslator : public QObject +{ + Q_OBJECT +public: + /// @brief Gets the global (single) Retranslator object + static Retranslator* instance(); + + /// @brief Helper function for attaching lambdas + static void attach( QObject* o, std::function< void( void ) > f ); + +signals: + void languageChanged(); + +protected: + bool eventFilter( QObject* obj, QEvent* e ) override; + +private: + explicit Retranslator( QObject* parent ); +}; + +} // namespace Calamares + +/** @brief Call code for this object when language changes + * + * @p body should be a code block (it does not need braces) that can be wrapped + * up as a lambda. When the language changes, the lambda is called. Note that + * this macro should be used in constructors or other code that is run only + * once, since otherwise you will end up with multiple calls to @p body. + * + * NOTE: unlike plain QObject::connect(), the body is **also** called + * immediately after setting up the connection. This allows + * setup and translation code to be mixed together. + */ +#define CALAMARES_RETRANSLATE( body ) Calamares::Retranslator::attach( this, [ = ] { body } ) +/** @brief Call code for the given object (widget) when language changes + * + * This is identical to CALAMARES_RETRANSLATE, except the @p body is called + * for the given object, not this object. + * + * NOTE: unlike plain QObject::connect(), the body is **also** called + * immediately after setting up the connection. This allows + * setup and translation code to be mixed together. + */ +#define CALAMARES_RETRANSLATE_FOR( object, body ) Calamares::Retranslator::attach( object, [ = ] { body } ) +/** @brief Call a slot in this object when language changes + * + * Given a slot (in method-function-pointer notation), call that slot when the + * language changes. This is shorthand for connecting the Retranslator's + * signal to the given slot. + * + * NOTE: unlike plain QObject::connect(), the slot is **also** called + * immediately after setting up the connection. This allows + * setup and translation code to be mixed together. + */ +#define CALAMARES_RETRANSLATE_SLOT( slotfunc ) \ + do \ + { \ + connect( Calamares::Retranslator::instance(), &Calamares::Retranslator::languageChanged, this, slotfunc ); \ + ( this->*slotfunc )(); \ + } while ( false ) + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Runner.h b/airootfs/usr/include/libcalamares/utils/Runner.h new file mode 100644 index 0000000..aa80876 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Runner.h @@ -0,0 +1,135 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_RUNNER_H +#define UTILS_RUNNER_H + +#include "System.h" + +#include +#include +#include + +#include +#include +#include + +namespace Calamares +{ +namespace Utils +{ + +using RunLocation = Calamares::System::RunLocation; +using ProcessResult = Calamares::ProcessResult; + +/** @brief A Runner wraps a process and handles running it and processing output + * + * This is basically a QProcess, but handles both running in the + * host system (through env(1)) or in the target (by calling chroot(8)). + * It has an output signal that handles output one line at a time + * (unlike QProcess that lets you do the buffering yourself). + * This output processing is only enabled if you do so explicitly. + * + * Use the set*() methods to configure the runner. + * + * If you call enableOutputProcessing(), then you can connect to + * the output() signal to receive each line (including trailing newline!). + * + * Processes are always run with LC_ALL and LANG set to "C". + */ +class DLLEXPORT Runner : public QObject +{ + Q_OBJECT + +public: + /** @brief Create an empty runner + * + * This is a runner with no commands, nothing; call set*() methods + * to configure it. + */ + Runner(); + /** @brief Create a runner with a specified command + * + * Equivalent to Calamares::Utils::Runner::Runner() followed by + * calling setCommand(). + */ + Runner( const QStringList& command ); + virtual ~Runner() override; + + Runner& setCommand( const QStringList& command ) + { + m_command = command; + return *this; + } + Runner& setLocation( RunLocation r ) + { + m_location = r; + return *this; + } + Runner& setWorkingDirectory( const QDir& directory ) + { + m_directory = directory.absolutePath(); + return *this; + } + Runner& setWorkingDirectory( const QString& directory ) + { + m_directory = directory; + return *this; + } + Runner& setTimeout( std::chrono::seconds timeout ) + { + m_timeout = timeout; + return *this; + } + Runner& setInput( const QString& input ) + { + m_input = input; + return *this; + } + Runner& setOutputProcessing( bool enable ) + { + m_output = enable; + return *this; + } + + Runner& enableOutputProcessing() + { + m_output = true; + return *this; + } + + ProcessResult run(); + /** @brief The executable (argv[0]) that this runner will run + * + * This is the first element of the command; it does not include + * env(1) or chroot(8) which are injected when actually running + * the command. + */ + QString executable() const { return m_command.isEmpty() ? QString() : m_command.first(); } + +signals: + void output( QString line ); + +private: + // What to run, and where. + QStringList m_command; + QString m_directory; + RunLocation m_location { RunLocation::RunInHost }; + + // Settings for when it actually runs + QString m_input; + std::chrono::milliseconds m_timeout { 0 }; + bool m_output = false; +}; + +} // namespace Utils +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/String.h b/airootfs/usr/include/libcalamares/utils/String.h new file mode 100644 index 0000000..cf942f1 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/String.h @@ -0,0 +1,112 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2013-2016 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Originally from Tomahawk, portions: + * SPDX-FileCopyrightText: 2010-2011 Christian Muehlhaeuser + * SPDX-FileCopyrightText: 2010-2011 Leo Franchi + * SPDX-FileCopyrightText: 2010-2012 Jeff Mitchell + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_STRING_H +#define UTILS_STRING_H + +#include "DllMacro.h" + +#include + +/* Qt 5.14 changed the API to QString::split(), adding new overloads + * that take a different enum, then Qt 5.15 deprecated the old ones. + * To avoid overly-many warnings related to the API change, introduce + * Calamares-specific constants that pull from the correct enum. + */ +constexpr static const auto SplitSkipEmptyParts = Qt::SkipEmptyParts; + +constexpr static const auto SplitKeepEmptyParts = Qt::KeepEmptyParts; + +namespace Calamares +{ +/** + * @brief The Calamares::String namespace + * + * This namespace contains functions related to string-handling, + * string-expansion, etc. + */ +namespace String +{ +/** + * @brief removeDiacritics replaces letters with diacritics and ligatures with + * alternative forms and digraphs. + * @param string the string to transform. + * @return the output string with plain characters. + */ +DLLEXPORT QString removeDiacritics( const QString& string ); + +/** + * @brief obscure is a bidirectional obfuscation function, from KStringHandler. + * @param string the input string. + * @return the obfuscated string. + */ +DLLEXPORT QString obscure( const QString& string ); + +/** @brief Parameter for counting lines at beginning and end of string + * + * This is used by truncateMultiLine() to indicate how many lines from + * the beginning and how many from the end should be kept. + */ +struct LinesStartEnd +{ + int atStart = 0; + int atEnd = 0; +}; + +/** @brief Parameter for counting characters in truncateMultiLine() + */ +struct CharCount +{ + int total = 0; +}; + +/** @brief Truncate a string to some reasonable length for display + * + * Keep the first few, or last few (or both) lines of a possibly lengthy + * message @p string and reduce it to a displayable size (e.g. for + * pop-up windows that display the message). If the message is longer + * than @p chars, then characters are removed from the front (if + * @p lines.atStart is zero) or end (if @p lines.atEnd is zero) or in the middle + * (if both are nonzero). + * + * Asking for 0 lines will make this behave like QString::truncate(). + * + * @param string the input string. + * @param lines number of lines to preserve. + * @param chars maximum number of characters in the returned string. + * @return a string built from parts of the input string. + */ +DLLEXPORT QString truncateMultiLine( const QString& string, + LinesStartEnd lines = LinesStartEnd { 3, 5 }, + CharCount chars = CharCount { 812 } ); + +/** @brief Remove all @p c at the beginning of @p string + * + * Modifies the @p string in-place. If @p c is not the first character + * of @p string, the string is left unchanged; otherwise the first character + * is removed and the process repeats. + */ +DLLEXPORT void removeLeading( QString& string, QChar c ); +/** @brief Remove all @p c at the end of @p string + * + * Like removeLeading(), but at the end of the string. + */ +DLLEXPORT void removeTrailing( QString& string, QChar c ); + +} // namespace String +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/StringExpander.h b/airootfs/usr/include/libcalamares/utils/StringExpander.h new file mode 100644 index 0000000..fa46bf9 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/StringExpander.h @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_STRINGEXPANDER_H +#define UTILS_STRINGEXPANDER_H + +#include "DllMacro.h" + +#include + +#include +#include + +#include + +namespace Calamares +{ +namespace String +{ + +/** @brief Expand variables in a string against a dictionary. + * + * This class provides a convenience API for building up a dictionary + * and using it to expand strings. Use the `expand()` method to + * do standard word-based expansion with `$` as macro-symbol. + * + * Unlike straight-up `KMacroExpander::expandMacros()`, this + * provides an API to find out which variables were missing + * from the dictionary during expansion. Use `hasErrors()` and + * `errorNames()` to find out which variables those were. + * + * Call `clearErrors()` to reset the stored errors. Calling + * `expand()` implicitly clears the errors before starting + * a new expansion, as well. + */ +class DLLEXPORT DictionaryExpander : public KWordMacroExpander +{ +public: + DictionaryExpander(); + DictionaryExpander( DictionaryExpander&& ); + virtual ~DictionaryExpander() override; + + void insert( const QString& key, const QString& value ); + /** @brief As insert(), but supports method-chaining. + * + */ + DictionaryExpander& add( const QString& key, const QString& value ) + { + insert( key, value ); + return *this; + } + + void clearErrors(); + bool hasErrors() const; + QStringList errorNames() const; + + QString expand( QString s ); + +protected: + virtual bool expandMacro( const QString& str, QStringList& ret ) override; + +private: + struct Private; + std::unique_ptr< Private > d; +}; + +} // namespace String +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/System.h b/airootfs/usr/include/libcalamares/utils/System.h new file mode 100644 index 0000000..6b6eba1 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/System.h @@ -0,0 +1,365 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef UTILS_CALAMARESUTILSSYSTEM_H +#define UTILS_CALAMARESUTILSSYSTEM_H + +#include "DllMacro.h" + +#include "Job.h" + +#include +#include +#include + +#include + +namespace Calamares +{ +class ProcessResult : public QPair< int, QString > +{ +public: + enum class Code : int + { + Crashed = -1, // Must match special return values from QProcess + FailedToStart = -2, // Must match special return values from QProcess + NoWorkingDirectory = -3, + TimedOut = -4 + }; + + /** @brief Implicit one-argument constructor has no output, only a return code */ + ProcessResult( Code r ) + : QPair< int, QString >( static_cast< int >( r ), QString() ) + { + } + ProcessResult( int r, QString s ) + : QPair< int, QString >( r, s ) + { + } + + int getExitCode() const { return first; } + QString getOutput() const { return second; } + + /** @brief Explain a typical external process failure. + * + * @param errorCode Return code from runCommand() or similar + * (negative values get special explanation). The member + * function uses the exit code stored in the ProcessResult + * @param output (error) output from the command, used when there is + * an error to report (exit code > 0). The member + * function uses the output stored in the ProcessResult. + * @param command String or split-up string of the command + * that was invoked. + * @param timeout Timeout passed to the process runner, for explaining + * error code -4 (timeout). + */ + static DLLEXPORT Calamares::JobResult + explainProcess( int errorCode, const QString& command, const QString& output, std::chrono::seconds timeout ); + + /// @brief Convenience wrapper for explainProcess() + inline Calamares::JobResult explainProcess( const QString& command, std::chrono::seconds timeout ) const + { + return explainProcess( getExitCode(), command, getOutput(), timeout ); + } + + /// @brief Convenience wrapper for explainProcess() + inline Calamares::JobResult explainProcess( const QStringList& command, std::chrono::seconds timeout ) const + { + return explainProcess( getExitCode(), command.join( ' ' ), getOutput(), timeout ); + } +}; + +/** @brief The result of a create*() action, for status + * + * A CreationResult has a status field, can be converted to bool + * (true only on success) and can report the full pathname of + * the thing created if it was successful. + */ +class CreationResult : public QPair< int, QString > +{ +public: + enum class Code : int + { + // These are "not failed", but only OK is a success + OK = 0, + AlreadyExists = 1, + // These are "failed" + Invalid = -1, + Failed = -2 + }; + + CreationResult( Code r ) + : QPair< int, QString >( static_cast< int >( r ), QString() ) + { + } + explicit CreationResult( const QString& path ) + : QPair< int, QString >( 0, path ) + { + } + + Code code() const { return static_cast< Code >( first ); } + QString path() const { return second; } + + bool failed() const { return first < 0; } + operator bool() const { return first == 0; } +}; + +/** + * @brief The System class is a singleton with utility functions that perform + * system-specific operations. + */ +class DLLEXPORT System : public QObject +{ + Q_OBJECT +public: + /** + * @brief System the constructor. Only call this once in a Calamares instance. + * @param doChroot set to true if all external commands should run in the + * target system chroot, otherwise false to run everything on the current system. + * @param parent the QObject parent. + */ + explicit System( bool doChroot, QObject* parent = nullptr ); + ~System() override; + + static System* instance(); + + /** (Typed) Boolean describing where a particular command should be run, + * whether in the host (live) system or in the (chroot) target system. + */ + enum class RunLocation + { + RunInHost, + RunInTarget + }; + + /** @brief Runs a command in the host or the target (select explicitly) + * + * @param location whether to run in the host or the target + * @param args the command with arguments, as a string list. + * @param workingPath the current working directory for the QProcess + * call (optional). + * @param stdInput the input string to send to the running process as + * standard input (optional). + * @param timeoutSec the timeout after which the process will be + * killed (optional, default is 0 i.e. no timeout). + * + * @returns the program's exit code and its output (if any). Special + * exit codes (which will never have any output) are: + * Crashed = QProcess crash + * FailedToStart = QProcess cannot start + * NoWorkingDirectory = bad arguments + * TimedOut = QProcess timeout + */ + static DLLEXPORT ProcessResult runCommand( RunLocation location, + const QStringList& args, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) ); + + /** @brief Convenience wrapper for runCommand() in the host + * + * Runs the given command-line @p args in the **host** in the current direcory + * with no input, and the given @p timeoutSec for completion. + */ + static inline ProcessResult runCommand( const QStringList& args, std::chrono::seconds timeoutSec ) + { + return runCommand( RunLocation::RunInHost, args, QString(), QString(), timeoutSec ); + } + + /** @brief Convenience wrapper for runCommand(). + * + * Runs the command in the location specified through the boolean + * doChroot(), which is what you usually want for running commands + * during installation. + */ + inline ProcessResult targetEnvCommand( const QStringList& args, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) ) + { + return runCommand( + m_doChroot ? RunLocation::RunInTarget : RunLocation::RunInHost, args, workingPath, stdInput, timeoutSec ); + } + + /** @brief Convenience wrapper for targetEnvCommand() which returns only the exit code */ + inline int targetEnvCall( const QStringList& args, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) ) + { + return targetEnvCommand( args, workingPath, stdInput, timeoutSec ).first; + } + + /** @brief Convenience wrapper for targetEnvCommand() which returns only the exit code */ + inline int targetEnvCall( const QString& command, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) ) + { + return targetEnvCall( QStringList { command }, workingPath, stdInput, timeoutSec ); + } + + /** @brief Convenience wrapper for targetEnvCommand() which returns only the exit code + * + * Places the called program's output in the @p output string. + */ + int targetEnvOutput( const QStringList& args, + QString& output, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) ) + { + auto r = targetEnvCommand( args, workingPath, stdInput, timeoutSec ); + output = r.second; + return r.first; + } + + /** @brief Convenience wrapper for targetEnvCommand() which returns only the exit code + * + * Places the called program's output in the @p output string. + */ + inline int targetEnvOutput( const QString& command, + QString& output, + const QString& workingPath = QString(), + const QString& stdInput = QString(), + std::chrono::seconds timeoutSec = std::chrono::seconds( 0 ) ) + { + return targetEnvOutput( QStringList { command }, output, workingPath, stdInput, timeoutSec ); + } + + /** @brief Gets a path to a file in the target system, from the host. + * + * @param path Path to the file; this is interpreted + * from the root of the target system (whatever that may be, + * but / in the chroot, or / in OEM modes). + * + * @return The complete path to the target file, from + * the root of the host system, or empty on failure. + * + * For instance, during installation where the target root is + * mounted on /tmp/calamares-something, asking for targetPath("/etc/passwd") + * will give you /tmp/calamares-something/etc/passwd. + * + * No attempt is made to canonicalize anything, since paths might not exist. + */ + DLLEXPORT QString targetPath( const QString& path ) const; + + enum class WriteMode + { + KeepExisting, + Overwrite + }; + + /** @brief Create a (small-ish) file in the target system. + * + * @param path Path to the file; this is interpreted + * from the root of the target system (whatever that may be, + * but / in the chroot, or / in OEM modes). + * @param contents Actual content of the file. + * + * If the target already exists: + * - returns AlreadyExists as a result (and does not overwrite), + * - **unless** @p mode is set to Overwrite, then it tries writing as + * usual and will not return AlreadyExists. + * + * @return The complete canonical path to the target file from the + * root of the host system, or empty on failure. (Here, it is + * possible to be canonical because the file exists). + */ + DLLEXPORT CreationResult createTargetFile( const QString& path, + const QByteArray& contents, + WriteMode mode = WriteMode::KeepExisting ) const; + + /** @brief Remove a file from the target system. + * + * @param path Path to the file; this is interpreted from the root + * of the target system (@see targetPath()). + * + * Does no error checking to see if the target file was really removed. + */ + DLLEXPORT void removeTargetFile( const QString& path ) const; + + /** @brief Reads a file from the target system. + * + * @param path Path to the file; this is interpreted from the root of + * the target system (@see targetPath()). + * + * Does no error checking, and returns an empty list if the file does + * not exist. + * + * NOTE: This function is now basically the same as QFile::readAll(), + * splitting into lines, but Calamares may need to change + * permissions or raise privileges to actually read the file, + * which is why there is an API. + * + * NOTE: Since this buffers the whole file in memory, reading big files + * is not recommended. + */ + DLLEXPORT QStringList readTargetFile( const QString& path ) const; + + /** @brief Ensure that the directory @p path exists + * + * @param path a full pathname to a desired directory. + * + * All the directory components including the last path component are + * created, as needed. Returns true on success. + * + * @see QDir::mkpath + */ + DLLEXPORT bool createTargetDirs( const QString& path ) const; + + /** @brief Convenience to create parent directories of a file path. + * + * Creates all the parent directories until the last + * component of @p filePath . @see createTargetDirs() + */ + DLLEXPORT bool createTargetParentDirs( const QString& filePath ) const; + + /** + * @brief getTotalMemoryB returns the total main memory, in bytes. + * + * Since it is difficult to get the RAM memory size exactly -- either + * by reading information from the DIMMs, which may fail on virtual hosts + * or from asking the kernel, which doesn't report some memory areas -- + * this returns a pair of guessed-size (in bytes) and a "guesstimate factor" + * which says how good the guess is. Generally, assume the *real* memory + * available is size * guesstimate. + * + * If nothing can be found, returns a 0 size and 0 guesstimate. + * + * @return size, guesstimate-factor + */ + DLLEXPORT QPair< quint64, qreal > getTotalMemoryB() const; + + /** + * @brief getCpuDescription returns a string describing the CPU. + * + * Returns the value of the "model name" line in /proc/cpuinfo. + */ + DLLEXPORT QString getCpuDescription() const; + + /** + * @brief getTotalDiskB returns the total disk attached, in bytes. + * + * If nothing can be found, returns a 0. + */ + DLLEXPORT quint64 getTotalDiskB() const; + + DLLEXPORT bool doChroot() const; + +private: + static System* s_instance; + + bool m_doChroot; +}; + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Traits.h b/airootfs/usr/include/libcalamares/utils/Traits.h new file mode 100644 index 0000000..9c0282b --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Traits.h @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_TRAITS_H +#define UTILS_TRAITS_H + +#include + +namespace Calamares +{ + +/** @brief Traits machinery lives in this namespace + * + * The primary purpose of this namespace is to hold machinery that + * is created by the DECLARE_HAS_METHOD macro. + * + * The DECLARE_HAS_METHOD macro builds machinery to check whether + * a class has a particular named method. This can be used to + * specialize templates elsewhere for use with classes with, or without, + * the named method. + * + * To use the machinery (which is not that sophisticated): + * + * - Put `DECLARE_HAS_METHOD(myFunction)` somewhere in file scope. + * This puts together the machinery for detecting if `myFunction` + * is a method of some class. + * - At global scope, `has_myFunction` is now either std::true_type, + * if the type `T` has a method `T::myFunction`, or std::false_type, + * if it does not. + * + * To specialize template methods based on the presence of the named + * method, write **three** overloads: + * + * - `template myMethod(args ..., const std::true_type& )` + * This is the implementation where class T has `myFunction`. + * - `template myMethod(args ..., const std::false_type& )` + * This is the implementation without. + * - `template myMethod(args ...)` is the general implementation, + * which can call the specialized implementations with + * `return myMethod(args ..., has_myFunction{})` + */ +namespace Traits +{ +template < class > +struct sfinae_true : std::true_type +{ +}; +} // namespace Traits +} // namespace Calamares + +#define DECLARE_HAS_METHOD( m ) \ + namespace Calamares \ + { \ + namespace Traits \ + { \ + struct has_##m \ + { \ + template < class T > \ + static auto f( int ) -> sfinae_true< decltype( &T::m ) >; \ + template < class T > \ + static auto f( long ) -> std::false_type; \ + template < class T > \ + using t = decltype( f< T >( 0 ) ); \ + }; \ + } \ + } \ + template < class T > \ + using has_##m = Calamares::Traits::has_##m ::t< T >; + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/UMask.h b/airootfs/usr/include/libcalamares/utils/UMask.h new file mode 100644 index 0000000..552445c --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/UMask.h @@ -0,0 +1,50 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ +#ifndef UTILS_UMASK_H +#define UTILS_UMASK_H + +#include "DllMacro.h" + +#include + +namespace Calamares +{ +/** @brief Wrapper for umask(2) + * + * Like umask(2), sets the umask and returns the previous value of the mask. + */ +DLLEXPORT mode_t setUMask( mode_t u ); + +/** @brief RAII for setting and re-setting umask. + * + * Create an object of this class to set the umask, + * and the umask is reset to its original value when + * the object goes out of scope. + */ +class DLLEXPORT UMask +{ +public: + UMask( mode_t u ); + ~UMask(); + + /** @brief a "safe" umask + * + * This umask will switch off group- and other- permissions for + * files, so that the file cannot be read, written, or executed + * except by the owner. + */ + static constexpr mode_t Safe = 077; // octal! +private: + mode_t m_mode; +}; +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Units.h b/airootfs/usr/include/libcalamares/utils/Units.h new file mode 100644 index 0000000..824679b --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Units.h @@ -0,0 +1,181 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-FileCopyrightText: 2019 Collabora Ltd + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_UNITS_H +#define UTILS_UNITS_H + +#include + +namespace Calamares +{ +/// @brief Type for expressing units +using intunit_t = quint64; + +namespace Units +{ + +/** User defined literals, 1_KB is 1 KiloByte (= 10^3 bytes) */ +constexpr qint64 +operator""_KB( unsigned long long m ) +{ + return qint64( m ) * 1000; +} + +/** User defined literals, 1_KiB is 1 KibiByte (= 2^10 bytes) */ +constexpr qint64 +operator""_KiB( unsigned long long m ) +{ + return qint64( m ) * 1024; +} + +/** User defined literals, 1_MB is 1 MegaByte (= 10^6 bytes) */ +constexpr qint64 +operator""_MB( unsigned long long m ) +{ + return operator""_KB( m ) * 1000; +} + +/** User defined literals, 1_MiB is 1 MibiByte (= 2^20 bytes) */ +constexpr qint64 +operator""_MiB( unsigned long long m ) +{ + return operator""_KiB( m ) * 1024; +} + +/** User defined literals, 1_GB is 1 GigaByte (= 10^9 bytes) */ +constexpr qint64 +operator""_GB( unsigned long long m ) +{ + return operator""_MB( m ) * 1000; +} + +/** User defined literals, 1_GiB is 1 GibiByte (= 2^30 bytes) */ +constexpr qint64 +operator""_GiB( unsigned long long m ) +{ + return operator""_MiB( m ) * 1024; +} + +} // namespace Units + +constexpr qint64 +KBtoBytes( unsigned long long m ) +{ + return Units::operator""_KB( m ); +} + +constexpr qint64 +KiBtoBytes( unsigned long long m ) +{ + return Units::operator""_KiB( m ); +} + +constexpr qint64 +MBtoBytes( unsigned long long m ) +{ + return Units::operator""_MB( m ); +} + +constexpr qint64 +MiBtoBytes( unsigned long long m ) +{ + return Units::operator""_MiB( m ); +} + +constexpr qint64 +GBtoBytes( unsigned long long m ) +{ + return Units::operator""_GB( m ); +} + +constexpr qint64 +GiBtoBytes( unsigned long long m ) +{ + return Units::operator""_GiB( m ); +} + +constexpr qint64 +KBtoBytes( double m ) +{ + return qint64( m * 1000 ); +} + +constexpr qint64 +KiBtoBytes( double m ) +{ + return qint64( m * 1024 ); +} + +constexpr qint64 +MBtoBytes( double m ) +{ + return qint64( m * 1000 * 1000 ); +} + +constexpr qint64 +MiBtoBytes( double m ) +{ + return qint64( m * 1024 * 1024 ); +} + +constexpr qint64 +GBtoBytes( double m ) +{ + return qint64( m * 1000 * 1000 * 1000 ); +} + +constexpr qint64 +GiBtoBytes( double m ) +{ + return qint64( m * 1024 * 1024 * 1024 ); +} + +constexpr int +BytesToMiB( qint64 b ) +{ + return int( b / 1024 / 1024 ); +} + +// TODO: deprecate signed version +constexpr int +BytesToGiB( qint64 b ) +{ + return int( b / 1024 / 1024 / 1024 ); +} + +constexpr intunit_t +BytesToGiB( intunit_t b ) +{ + return b / 1024 / 1024 / 1024; +} + +constexpr qint64 +alignBytesToBlockSize( qint64 bytes, qint64 blocksize ) +{ + qint64 blocks = bytes / blocksize; + + if ( blocks * blocksize != bytes ) + { + ++blocks; + } + return blocks * blocksize; +} + +constexpr qint64 +bytesToSectors( qint64 bytes, qint64 blocksize ) +{ + return alignBytesToBlockSize( alignBytesToBlockSize( bytes, blocksize ), MiBtoBytes( 1ULL ) ) / blocksize; +} + +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Variant.h b/airootfs/usr/include/libcalamares/utils/Variant.h new file mode 100644 index 0000000..1c1784f --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Variant.h @@ -0,0 +1,78 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2013-2016 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +#ifndef UTILS_VARIANT_H +#define UTILS_VARIANT_H + +#include "DllMacro.h" + +#include +#include +#include + +namespace Calamares +{ +/** + * Get a bool value from a mapping with a given key; returns @p d if no value. + */ +DLLEXPORT bool getBool( const QVariantMap& map, const QString& key, bool d = false ); + +/** @brief Get a string value from a mapping with a given key; returns @p d if no value. + * + * The value must be an actual string; numbers are not automatically converted to strings, + * nor are lists flattened or converted. + */ +DLLEXPORT QString getString( const QVariantMap& map, const QString& key, const QString& d = QString() ); + +/** @brief Get a string list from a mapping with a given key; returns @p d if no value. + * + * This is slightly more lenient than getString(), and a single-string value will + * be returned as a 1-item list. + */ +DLLEXPORT QStringList getStringList( const QVariantMap& map, const QString& key, const QStringList& d = QStringList() ); + +/** + * Get a list from a mapping with a given key; returns @p d if no value. + */ +DLLEXPORT QList< QVariant > +getList( const QVariantMap& map, const QString& key, const QList< QVariant >& d = QList< QVariant >() ); + +/** + * Get an integer value from a mapping with a given key; returns @p d if no value. + */ +DLLEXPORT qint64 getInteger( const QVariantMap& map, const QString& key, qint64 d = 0 ); + +/** + * Get an unsigned integer value from a mapping with a given key; returns @p d if no value. + */ +DLLEXPORT quint64 getUnsignedInteger( const QVariantMap& map, const QString& key, quint64 d = 0 ); + +/** + * Get a double value from a mapping with a given key (integers are converted); returns @p d if no value. + */ +DLLEXPORT double getDouble( const QVariantMap& map, const QString& key, double d = 0.0 ); + +/** + * Returns a sub-map (i.e. a nested map) from a given mapping with a + * given key. @p success is set to true if the @p key exists + * in @p map and converts to a map, false otherwise. + * + * Returns @p d if there is no such key or it is not a map-value. + * (e.g. if @p success is false). + */ +DLLEXPORT QVariantMap getSubMap( const QVariantMap& map, + const QString& key, + bool& success, + const QVariantMap& d = QVariantMap() ); +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/Yaml.h b/airootfs/usr/include/libcalamares/utils/Yaml.h new file mode 100644 index 0000000..b39db68 --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/Yaml.h @@ -0,0 +1,85 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* + * YAML conversions and YAML convenience header. + * + * Includes the system YAMLCPP headers without warnings (by switching off + * the expected warnings) and provides a handful of methods for + * converting between YAML and QVariant. + */ +#ifndef UTILS_YAML_H +#define UTILS_YAML_H + +#include "DllMacro.h" + +#include +#include +#include +#include + +class QByteArray; +class QFileInfo; + +// The yaml-cpp headers are not C++11 warning-proof, especially +// with picky compilers like Clang 8. Since we use Clang for the +// find-all-the-warnings case, switch those warnings off for +// the we-can't-change-them system headers. +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG( "-Wzero-as-null-pointer-constant" ) +QT_WARNING_DISABLE_CLANG( "-Wshadow" ) +QT_WARNING_DISABLE_CLANG( "-Wfloat-equal" ) +QT_WARNING_DISABLE_CLANG( "-Wsuggest-destructor-override" ) + +#include + +QT_WARNING_POP + +/// @brief Appends all the elements of @p node to the string list @p v +DLLEXPORT void operator>>( const ::YAML::Node& node, QStringList& v ); + +namespace Calamares +{ +namespace YAML +{ +/** + * Loads a given @p filename and returns the YAML data + * as a QVariantMap. If filename doesn't exist, or is + * malformed in some way, returns an empty map and sets + * @p *ok to false. Otherwise sets @p *ok to true. + */ +DLLEXPORT QVariantMap load( const QString& filename, bool* ok = nullptr ); +/** Convenience overload. */ +DLLEXPORT QVariantMap load( const QFileInfo&, bool* ok = nullptr ); + +DLLEXPORT QVariant toVariant( const ::YAML::Node& node ); +DLLEXPORT QVariant scalarToVariant( const ::YAML::Node& scalarNode ); +DLLEXPORT QVariantList sequenceToVariant( const ::YAML::Node& sequenceNode ); +DLLEXPORT QVariantMap mapToVariant( const ::YAML::Node& mapNode ); + +/// @brief Returns all the elements of @p listNode in a StringList +DLLEXPORT QStringList toStringList( const ::YAML::Node& listNode ); + +/// @brief Save a @p map to @p filename as YAML +DLLEXPORT bool save( const QString& filename, const QVariantMap& map ); + +/** + * Given an exception from the YAML parser library, explain + * what is going on in terms of the data passed to the parser. + * Uses @p label when labeling the data source (e.g. "netinstall data") + */ +DLLEXPORT void explainException( const ::YAML::Exception& e, const QByteArray& data, const char* label ); +DLLEXPORT void explainException( const ::YAML::Exception& e, const QByteArray& data, const QString& label ); +DLLEXPORT void explainException( const ::YAML::Exception& e, const QByteArray& data ); + +} // namespace YAML +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/utils/moc-warnings.h b/airootfs/usr/include/libcalamares/utils/moc-warnings.h new file mode 100644 index 0000000..bbe382a --- /dev/null +++ b/airootfs/usr/include/libcalamares/utils/moc-warnings.h @@ -0,0 +1,35 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * + * Calamares is Free Software: see the License-Identifier above. + * + * + */ + +/* @file Turn off warnings on MOC-generated code + * + * This header file exists **only** to reduce warnings during compilation. + * Code generated by Qt's MOC, in combination with Clang (version 6 or later, + * I'm fairly sure) and the plenty-of-warnings settings that Calamares uses, + * triggers tons of warnings. Since those warnings are not something we + * can do anything about, turn them off by `#include`ing this header + * before a MOC file. + * + * Note that not many files in Calamares use MOC directly: mostly CMake's + * automoc does all the work for us. + */ +#ifdef __clang__ +#include +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG( "-Wextra-semi-stmt" ) +QT_WARNING_DISABLE_CLANG( "-Wredundant-parens" ) +QT_WARNING_DISABLE_CLANG( "-Wreserved-identifier" ) + +#if __clang_major__ >= 17 +QT_WARNING_DISABLE_CLANG( "-Wunsafe-buffer-usage" ) +#endif +QT_WARNING_POP +#endif diff --git a/airootfs/usr/include/libcalamares/viewpages/BlankViewStep.h b/airootfs/usr/include/libcalamares/viewpages/BlankViewStep.h new file mode 100644 index 0000000..1845fcd --- /dev/null +++ b/airootfs/usr/include/libcalamares/viewpages/BlankViewStep.h @@ -0,0 +1,54 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef BLANKVIEWSTEP_H +#define BLANKVIEWSTEP_H + +#include "viewpages/ViewStep.h" + +namespace Calamares +{ + +/** @brief A "blank" view step, used for error and status reporting + * + * This view step never allows navigation (forward or back); it's a trap. + * It displays a title and explanation, and optional details. + */ +class BlankViewStep : public Calamares::ViewStep +{ + Q_OBJECT + +public: + explicit BlankViewStep( const QString& title, + const QString& description, + const QString& details = QString(), + QObject* parent = nullptr ); + ~BlankViewStep() override; + + QString prettyName() const override; + + QWidget* widget() override; + + void next() override; + void back() override; + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + + bool isAtBeginning() const override; + bool isAtEnd() const override; + + Calamares::JobList jobs() const override; + +private: + QWidget* m_widget; +}; + +} // namespace Calamares +#endif // BLANKVIEWSTEP_H diff --git a/airootfs/usr/include/libcalamares/viewpages/ExecutionViewStep.h b/airootfs/usr/include/libcalamares/viewpages/ExecutionViewStep.h new file mode 100644 index 0000000..df52e55 --- /dev/null +++ b/airootfs/usr/include/libcalamares/viewpages/ExecutionViewStep.h @@ -0,0 +1,80 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Aurélien Gâteau + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef EXECUTIONVIEWSTEP_H +#define EXECUTIONVIEWSTEP_H + +#include "ViewStep.h" +#include "modulesystem/InstanceKey.h" +#include "widgets/LogWidget.h" + +#include + +class QLabel; +class QObject; +class QProgressBar; +class QTabWidget; + +namespace Calamares +{ + +class Slideshow; + +/** + * @class + * + * This is the implementation of the special ViewStep "Install" + * which takes care of an *exec* phase in the sequence. It runs + * jobs, shows the slideshow, etc. + */ +class UIDLLEXPORT ExecutionViewStep : public ViewStep +{ + Q_OBJECT +public: + explicit ExecutionViewStep( QObject* parent = nullptr ); + + QString prettyName() const override; + + QWidget* widget() override; + + void next() override; + void back() override; + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + + bool isAtBeginning() const override; + bool isAtEnd() const override; + + void onActivate() override; + void onLeave() override; + + JobList jobs() const override; + + void appendJobModuleInstanceKey( const ModuleSystem::InstanceKey& instanceKey ); + +private: + QWidget* m_widget; + QProgressBar* m_progressBar; + QLabel* m_label; + Slideshow* m_slideshow; + QTabWidget* m_tab_widget; + LogWidget* m_log_widget; + + QList< ModuleSystem::InstanceKey > m_jobInstanceKeys; + + void updateFromJobQueue( qreal percent, const QString& message ); + + void toggleLog(); +}; + +} // namespace Calamares + +#endif /* EXECUTIONVIEWSTEP_H */ diff --git a/airootfs/usr/include/libcalamares/viewpages/QmlViewStep.h b/airootfs/usr/include/libcalamares/viewpages/QmlViewStep.h new file mode 100644 index 0000000..0d9f6cf --- /dev/null +++ b/airootfs/usr/include/libcalamares/viewpages/QmlViewStep.h @@ -0,0 +1,127 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef QMLVIEWSTEP_H +#define QMLVIEWSTEP_H + +#include "DllMacro.h" +#include "utils/Qml.h" +#include "viewpages/ViewStep.h" + +class QQmlComponent; +class QQmlEngine; +class QQuickItem; +class QQuickWidget; +class WaitingWidget; + +namespace Calamares +{ + +/** @brief A viewstep that uses QML for the UI + * + * This is generally a **base** class for other view steps, but + * it can be used stand-alone for viewsteps that don't really have + * any functionality. + * + * Most subclasses will override the following methods: + * - prettyName() to provide a meaningful human-readable name + * - jobs() if there is real work to be done during installation + * - getConfig() to return a meaningful configuration object + * + * For details on the interaction between the config object and + * the QML in the module, see the module documentation: + * src/modules/README.md + */ +class UIDLLEXPORT QmlViewStep : public Calamares::ViewStep +{ + Q_OBJECT +public: + /** @brief Creates a QML view step + * + * The search behavior for the actial QML depends on a QmlSearch value. + * This is set through common configuration key *qmlSearch*. + * The filename used comes from the module identifier, or can be + * set in the configuration file through *qmlFilename*. + * + * @see Qml.h for available Calamares internals. + */ + QmlViewStep( QObject* parent = nullptr ); + ~QmlViewStep() override; + + virtual QString prettyName() const override; + + virtual QWidget* widget() override; + virtual QSize widgetMargins( Qt::Orientations panelSides ) override; + + virtual bool isNextEnabled() const override; + virtual bool isBackEnabled() const override; + + virtual bool isAtBeginning() const override; + virtual bool isAtEnd() const override; + + virtual void onActivate() override; + virtual void onLeave() override; + + /// @brief QML widgets don't produce jobs by default + virtual JobList jobs() const override; + + /// @brief Configure search paths; subclasses should call this at the **end** of their own implementation + virtual void setConfigurationMap( const QVariantMap& configurationMap ) override; + +protected: + /** @brief Gets a pointer to the Config of this view step + * + * Parts of the configuration of the viewstep can be passed to QML + * by placing them in a QObject (as properties). The default + * implementation returns nullptr, for no-config. + * + * Ownership of the config object remains with the ViewStep; it is possible + * to return a pointer to a member variable. + * + * This object is made available as a context-property *config* in QML. + */ + virtual QObject* getConfig(); + + /** @brief Adds a context property for this QML file + * + * Does not take ownership. + */ + void setContextProperty( const char* name, QObject* property ); + +private Q_SLOTS: + void loadComplete(); + +private: + /// @brief Swap out the spinner for the QQuickWidget + void showQml(); + /// @brief Show error message in spinner. + void showFailedQml(); + + /// @brief Controls where m_name is searched + Calamares::QmlSearch m_searchMethod; + + QString m_name; + QString m_qmlFileName; + + QWidget* m_widget = nullptr; + WaitingWidget* m_spinner = nullptr; + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QQuickWidget* m_qmlWidget = nullptr; +#else + QWidget* m_qmlWidget = nullptr; // Qt6: container for QQuickWindow +#endif + + QQmlEngine* m_qmlEngine = nullptr; // Qt5: points to QuickWidget engine, Qt6: separate engine + QQmlComponent* m_qmlComponent = nullptr; + QQuickItem* m_qmlObject = nullptr; +}; + +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/viewpages/Slideshow.h b/airootfs/usr/include/libcalamares/viewpages/Slideshow.h new file mode 100644 index 0000000..41dacd4 --- /dev/null +++ b/airootfs/usr/include/libcalamares/viewpages/Slideshow.h @@ -0,0 +1,142 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Aurélien Gâteau + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARESUI_SLIDESHOW_H +#define LIBCALAMARESUI_SLIDESHOW_H + +#include "CalamaresConfig.h" + +#include +#include +#include + +class QLabel; +class QTimer; +#ifdef WITH_QML +class QQmlComponent; +class QQuickItem; +class QQuickWidget; +#endif + +namespace Calamares +{ + +/** @brief API for Slideshow objects + * + * A Slideshow (subclass) object is created by the ExecutionViewStep + * and needs to manage its own configuration (e.g. from Branding). + * The slideshow is started and stopped when it becomes visible + * and when installation is over, by calling changeSlideShowState() + * as appropriate. + */ +class Slideshow : public QObject +{ + Q_OBJECT +public: + /// @brief State-change of the slideshow, for changeSlideShowState() + enum Action + { + Start, + Stop + }; + + Slideshow( QWidget* parent = nullptr ) + : QObject( parent ) + { + } + ~Slideshow() override; + + /// @brief Is the slideshow being shown **right now**? + bool isActive() const { return m_state == Start; } + + /** @brief The actual widget to show the user. + * + * Depending on the style of slideshow, this might be a QQuickWidget, + * or a QLabel, or something else entirely. + */ + virtual QWidget* widget() = 0; + + /** @brief Tells the slideshow we activated or left the show. + * + * If @p state is @c Slideshow::Start, calls suitable activation procedures. + * If @p state is @c Slideshow::Stop, calls deactivation procedures. + */ + virtual void changeSlideShowState( Action a ) = 0; + +protected: + QMutex m_mutex; + Action m_state = Stop; +}; + +#ifdef WITH_QML +/** @brief Slideshow using a QML file + * + * This is the "classic" slideshow in Calamares, which runs some QML + * while the installation is in progress. It is configured through + * Branding settings *slideshow* and *slideshowAPI*, showing the QML + * file from *slideshow*. The API version influences when and how the + * QML is loaded; version 1 does so only when the slideshow is activated, + * while version 2 does so asynchronously. + */ +class SlideshowQML : public Slideshow +{ + Q_OBJECT +public: + SlideshowQML( QWidget* parent ); + ~SlideshowQML() override; + + QWidget* widget() override; + void changeSlideShowState( Action a ) override; + +public slots: + void loadQmlV2Complete(); + void loadQmlV2(); ///< Loads the slideshow QML (from branding) for API version 2 + + /// Implementation detail + void startSlideShow(); + +private: + QQuickWidget* m_qmlShow; + QQmlComponent* m_qmlComponent; + QQuickItem* m_qmlObject; ///< The actual show +}; +#endif + +/** @brief Slideshow using images + * + * This is an "oldschool" slideshow, but new in Calamares, which + * displays static image files one-by-one. It is for systems that + * do not use QML at all. It is configured through the Branding + * setting *slideshow*. When using this widget, the setting must + * be a list of filenames; the API is set to -1. + */ +class SlideshowPictures : public Slideshow +{ + Q_OBJECT +public: + SlideshowPictures( QWidget* parent ); + ~SlideshowPictures() override; + + QWidget* widget() override; + virtual void changeSlideShowState( Action a ) override; + +public slots: + void next(); + +private: + QLabel* m_label; + QTimer* m_timer; + int m_imageIndex; + QStringList m_images; +}; + +} // namespace Calamares +#endif diff --git a/airootfs/usr/include/libcalamares/viewpages/ViewStep.h b/airootfs/usr/include/libcalamares/viewpages/ViewStep.h new file mode 100644 index 0000000..1a52ef6 --- /dev/null +++ b/airootfs/usr/include/libcalamares/viewpages/ViewStep.h @@ -0,0 +1,199 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef VIEWSTEP_H +#define VIEWSTEP_H + +#include "DllMacro.h" +#include "Job.h" + +#include "modulesystem/InstanceKey.h" +#include "modulesystem/Requirement.h" + +#include +#include +#include + +namespace Calamares +{ + +/** + * @brief The ViewStep class is the base class for all view modules. + * A view module is a Calamares module which has at least one UI page (exposed as + * ViewStep::widget), and can optionally create Calamares jobs at runtime. + * As of early 2020, a view module can be implemented by deriving from ViewStep + * in C++ (as a Qt Plugin or a Qml ViewStep). + * + * A ViewStep can describe itself in human-readable format for the SummaryPage + * (which shows all of the things which have been collected to be done in the + * next exec-step) through prettyStatus() and createSummaryWidget(). + */ +class UIDLLEXPORT ViewStep : public QObject +{ + Q_OBJECT +public: + explicit ViewStep( QObject* parent = nullptr ); + ~ViewStep() override; + + /** @brief Human-readable name of the step + * + * This (translated) string is shown in the sidebar (progress) + * and during installation. There is no default. + */ + virtual QString prettyName() const = 0; + + /** @brief Describe what this step will do during install + * + * Optional. May return a non-empty string describing what this + * step is going to do (should be translated). This is also used + * in the summary page to describe what is going to be done. + * Return an empty string to provide no description. + * + * The default implementation returns an empty string, so nothing + * will be displayed for this step when a summary is shown. + */ + virtual QString prettyStatus() const; + + /** @brief Return a long description what this step will do during install + * + * Optional. May return a widget which will be inserted in the summary + * page. The caller takes ownership of the widget. Return nullptr to + * provide no widget. In general, this is only used for complicated + * steps where prettyStatus() is not sufficient. + * + * The default implementation returns nullptr, so nothing + * will be displayed for this step when a summary is shown. + */ + virtual QWidget* createSummaryWidget() const; + + /** @brief Get (or create) the widget for this view step + * + * While a view step **may** create the widget when it is loaded, + * it is recommended to wait with widget creation until the + * widget is actually asked for: a view step **may** be used + * without a UI. + */ + virtual QWidget* widget() = 0; + + /** @brief Get margins for this widget + * + * This is called by the layout manager to find the desired + * margins (width is used for left and right margin, height is + * used for top and bottom margins) for the widget. The + * @p panelSides indicates where there are panels in the overall + * layout: horizontally and / or vertically adjacent (or none!) + * to the view step's widget. + * + * Should return a size based also on QStyle metrics for layout. + * The default implementation just returns the default layout metrics + * (often 11 pixels on a side). + */ + virtual QSize widgetMargins( Qt::Orientations panelSides ); + + /** + * @brief Multi-page support, go next + * + * Multi-page view steps need to manage the content visible in the widget + * themselves. This method is called when the user clicks the *next* + * button, and should switch to the next of the multiple-pages. It needs + * to be consistent with both isNextEnabled() and isAtEnd(). + * + * In particular: when isAtEnd() returns false, next() is called when + * the user clicks the button and a new page should be shown by this + * view step. When isAtEnd() returns true, clicking the button will + * switch to the next view step in sequence, rather than a next page + * in the current view step. + */ + virtual void next(); + /// @brief Multi-page support, go back + virtual void back(); + + /// @brief Can the user click *next* with currently-filled-in data? + virtual bool isNextEnabled() const = 0; + /// @brief Can the user click *previous* with currently-filled-in data? + virtual bool isBackEnabled() const = 0; + + /** + * @brief Multi-page support, switch to previous view step? + * + * For a multi-page view step, this indicates that the first (beginning) + * page is showing. Clicking *previous* when at the beginning of a view + * step, switches to the previous step, not the previous page of the + * current view step. + */ + virtual bool isAtBeginning() const = 0; + /// @brief Multi-page support, switch to next view step? + virtual bool isAtEnd() const = 0; + + /** + * @brief onActivate called every time a ViewStep is shown, either by going forward + * or backward. + * The default implementation does nothing. + */ + virtual void onActivate(); + + /** + * @brief onLeave called every time a ViewStep is hidden and control passes to + * another ViewStep, either by going forward or backward. + * The default implementation does nothing. + */ + virtual void onLeave(); + + /** + * @brief Jobs needed to run this viewstep + * + * When a ViewStep is listed in the exec section, its jobs are executed instead. + * This function returns that list of jobs; an empty list is ok. + */ + virtual JobList jobs() const = 0; + + void setModuleInstanceKey( const Calamares::ModuleSystem::InstanceKey& instanceKey ); + Calamares::ModuleSystem::InstanceKey moduleInstanceKey() const { return m_instanceKey; } + + virtual void setConfigurationMap( const QVariantMap& configurationMap ); + + /** + * @brief Can this module proceed, on this machine? + * + * This is called asynchronously at startup, and returns a list of + * the requirements that the module has checked, and their status. + * See Calamares::RequirementEntry for details. + */ + virtual RequirementsList checkRequirements(); + + /** + * @brief Called when the user cancels the installation + * + * View steps are expected to leave the system unchanged when + * the installation is cancelled. The default implementation + * does nothing. + */ + virtual void onCancel(); + +signals: + /// @brief Tells the viewmanager to enable the *next* button according to @p status + void nextStatusChanged( bool status ); + + /* Emitted when the viewstep thinks it needs more space than is currently + * available for display. @p size is the requested space, that is needed + * to display the entire page. + * + * This request may be silently ignored. + */ + void ensureSize( QSize enlarge ) const; + +protected: + Calamares::ModuleSystem::InstanceKey m_instanceKey; +}; + +using ViewStepList = QList< ViewStep* >; +} // namespace Calamares + +#endif // VIEWSTEP_H diff --git a/airootfs/usr/include/libcalamares/widgets/ClickableLabel.h b/airootfs/usr/include/libcalamares/widgets/ClickableLabel.h new file mode 100644 index 0000000..259f4a7 --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/ClickableLabel.h @@ -0,0 +1,51 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARESUI_WIDGETS_CLICKABLELABEL_H +#define LIBCALAMARESUI_WIDGETS_CLICKABLELABEL_H + +#include +#include + +#include "DllMacro.h" + +namespace Calamares +{ +namespace Widgets +{ +/** @brief A Label where the whole label area is clickable + * + * When clicking anywhere on the Label (text, background, whatever) + * the signal clicked() is emitted. Use this as a buddy for radio + * buttons or other clickable things where you want mouse interaction + * with the label, to be the same as mouse interaction with the control. + */ +class UIDLLEXPORT ClickableLabel : public QLabel +{ + Q_OBJECT +public: + explicit ClickableLabel( QWidget* parent = nullptr ); + explicit ClickableLabel( const QString& text, QWidget* parent = nullptr ); + ~ClickableLabel() override; + +signals: + void clicked(); + +protected: + virtual void mousePressEvent( QMouseEvent* event ) override; + virtual void mouseReleaseEvent( QMouseEvent* event ) override; + +private: + QElapsedTimer m_time; +}; +} // namespace Widgets +} // namespace Calamares + +#endif // LIBCALAMARESUI_WIDGETS_CLICKABLELABEL_H diff --git a/airootfs/usr/include/libcalamares/widgets/ErrorDialog.h b/airootfs/usr/include/libcalamares/widgets/ErrorDialog.h new file mode 100644 index 0000000..28ca70f --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/ErrorDialog.h @@ -0,0 +1,83 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Artem Grinev + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARESUI_ERRORDIALOG_H +#define LIBCALAMARESUI_ERRORDIALOG_H + +#include + +namespace Ui +{ +class ErrorDialog; +} // namespace Ui + +namespace Calamares +{ +class ErrorDialog : public QDialog +{ + Q_OBJECT + + Q_PROPERTY( QString heading READ heading WRITE setHeading NOTIFY headingChanged ) + Q_PROPERTY( QString informativeText READ informativeText WRITE setInformativeText NOTIFY informativeTextChanged ) + Q_PROPERTY( QString details READ details WRITE setDetails NOTIFY detailsChanged ) + Q_PROPERTY( bool shouldOfferWebPaste READ shouldOfferWebPaste WRITE setShouldOfferWebPaste NOTIFY + shouldOfferWebPasteChanged ) + +public: + explicit ErrorDialog( QWidget* parent = nullptr ); + ~ErrorDialog() override; + + /** @brief The heading (title) of the error dialog + * + * This is a short (one-line) title. It is human-readable, so should + * be translated at the time it is set. + */ + QString heading() const; + void setHeading( const QString& newHeading ); + + /** @brief The description of the problem + * + * Longer, human-readable, description of the problem. This text + * is word-wrapped as necessary. + */ + QString informativeText() const; + void setInformativeText( const QString& newInformativeText ); + + /** @brief Details of the problem + * + * This is generally command-output; it might not be translated + * when set. It should be considered "background to the informative + * text", or maybe "the reasons". Write the informative text for + * the end-user. + */ + QString details() const; + void setDetails( const QString& newDetails ); + + /** @brief Enable web-paste button + * + * The web-paste button can be configured at a global level, + * but each individual error dialog can be set separately. + */ + bool shouldOfferWebPaste() const; + void setShouldOfferWebPaste( bool newShouldOfferWebPaste ); + +signals: + void headingChanged(); + void informativeTextChanged(); + void detailsChanged(); + void shouldOfferWebPasteChanged(); + +private: + Ui::ErrorDialog* ui; + bool m_shouldOfferWebPaste = false; +}; + +} // namespace Calamares + +#endif // LIBCALAMARESUI_ERRORDIALOG_H diff --git a/airootfs/usr/include/libcalamares/widgets/FixedAspectRatioLabel.h b/airootfs/usr/include/libcalamares/widgets/FixedAspectRatioLabel.h new file mode 100644 index 0000000..58d0956 --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/FixedAspectRatioLabel.h @@ -0,0 +1,34 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef FIXEDASPECTRATIOLABEL_H +#define FIXEDASPECTRATIOLABEL_H + +#include "DllMacro.h" + +#include +#include + +class UIDLLEXPORT FixedAspectRatioLabel : public QLabel +{ + Q_OBJECT +public: + explicit FixedAspectRatioLabel( QWidget* parent = nullptr ); + ~FixedAspectRatioLabel() override; + +public slots: + void setPixmap( const QPixmap& pixmap ); + void resizeEvent( QResizeEvent* event ) override; + +private: + QPixmap m_pixmap; +}; + +#endif // FIXEDASPECTRATIOLABEL_H diff --git a/airootfs/usr/include/libcalamares/widgets/LogWidget.h b/airootfs/usr/include/libcalamares/widgets/LogWidget.h new file mode 100644 index 0000000..5b3ef17 --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/LogWidget.h @@ -0,0 +1,55 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2022 Bob van der Linden + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARESUI_LOGWIDGET_H +#define LIBCALAMARESUI_LOGWIDGET_H + +#include +#include +#include + +namespace Calamares +{ + +class LogThread : public QThread +{ + Q_OBJECT + + void run() override; + +public: + explicit LogThread( QObject* parent = nullptr ); + ~LogThread() override; + +Q_SIGNALS: + void onLogChunk( const QString& logChunk ); +}; + +class LogWidget : public QWidget +{ + Q_OBJECT + + QPlainTextEdit* m_text; + LogThread m_log_thread; + +public: + explicit LogWidget( QWidget* parent = nullptr ); + +public Q_SLOTS: + /// @brief Called by the thread when there is new data + void handleLogChunk( const QString& logChunk ); + + /// @brief Stop watching for log data + void stop(); + /// @brief Start watching for new log data + void start(); +}; + +} // namespace Calamares +#endif // LOGWIDGET_H diff --git a/airootfs/usr/include/libcalamares/widgets/PrettyRadioButton.h b/airootfs/usr/include/libcalamares/widgets/PrettyRadioButton.h new file mode 100644 index 0000000..fd00911 --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/PrettyRadioButton.h @@ -0,0 +1,79 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARESUI_WIDGETS_PRETTYRADIOBUTTON_H +#define LIBCALAMARESUI_WIDGETS_PRETTYRADIOBUTTON_H + +#include "DllMacro.h" + +#include + +class QButtonGroup; +class QComboBox; +class QGridLayout; +class QHBoxLayout; + +namespace Calamares +{ +namespace Widgets +{ +class ClickableLabel; + +/** @brief A radio button with fancy label next to it. + * + * The fancy label is used so that the text alongside the radio + * button can word-wrap, be multi-line, and support rich text. + * + * The radio button itself can be retrieved with buttonWidget(), + * and the whole behaves a lot like a label. Extra options can be + * added to the display (options are hidden when the button is + * not selected) with addOptionsComboBox(). + */ +class UIDLLEXPORT PrettyRadioButton : public QWidget +{ + Q_OBJECT +public: + explicit PrettyRadioButton( QWidget* parent = nullptr ); + ~PrettyRadioButton() override {} + + /// @brief Passes @p text on to the ClickableLabel + void setText( const QString& text ); + + // Icon applies to the radio-button part + void setIconSize( const QSize& size ); + QSize iconSize() const; + void setIcon( const QIcon& icon ); + + // Applies to the radio-button part + void setChecked( bool checked ); + bool isChecked() const; + + /** @brief Adds the radio-button part to the given @p group + * + * For managing the pretty-radio-button in button groups like normal + * radio buttons, call addToGroup() rather that group->addButton(). + */ + void addToGroup( QButtonGroup* group, int id = -1 ); + + /// @brief Add an options drop-down to this button. + void addOptionsComboBox( QComboBox* ); + +protected slots: + /// Options are hidden when the radio button is off + void toggleOptions( bool checked ); + +protected: + ClickableLabel* m_label; + QRadioButton* m_radio; + QGridLayout* m_mainLayout; + QHBoxLayout* m_optionsLayout; +}; +} // namespace Widgets +} // namespace Calamares +#endif // LIBCALAMARESUI_WIDGETS_PRETTYRADIOBUTTON_H diff --git a/airootfs/usr/include/libcalamares/widgets/TranslationFix.h b/airootfs/usr/include/libcalamares/widgets/TranslationFix.h new file mode 100644 index 0000000..89ee9a5 --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/TranslationFix.h @@ -0,0 +1,34 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2021 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef LIBCALAMARESUI_WIDGETS_TRANSLATIONFIX_H +#define LIBCALAMARESUI_WIDGETS_TRANSLATIONFIX_H + +#include "DllMacro.h" + +class QMessageBox; +class QDialogButtonBox; + +namespace Calamares +{ + +/** @brief Fixes the labels on the standard buttons of the message box + * + * Updates OK / Cancel / Yes / No because there does not + * seem to be a way to do so in the Retranslator code + * (in libcalamares) since the translated strings may come + * from a variety of platform-plugin sources and we can't + * guess the context. + */ +void UIDLLEXPORT fixButtonLabels( QMessageBox* ); + +void UIDLLEXPORT fixButtonLabels( QDialogButtonBox* ); +} // namespace Calamares + +#endif diff --git a/airootfs/usr/include/libcalamares/widgets/WaitingWidget.h b/airootfs/usr/include/libcalamares/widgets/WaitingWidget.h new file mode 100644 index 0000000..312914e --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/WaitingWidget.h @@ -0,0 +1,78 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2014 Teo Mrnjavac + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef WAITINGWIDGET_H +#define WAITINGWIDGET_H + +#include "DllMacro.h" + +#include "widgets/waitingspinnerwidget.h" + +#include +#include + +class QLabel; +class QTimer; + +/** @brief A spinner and a label below it + * + * The spinner has a fixed size of 4* the font height, + * and the text is displayed centered below it. Use this + * to display a long-term waiting situation with a status report. + */ +class UIDLLEXPORT WaitingWidget : public WaitingSpinnerWidget +{ +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 + * + * The spinner is sized to the text-height and displays a + * numeric countdown iside the spinner. The countdown is updated + * every second. The signal timeout() is sent every time + * the countdown reaches 0. + */ +class UIDLLEXPORT CountdownWaitingWidget : public WaitingSpinnerWidget +{ + Q_OBJECT +public: + /// Create a countdown widget with a given @p duration + explicit CountdownWaitingWidget( std::chrono::seconds duration = std::chrono::seconds( 5 ), + QWidget* parent = nullptr ); + ~CountdownWaitingWidget() override; + + /// Changes the duration used and resets the countdown + void setInterval( std::chrono::seconds duration ); + + /// Start the countdown, resets to the full duration + void start(); + /// Stop the countdown + void stop(); + +Q_SIGNALS: + void timeout(); + +protected Q_SLOTS: + void tick(); + +protected: + void changeEvent( QEvent* event ) override; + +private: + struct Private; + std::unique_ptr< Private > d; +}; + +#endif // WAITINGWIDGET_H diff --git a/airootfs/usr/include/libcalamares/widgets/waitingspinnerwidget.h b/airootfs/usr/include/libcalamares/widgets/waitingspinnerwidget.h new file mode 100644 index 0000000..29f98af --- /dev/null +++ b/airootfs/usr/include/libcalamares/widgets/waitingspinnerwidget.h @@ -0,0 +1,162 @@ +/* + * SPDX-FileCopyrightText: 2012-2014 Alexander Turkin + * SPDX-FileCopyrightText: 2014 William Hallatt + * SPDX-FileCopyrightText: 2015 Jacob Dawid + * SPDX-FileCopyrightText: 2018 huxingyi + * SPDX-License-Identifier: MIT + */ + +/* Original Work Copyright (c) 2012-2014 Alexander Turkin + Modified 2014 by William Hallatt + Modified 2015 by Jacob Dawid + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#pragma once + +#include "DllMacro.h" + +#include +#include +#include + +class UIDLLEXPORT WaitingSpinnerWidget : public QWidget +{ + Q_OBJECT +public: + /** @brief Constructor for "standard" widget behaviour + * + * Use this constructor if you wish to, e.g. embed your widget in another. + */ + WaitingSpinnerWidget( QWidget* parent = nullptr, + bool centerOnParent = true, + bool disableParentWhenSpinning = true ); + + /** @brief Constructor + * + * Use this constructor to automatically create a modal + * ("blocking") spinner on top of the calling widget/window. If a valid + * parent widget is provided, "centreOnParent" will ensure that + * QtWaitingSpinner automatically centres itself on it, if not, + * @p centerOnParent is ignored. + */ + WaitingSpinnerWidget( Qt::WindowModality modality, + QWidget* parent = nullptr, + bool centerOnParent = true, + bool disableParentWhenSpinning = true ); + + WaitingSpinnerWidget( const WaitingSpinnerWidget& ) = delete; + WaitingSpinnerWidget& operator=( const WaitingSpinnerWidget& ) = delete; + + void setColor( QColor color ); + void setTextColor( QColor color ); + void setRoundness( qreal roundness ); + void setMinimumTrailOpacity( qreal minimumTrailOpacity ); + void setTrailFadePercentage( qreal trail ); + void setRevolutionsPerSecond( qreal revolutionsPerSecond ); + void setNumberOfLines( int lines ); + void setLineLength( int length ); + void setLineWidth( int width ); + void setInnerRadius( int radius ); + + /** @brief Sets the text displayed in or below the spinner + * + * If the text is empty, no text is displayed. The text is displayed + * in or below the spinner depending on the value of alignment(). + * With AlignBottom, the text is displayed below the spinner, + * centered horizontally relative to the spinner; any other alignment + * will put the text in the middle of the spinner itself. + * + * TODO: this does not support rich text. Rich text could be done + * through a QStaticText, or an HTML document. However, then + * we need to do more alignment calculations ourselves. + */ + void setText( const QString& text ); + /** @brief Sets the alignment of text for the spinner + * + * The only meaningful values are AlignBottom and AlignVCenter, + * for text below the spinner and text in the middle. + */ + void setAlignment( Qt::AlignmentFlag align ); + /// Convenience to set text-in-the-middle (@c true) or text-at-bottom (@c false) + void setCenteredText( bool centered ) + { + setAlignment( centered ? Qt::AlignmentFlag::AlignVCenter : Qt::AlignmentFlag::AlignBottom ); + } + + QColor color() const; + QColor textColor() const; + QString text() const; + Qt::AlignmentFlag alignment() const { return _alignment; } + qreal roundness() const; + qreal minimumTrailOpacity() const; + qreal trailFadePercentage() const; + qreal revolutionsPersSecond() const; + int numberOfLines() const; + int lineLength() const; + int lineWidth() const; + int innerRadius() const; + + bool isSpinning() const; + +public Q_SLOTS: + void start(); + void stop(); + +private Q_SLOTS: + void rotate(); + +protected: + void paintEvent( QPaintEvent* paintEvent ) override; + +private: + void updateSize(); + void updateTimer(); + void updatePosition(); + +private: + // PI, leading to a full fade in one whole revolution + static constexpr const auto radian = 3.14159265358979323846; + + // Spinner-wheel related settings + QColor _color = Qt::black; + qreal _roundness = 100.0; // 0..100 + qreal _minimumTrailOpacity = radian; + qreal _trailFadePercentage = 80.0; + qreal _revolutionsPerSecond = radian / 2; + int _numberOfLines = 20; + int _lineLength = 10; + int _lineWidth = 2; + int _innerRadius = 10; + QSize _imageSize; + + // Text-related settings + Qt::AlignmentFlag _alignment = Qt::AlignmentFlag::AlignBottom; + QString _text; + QColor _textColor = Qt::black; + + // Environment settings + bool _centerOnParent = true; + bool _disableParentWhenSpinning = true; + + // Internal bits + QTimer* _timer = nullptr; + int _currentCounter = 0; + bool _isSpinning = false; +}; diff --git a/airootfs/usr/lib/calamares/libcalamares.so b/airootfs/usr/lib/calamares/libcalamares.so new file mode 120000 index 0000000..dccc913 --- /dev/null +++ b/airootfs/usr/lib/calamares/libcalamares.so @@ -0,0 +1 @@ +../libcalamares.so.3.4.0 \ No newline at end of file diff --git a/airootfs/usr/lib/calamares/modules/bootloader/bootloader.schema.yaml b/airootfs/usr/lib/calamares/modules/bootloader/bootloader.schema.yaml new file mode 100644 index 0000000..878e761 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/bootloader/bootloader.schema.yaml @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/bootloader +additionalProperties: false +type: object +properties: + efiBootLoaderVar: { type: string } + efiBootLoader: { type: string } + kernelSearchPath: { type: string } + kernelName: { type: string } + kernelParams: { type: array, items: { type: string } } + kernelPattern: { type: string } + loaderEntries: { type: array, items: { type: string } } + refindKernelList: { type: array, items: { type: string } } + + # Programs + grubInstall: { type: string } + grubMkconfig: { type: string } + grubCfg: { type: string } + grubProbe: { type: string } + efiBootMgr: { type: string } + + efiBootloaderId: { type: string } + installEFIFallback: { type: boolean } + installHybridGRUB: { type: boolean } diff --git a/airootfs/usr/lib/calamares/modules/bootloader/main.py b/airootfs/usr/lib/calamares/modules/bootloader/main.py new file mode 100644 index 0000000..4ac067b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/bootloader/main.py @@ -0,0 +1,966 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Aurélien Gâteau +# SPDX-FileCopyrightText: 2014 Anke Boersma +# SPDX-FileCopyrightText: 2014 Daniel Hillenbrand +# SPDX-FileCopyrightText: 2014 Benjamin Vaudour +# SPDX-FileCopyrightText: 2014-2019 Kevin Kofler +# SPDX-FileCopyrightText: 2015-2018 Philip Mueller +# SPDX-FileCopyrightText: 2016-2017 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot +# SPDX-FileCopyrightText: 2017 Gabriel Craciunescu +# SPDX-FileCopyrightText: 2017 Ben Green +# SPDX-FileCopyrightText: 2021 Neal Gompa +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import fileinput +import os +import re +import shutil +import subprocess + +import libcalamares + +from libcalamares.utils import check_target_env_call, check_target_env_output + +import gettext + +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + +# This is the sanitizer used all over to tidy up filenames +# to make identifiers (or to clean up names to make filenames). +file_name_sanitizer = str.maketrans(" /()", "_-__") + + +def pretty_name(): + return _("Install bootloader.") + + +def get_uuid(): + """ + Checks and passes 'uuid' to other routine. + + :return: + """ + partitions = libcalamares.globalstorage.value("partitions") + + for partition in partitions: + if partition["mountPoint"] == "/": + libcalamares.utils.debug("Root partition uuid: \"{!s}\"".format(partition["uuid"])) + return partition["uuid"] + + return "" + + +def get_kernel_line(kernel_type): + """ + Passes 'kernel_line' to other routine based on configuration file. + + :param kernel_type: + :return: + """ + if kernel_type == "fallback": + if "fallbackKernelLine" in libcalamares.job.configuration: + return libcalamares.job.configuration["fallbackKernelLine"] + else: + return " (fallback)" + else: + if "kernelLine" in libcalamares.job.configuration: + return libcalamares.job.configuration["kernelLine"] + else: + return "" + + +def get_zfs_root(): + """ + Looks in global storage to find the zfs root + + :return: A string containing the path to the zfs root or None if it is not found + """ + + zfs = libcalamares.globalstorage.value("zfsDatasets") + + if not zfs: + libcalamares.utils.warning("Failed to locate zfs dataset list") + return None + + # Find the root dataset + for dataset in zfs: + try: + if dataset["mountpoint"] == "/": + return dataset["zpool"] + "/" + dataset["dsName"] + except KeyError: + # This should be impossible + libcalamares.utils.warning("Internal error handling zfs dataset") + raise + + return None + + +def is_btrfs_root(partition): + """ Returns True if the partition object refers to a btrfs root filesystem + + :param partition: A partition map from global storage + :return: True if btrfs and root, False otherwise + """ + return partition["mountPoint"] == "/" and partition["fs"] == "btrfs" + + +def is_zfs_root(partition): + """ Returns True if the partition object refers to a zfs root filesystem + + :param partition: A partition map from global storage + :return: True if zfs and root, False otherwise + """ + 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): + # Configured kernel parameters (default "quiet"), if plymouth installed, add splash + # screen parameter and then "rw". + kernel_params = libcalamares.job.configuration.get("kernelParams", ["quiet"]) + if have_program_in_target("plymouth"): + kernel_params.append("splash") + 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") + + cryptdevice_params = [] + swap_uuid = "" + swap_outer_mappername = None + swap_outer_uuid = None + + # Take over swap settings: + # - unencrypted swap partition sets swap_uuid + # - encrypted root sets cryptdevice_params + for partition in partitions: + if partition["fs"] == "linuxswap" and not partition.get("claimed", None): + # Skip foreign swap + continue + has_luks = "luksMapperName" in partition + if partition["fs"] == "linuxswap" and not has_luks: + swap_uuid = partition["uuid"] + + if partition["fs"] == "linuxswap" and has_luks: + swap_outer_mappername = partition["luksMapperName"] + swap_outer_uuid = partition["luksUuid"] + + if partition["mountPoint"] == "/" and has_luks: + if use_systemd_naming: + cryptdevice_params = [f"rd.luks.uuid={partition['luksUuid']}"] + else: + cryptdevice_params = [f"cryptdevice=UUID={partition['luksUuid']}:{partition['luksMapperName']}"] + cryptdevice_params.append(f"root=/dev/mapper/{partition['luksMapperName']}") + + # btrfs and zfs handling + for partition in partitions: + # If a btrfs root subvolume wasn't set, it means the root is directly on the partition + # and this option isn't needed + if is_btrfs_root(partition): + btrfs_root_subvolume = libcalamares.globalstorage.value("btrfsRootSubvolume") + if btrfs_root_subvolume: + kernel_params.append("rootflags=subvol=" + btrfs_root_subvolume) + + # zfs needs to be told the location of the root dataset + if is_zfs_root(partition): + zfs_root_path = get_zfs_root() + if zfs_root_path is not None: + kernel_params.append("root=ZFS=" + zfs_root_path) + else: + # Something is really broken if we get to this point + libcalamares.utils.warning("Internal error handling zfs dataset") + raise Exception("Internal zfs data missing, please contact your distribution") + + if cryptdevice_params: + kernel_params.extend(cryptdevice_params) + else: + kernel_params.append("root=UUID={!s}".format(uuid)) + + if swap_uuid: + kernel_params.append("resume=UUID={!s}".format(swap_uuid)) + + if use_systemd_naming and swap_outer_uuid: + kernel_params.append(f"rd.luks.uuid={swap_outer_uuid}") + + if swap_outer_mappername: + kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}") + + return kernel_params + + +def create_systemd_boot_conf(installation_root_path, efi_dir, uuid, kernel, kernel_version): + """ + Creates systemd-boot configuration files based on given parameters. + + :param installation_root_path: A string containing the absolute path to the root of the installation + :param efi_dir: A string containing the path to the efi dir relative to the root of the installation + :param uuid: A string containing the UUID of the root volume + :param kernel: A string containing the path to the kernel relative to the root of the installation + :param kernel_version: The kernel version string + """ + + # Get the kernel params and write them to /etc/kernel/cmdline + # This file is used by kernel-install + kernel_params = " ".join(get_kernel_params(uuid)) + kernel_cmdline_path = os.path.join(installation_root_path, "etc", "kernel") + os.makedirs(kernel_cmdline_path, exist_ok=True) + with open(os.path.join(kernel_cmdline_path, "cmdline"), "w") as cmdline_file: + cmdline_file.write(kernel_params) + + libcalamares.utils.debug(f"Configuring kernel version {kernel_version}") + + # get the machine-id + with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file: + machine_id = machineid_file.read().rstrip('\n') + + # Ensure the directory exists + machine_dir = os.path.join(installation_root_path + efi_dir, machine_id) + os.makedirs(machine_dir, exist_ok=True) + + # Call kernel-install for each kernel + libcalamares.utils.target_env_process_output(["kernel-install", + "add", + kernel_version, + os.path.join("/", kernel)]) + + +def create_loader(loader_path, installation_root_path): + """ + Writes configuration for loader. + + :param loader_path: The absolute path to the loader.conf file + :param installation_root_path: The path to the root of the target installation + """ + + # get the machine-id + with open(os.path.join(installation_root_path, "etc", "machine-id"), 'r') as machineid_file: + machine_id = machineid_file.read().rstrip('\n') + + try: + loader_entries = libcalamares.job.configuration["loaderEntries"] + except KeyError: + libcalamares.utils.debug("No aditional loader entries found in config") + loader_entries = [] + pass + + lines = [f"default {machine_id}*"] + + lines.extend(loader_entries) + + with open(loader_path, 'w') as loader_file: + for line in lines: + loader_file.write(line + "\n") + + +class SuffixIterator(object): + """ + Wrapper for one of the "generator" classes below to behave like + a proper Python iterator. The iterator is initialized with a + maximum number of attempts to generate a new suffix. + """ + + def __init__(self, attempts, generator): + self.generator = generator + self.attempts = attempts + self.counter = 0 + + def __iter__(self): + return self + + def __next__(self): + self.counter += 1 + if self.counter <= self.attempts: + return self.generator.next() + raise StopIteration + + +class serialEfi(object): + """ + EFI Id generator that appends a serial number to the given name. + """ + + def __init__(self, name): + self.name = name + # So the first call to next() will bump it to 0 + self.counter = -1 + + def next(self): + self.counter += 1 + if self.counter > 0: + return "{!s}{!s}".format(self.name, self.counter) + else: + return self.name + + +def render_in_base(value, base_values, length=-1): + """ + Renders @p value in base-N, where N is the number of + items in @p base_values. When rendering, use the items + of @p base_values (e.g. use "0123456789" to get regular decimal + rendering, or "ABCDEFGHIJ" for letters-as-numbers 'encoding'). + + If length is positive, pads out to at least that long with + leading "zeroes", whatever base_values[0] is. + """ + if value < 0: + raise ValueError("Cannot render negative values") + if len(base_values) < 2: + raise ValueError("Insufficient items for base-N rendering") + if length < 1: + length = 1 + digits = [] + base = len(base_values) + while value > 0: + place = value % base + value = value // base + digits.append(base_values[place]) + while len(digits) < length: + digits.append(base_values[0]) + return "".join(reversed(digits)) + + +class randomEfi(object): + """ + EFI Id generator that appends a random 4-digit hex number to the given name. + """ + + def __init__(self, name): + self.name = name + # So the first call to next() will bump it to 0 + self.counter = -1 + + def next(self): + self.counter += 1 + if self.counter > 0: + import random + v = random.randint(0, 65535) # 16 bits + return "{!s}{!s}".format(self.name, render_in_base(v, "0123456789ABCDEF", 4)) + else: + return self.name + + +class phraseEfi(object): + """ + EFI Id generator that appends a random phrase to the given name. + """ + words = ("Sun", "Moon", "Mars", "Soyuz", "Falcon", "Kuaizhou", "Gaganyaan") + + def __init__(self, name): + self.name = name + # So the first call to next() will bump it to 0 + self.counter = -1 + + def next(self): + self.counter += 1 + if self.counter > 0: + import random + desired_length = 1 + self.counter // 5 + v = random.randint(0, len(self.words) ** desired_length) + return "{!s}{!s}".format(self.name, render_in_base(v, self.words)) + else: + return self.name + + +def get_efi_suffix_generator(name): + """ + Handle EFI bootloader Ids with ${} for suffix-processing. + """ + if "${" not in name: + raise ValueError("Misplaced call to get_efi_suffix_generator, no ${}") + if not name.endswith("}"): + raise ValueError("Misplaced call to get_efi_suffix_generator, no trailing ${}") + if name.count("${") > 1: + raise ValueError("EFI ID {!r} contains multiple generators".format(name)) + import re + prefix, generator_name = re.match("(.*)\${([^}]*)}$", name).groups() + if generator_name not in ("SERIAL", "RANDOM", "PHRASE"): + raise ValueError("EFI suffix {!r} is unknown".format(generator_name)) + + generator = None + if generator_name == "SERIAL": + generator = serialEfi(prefix) + elif generator_name == "RANDOM": + generator = randomEfi(prefix) + elif generator_name == "PHRASE": + generator = phraseEfi(prefix) + if generator is None: + raise ValueError("EFI suffix {!r} is unsupported".format(generator_name)) + + return generator + + +def change_efi_suffix(efi_directory, bootloader_id): + """ + Returns a label based on @p bootloader_id that is usable within + @p efi_directory. If there is a ${} suffix marker + in the given id, tries to generate a unique label. + """ + if bootloader_id.endswith("}") and "${" in bootloader_id: + # Do 10 attempts with any suffix generator + g = SuffixIterator(10, get_efi_suffix_generator(bootloader_id)) + else: + # Just one attempt + g = [bootloader_id] + + for candidate_name in g: + if not os.path.exists(os.path.join(efi_directory, candidate_name)): + return candidate_name + return bootloader_id + + +def efi_label(efi_directory): + """ + Returns a sanitized label, possibly unique, that can be + used within @p efi_directory. + """ + if "efiBootloaderId" in libcalamares.job.configuration: + efi_bootloader_id = change_efi_suffix(efi_directory, libcalamares.job.configuration["efiBootloaderId"]) + else: + branding = libcalamares.globalstorage.value("branding") + efi_bootloader_id = branding["bootloaderEntryName"] + + return efi_bootloader_id.translate(file_name_sanitizer) + + +def efi_word_size(): + # get bitness of the underlying UEFI + try: + sysfile = open("/sys/firmware/efi/fw_platform_size", "r") + efi_bitness = sysfile.read(2) + except Exception: + # if the kernel is older than 4.0, the UEFI bitness likely isn't + # exposed to the userspace so we assume a 64 bit UEFI here + efi_bitness = "64" + return efi_bitness + + +def efi_boot_next(): + """ + Tell EFI to definitely boot into the just-installed + system next time. + """ + boot_mgr = libcalamares.job.configuration["efiBootMgr"] + boot_entry = None + efi_bootvars = subprocess.check_output([boot_mgr], universal_newlines=True) + for line in efi_bootvars.split('\n'): + if not line: + continue + words = line.split() + if len(words) >= 2 and words[0] == "BootOrder:": + boot_entry = words[1].split(',')[0] + break + if boot_entry: + subprocess.call([boot_mgr, "-n", boot_entry]) + + +def get_kernels(installation_root_path): + """ + Gets a list of kernels and associated values for each kernel. This will work as is for many distros. + If not, it should be safe to modify it to better support your distro + + :param installation_root_path: A string with the absolute path to the root of the installation + + Returns a list of 3-tuples + + Each 3-tuple contains the kernel, kernel_type and kernel_version + """ + try: + kernel_search_path = libcalamares.job.configuration["kernelSearchPath"] + except KeyError: + libcalamares.utils.warning("No kernel pattern found in configuration, using '/usr/lib/modules'") + kernel_search_path = "/usr/lib/modules" + pass + + kernel_list = [] + + try: + kernel_pattern = libcalamares.job.configuration["kernelPattern"] + except KeyError: + libcalamares.utils.warning("No kernel pattern found in configuration, using 'vmlinuz'") + kernel_pattern = "vmlinuz" + pass + + # find all the installed kernels + for root, dirs, files in os.walk(os.path.join(installation_root_path, kernel_search_path.lstrip('/'))): + for file in files: + if re.search(kernel_pattern, file): + rel_root = os.path.relpath(root, installation_root_path) + kernel_list.append((os.path.join(rel_root, file), "default", os.path.basename(root))) + + return kernel_list + + +def install_clr_boot_manager(): + """ + Installs clr-boot-manager as the bootloader for EFI systems + """ + libcalamares.utils.debug("Bootloader: clr-boot-manager") + + installation_root_path = libcalamares.globalstorage.value("rootMountPoint") + kernel_config_path = os.path.join(installation_root_path, "etc", "kernel") + os.makedirs(kernel_config_path, exist_ok=True) + cmdline_path = os.path.join(kernel_config_path, "cmdline") + + # Get the kernel params + uuid = get_uuid() + kernel_params = " ".join(get_kernel_params(uuid)) + + # Write out the cmdline file for clr-boot-manager + with open(cmdline_path, "w") as cmdline_file: + cmdline_file.write(kernel_params) + + check_target_env_call(["clr-boot-manager", "update"]) + + +def install_systemd_boot(efi_directory): + """ + Installs systemd-boot as bootloader for EFI setups. + + :param efi_directory: + """ + libcalamares.utils.debug("Bootloader: systemd-boot") + installation_root_path = libcalamares.globalstorage.value("rootMountPoint") + install_efi_directory = installation_root_path + efi_directory + uuid = get_uuid() + loader_path = os.path.join(install_efi_directory, + "loader", + "loader.conf") + subprocess.call(["bootctl", + "--path={!s}".format(install_efi_directory), + "install"]) + + for (kernel, kernel_type, kernel_version) in get_kernels(installation_root_path): + create_systemd_boot_conf(installation_root_path, + efi_directory, + uuid, + kernel, + kernel_version) + + create_loader(loader_path, installation_root_path) + + +def get_grub_efi_parameters(): + """ + Returns a 3-tuple of suitable parameters for GRUB EFI installation, + depending on the host machine architecture. The return is + - target name + - grub.efi name + - boot.efi name + all three are strings. May return None if there is no suitable + set for the current machine. May return unsuitable values if the + host architecture is unknown (e.g. defaults to x86_64). + """ + import platform + efi_bitness = efi_word_size() + cpu_type = platform.machine() + + if efi_bitness == "32": + # Assume all 32-bitters are legacy x86 + return "i386-efi", "grubia32.efi", "bootia32.efi" + elif efi_bitness == "64" and cpu_type == "aarch64": + return "arm64-efi", "grubaa64.efi", "bootaa64.efi" + elif efi_bitness == "64" and cpu_type == "loongarch64": + return "loongarch64-efi", "grubloongarch64.efi", "bootloongarch64.efi" + elif efi_bitness == "64": + # If it's not ARM, must by AMD64 + return "x86_64-efi", "grubx64.efi", "bootx64.efi" + libcalamares.utils.warning( + "Could not find GRUB parameters for bits {b} and cpu {c}".format(b=repr(efi_bitness), c=repr(cpu_type))) + return None + + +def run_grub_mkconfig(partitions, output_file): + """ + Runs grub-mkconfig in the target environment + + :param partitions: The partitions list from global storage + :param output_file: A string containing the path to the generating grub config file + :return: + """ + + # zfs needs an environment variable set for grub-mkconfig + if any([is_zfs_root(partition) for partition in partitions]): + check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 " + + libcalamares.job.configuration["grubMkconfig"] + " -o " + output_file]) + else: + # The input file /etc/default/grub should already be filled out by the + # grubcfg job module. + check_target_env_call([libcalamares.job.configuration["grubMkconfig"], "-o", output_file]) + + +def run_grub_install(fw_type, partitions, efi_directory, install_hybrid_grub): + """ + Runs grub-install in the target environment + + :param fw_type: A string which is "efi" for UEFI installs. Any other value results in a BIOS install + :param partitions: The partitions list from global storage + :param efi_directory: The path of the efi directory relative to the root of the install + :return: + """ + + is_zfs = any([is_zfs_root(partition) for partition in partitions]) + + # zfs needs an environment variable set for grub + if is_zfs: + check_target_env_call(["sh", "-c", "echo ZPOOL_VDEV_NAME_PATH=1 >> /etc/environment"]) + + if fw_type == "efi": + assert efi_directory is not None + efi_bootloader_id = efi_label(efi_directory) + efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters() + + if is_zfs: + check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 " + libcalamares.job.configuration["grubInstall"] + + " --target=" + efi_target + " --efi-directory=" + efi_directory + + " --bootloader-id=" + efi_bootloader_id + " --force"]) + else: + check_target_env_call([libcalamares.job.configuration["grubInstall"], + "--target=" + efi_target, + "--efi-directory=" + efi_directory, + "--bootloader-id=" + efi_bootloader_id, + "--force"]) + else: + if efi_directory is not None and not install_hybrid_grub: + libcalamares.utils.warning(_("Cannot install BIOS bootloader on UEFI installation when install_hybrid_grub is 'False'!")) + return + + if libcalamares.globalstorage.value("bootLoader") is None: + efi_install_path = libcalamares.globalstorage.value("efiSystemPartition") + if efi_install_path is None or efi_install_path == "": + efi_install_path = "/boot/efi" + find_esp_disk_command = f"lsblk -o PKNAME \"$(df --output=source '{efi_install_path}' | tail -n1)\"" + boot_loader_install_path = check_target_env_output(["sh", "-c", find_esp_disk_command]).strip() + if not "\n" in boot_loader_install_path: + libcalamares.utils.warning(_("Cannot find the drive containing the EFI system partition!")) + return + boot_loader_install_path = "/dev/" + boot_loader_install_path.split("\n")[1] + else: + boot_loader = libcalamares.globalstorage.value("bootLoader") + boot_loader_install_path = boot_loader["installPath"] + if boot_loader_install_path is None: + return + + # boot_loader_install_path points to the physical disk to install GRUB + # to. It should start with "/dev/", and be at least as long as the + # string "/dev/sda". + if not boot_loader_install_path.startswith("/dev/") or len(boot_loader_install_path) < 8: + raise ValueError(f"boot_loader_install_path contains unexpected value '{boot_loader_install_path}'") + + if is_zfs: + check_target_env_call(["sh", "-c", "ZPOOL_VDEV_NAME_PATH=1 " + + libcalamares.job.configuration["grubInstall"] + + " --target=i386-pc --recheck --force " + + boot_loader_install_path]) + else: + check_target_env_call([libcalamares.job.configuration["grubInstall"], + "--target=i386-pc", + "--recheck", + "--force", + boot_loader_install_path]) + + +def install_grub(efi_directory, fw_type, install_hybrid_grub): + """ + Installs grub as bootloader, either in pc or efi mode. + + :param efi_directory: + :param fw_type: + :param install_hybrid_grub: + """ + # get the partition from global storage + partitions = libcalamares.globalstorage.value("partitions") + if not partitions: + libcalamares.utils.warning(_("Failed to install grub, no partitions defined in global storage")) + return + + if fw_type != "bios" and fw_type != "efi": + raise ValueError("fw_type must be 'bios' or 'efi'") + + if fw_type == "efi" or install_hybrid_grub: + libcalamares.utils.debug("Bootloader: grub (efi)") + libcalamares.utils.debug(f"install_hybrid_grub: {install_hybrid_grub}") + installation_root_path = libcalamares.globalstorage.value("rootMountPoint") + install_efi_directory = installation_root_path + efi_directory + + if not os.path.isdir(install_efi_directory): + os.makedirs(install_efi_directory) + + efi_bootloader_id = efi_label(efi_directory) + + efi_target, efi_grub_file, efi_boot_file = get_grub_efi_parameters() + + run_grub_install("efi", partitions, efi_directory, install_hybrid_grub) + + # VFAT is weird, see issue CAL-385 + install_efi_directory_firmware = (vfat_correct_case( + install_efi_directory, + "EFI")) + if not os.path.exists(install_efi_directory_firmware): + os.makedirs(install_efi_directory_firmware) + + # there might be several values for the boot directory + # most usual they are boot, Boot, BOOT + + install_efi_boot_directory = (vfat_correct_case( + install_efi_directory_firmware, + "boot")) + if not os.path.exists(install_efi_boot_directory): + os.makedirs(install_efi_boot_directory) + + # Workaround for some UEFI firmwares + fallback = "installEFIFallback" + libcalamares.utils.debug("UEFI Fallback: " + str(libcalamares.job.configuration.get(fallback, ""))) + if libcalamares.job.configuration.get(fallback, True): + libcalamares.utils.debug(" .. installing '{!s}' fallback firmware".format(efi_boot_file)) + efi_file_source = os.path.join(install_efi_directory_firmware, + efi_bootloader_id, + efi_grub_file) + efi_file_target = os.path.join(install_efi_boot_directory, efi_boot_file) + + shutil.copy2(efi_file_source, efi_file_target) + if fw_type == "bios" or install_hybrid_grub: + libcalamares.utils.debug("Bootloader: grub (bios)") + run_grub_install("bios", partitions, efi_directory, install_hybrid_grub) + + run_grub_mkconfig(partitions, libcalamares.job.configuration["grubCfg"]) + + +def install_secureboot(efi_directory): + """ + Installs the secureboot shim in the system by calling efibootmgr. + """ + efi_bootloader_id = efi_label(efi_directory) + + installation_root_path = libcalamares.globalstorage.value("rootMountPoint") + install_efi_directory = installation_root_path + efi_directory + + if efi_word_size() == "64": + install_efi_bin = "shimx64.efi" + elif efi_word_size() == "32": + install_efi_bin = "shimia32.efi" + else: + libcalamares.utils.warning(f"Unknown efi word size of {efi_word_size()} found") + return None + + # Copied, roughly, from openSUSE's install script, + # and pythonified. *disk* is something like /dev/sda, + # while *drive* may return "(disk/dev/sda,gpt1)" .. + # we're interested in the numbers in the second part + # of that tuple. + efi_drive = subprocess.check_output([ + libcalamares.job.configuration["grubProbe"], + "-t", "drive", "--device-map=", install_efi_directory]).decode("ascii") + efi_disk = subprocess.check_output([ + libcalamares.job.configuration["grubProbe"], + "-t", "disk", "--device-map=", install_efi_directory]).decode("ascii") + + efi_drive_partition = efi_drive.replace("(", "").replace(")", "").split(",")[1] + # Get the first run of digits from the partition + efi_partition_number = None + c = 0 + start = None + while c < len(efi_drive_partition): + if efi_drive_partition[c].isdigit() and start is None: + start = c + if not efi_drive_partition[c].isdigit() and start is not None: + efi_partition_number = efi_drive_partition[start:c] + break + c += 1 + if efi_partition_number is None: + raise ValueError("No partition number found for %s" % install_efi_directory) + + subprocess.call([ + libcalamares.job.configuration["efiBootMgr"], + "-c", + "-w", + "-L", efi_bootloader_id, + "-d", efi_disk, + "-p", efi_partition_number, + "-l", install_efi_directory + "/" + install_efi_bin]) + + efi_boot_next() + + # The input file /etc/default/grub should already be filled out by the + # grubcfg job module. + check_target_env_call([libcalamares.job.configuration["grubMkconfig"], + "-o", os.path.join(efi_directory, "EFI", + efi_bootloader_id, "grub.cfg")]) + + +def vfat_correct_case(parent, name): + for candidate in os.listdir(parent): + if name.lower() == candidate.lower(): + return os.path.join(parent, candidate) + return os.path.join(parent, name) + + +def efi_partitions(efi_boot_path): + """ + The (one) partition mounted on @p efi_boot_path, or an empty list. + """ + return [p for p in libcalamares.globalstorage.value("partitions") if p["mountPoint"] == efi_boot_path] + + +def update_refind_config(efi_directory, installation_root_path): + """ + :param efi_directory: The path to the efi directory relative to the root + :param installation_root_path: The path to the root of the installation + """ + try: + kernel_list = libcalamares.job.configuration["refindKernelList"] + except KeyError: + libcalamares.utils.warning('refindKernelList not set. Skipping updating refind.conf') + return + + # Update the config in the file + for line in fileinput.input(installation_root_path + efi_directory + "/EFI/refind/refind.conf", inplace=True): + line = line.strip() + if line.startswith("#extra_kernel_version_strings") or line.startswith("extra_kernel_version_strings"): + line = line.lstrip("#") + for kernel in kernel_list: + if kernel not in line: + line += "," + kernel + print(line) + + +def install_refind(efi_directory): + try: + installation_root_path = libcalamares.globalstorage.value("rootMountPoint") + except KeyError: + libcalamares.utils.warning('Global storage value "rootMountPoint" missing') + + install_efi_directory = installation_root_path + efi_directory + uuid = get_uuid() + kernel_params = " ".join(get_kernel_params(uuid)) + conf_path = os.path.join(installation_root_path, "boot/refind_linux.conf") + + check_target_env_call(["refind-install"]) + + with open(conf_path, "r") as refind_file: + filedata = [x.strip() for x in refind_file.readlines()] + + with open(conf_path, 'w') as refind_file: + for line in filedata: + if line.startswith('"Boot with standard options"'): + line = f'"Boot with standard options" "{kernel_params}"' + elif line.startswith('"Boot to single-user mode"'): + line = f'"Boot to single-user mode" "{kernel_params}" single' + refind_file.write(line + "\n") + + update_refind_config(efi_directory, installation_root_path) + + +def prepare_bootloader(fw_type, install_hybrid_grub): + """ + Prepares bootloader. + Based on value 'efi_boot_loader', it either calls systemd-boot + or grub to be installed. + + :param fw_type: + :return: + """ + + # Get the boot loader selection from global storage if it is set in the config file + try: + gs_name = libcalamares.job.configuration["efiBootLoaderVar"] + if libcalamares.globalstorage.contains(gs_name): + efi_boot_loader = libcalamares.globalstorage.value(gs_name) + else: + libcalamares.utils.warning( + f"Specified global storage value not found in global storage") + return None + except KeyError: + # If the conf value for using global storage is not set, use the setting from the config file. + try: + efi_boot_loader = libcalamares.job.configuration["efiBootLoader"] + except KeyError: + if fw_type == "efi" or install_hybrid_grub: + libcalamares.utils.warning("Configuration missing both efiBootLoader and efiBootLoaderVar on an EFI-enabled " + "system, bootloader not installed") + return + else: + pass + + # If the user has selected not to install bootloader, bail out here + if efi_boot_loader.casefold() == "none": + libcalamares.utils.debug("Skipping bootloader installation since no bootloader was selected") + return None + + efi_directory = libcalamares.globalstorage.value("efiSystemPartition") + + if efi_boot_loader == "clr-boot-manager": + if fw_type != "efi": + # Grub has to be installed first on non-EFI systems + install_grub(efi_directory, fw_type) + install_clr_boot_manager() + elif efi_boot_loader == "systemd-boot" and fw_type == "efi": + install_systemd_boot(efi_directory) + elif efi_boot_loader == "sb-shim" and fw_type == "efi": + install_secureboot(efi_directory) + elif efi_boot_loader == "refind" and fw_type == "efi": + install_refind(efi_directory) + elif efi_boot_loader == "grub" or fw_type != "efi": + install_grub(efi_directory, fw_type, install_hybrid_grub) + else: + libcalamares.utils.debug("WARNING: the combination of " + "boot-loader '{!s}' and firmware '{!s}' " + "is not supported.".format(efi_boot_loader, fw_type)) + + +def run(): + """ + Starts procedure and passes 'fw_type' to other routine. + + :return: + """ + + fw_type = libcalamares.globalstorage.value("firmwareType") + boot_loader = libcalamares.globalstorage.value("bootLoader") + + install_hybrid_grub = libcalamares.job.configuration.get("installHybridGRUB", False) + efi_boot_loader = libcalamares.job.configuration.get("efiBootLoader", "") + + if install_hybrid_grub == True and efi_boot_loader != "grub": + raise ValueError(f"efi_boot_loader '{efi_boot_loader}' is illegal when install_hybrid_grub is 'true'!") + + if boot_loader is None and fw_type != "efi": + libcalamares.utils.warning("Non-EFI system, and no bootloader is set.") + return None + + partitions = libcalamares.globalstorage.value("partitions") + if fw_type == "efi": + efi_system_partition = libcalamares.globalstorage.value("efiSystemPartition") + esp_found = [p for p in partitions if p["mountPoint"] == efi_system_partition] + if not esp_found: + libcalamares.utils.warning("EFI system, but nothing mounted on {!s}".format(efi_system_partition)) + return None + + try: + prepare_bootloader(fw_type, install_hybrid_grub) + except subprocess.CalledProcessError as e: + libcalamares.utils.warning(str(e)) + libcalamares.utils.debug("stdout:" + str(e.stdout)) + libcalamares.utils.debug("stderr:" + str(e.stderr)) + return (_("Bootloader installation error"), + _("The bootloader could not be installed. The installation command
{!s}
returned error " + "code {!s}.") + .format(e.cmd, e.returncode)) + + return None diff --git a/airootfs/usr/lib/calamares/modules/bootloader/module.desc b/airootfs/usr/lib/calamares/modules/bootloader/module.desc new file mode 100644 index 0000000..44a1c0e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/bootloader/module.desc @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +interface: "python" +name: "bootloader" +script: "main.py" +# The partition module sets up the EFI firmware type +# global key, which is used to decide how to install. +requiredModules: [ "partition" ] diff --git a/airootfs/usr/lib/calamares/modules/bootloader/test.yaml b/airootfs/usr/lib/calamares/modules/bootloader/test.yaml new file mode 100644 index 0000000..4623b55 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/bootloader/test.yaml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +rootMountPoint: /tmp/mount +bootLoader: + installPath: /dev/sdb +branding: + shortProductName: "Generic Distro" diff --git a/airootfs/usr/lib/calamares/modules/contextualprocess/libcalamares_job_contextualprocess.so b/airootfs/usr/lib/calamares/modules/contextualprocess/libcalamares_job_contextualprocess.so new file mode 100755 index 0000000..9f9a57f Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/contextualprocess/libcalamares_job_contextualprocess.so differ diff --git a/airootfs/usr/lib/calamares/modules/contextualprocess/module.desc b/airootfs/usr/lib/calamares/modules/contextualprocess/module.desc new file mode 100644 index 0000000..9dd055a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/contextualprocess/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "contextualprocess" +interface: "qtplugin" +load: "libcalamares_job_contextualprocess.so" diff --git a/airootfs/usr/lib/calamares/modules/displaymanager/displaymanager.schema.yaml b/airootfs/usr/lib/calamares/modules/displaymanager/displaymanager.schema.yaml new file mode 100644 index 0000000..dcd2baa --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/displaymanager/displaymanager.schema.yaml @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/displaymanager +additionalProperties: false +type: object +properties: + displaymanagers: + type: array + items: + type: string + enum: [slim, sddm, lightdm, gdm, mdm, lxdm, greetd] + minItems: 1 # Must be non-empty, if present at all + defaultDesktopEnvironment: + type: object + properties: + executable: { type: string } + desktopFile: { type: string } + required: [ executable, desktopFile ] + basicSetup: { type: boolean, default: false } + sysconfigSetup: { type: boolean, default: false } + greetd: + type: object + properties: + greeter_user: { type: string } + greeter_group: { type: string } + greeter_css_location: { type: string } + additionalProperties: false + lightdm: + type: object + properties: + preferred_greeters: { type: array, items: { type: string } } + additionalProperties: false + sddm: + type: object + properties: + configuration_file: { type: string } + additionalProperties: false diff --git a/airootfs/usr/lib/calamares/modules/displaymanager/main.py b/airootfs/usr/lib/calamares/modules/displaymanager/main.py new file mode 100644 index 0000000..6ca279e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/displaymanager/main.py @@ -0,0 +1,1046 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014-2018 Philip Müller +# SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac +# SPDX-FileCopyrightText: 2014 Kevin Kofler +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2017 Bernhard Landauer +# SPDX-FileCopyrightText: 2017 2019, Adriaan de Groot +# SPDX-FileCopyrightText: 2019 Dominic Hayes +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import abc +import os +import libcalamares + +from libcalamares.utils import gettext_path, gettext_languages + +import gettext +_translation = gettext.translation("calamares-python", + localedir=gettext_path(), + languages=gettext_languages(), + fallback=True) +_ = _translation.gettext +_n = _translation.ngettext + +class DesktopEnvironment: + """ + Desktop Environment -- some utility functions for a desktop + environment (e.g. finding out if it is installed). This + is independent of the *Display Manager*, which is what + we're configuring in this module. + """ + def __init__(self, exec, desktop): + self.executable = exec + self.desktop_file = desktop + + def _search_executable(self, root_mount_point, pathname): + """ + Search for @p pathname within @p root_mount_point . + If the pathname is absolute, just check there inside + the target, otherwise earch in a sort-of-sensible $PATH. + + Returns the full (including @p root_mount_point) path + to that executable, or None. + """ + if pathname.startswith("/"): + path = [""] + else: + path = ["/bin/", "/usr/bin/", "/sbin/", "/usr/local/bin/"] + + for p in path: + absolute_path = "{!s}{!s}{!s}".format(root_mount_point, p, pathname) + if os.path.exists(absolute_path): + return absolute_path + return None + + def _search_tryexec(self, root_mount_point, absolute_desktop_file): + """ + Check @p absolute_desktop_file for a TryExec line and, if that is + found, search for the command (executable pathname) within + @p root_mount_point. The .desktop file must live within the + target root. + + Returns the full (including @p root_mount_point) for the executable + from TryExec, or None. + """ + assert absolute_desktop_file.startswith(root_mount_point) + with open(absolute_desktop_file, "r") as f: + for tryexec_line in [x for x in f.readlines() if x.startswith("TryExec")]: + try: + key, value = tryexec_line.split("=") + if key.strip() == "TryExec": + return self._search_executable(root_mount_point, value.strip()) + except: + pass + return None + + def find_executable(self, root_mount_point): + """ + Returns the full path of the configured executable within @p root_mount_point, + or None if it isn't found. May search in a semi-sensible $PATH. + """ + return self._search_executable(root_mount_point, self.executable) + + def find_desktop_file(self, root_mount_point): + """ + Returns the full path of the .desktop file within @p root_mount_point, + or None if it isn't found. Searches both X11 and Wayland sessions. + """ + x11_sessions = "{!s}/usr/share/xsessions/{!s}.desktop".format(root_mount_point, self.desktop_file) + wayland_sessions = "{!s}/usr/share/wayland-sessions/{!s}.desktop".format(root_mount_point, self.desktop_file) + for candidate in (x11_sessions, wayland_sessions): + if os.path.exists(candidate): + return candidate + return None + + def is_installed(self, root_mount_point): + """ + Check if this environment is installed in the + target system at @p root_mount_point. + """ + desktop_file = self.find_desktop_file(root_mount_point) + if desktop_file is None: + return False + + return (self.find_executable(root_mount_point) is not None or + self._search_tryexec(root_mount_point, desktop_file) is not None) + + def update_from_desktop_file(self, root_mount_point): + """ + Find thie DE in the target system at @p root_mount_point. + This can update the *executable* configuration value if + the configured executable isn't found but the TryExec line + from the .desktop file is. + + The .desktop file is mandatory for a DE. + + Returns True if the DE is installed. + """ + desktop_file = self.find_desktop_file(root_mount_point) + if desktop_file is None: + return False + + executable_file = self.find_executable(root_mount_point) + if executable_file is not None: + # .desktop found and executable as well. + return True + + executable_file = self._search_tryexec(root_mount_point, desktop_file) + if executable_file is not None: + # Found from the .desktop file, so update own executable config + if root_mount_point and executable_file.startswith(root_mount_point): + executable_file = executable_file[len(root_mount_point):] + if not executable_file: + # Somehow chopped down to nothing + return False + + if executable_file[0] != "/": + executable_file = "/" + executable_file + self.executable = executable_file + return True + # This is to double-check + return self.is_installed(root_mount_point) + + +# This is the list of desktop environments that Calamares looks +# for; if no default environment is **explicitly** configured +# in the `displaymanager.conf` then the first one from this list +# that is found, is used. +# +# Each DE has a sample executable to look for, and a .desktop filename. +# If the executable exists, the DE is assumed to be installed +# and to use the given .desktop filename. +# +# If the .desktop file exists and contains a TryExec line and that +# TryExec executable exists (searched in /bin, /usr/bin, /sbin and +# /usr/local/bin) then the DE is assumed to be installed +# and to use that .desktop filename. +desktop_environments = [ + DesktopEnvironment('/usr/bin/startplasma-x11', 'plasma'), # KDE Plasma 5.17+ + DesktopEnvironment('/usr/bin/startkde', 'plasma'), # KDE Plasma 5 + DesktopEnvironment('/usr/bin/startkde', 'kde-plasma'), # KDE Plasma 4 + DesktopEnvironment( + '/usr/bin/budgie-desktop', 'budgie-desktop' # Budgie v10 + ), + DesktopEnvironment( + '/usr/bin/budgie-session', 'budgie-desktop' # Budgie v8 + ), + DesktopEnvironment('/usr/bin/gnome-session', 'gnome'), + DesktopEnvironment('/usr/bin/cinnamon-session-cinnamon', 'cinnamon'), + DesktopEnvironment('/usr/bin/mate-session', 'mate'), + DesktopEnvironment('/usr/bin/enlightenment_start', 'enlightenment'), + DesktopEnvironment('/usr/bin/lxsession', 'LXDE'), + DesktopEnvironment('/usr/bin/startlxde', 'LXDE'), + DesktopEnvironment('/usr/bin/lxqt-session', 'lxqt'), + DesktopEnvironment('/usr/bin/pekwm', 'pekwm'), + DesktopEnvironment('/usr/bin/pantheon-session', 'pantheon'), + DesktopEnvironment('/usr/bin/startdde', 'deepin'), + DesktopEnvironment('/usr/bin/startxfce4', 'xfce'), + DesktopEnvironment('/usr/bin/openbox-session', 'openbox'), + DesktopEnvironment('/usr/bin/i3', 'i3'), + DesktopEnvironment('/usr/bin/awesome', 'awesome'), + DesktopEnvironment('/usr/bin/bspwm', 'bspwm'), + DesktopEnvironment('/usr/bin/herbstluftwm', 'herbstluftwm'), + DesktopEnvironment('/usr/bin/qtile', 'qtile'), + DesktopEnvironment('/usr/bin/xmonad', 'xmonad'), + DesktopEnvironment('/usr/bin/dwm', 'dwm'), + DesktopEnvironment('/usr/bin/jwm', 'jwm'), + DesktopEnvironment('/usr/bin/icewm-session', 'icewm-session'), + DesktopEnvironment('/usr/bin/fvwm3', 'fvwm3'), + DesktopEnvironment('/usr/bin/sway', 'sway'), + DesktopEnvironment('/usr/bin/ukui-session', 'ukui'), + DesktopEnvironment('/usr/bin/cutefish-session', 'cutefish-xsession'), + DesktopEnvironment('/usr/bin/river', 'river'), + DesktopEnvironment('/usr/bin/Hyprland', 'hyprland'), +] + + +def find_desktop_environment(root_mount_point): + """ + Checks which desktop environment is currently installed. + + :param root_mount_point: + :return: + """ + libcalamares.utils.debug("Using rootMountPoint {!r}".format(root_mount_point)) + for desktop_environment in desktop_environments: + if desktop_environment.is_installed(root_mount_point): + libcalamares.utils.debug(".. selected DE {!s}".format(desktop_environment.desktop_file)) + return desktop_environment + return None + + +class DisplayManager(metaclass=abc.ABCMeta): + """ + Display Manager -- a base class for DM configuration. + """ + name = None + executable = None + + def __init__(self, root_mount_point): + self.root_mount_point = root_mount_point + + def have_dm(self): + """ + Is this DM installed in the target system? + The default implementation checks for `executable` + in the target system. + """ + if self.executable is None: + return False + + bin_path = "{!s}/usr/bin/{!s}".format(self.root_mount_point, self.executable) + sbin_path = "{!s}/usr/sbin/{!s}".format(self.root_mount_point, self.executable) + return os.path.exists(bin_path) or os.path.exists(sbin_path) + + # The four abstract methods below are called in the order listed here. + # They must all be implemented by subclasses, but not all of them + # actually do something for all DMs. + + @abc.abstractmethod + def basic_setup(self): + """ + Do basic setup (e.g. users, groups, directory creation) for this DM. + """ + # Some implementations do nothing + + @abc.abstractmethod + def desktop_environment_setup(self, desktop_environment): + """ + Configure the given @p desktop_environment as the default one, in + the configuration files for this DM. + """ + # Many implementations do nothing + + @abc.abstractmethod + def greeter_setup(self): + """ + Additional setup for the greeter. + """ + # Most implementations do nothing + + @abc.abstractmethod + def set_autologin(self, username, do_autologin, default_desktop_environment): + """ + Configure the DM inside the given @p root_mount_point with + autologin (if @p do_autologin is True) for the given @p username. + If the DM supports it, set the default DE to @p default_desktop_environment + as well. + """ + + +class DMmdm(DisplayManager): + name = "mdm" + executable = "mdm" + + def set_autologin(self, username, do_autologin, default_desktop_environment): + # Systems with MDM as Desktop Manager + mdm_conf_path = os.path.join(self.root_mount_point, "etc/mdm/custom.conf") + + if os.path.exists(mdm_conf_path): + with open(mdm_conf_path, 'r') as mdm_conf: + text = mdm_conf.readlines() + + with open(mdm_conf_path, 'w') as mdm_conf: + for line in text: + if 'AutomaticLogin=' in line: + line = "" + if 'AutomaticLoginEnable=True' in line: + line = "" + if '[daemon]' in line: + if do_autologin: + line = ( + "[daemon]\n" + "AutomaticLogin={!s}\n" + "AutomaticLoginEnable=True\n".format(username) + ) + else: + line = ( + "[daemon]\n" + "AutomaticLoginEnable=False\n" + ) + + mdm_conf.write(line) + else: + with open(mdm_conf_path, 'w') as mdm_conf: + mdm_conf.write( + '# Calamares - Configure automatic login for user\n' + ) + mdm_conf.write('[daemon]\n') + + if do_autologin: + mdm_conf.write("AutomaticLogin={!s}\n".format(username)) + mdm_conf.write('AutomaticLoginEnable=True\n') + else: + mdm_conf.write('AutomaticLoginEnable=False\n') + + def basic_setup(self): + if libcalamares.utils.target_env_call( + ['getent', 'group', 'mdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['groupadd', '-g', '128', 'mdm'] + ) + + if libcalamares.utils.target_env_call( + ['getent', 'passwd', 'mdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['useradd', + '-c', '"Linux Mint Display Manager"', + '-u', '128', + '-g', 'mdm', + '-d', '/var/lib/mdm', + '-s', '/usr/bin/nologin', + 'mdm' + ] + ) + + libcalamares.utils.target_env_call( + ['passwd', '-l', 'mdm'] + ) + libcalamares.utils.target_env_call( + ['chown', 'root:mdm', '/var/lib/mdm'] + ) + libcalamares.utils.target_env_call( + ['chmod', '1770', '/var/lib/mdm'] + ) + + def desktop_environment_setup(self, default_desktop_environment): + os.system( + "sed -i \"s|default.desktop|{!s}.desktop|g\" " + "{!s}/etc/mdm/custom.conf".format( + default_desktop_environment.desktop_file, + self.root_mount_point + ) + ) + + def greeter_setup(self): + pass + + +class DMgdm(DisplayManager): + name = "gdm" + executable = "gdm" + config = None # Set by have_dm() + + def have_dm(self): + """ + GDM exists with different executable names, so search + for one of them and use it. + """ + candidates = ( + ( "gdm", "etc/gdm/custom.conf" ), + ( "gdm3", "etc/gdm3/daemon.conf" ), + ( "gdm3", "etc/gdm3/custom.conf" ), + ) + + def have_executable(executable : str): + bin_path = "{!s}/usr/bin/{!s}".format(self.root_mount_point, executable) + sbin_path = "{!s}/usr/sbin/{!s}".format(self.root_mount_point, executable) + return os.path.exists(bin_path) or os.path.exists(sbin_path) + + def have_config(config : str): + config_path = "{!s}/{!s}".format(self.root_mount_point, config) + return os.path.exists(config_path) + + # Look for an existing configuration file as a hint, then + # keep the found-executable name and config around for later. + for executable, config in candidates: + if have_config(config) and have_executable(executable): + self.executable = executable + self.config = config + return True + for executable, config in candidates: + if have_executable(executable): + self.executable = executable + self.config = config + return True + + return False + + def set_autologin(self, username, do_autologin, default_desktop_environment): + if self.config is None: + raise ValueError( "No config file for GDM has been set." ) + + # Systems with GDM as Desktop Manager + gdm_conf_path = os.path.join(self.root_mount_point, self.config) + + if os.path.exists(gdm_conf_path): + with open(gdm_conf_path, 'r') as gdm_conf: + text = gdm_conf.readlines() + + with open(gdm_conf_path, 'w') as gdm_conf: + for line in text: + if 'AutomaticLogin=' in line: + line = "" + if 'AutomaticLoginEnable=True' in line: + line = "" + if '[daemon]' in line: + if do_autologin: + line = ( + "[daemon]\n" + "AutomaticLogin={!s}\n" + "AutomaticLoginEnable=True\n".format(username) + ) + else: + line = "[daemon]\nAutomaticLoginEnable=False\n" + + gdm_conf.write(line) + else: + with open(gdm_conf_path, 'w') as gdm_conf: + gdm_conf.write( + '# Calamares - Enable automatic login for user\n' + ) + gdm_conf.write('[daemon]\n') + + if do_autologin: + gdm_conf.write("AutomaticLogin={!s}\n".format(username)) + gdm_conf.write('AutomaticLoginEnable=True\n') + else: + gdm_conf.write('AutomaticLoginEnable=False\n') + + if (do_autologin): + accountservice_dir = "{!s}/var/lib/AccountsService/users".format( + self.root_mount_point + ) + userfile_path = "{!s}/{!s}".format(accountservice_dir, username) + if os.path.exists(accountservice_dir): + with open(userfile_path, "w") as userfile: + userfile.write("[User]\n") + + if default_desktop_environment is not None: + userfile.write("XSession={!s}\n".format( + default_desktop_environment.desktop_file)) + + userfile.write("Icon=\n") + + def basic_setup(self): + if libcalamares.utils.target_env_call( + ['getent', 'group', 'gdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['groupadd', '-g', '120', 'gdm'] + ) + + if libcalamares.utils.target_env_call( + ['getent', 'passwd', 'gdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['useradd', + '-c', '"Gnome Display Manager"', + '-u', '120', + '-g', 'gdm', + '-d', '/var/lib/gdm', + '-s', '/usr/bin/nologin', + 'gdm' + ] + ) + + libcalamares.utils.target_env_call( + ['passwd', '-l', 'gdm'] + ) + libcalamares.utils.target_env_call( + ['chown', '-R', 'gdm:gdm', '/var/lib/gdm'] + ) + + def desktop_environment_setup(self, desktop_environment): + pass + + def greeter_setup(self): + pass + + +class DMlxdm(DisplayManager): + name = "lxdm" + executable = "lxdm" + + def set_autologin(self, username, do_autologin, default_desktop_environment): + # Systems with LXDM as Desktop Manager + lxdm_conf_path = os.path.join(self.root_mount_point, "etc/lxdm/lxdm.conf") + text = [] + + if os.path.exists(lxdm_conf_path): + with open(lxdm_conf_path, 'r') as lxdm_conf: + text = lxdm_conf.readlines() + + with open(lxdm_conf_path, 'w') as lxdm_conf: + for line in text: + if 'autologin=' in line: + if do_autologin: + line = "autologin={!s}\n".format(username) + else: + line = "# autologin=\n" + + lxdm_conf.write(line) + else: + return ( + _("Cannot write LXDM configuration file"), + _("LXDM config file {!s} does not exist").format(lxdm_conf_path) + ) + + def basic_setup(self): + if libcalamares.utils.target_env_call( + ['getent', 'group', 'lxdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['groupadd', '--system', 'lxdm'] + ) + + libcalamares.utils.target_env_call( + ['chgrp', '-R', 'lxdm', '/var/lib/lxdm'] + ) + libcalamares.utils.target_env_call( + ['chgrp', 'lxdm', '/etc/lxdm/lxdm.conf'] + ) + libcalamares.utils.target_env_call( + ['chmod', '+r', '/etc/lxdm/lxdm.conf'] + ) + + def desktop_environment_setup(self, default_desktop_environment): + os.system( + "sed -i -e \"s|^.*session=.*|session={!s}|\" " + "{!s}/etc/lxdm/lxdm.conf".format( + default_desktop_environment.executable, + self.root_mount_point + ) + ) + + def greeter_setup(self): + pass + + +class DMlightdm(DisplayManager): + name = "lightdm" + executable = "lightdm" + + # Can be overridden in the .conf file. With no value it won't match any + # desktop file in the xgreeters directory and instead we end up picking + # the alphabetically first file there. + preferred_greeters = [] + + def set_autologin(self, username, do_autologin, default_desktop_environment): + # Systems with LightDM as Desktop Manager + # Ideally, we should use configparser for the ini conf file, + # but we just do a simple text replacement for now, as it + # worksforme(tm) + lightdm_conf_path = os.path.join( + self.root_mount_point, "etc/lightdm/lightdm.conf" + ) + text = [] + addseat = False + loopcount = 0 + + if os.path.exists(lightdm_conf_path): + with open(lightdm_conf_path, 'r') as lightdm_conf: + text = lightdm_conf.readlines() + # Check to make sure [SeatDefaults] or [Seat:*] is in the config, + # otherwise we'll risk malforming the config + addseat = '[SeatDefaults]' not in text and '[Seat:*]' not in text + + with open(lightdm_conf_path, 'w') as lightdm_conf: + if addseat: + # Prepend Seat line to start of file rather than leaving it without one + # This keeps the config from being malformed for LightDM + text = ["[Seat:*]\n"] + text + for line in text: + if 'autologin-user=' in line: + if do_autologin: + line = "autologin-user={!s}\n".format(username) + else: + line = "#autologin-user=\n" + + lightdm_conf.write(line) + else: + try: + # Create a new lightdm.conf file; this is documented to be + # read last, after everything in lightdm.conf.d/ + with open(lightdm_conf_path, 'w') as lightdm_conf: + if do_autologin: + lightdm_conf.write( + "[Seat:*]\nautologin-user={!s}\n".format(username)) + else: + lightdm_conf.write( + "[Seat:*]\n#autologin-user=\n") + except FileNotFoundError: + return ( + _("Cannot write LightDM configuration file"), + _("LightDM config file {!s} does not exist").format(lightdm_conf_path) + ) + + def basic_setup(self): + libcalamares.utils.target_env_call( + ['mkdir', '-p', '/run/lightdm'] + ) + + if libcalamares.utils.target_env_call( + ['getent', 'group', 'lightdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['groupadd', '-g', '620', 'lightdm'] + ) + + if libcalamares.utils.target_env_call( + ['getent', 'passwd', 'lightdm'] + ) != 0: + libcalamares.utils.target_env_call( + ['useradd', '-c', + '"LightDM Display Manager"', + '-u', '620', + '-g', 'lightdm', + '-d', '/var/run/lightdm', + '-s', '/usr/bin/nologin', + 'lightdm' + ] + ) + + libcalamares.utils.target_env_call(['passwd', '-l', 'lightdm']) + libcalamares.utils.target_env_call(['chown', '-R', 'lightdm:lightdm', '/run/lightdm']) + libcalamares.utils.target_env_call(['chmod', '+r' '/etc/lightdm/lightdm.conf']) + + def desktop_environment_setup(self, default_desktop_environment): + os.system( + "sed -i -e \"s/^.*user-session=.*/user-session={!s}/\" " + "{!s}/etc/lightdm/lightdm.conf".format( + default_desktop_environment.desktop_file, + self.root_mount_point + ) + ) + + def find_preferred_greeter(self): + """ + On Debian, lightdm-greeter.desktop is typically a symlink managed + by update-alternatives pointing to /etc/alternatives/lightdm-greeter + which is also a symlink to a real .desktop file back in /usr/share/xgreeters/ + + Returns a path *into the mounted target* of the preferred greeter -- usually + a .desktop file that specifies where the actual executable is. May return + None to indicate nothing-was-found. + """ + greeters_dir = "usr/share/xgreeters" + greeters_target_path = os.path.join(self.root_mount_point, greeters_dir) + available_names = os.listdir(greeters_target_path) + available_names.sort() + desktop_names = [n for n in self.preferred_greeters if n in available_names] # Preferred ones + if desktop_names: + return desktop_names[0] + desktop_names = [n for n in available_names if n.endswith(".desktop")] # .. otherwise any .desktop + if desktop_names: + return desktop_names[0] + return None + + def greeter_setup(self): + lightdm_conf_path = os.path.join(self.root_mount_point, "etc/lightdm/lightdm.conf") + greeter_name = self.find_preferred_greeter() + + if greeter_name is not None: + greeter = os.path.basename(greeter_name) # Follow symlinks, hope they are not absolute + if greeter.endswith('.desktop'): + greeter = greeter[:-8] # Remove ".desktop" from end + + libcalamares.utils.debug("found greeter {!s}".format(greeter)) + os.system( + "sed -i -e \"s/^.*greeter-session=.*" + "/greeter-session={!s}/\" {!s}".format( + greeter, + lightdm_conf_path + ) + ) + libcalamares.utils.debug("{!s} configured as greeter.".format(greeter)) + else: + libcalamares.utils.error("No greeter found at all, preferred {!s}".format(self.preferred_greeters)) + return ( + _("Cannot configure LightDM"), + _("No LightDM greeter installed.") + ) + + +class DMslim(DisplayManager): + name = "slim" + executable = "slim" + + def set_autologin(self, username, do_autologin, default_desktop_environment): + # Systems with Slim as Desktop Manager + slim_conf_path = os.path.join(self.root_mount_point, "etc/slim.conf") + text = [] + + if os.path.exists(slim_conf_path): + with open(slim_conf_path, 'r') as slim_conf: + text = slim_conf.readlines() + + with open(slim_conf_path, 'w') as slim_conf: + for line in text: + if 'auto_login' in line: + if do_autologin: + line = 'auto_login yes\n' + else: + line = 'auto_login no\n' + + if do_autologin and 'default_user' in line: + line = "default_user {!s}\n".format(username) + + slim_conf.write(line) + else: + return ( + _("Cannot write SLIM configuration file"), + _("SLIM config file {!s} does not exist").format(slim_conf_path) + ) + + + def basic_setup(self): + pass + + def desktop_environment_setup(self, desktop_environment): + pass + + def greeter_setup(self): + pass + + +class DMsddm(DisplayManager): + name = "sddm" + executable = "sddm" + + configuration_file = "/etc/sddm.conf" + + def set_autologin(self, username, do_autologin, default_desktop_environment): + import configparser + + # Systems with Sddm as Desktop Manager + sddm_conf_path = os.path.join(self.root_mount_point, self.configuration_file.lstrip('/')) + + sddm_config = configparser.ConfigParser(strict=False) + # Make everything case sensitive + sddm_config.optionxform = str + + if os.path.isfile(sddm_conf_path): + sddm_config.read(sddm_conf_path) + + if 'Autologin' not in sddm_config: + sddm_config.add_section('Autologin') + + if do_autologin: + sddm_config.set('Autologin', 'User', username) + elif sddm_config.has_option('Autologin', 'User'): + sddm_config.remove_option('Autologin', 'User') + + if default_desktop_environment is not None: + sddm_config.set( + 'Autologin', + 'Session', + default_desktop_environment.desktop_file + ) + + with open(sddm_conf_path, 'w') as sddm_config_file: + sddm_config.write(sddm_config_file, space_around_delimiters=False) + + + def basic_setup(self): + pass + + def desktop_environment_setup(self, desktop_environment): + pass + + def greeter_setup(self): + pass + + +class DMgreetd(DisplayManager): + name = "greetd" + executable = "greetd" + greeter_user = "greeter" + greeter_group = "greetd" + greeter_css_location = None + config_data = {} + + def os_path(self, path): + return os.path.join(self.root_mount_point, path) + + def config_path(self): + return self.os_path("etc/greetd/config.toml") + + def environments_path(self): + return self.os_path("etc/greetd/environments") + + def config_load(self): + import toml + + if (os.path.exists(self.config_path())): + with open(self.config_path(), "r") as f: + self.config_data = toml.load(f) + + self.config_data['terminal'] = dict(vt = "next") + + default_session_group = self.config_data.get('default_session', None) + if not default_session_group: + self.config_data['default_session'] = {} + + self.config_data['default_session']['user'] = self.greeter_user + + return self.config_data + + def config_write(self): + import toml + with open(self.config_path(), "w") as f: + toml.dump(self.config_data, f) + + def basic_setup(self): + if libcalamares.utils.target_env_call( + ['getent', 'group', self.greeter_group] + ) != 0: + libcalamares.utils.target_env_call( + ['groupadd', self.greeter_group] + ) + + if libcalamares.utils.target_env_call( + ['getent', 'passwd', self.greeter_user] + ) != 0: + libcalamares.utils.target_env_call( + ['useradd', + '-c', '"Greeter User"', + '-g', self.greeter_group, + '-s', '/bin/bash', + self.greeter_user + ] + ) + + def desktop_environment_setup(self, default_desktop_environment): + with open(self.environments_path(), 'w') as envs_file: + envs_file.write(default_desktop_environment.executable) + envs_file.write("\n") + + def greeter_setup(self): + pass + + def set_autologin(self, username, do_autologin, default_desktop_environment): + self.config_load() + + de_command = default_desktop_environment.executable + if os.path.exists(self.os_path("usr/bin/gtkgreet")) and os.path.exists(self.os_path("usr/bin/cage")): + self.config_data['default_session']['command'] = "cage -d -s -- gtkgreet" + if self.greeter_css_location: + self.config_data['default_session']['command'] += f" -s {self.greeter_css_location}" + elif os.path.exists(self.os_path("usr/bin/tuigreet")): + tuigreet_base_cmd = "tuigreet --remember --time --issue --asterisks --cmd " + self.config_data['default_session']['command'] = tuigreet_base_cmd + de_command + elif os.path.exists(self.os_path("usr/bin/ddlm")): + self.config_data['default_session']['command'] = "ddlm --target " + de_command + else: + self.config_data['default_session']['command'] = "agreety --cmd " + de_command + + if do_autologin: + # Log in as user, with given DE + self.config_data['initial_session'] = dict(command = de_command, user = username) + elif 'initial_session' in self.config_data: + # No autologin, remove any autologin that was copied from the live ISO + del self.config_data['initial_session'] + + self.config_write() + + +class DMsysconfig(DisplayManager): + name = "sysconfig" + executable = None + + def set_autologin(self, username, do_autologin, default_desktop_environment): + dmauto = "DISPLAYMANAGER_AUTOLOGIN" + + os.system( + "sed -i -e 's|^{!s}=.*|{!s}=\"{!s}\"|' " + "{!s}/etc/sysconfig/displaymanager".format( + dmauto, dmauto, + username if do_autologin else "", + self.root_mount_point + ) + ) + + + def basic_setup(self): + pass + + def desktop_environment_setup(self, desktop_environment): + pass + + def greeter_setup(self): + pass + + # For openSUSE-derivatives, there is only sysconfig to configure, + # and no special DM configuration for it. Instead, check that + # sysconfig is available in the target. + def have_dm(self): + config = "{!s}/etc/sysconfig/displaymanager".format(self.root_mount_point) + return os.path.exists(config) + + +# Collect all the subclasses of DisplayManager defined above, +# and index them based on the name property of each class. +display_managers = [ + (c.name, c) + for c in globals().values() + if type(c) is abc.ABCMeta and issubclass(c, DisplayManager) and c.name +] + + +def run(): + """ + Configure display managers. + + We acquire a list of displaymanagers, either from config or (overridden) + from globalstorage. This module will try to set up (including autologin) + all the displaymanagers in the list, in that specific order. Most distros + will probably only ship one displaymanager. + If a displaymanager is in the list but not installed, a debugging message + is printed and the entry ignored. + """ + # Get configuration settings for display managers + displaymanagers = None + if "displaymanagers" in libcalamares.job.configuration: + displaymanagers = libcalamares.job.configuration["displaymanagers"] + + if libcalamares.globalstorage.contains("displayManagers"): + displaymanagers = libcalamares.globalstorage.value("displayManagers") + + if ("sysconfigSetup" in libcalamares.job.configuration + and libcalamares.job.configuration["sysconfigSetup"]): + displaymanagers = ["sysconfig"] + + if not displaymanagers: + return ( + _("No display managers selected for the displaymanager module."), + _("The displaymanagers list is empty or undefined in both " + "globalstorage and displaymanager.conf.") + ) + + # Get instances that are actually installed + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + dm_impl = [] + dm_names = displaymanagers[:] + for dm in dm_names: + # Find the implementation class + dm_instance = None + impl = [ cls for name, cls in display_managers if name == dm ] + if len(impl) == 1: + dm_instance = impl[0](root_mount_point) + if dm_instance.have_dm(): + dm_impl.append(dm_instance) + else: + dm_instance = None + else: + libcalamares.utils.debug("{!s} has {!s} implementation classes.".format(dm, len(impl))) + + if dm_instance is None: + libcalamares.utils.debug("{!s} selected but not installed".format(dm)) + if dm in displaymanagers: + displaymanagers.remove(dm) + + if not dm_impl: + libcalamares.utils.warning( + "No display managers selected for the displaymanager module. " + "The list is empty after checking for installed display managers." + ) + return None + + # Pick up remaining settings + if "defaultDesktopEnvironment" in libcalamares.job.configuration: + entry = libcalamares.job.configuration["defaultDesktopEnvironment"] + default_desktop_environment = DesktopEnvironment( + entry["executable"], entry["desktopFile"] + ) + # Adjust if executable is bad, but desktopFile isn't. + if not default_desktop_environment.update_from_desktop_file(root_mount_point): + libcalamares.utils.warning( + "The configured default desktop environment, {!s}, " + "can not be found.".format(default_desktop_environment.desktop_file)) + else: + default_desktop_environment = find_desktop_environment( + root_mount_point + ) + + if "basicSetup" in libcalamares.job.configuration: + enable_basic_setup = libcalamares.job.configuration["basicSetup"] + else: + enable_basic_setup = False + + username = libcalamares.globalstorage.value("autoLoginUser") + if username is not None: + do_autologin = True + libcalamares.utils.debug("Setting up autologin for user {!s}.".format(username)) + else: + do_autologin = False + libcalamares.utils.debug("Unsetting autologin.") + + libcalamares.globalstorage.insert("displayManagers", displaymanagers) + + # Do the actual configuration and collect messages + dm_setup_message = [] + for dm in dm_impl: + dm_specific_configuration = libcalamares.job.configuration.get(dm.name, None) + if dm_specific_configuration and isinstance(dm_specific_configuration, dict): + for k, v in dm_specific_configuration.items(): + if hasattr(dm, k): + setattr(dm, k, v) + dm_message = None + if enable_basic_setup: + dm_message = dm.basic_setup() + if default_desktop_environment is not None and dm_message is None: + dm_message = dm.desktop_environment_setup(default_desktop_environment) + if dm_message is None: + dm_message = dm.greeter_setup() + if dm_message is None: + dm_message = dm.set_autologin(username, do_autologin, default_desktop_environment) + + if dm_message is not None: + dm_setup_message.append("{!s}: {!s}".format(*dm_message)) + + if dm_setup_message: + return ( + _("Display manager configuration was incomplete"), + "\n".join(dm_setup_message) + ) diff --git a/airootfs/usr/lib/calamares/modules/displaymanager/module.desc b/airootfs/usr/lib/calamares/modules/displaymanager/module.desc new file mode 100644 index 0000000..a589418 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/displaymanager/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "displaymanager" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/finished/libcalamares_viewmodule_finished.so b/airootfs/usr/lib/calamares/modules/finished/libcalamares_viewmodule_finished.so new file mode 100755 index 0000000..31ba7a5 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/finished/libcalamares_viewmodule_finished.so differ diff --git a/airootfs/usr/lib/calamares/modules/finished/module.desc b/airootfs/usr/lib/calamares/modules/finished/module.desc new file mode 100644 index 0000000..4019ed6 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/finished/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "finished" +interface: "qtplugin" +load: "libcalamares_viewmodule_finished.so" diff --git a/airootfs/usr/lib/calamares/modules/finishedq/libcalamares_viewmodule_finishedq.so b/airootfs/usr/lib/calamares/modules/finishedq/libcalamares_viewmodule_finishedq.so new file mode 100755 index 0000000..7177928 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/finishedq/libcalamares_viewmodule_finishedq.so differ diff --git a/airootfs/usr/lib/calamares/modules/finishedq/module.desc b/airootfs/usr/lib/calamares/modules/finishedq/module.desc new file mode 100644 index 0000000..1aa28ae --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/finishedq/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "finishedq" +interface: "qtplugin" +load: "libcalamares_viewmodule_finishedq.so" diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/CMakeLists.txt b/airootfs/usr/lib/calamares/modules/flatpakinfo/CMakeLists.txt new file mode 100644 index 0000000..5ecba15 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/CMakeLists.txt @@ -0,0 +1,17 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +calamares_add_plugin(flatpakInfo + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + FlatpakInfoJob.h + ItemFlatpak.h + PackagePool.h + FlatpakInfoJob.cpp + ItemFlatpak.cpp + PackagePool.cpp + SHARED_LIB +) diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/FlatpakInfoJob.cpp b/airootfs/usr/lib/calamares/modules/flatpakinfo/FlatpakInfoJob.cpp new file mode 100644 index 0000000..9b492db --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/FlatpakInfoJob.cpp @@ -0,0 +1,63 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#include "FlatpakInfoJob.h" + +#include "utils/Runner.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" + +#include + +#include + +#include "ItemFlatpak.h" +#include "PackagePool.h" + +FlatpakInfoJob::FlatpakInfoJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + +FlatpakInfoJob::~FlatpakInfoJob() +{ + ItemFlatpak_freeMem(); +} + +QString +FlatpakInfoJob::prettyName() const +{ + return tr( "Fill netinstall with flatpak packages" ); +} + + +Calamares::JobResult +FlatpakInfoJob::exec() +{ + QVariantList partitions; + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage(); + + + downloadPackagesInfo(); + serializePackagesInfo(); + + return Calamares::JobResult::ok(); +} + + +void +FlatpakInfoJob::setConfigurationMap( const QVariantMap& map ) +{ +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( FlatpakInfoJobFactory, registerPlugin< FlatpakInfoJob >(); ) diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/FlatpakInfoJob.h b/airootfs/usr/lib/calamares/modules/flatpakinfo/FlatpakInfoJob.h new file mode 100644 index 0000000..1371343 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/FlatpakInfoJob.h @@ -0,0 +1,43 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +#ifndef FLATPAKINFOJOB_H +#define FLATPAKINFOJOB_H + +#include +#include +#include + +#include "CppJob.h" + +#include "utils/PluginFactory.h" + +#include "DllMacro.h" + +/** @brief Create zpools and zfs datasets + * + */ +class PLUGINDLLEXPORT FlatpakInfoJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit FlatpakInfoJob( QObject* parent = nullptr ); + ~FlatpakInfoJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( FlatpakInfoJobFactory ) + +#endif // ZFSJOB_H diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/ItemFlatpak.cpp b/airootfs/usr/lib/calamares/modules/flatpakinfo/ItemFlatpak.cpp new file mode 100644 index 0000000..540c2e2 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/ItemFlatpak.cpp @@ -0,0 +1,66 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* Qt */ +#include + +/* CPP */ +#include +#include + +/* Calamares */ +#include "utils/Runner.h" + +/* Module */ +#include "ItemFlatpak.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +PackageItem +fromFlatpak( const QVariantMap& itemMap, InstalledList &installed ) +{ + // check if it is installed + PackageItem item( Calamares::getString( itemMap, "appstream" ) ); + item.setInstalled( false ); + + item.setInstalled( installed.contains( Calamares::getString( itemMap, "appstream" ) ) ); + + return item; +} + +InstalledList::InstalledList() +{ + long long int prev_pos; + long long int pos = 0; + QString line; + auto process = Calamares::System::instance()->targetEnvCommand( + QStringList { QString::fromLatin1( "flatpak" ), + QString::fromLatin1( "list" ), + QString::fromLatin1( "--app" ), + QString::fromLatin1( "--columns=application" ) } ); + auto outputStr = process.second; + + do { + prev_pos = pos; + + pos = outputStr.indexOf('\n', prev_pos); + QString line = outputStr.mid(prev_pos, pos); + installed.append(line); + + /* Increase by 1 to not stuck on newline */ + ++pos; + + /* QString::indexOf returns -1 since no occurences. 0 = -1 + 1.*/ + } while (0 != pos); +} + +InstalledList::~InstalledList() +{ + installed.clear(); +} diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/ItemFlatpak.h b/airootfs/usr/lib/calamares/modules/flatpakinfo/ItemFlatpak.h new file mode 100644 index 0000000..e69de29 diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/PackagePool.cpp b/airootfs/usr/lib/calamares/modules/flatpakinfo/PackagePool.cpp new file mode 100644 index 0000000..dca9a5e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/PackagePool.cpp @@ -0,0 +1,110 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2023 Sławomir Lach + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "utils/Logger.h" +#include "utils/Variant.h" +#include "ItemFlatpak.h" +#include "PackagePool.h" + +#include "utils/System.h" + +void PackagePool::downloadPackagesInfo(InstalledList &list) +{ + QHash addedPackages; + QString line; + auto process = Calamares::System::instance()->targetEnvCommand( QStringList { QString::fromStdString( "flatpak" ), QString::fromStdString( "remotes" ), QString::fromStdString( "--columns=name" ) }); + auto outputStr = process.second; + QTextStream output(&outputStr); + + while (output.readLineInto(&line)) + { + QString line2; + auto process2 = Calamares::System::instance()->targetEnvCommand( + QStringList { QString::fromStdString( "flatpak" ), + QString::fromStdString( "remote-ls" ), + QString::fromStdString( "--app" ), + QString::fromStdString( "--columns=application" ), + line } ); + auto output2Str = process2.second; + QTextStream output2( &output2Str ); + + while ( output2.readLineInto( &line2 ) ) + { + if ( line2 == "" ) + { + continue; + } + QVariantMap itemMap; + + if ( addedPackages.contains( line2 ) ) + { + continue; + } + + addedPackages.insert( line2, true ); + + itemMap.insert( "appstream", QVariant( line2 ) ); + itemMap.insert( "id", QVariant( line2 ) ); + + PackageItem item = fromFlatpak( itemMap, list ); + packages.append( item ); + } + } + + serializePackagesInfo(); +} + +void PackagePool::serializePackagesInfo() +{ + QList changedValue; + auto* gs = Calamares::JobQueue::instance()->globalStorage(); + + // If an earlier packagechooser instance added this data to global storage, combine them + if ( gs->contains( "netinstallAdd" ) ) + { + auto selectedOrig = gs->value( "netinstallAdd" ); + + changedValue = selectedOrig.toList(); + for (auto current: packages) + { + QStringList selfInstall; + QVariantMap newValue; + newValue.insert("name", current.getAppStreamId()); + + if (current.getInstalled()) + { + newValue.insert("selected", true); + newValue.insert("immutable", true); + newValue.insert("description", "[Already installed; cannot be uninstalled]"); + } + else + { + newValue.insert("selected", false); + } + selfInstall.append(current.getAppStreamId()); + newValue.insert("packages", selfInstall); + changedValue.append(newValue); + } + + gs->remove( "netinstallAdd" ); + } + gs->insert( "netinstallAdd", changedValue ); +} diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/PackagePool.h b/airootfs/usr/lib/calamares/modules/flatpakinfo/PackagePool.h new file mode 100644 index 0000000..e69de29 diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/flatpakInfo.conf b/airootfs/usr/lib/calamares/modules/flatpakinfo/flatpakInfo.conf new file mode 100644 index 0000000..93b2349 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/flatpakInfo.conf @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The flatpakinfo module will collect package list from configured flatpak repositories +# +# +# +--- diff --git a/airootfs/usr/lib/calamares/modules/flatpakinfo/module.desc b/airootfs/usr/lib/calamares/modules/flatpakinfo/module.desc new file mode 100644 index 0000000..b5ac4cb --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/flatpakinfo/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "flatpakinfo" +interface: "qtplugin" +load: "libcalamares_job_flatpakInfo.so" diff --git a/airootfs/usr/lib/calamares/modules/freebsddisk/CMakeLists.txt b/airootfs/usr/lib/calamares/modules/freebsddisk/CMakeLists.txt new file mode 100644 index 0000000..4739482 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/freebsddisk/CMakeLists.txt @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +if( NOT Calamares_WITH_QML ) + calamares_skip_module( "freebsddisk (QML is not supported in this build)" ) + return() +endif() + +calamares_add_plugin( freebsddisk + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + FreeBSDDiskViewStep.cpp + RESOURCES + freebsddisk.qrc + SHARED_LIB +) diff --git a/airootfs/usr/lib/calamares/modules/freebsddisk/FreeBSDDiskViewStep.cpp b/airootfs/usr/lib/calamares/modules/freebsddisk/FreeBSDDiskViewStep.cpp new file mode 100644 index 0000000..11d4a85 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/freebsddisk/FreeBSDDiskViewStep.cpp @@ -0,0 +1,42 @@ +/* === This file is part of Calamares - === + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSES/GPL-3.0 + */ + +#include "FreeBSDDiskViewStep.h" + +FreeBSDDiskViewStep::FreeBSDDiskViewStep( QObject* parent ) + : Calamares::QmlViewStep( parent ) +{ +} + +FreeBSDDiskViewStep::~FreeBSDDiskViewStep() {} + +QString +FreeBSDDiskViewStep::prettyName() const +{ + return tr( "Disk Setup" ); +} + +void +FreeBSDDiskViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + Calamares::QmlViewStep::setConfigurationMap( configurationMap ); // call parent implementation last +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDDiskViewStepFactory, registerPlugin< FreeBSDDiskViewStep >(); ) diff --git a/airootfs/usr/lib/calamares/modules/freebsddisk/FreeBSDDiskViewStep.h b/airootfs/usr/lib/calamares/modules/freebsddisk/FreeBSDDiskViewStep.h new file mode 100644 index 0000000..ba19a01 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/freebsddisk/FreeBSDDiskViewStep.h @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSES/GPL-3.0 + */ + + +#ifndef FREEBSDDISKVIEWSTEP_H +#define FREEBSDDISKVIEWSTEP_H + +#include "DllMacro.h" +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" + +class PLUGINDLLEXPORT FreeBSDDiskViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + FreeBSDDiskViewStep( QObject* parent = nullptr ); + virtual ~FreeBSDDiskViewStep() override; + + QString prettyName() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDDiskViewStepFactory ) + +#endif diff --git a/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.conf b/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.conf new file mode 100644 index 0000000..c0462bd --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.conf @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The *freebsddisk* module can be used to pick a disk +# as an installer step. This module supports ZFSroot +# on one whole disk, and UFSroot on one whole disk. +# +--- +qmlSearch: both + diff --git a/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.qml b/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.qml new file mode 100644 index 0000000..57b2fba --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.qml @@ -0,0 +1,35 @@ +/* === This file is part of Calamares - === + * + * Calamares is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Calamares is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Calamares. If not, see . + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSES/GPL-3.0 + */ + +import io.calamares.ui 1.0 + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.3 +import QtQuick.Controls.Material 2.1 + +Item { + Text { + anchors.top: parent.top + anchors.topMargin: 10 + text: "Select a disk on which to install FreeBSD." + } +} diff --git a/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.qrc b/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.qrc new file mode 100644 index 0000000..3f87d28 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/freebsddisk/freebsddisk.qrc @@ -0,0 +1,5 @@ + + + freebsddisk.qml + + diff --git a/airootfs/usr/lib/calamares/modules/fsresizer/libcalamares_job_fsresizer.so b/airootfs/usr/lib/calamares/modules/fsresizer/libcalamares_job_fsresizer.so new file mode 100755 index 0000000..5a83f56 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/fsresizer/libcalamares_job_fsresizer.so differ diff --git a/airootfs/usr/lib/calamares/modules/fsresizer/module.desc b/airootfs/usr/lib/calamares/modules/fsresizer/module.desc new file mode 100644 index 0000000..7d80a40 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/fsresizer/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "fsresizer" +interface: "qtplugin" +load: "libcalamares_job_fsresizer.so" diff --git a/airootfs/usr/lib/calamares/modules/fstab/fstab.schema.yaml b/airootfs/usr/lib/calamares/modules/fstab/fstab.schema.yaml new file mode 100644 index 0000000..e298a9d --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/fstab/fstab.schema.yaml @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/fstab +additionalProperties: false +type: object +properties: + crypttabOptions: { type: string } + tmpOptions: + type: object + additionalProperties: false + properties: + "default": + type: object + additionalProperties: false + properties: + tmpfs: { type: boolean } + options: { type: string } + ssd: + type: object + additionalProperties: false + properties: + tmpfs: { type: boolean } + options: { type: string } + required: [ "default" ] +required: + - tmpOptions diff --git a/airootfs/usr/lib/calamares/modules/fstab/main.py b/airootfs/usr/lib/calamares/modules/fstab/main.py new file mode 100644 index 0000000..bd7ae77 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/fstab/main.py @@ -0,0 +1,436 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Aurélien Gâteau +# SPDX-FileCopyrightText: 2016 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import os +import re +import copy + +import libcalamares + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Writing fstab.") + + +FSTAB_HEADER = """# /etc/fstab: static file system information. +# +# Use 'blkid' to print the universally unique identifier for a device; this may +# be used with UUID= as a more robust way to name devices that works even if +# disks are added and removed. See fstab(5). +# +# """ + +CRYPTTAB_HEADER = """# /etc/crypttab: mappings for encrypted partitions. +# +# Each mapped device will be created in /dev/mapper, so your /etc/fstab +# should use the /dev/mapper/ paths for encrypted devices. +# +# See crypttab(5) for the supported syntax. +# +# NOTE: You need not list your root (/) partition here, but it must be set up +# beforehand by the initramfs (/etc/mkinitcpio.conf). The same applies +# to encrypted swap, which should be set up with mkinitcpio-openswap +# for resume support. +# +# """ + +# Turn Parted filesystem names into fstab names +FS_MAP = { + "fat16": "vfat", + "fat32": "vfat", + "linuxswap": "swap", +} + + +def mkdir_p(path): + """ Create directory. + + :param path: + """ + if not os.path.exists(path): + os.makedirs(path) + + +def is_ssd_disk(disk_name): + """ Checks if given disk is actually a ssd disk. + + :param disk_name: + :return: + """ + filename = os.path.join("/sys/block", disk_name, "queue/rotational") + + if not os.path.exists(filename): + # Should not happen unless sysfs changes, but better safe than sorry + return False + + with open(filename) as sysfile: + return sysfile.read() == "0\n" + + +def disk_name_for_partition(partition): + """ Returns disk name for each found partition. + + :param partition: + :return: + """ + name = os.path.basename(partition["device"]) + + if name.startswith("mmcblk") or name.startswith("nvme"): + # Typical mmc device is mmcblk0p1, nvme looks like nvme0n1p2 + return re.sub("p[0-9]+$", "", name) + + return re.sub("[0-9]+$", "", name) + + +class FstabGenerator(object): + def __init__(self, partitions, root_mount_point, mount_options_list, + crypttab_options, tmp_options): + self.partitions = partitions + self.root_mount_point = root_mount_point + self.mount_options_list = mount_options_list + self.crypttab_options = crypttab_options + self.tmp_options = tmp_options + self.ssd_disks = set() + self.root_is_ssd = False + + def run(self): + """ Calls needed sub routines. + + :return: + """ + self.find_ssd_disks() + self.generate_fstab() + self.generate_crypttab() + self.create_mount_points() + + return None + + def find_ssd_disks(self): + """ Checks for ssd disks """ + disks = {disk_name_for_partition(x) for x in self.partitions} + self.ssd_disks = {x for x in disks if is_ssd_disk(x)} + + def generate_crypttab(self): + """ Create crypttab. """ + mkdir_p(os.path.join(self.root_mount_point, "etc")) + crypttab_path = os.path.join(self.root_mount_point, "etc", "crypttab") + + with open(crypttab_path, "w") as crypttab_file: + print(CRYPTTAB_HEADER, file=crypttab_file) + + for partition in self.partitions: + dct = self.generate_crypttab_line_info(partition) + + if dct: + self.print_crypttab_line(dct, file=crypttab_file) + + def generate_crypttab_line_info(self, partition): + """ Generates information for each crypttab entry. """ + if "luksMapperName" not in partition or "luksUuid" not in partition: + return None + + mapper_name = partition["luksMapperName"] + luks_uuid = partition["luksUuid"] + if not mapper_name or not luks_uuid: + return None + + crypttab_options = self.crypttab_options + # Make sure to not use missing keyfile + if os.path.isfile(os.path.join(self.root_mount_point, "crypto_keyfile.bin")): + password = "/crypto_keyfile.bin" + else: + password = "none" + + # Set crypttab password for partition to none and remove crypttab options + # if root partition was not encrypted + if any([p["mountPoint"] == "/" + and "luksMapperName" not in p + for p in self.partitions]): + password = "none" + crypttab_options = "" + # on root partition when /boot is unencrypted + elif partition["mountPoint"] == "/": + if any([p["mountPoint"] == "/boot" + and "luksMapperName" not in p + for p in self.partitions]): + password = "none" + crypttab_options = "" + + return dict( + name=mapper_name, + device="UUID=" + luks_uuid, + password=password, + options=crypttab_options, + ) + + def print_crypttab_line(self, dct, file=None): + """ Prints line to '/etc/crypttab' file. """ + line = "{:21} {:<45} {} {}".format(dct["name"], + dct["device"], + dct["password"], + dct["options"], + ) + + print(line, file=file) + + def generate_fstab(self): + """ Create fstab. """ + mkdir_p(os.path.join(self.root_mount_point, "etc")) + fstab_path = os.path.join(self.root_mount_point, "etc", "fstab") + + with open(fstab_path, "w") as fstab_file: + print(FSTAB_HEADER, file=fstab_file) + + for partition in self.partitions: + # Special treatment for a btrfs subvolumes + if (partition["fs"] == "btrfs" + and partition["mountPoint"] == "/"): + # Subvolume list has been created in mount.conf and curated in mount module, + # so all subvolumes here should be safe to add to fstab + btrfs_subvolumes = libcalamares.globalstorage.value("btrfsSubvolumes") + for s in btrfs_subvolumes: + mount_entry = copy.deepcopy(partition) + mount_entry["mountPoint"] = s["mountPoint"] + mount_entry["subvol"] = s["subvolume"] + dct = self.generate_fstab_line_info(mount_entry) + if dct: + self.print_fstab_line(dct, file=fstab_file) + elif partition["fs"] != "zfs": # zfs partitions don't need an entry in fstab + dct = self.generate_fstab_line_info(partition) + if dct: + self.print_fstab_line(dct, file=fstab_file) + + if self.root_is_ssd: + # Old behavior was to mount /tmp as tmpfs + # New behavior is to use tmpOptions to decide + # if mounting /tmp as tmpfs and which options to use + ssd = self.tmp_options.get("ssd", {}) + if not ssd: + ssd = self.tmp_options.get("default", {}) + # Default to True to mimic old behavior + tmpfs = ssd.get("tmpfs", True) + + if tmpfs: + options = ssd.get("options", "defaults,noatime,mode=1777") + # Mount /tmp on a tmpfs + dct = dict(device="tmpfs", + mount_point="/tmp", + fs="tmpfs", + options=options, + check=0, + ) + self.print_fstab_line(dct, file=fstab_file) + + def generate_fstab_line_info(self, partition): + """ + Generates information (a dictionary of fstab-fields) + for the given @p partition. + """ + # Some "fs" names need special handling in /etc/fstab, so remap them. + filesystem = partition["fs"].lower() + filesystem = FS_MAP.get(filesystem, filesystem) + luks_mapper_name = partition.get("luksMapperName", None) + mount_point = partition["mountPoint"] + disk_name = disk_name_for_partition(partition) + is_ssd = disk_name in self.ssd_disks + + # Swap partitions are called "linuxswap" by parted. + # That "fs" is visible in GS, but that gets mapped + # to "swap", above, because that's the spelling needed in /etc/fstab + if not mount_point and not filesystem == "swap": + return None + if not mount_point: + mount_point = "swap" + + if filesystem == "swap" and not partition.get("claimed", None): + libcalamares.utils.debug("Ignoring foreign swap {!s} {!s}".format(disk_name, partition.get("uuid", None))) + return None + + options = self.get_mount_options(mount_point) + + if mount_point == "/" and filesystem != "btrfs": + check = 1 + elif mount_point and mount_point != "swap" and filesystem != "btrfs": + check = 2 + else: + check = 0 + + if mount_point == "/": + self.root_is_ssd = is_ssd + + # If there's a set-and-not-empty subvolume set, add it + if filesystem == "btrfs" and partition.get("subvol",None): + options = "subvol={},".format(partition["subvol"]) + options + + device = None + if luks_mapper_name: + device = "/dev/mapper/" + luks_mapper_name + elif partition["uuid"]: + device = "UUID=" + partition["uuid"] + else: + device = partition["device"] + + if not device: + # TODO: we get here when the user mounted a previously encrypted partition + # This should be catched early in the process + return None + + return dict(device=device, + mount_point=mount_point, + fs=filesystem, + options=options, + check=check, + ) + + def print_fstab_line(self, dct, file=None): + """ Prints line to '/etc/fstab' file. """ + line = "{:41} {:<14} {:<7} {:<10} 0 {}".format(dct["device"], + dct["mount_point"], + dct["fs"], + dct["options"], + dct["check"], + ) + print(line, file=file) + + def create_mount_points(self): + """ Creates mount points """ + for partition in self.partitions: + if partition["mountPoint"]: + mkdir_p(self.root_mount_point + partition["mountPoint"]) + + def get_mount_options(self, mountpoint): + """ + Returns the mount options for a given mountpoint + + :param mountpoint: A string containing the mountpoint for the fstab entry + :return: A string containing the mount options for the entry or "defaults" if nothing is found + """ + mount_options_item = next((x for x in self.mount_options_list if x.get("mountpoint") == mountpoint), None) + if mount_options_item: + return mount_options_item.get("option_string", "defaults") + else: + return "defaults" + + +def create_swapfile(root_mount_point, root_btrfs): + """ + Creates /swapfile in @p root_mount_point ; if the root filesystem + is on btrfs, then handle some btrfs specific features as well, + as documented in + https://wiki.archlinux.org/index.php/Swap#Swap_file + + The swapfile-creation covers progress from 0.2 to 0.5 + """ + libcalamares.job.setprogress(0.2) + if root_btrfs: + # btrfs swapfiles must reside on a subvolume that is not snapshotted to prevent file system corruption + swapfile_path = os.path.join(root_mount_point, "swap/swapfile") + with open(swapfile_path, "wb") as f: + pass + libcalamares.utils.host_env_process_output(["chattr", "+C", "+m", swapfile_path]) # No Copy-on-Write, no compression + else: + swapfile_path = os.path.join(root_mount_point, "swapfile") + with open(swapfile_path, "wb") as f: + pass + # Create the swapfile; swapfiles are small-ish + zeroes = bytes(16384) + with open(swapfile_path, "wb") as f: + total = 0 + desired_size = 512 * 1024 * 1024 # 512MiB + while total < desired_size: + chunk = f.write(zeroes) + if chunk < 1: + libcalamares.utils.debug("Short write on {!s}, cancelling.".format(swapfile_path)) + break + libcalamares.job.setprogress(0.2 + 0.3 * ( total / desired_size ) ) + total += chunk + os.chmod(swapfile_path, 0o600) + libcalamares.utils.host_env_process_output(["mkswap", swapfile_path]) + libcalamares.job.setprogress(0.5) + + +def run(): + """ Configures fstab. + + :return: + """ + global_storage = libcalamares.globalstorage + conf = libcalamares.job.configuration + partitions = global_storage.value("partitions") + root_mount_point = global_storage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}" + .format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}
to use.") + .format("fstab")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}" + .format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}
to use.") + .format("fstab")) + + # This follows the GS settings from the partition module's Config object + swap_choice = global_storage.value( "partitionChoices" ) + if swap_choice: + swap_choice = swap_choice.get( "swap", None ) + if swap_choice and swap_choice == "file": + # There's no formatted partition for it, so we'll sneak in an entry + root_partitions = [ p["fs"].lower() for p in partitions if p["mountPoint"] == "/" ] + root_btrfs = (root_partitions[0] == "btrfs") if root_partitions else False + if root_btrfs: + partitions.append( dict(fs="swap", mountPoint=None, claimed=True, device="/swap/swapfile", uuid=None) ) + else: + partitions.append( dict(fs="swap", mountPoint=None, claimed=True, device="/swapfile", uuid=None) ) + else: + swap_choice = None + + libcalamares.job.setprogress(0.1) + mount_options_list = global_storage.value("mountOptionsList") + crypttab_options = conf.get("crypttabOptions", "luks") + tmp_options = conf.get("tmpOptions", {}) + + # We rely on mount_options having a default; if there wasn't one, + # bail out with a meaningful error. + if not mount_options_list: + libcalamares.utils.warning("No mount options defined, {!s} partitions".format(len(partitions))) + return (_("Configuration Error"), + _("No
{!s}
configuration is given for
{!s}
to use.") + .format("mountOptions", "fstab")) + + generator = FstabGenerator(partitions, + root_mount_point, + mount_options_list, + crypttab_options, + tmp_options) + + if swap_choice is not None: + libcalamares.job.setprogress(0.2) + root_partitions = [ p["fs"].lower() for p in partitions if p["mountPoint"] == "/" ] + root_btrfs = (root_partitions[0] == "btrfs") if root_partitions else False + create_swapfile(root_mount_point, root_btrfs) + + try: + libcalamares.job.setprogress(0.5) + return generator.run() + finally: + libcalamares.job.setprogress(1.0) diff --git a/airootfs/usr/lib/calamares/modules/fstab/module.desc b/airootfs/usr/lib/calamares/modules/fstab/module.desc new file mode 100644 index 0000000..77cb7ad --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/fstab/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "fstab" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/fstab/test.yaml b/airootfs/usr/lib/calamares/modules/fstab/test.yaml new file mode 100644 index 0000000..cd20345 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/fstab/test.yaml @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +rootMountPoint: /tmp/mount +partitions: + - device: /dev/sda1 + fs: ext4 + mountPoint: / + uuid: 2a00f1d5-1217-49a7-bedd-b55c85764732 + - device: /dev/sda2 + fs: swap + uuid: 59406569-446f-4730-a874-9f6b4b44fee3 + mountPoint: + - device: /dev/sdb1 + fs: btrfs + mountPoint: /home + uuid: 59406569-abcd-1234-a874-9f6b4b44fee3 diff --git a/airootfs/usr/lib/calamares/modules/fstab/test2.yaml b/airootfs/usr/lib/calamares/modules/fstab/test2.yaml new file mode 100644 index 0000000..0e91bf6 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/fstab/test2.yaml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# This test shows how btrfs root would work +rootMountPoint: /tmp/mount +partitions: + - device: /dev/sda1 + fs: btrfs + mountPoint: / + uuid: 2a00f1d5-1217-49a7-bedd-b55c85764732 + - device: /dev/sda2 + fs: swap + uuid: 59406569-446f-4730-a874-9f6b4b44fee3 + mountPoint: + - device: /dev/sdb1 + fs: btrfs + mountPoint: /home + uuid: 59406569-abcd-1234-a874-9f6b4b44fee3 +btrfsSubvolumes: + - mountPoint: / + subvolume: "@ROOT" + - mountPoint: /var + subvolume: "@var" + - mountPoint: /usr/local + subvolume: "@local" diff --git a/airootfs/usr/lib/calamares/modules/grubcfg/grubcfg.schema.yaml b/airootfs/usr/lib/calamares/modules/grubcfg/grubcfg.schema.yaml new file mode 100644 index 0000000..c04c0b7 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/grubcfg/grubcfg.schema.yaml @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/grubcfg +additionalProperties: false +type: object +properties: + overwrite: { type: boolean, default: false } + keep_distributor: { type: boolean, default: false } + prefer_grub_d: { type: boolean, default: false } + kernel_params: { type: array, items: { type: string } } + defaults: + type: object + additionalProperties: true # Other fields are acceptable + properties: + GRUB_TIMEOUT: { type: integer } + GRUB_DEFAULT: { type: string } + GRUB_DISABLE_SUBMENU: { type: boolean, default: true } + GRUB_TERMINAL_OUTPUT: { type: string } + GRUB_DISABLE_RECOVERY: { type: boolean, default: true } + required: [ GRUB_TIMEOUT, GRUB_DEFAULT ] + always_use_defaults: { type: boolean, default: false } diff --git a/airootfs/usr/lib/calamares/modules/grubcfg/main.py b/airootfs/usr/lib/calamares/modules/grubcfg/main.py new file mode 100644 index 0000000..346a267 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/grubcfg/main.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014-2015 Philip Müller +# SPDX-FileCopyrightText: 2015-2017 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2017 2019, Adriaan de Groot +# SPDX-FileCopyrightText: 2017-2018 Gabriel Craciunescu +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares +import fileinput +import os +import re +import shutil + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configure GRUB.") + + +def get_grub_config_path(root_mount_point): + """ + Figures out where to put the grub config files. Returns + a the full path of a file inside that + directory, as "the config file". + + Returns a path into @p root_mount_point. + """ + default_dir = os.path.join(root_mount_point, "etc/default") + default_config_file = "grub" + + if "prefer_grub_d" in libcalamares.job.configuration and libcalamares.job.configuration["prefer_grub_d"]: + possible_dir = os.path.join(root_mount_point, "etc/default/grub.d") + if os.path.exists(possible_dir) and os.path.isdir(possible_dir): + default_dir = possible_dir + default_config_file = "00calamares.cfg" + + if not os.path.exists(default_dir): + try: + os.mkdir(default_dir) + except Exception as error: + # exception as error is still redundant, but it print out the error + # identify a solution for each exception and + # if possible and code it within. + libcalamares.utils.debug(f"Failed to create {default_dir}") + libcalamares.utils.debug(f"{error}") + raise + + return os.path.join(default_dir, default_config_file) + + +def get_zfs_root(): + """ + Looks in global storage to find the zfs root + + :return: A string containing the path to the zfs root or None if it is not found + """ + + zfs = libcalamares.globalstorage.value("zfsDatasets") + + if not zfs: + libcalamares.utils.warning("Failed to locate zfs dataset list") + return None + + # Find the root dataset + for dataset in zfs: + try: + if dataset["mountpoint"] == "/": + return dataset["zpool"] + "/" + dataset["dsName"] + except KeyError: + # This should be impossible + libcalamares.utils.warning("Internal error handling zfs dataset") + raise + + return None + + +def update_existing_config(default_grub, grub_config_items): + """ + Updates the existing grub configuration file with any items present in @p grub_config_items + + Items that exist in the file will be updated and new items will be appended to the end + + :param default_grub: The absolute path to the grub config file + :param grub_config_items: A dict holding the key value pairs representing the items + """ + + default_grub_orig = default_grub + ".calamares" + shutil.move(default_grub, default_grub_orig) + + with open(default_grub, "w") as grub_file: + with open(default_grub_orig, "r") as grub_orig_file: + for line in grub_orig_file.readlines(): + line = line.strip() + if "=" in line: + # This may be a key, strip the leading comment if it has one + key = line.lstrip("#").split("=")[0].strip() + + # check if this is noe of the keys we care about + if key in grub_config_items.keys(): + print(f"{key}={grub_config_items[key]}", file=grub_file) + del grub_config_items[key] + else: + print(line, file=grub_file) + else: + print(line, file=grub_file) + + if len(grub_config_items) != 0: + for dict_key, dict_val in grub_config_items.items(): + print(f"{dict_key}={dict_val}", file=grub_file) + + os.remove(default_grub_orig) + + +def modify_grub_default(partitions, root_mount_point, distributor): + """ + Configures '/etc/default/grub' for hibernation and plymouth. + + @see bootloader/main.py, for similar handling of kernel parameters + + :param partitions: + :param root_mount_point: + :param distributor: name of the distributor to fill in for + GRUB_DISTRIBUTOR. Must be a string. If the job setting + *keep_distributor* is set, then this is only used if no + GRUB_DISTRIBUTOR is found at all (otherwise, when *keep_distributor* + is set, the GRUB_DISTRIBUTOR lines are left unchanged). + If *keep_distributor* is unset or false, then GRUB_DISTRIBUTOR + is always updated to set this value. + :return: + """ + default_grub = get_grub_config_path(root_mount_point) + distributor = distributor.replace("'", "'\\''") + dracut_bin = libcalamares.utils.target_env_call( + ["sh", "-c", "which dracut"] + ) + plymouth_bin = libcalamares.utils.target_env_call( + ["sh", "-c", "which plymouth"] + ) + uses_systemd_hook = libcalamares.utils.target_env_call( + ["sh", "-c", "grep -q \"^HOOKS.*systemd\" /etc/mkinitcpio.conf"] + ) == 0 + # Shell exit value 0 means success + have_plymouth = plymouth_bin == 0 + use_systemd_naming = dracut_bin == 0 or uses_systemd_hook + + use_splash = "" + swap_uuid = "" + swap_outer_uuid = "" + swap_outer_mappername = None + no_save_default = False + unencrypted_separate_boot = any(p["mountPoint"] == "/boot" and "luksMapperName" not in p for p in partitions) + # If there is no dracut, and the root partition is ZFS, this gets set below + zfs_root_path = None + + for partition in partitions: + if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs", "zfs"): + no_save_default = True + break + + if have_plymouth: + use_splash = "splash" + + cryptdevice_params = [] + + if use_systemd_naming: + for partition in partitions: + if partition["fs"] == "linuxswap" and not partition.get("claimed", None): + # Skip foreign swap + continue + has_luks = "luksMapperName" in partition + if partition["fs"] == "linuxswap" and not has_luks: + swap_uuid = partition["uuid"] + + if partition["fs"] == "linuxswap" and has_luks: + swap_outer_uuid = partition["luksUuid"] + swap_outer_mappername = partition["luksMapperName"] + + if partition["mountPoint"] == "/" and has_luks: + cryptdevice_params = [f"rd.luks.uuid={partition['luksUuid']}"] + if not unencrypted_separate_boot and uses_systemd_hook: + cryptdevice_params.append("rd.luks.key=/crypto_keyfile.bin") + else: + for partition in partitions: + if partition["fs"] == "linuxswap" and not partition.get("claimed", None): + # Skip foreign swap + continue + has_luks = "luksMapperName" in partition + if partition["fs"] == "linuxswap" and not has_luks: + swap_uuid = partition["uuid"] + + if partition["fs"] == "linuxswap" and has_luks: + swap_outer_mappername = partition["luksMapperName"] + + if partition["mountPoint"] == "/" and has_luks: + cryptdevice_params = [ + f"cryptdevice=UUID={partition['luksUuid']}:{partition['luksMapperName']}", + f"root=/dev/mapper/{partition['luksMapperName']}" + ] + + if partition["fs"] == "zfs" and partition["mountPoint"] == "/": + zfs_root_path = get_zfs_root() + + kernel_params = libcalamares.job.configuration.get("kernel_params", ["quiet"]) + + # Currently, grub doesn't detect this properly so it must be set manually + if zfs_root_path: + kernel_params.insert(0, "zfs=" + zfs_root_path) + + if cryptdevice_params: + kernel_params.extend(cryptdevice_params) + + if use_splash: + kernel_params.append(use_splash) + + if swap_uuid: + kernel_params.append(f"resume=UUID={swap_uuid}") + + if use_systemd_naming and swap_outer_uuid: + kernel_params.append(f"rd.luks.uuid={swap_outer_uuid}") + if swap_outer_mappername: + kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}") + + overwrite = libcalamares.job.configuration.get("overwrite", False) + + grub_config_items = {} + # read the lines we need from the existing config + if os.path.exists(default_grub) and not overwrite: + with open(default_grub, 'r') as grub_file: + lines = [x.strip() for x in grub_file.readlines()] + + for line in lines: + if line.startswith("GRUB_CMDLINE_LINUX_DEFAULT"): + existing_params = re.sub(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*", "", line).strip("\"'").split() + + for existing_param in existing_params: + existing_param_name = existing_param.split("=")[0].strip() + + # Ensure we aren't adding duplicated params + param_exists = False + for param in kernel_params: + if param.split("=")[0].strip() == existing_param_name: + param_exists = True + break + if not param_exists and existing_param_name not in ["quiet", "resume", "splash"]: + kernel_params.append(existing_param) + + elif line.startswith("GRUB_DISTRIBUTOR") and libcalamares.job.configuration.get("keep_distributor", False): + distributor_parts = line.split("=") + if len(distributor_parts) > 1: + distributor = distributor_parts[1].strip("'\"") + + # If a filesystem grub can't write to is used, disable save default + if no_save_default and line.strip().startswith("GRUB_SAVEDEFAULT"): + grub_config_items["GRUB_SAVEDEFAULT"] = "false" + + always_use_defaults = libcalamares.job.configuration.get("always_use_defaults", False) + + # If applicable add the items from defaults to the dict containing the grub config to wirte/modify + if always_use_defaults or overwrite or not os.path.exists(default_grub): + if "defaults" in libcalamares.job.configuration: + for key, value in libcalamares.job.configuration["defaults"].items(): + if isinstance(value, bool): + if value: + escaped_value = "true" + else: + escaped_value = "false" + else: + escaped_value = str(value).replace("'", "'\\''") + + grub_config_items[key] = f"'{escaped_value}'" + + grub_config_items['GRUB_CMDLINE_LINUX_DEFAULT'] = f"'{' '.join(kernel_params)}'" + grub_config_items["GRUB_DISTRIBUTOR"] = f"'{distributor}'" + + if cryptdevice_params and not unencrypted_separate_boot: + grub_config_items["GRUB_ENABLE_CRYPTODISK"] = "y" + + if overwrite or not os.path.exists(default_grub) or libcalamares.job.configuration.get("prefer_grub_d", False): + with open(default_grub, 'w') as grub_file: + for key, value in grub_config_items.items(): + grub_file.write(f"{key}={value}\n") + else: + update_existing_config(default_grub, grub_config_items) + + return None + + +def run(): + """ + Calls routine with given parameters to modify '/etc/default/grub'. + + :return: + """ + fw_type = libcalamares.globalstorage.value("firmwareType") + partitions = libcalamares.globalstorage.value("partitions") + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + branding = libcalamares.globalstorage.value("branding") + if branding is None: + distributor = None + else: + distributor = branding["bootloaderEntryName"] + + if libcalamares.globalstorage.value("bootLoader") is None and fw_type != "efi": + return None + + if fw_type == "efi": + esp_found = False + + for partition in partitions: + if partition["mountPoint"] == libcalamares.globalstorage.value("efiSystemPartition"): + esp_found = True + + if not esp_found: + return None + + return modify_grub_default(partitions, root_mount_point, distributor) diff --git a/airootfs/usr/lib/calamares/modules/grubcfg/module.desc b/airootfs/usr/lib/calamares/modules/grubcfg/module.desc new file mode 100644 index 0000000..293e75b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/grubcfg/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "grubcfg" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/hostinfo/libcalamares_job_hostinfo.so b/airootfs/usr/lib/calamares/modules/hostinfo/libcalamares_job_hostinfo.so new file mode 100755 index 0000000..b276847 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/hostinfo/libcalamares_job_hostinfo.so differ diff --git a/airootfs/usr/lib/calamares/modules/hostinfo/module.desc b/airootfs/usr/lib/calamares/modules/hostinfo/module.desc new file mode 100644 index 0000000..5b87cd7 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/hostinfo/module.desc @@ -0,0 +1,8 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "hostinfo" +interface: "qtplugin" +load: "libcalamares_job_hostinfo.so" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/hwclock/main.py b/airootfs/usr/lib/calamares/modules/hwclock/main.py new file mode 100644 index 0000000..be9fabf --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/hwclock/main.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014-2015 Philip Müller +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2017-2018 Gabriel Craciunescu +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Setting hardware clock.") + + +def run(): + """ + Set hardware clock. + """ + hwclock_rtc = ["hwclock", "--systohc", "--utc"] + hwclock_isa = ["hwclock", "--systohc", "--utc", "--directisa"] + is_broken_rtc = False + is_broken_isa = False + + ret = libcalamares.utils.target_env_call(hwclock_rtc) + if ret != 0: + is_broken_rtc = True + libcalamares.utils.debug("Hwclock returned error code {}".format(ret)) + libcalamares.utils.debug(" .. RTC method failed, trying ISA bus method.") + else: + libcalamares.utils.debug("Hwclock set using RTC method.") + if is_broken_rtc: + ret = libcalamares.utils.target_env_call(hwclock_isa) + if ret != 0: + is_broken_isa = True + libcalamares.utils.debug("Hwclock returned error code {}".format(ret)) + libcalamares.utils.debug(" .. ISA bus method failed.") + else: + libcalamares.utils.debug("Hwclock set using ISA bus method.") + if is_broken_rtc and is_broken_isa: + libcalamares.utils.debug("BIOS or Kernel BUG: Setting hwclock failed.") + + return None diff --git a/airootfs/usr/lib/calamares/modules/hwclock/module.desc b/airootfs/usr/lib/calamares/modules/hwclock/module.desc new file mode 100644 index 0000000..d13435b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/hwclock/module.desc @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "hwclock" +interface: "python" +script: "main.py" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/initcpio/encrypt_hook b/airootfs/usr/lib/calamares/modules/initcpio/encrypt_hook new file mode 100755 index 0000000..70d661a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpio/encrypt_hook @@ -0,0 +1,29 @@ +#!/bin/sh +# +# SPDX-FileCopyrightText: 2016 David McKinney +# SPDX-License-Identifier: GPL-3.0-or-later + + PREREQ="" + + prereqs() + { + echo "$PREREQ" + } + + case $1 in + # get pre-requisites + prereqs) + prereqs + exit 0 + ;; + esac + + . /usr/share/initramfs-tools/hook-functions + if [ -f /crypto_keyfile.bin ] + then + cp /crypto_keyfile.bin ${DESTDIR} + fi + if [ -f /etc/crypttab ] + then + cp /etc/crypttab ${DESTDIR}/etc/ + fi diff --git a/airootfs/usr/lib/calamares/modules/initcpio/encrypt_hook_nokey b/airootfs/usr/lib/calamares/modules/initcpio/encrypt_hook_nokey new file mode 100755 index 0000000..8ee669c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpio/encrypt_hook_nokey @@ -0,0 +1,25 @@ +#!/bin/sh +# +# SPDX-FileCopyrightText: 2016 David McKinney +# SPDX-License-Identifier: GPL-3.0-or-later + + PREREQ="" + + prereqs() + { + echo "$PREREQ" + } + + case $1 in + # get pre-requisites + prereqs) + prereqs + exit 0 + ;; + esac + + . /usr/share/initramfs-tools/hook-functions + if [ -f /etc/crypttab ] + then + cp /etc/crypttab ${DESTDIR}/etc/ + fi diff --git a/airootfs/usr/lib/calamares/modules/initcpio/libcalamares_job_initcpio.so b/airootfs/usr/lib/calamares/modules/initcpio/libcalamares_job_initcpio.so new file mode 100755 index 0000000..bbb5400 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/initcpio/libcalamares_job_initcpio.so differ diff --git a/airootfs/usr/lib/calamares/modules/initcpio/main.py b/airootfs/usr/lib/calamares/modules/initcpio/main.py new file mode 100644 index 0000000..974e263 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpio/main.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Rohan Garg +# SPDX-FileCopyrightText: 2015 Philip Müller +# SPDX-FileCopyrightText: 2016 David McKinney +# SPDX-FileCopyrightText: 2016 Kevin Kofler +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2017 2019, Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares + +import inspect +import os +import shutil + + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configuring initramfs.") + + +def copy_initramfs_hooks(partitions, root_mount_point): + """ + Copies initramfs hooks so they are picked up by update-initramfs + + :param partitions: + :param root_mount_point: + """ + encrypt_hook = False + unencrypted_separate_boot = False + + for partition in partitions: + if partition["mountPoint"] == "/" and "luksMapperName" in partition: + encrypt_hook = True + + if (partition["mountPoint"] == "/boot" + and "luksMapperName" not in partition): + unencrypted_separate_boot = True + + if encrypt_hook: + target = "{!s}/usr/share/initramfs-tools/hooks/encrypt_hook".format( + root_mount_point) + + # Find where this module is installed + _filename = inspect.getframeinfo(inspect.currentframe()).filename + _path = os.path.dirname(os.path.abspath(_filename)) + + if unencrypted_separate_boot: + shutil.copy2( + os.path.join(_path, "encrypt_hook_nokey"), + target + ) + else: + shutil.copy2( + os.path.join(_path, "encrypt_hook"), + target + ) + os.chmod(target, 0o755) + + +def run(): + """ + Calls routine with given parameters to configure initramfs + + :return: + """ + partitions = libcalamares.globalstorage.value("partitions") + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}
to use." ).format("initramfscfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}
to use." ).format("initramfscfg")) + + copy_initramfs_hooks(partitions, root_mount_point) + + return None diff --git a/airootfs/usr/lib/calamares/modules/initcpio/module.desc b/airootfs/usr/lib/calamares/modules/initcpio/module.desc new file mode 100644 index 0000000..766d16b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpio/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "initcpio" +interface: "qtplugin" +load: "libcalamares_job_initcpio.so" diff --git a/airootfs/usr/lib/calamares/modules/initcpiocfg/initcpiocfg.schema.yaml b/airootfs/usr/lib/calamares/modules/initcpiocfg/initcpiocfg.schema.yaml new file mode 100644 index 0000000..ddbe43b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpiocfg/initcpiocfg.schema.yaml @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2023 Evan James +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/initcpiocfg +additionalProperties: false +type: object +properties: + useSystemdHook: { type: boolean } + hooks: + type: object + additionalProperties: false + properties: + prepend: { type: array, items: { type: string } } + append: { type: array, items: { type: string } } + remove: { type: array, items: { type: string } } + source: { type: string } diff --git a/airootfs/usr/lib/calamares/modules/initcpiocfg/main.py b/airootfs/usr/lib/calamares/modules/initcpiocfg/main.py new file mode 100644 index 0000000..b6edc31 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpiocfg/main.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Rohan Garg +# SPDX-FileCopyrightText: 2015 2019-2020, Philip Müller +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# +import libcalamares +from libcalamares.utils import debug, target_env_call +import os +from collections import OrderedDict + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configuring mkinitcpio.") + + +def detect_plymouth(): + """ + Checks existence (runnability) of plymouth in the target system. + + @return True if plymouth exists in the target, False otherwise + """ + # Used to only check existence of path /usr/bin/plymouth in target + return target_env_call(["sh", "-c", "which plymouth"]) == 0 + + +def get_host_initcpio(): + """ + Reads the host system mkinitcpio.conf and returns all + the lines from that file, or an empty list if it does + not exist. + """ + hostfile = libcalamares.job.configuration.get("source", None) or "/etc/mkinitcpio.conf" + try: + with open(hostfile, "r") as mkinitcpio_file: + mklins = [x.strip() for x in mkinitcpio_file.readlines()] + except FileNotFoundError: + libcalamares.utils.debug(f"Could not open host file {hostfile}") + mklins = [] + + return mklins + + +def write_mkinitcpio_lines(hooks, modules, files, binaries, root_mount_point): + """ + Set up mkinitcpio.conf. + + :param hooks: + :param modules: + :param files: + :param root_mount_point: + """ + mklins = get_host_initcpio() + + target_path = os.path.join(root_mount_point, "etc/mkinitcpio.conf") + with open(target_path, "w") as mkinitcpio_file: + for line in mklins: + # Replace HOOKS, MODULES, BINARIES and FILES lines with what we + # have found via find_initcpio_features() + if line.startswith("HOOKS"): + line = f"HOOKS=({str.join(' ', hooks)})" + elif line.startswith("BINARIES"): + line = f"BINARIES=({str.join(' ', binaries)})" + elif line.startswith("MODULES"): + line = f"MODULES=({str.join(' ', modules)})" + elif line.startswith("FILES"): + line = f"FILES=({str.join(' ', files)})" + mkinitcpio_file.write(line + "\n") + + +def find_initcpio_features(partitions, root_mount_point): + """ + Returns a tuple (hooks, modules, files) needed to support + the given @p partitions (filesystems types, encryption, etc) + in the target. + + :param partitions: (from GS) + :param root_mount_point: (from GS) + + :return 3-tuple of lists + """ + hooks = [ + "autodetect", + "microcode", + "kms", + "modconf", + "block", + "keyboard", + ] + + systemd_hook_allowed = libcalamares.job.configuration.get("useSystemdHook", False) + + use_systemd = systemd_hook_allowed and target_env_call(["sh", "-c", "which systemd-cat"]) == 0 + + if use_systemd: + hooks.insert(0, "systemd") + hooks.append("sd-vconsole") + else: + hooks.insert(0, "udev") + hooks.insert(0, "base") + hooks.append("keymap") + hooks.append("consolefont") + + hooks_map = libcalamares.job.configuration.get("hooks", None) + if not hooks_map: + hooks_map = dict() + hooks_prepend = hooks_map.get("prepend", None) or [] + hooks_append = hooks_map.get("append", None) or [] + hooks_remove = hooks_map.get("remove", None) or [] + + modules = [] + files = [] + binaries = [] + + swap_uuid = "" + uses_btrfs = False + uses_zfs = False + uses_lvm2 = False + encrypt_hook = False + openswap_hook = False + unencrypted_separate_boot = False + + # It is important that the plymouth hook comes before any encrypt hook + if detect_plymouth(): + hooks.append("plymouth") + + for partition in partitions: + if partition["fs"] == "linuxswap" and not partition.get("claimed", None): + # Skip foreign swap + continue + + if partition["fs"] == "linuxswap": + swap_uuid = partition["uuid"] + if "luksMapperName" in partition: + openswap_hook = True + + if partition["fs"] == "btrfs": + uses_btrfs = True + + # In addition to checking the filesystem, check to ensure that zfs is enabled + if partition["fs"] == "zfs" and libcalamares.globalstorage.contains("zfsPoolInfo"): + uses_zfs = True + + if "lvm2" in partition["fs"]: + uses_lvm2 = True + + if partition["mountPoint"] == "/" and "luksMapperName" in partition: + encrypt_hook = True + + if partition["mountPoint"] == "/boot" and "luksMapperName" not in partition: + unencrypted_separate_boot = True + + if partition["mountPoint"] == "/usr": + hooks.append("usr") + + if encrypt_hook: + if use_systemd: + hooks.append("sd-encrypt") + else: + hooks.append("encrypt") + crypto_file = "crypto_keyfile.bin" + if not unencrypted_separate_boot and os.path.isfile(os.path.join(root_mount_point, crypto_file)): + files.append(f"/{crypto_file}") + + if uses_lvm2: + hooks.append("lvm2") + + if uses_zfs: + hooks.append("zfs") + + if swap_uuid != "": + if encrypt_hook and openswap_hook: + hooks.extend(["openswap"]) + hooks.extend(["resume", "filesystems"]) + else: + hooks.extend(["filesystems"]) + + if not uses_btrfs: + hooks.append("fsck") + + # Modify according to the keys in the configuration + hooks = [h for h in (hooks_prepend + hooks + hooks_append) if h not in hooks_remove] + + return hooks, modules, files, binaries + + +def run(): + """ + Calls routine with given parameters to modify "/etc/mkinitcpio.conf". + + :return: + """ + partitions = libcalamares.globalstorage.value("partitions") + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning(f"partitions are empty, {partitions}") + return (_("Configuration Error"), + _("No partitions are defined for
initcpiocfg
.")) + if not root_mount_point: + libcalamares.utils.warning(f"rootMountPoint is empty, {root_mount_point}") + return (_("Configuration Error"), + _("No root mount point for
initcpiocfg
.")) + + hooks, modules, files, binaries = find_initcpio_features(partitions, root_mount_point) + write_mkinitcpio_lines(hooks, modules, files, binaries, root_mount_point) + + return None diff --git a/airootfs/usr/lib/calamares/modules/initcpiocfg/module.desc b/airootfs/usr/lib/calamares/modules/initcpiocfg/module.desc new file mode 100644 index 0000000..9d7bfdf --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpiocfg/module.desc @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Writes a mkinitcpio.conf into the target system. It copies +# the host system's /etc/mkinitcpio.conf, and replaces any +# HOOKS, MODULES, and FILES lines with calculated values +# based on what the installation (seems to) need. +--- +type: "job" +name: "initcpiocfg" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/initcpiocfg/test.yaml b/airootfs/usr/lib/calamares/modules/initcpiocfg/test.yaml new file mode 100644 index 0000000..f832bec --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initcpiocfg/test.yaml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +rootMountPoint: /tmp/mount +partitions: + - fs: ext4 + mountPoint: "/" + diff --git a/airootfs/usr/lib/calamares/modules/initramfs/encrypt_hook b/airootfs/usr/lib/calamares/modules/initramfs/encrypt_hook new file mode 100755 index 0000000..70d661a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initramfs/encrypt_hook @@ -0,0 +1,29 @@ +#!/bin/sh +# +# SPDX-FileCopyrightText: 2016 David McKinney +# SPDX-License-Identifier: GPL-3.0-or-later + + PREREQ="" + + prereqs() + { + echo "$PREREQ" + } + + case $1 in + # get pre-requisites + prereqs) + prereqs + exit 0 + ;; + esac + + . /usr/share/initramfs-tools/hook-functions + if [ -f /crypto_keyfile.bin ] + then + cp /crypto_keyfile.bin ${DESTDIR} + fi + if [ -f /etc/crypttab ] + then + cp /etc/crypttab ${DESTDIR}/etc/ + fi diff --git a/airootfs/usr/lib/calamares/modules/initramfs/encrypt_hook_nokey b/airootfs/usr/lib/calamares/modules/initramfs/encrypt_hook_nokey new file mode 100755 index 0000000..8ee669c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initramfs/encrypt_hook_nokey @@ -0,0 +1,25 @@ +#!/bin/sh +# +# SPDX-FileCopyrightText: 2016 David McKinney +# SPDX-License-Identifier: GPL-3.0-or-later + + PREREQ="" + + prereqs() + { + echo "$PREREQ" + } + + case $1 in + # get pre-requisites + prereqs) + prereqs + exit 0 + ;; + esac + + . /usr/share/initramfs-tools/hook-functions + if [ -f /etc/crypttab ] + then + cp /etc/crypttab ${DESTDIR}/etc/ + fi diff --git a/airootfs/usr/lib/calamares/modules/initramfs/main.py b/airootfs/usr/lib/calamares/modules/initramfs/main.py new file mode 100644 index 0000000..974e263 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initramfs/main.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Rohan Garg +# SPDX-FileCopyrightText: 2015 Philip Müller +# SPDX-FileCopyrightText: 2016 David McKinney +# SPDX-FileCopyrightText: 2016 Kevin Kofler +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2017 2019, Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares + +import inspect +import os +import shutil + + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configuring initramfs.") + + +def copy_initramfs_hooks(partitions, root_mount_point): + """ + Copies initramfs hooks so they are picked up by update-initramfs + + :param partitions: + :param root_mount_point: + """ + encrypt_hook = False + unencrypted_separate_boot = False + + for partition in partitions: + if partition["mountPoint"] == "/" and "luksMapperName" in partition: + encrypt_hook = True + + if (partition["mountPoint"] == "/boot" + and "luksMapperName" not in partition): + unencrypted_separate_boot = True + + if encrypt_hook: + target = "{!s}/usr/share/initramfs-tools/hooks/encrypt_hook".format( + root_mount_point) + + # Find where this module is installed + _filename = inspect.getframeinfo(inspect.currentframe()).filename + _path = os.path.dirname(os.path.abspath(_filename)) + + if unencrypted_separate_boot: + shutil.copy2( + os.path.join(_path, "encrypt_hook_nokey"), + target + ) + else: + shutil.copy2( + os.path.join(_path, "encrypt_hook"), + target + ) + os.chmod(target, 0o755) + + +def run(): + """ + Calls routine with given parameters to configure initramfs + + :return: + """ + partitions = libcalamares.globalstorage.value("partitions") + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}
to use." ).format("initramfscfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}
to use." ).format("initramfscfg")) + + copy_initramfs_hooks(partitions, root_mount_point) + + return None diff --git a/airootfs/usr/lib/calamares/modules/initramfs/module.desc b/airootfs/usr/lib/calamares/modules/initramfs/module.desc new file mode 100644 index 0000000..489f643 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/initramfs/module.desc @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "initramfs" +interface: "python" +script: "main.py" + diff --git a/airootfs/usr/lib/calamares/modules/keyboard/libcalamares_viewmodule_keyboard.so b/airootfs/usr/lib/calamares/modules/keyboard/libcalamares_viewmodule_keyboard.so new file mode 100755 index 0000000..ab24bee Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/keyboard/libcalamares_viewmodule_keyboard.so differ diff --git a/airootfs/usr/lib/calamares/modules/keyboard/module.desc b/airootfs/usr/lib/calamares/modules/keyboard/module.desc new file mode 100644 index 0000000..2c7af5c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/keyboard/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "keyboard" +interface: "qtplugin" +load: "libcalamares_viewmodule_keyboard.so" diff --git a/airootfs/usr/lib/calamares/modules/keyboardq/libcalamares_viewmodule_keyboardq.so b/airootfs/usr/lib/calamares/modules/keyboardq/libcalamares_viewmodule_keyboardq.so new file mode 100755 index 0000000..792fe6b Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/keyboardq/libcalamares_viewmodule_keyboardq.so differ diff --git a/airootfs/usr/lib/calamares/modules/keyboardq/module.desc b/airootfs/usr/lib/calamares/modules/keyboardq/module.desc new file mode 100644 index 0000000..ce5f258 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/keyboardq/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "keyboardq" +interface: "qtplugin" +load: "libcalamares_viewmodule_keyboardq.so" diff --git a/airootfs/usr/lib/calamares/modules/license/libcalamares_viewmodule_license.so b/airootfs/usr/lib/calamares/modules/license/libcalamares_viewmodule_license.so new file mode 100755 index 0000000..0f6214e Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/license/libcalamares_viewmodule_license.so differ diff --git a/airootfs/usr/lib/calamares/modules/license/module.desc b/airootfs/usr/lib/calamares/modules/license/module.desc new file mode 100644 index 0000000..69a7d5e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/license/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "license" +interface: "qtplugin" +load: "libcalamares_viewmodule_license.so" diff --git a/airootfs/usr/lib/calamares/modules/locale/libcalamares_viewmodule_locale.so b/airootfs/usr/lib/calamares/modules/locale/libcalamares_viewmodule_locale.so new file mode 100755 index 0000000..dadd31c Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/locale/libcalamares_viewmodule_locale.so differ diff --git a/airootfs/usr/lib/calamares/modules/locale/module.desc b/airootfs/usr/lib/calamares/modules/locale/module.desc new file mode 100644 index 0000000..4a82dca --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/locale/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "locale" +interface: "qtplugin" +load: "libcalamares_viewmodule_locale.so" diff --git a/airootfs/usr/lib/calamares/modules/localecfg/main.py b/airootfs/usr/lib/calamares/modules/localecfg/main.py new file mode 100644 index 0000000..9cf5930 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/localecfg/main.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Anke Boersma +# SPDX-FileCopyrightText: 2015 Philip Müller +# SPDX-FileCopyrightText: 2016 Teo Mrnjavac +# SPDX-FileCopyrightText: 2018 AlmAck +# SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import os +import re +import shutil + + +import libcalamares + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configuring locales.") + + +RE_IS_COMMENT = re.compile("^ *#") +def is_comment(line): + """ + Does the @p line look like a comment? Whitespace, followed by a # + is a comment-only line. + """ + return bool(RE_IS_COMMENT.match(line)) + +RE_TRAILING_COMMENT = re.compile("#.*$") +RE_REST_OF_LINE = re.compile("\\s.*$") +def extract_locale(line): + """ + Extracts a locale from the @p line, and returns a pair of + (extracted-locale, uncommented line). The locale is the + first word of the line after uncommenting (in the human- + readable text explanation at the top of most /etc/locale.gen + files, the locales may be bogus -- either "" or e.g. "Configuration") + """ + # Remove leading spaces and comment signs + line = RE_IS_COMMENT.sub("", line) + uncommented = line.strip() + fields = RE_TRAILING_COMMENT.sub("", uncommented).strip().split() + if len(fields) != 2: + # Not exactly two fields, can't be a proper locale line + return "", uncommented + else: + # Drop all but first field + locale = RE_REST_OF_LINE.sub("", uncommented) + return locale, uncommented + + +def rewrite_locale_gen(srcfilename, destfilename, locale_conf): + """ + Copies a locale.gen file from @p srcfilename to @p destfilename + (this may be the same name), enabling those locales that can + be found in the map @p locale_conf. Also always enables en_US.UTF-8. + """ + en_us_locale = 'en_US.UTF-8' + + # Get entire source-file contents + text = [] + try: + with open(srcfilename, "r") as gen: + text = gen.readlines() + except FileNotFoundError: + # That's ok, the file doesn't exist so assume empty + pass + + # we want unique values, so locale_values should have 1 or 2 items + locale_values = set(locale_conf.values()) + locale_values.add(en_us_locale) # Always enable en_US as well + + enabled_locales = {} + seen_locales = set() + + # Write source out again, enabling some + with open(destfilename, "w") as gen: + for line in text: + c = is_comment(line) + locale, uncommented = extract_locale(line) + + # Non-comment lines are preserved, and comment lines + # may be enabled if they match a desired locale + if not c: + seen_locales.add(locale) + else: + for locale_value in locale_values: + if locale.startswith(locale_value): + enabled_locales[locale] = uncommented + gen.write(line) + + gen.write("\n###\n#\n# Locales enabled by Calamares\n") + for locale, line in enabled_locales.items(): + if locale not in seen_locales: + gen.write(line + "\n") + seen_locales.add(locale) + + for locale in locale_values: + if locale not in seen_locales: + gen.write("# Missing: %s\n" % locale) + + +def run(): + """ Create locale """ + import libcalamares + + locale_conf = libcalamares.globalstorage.value("localeConf") + + if not locale_conf: + locale_conf = { + 'LANG': 'en_US.UTF-8', + 'LC_NUMERIC': 'en_US.UTF-8', + 'LC_TIME': 'en_US.UTF-8', + 'LC_MONETARY': 'en_US.UTF-8', + 'LC_PAPER': 'en_US.UTF-8', + 'LC_NAME': 'en_US.UTF-8', + 'LC_ADDRESS': 'en_US.UTF-8', + 'LC_TELEPHONE': 'en_US.UTF-8', + 'LC_MEASUREMENT': 'en_US.UTF-8', + 'LC_IDENTIFICATION': 'en_US.UTF-8' + } + + install_path = libcalamares.globalstorage.value("rootMountPoint") + + if install_path is None: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(install_path)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}
to use." ).format("localecfg")) + + target_locale_gen = "{!s}/etc/locale.gen".format(install_path) + target_locale_gen_bak = target_locale_gen + ".bak" + target_locale_conf_path = "{!s}/etc/locale.conf".format(install_path) + target_etc_default_path = "{!s}/etc/default".format(install_path) + + # restore backup if available + if os.path.exists(target_locale_gen_bak): + shutil.copy2(target_locale_gen_bak, target_locale_gen) + libcalamares.utils.debug("Restored backup {!s} -> {!s}" + .format(target_locale_gen_bak, target_locale_gen)) + + # run locale-gen if detected; this *will* cause an exception + # if the live system has locale.gen, but the target does not: + # in that case, fix your installation filesystem. + if os.path.exists('/etc/locale.gen'): + rewrite_locale_gen(target_locale_gen, target_locale_gen, locale_conf) + libcalamares.utils.target_env_call(['locale-gen']) + libcalamares.utils.debug('{!s} done'.format(target_locale_gen)) + + # write /etc/locale.conf + with open(target_locale_conf_path, "w") as lcf: + for k, v in locale_conf.items(): + lcf.write("{!s}={!s}\n".format(k, v)) + libcalamares.utils.debug('{!s} done'.format(target_locale_conf_path)) + + # write /etc/default/locale if /etc/default exists and is a dir + if os.path.isdir(target_etc_default_path): + with open(os.path.join(target_etc_default_path, "locale"), "w") as edl: + for k, v in locale_conf.items(): + edl.write("{!s}={!s}\n".format(k, v)) + libcalamares.utils.debug('{!s} done'.format(target_etc_default_path)) + + return None diff --git a/airootfs/usr/lib/calamares/modules/localecfg/module.desc b/airootfs/usr/lib/calamares/modules/localecfg/module.desc new file mode 100644 index 0000000..030846a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/localecfg/module.desc @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# Enable the configured locales (those set by the user on the +# user page) in /etc/locale.gen, if they are available in the +# target system. +--- +type: "job" +name: "localecfg" +interface: "python" +script: "main.py" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/localeq/libcalamares_viewmodule_localeq.so b/airootfs/usr/lib/calamares/modules/localeq/libcalamares_viewmodule_localeq.so new file mode 100755 index 0000000..e90d57e Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/localeq/libcalamares_viewmodule_localeq.so differ diff --git a/airootfs/usr/lib/calamares/modules/localeq/module.desc b/airootfs/usr/lib/calamares/modules/localeq/module.desc new file mode 100644 index 0000000..7bc658a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/localeq/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "localeq" +interface: "qtplugin" +load: "libcalamares_viewmodule_localeq.so" diff --git a/airootfs/usr/lib/calamares/modules/luksbootkeyfile/libcalamares_job_luksbootkeyfile.so b/airootfs/usr/lib/calamares/modules/luksbootkeyfile/libcalamares_job_luksbootkeyfile.so new file mode 100755 index 0000000..2046abf Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/luksbootkeyfile/libcalamares_job_luksbootkeyfile.so differ diff --git a/airootfs/usr/lib/calamares/modules/luksbootkeyfile/module.desc b/airootfs/usr/lib/calamares/modules/luksbootkeyfile/module.desc new file mode 100644 index 0000000..7ca5775 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/luksbootkeyfile/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "luksbootkeyfile" +interface: "qtplugin" +load: "libcalamares_job_luksbootkeyfile.so" diff --git a/airootfs/usr/lib/calamares/modules/luksopenswaphookcfg/libcalamares_job_luksopenswaphookcfg.so b/airootfs/usr/lib/calamares/modules/luksopenswaphookcfg/libcalamares_job_luksopenswaphookcfg.so new file mode 100755 index 0000000..59f6f6e Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/luksopenswaphookcfg/libcalamares_job_luksopenswaphookcfg.so differ diff --git a/airootfs/usr/lib/calamares/modules/luksopenswaphookcfg/module.desc b/airootfs/usr/lib/calamares/modules/luksopenswaphookcfg/module.desc new file mode 100644 index 0000000..682d2d9 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/luksopenswaphookcfg/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "luksopenswaphookcfg" +interface: "qtplugin" +load: "libcalamares_job_luksopenswaphookcfg.so" diff --git a/airootfs/usr/lib/calamares/modules/machineid/libcalamares_job_machineid.so b/airootfs/usr/lib/calamares/modules/machineid/libcalamares_job_machineid.so new file mode 100755 index 0000000..6f26a43 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/machineid/libcalamares_job_machineid.so differ diff --git a/airootfs/usr/lib/calamares/modules/machineid/module.desc b/airootfs/usr/lib/calamares/modules/machineid/module.desc new file mode 100644 index 0000000..20b9b33 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/machineid/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "machineid" +interface: "qtplugin" +load: "libcalamares_job_machineid.so" diff --git a/airootfs/usr/lib/calamares/modules/mkinitfs/main.py b/airootfs/usr/lib/calamares/modules/mkinitfs/main.py new file mode 100644 index 0000000..e3e6e17 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mkinitfs/main.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014-2015 Philip Müller +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares +from libcalamares.utils import target_env_call + + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Creating initramfs with mkinitfs.") + + +def run_mkinitfs(): + """ + Creates initramfs, even when initramfs already exists. + + :return: + """ + return target_env_call(['mkinitfs']) + + +def run(): + """ + Starts routine to create initramfs. It passes back the exit code + if it fails. + + :return: + """ + return_code = run_mkinitfs() + + if return_code != 0: + return ( _("Failed to run mkinitfs on the target"), + _("The exit code was {}").format(return_code) ) diff --git a/airootfs/usr/lib/calamares/modules/mkinitfs/module.desc b/airootfs/usr/lib/calamares/modules/mkinitfs/module.desc new file mode 100644 index 0000000..decc325 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mkinitfs/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "mkinitfs" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/mobile/CMakeLists.txt b/airootfs/usr/lib/calamares/modules/mobile/CMakeLists.txt new file mode 100644 index 0000000..68815e3 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2020 Oliver Smith +# SPDX-License-Identifier: GPL-3.0-or-later + +calamares_add_plugin( mobile + TYPE viewmodule + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + Config.cpp + MobileQmlViewStep.cpp + PartitionJob.cpp + UsersJob.cpp + RESOURCES + mobile.qrc + SHARED_LIB +) diff --git a/airootfs/usr/lib/calamares/modules/mobile/Config.cpp b/airootfs/usr/lib/calamares/modules/mobile/Config.cpp new file mode 100644 index 0000000..93bbfd8 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/Config.cpp @@ -0,0 +1,221 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "Config.h" +#include "PartitionJob.h" +#include "UsersJob.h" + +#include "ViewManager.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include + +Config::Config( QObject* parent ) + : QObject( parent ) +{ +} + +void +Config::setConfigurationMap( const QVariantMap& cfgMap ) +{ + using namespace Calamares; + + if ( getBool( cfgMap, "bogus", false ) ) + { + cWarning() << "Configuration key \"bogus\" is still set for *mobile*"; + } + + m_osName = getString( cfgMap, "osName", "(unknown)" ); + m_arch = getString( cfgMap, "arch", "(unknown)" ); + m_device = getString( cfgMap, "device", "(unknown)" ); + m_userInterface = getString( cfgMap, "userInterface", "(unknown)" ); + m_version = getString( cfgMap, "version", "(unknown)" ); + + m_reservedUsernames = getStringList( cfgMap, "reservedUsernames", QStringList { "adm", "at ", "bin", "colord", + "cron", "cyrus", "daemon", "ftp", "games", "geoclue", "guest", "halt", "lightdm", "lp", "mail", "man", + "messagebus", "news", "nobody", "ntp", "operator", "polkitd", "postmaster", "pulse", "root", "shutdown", + "smmsp", "squid", "sshd", "sync", "uucp", "vpopmail", "xfs" } ); + + // ensure m_cmdUsermod matches m_username + m_username = getString( cfgMap, "username", "user" ); + m_userPasswordNumeric = getBool( cfgMap, "userPasswordNumeric", true ); + + m_builtinVirtualKeyboard = getBool( cfgMap, "builtinVirtualKeyboard", true ); + + m_featureSshd = getBool( cfgMap, "featureSshd", true ); + m_featureFsType = getBool( cfgMap, "featureFsType", false ); + + m_cmdLuksFormat = getString( cfgMap, "cmdLuksFormat", "cryptsetup luksFormat --use-random" ); + m_cmdLuksOpen = getString( cfgMap, "cmdLuksOpen", "cryptsetup luksOpen" ); + m_cmdMount = getString( cfgMap, "cmdMount", "mount" ); + m_targetDeviceRoot = getString( cfgMap, "targetDeviceRoot", "/dev/unknown" ); + m_targetDeviceRootInternal = getString( cfgMap, "targetDeviceRootInternal", "" ); + + m_cmdMkfsRootBtrfs = getString( cfgMap, "cmdMkfsRootBtrfs", "mkfs.btrfs -L 'unknownOS_root'" ); + m_cmdMkfsRootExt4 = getString( cfgMap, "cmdMkfsRootExt4", "mkfs.ext4 -L 'unknownOS_root'" ); + m_cmdMkfsRootF2fs = getString( cfgMap, "cmdMkfsRootF2fs", "mkfs.f2fs -l 'unknownOS_root'" ); + m_fsList = getStringList( cfgMap, "fsModel", QStringList { "ext4", "f2fs", "btrfs" } ); + m_defaultFs = getString( cfgMap, "defaultFs", "ext4" ); + m_fsIndex = m_fsList.indexOf( m_defaultFs ); + m_fsType = m_defaultFs; + + m_cmdInternalStoragePrepare = getString( cfgMap, "cmdInternalStoragePrepare", "ondev-internal-storage-prepare" ); + m_cmdPasswd = getString( cfgMap, "cmdPasswd", "passwd" ); + m_cmdUsermod = getString( cfgMap, "cmdUsermod", "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user"); + + m_cmdSshdEnable = getString( cfgMap, "cmdSshdEnable", "systemctl enable sshd.service" ); + m_cmdSshdDisable = getString( cfgMap, "cmdSshdDisable", "systemctl disable sshd.service" ); + m_cmdSshdUseradd = getString( cfgMap, "cmdSshdUseradd", "useradd -G wheel -m" ); +} + +Calamares::JobList +Config::createJobs() +{ + QList< Calamares::job_ptr > list; + QString cmdSshd = m_isSshEnabled ? m_cmdSshdEnable : m_cmdSshdDisable; + + /* Put users job in queue (should run after unpackfs) */ + Calamares::Job* j = new UsersJob( m_featureSshd, + m_cmdPasswd, + m_cmdUsermod, + cmdSshd, + m_cmdSshdUseradd, + m_isSshEnabled, + m_username, + m_userPassword, + m_sshdUsername, + m_sshdPassword ); + list.append( Calamares::job_ptr( j ) ); + + return list; +} + +void +Config::runPartitionJobThenLeave( bool b ) +{ + Calamares::ViewManager* v = Calamares::ViewManager::instance(); + QString cmdMkfsRoot; + if ( m_fsType == QStringLiteral( "btrfs" ) ) + { + cmdMkfsRoot = m_cmdMkfsRootBtrfs; + } + else if ( m_fsType == QStringLiteral( "f2fs" ) ) + { + cmdMkfsRoot = m_cmdMkfsRootF2fs; + } + else if ( m_fsType == QStringLiteral( "ext4" ) ) + { + cmdMkfsRoot = m_cmdMkfsRootExt4; + } + else + { + v->onInstallationFailed( "Unknown filesystem: '" + m_fsType + "'", "" ); + } + /* HACK: run partition job + * The "mobile" module has two jobs, the partition job and the users job. + * If we added both of them in Config::createJobs(), Calamares would run + * them right after each other. But we need the "unpackfs" module to run + * inbetween, that's why as workaround, the partition job is started here. + * To solve this properly, we would need to place the partition job in an + * own module and pass everything via globalstorage. But then we might as + * well refactor everything so we can unify the mobile's partition job with + * the proper partition job from Calamares. */ + Calamares::Job* j = new PartitionJob( m_cmdInternalStoragePrepare, + m_cmdLuksFormat, + m_cmdLuksOpen, + cmdMkfsRoot, + m_cmdMount, + m_targetDeviceRoot, + m_targetDeviceRootInternal, + m_installFromExternalToInternal, + m_isFdeEnabled, + m_fdePassword ); + Calamares::JobResult res = j->exec(); + + if ( res ) + { + v->next(); + } + else + { + v->onInstallationFailed( res.message(), res.details() ); + } +} + +void +Config::setUsername( const QString& username ) +{ + m_username = username; + emit usernameChanged( m_username ); +} + +void +Config::setUserPassword( const QString& userPassword ) +{ + m_userPassword = userPassword; + emit userPasswordChanged( m_userPassword ); +} + +void +Config::setSshdUsername( const QString& sshdUsername ) +{ + m_sshdUsername = sshdUsername; + emit sshdUsernameChanged( m_sshdUsername ); +} + +void +Config::setSshdPassword( const QString& sshdPassword ) +{ + m_sshdPassword = sshdPassword; + emit sshdPasswordChanged( m_sshdPassword ); +} + +void +Config::setIsSshEnabled( const bool isSshEnabled ) +{ + m_isSshEnabled = isSshEnabled; +} + +void +Config::setFdePassword( const QString& fdePassword ) +{ + m_fdePassword = fdePassword; + emit fdePasswordChanged( m_fdePassword ); +} + +void +Config::setIsFdeEnabled( const bool isFdeEnabled ) +{ + m_isFdeEnabled = isFdeEnabled; +} + +void +Config::setInstallFromExternalToInternal( const bool val ) +{ + m_installFromExternalToInternal = val; +} + +void +Config::setFsType( int idx ) +{ + if ( idx >= 0 && idx < m_fsList.length() ) + { + setFsType( m_fsList[ idx ] ); + } +} +void +Config::setFsType( const QString& fsType ) +{ + if ( fsType != m_fsType ) + { + m_fsType = fsType; + emit fsTypeChanged( m_fsType ); + } +} + +void +Config::setFsIndex( const int fsIndex ) +{ + m_fsIndex = fsIndex; + emit fsIndexChanged( m_fsIndex ); +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/Config.h b/airootfs/usr/lib/calamares/modules/mobile/Config.h new file mode 100644 index 0000000..3d77561 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/Config.h @@ -0,0 +1,207 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#pragma once + +#include "Job.h" + +#include +#include + +class Config : public QObject +{ + Q_OBJECT + /* installer UI */ + Q_PROPERTY( bool builtinVirtualKeyboard READ builtinVirtualKeyboard CONSTANT FINAL ) + + /* welcome */ + Q_PROPERTY( QString osName READ osName CONSTANT FINAL ) + Q_PROPERTY( QString arch READ arch CONSTANT FINAL ) + Q_PROPERTY( QString device READ device CONSTANT FINAL ) + Q_PROPERTY( QString userInterface READ userInterface CONSTANT FINAL ) + Q_PROPERTY( QString version READ version CONSTANT FINAL ) + + /* reserved usernames (user_pass, ssh_credentials )*/ + Q_PROPERTY( QStringList reservedUsernames READ reservedUsernames CONSTANT FINAL ) + + /* default user */ + Q_PROPERTY( QString username READ username WRITE setUsername NOTIFY usernameChanged ) + Q_PROPERTY( QString userPassword READ userPassword WRITE setUserPassword NOTIFY userPasswordChanged ) + Q_PROPERTY( bool userPasswordNumeric READ userPasswordNumeric CONSTANT FINAL ) + + /* ssh server + credentials */ + Q_PROPERTY( bool featureSshd READ featureSshd CONSTANT FINAL ) + Q_PROPERTY( QString sshdUsername READ sshdUsername WRITE setSshdUsername NOTIFY sshdUsernameChanged ) + Q_PROPERTY( QString sshdPassword READ sshdPassword WRITE setSshdPassword NOTIFY sshdPasswordChanged ) + Q_PROPERTY( bool isSshEnabled READ isSshEnabled WRITE setIsSshEnabled ) + + /* full disk encryption */ + Q_PROPERTY( QString fdePassword READ fdePassword WRITE setFdePassword NOTIFY fdePasswordChanged ) + Q_PROPERTY( bool isFdeEnabled READ isFdeEnabled WRITE setIsFdeEnabled ) + + /* filesystem selection */ + Q_PROPERTY( QString fsType READ fsType WRITE setFsType NOTIFY fsTypeChanged ) + Q_PROPERTY( bool featureFsType READ featureFsType CONSTANT FINAL ) + Q_PROPERTY( QStringList fsList READ fsList CONSTANT FINAL ) + Q_PROPERTY( QString defaultFs READ defaultFs CONSTANT FINAL ) + Q_PROPERTY( int fsIndex READ fsIndex WRITE setFsIndex NOTIFY fsIndexChanged ) + + /* partition job */ + Q_PROPERTY( bool runPartitionJobThenLeave READ runPartitionJobThenLeaveDummy WRITE runPartitionJobThenLeave ) + Q_PROPERTY( QString cmdInternalStoragePrepare READ cmdInternalStoragePrepare CONSTANT FINAL ) + Q_PROPERTY( QString cmdLuksFormat READ cmdLuksFormat CONSTANT FINAL ) + Q_PROPERTY( QString cmdLuksOpen READ cmdLuksOpen CONSTANT FINAL ) + Q_PROPERTY( QString cmdMount READ cmdMount CONSTANT FINAL ) + Q_PROPERTY( QString targetDeviceRoot READ targetDeviceRoot CONSTANT FINAL ) + Q_PROPERTY( QString targetDeviceRootInternal READ targetDeviceRootInternal CONSTANT FINAL ) + Q_PROPERTY( + bool installFromExternalToInternal READ installFromExternalToInternal WRITE setInstallFromExternalToInternal ) + + /* users job */ + Q_PROPERTY( QString cmdSshdEnable READ cmdSshdEnable CONSTANT FINAL ) + Q_PROPERTY( QString cmdSshdDisable READ cmdSshdDisable CONSTANT FINAL ) + +public: + Config( QObject* parent = nullptr ); + void setConfigurationMap( const QVariantMap& ); + Calamares::JobList createJobs(); + + /* installer UI */ + bool builtinVirtualKeyboard() { return m_builtinVirtualKeyboard; } + + /* welcome */ + QString osName() const { return m_osName; } + QString arch() const { return m_arch; } + QString device() const { return m_device; } + QString userInterface() const { return m_userInterface; } + QString version() const { return m_version; } + + /* reserved usernames (user_pass, ssh_credentials) */ + QStringList reservedUsernames() const { return m_reservedUsernames; }; + + /* user */ + QString username() const { return m_username; } + QString userPassword() const { return m_userPassword; } + void setUsername( const QString& username ); + void setUserPassword( const QString& userPassword ); + bool userPasswordNumeric() const { return m_userPasswordNumeric; } + + /* ssh server + credentials */ + bool featureSshd() { return m_featureSshd; } + QString sshdUsername() const { return m_sshdUsername; } + QString sshdPassword() const { return m_sshdPassword; } + bool isSshEnabled() { return m_isSshEnabled; } + void setSshdUsername( const QString& sshdUsername ); + void setSshdPassword( const QString& sshdPassword ); + void setIsSshEnabled( bool isSshEnabled ); + + /* full disk encryption */ + QString fdePassword() const { return m_fdePassword; } + bool isFdeEnabled() { return m_isFdeEnabled; } + void setFdePassword( const QString& fdePassword ); + void setIsFdeEnabled( bool isFdeEnabled ); + + /* filesystem selection */ + bool featureFsType() { return m_featureFsType; }; + QString fsType() const { return m_fsType; }; + void setFsType( int idx ); + void setFsType( const QString& fsType ); + QStringList fsList() const { return m_fsList; }; + int fsIndex() const { return m_fsIndex; }; + void setFsIndex( const int fsIndex ); + QString defaultFs() const { return m_defaultFs; }; + + /* partition job */ + bool runPartitionJobThenLeaveDummy() { return 0; } + void runPartitionJobThenLeave( bool b ); + QString cmdInternalStoragePrepare() const { return m_cmdInternalStoragePrepare; } + QString cmdLuksFormat() const { return m_cmdLuksFormat; } + QString cmdLuksOpen() const { return m_cmdLuksOpen; } + QString cmdMkfsRootBtrfs() const { return m_cmdMkfsRootBtrfs; } + QString cmdMkfsRootExt4() const { return m_cmdMkfsRootExt4; } + QString cmdMkfsRootF2fs() const { return m_cmdMkfsRootF2fs; } + QString cmdMount() const { return m_cmdMount; } + QString targetDeviceRoot() const { return m_targetDeviceRoot; } + QString targetDeviceRootInternal() const { return m_targetDeviceRootInternal; } + bool installFromExternalToInternal() { return m_installFromExternalToInternal; } + void setInstallFromExternalToInternal( const bool val ); + + /* users job */ + QString cmdPasswd() const { return m_cmdPasswd; } + QString cmdUsermod() const { return m_cmdUsermod; } + QString cmdSshdEnable() const { return m_cmdSshdEnable; } + QString cmdSshdDisable() const { return m_cmdSshdDisable; } + QString cmdSshdUseradd() const { return m_cmdSshdUseradd; } + +private: + /* installer UI */ + bool m_builtinVirtualKeyboard; + + /* welcome */ + QString m_osName; + QString m_arch; + QString m_device; + QString m_userInterface; + QString m_version; + + /* reserved usernames (user_pass, ssh_credentials) */ + QStringList m_reservedUsernames; + + /* default user */ + QString m_username; + QString m_userPassword; + bool m_userPasswordNumeric; + + /* ssh server + credentials */ + bool m_featureSshd = false; + QString m_sshdUsername; + QString m_sshdPassword; + bool m_isSshEnabled = false; + + /* full disk encryption */ + QString m_fdePassword; + bool m_isFdeEnabled = false; + + /* filesystem selection */ + bool m_featureFsType = false; + QString m_defaultFs; + QString m_fsType; + // Index of the currently selected filesystem in UI. + int m_fsIndex = -1; + QStringList m_fsList; + + /* partition job */ + QString m_cmdInternalStoragePrepare; + QString m_cmdLuksFormat; + QString m_cmdLuksOpen; + QString m_cmdMkfsRootBtrfs; + QString m_cmdMkfsRootExt4; + QString m_cmdMkfsRootF2fs; + QString m_cmdMount; + QString m_targetDeviceRoot; + QString m_targetDeviceRootInternal; + bool m_installFromExternalToInternal = false; + + /* users job */ + QString m_cmdPasswd; + QString m_cmdUsermod; + QString m_cmdSshdEnable; + QString m_cmdSshdDisable; + QString m_cmdSshdUseradd; + +signals: + /* booleans we don't read from QML (like isSshEnabled) don't need a signal */ + + /* default user */ + void userPasswordChanged( QString userPassword ); + void usernameChanged( QString username ); + + /* ssh server + credentials */ + void sshdUsernameChanged( QString sshdUsername ); + void sshdPasswordChanged( QString sshdPassword ); + + /* full disk encryption */ + void fdePasswordChanged( QString fdePassword ); + + void fsTypeChanged( QString fsType ); + void fsIndexChanged( int fsIndex ); +}; diff --git a/airootfs/usr/lib/calamares/modules/mobile/MobileQmlViewStep.cpp b/airootfs/usr/lib/calamares/modules/mobile/MobileQmlViewStep.cpp new file mode 100644 index 0000000..a4ac96f --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/MobileQmlViewStep.cpp @@ -0,0 +1,73 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "MobileQmlViewStep.h" + +#include "Branding.h" +#include "GlobalStorage.h" +#include "locale/TranslationsModel.h" +#include "modulesystem/ModuleManager.h" +#include "utils/Dirs.h" +#include "utils/Logger.h" +#include "utils/Variant.h" + +#include + +CALAMARES_PLUGIN_FACTORY_DEFINITION( MobileQmlViewStepFactory, registerPlugin< MobileQmlViewStep >(); ) + +void +MobileQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap ) +{ + m_config->setConfigurationMap( configurationMap ); + Calamares::QmlViewStep::setConfigurationMap( configurationMap ); +} + +MobileQmlViewStep::MobileQmlViewStep( QObject* parent ) + : Calamares::QmlViewStep( parent ) + , m_config( new Config( this ) ) +{ +} + +void +MobileQmlViewStep::onLeave() +{ + return; +} + +bool +MobileQmlViewStep::isNextEnabled() const +{ + return false; +} + +bool +MobileQmlViewStep::isBackEnabled() const +{ + return false; +} + + +bool +MobileQmlViewStep::isAtBeginning() const +{ + return true; +} + + +bool +MobileQmlViewStep::isAtEnd() const +{ + return true; +} + + +Calamares::JobList +MobileQmlViewStep::jobs() const +{ + return m_config->createJobs(); +} + +QObject* +MobileQmlViewStep::getConfig() +{ + return m_config; +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/MobileQmlViewStep.h b/airootfs/usr/lib/calamares/modules/mobile/MobileQmlViewStep.h new file mode 100644 index 0000000..a595680 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/MobileQmlViewStep.h @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef PARTITION_QMLVIEWSTEP_H +#define PARTITION_QMLVIEWSTEP_H +#include "Config.h" + +#include "utils/PluginFactory.h" +#include "viewpages/QmlViewStep.h" + +#include + +#include +#include + +class PLUGINDLLEXPORT MobileQmlViewStep : public Calamares::QmlViewStep +{ + Q_OBJECT + +public: + explicit MobileQmlViewStep( QObject* parent = nullptr ); + + bool isNextEnabled() const override; + bool isBackEnabled() const override; + bool isAtBeginning() const override; + bool isAtEnd() const override; + + Calamares::JobList jobs() const override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + void onLeave() override; + QObject* getConfig() override; + +private: + Config* m_config; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( MobileQmlViewStepFactory ) + +#endif // PARTITION_QMLVIEWSTEP_H diff --git a/airootfs/usr/lib/calamares/modules/mobile/PartitionJob.cpp b/airootfs/usr/lib/calamares/modules/mobile/PartitionJob.cpp new file mode 100644 index 0000000..ecb6ad9 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/PartitionJob.cpp @@ -0,0 +1,136 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "PartitionJob.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" +#include "utils/System.h" +#include "utils/Logger.h" + +#include +#include + + +PartitionJob::PartitionJob( const QString& cmdInternalStoragePrepare, + const QString& cmdLuksFormat, + const QString& cmdLuksOpen, + const QString& cmdMkfsRoot, + const QString& cmdMount, + const QString& targetDeviceRoot, + const QString& targetDeviceRootInternal, + bool installFromExternalToInternal, + bool isFdeEnabled, + const QString& password ) + : Calamares::Job() + , m_cmdInternalStoragePrepare( cmdInternalStoragePrepare ) + , m_cmdLuksFormat( cmdLuksFormat ) + , m_cmdLuksOpen( cmdLuksOpen ) + , m_cmdMkfsRoot( cmdMkfsRoot ) + , m_cmdMount( cmdMount ) + , m_targetDeviceRoot( targetDeviceRoot ) + , m_targetDeviceRootInternal( targetDeviceRootInternal ) + , m_installFromExternalToInternal( installFromExternalToInternal ) + , m_isFdeEnabled( isFdeEnabled ) + , m_password( password ) +{ +} + + +QString +PartitionJob::prettyName() const +{ + return "Creating and formatting installation partition"; +} + +/* Fill the "global storage", so the following jobs (like unsquashfs) work. + The code is similar to modules/partition/jobs/FillGlobalStorageJob.cpp in + Calamares. */ +void +FillGlobalStorage( const QString device, const QString pathMount ) +{ + using namespace Calamares; + + GlobalStorage* gs = JobQueue::instance()->globalStorage(); + QVariantList partitions; + QVariantMap partition; + + /* See mapForPartition() in FillGlobalStorageJob.cpp */ + partition[ "device" ] = device; + partition[ "mountPoint" ] = "/"; + partition[ "claimed" ] = true; + + /* Ignored by calamares modules used in combination with the "mobile" + * module, so we can get away with leaving them empty for now. */ + partition[ "uuid" ] = ""; + partition[ "fsName" ] = ""; + partition[ "fs" ] = ""; + + partitions << partition; + gs->insert( "partitions", partitions ); + gs->insert( "rootMountPoint", pathMount ); +} + +Calamares::JobResult +PartitionJob::exec() +{ + using namespace Calamares; + using namespace Calamares; + using namespace std; + + const QString pathMount = "/mnt/install"; + const QString cryptName = "calamares_crypt"; + QString cryptDev = "/dev/mapper/" + cryptName; + QString passwordStdin = m_password + "\n"; + QString dev = m_targetDeviceRoot; + QList< QPair< QStringList, QString > > commands = {}; + + if ( m_installFromExternalToInternal ) + { + dev = m_targetDeviceRootInternal; + + commands.append( { + { { "sh", "-c", m_cmdInternalStoragePrepare }, QString() }, + } ); + } + + commands.append( { { { "mkdir", "-p", pathMount }, QString() } } ); + + if ( m_isFdeEnabled ) + { + commands.append( { + { { "sh", "-c", m_cmdLuksFormat + " " + dev }, passwordStdin }, + { { "sh", "-c", m_cmdLuksOpen + " " + dev + " " + cryptName }, passwordStdin }, + { { "sh", "-c", m_cmdMkfsRoot + " " + cryptDev }, QString() }, + { { "sh", "-c", m_cmdMount + " " + cryptDev + " " + pathMount }, QString() }, + } ); + } + else + { + commands.append( { { { "sh", "-c", m_cmdMkfsRoot + " " + dev }, QString() }, + { { "sh", "-c", m_cmdMount + " " + dev + " " + pathMount }, QString() } } ); + } + + foreach ( auto command, commands ) + { + const QStringList args = command.first; + const QString stdInput = command.second; + const QString pathRoot = "/"; + + ProcessResult res + = System::runCommand( System::RunLocation::RunInHost, args, pathRoot, stdInput, chrono::seconds( 600 ) ); + if ( res.getExitCode() ) + { + return JobResult::error( "Command failed:

" + "'" + + args.join( " " ) + + "'

" + " with output:

" + "'" + + res.getOutput() + "'" ); + } + } + + FillGlobalStorage( m_isFdeEnabled ? cryptDev : dev, pathMount ); + return JobResult::ok(); +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/PartitionJob.h b/airootfs/usr/lib/calamares/modules/mobile/PartitionJob.h new file mode 100644 index 0000000..e265abd --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/PartitionJob.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#pragma once +#include "Job.h" + + +class PartitionJob : public Calamares::Job +{ + Q_OBJECT +public: + PartitionJob( const QString& cmdInternalStoragePrepare, + const QString& cmdLuksFormat, + const QString& cmdLuksOpen, + const QString& cmdMkfsRoot, + const QString& cmdMount, + const QString& targetDeviceRoot, + const QString& targetDeviceRootInternal, + bool installFromExternalToInternal, + bool isFdeEnabled, + const QString& password ); + + QString prettyName() const override; + Calamares::JobResult exec() override; + + Calamares::JobList createJobs(); + +private: + QString m_cmdInternalStoragePrepare; + QString m_cmdLuksFormat; + QString m_cmdLuksOpen; + QString m_cmdMkfsRoot; + QString m_cmdMount; + QString m_targetDeviceRoot; + QString m_targetDeviceRootInternal; + bool m_installFromExternalToInternal; + bool m_isFdeEnabled; + QString m_password; +}; diff --git a/airootfs/usr/lib/calamares/modules/mobile/UsersJob.cpp b/airootfs/usr/lib/calamares/modules/mobile/UsersJob.cpp new file mode 100644 index 0000000..7d637d6 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/UsersJob.cpp @@ -0,0 +1,92 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "UsersJob.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" +#include "utils/System.h" +#include "utils/Logger.h" + +#include +#include + + +UsersJob::UsersJob( bool featureSshd, + const QString& cmdPasswd, + const QString& cmdUsermod, + const QString& cmdSshd, + const QString& cmdSshdUseradd, + bool isSshEnabled, + const QString& username, + const QString& password, + const QString& sshdUsername, + const QString& sshdPassword ) + : Calamares::Job() + , m_featureSshd( featureSshd ) + , m_cmdPasswd( cmdPasswd ) + , m_cmdUsermod( cmdUsermod ) + , m_cmdSshd( cmdSshd ) + , m_cmdSshdUseradd( cmdSshdUseradd ) + , m_isSshEnabled( isSshEnabled ) + , m_username( username ) + , m_password( password ) + , m_sshdUsername( sshdUsername ) + , m_sshdPassword( sshdPassword ) +{ +} + + +QString +UsersJob::prettyName() const +{ + return "Configuring users"; +} + +Calamares::JobResult +UsersJob::exec() +{ + using namespace Calamares; + using namespace Calamares; + using namespace std; + + QList< QPair< QStringList, QString > > commands = { + { { "sh", "-c", m_cmdUsermod }, m_username + "\n" } + }; + + commands.append( { { "sh", "-c", m_cmdPasswd + " " + m_username }, m_password + "\n" + m_password + "\n" } ); + + if ( m_featureSshd ) + { + commands.append( { { "sh", "-c", m_cmdSshd }, QString() } ); + + if ( m_isSshEnabled ) + { + commands.append( { { "sh", "-c", m_cmdSshdUseradd + " " + m_sshdUsername }, QString() } ); + commands.append( + { { "sh", "-c", m_cmdPasswd + " " + m_sshdUsername }, m_sshdPassword + "\n" + m_sshdPassword + "\n" } ); + } + } + + foreach ( auto command, commands ) + { + auto location = System::RunLocation::RunInTarget; + const QString pathRoot = "/"; + const QStringList args = command.first; + const QString stdInput = command.second; + + ProcessResult res = System::runCommand( location, args, pathRoot, stdInput, chrono::seconds( 30 ) ); + if ( res.getExitCode() ) + { + return JobResult::error( "Command failed:

" + "'" + + args.join( " " ) + + "'

" + " with output:

" + "'" + + res.getOutput() + "'" ); + } + } + + return JobResult::ok(); +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/UsersJob.h b/airootfs/usr/lib/calamares/modules/mobile/UsersJob.h new file mode 100644 index 0000000..8c4b285 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/UsersJob.h @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +#pragma once +#include "Job.h" + + +class UsersJob : public Calamares::Job +{ + Q_OBJECT +public: + UsersJob( bool featureSshd, + const QString& cmdPasswd, + const QString& cmdUsermod, + const QString& cmdSshd, + const QString& cmdSshdUseradd, + bool isSshEnabled, + const QString& username, + const QString& password, + const QString& sshdUsername, + const QString& sshdPassword ); + + QString prettyName() const override; + Calamares::JobResult exec() override; + + Calamares::JobList createJobs(); + +private: + bool m_featureSshd; + QString m_cmdPasswd; + QString m_cmdUsermod; + QString m_cmdSshd; + QString m_cmdSshdUseradd; + bool m_isSshEnabled; + QString m_username; + QString m_password; + QString m_sshdUsername; + QString m_sshdPassword; +}; diff --git a/airootfs/usr/lib/calamares/modules/mobile/fde_confirm.qml b/airootfs/usr/lib/calamares/modules/mobile/fde_confirm.qml new file mode 100644 index 0000000..94ceac2 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/fde_confirm.qml @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "To protect your data in case your device gets stolen," + + " it is recommended to enable full disk encryption.
" + + "
" + + "If you enable full disk encryption, you will be asked for" + + " a password. Without this password, it is not possible to" + + " boot your device or access any data on it. Make sure that" + + " you don't lose this password!" + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Enable") + onClicked: { + config.isFdeEnabled = true; + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Disable") + onClicked: { + config.isFdeEnabled = false; + navNextFeature(); + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/fde_pass.qml b/airootfs/usr/lib/calamares/modules/mobile/fde_pass.qml new file mode 100644 index 0000000..9806897 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/fde_pass.qml @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + TextField { + id: password + anchors.top: parent.top + placeholderText: qsTr("Password") + inputMethodHints: Qt.ImhPreferLowercase + echoMode: TextInput.Password + onTextChanged: validatePassword(password, passwordRepeat, + errorText) + text: config.fdePassword + + onActiveFocusChanged: { + if(activeFocus) { + Qt.inputMethod.update(Qt.ImQueryInput); + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + TextField { + id: passwordRepeat + anchors.top: password.bottom + placeholderText: qsTr("Password (repeat)") + echoMode: TextInput.Password + onTextChanged: validatePassword(password, passwordRepeat, + errorText) + text: config.fdePassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Text { + id: errorText + anchors.top: passwordRepeat.bottom + visible: false + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + wrapMode: Text.WordWrap + } + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: errorText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: { + if (validatePassword(password, passwordRepeat, errorText)) { + config.fdePassword = password.text; + navNext(); + } + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/fs_selection.qml b/airootfs/usr/lib/calamares/modules/mobile/fs_selection.qml new file mode 100644 index 0000000..5924b85 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/fs_selection.qml @@ -0,0 +1,59 @@ +/* SPDX-FileCopyrightText: 2020 Undef + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "Select the filesystem for root partition. If unsure, leave the default." + + width: 200 + } + + ComboBox { + id: fsTypeCB + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 150 + height: 30 + editable: false + model: config.fsList + /* Save the current state on selection so it is there when the back button is pressed */ + onActivated: config.fsType = fsTypeCB.currentText; + Component.onCompleted: fsTypeCB.currentIndex = find( config.fsType, Qt.MatchContains ); + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: fsTypeCB.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: { + config.fsType = fsTypeCB.currentText; + navNextFeature(); + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/install_confirm.qml b/airootfs/usr/lib/calamares/modules/mobile/install_confirm.qml new file mode 100644 index 0000000..2e2bdd8 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/install_confirm.qml @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: (function() { + var ret = "Once you hit 'install', the installation will begin." + + " It will typically take a few minutes. Do not power off the" + + " device until it is done.
"; + + if (config.installFromExternalToInternal) { + ret += "After the installation, your device will shutdown" + + " automatically. You must remove the external storage" + + " (SD card) before booting again." + + "

" + + "Otherwise, your device will boot into the installer" + + " again, and not into the installed system." + } else { + ret += "Afterwards, it will reboot into the installed system."; + } + + return ret; + }()) + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Install") + onClicked: navFinish() + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/install_target.qml b/airootfs/usr/lib/calamares/modules/mobile/install_target.qml new file mode 100644 index 0000000..e15c4ff --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/install_target.qml @@ -0,0 +1,64 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "The installation was started from an external storage medium." + + "
" + + "You can either install to the same medium and overwrite the" + + " installer, or install to the internal storage.
" + + "
" + + "Where would you like to install " + config.osName + "?" + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Internal (eMMC)") + onClicked: { + config.installFromExternalToInternal = true; + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("External (SD card)") + onClicked: { + config.installFromExternalToInternal = false; + navNextFeature(); + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/install_target_confirm.qml b/airootfs/usr/lib/calamares/modules/mobile/install_target_confirm.qml new file mode 100644 index 0000000..4064329 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/install_target_confirm.qml @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: "Are you sure that you want to overwrite the internal storage?" + + "

" + + "All existing data on the device will be lost!" + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Yes") + onClicked: { + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("No") + onClicked: { + navBack(); + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/mobile.conf b/airootfs/usr/lib/calamares/modules/mobile/mobile.conf new file mode 100644 index 0000000..f2a1a9a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/mobile.conf @@ -0,0 +1,173 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Commented out values are defaults. +# All commands are running with 'sh -c'. +--- +# This entry exists only to keep the tests happy, remove it in +# any production configuration. +bogus: true + +####### +### Target OS information +####### + +## Operating System Name +# osName: "(unknown)" + +## User Interface name (e.g. Plasma Mobile) +# userInterface: "(unknown)" + +## User Interface assumes that the password is numeric (as of writing, this is +## the case with Plasma Mobile and Phosh) +# userPasswordNumeric: true + +## OS version +# version: "(unknown)" + +## Default username (for which the password will be set) +## Ensure also cmdUsermod command matches the default user, so it can be changed if desired. +# username: "user" + +## reserved usernames (for user_pass username prompt and ssh_credentials) +# reservedUsernames: +# - adm +# - at +# - bin +# - colord +# - cron +# - cyrus +# - daemon +# - ftp +# - games +# - geoclue +# - guest +# - halt +# - lightdm +# - lp +# - mail +# - man +# - messagebus +# - news +# - nobody +# - ntp +# - operator +# - polkitd +# - postmaster +# - pulse +# - root +# - shutdown +# - smmsp +# - squid +# - sshd +# - sync +# - uucp +# - vpopmail +# - xfs + +####### +### Target device information +####### + +## Architecture (e.g. aarch64) +# arch: "(unknown)" + +## Name of the device (e.g. PinePhone) +# device: "(unknown)" + +## Partition that will be formatted and mounted (optionally with FDE) for the +## rootfs +# targetDeviceRoot: "/dev/unknown" + +## Partition that will be formatted and mounted (optionally with FDE) for the +## rootfs, on internal storage. The installer OS must not set this, if it was +## booted from the internal storage (this is not checked in the mobile +## module!). +## If this is set, the user gets asked whether they want to install on internal +## or external storage. If the user chose internal storage, +## cmdInternalStoragePrepare (see below) runs before this partition gets +## formatted (see below). A note is displayed, that the device is powered off +## after installation and that the user should remove the external storage +## medium. So you need to adjust the installer OS to poweroff in that case, and +## not reboot. See postmarketos-ondev.git for reference. +# targetDeviceRootInternal: "" + +###### +### Installer Features +###### + +## Ask whether sshd should be enabled or not. If enabled, add a dedicated ssh +## user with proper username and password and suggest to change to key-based +## authentication after installation. +# featureSshd: true + +## Ask the user, which filesystem to use. +# featureFsType: false +## Filesystems that the user can choose from. +#fsModel: +# - ext4 +# - f2fs +# - btrfs +## Default filesystem to display in the dialog. If featureFsType is disabled, +## this gets used without asking the user. +# defaultFs: ext4 + +## Start Qt's virtual keyboard within the mobile module. Disable if you bring +## your own virtual keyboard (e.g. svkbd). +# builtinVirtualKeyboard: true + +####### +### Commands running in the installer OS +####### + +## Format the target partition with LUKS +## Arguments: +## Stdin: password with \n +# cmdLuksFormat: "cryptsetup luksFormat --use-random" + +## Open the formatted partition +## Arguments: +## Stdin: password with \n +# cmdLuksOpen: "cryptsetup luksOpen" + +## Format the rootfs with a file system +## Arguments: +## Btrfs: to allow snapshots to work on the root subvolume, it is recommended that this +## command be a script which will create a subvolume and make it default +## An example can be found at: +## https://gitlab.com/mobian1/calamares-settings-mobian/-/merge_requests/2/diffs#diff-content-dde34f5f1c89e3dea63608c553bbc452dedf428f +# cmdMkfsRootBtrfs: "mkfs.btrfs -L 'unknownOS_root'" +# cmdMkfsRootExt4: "mkfs.ext4 -L 'unknownOS_root'" +# cmdMkfsRootF2fs: "mkfs.f2fs -l 'unknownOS_root'" + +## Mount the partition after formatting with file system +## Arguments: +# cmdMount: "mount" + +## When user selects installation from external storage to internal storage +## (see targetDeviceRootInternal above), use this command to prepare the +## internal storage medium. The command must create a partition table with +## two partitions (boot, root) and fill the boot partition. See the +## ondev-internal-storage-prepare.sh in postmarketos-ondev as example. +# cmdInternalStoragePrepare: "ondev-internal-storage-prepare" + +####### +### Commands running in the target OS (chroot) +####### + +## Change the username for the default user +## Stdin: username with \n +# cmdUsermod: "xargs -I{} -n1 usermod -m -d /home/{} -l {} -c {} user" + +## Set the password for default user and sshd user +## Arguments: +## Stdin: password twice, each time with \n +# cmdPasswd: "passwd" + +## Enable or disable sshd +# cmdSshdEnable: "systemctl enable sshd.service" +# cmdSshdDisable: "systemctl disable sshd.service" + +## Create the user for sshd +## Arguments: +# cmdSshdUseradd: "useradd -G wheel -m" diff --git a/airootfs/usr/lib/calamares/modules/mobile/mobile.qml b/airootfs/usr/lib/calamares/modules/mobile/mobile.qml new file mode 100644 index 0000000..18ef706 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/mobile.qml @@ -0,0 +1,390 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Page +{ + property var screen: "welcome" + property var screenPrevious: [] + property var titles: { + "welcome": null, /* titlebar disabled */ + "install_target": "Installation target", + "install_target_confirm": "Warning", + "user_pass": "User password", + "ssh_confirm": "SSH server", + "ssh_credentials": "SSH credentials", + "fs_selection": "Root filesystem", + "fde_confirm": "Full disk encryption", + "fde_pass": "Full disk encryption", + "install_confirm": "Ready to install", + "wait": null + } + property var features: [ + {"name": "welcome", + "screens": ["welcome"]}, + {"name": "installTarget", + "screens": ["install_target", "install_target_confirm"]}, + {"name": "userPassword", + "screens": ["user_pass"]}, + {"name": "sshd", + "screens": ["ssh_confirm", "ssh_credentials"]}, + {"name": "fsType", + "screens": ["fs_selection"]}, + {"name": "fde", + "screens": ["fde_confirm", "fde_pass"]}, + {"name": "installConfirm", + "screens": ["install_confirm", "wait"]} + ] + property var featureIdByScreen: (function() { + /* Put "features" above into an index of screen name -> feature id: + * featureIdByScreen = {"welcome": 0, "user_pass": 1, ...} */ + var ret = {}; + for (var i=0; i=[]" + + /* bottom row */ " ." + + Item { + id: appContainer + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: inputPanel.top + Item { + width: parent.width + height: parent.height + + Rectangle { + id: mobileNavigation + width: parent.width + height: 30 + color: "#e6e4e1" + Layout.fillWidth: true + + border.width: 1 + border.color: "#a7a7a7" + + anchors.left: parent.left + anchors.right: parent.right + + RowLayout { + width: parent.width + height: parent.height + spacing: 6 + + Button { + Layout.leftMargin: 6 + id: mobileBack + text: "<" + + background: Rectangle { + implicitWidth: 10 + implicitHeight: 7 + border.color: "#c1bab5" + border.width: 1 + radius: 4 + color: mobileBack.down ? "#dbdbdb" : "#f2f2f2" + } + + onClicked: navBack() + } + Rectangle { + implicitHeight: 10 + Layout.fillWidth: true + color: "#e6e4e1" + + Text { + id: mobileTitle + text: "" + color: "#303638" + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + Rectangle { + color: "#e6e4e1" + Layout.rightMargin: 6 + implicitWidth: 32 + implicitHeight: 30 + id: filler + } + } + } + + Loader { + id: load + anchors.left: parent.left + anchors.top: mobileNavigation.bottom + anchors.right: parent.right + } + } + } + InputPanel { + id: inputPanel + y: Qt.inputMethod.visible ? parent.height - inputPanel.height : parent.height + visible: config.builtinVirtualKeyboard + anchors.left: parent.left + anchors.right: parent.right + } + + Timer { + id: timer + } + + function skipFeatureInstallTarget() { + return config.targetDeviceRootInternal == ""; + } + + /* Navigation related */ + function navTo(name, historyPush=true) { + console.log("Navigating to screen: " + name); + if (historyPush) + screenPrevious.push(screen); + screen = name; + load.source = name + ".qml"; + mobileNavigation.visible = (titles[name] !== null); + mobileTitle.text = "" + titles[name] + ""; + Qt.inputMethod.hide(); + } + function navFinish() { + /* Show a waiting screen and wait a second (so it can render). The big + * comment in Config.cpp::runPartitionJobThenLeave() explains why this + * is necessary. */ + navTo("wait"); + timer.interval = 1000; + timer.repeat = false; + timer.triggered.connect(function() { + /* Trigger Config.cpp::runPartitionJobThenLeave(). (We could expose + * the function directly with qmlRegisterSingletonType somehow, but + * I haven't seen existing Calamares code do that with the Config + * object, so just use the side effect of setting the variable, as + * done in existing code of Calamares modules.) */ + config.runPartitionJobThenLeave = 1 + }); + timer.start(); + } + function navNextFeature() { + var id; + + /* Skip disabled features */ + for (id = featureIdByScreen[screen] + 1; id < features.length; id++) { + /* First letter uppercase */ + var name = features[id]["name"]; + var nameUp = name.charAt(0).toUpperCase() + name.slice(1); + + /* Check config.Feature */ + var configOption = "feature" + nameUp; + if (config[configOption] === false) { + console.log("Skipping feature (disabled in config): " + name); + continue; + } + + /* Check skipFeature() */ + var funcName = "skipFeature" + nameUp; + if (eval("typeof " + funcName) === "function" + && eval(funcName + "()")) { + console.log("Skipping feature (skip function): " + name); + continue; + } + + break; + } + + console.log("Navigating to feature: " + features[id]["name"]); + return navTo(features[id]["screens"][0]); + } + function navNext() { + var featureId = featureIdByScreen[screen]; + var featureScreens = features[featureId]["screens"]; + for (var i = 0; i" + + " underscores or minus signs."); + } else { + if (!name[i].match(/^[a-z_]$/)) + return validationFailure(errorText, + "First character must be a" + + " lowercase letter or an" + + " underscore."); + } + } + + /* Validate against reserved usernames */ + for (var i = 0; i < reserved.length; i++) { + if (name == reserved[i]) + return validationFailure(errorText, "Username '" + + reserved[i] + + "' is reserved."); + } + + /* Passed */ + return validationFailureClear(errorText); + } + + function validateSshdUsername(username, errorText) { + return validateUsername(username, errorText, [config.username]); + } + function validateSshdPassword(password, passwordRepeat, errorText) { + var pass = password.text; + var repeat = passwordRepeat.text; + + if (pass == "") + return validationFailure(errorText); + + if (pass.length < 6) + return validationFailure(errorText, + "Too short: needs at least 6" + + " digits/characters."); + + if (repeat == "") + return validationFailure(errorText); + + if (pass != repeat) + return validationFailure(errorText, "Passwords don't match."); + + return validationFailureClear(errorText); + } + function check_chars(input) { + for (var i = 0; i < input.length; i++) { + if (allowed_chars.indexOf(input[i]) == -1) + return false; + } + return true; + } + function allowed_chars_multiline() { + /* return allowed_chars split across multiple lines */ + var step = 20; + var ret = ""; + for (var i = 0; i < allowed_chars.length + step; i += step) + ret += allowed_chars.slice(i, i + step) + "\n"; + return ret.trim(); + } + function validatePassword(password, passwordRepeat, errorText) { + var pass = password.text; + var repeat = passwordRepeat.text; + + if (pass == "") + return validationFailure(errorText); + + /* This function gets called for the FDE password and for the user + * password. As of writing, all distributions shipping the mobile + * module are using osk-sdl to type in the FDE password after the + * installation, and another keyboard after booting up, to type in the + * user password. The osk-sdl password has the same keys as + * squeekboard's default layout, and other keyboards should be able to + * type these characters in as well. For now, verify that the password + * only contains characters that can be typed in by osk-sdl. If you + * need this to be more sophisticated, feel free to submit patches to + * make this more configurable. */ + if (!check_chars(pass)) + return validationFailure(errorText, + "The password must only contain" + + " these characters, others can possibly" + + " not be typed in after installation:\n" + + "\n" + + allowed_chars_multiline()); + + if (pass.length < 6) + return validationFailure(errorText, + "Too short: needs at least 6" + + " digits/characters."); + + if (repeat == "") + return validationFailure(errorText); + + if (pass != repeat) + return validationFailure(errorText, "Passwords don't match."); + + return validationFailureClear(errorText); + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/mobile.qrc b/airootfs/usr/lib/calamares/modules/mobile/mobile.qrc new file mode 100644 index 0000000..d8ef887 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/mobile.qrc @@ -0,0 +1,21 @@ + + + mobile.qml + + welcome.qml + + install_target.qml + install_target_confirm.qml + + user_pass.qml + ssh_confirm.qml + ssh_credentials.qml + fs_selection.qml + + fde_confirm.qml + fde_pass.qml + install_confirm.qml + + wait.qml + + diff --git a/airootfs/usr/lib/calamares/modules/mobile/ssh_confirm.qml b/airootfs/usr/lib/calamares/modules/mobile/ssh_confirm.qml new file mode 100644 index 0000000..8bf9e0f --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/ssh_confirm.qml @@ -0,0 +1,68 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 30 + wrapMode: Text.WordWrap + + text: "If you don't know what SSH is, choose 'disable'.
" + + "
" + + "With 'enable', you will be asked for a second username and" + + " password. You will be able to login to the SSH server with" + + " these credentials via USB (172.16.42.1), Wi-Fi and possibly" + + " cellular network. It is recommended to replace the password" + + " with an SSH key after the installation.
" + + "
" + + "More information:
" + + "https://postmarketos.org/ssh" + + width: 200 + } + + Button { + id: firstButton + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 40 + width: 200 + + text: qsTr("Enable") + onClicked: { + config.isSshEnabled = true; + navNext(); + } + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: firstButton.bottom + anchors.topMargin: 40 + width: 200 + + text: qsTr("Disable") + onClicked: { + config.isSshEnabled = false; + navNextFeature(); + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/ssh_credentials.qml b/airootfs/usr/lib/calamares/modules/mobile/ssh_credentials.qml new file mode 100644 index 0000000..bfb54c7 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/ssh_credentials.qml @@ -0,0 +1,107 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + TextField { + id: username + anchors.top: parent.top + placeholderText: qsTr("SSH username") + inputMethodHints: Qt.ImhPreferLowercase + onTextChanged: validateSshdUsername(username, errorTextUsername) + text: config.sshdUsername + + onActiveFocusChanged: { + if(activeFocus) { + Qt.inputMethod.update(Qt.ImQueryInput); + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + Text { + id: errorTextUsername + anchors.top: username.bottom + visible: false + wrapMode: Text.WordWrap + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + TextField { + id: password + anchors.top: errorTextUsername.bottom + placeholderText: qsTr("SSH password") + echoMode: TextInput.Password + onTextChanged: validateSshdPassword(password, passwordRepeat, + errorTextPassword) + text: config.sshdPassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + TextField { + id: passwordRepeat + anchors.top: password.bottom + placeholderText: qsTr("SSH password (repeat)") + echoMode: TextInput.Password + onTextChanged: validateSshdPassword(password, passwordRepeat, + errorTextPassword) + text: config.sshdPassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + + Text { + id: errorTextPassword + anchors.top: passwordRepeat.bottom + visible: false + wrapMode: Text.WordWrap + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 50 + width: 200 + } + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: errorTextPassword.bottom + anchors.topMargin: 40 + width: 200 + + text: qsTr("Continue") + onClicked: { + if (validateSshdUsername(username, errorTextUsername) && + validateSshdPassword(password, passwordRepeat, + errorTextPassword)) { + config.sshdUsername = username.text; + config.sshdPassword = password.text; + + navNext(); + } + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/user_pass.qml b/airootfs/usr/lib/calamares/modules/mobile/user_pass.qml new file mode 100644 index 0000000..9449579 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/user_pass.qml @@ -0,0 +1,143 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Item { + property var passPlaceholder: (config.userPasswordNumeric + ? "PIN" + : "Password") + property var hints: (config.userPasswordNumeric + ? Qt.ImhDigitsOnly + : Qt.ImhPreferLowercase) + property var validatePassFunc: (config.userPasswordNumeric + ? validatePin + : validatePassword); + + property var validateNameFunc: validateUsername; + + + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Text { + id: usernameDescription + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: (function() { + return "Set the username of your user. The default" + + " username is \"" + config.username + "\"."; + }()) + + width: 200 + } + + TextField { + id: username + anchors.top: usernameDescription.bottom + placeholderText: qsTr("Username") + onTextChanged: validateNameFunc(username, errorText) + text: config.username + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Text { + id: userPassDescription + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: username.bottom + anchors.topMargin: 10 + wrapMode: Text.WordWrap + + text: (function() { + if (config.userPasswordNumeric) { + return "Set the numeric password of your user. The" + + " lockscreen will ask for this PIN. This is" + + " not the PIN of your SIM card. Make sure to" + + " remember it."; + } else { + return "Set the password of your user. The lockscreen will" + + " ask for this password. Make sure to remember it."; + } + }()) + + width: 200 + } + + TextField { + id: userPass + anchors.top: userPassDescription.bottom + placeholderText: qsTr(passPlaceholder) + echoMode: TextInput.Password + onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText) + text: config.userPassword + + /* Let the virtual keyboard change to digits only */ + inputMethodHints: hints + onActiveFocusChanged: { + if(activeFocus) { + Qt.inputMethod.update(Qt.ImQueryInput) + } + } + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + TextField { + id: userPassRepeat + anchors.top: userPass.bottom + placeholderText: qsTr(passPlaceholder + " (repeat)") + inputMethodHints: hints + echoMode: TextInput.Password + onTextChanged: validatePassFunc(userPass, userPassRepeat, errorText) + text: config.userPassword + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Text { + anchors.top: userPassRepeat.bottom + id: errorText + visible: false + wrapMode: Text.WordWrap + + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: 10 + width: 200 + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: errorText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: { + if (validatePassFunc(userPass, userPassRepeat, errorText) && validateNameFunc(username, errorText)) { + config.userPassword = userPass.text; + config.username = username.text; + navNext(); + } + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/wait.qml b/airootfs/usr/lib/calamares/modules/mobile/wait.qml new file mode 100644 index 0000000..cd50e25 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/wait.qml @@ -0,0 +1,45 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Page +{ + id: fdeWait + + Item { + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + width: parent.width + height: parent.height + + Image { + id: logo + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + height: 50 + fillMode: Image.PreserveAspectFit + source: "file:///usr/share/calamares/branding/default-mobile/logo.png" + } + Text { + id: waitText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logo.bottom + anchors.topMargin: 50 + wrapMode: Text.WordWrap + text: "Formatting and mounting target partition. This may" + + " take up to ten minutes, please be patient." + width: 200 + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mobile/welcome.qml b/airootfs/usr/lib/calamares/modules/mobile/welcome.qml new file mode 100644 index 0000000..67fcce8 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mobile/welcome.qml @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2020 Oliver Smith + * SPDX-License-Identifier: GPL-3.0-or-later */ +import io.calamares.core 1.0 +import io.calamares.ui 1.0 + +import QtQuick 2.10 +import QtQuick.Controls 2.10 +import QtQuick.Layouts 1.3 +import org.kde.kirigami 2.7 as Kirigami +import QtGraphicalEffects 1.0 +import QtQuick.Window 2.3 +import QtQuick.VirtualKeyboard 2.1 + +Page +{ + id: welcome + + Item { + id: appContainer + anchors.left: parent.left + anchors.top: parent.top + anchors.right: parent.right + Item { + width: parent.width + height: parent.height + + Image { + id: logo + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + anchors.topMargin: 10 + height: 50 + fillMode: Image.PreserveAspectFit + source: "file:///usr/share/calamares/branding/default-mobile/logo.png" + } + Text { + id: mainText + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: logo.bottom + anchors.topMargin: 10 + horizontalAlignment: Text.AlignRight + text: "You are about to install
" + + "" + config.osName + + " " + config.version + "
" + + "user interface " + + "" + config.userInterface + "
" + + "architecture " + + "" + config.arch + "
" + + "on your
" + + "" + config.device + "
" + width: 200 + } + + Button { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: mainText.bottom + anchors.topMargin: 10 + width: 200 + + text: qsTr("Continue") + onClicked: navNext() + } + } + } +} diff --git a/airootfs/usr/lib/calamares/modules/mount/main.py b/airootfs/usr/lib/calamares/modules/mount/main.py new file mode 100644 index 0000000..4a16f88 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mount/main.py @@ -0,0 +1,395 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Aurélien Gâteau +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-FileCopyrightText: 2019 Kevin Kofler +# SPDX-FileCopyrightText: 2019-2020 Collabora Ltd +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import tempfile +import subprocess +import os +import re + +import libcalamares + +import gettext + +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +class ZfsException(Exception): + """Exception raised when there is a problem with zfs + + Attributes: + message -- explanation of the error + """ + + def __init__(self, message): + self.message = message + + +def pretty_name(): + return _("Mounting partitions.") + + +def disk_name_for_partition(partition): + """ Returns disk name for each found partition. + + :param partition: + :return: + """ + name = os.path.basename(partition["device"]) + + if name.startswith("/dev/mmcblk") or name.startswith("/dev/nvme"): + return re.sub("p[0-9]+$", "", name) + + return re.sub("[0-9]+$", "", name) + + +def is_ssd_disk(partition): + """ Checks if given partition is on an ssd disk. + + :param partition: A dict containing the partition information + :return: True is the partition in on an ssd, False otherwise + """ + + try: + disk_name = disk_name_for_partition(partition) + filename = os.path.join("/sys/block", disk_name, "queue/rotational") + + with open(filename) as sysfile: + return sysfile.read() == "0\n" + except: + return False + + +def get_mount_options(filesystem, mount_options, partition, efi_location = None): + """ + Returns the mount options for the partition object and filesystem + + :param filesystem: A string containing the filesystem + :param mount_options: A list of dicts that descripes the mount options for each mountpoint + :param partition: A dict containing information about the partition + :param efi_location: A string holding the location of the EFI partition or None + :return: A comma seperated string containing the mount options suitable for passing to mount + """ + + # Extra mounts can optionally have "options" set, in this case, they override other all other settings + if "options" in partition: + return ",".join(partition["options"]) + + # If there are no mount options defined then we use the defaults + if mount_options is None: + return "defaults" + + # The EFI partition uses special mounting options + if efi_location and partition["mountPoint"] == efi_location: + effective_filesystem = "efi" + else: + effective_filesystem = filesystem + + options = next((x for x in mount_options if x["filesystem"] == effective_filesystem), None) + + # If there is no match then check for default options + if options is None: + options = next((x for x in mount_options if x["filesystem"] == "default"), None) + + # If it is still None, then fallback to returning defaults + if options is None: + return "defaults" + + option_items = options.get("options", []).copy() + + # Append the appropriate options for ssd or hdd if set + if is_ssd_disk(partition): + option_items.extend(options.get("ssdOptions", [])) + else: + option_items.extend(options.get("hddOptions", [])) + + if option_items: + return ",".join(option_items) + else: + return "defaults" + + +def get_btrfs_subvolumes(partitions): + """ + Gets the job-configuration for btrfs subvolumes, or if there is + none given, returns a default configuration that matches + the setup (/ and /home) from before configurability was introduced. + + @param partitions + The partitions (from the partitioning module) that will exist on disk. + This is used to filter out subvolumes that don't need to be created + because they get a dedicated partition instead. + """ + btrfs_subvolumes = libcalamares.job.configuration.get("btrfsSubvolumes", None) + # Warn if there's no configuration at all, and empty configurations are + # replaced by a simple root-only layout. + if btrfs_subvolumes is None: + libcalamares.utils.warning("No configuration for btrfsSubvolumes") + if not btrfs_subvolumes: + btrfs_subvolumes = [dict(mountPoint="/", subvolume="/@"), dict(mountPoint="/home", subvolume="/@home")] + + # Filter out the subvolumes which have a dedicated partition + non_root_partition_mounts = [m for m in [p.get("mountPoint", None) for p in partitions] if + m is not None and m != '/'] + btrfs_subvolumes = list(filter(lambda s: s["mountPoint"] not in non_root_partition_mounts, btrfs_subvolumes)) + + # If we have a swap **file**, give it a separate subvolume. + swap_choice = libcalamares.globalstorage.value("partitionChoices") + if swap_choice and swap_choice.get("swap", None) == "file": + swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") + btrfs_subvolumes.append({'mountPoint': '/swap', 'subvolume': swap_subvol}) + libcalamares.globalstorage.insert("btrfsSwapSubvol", swap_subvol) + + return btrfs_subvolumes + + +def mount_zfs(root_mount_point, partition): + """ Mounts a zfs partition at @p root_mount_point + + :param root_mount_point: The absolute path to the root of the install + :param partition: The partition map from global storage for this partition + :return: + """ + # Get the list of zpools from global storage + zfs_pool_list = libcalamares.globalstorage.value("zfsPoolInfo") + if not zfs_pool_list: + libcalamares.utils.warning("Failed to locate zfsPoolInfo data in global storage") + raise ZfsException(_("Internal error mounting zfs datasets")) + + # Find the zpool matching this partition + for zfs_pool in zfs_pool_list: + if zfs_pool["mountpoint"] == partition["mountPoint"]: + pool_name = zfs_pool["poolName"] + ds_name = zfs_pool["dsName"] + + # import the zpool + try: + libcalamares.utils.host_env_process_output(["zpool", "import", "-N", "-R", root_mount_point, pool_name], None) + except subprocess.CalledProcessError: + raise ZfsException(_("Failed to import zpool")) + + # Get the encrpytion information from global storage + zfs_info_list = libcalamares.globalstorage.value("zfsInfo") + encrypt = False + if zfs_info_list: + for zfs_info in zfs_info_list: + if zfs_info["mountpoint"] == partition["mountPoint"] and zfs_info["encrypted"] is True: + encrypt = True + passphrase = zfs_info["passphrase"] + + if encrypt is True: + # The zpool is encrypted, we need to unlock it + try: + libcalamares.utils.host_env_process_output(["zfs", "load-key", pool_name], None, passphrase) + except subprocess.CalledProcessError: + raise ZfsException(_("Failed to unlock zpool")) + + if partition["mountPoint"] == '/': + # Get the zfs dataset list from global storage + zfs = libcalamares.globalstorage.value("zfsDatasets") + + if not zfs: + libcalamares.utils.warning("Failed to locate zfs dataset list") + raise ZfsException(_("Internal error mounting zfs datasets")) + + zfs.sort(key=lambda x: x["mountpoint"]) + for dataset in zfs: + try: + if dataset["canMount"] == "noauto" or dataset["canMount"] is True: + libcalamares.utils.host_env_process_output(["zfs", "mount", + dataset["zpool"] + '/' + dataset["dsName"]]) + except subprocess.CalledProcessError: + raise ZfsException(_("Failed to set zfs mountpoint")) + else: + try: + libcalamares.utils.host_env_process_output(["zfs", "mount", pool_name + '/' + ds_name]) + except subprocess.CalledProcessError: + raise ZfsException(_("Failed to set zfs mountpoint")) + + +def mount_partition(root_mount_point, partition, partitions, mount_options, mount_options_list, efi_location): + """ + Do a single mount of @p partition inside @p root_mount_point. + + :param root_mount_point: A string containing the root of the install + :param partition: A dict containing information about the partition + :param partitions: The full list of partitions used to filter out btrfs subvols which have duplicate mountpoints + :param mount_options: The mount options from the config file + :param mount_options_list: A list of options for each mountpoint to be placed in global storage for future modules + :param efi_location: A string holding the location of the EFI partition or None + :return: + """ + # Create mount point with `+` rather than `os.path.join()` because + # `partition["mountPoint"]` starts with a '/'. + raw_mount_point = partition["mountPoint"] + if not raw_mount_point: + return + + mount_point = root_mount_point + raw_mount_point + + # Ensure that the created directory has the correct SELinux context on + # SELinux-enabled systems. + + os.makedirs(mount_point, exist_ok=True) + + try: + subprocess.call(['chcon', '--reference=' + raw_mount_point, mount_point]) + except FileNotFoundError as e: + libcalamares.utils.warning(str(e)) + except OSError: + libcalamares.utils.error("Cannot run 'chcon' normally.") + raise + + fstype = partition.get("fs", "").lower() + if fstype == "unformatted": + return + + if fstype == "fat16" or fstype == "fat32": + fstype = "vfat" + + device = partition["device"] + + if "luksMapperName" in partition: + device = os.path.join("/dev/mapper", partition["luksMapperName"]) + + if fstype == "zfs": + mount_zfs(root_mount_point, partition) + else: # fstype == "zfs" + mount_options_string = get_mount_options(fstype, mount_options, partition, efi_location) + if libcalamares.utils.mount(device, + mount_point, + fstype, + mount_options_string) != 0: + libcalamares.utils.warning("Cannot mount {}".format(device)) + mount_options_list.append({"mountpoint": raw_mount_point, "option_string": mount_options_string}) + + # Special handling for btrfs subvolumes. Create the subvolumes listed in mount.conf + if fstype == "btrfs" and partition["mountPoint"] == '/': + # Root has been mounted to btrfs volume -> create subvolumes from configuration + btrfs_subvolumes = get_btrfs_subvolumes(partitions) + + # Store created list in global storage so it can be used in the fstab module + libcalamares.globalstorage.insert("btrfsSubvolumes", btrfs_subvolumes) + # Create the subvolumes that are in the completed list + for s in btrfs_subvolumes: + if not s["subvolume"]: + continue + os.makedirs(root_mount_point + os.path.dirname(s["subvolume"]), exist_ok=True) + subprocess.check_call(["btrfs", "subvolume", "create", + root_mount_point + s["subvolume"]]) + if s["mountPoint"] == "/": + # insert the root subvolume into global storage + libcalamares.globalstorage.insert("btrfsRootSubvolume", s["subvolume"]) + subprocess.check_call(["umount", "-v", root_mount_point]) + + device = partition["device"] + + if "luksMapperName" in partition: + device = os.path.join("/dev/mapper", partition["luksMapperName"]) + + # Mount the subvolumes + swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") + for s in btrfs_subvolumes: + if s['subvolume'] == swap_subvol: + mount_option_no_subvol = get_mount_options("btrfs_swap", mount_options, partition) + else: + mount_option_no_subvol = get_mount_options(fstype, mount_options, partition) + + # Only add subvol= argument if we are not mounting the entire filesystem + if s['subvolume']: + mount_option = f"subvol={s['subvolume']},{mount_option_no_subvol}" + else: + mount_option = mount_option_no_subvol + subvolume_mountpoint = mount_point[:-1] + s['mountPoint'] + mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option_no_subvol}) + if libcalamares.utils.mount(device, + subvolume_mountpoint, + fstype, + mount_option) != 0: + libcalamares.utils.warning("Cannot mount {}".format(device)) + + +def enable_swap_partition(devices): + try: + for d in devices: + libcalamares.utils.host_env_process_output(["swapon", d]) + except subprocess.CalledProcessError: + libcalamares.utils.warning(f"Failed to enable swap for devices: {devices}") + + +def run(): + """ + Mount all the partitions from GlobalStorage and from the job configuration. + Partitions are mounted in-lexical-order of their mountPoint. + """ + + partitions = libcalamares.globalstorage.value("partitions") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}
to use.").format("mount")) + + # Find existing swap partitions that are part of the installation and enable them now + claimed_swap_partitions = [p for p in partitions if p["fs"] == "linuxswap" and p.get("claimed", False)] + plain_swap = [p for p in claimed_swap_partitions if p["fsName"] == "linuxswap"] + luks_swap = [p for p in claimed_swap_partitions if p["fsName"] == "luks" or p["fsName"] == "luks2"] + swap_devices = [p["device"] for p in plain_swap] + ["/dev/mapper/" + p["luksMapperName"] for p in luks_swap] + + enable_swap_partition(swap_devices) + + root_mount_point = tempfile.mkdtemp(prefix="calamares-root-") + + # Get the mountOptions, if this is None, that is OK and will be handled later + mount_options = libcalamares.job.configuration.get("mountOptions") + + # Guard against missing keys (generally a sign that the config file is bad) + extra_mounts = libcalamares.job.configuration.get("extraMounts") or [] + if not extra_mounts: + libcalamares.utils.warning("No extra mounts defined. Does mount.conf exist?") + + efi_location = None + if libcalamares.globalstorage.value("firmwareType") == "efi": + efi_location = libcalamares.globalstorage.value("efiSystemPartition") + else: + for mount in extra_mounts: + if mount.get("efi", None) is True: + extra_mounts.remove(mount) + + # Add extra mounts to the partitions list and sort by mount points. + # This way, we ensure / is mounted before the rest, and every mount point + # is created on the right partition (e.g. if a partition is to be mounted + # under /tmp, we make sure /tmp is mounted before the partition) + mountable_partitions = [p for p in partitions + extra_mounts if "mountPoint" in p and p["mountPoint"]] + mountable_partitions.sort(key=lambda x: x["mountPoint"]) + + # mount_options_list will be inserted into global storage for use in fstab later + mount_options_list = [] + try: + for partition in mountable_partitions: + mount_partition(root_mount_point, partition, partitions, mount_options, mount_options_list, efi_location) + except ZfsException as ze: + return _("zfs mounting error"), ze.message + + if not mount_options_list: + libcalamares.utils.warning("No mount options defined, {!s} partitions, {!s} mountable".format(len(partitions), len(mountable_partitions))) + + libcalamares.globalstorage.insert("rootMountPoint", root_mount_point) + libcalamares.globalstorage.insert("mountOptionsList", mount_options_list) + + # Remember the extra mounts for the unpackfs module + libcalamares.globalstorage.insert("extraMounts", extra_mounts) diff --git a/airootfs/usr/lib/calamares/modules/mount/module.desc b/airootfs/usr/lib/calamares/modules/mount/module.desc new file mode 100644 index 0000000..e4486cf --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mount/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "mount" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/mount/mount.schema.yaml b/airootfs/usr/lib/calamares/modules/mount/mount.schema.yaml new file mode 100644 index 0000000..a083ed7 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/mount/mount.schema.yaml @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/mount +additionalProperties: false +type: object +properties: + extraMounts: + type: array + items: + type: object + additionalProperties: false + properties: + device: { type: string } + fs: { type: string } + mountPoint: { type: string } + options: { type: array, items: { type: string } } + efi: { type: boolean, default: false } + required: [ device, mountPoint ] + btrfsSubvolumes: + type: array + items: + type: object + additionalProperties: false + properties: + mountPoint: { type: string } + subvolume: { type: string } + required: [ subvolume, mountPoint ] + btrfsSwapSubvol: { type: string } + mountOptions: + type: array + items: + type: object + additionalProperties: false + properties: + filesystem: { type: string } + options: { type: array, items: { type: string } } + ssdOptions: { type: array, items: { type: string } } + hddOptions: { type: array, items: { type: string } } + required: [ filesystem ] + + diff --git a/airootfs/usr/lib/calamares/modules/netinstall/libcalamares_viewmodule_netinstall.so b/airootfs/usr/lib/calamares/modules/netinstall/libcalamares_viewmodule_netinstall.so new file mode 100755 index 0000000..b1d4e5d Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/netinstall/libcalamares_viewmodule_netinstall.so differ diff --git a/airootfs/usr/lib/calamares/modules/netinstall/module.desc b/airootfs/usr/lib/calamares/modules/netinstall/module.desc new file mode 100644 index 0000000..0106bd4 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/netinstall/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "netinstall" +interface: "qtplugin" +load: "libcalamares_viewmodule_netinstall.so" diff --git a/airootfs/usr/lib/calamares/modules/networkcfg/main.py b/airootfs/usr/lib/calamares/modules/networkcfg/main.py new file mode 100644 index 0000000..efe6930 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/networkcfg/main.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Philip Müller +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-FileCopyrightText: 2021 Anke boersma +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import os +import glob +import shutil + +import libcalamares + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Saving network configuration.") + + +def get_live_user(): + """ + Gets the "live user" login. This might be "live", or "nitrux", + or something similar: it is the login name used *right now*, + and network configurations saved for that user, should be applied + also for the installed user (which probably has a different name). + """ + # getlogin() is a thin-wrapper, and depends on getlogin(3), + # which reads utmp -- and utmp isn't always set up right. + try: + return os.getlogin() + except OSError: + pass + # getpass will return the **current** user, which is generally root. + # That isn't very useful, because the network settings have been + # made outside of Calamares-running-as-root, as a different user. + # + # If Calamares is running as non-root, though, this is fine. + import getpass + name = getpass.getuser() + if name != "root": + return name + + # TODO: other mechanisms, e.g. guessing that "live" is the name + # TODO: support a what-is-the-live-user setting + return None + + +def replace_username(nm_config_filename, live_user, target_user): + """ + If @p live_user isn't None, then go through the given + file and replace @p live_user by the @p target_user. + + Reads the file, then (re-)writes it with new permissions lives. + """ + # FIXME: Perhaps if live_user is None, we should just replace **all** + # permissions lines? After all, this is supposed to be a live + # system so **whatever** NM networks are configured, should be + # available to the new user. + if live_user is None: + return + if not os.path.exists(nm_config_filename): + return + + with open(nm_config_filename, "r", encoding="UTF-8") as network_conf: + text = network_conf.readlines() + + live_permissions = 'permissions=user:{}:;'.format(live_user) + target_permissions = 'permissions=user:{}:;\n'.format(target_user) + with open(nm_config_filename, "w", encoding="UTF-8") as network_conf: + for line in text: + if live_permissions in line: + line = target_permissions + network_conf.write(line) + + +def path_pair(root_mount_point, relative_path): + """ + Returns /relative_path and the relative path in the target system. + """ + return ("/" + relative_path, os.path.join(root_mount_point, relative_path)) + + +def run(): + """ + Setup network configuration + """ + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + user = libcalamares.globalstorage.value("username") + live_user = get_live_user() + + if root_mount_point is None: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}
to use." ).format("networkcfg")) + + source_nm, target_nm = path_pair(root_mount_point, "etc/NetworkManager/system-connections/") + + # Sanity checks. We don't want to do anything if a network + # configuration already exists on the target + if os.path.exists(source_nm) and os.path.exists(target_nm): + for network in os.listdir(source_nm): + # Skip LTSP live + if network == "LTSP": + continue + + source_network = os.path.join(source_nm, network) + target_network = os.path.join(target_nm, network) + + if os.path.exists(target_network): + continue + + try: + shutil.copy(source_network, target_network, follow_symlinks=False) + replace_username(target_network, live_user, user) + except FileNotFoundError: + libcalamares.utils.debug( + "Can't copy network configuration files in {}".format(source_network) + ) + except FileExistsError: + pass + + # Also install netplan files + source_netplan = "/etc/netplan" + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + target_netplan = os.path.join(root_mount_point, source_netplan.lstrip('/')) + + if os.path.exists(source_netplan) and os.path.exists(target_netplan): + # Set NetworkManager to be the default renderer if Netplan is installed + # TODO: We might rather do that inside the network-manager package, see: + # https://bugs.launchpad.net/ubuntu/+source/ubuntu-settings/+bug/2020110 + default_renderer = os.path.join(root_mount_point, "usr/lib/netplan", + "00-network-manager-all.yaml") + if not os.path.exists(default_renderer): + renderer_file = os.path.join(target_netplan, + "01-network-manager-all.yaml") + nm_renderer = """# This file was written by calamares. +# Let NetworkManager manage all devices on this system. +# For more information, see netplan(5). +network: + version: 2 + renderer: NetworkManager +""" + with open(renderer_file, 'w') as f: + f.writelines(nm_renderer) + os.chmod(f.fileno(), 0o600) + + # Copy existing Netplan configuration + for cfg in glob.glob(os.path.join(source_netplan, "*.yaml")): + source_cfg = os.path.join(source_netplan, cfg) + target_cfg = os.path.join(target_netplan, os.path.basename(cfg)) + + if os.path.exists(target_cfg): + continue + + shutil.copy(source_cfg, target_cfg) + + # We need to overwrite the default resolv.conf in the chroot. + source_resolv, target_resolv = path_pair(root_mount_point, "etc/resolv.conf") + if source_resolv != target_resolv and os.path.exists(source_resolv): + try: + os.remove(target_resolv) + except Exception as err: + libcalamares.utils.debug( + "Couldn't remove {}: {}".format(target_resolv, err) + ) + + try: + shutil.copy(source_resolv, target_resolv, follow_symlinks=False) + except Exception as err: + libcalamares.utils.debug( + "Can't copy resolv.conf from {}: {}".format(source_resolv, err) + ) + + return None diff --git a/airootfs/usr/lib/calamares/modules/networkcfg/module.desc b/airootfs/usr/lib/calamares/modules/networkcfg/module.desc new file mode 100644 index 0000000..cbafe8c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/networkcfg/module.desc @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "networkcfg" +interface: "python" +script: "main.py" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/notesqml/libcalamares_viewmodule_notesqml.so b/airootfs/usr/lib/calamares/modules/notesqml/libcalamares_viewmodule_notesqml.so new file mode 100755 index 0000000..abb3b21 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/notesqml/libcalamares_viewmodule_notesqml.so differ diff --git a/airootfs/usr/lib/calamares/modules/notesqml/module.desc b/airootfs/usr/lib/calamares/modules/notesqml/module.desc new file mode 100644 index 0000000..ba56761 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/notesqml/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "notesqml" +interface: "qtplugin" +load: "libcalamares_viewmodule_notesqml.so" diff --git a/airootfs/usr/lib/calamares/modules/oemid/libcalamares_viewmodule_oemid.so b/airootfs/usr/lib/calamares/modules/oemid/libcalamares_viewmodule_oemid.so new file mode 100755 index 0000000..964e969 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/oemid/libcalamares_viewmodule_oemid.so differ diff --git a/airootfs/usr/lib/calamares/modules/oemid/module.desc b/airootfs/usr/lib/calamares/modules/oemid/module.desc new file mode 100644 index 0000000..b4a4189 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/oemid/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "oemid" +interface: "qtplugin" +load: "libcalamares_viewmodule_oemid.so" diff --git a/airootfs/usr/lib/calamares/modules/openrcdmcryptcfg/main.py b/airootfs/usr/lib/calamares/modules/openrcdmcryptcfg/main.py new file mode 100644 index 0000000..06f21da --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/openrcdmcryptcfg/main.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2017 Ghiunhan Mamut +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import os.path + +import libcalamares + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + + +def pretty_name(): + return _("Configuring OpenRC dmcrypt service.") + + +def write_dmcrypt_conf(partitions, root_mount_point, dmcrypt_conf_path): + crypto_target = "" + crypto_source = "" + unencrypted_separate_boot = any(p["mountPoint"] == "/boot" and "luksMapperName" not in p for p in partitions) + + for partition in partitions: + has_luks = "luksMapperName" in partition + skip_partitions = partition["mountPoint"] == "/" or partition["fs"] == "linuxswap" + + if not has_luks and not skip_partitions: + libcalamares.utils.debug( + "Skip writing OpenRC LUKS configuration for partition {!s}".format(partition["mountPoint"])) + if has_luks and not skip_partitions: + crypto_target = partition["luksMapperName"] + crypto_source = "/dev/disk/by-uuid/{!s}".format(partition["uuid"]) + libcalamares.utils.debug( + "Writing OpenRC LUKS configuration for partition {!s}".format(partition["mountPoint"])) + + with open(os.path.join(root_mount_point, dmcrypt_conf_path), 'a+') as dmcrypt_file: + dmcrypt_file.write("\ntarget=" + crypto_target) + dmcrypt_file.write("\nsource=" + crypto_source) + # Don't use keyfile if boot is unencrypted, keys must not be stored on unencrypted partitions + if not unencrypted_separate_boot: + dmcrypt_file.write("\nkey=/crypto_keyfile.bin") + dmcrypt_file.write("\n") + + if has_luks and skip_partitions: + pass # root and swap partitions should be handled by initramfs generators + + return None + +def run(): + """ + This module configures OpenRC dmcrypt service for LUKS encrypted partitions. + :return: + """ + + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + dmcrypt_conf_path = libcalamares.job.configuration["configFilePath"] + partitions = libcalamares.globalstorage.value("partitions") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}
to use." ).format("openrcdmcryptcfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}
to use." ).format("openrcdmcryptcfg")) + + dmcrypt_conf_path = dmcrypt_conf_path.lstrip('/') + + return write_dmcrypt_conf(partitions, root_mount_point, dmcrypt_conf_path) diff --git a/airootfs/usr/lib/calamares/modules/openrcdmcryptcfg/module.desc b/airootfs/usr/lib/calamares/modules/openrcdmcryptcfg/module.desc new file mode 100644 index 0000000..e633395 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/openrcdmcryptcfg/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "openrcdmcryptcfg" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/os-freebsd/CMakeLists.txt b/airootfs/usr/lib/calamares/modules/os-freebsd/CMakeLists.txt new file mode 100644 index 0000000..d666700 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/os-freebsd/CMakeLists.txt @@ -0,0 +1,20 @@ +# The OS-FreeBSD module does "all the things" in a FreeBSD installation. +# Since the other modules -- users, fstab, grub, pretty much all of them +# -- are Linux-specific, it doesn't make much sense to fork each of them +# or provide alternatives, so instead we have one module that completes +# a FreeBSD installation based on the GlobalStorage values set by +# Calamares viewmodules. +# +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# License-Filename: LICENSE +# + +calamares_add_plugin( os-freebsd + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + FreeBSDJob.cpp + SHARED_LIB + NO_CONFIG +) diff --git a/airootfs/usr/lib/calamares/modules/os-freebsd/FreeBSDJob.cpp b/airootfs/usr/lib/calamares/modules/os-freebsd/FreeBSDJob.cpp new file mode 100644 index 0000000..8e845e8 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/os-freebsd/FreeBSDJob.cpp @@ -0,0 +1,58 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSE + */ + +#include "FreeBSDJob.h" + +#include "CalamaresVersion.h" +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "utils/Logger.h" + +#include +#include +#include + + +FreeBSDJob::FreeBSDJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + + +FreeBSDJob::~FreeBSDJob() {} + + +QString +FreeBSDJob::prettyName() const +{ + return tr( "FreeBSD Installation Job" ); +} + +Calamares::JobResult +FreeBSDJob::exec() +{ + emit progress( 0.1 ); + cDebug() << "[FREEBSD]"; + + Calamares::JobQueue::instance()->globalStorage()->debugDump(); + emit progress( 0.5 ); + + QThread::sleep( 3 ); + emit progress( 1.0 ); + + return Calamares::JobResult::ok(); +} + + +void +FreeBSDJob::setConfigurationMap( const QVariantMap& configurationMap ) +{ + // TODO: actually fetch something from that configuration + m_configurationMap = configurationMap; +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( FreeBSDJobFactory, registerPlugin< FreeBSDJob >(); ) diff --git a/airootfs/usr/lib/calamares/modules/os-freebsd/FreeBSDJob.h b/airootfs/usr/lib/calamares/modules/os-freebsd/FreeBSDJob.h new file mode 100644 index 0000000..5f39f08 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/os-freebsd/FreeBSDJob.h @@ -0,0 +1,39 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2019 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSE + */ + +#ifndef FREEBSDJOB_H +#define FREEBSDJOB_H + +#include "CppJob.h" +#include "DllMacro.h" +#include "utils/PluginFactory.h" + +#include +#include + + +class PLUGINDLLEXPORT FreeBSDJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit FreeBSDJob( QObject* parent = nullptr ); + virtual ~FreeBSDJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private: + QVariantMap m_configurationMap; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( FreeBSDJobFactory ) + +#endif // FREEBSDJOB_H diff --git a/airootfs/usr/lib/calamares/modules/os-nixos/main.py b/airootfs/usr/lib/calamares/modules/os-nixos/main.py new file mode 100644 index 0000000..469c613 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/os-nixos/main.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# Calamares is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Calamares is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Calamares. If not, see . +# +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +=== NixOS Configuration + +NixOS has its own "do all the things" configuration file which +declaratively handles what things need to be done in the target +system, and it has an existing tool to "execute" that declarative +specification. This module takes configuration values set by +Calamares viewmodules (e.g. the users module) and puts +them into the configuration file in the target system, +and then runs the necessary NixOS specific tools. +""" + +import libcalamares +import os +from time import gmtime, strftime, sleep + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +# The following long **long** string is the entire text of +# a nix-configuration file. It is cribbed from, and adapted from, +# the sample file in https://github.com/itamar567/dotnix . +# +# We are going to substitute values into this text. However, +# Python's .format() function wants parens { } around variable +# names, and Nix's config file wants to use parens { } for block +# structure. So we have a compromise format here: +# +# - Write the config file as you would normally, +# - Write @@variable@@ instead of {variable} +# +# Some minor trickery later will massage this and substitute variables. +# +configuration_nix_sample = """# Nix configuration file +{ config, pkgs, ... }: + +{ + imports = + [ # Include the results of the hardware scan. + ./hardware-configuration.nix + ./command-not-found/command-not-found.nix + ]; + + # Use the systemd-boot EFI boot loader. + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + # Use Zen Kernel + boot.kernelPackages = pkgs.linuxPackages_zen; + + networking.hostName = "@@hostname@@"; # Define your hostname. + # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. + + # Set your time zone. + time.timeZone = "@@timezone@@"; + + # The global useDHCP flag is deprecated, therefore explicitly set to false here. + networking.useDHCP = false; + networking.interfaces.enp42s0.useDHCP = true; + + # Select internationalisation properties. + i18n.defaultLocale = "en_US.UTF-8"; + + # Configure X11 + services.xserver = { + enable = true; + windowManager.i3 = { + enable = true; + package = pkgs.i3-gaps; + }; + + # Set i3 to the default session in the display manager + displayManager.defaultSession = "none+i3"; + }; + + # SSH fix + programs.ssh.askPassword = pkgs.lib.mkForce ""; + + # Enable CUPS to print documents. + services.printing.enable = true; + + # Enable sound. + sound.enable = true; + hardware.pulseaudio.enable = true; + + # Define a user account. Don't forget to set a password with ‘passwd’. + users.users.username = { + isNormalUser = true; + extraGroups = [ "wheel" "libvirtd" ]; + }; + + # Disable password for sudo + security.sudo.extraRules= [{ + groups = [ "wheel" ]; + commands = [{ + command = "ALL" ; + options= [ "NOPASSWD" ]; + }]; + }]; + + # Set ZSH as the default shell + users.defaultUserShell = pkgs.zsh; + + # clean /tmp on boot + boot.cleanTmpDir=true; + + # Config packages + nixpkgs.config = { + allowUnfree = true; + + chromium = { + enableWideVine = true; + }; + }; + + # Automatically upgrade the system + # This service is a modified version of https://github.com/NixOS/nixpkgs/blob/nixos-21.05/nixos/modules/tasks/auto-upgrade.nix#L122 + systemd = { + services.nixos-upgrade = { + description = "NixOS Upgrade"; + + # We use --upgrade, so we need internet access + wants = [ "network-online.target" ]; + + restartIfChanged = false; + unitConfig.X-StopOnRemoval = false; + + serviceConfig.Type = "oneshot"; + + environment = config.nix.envVars // { + inherit (config.environment.sessionVariables) NIX_PATH; + HOME = "/root"; + } // config.networking.proxy.envVars; + + path = with pkgs; [ + coreutils + gnutar + xz.bin + gzip + gitMinimal + config.nix.package.out + ]; + + script = let + nixos-rebuild = + "${config.system.build.nixos-rebuild}/bin/nixos-rebuild"; + in '' + ${nixos-rebuild} boot --upgrade + booted="$(readlink /run/booted-system/{initrd,kernel,kernel-modules})" + built="$(readlink /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})" + if [ "$booted" = "$built" ]; then + ${nixos-rebuild} switch + fi + ''; + }; + + # To start the service at boot, we will use a systemd timer + timers.nixos-upgrade = { + wantedBy = [ "timers.target" ]; + partOf = [ "nixos-upgrade.service" ]; + timerConfig.OnBootSec = "5s"; + }; + }; + + # Some programs need SUID wrappers, can be configured further or are + # started in user sessions. + programs.mtr.enable = true; + programs.gnupg.agent = { + enable = true; + enableSSHSupport = true; + }; + + # Enable the OpenSSH daemon. + services.openssh.enable = true; + + # Open ports in the firewall. + networking.firewall.enable = false; + + # This value determines the NixOS release from which the default + # settings for stateful data, like file locations and database versions + # on your system were taken. It‘s perfectly fine and recommended to leave + # this value at the release version of the first install of this system. + # Before changing this value read the documentation for this option + # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). + system.stateVersion = "20.09"; # Did you read the comment? +} +""" + + +def pretty_name(): + return _("NixOS Configuration.") + + +def catenate(d, key, *values): + """ + Sets @p d[key] to the string-concatenation of @p values + if none of the values are None. + + This can be used to set keys conditionally based on + the values being found. + """ + if [v for v in values if v is None]: + return + + d[key] = "".join(values) + + +def run(): + """NixOS Configuration.""" + + gs = libcalamares.globalstorage + text = configuration_nix_sample + + # Collect variables to substitute into the main text + variables = dict() + catenate(variables, "hostname", gs.value("hostname")) + catenate(variables, "timezone", gs.value("locationRegion"), "/", gs.value("locationZone")) + + # Check that all variables are used + for key in variables.keys(): + pattern = "@@{key}@@".format(key=key) + if not pattern in text: + libcalamares.utils.warning("Variable '{key}' is not used.".format(key=key)) + + # Check that all patterns exist + import re + variable_pattern = re.compile("@@\w+@@") + for match in variable_pattern.finditer(text): + variable_name = text[match.start()+2:match.end()-2] + if not variable_name in variables: + libcalamares.utils.warning("Variable '{key}' is used but not defined.".format(key=variable_name)) + + # Do the substitutions + for key in variables.keys(): + pattern = "@@{key}@@".format(key=key) + text = text.replace(pattern, str(variables[key])) + + # Write the result to a temp-file, then run the main tool. + # There is no progress reporting from the tool, so it's going + # to seem like the module is hanging (see issue #1740). + configuration_filename = "/tmp/configuration.nix" + with open(configuration_filename, "w") as f: + f.write(text) + + libcalamares.job.setprogress(0.1) + libcalamares.utils.check_target_env_call(["nix", configuration_filename]) + + # To indicate an error, return a tuple of: + # (message, detailed-error-message) + return None diff --git a/airootfs/usr/lib/calamares/modules/os-nixos/module.desc b/airootfs/usr/lib/calamares/modules/os-nixos/module.desc new file mode 100644 index 0000000..c6f3568 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/os-nixos/module.desc @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The NixOS module writes a nix-configuration file and then calls +# the Nix configuration tool to do the actual work of building +# the target system. +--- +type: "job" +name: "os-nixos" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/os-nixos/tests/1.global b/airootfs/usr/lib/calamares/modules/os-nixos/tests/1.global new file mode 100644 index 0000000..b68dc4e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/os-nixos/tests/1.global @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +hostname: my-nix-host +locationRegion: Asia +locationZone: Kolkata diff --git a/airootfs/usr/lib/calamares/modules/packages/main.py b/airootfs/usr/lib/calamares/modules/packages/main.py new file mode 100644 index 0000000..b1aa6e3 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/packages/main.py @@ -0,0 +1,823 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Pier Luigi Fiorini +# SPDX-FileCopyrightText: 2015-2017 Teo Mrnjavac +# SPDX-FileCopyrightText: 2016-2017 Kyle Robbertze +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2018 Adriaan de Groot +# SPDX-FileCopyrightText: 2018 Philip Müller +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import abc +from string import Template +import subprocess + +import libcalamares +from libcalamares.utils import check_target_env_call, target_env_call +from libcalamares.utils import gettext_path, gettext_languages + +import gettext +_translation = gettext.translation("calamares-python", + localedir=gettext_path(), + languages=gettext_languages(), + fallback=True) +_ = _translation.gettext +_n = _translation.ngettext + + +total_packages = 0 # For the entire job +completed_packages = 0 # Done so far for this job +group_packages = 0 # One group of packages from an -install or -remove entry + +# A PM object may set this to a string (take care of translations!) +# to override the string produced by pretty_status_message() +custom_status_message = None + +INSTALL = object() +REMOVE = object() +mode_packages = None # Changes to INSTALL or REMOVE + + +def _change_mode(mode): + global mode_packages + mode_packages = mode + libcalamares.job.setprogress(completed_packages * 1.0 / total_packages) + + +def pretty_name(): + return _("Install packages.") + + +def pretty_status_message(): + if custom_status_message is not None: + return custom_status_message + if not group_packages: + if (total_packages > 0): + # Outside the context of an operation + s = _("Processing packages (%(count)d / %(total)d)") + else: + s = _("Install packages.") + + elif mode_packages is INSTALL: + s = _n("Installing one package.", + "Installing %(num)d packages.", group_packages) + elif mode_packages is REMOVE: + s = _n("Removing one package.", + "Removing %(num)d packages.", group_packages) + else: + # No mode, generic description + s = _("Install packages.") + + return s % {"num": group_packages, + "count": completed_packages, + "total": total_packages} + + +class PackageManager(metaclass=abc.ABCMeta): + """ + Package manager base class. A subclass implements package management + for a specific backend, and must have a class property `backend` + with the string identifier for that backend. + + Subclasses are collected below to populate the list of possible + backends. + """ + backend = None + + @abc.abstractmethod + def install(self, pkgs, from_local=False): + """ + Install a list of packages (named) into the system. + Although this handles lists, in practice it is called + with one package at a time. + + @param pkgs: list[str] + list of package names + @param from_local: bool + if True, then these are local packages (on disk) and the + pkgs names are paths. + """ + pass + + @abc.abstractmethod + def remove(self, pkgs): + """ + Removes packages. + + @param pkgs: list[str] + list of package names + """ + pass + + @abc.abstractmethod + def update_db(self): + pass + + def run(self, script): + if script != "": + check_target_env_call(script.split(" ")) + + def install_package(self, packagedata, from_local=False): + """ + Install a package from a single entry in the install list. + This can be either a single package name, or an object + with pre- and post-scripts. If @p packagedata is a dict, + it is assumed to follow the documented structure. + + @param packagedata: str|dict + @param from_local: bool + see install.from_local + """ + if isinstance(packagedata, str): + self.install([packagedata], from_local=from_local) + else: + self.run(packagedata["pre-script"]) + self.install([packagedata["package"]], from_local=from_local) + self.run(packagedata["post-script"]) + + def remove_package(self, packagedata): + """ + Remove a package from a single entry in the remove list. + This can be either a single package name, or an object + with pre- and post-scripts. If @p packagedata is a dict, + it is assumed to follow the documented structure. + + @param packagedata: str|dict + """ + if isinstance(packagedata, str): + self.remove([packagedata]) + else: + self.run(packagedata["pre-script"]) + self.remove([packagedata["package"]]) + self.run(packagedata["post-script"]) + + def operation_install(self, package_list, from_local=False): + """ + Installs the list of packages named in @p package_list . + These can be strings -- plain package names -- or + structures (with a pre- and post-install step). + + This operation is called for "critical" packages, + which are expected to succeed, or fail, all together. + However, if there are packages with pre- or post-scripts, + then packages are installed one-by-one instead. + + NOTE: package managers may reimplement this method + NOTE: exceptions are expected to leave this method, to indicate + failure of the installation. + """ + if all([isinstance(x, str) for x in package_list]): + self.install(package_list, from_local=from_local) + else: + for package in package_list: + self.install_package(package, from_local=from_local) + + def operation_try_install(self, package_list): + """ + Installs the list of packages named in @p package_list . + These can be strings -- plain package names -- or + structures (with a pre- and post-install step). + + This operation is called for "non-critical" packages, + which can succeed or fail without affecting the overall installation. + Packages are installed one-by-one to support package managers + that do not have a "install as much as you can" mode. + + NOTE: package managers may reimplement this method + NOTE: no package-installation exceptions should be raised + """ + # we make a separate package manager call for each package so a + # single failing package won't stop all of them + for package in package_list: + try: + self.install_package(package) + except subprocess.CalledProcessError: + libcalamares.utils.warning("Could not install package %s" % package) + + def operation_remove(self, package_list): + """ + Removes the list of packages named in @p package_list . + These can be strings -- plain package names -- or + structures (with a pre- and post-install step). + + This operation is called for "critical" packages, which are + expected to succeed or fail all together. + However, if there are packages with pre- or post-scripts, + then packages are removed one-by-one instead. + + NOTE: package managers may reimplement this method + NOTE: exceptions should be raised to indicate failure + """ + if all([isinstance(x, str) for x in package_list]): + self.remove(package_list) + else: + for package in package_list: + self.remove_package(package) + + def operation_try_remove(self, package_list): + """ + Same relation as try_install has to install, except it removes + packages instead. Packages are removed one-by-one. + + NOTE: package managers may reimplement this method + NOTE: no package-installation exceptions should be raised + """ + for package in package_list: + try: + self.remove_package(package) + except subprocess.CalledProcessError: + libcalamares.utils.warning("Could not remove package %s" % package) + +### PACKAGE MANAGER IMPLEMENTATIONS +# +# Keep these alphabetical (presumably both by class name and backend name), +# even the Dummy implementation. +# + +class PMApk(PackageManager): + backend = "apk" + + def install(self, pkgs, from_local=False): + for pkg in pkgs: + check_target_env_call(["apk", "add", pkg]) + + def remove(self, pkgs): + for pkg in pkgs: + check_target_env_call(["apk", "del", pkg]) + + def update_db(self): + check_target_env_call(["apk", "update"]) + + def update_system(self): + check_target_env_call(["apk", "upgrade", "--available"]) + + +class PMApt(PackageManager): + backend = "apt" + + def install(self, pkgs, from_local=False): + check_target_env_call(["apt-get", "-q", "-y", "install"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["apt-get", "--purge", "-q", "-y", + "remove"] + pkgs) + check_target_env_call(["apt-get", "--purge", "-q", "-y", + "autoremove"]) + + def update_db(self): + check_target_env_call(["apt-get", "update"]) + + def update_system(self): + # Doesn't need to update the system explicitly + pass + + +class PMDnf(PackageManager): + """ + This is "legacy" DNF, called DNF-4 even though the + executable is dnf-3 in modern Fedora. Executable dnf + is a symlink to dnf-3 in systems that use it. + """ + backend = "dnf" + + def install(self, pkgs, from_local=False): + check_target_env_call(["dnf-3", "-y", "install"] + pkgs) + + def remove(self, pkgs): + # ignore the error code for now because dnf thinks removing a + # nonexistent package is an error + target_env_call(["dnf-3", "--disablerepo=*", "-C", "-y", + "remove"] + pkgs) + + def update_db(self): + # Doesn't need updates + pass + + def update_system(self): + check_target_env_call(["dnf-3", "-y", "upgrade"]) + + +class PMDnf5(PackageManager): + """ + This is "modern" DNF, DNF-5 which is for Fedora 41 (presumably) + and later. Executable dnf is a symlink to dnf5 in systems that use it. + """ + backend = "dnf5" + + def install(self, pkgs, from_local=False): + check_target_env_call(["dnf5", "-y", "install"] + pkgs) + + def remove(self, pkgs): + # ignore the error code for now because dnf thinks removing a + # nonexistent package is an error + target_env_call(["dnf5", "--disablerepo=*", "-C", "-y", + "remove"] + pkgs) + + def update_db(self): + # Doesn't need updates + pass + + def update_system(self): + check_target_env_call(["dnf5", "-y", "upgrade"]) + + +class PMDummy(PackageManager): + backend = "dummy" + + def install(self, pkgs, from_local=False): + from time import sleep + libcalamares.utils.debug("Dummy backend: Installing " + str(pkgs)) + sleep(3) + + def remove(self, pkgs): + from time import sleep + libcalamares.utils.debug("Dummy backend: Removing " + str(pkgs)) + sleep(3) + + def update_db(self): + libcalamares.utils.debug("Dummy backend: Updating DB") + + def update_system(self): + libcalamares.utils.debug("Dummy backend: Updating System") + + def run(self, script): + libcalamares.utils.debug("Dummy backend: Running script '" + str(script) + "'") + + +class PMEntropy(PackageManager): + backend = "entropy" + + def install(self, pkgs, from_local=False): + check_target_env_call(["equo", "i"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["equo", "rm"] + pkgs) + + def update_db(self): + check_target_env_call(["equo", "update"]) + + def update_system(self): + # Doesn't need to update the system explicitly + pass + +class PMFlatpak(PackageManager): + backend = "flatpak" + + def install(self, pkgs, from_local=False): + check_target_env_call(["flatpak", "install", "--assumeyes"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["flatpak", "uninstall", "--noninteractive"] + pkgs) + + def update_db(self): + pass + + def update_system(self): + # Doesn't need to update the system explicitly + pass + +class PMLuet(PackageManager): + backend = "luet" + + def install(self, pkgs, from_local=False): + check_target_env_call(["luet", "install", "-y"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["luet", "uninstall", "-y"] + pkgs) + + def update_db(self): + # Luet checks for DB update everytime its ran. + pass + + def update_system(self): + check_target_env_call(["luet", "upgrade", "-y"]) + + +class PMPackageKit(PackageManager): + backend = "packagekit" + + def install(self, pkgs, from_local=False): + for pkg in pkgs: + check_target_env_call(["pkcon", "-py", "install", pkg]) + + def remove(self, pkgs): + for pkg in pkgs: + check_target_env_call(["pkcon", "-py", "remove", pkg]) + + def update_db(self): + check_target_env_call(["pkcon", "refresh"]) + + def update_system(self): + check_target_env_call(["pkcon", "-py", "update"]) + + +class PMPacman(PackageManager): + backend = "pacman" + + def __init__(self): + import re + progress_match = re.compile("^\\((\\d+)/(\\d+)\\)") + + def line_cb(line): + if line.startswith(":: "): + self.in_package_changes = "package" in line or "hooks" in line + else: + if self.in_package_changes and line.endswith("...\n"): + # Update the message, untranslated; do not change the + # progress percentage, since there may be more "installing..." + # lines in the output for the group, than packages listed + # explicitly. We don't know how to calculate proper progress. + global custom_status_message + custom_status_message = "pacman: " + line.strip() + libcalamares.job.setprogress(self.progress_fraction) + libcalamares.utils.debug(line) + + self.in_package_changes = False + self.line_cb = line_cb + + pacman = libcalamares.job.configuration.get("pacman", None) + if pacman is None: + pacman = dict() + if type(pacman) is not dict: + libcalamares.utils.warning("Job configuration *pacman* will be ignored.") + pacman = dict() + self.pacman_num_retries = pacman.get("num_retries", 0) + self.pacman_disable_timeout = pacman.get("disable_download_timeout", False) + self.pacman_needed_only = pacman.get("needed_only", False) + + def reset_progress(self): + self.in_package_changes = False + # These are globals + self.progress_fraction = (completed_packages * 1.0 / total_packages) + + def run_pacman(self, command, callback=False): + """ + Call pacman in a loop until it is successful or the number of retries is exceeded + :param command: The pacman command to run + :param callback: An optional boolean that indicates if this pacman run should use the callback + :return: + """ + + pacman_count = 0 + while pacman_count <= self.pacman_num_retries: + pacman_count += 1 + try: + if False: # callback: + libcalamares.utils.target_env_process_output(command, self.line_cb) + else: + libcalamares.utils.target_env_process_output(command) + + return + except subprocess.CalledProcessError: + if pacman_count <= self.pacman_num_retries: + pass + else: + raise + + def install(self, pkgs, from_local=False): + command = ["pacman"] + + if from_local: + command.append("-U") + else: + command.append("-S") + + # Don't ask for user intervention, take the default action + command.append("--noconfirm") + + # Don't report download progress for each file + command.append("--noprogressbar") + + if self.pacman_needed_only is True: + command.append("--needed") + + if self.pacman_disable_timeout is True: + command.append("--disable-download-timeout") + + command += pkgs + + self.reset_progress() + self.run_pacman(command, True) + + def remove(self, pkgs): + self.reset_progress() + self.run_pacman(["pacman", "-Rs", "--noconfirm"] + pkgs, True) + + def update_db(self): + self.run_pacman(["pacman", "-Sy"]) + + def update_system(self): + command = ["pacman", "-Su", "--noconfirm"] + if self.pacman_disable_timeout is True: + command.append("--disable-download-timeout") + + self.run_pacman(command) + + +class PMPamac(PackageManager): + backend = "pamac" + + def del_db_lock(self, lock="/var/lib/pacman/db.lck"): + # In case some error or crash, the database will be locked, + # resulting in remaining packages not being installed. + check_target_env_call(["rm", "-f", lock]) + + def install(self, pkgs, from_local=False): + self.del_db_lock() + check_target_env_call([self.backend, "install", "--no-confirm"] + pkgs) + + def remove(self, pkgs): + self.del_db_lock() + check_target_env_call([self.backend, "remove", "--no-confirm"] + pkgs) + + def update_db(self): + self.del_db_lock() + check_target_env_call([self.backend, "update", "--no-confirm"]) + + def update_system(self): + self.del_db_lock() + check_target_env_call([self.backend, "upgrade", "--no-confirm"]) + + +class PMPisi(PackageManager): + backend = "pisi" + + def install(self, pkgs, from_local=False): + check_target_env_call(["pisi", "install" "-y"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["pisi", "remove", "-y"] + pkgs) + + def update_db(self): + check_target_env_call(["pisi", "update-repo"]) + + def update_system(self): + # Doesn't need to update the system explicitly + pass + + +class PMPortage(PackageManager): + backend = "portage" + + def install(self, pkgs, from_local=False): + check_target_env_call(["emerge", "-v"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["emerge", "-C"] + pkgs) + check_target_env_call(["emerge", "--depclean", "-q"]) + + def update_db(self): + check_target_env_call(["emerge", "--sync"]) + + def update_system(self): + # Doesn't need to update the system explicitly + pass + + +class PMXbps(PackageManager): + backend = "xbps" + + def line_cb(self, line): + libcalamares.utils.debug(line) + + def run_xbps(self, command): + libcalamares.utils.target_env_process_output(command, self.line_cb); + + def install(self, pkgs, from_local=False): + self.run_xbps(["xbps-install", "-Sy"] + pkgs) + + def remove(self, pkgs): + self.run_xbps(["xbps-remove", "-Ry"] + pkgs) + + def update_db(self): + self.run_xbps(["xbps-install", "-S"]) + + def update_system(self): + self.run_xbps(["xbps", "-Suy"]) + + +class PMYum(PackageManager): + backend = "yum" + + def install(self, pkgs, from_local=False): + check_target_env_call(["yum", "-y", "install"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["yum", "--disablerepo=*", "-C", "-y", + "remove"] + pkgs) + + def update_db(self): + # Doesn't need updates + pass + + def update_system(self): + check_target_env_call(["yum", "-y", "upgrade"]) + + +class PMZypp(PackageManager): + backend = "zypp" + + def install(self, pkgs, from_local=False): + check_target_env_call(["zypper", "--non-interactive", + "--quiet-install", "install", + "--auto-agree-with-licenses", + "install"] + pkgs) + + def remove(self, pkgs): + check_target_env_call(["zypper", "--non-interactive", + "remove"] + pkgs) + + def update_db(self): + check_target_env_call(["zypper", "--non-interactive", "update"]) + + def update_system(self): + # Doesn't need to update the system explicitly + pass + + +# Collect all the subclasses of PackageManager defined above, +# and index them based on the backend property of each class. +backend_managers = [ + (c.backend, c) + for c in globals().values() + if type(c) is abc.ABCMeta and issubclass(c, PackageManager) and c.backend] + + +def subst_locale(plist): + """ + Returns a locale-aware list of packages, based on @p plist. + Package names that contain LOCALE are localized with the + BCP47 name of the chosen system locale; if the system + locale is 'en' (e.g. English, US) then these localized + packages are dropped from the list. + + @param plist: list[str|dict] + Candidate packages to install. + @return: list[str|dict] + """ + locale = libcalamares.globalstorage.value("locale") + if not locale: + # It is possible to skip the locale-setting entirely. + # Then pretend it is "en", so that {LOCALE}-decorated + # package names are removed from the list. + locale = "en" + + ret = [] + for packagedata in plist: + if isinstance(packagedata, str): + packagename = packagedata + else: + packagename = packagedata["package"] + + # Update packagename: substitute LOCALE, and drop packages + # if locale is en and LOCALE is in the package name. + if locale != "en": + packagename = Template(packagename).safe_substitute(LOCALE=locale) + elif 'LOCALE' in packagename: + packagename = None + + if packagename is not None: + # Put it back in packagedata + if isinstance(packagedata, str): + packagedata = packagename + else: + packagedata["package"] = packagename + + ret.append(packagedata) + + return ret + + +def run_operations(pkgman, entry): + """ + Call package manager with suitable parameters for the given + package actions. + + :param pkgman: PackageManager + This is the manager that does the actual work. + :param entry: dict + Keys are the actions -- e.g. "install" -- to take, and the values + are the (list of) packages to apply the action to. The actions are + not iterated in a specific order, so it is recommended to use only + one action per dictionary. The list of packages may be package + names (strings) or package information dictionaries with pre- + and post-scripts. + """ + global group_packages, completed_packages, mode_packages + + for key in entry.keys(): + package_list = subst_locale(entry[key]) + group_packages = len(package_list) + if key == "install": + _change_mode(INSTALL) + pkgman.operation_install(package_list) + elif key == "try_install": + _change_mode(INSTALL) + pkgman.operation_try_install(package_list) + elif key == "remove": + _change_mode(REMOVE) + pkgman.operation_remove(package_list) + elif key == "try_remove": + _change_mode(REMOVE) + pkgman.operation_try_remove(package_list) + elif key == "localInstall": + _change_mode(INSTALL) + pkgman.operation_install(package_list, from_local=True) + elif key == "source": + libcalamares.utils.debug("Package-list from {!s}".format(entry[key])) + else: + libcalamares.utils.warning("Unknown package-operation key {!s}".format(key)) + completed_packages += len(package_list) + libcalamares.job.setprogress(completed_packages * 1.0 / total_packages) + libcalamares.utils.debug("Pretty name: {!s}, setting progress..".format(pretty_name())) + + group_packages = 0 + _change_mode(None) + + +def run(): + """ + Calls routine with detected package manager to install locale packages + or remove drivers not needed on the installed system. + + :return: + """ + global mode_packages, total_packages, completed_packages, group_packages + + backend = libcalamares.job.configuration.get("backend") + + for identifier, impl in backend_managers: + if identifier == backend: + pkgman = impl() + break + else: + return "Bad backend", "backend=\"{}\"".format(backend) + + skip_this = libcalamares.job.configuration.get("skip_if_no_internet", False) + if skip_this and not libcalamares.globalstorage.value("hasInternet"): + libcalamares.utils.warning( "Package installation has been skipped: no internet" ) + return None + + update_db = libcalamares.job.configuration.get("update_db", False) + if update_db and libcalamares.globalstorage.value("hasInternet"): + try: + pkgman.update_db() + except subprocess.CalledProcessError as e: + libcalamares.utils.warning(str(e)) + libcalamares.utils.debug("stdout:" + str(e.stdout)) + libcalamares.utils.debug("stderr:" + str(e.stderr)) + return (_("Package Manager error"), + _("The package manager could not prepare updates. The command
{!s}
returned error code {!s}.") + .format(e.cmd, e.returncode)) + + update_system = libcalamares.job.configuration.get("update_system", False) + if update_system and libcalamares.globalstorage.value("hasInternet"): + try: + pkgman.update_system() + except subprocess.CalledProcessError as e: + libcalamares.utils.warning(str(e)) + libcalamares.utils.debug("stdout:" + str(e.stdout)) + libcalamares.utils.debug("stderr:" + str(e.stderr)) + return (_("Package Manager error"), + _("The package manager could not update the system. The command
{!s}
returned error code {!s}.") + .format(e.cmd, e.returncode)) + + operations = libcalamares.job.configuration.get("operations", []) + if libcalamares.globalstorage.contains("packageOperations"): + operations += libcalamares.globalstorage.value("packageOperations") + + mode_packages = None + total_packages = 0 + completed_packages = 0 + for op in operations: + for packagelist in op.values(): + total_packages += len(subst_locale(packagelist)) + + if not total_packages: + # Avoids potential divide-by-zero in progress reporting + return None + + for entry in operations: + group_packages = 0 + libcalamares.utils.debug(pretty_name()) + try: + run_operations(pkgman, entry) + except subprocess.CalledProcessError as e: + libcalamares.utils.warning(str(e)) + libcalamares.utils.debug("stdout:" + str(e.stdout)) + libcalamares.utils.debug("stderr:" + str(e.stderr)) + return (_("Package Manager error"), + _("The package manager could not make changes to the installed system. The command
{!s}
returned error code {!s}.") + .format(e.cmd, e.returncode)) + + mode_packages = None + + libcalamares.job.setprogress(1.0) + + return None diff --git a/airootfs/usr/lib/calamares/modules/packages/module.desc b/airootfs/usr/lib/calamares/modules/packages/module.desc new file mode 100644 index 0000000..3e3053b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/packages/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "packages" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/packages/packages.schema.yaml b/airootfs/usr/lib/calamares/modules/packages/packages.schema.yaml new file mode 100644 index 0000000..3e3b516 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/packages/packages.schema.yaml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/packages +additionalProperties: false +type: object +properties: + backend: + type: string + enum: + - apk + - apt + - dnf + - dnf5 + - entropy + - luet + - packagekit + - pacman + - pamac + - portage + - yum + - zypp + - dummy + + update_db: { type: boolean, default: true } + update_system: { type: boolean, default: false } + skip_if_no_internet: { type: boolean, default: false } + + pacman: + additionalProperties: false + type: object + properties: + num_retries: { type: integer, default: 0 } + disable_download_timeout: { type: boolean, default: false } + needed_only: { type: boolean, default: false } + + operations: + type: array + items: + additionalProperties: false + type: object + properties: + # TODO: these are either-string-or-struct items, + # need their own little schema. + install: { type: array } + remove: { type: array } + try_install: { type: array } + try_remove: { type: array } + localInstall: { type: array } + source: { type: string } + + +required: [ backend ] diff --git a/airootfs/usr/lib/calamares/modules/partition/libcalamares_viewmodule_partition.so b/airootfs/usr/lib/calamares/modules/partition/libcalamares_viewmodule_partition.so new file mode 100755 index 0000000..eb632a1 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/partition/libcalamares_viewmodule_partition.so differ diff --git a/airootfs/usr/lib/calamares/modules/partition/module.desc b/airootfs/usr/lib/calamares/modules/partition/module.desc new file mode 100644 index 0000000..58caf78 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/partition/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "partition" +interface: "qtplugin" +load: "libcalamares_viewmodule_partition.so" diff --git a/airootfs/usr/lib/calamares/modules/plasmalnf/libcalamares_viewmodule_plasmalnf.so b/airootfs/usr/lib/calamares/modules/plasmalnf/libcalamares_viewmodule_plasmalnf.so new file mode 100755 index 0000000..a576877 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/plasmalnf/libcalamares_viewmodule_plasmalnf.so differ diff --git a/airootfs/usr/lib/calamares/modules/plasmalnf/module.desc b/airootfs/usr/lib/calamares/modules/plasmalnf/module.desc new file mode 100644 index 0000000..91af47a --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/plasmalnf/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "plasmalnf" +interface: "qtplugin" +load: "libcalamares_viewmodule_plasmalnf.so" diff --git a/airootfs/usr/lib/calamares/modules/plymouthcfg/main.py b/airootfs/usr/lib/calamares/modules/plymouthcfg/main.py new file mode 100644 index 0000000..529d342 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/plymouthcfg/main.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2016 Artoo +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2018 Gabriel Craciunescu +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares + +from libcalamares.utils import debug, target_env_call + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configure Plymouth theme") + + +def detect_plymouth(): + """ + Checks existence (runnability) of plymouth in the target system. + + @return True if plymouth exists in the target, False otherwise + """ + # Used to only check existence of path /usr/bin/plymouth in target + return target_env_call(["sh", "-c", "which plymouth"]) == 0 + + +class PlymouthController: + + def __init__(self): + self.__root = libcalamares.globalstorage.value('rootMountPoint') + + @property + def root(self): + return self.__root + + def setTheme(self): + plymouth_theme = libcalamares.job.configuration["plymouth_theme"] + target_env_call(["plymouth-set-default-theme", plymouth_theme]) + + def run(self): + if detect_plymouth(): + if (("plymouth_theme" in libcalamares.job.configuration) and + (libcalamares.job.configuration["plymouth_theme"] is not None)): + self.setTheme() + return None + + +def run(): + pc = PlymouthController() + return pc.run() diff --git a/airootfs/usr/lib/calamares/modules/plymouthcfg/module.desc b/airootfs/usr/lib/calamares/modules/plymouthcfg/module.desc new file mode 100644 index 0000000..660aa71 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/plymouthcfg/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "plymouthcfg" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/plymouthcfg/plymouthcfg.schema.yaml b/airootfs/usr/lib/calamares/modules/plymouthcfg/plymouthcfg.schema.yaml new file mode 100644 index 0000000..27925ec --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/plymouthcfg/plymouthcfg.schema.yaml @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/plymouthcfg +additionalProperties: false +type: object +properties: + plymouth_theme: { type: string } diff --git a/airootfs/usr/lib/calamares/modules/preservefiles/libcalamares_job_preservefiles.so b/airootfs/usr/lib/calamares/modules/preservefiles/libcalamares_job_preservefiles.so new file mode 100755 index 0000000..b788a23 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/preservefiles/libcalamares_job_preservefiles.so differ diff --git a/airootfs/usr/lib/calamares/modules/preservefiles/module.desc b/airootfs/usr/lib/calamares/modules/preservefiles/module.desc new file mode 100644 index 0000000..8cfdfba --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/preservefiles/module.desc @@ -0,0 +1,8 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "preservefiles" +interface: "qtplugin" +load: "libcalamares_job_preservefiles.so" +emergency: true diff --git a/airootfs/usr/lib/calamares/modules/rawfs/main.py b/airootfs/usr/lib/calamares/modules/rawfs/main.py new file mode 100644 index 0000000..7604982 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/rawfs/main.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2019 Collabora Ltd +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares +import os +import stat +import subprocess +from time import gmtime, strftime, sleep +from math import gcd + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + +def pretty_name(): + return _("Installing data.") + +def lcm(a, b): + """ + Computes the Least Common Multiple of 2 numbers + """ + return a * b / gcd(a, b) + +def get_device_size(device): + """ + Returns a filesystem's total size and block size in bytes. + For block devices, block size is the device's block size. + For other files (fs images), block size is 1 byte. + + @param device: str + Absolute path to the device or filesystem image. + @return: tuple(int, int) + The filesystem's size and its block size. + """ + mode = os.stat(device).st_mode + if stat.S_ISBLK(mode): + basedevice = "" + partition = os.path.basename(device) + tmp = partition + while len(tmp) > 0: + tmp = tmp[:-1] + if os.path.exists("/sys/block/" + tmp): + basedevice = tmp + break + # Get device block size + file = open("/sys/block/" + basedevice + "/queue/hw_sector_size") + blocksize = int(file.readline()) + file.close() + # Get partition size + file = open("/sys/block/" + basedevice + "/" + partition + "/size") + size = int(file.readline()) * blocksize + file.close() + else: + size = os.path.getsize(device) + blocksize = 1 + + return size, blocksize + +class RawFSLowSpaceError(Exception): + pass + +class RawFSItem: + __slots__ = ['source', 'destination', 'filesystem', 'resize'] + + def copy(self, current=0, total=1): + """ + Copies a raw filesystem on a disk partition, and grow it to the full destination + partition's size if required. + + @param current: int + The index of the current item in the filesystems list + (used for progress reporting) + @param total: int + The number of items in the filesystems list + (used for progress reporting) + """ + count = 0 + + libcalamares.utils.debug("Copying {} to {}".format(self.source, self.destination)) + if libcalamares.job.configuration.get("bogus", False): + return + + srcsize, srcblksize = get_device_size(self.source) + destsize, destblksize = get_device_size(self.destination) + + if destsize < srcsize: + raise RawFSLowSpaceError + return + + # Compute transfer block size (100x the LCM of the block sizes seems a good fit) + blksize = int(100 * lcm(srcblksize, destblksize)) + + # Execute copy + src = open(self.source, "rb") + dest = open(self.destination, "wb") + buffer = src.read(blksize) + while len(buffer) > 0: + dest.write(buffer) + count += len(buffer) + # Compute job progress + progress = ((count / srcsize) + (current)) / total + libcalamares.job.setprogress(progress) + # Read next data block + buffer = src.read(blksize) + src.close() + dest.close() + + if self.resize: + if "ext" in self.filesystem: + libcalamares.utils.debug("Resizing filesystem on {}".format(self.destination)) + subprocess.run(["e2fsck", "-f", "-y", self.destination]) + subprocess.run(["resize2fs", self.destination]) + + def __init__(self, config, device, fs): + libcalamares.utils.debug("Adding an entry for raw copy of {} to {}".format( + config["source"], device)) + self.source = os.path.realpath(config["source"]) + # If source is a mount point, look for the actual device mounted on it + if os.path.ismount(self.source) and not libcalamares.job.configuration.get("bogus", False): + procmounts = open("/proc/mounts", "r") + for line in procmounts: + if self.source in line.split(): + self.source = line.split()[0] + break + + self.destination = device + self.filesystem = fs + try: + self.resize = bool(config["resize"]) + except KeyError: + self.resize = False + +def update_global_storage(item, gs): + for partition in gs: + if partition["device"] == item.destination: + ret = subprocess.run(["blkid", "-s", "UUID", "-o", "value", item.destination], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + if ret.returncode == 0: + uuid = ret.stdout.rstrip() + libcalamares.utils.debug("Setting {} UUID to {}".format(item.destination, + uuid or "")) + gs[gs.index(partition)]["uuid"] = uuid + gs[gs.index(partition)]["source"] = item.source + + libcalamares.globalstorage.remove("partitions") + libcalamares.globalstorage.insert("partitions", gs) + +def run(): + """Raw filesystem copy module""" + filesystems = list() + partitions = libcalamares.globalstorage.value("partitions") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}
to use." ).format("rawfs")) + + libcalamares.utils.debug("Copying {!s} raw partitions.".format(len(partitions))) + for partition in partitions: + if partition["mountPoint"]: + for src in libcalamares.job.configuration["targets"]: + if src["mountPoint"] == partition["mountPoint"]: + filesystems.append(RawFSItem(src, partition["device"], partition["fs"])) + + for item in filesystems: + try: + item.copy(filesystems.index(item), len(filesystems)) + except RawFSLowSpaceError: + return ("Not enough free space", + "{} partition is too small to copy {} on it".format(item.destination, item.source)) + update_global_storage(item, partitions) + + return None diff --git a/airootfs/usr/lib/calamares/modules/rawfs/module.desc b/airootfs/usr/lib/calamares/modules/rawfs/module.desc new file mode 100644 index 0000000..0c4f21f --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/rawfs/module.desc @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# Module metadata file for block-copy jobmodule +# Syntax is YAML 1.2 +--- +type: "job" +name: "rawfs" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/refind/main.py b/airootfs/usr/lib/calamares/modules/refind/main.py new file mode 100644 index 0000000..0239aaf --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/refind/main.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2021 Anke Boersma +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import libcalamares + +import os +import subprocess + +from libcalamares.utils import check_target_env_call + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Install rEFInd.") + + +def get_uuid(): + partitions = libcalamares.globalstorage.value("partitions") + for partition in partitions: + if partition["mountPoint"] == "/": + libcalamares.utils.debug(partition["uuid"]) + return partition["uuid"] + return None + + +def update_conf(uuid, conf_path): + """ + Updates the created rEFInd configuration file based on given parameters. + """ + partitions = libcalamares.globalstorage.value("partitions") + + kernel_params = ["quiet", "systemd.show_status=0"] + swap = None # Partition UUID + swap_luks = None # LUKS name + cryptdevice_params = [] + btrfs_params = "" + + for partition in partitions: + if partition["fs"] == "linuxswap" and not "luksMapperName" in partition: + swap = partition["uuid"] + + if partition["fs"] == "linuxswap" and "luksMapperName" in partition: + swap_luks = partition["luksMapperName"] + + if partition["mountPoint"] == "/" and "luksMapperName" in partition: + cryptdevice_params = [ + "cryptdevice=UUID={!s}:{!s}".format(partition["luksUuid"], + partition["luksMapperName"]), + "root=/dev/mapper/{!s}".format(partition["luksMapperName"]), + "resume=/dev/mapper/{!s}".format(partition["luksMapperName"]) + ] + + # rEFInd with a BTRFS root filesystem needs to be told + # about the root subvolume. + if partition["mountPoint"] == "/" and partition["fs"] == "btrfs": + btrfs_params = "rootflags=subvol=@" + + if cryptdevice_params: + kernel_params.extend(cryptdevice_params) + else: + kernel_params.append("root=UUID={!s}".format(uuid)) + + if swap: + kernel_params.append("resume=UUID={!s}".format(swap)) + if swap_luks: + kernel_params.append("resume=/dev/mapper/{!s}".format(swap_luks)) + if btrfs_params: + kernel_params.append(btrfs_params) + + with open(conf_path, "r") as refind_file: + filedata = [x.strip() for x in refind_file.readlines()] + + with open(conf_path, 'w') as refind_file: + for line in filedata: + if line.startswith('"Boot with standard options"'): + line = '"Boot with standard options" "rw {!s}"'.format(" ".join(kernel_params)) + refind_file.write(line + "\n") + + +def efi_partitions(efi_boot_path): + """ + The (one) partition mounted on @p efi_boot_path, or an empty list. + """ + return [p for p in libcalamares.globalstorage.value("partitions") if p["mountPoint"] == efi_boot_path] + + +def install_refind(): + install_path = libcalamares.globalstorage.value("rootMountPoint") + uuid = get_uuid() + conf_path = os.path.join(install_path, "boot/refind_linux.conf") + + # TODO: some distro's use /boot/efi , so maybe this needs to + # become configurable (that depends on what rEFInd likes). + efi_boot_path = "/boot" + + # Might not have a /boot configured in the system at all; warn and don't operate + if not efi_partitions(efi_boot_path): + libcalamares.utils.warning("No partition mounted on {!s}".format(efi_boot_path)) + # This isn't returned as an error, but the installation + # probably won't boot because no bootloader was installed. + return None + + subprocess.call( + ["refind-install", "--root", "{!s}".format(install_path)]) + update_conf(uuid, conf_path) + + +def run(): + """ + Optional entry for when providing bootloader choices. + Values taken from a packagechooser instance. + Module won't run, if value not present. + """ + bootchoice = libcalamares.globalstorage.value("packagechooser_bootchoice") + + if bootchoice == "refind": + return install_refind() diff --git a/airootfs/usr/lib/calamares/modules/refind/module.desc b/airootfs/usr/lib/calamares/modules/refind/module.desc new file mode 100644 index 0000000..342d60c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/refind/module.desc @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "refind" +interface: "python" +script: "main.py" +noconfig: true +# The partition module sets up the needed paths in +# global storage, which is used to decide how to install. +requiredModules: [ "partition" ] diff --git a/airootfs/usr/lib/calamares/modules/refind/refind.conf.in b/airootfs/usr/lib/calamares/modules/refind/refind.conf.in new file mode 100644 index 0000000..ad587c3 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/refind/refind.conf.in @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +## This file should be present in the same directory as the EFISTUB kernel and initramfs files +## More info at http://www.rodsbooks.com/refind/linux.html , http://www.rodsbooks.com/efi-bootloaders/efistub.html +## File is not needed when rEFInd is installed with the `refind-install` option, it will be created automatically. + +#"Boot with defaults" "root=PARTUUID=XXXXXXXX rootfstype=XXXX rw add_efi_memmap" +#"Boot to terminal" "root=PARTUUID=XXXXXXXX rootfstype=XXXX rw add_efi_memmap systemd.unit=multi-user.target" + + diff --git a/airootfs/usr/lib/calamares/modules/removeuser/libcalamares_job_removeuser.so b/airootfs/usr/lib/calamares/modules/removeuser/libcalamares_job_removeuser.so new file mode 100755 index 0000000..3853ac9 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/removeuser/libcalamares_job_removeuser.so differ diff --git a/airootfs/usr/lib/calamares/modules/removeuser/module.desc b/airootfs/usr/lib/calamares/modules/removeuser/module.desc new file mode 100644 index 0000000..60829a7 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/removeuser/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "removeuser" +interface: "qtplugin" +load: "libcalamares_job_removeuser.so" diff --git a/airootfs/usr/lib/calamares/modules/services-systemd/main.py b/airootfs/usr/lib/calamares/modules/services-systemd/main.py new file mode 100644 index 0000000..19c5974 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/services-systemd/main.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Philip Müller +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot +# SPDX-FileCopyrightText: 2022 shivanandvp +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. + +import libcalamares + + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Configure systemd units") + + +def systemctl(units): + """ + For each entry in @p units, run "systemctl ", + where each unit is a mapping of unit name, action, and a flag. + + Returns a failure message, or None if this was successful. + Units that are not mandatory have their failures suppressed + silently. + """ + + for unit in units: + if isinstance(unit, str): + name = unit + action = "enable" + mandatory = False + else: + if "name" not in unit: + libcalamares.utils.error("The key 'name' is missing from the mapping {_unit!s}. Continuing to the next unit.".format(_unit=str(unit))) + continue + name = unit["name"] + action = unit.get("action", "enable") + mandatory = unit.get("mandatory", False) + + exit_code = libcalamares.utils.target_env_call( + ['systemctl', action, name] + ) + + if exit_code != 0: + libcalamares.utils.warning( + "Cannot {} systemd unit {}".format(action, name) + ) + libcalamares.utils.warning( + "systemctl {} call in chroot returned error code {}".format(action, exit_code) + ) + if mandatory: + title = _("Cannot modify unit") + diagnostic = _("systemctl {_action!s} call in chroot returned error code {_exit_code!s}.").format(_action=action, _exit_code=exit_code) + description = _("Cannot {_action!s} systemd unit {_name!s}.").format(_action=action, _name=name) + return ( + title, + description + " " + diagnostic + ) + return None + + +def run(): + """ + Setup systemd units + """ + cfg = libcalamares.job.configuration + + return_value = systemctl( + cfg.get("units", []) + ) + if return_value is not None: + return return_value + + return None diff --git a/airootfs/usr/lib/calamares/modules/services-systemd/module.desc b/airootfs/usr/lib/calamares/modules/services-systemd/module.desc new file mode 100644 index 0000000..e016c6d --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/services-systemd/module.desc @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "services-systemd" +interface: "python" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/services-systemd/services-systemd.schema.yaml b/airootfs/usr/lib/calamares/modules/services-systemd/services-systemd.schema.yaml new file mode 100644 index 0000000..7e1fe05 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/services-systemd/services-systemd.schema.yaml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/services-systemd +definitions: + unit: + $id: 'definitions/unit' + type: object + description: a map containing a unit name, an action, and whether it is mandatory + additionalProperties: false + properties: + name: { type: string } + action: { type: string, default: "enable" } + mandatory: { type: boolean, default: false } + required: [ name ] + +additionalProperties: false +type: object +properties: + units: { type: array, items: { $ref: 'definitions/unit' } } diff --git a/airootfs/usr/lib/calamares/modules/shellprocess/libcalamares_job_shellprocess.so b/airootfs/usr/lib/calamares/modules/shellprocess/libcalamares_job_shellprocess.so new file mode 100755 index 0000000..83d8b9a Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/shellprocess/libcalamares_job_shellprocess.so differ diff --git a/airootfs/usr/lib/calamares/modules/shellprocess/module.desc b/airootfs/usr/lib/calamares/modules/shellprocess/module.desc new file mode 100644 index 0000000..6eb9bec --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/shellprocess/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "shellprocess" +interface: "qtplugin" +load: "libcalamares_job_shellprocess.so" diff --git a/airootfs/usr/lib/calamares/modules/slowpython/main.py b/airootfs/usr/lib/calamares/modules/slowpython/main.py new file mode 100644 index 0000000..d41f316 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/slowpython/main.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# Calamares is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Calamares is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Calamares. If not, see . +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +# License-Filename: LICENSES/GPL-3.0 + +""" +The slowpython module is slow. +""" + +import libcalamares +from time import sleep + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Slow python job.") + +status = _("Slow python step {}/10").format(0) + +def pretty_status_message(): + return status + +def run(): + """Slow python job.""" + try: + timeout = int(libcalamares.job.timeout) + except: + timeout = 30 + + if not (3 <= timeout <= 600): + timeout = 30 + + libcalamares.utils.debug("Slow python job for {} seconds".format(timeout)) + + global status + step = timeout / 10.0 + for x in range(11): + status = _("Slow python step {}/10").format(x) + libcalamares.job.setprogress(x / 10.0) + sleep(step) + return None diff --git a/airootfs/usr/lib/calamares/modules/slowpython/module.desc b/airootfs/usr/lib/calamares/modules/slowpython/module.desc new file mode 100644 index 0000000..5320003 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/slowpython/module.desc @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The slowpython module is .. just slow. It can be used +# in testing to allow the slideshow time to run. +--- +type: "job" +interface: "python" +name: "slowpython" +script: "main.py" diff --git a/airootfs/usr/lib/calamares/modules/slowpython/slowpython.conf b/airootfs/usr/lib/calamares/modules/slowpython/slowpython.conf new file mode 100644 index 0000000..d366768 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/slowpython/slowpython.conf @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# This is an example module for Python Job Modules. +# +# The slowpython module is just slow. It does produce +# progress output, in 10 steps during the timeout period. +--- + +# Number of seconds this module will take to run +timeout: 30 diff --git a/airootfs/usr/lib/calamares/modules/summary/libcalamares_viewmodule_summary.so b/airootfs/usr/lib/calamares/modules/summary/libcalamares_viewmodule_summary.so new file mode 100755 index 0000000..e4c546b Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/summary/libcalamares_viewmodule_summary.so differ diff --git a/airootfs/usr/lib/calamares/modules/summary/module.desc b/airootfs/usr/lib/calamares/modules/summary/module.desc new file mode 100644 index 0000000..b82be4e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/summary/module.desc @@ -0,0 +1,8 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "summary" +interface: "qtplugin" +load: "libcalamares_viewmodule_summary.so" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/summaryq/libcalamares_viewmodule_summaryq.so b/airootfs/usr/lib/calamares/modules/summaryq/libcalamares_viewmodule_summaryq.so new file mode 100755 index 0000000..c1bea46 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/summaryq/libcalamares_viewmodule_summaryq.so differ diff --git a/airootfs/usr/lib/calamares/modules/summaryq/module.desc b/airootfs/usr/lib/calamares/modules/summaryq/module.desc new file mode 100644 index 0000000..8e407d8 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/summaryq/module.desc @@ -0,0 +1,8 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "summaryq" +interface: "qtplugin" +load: "libcalamares_viewmodule_summaryq.so" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/tracking/libcalamares_viewmodule_tracking.so b/airootfs/usr/lib/calamares/modules/tracking/libcalamares_viewmodule_tracking.so new file mode 100755 index 0000000..a3172b4 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/tracking/libcalamares_viewmodule_tracking.so differ diff --git a/airootfs/usr/lib/calamares/modules/tracking/module.desc b/airootfs/usr/lib/calamares/modules/tracking/module.desc new file mode 100644 index 0000000..a7e4639 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/tracking/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "tracking" +interface: "qtplugin" +load: "libcalamares_viewmodule_tracking.so" diff --git a/airootfs/usr/lib/calamares/modules/umount/libcalamares_job_umount.so b/airootfs/usr/lib/calamares/modules/umount/libcalamares_job_umount.so new file mode 100755 index 0000000..cadb8a8 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/umount/libcalamares_job_umount.so differ diff --git a/airootfs/usr/lib/calamares/modules/umount/module.desc b/airootfs/usr/lib/calamares/modules/umount/module.desc new file mode 100644 index 0000000..179b25c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/umount/module.desc @@ -0,0 +1,8 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "umount" +interface: "qtplugin" +load: "libcalamares_job_umount.so" +emergency: true diff --git a/airootfs/usr/lib/calamares/modules/unpackfs/main.py b/airootfs/usr/lib/calamares/modules/unpackfs/main.py new file mode 100644 index 0000000..fdb4fc9 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/unpackfs/main.py @@ -0,0 +1,542 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2014 Daniel Hillenbrand +# SPDX-FileCopyrightText: 2014 Philip Müller +# SPDX-FileCopyrightText: 2017 Alf Gaida +# SPDX-FileCopyrightText: 2019 Kevin Kofler +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-FileCopyrightText: 2020 Gabriel Craciunescu +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import os +import re +import shutil +import subprocess +import sys +import tempfile + +import libcalamares + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + +def pretty_name(): + return _("Filling up filesystems.") + +# This is going to be changed from various methods +status = pretty_name() + +def pretty_status_message(): + return status + +class UnpackEntry: + """ + Extraction routine using rsync. + + :param source: + :param sourcefs: + :param destination: + """ + __slots__ = ('source', 'sourcefs', 'destination', 'copied', 'total', 'exclude', 'excludeFile', + 'mountPoint', 'weight', 'condition', 'optional') + + def __init__(self, source, sourcefs, destination): + """ + @p source is the source file name (might be an image file, or + a directory, too) + @p sourcefs is a type indication; "file" is special, as is + "squashfs". + @p destination is where the files from the source go. This is + **already** prefixed by rootMountPoint, so should be a + valid absolute path within the host system. + + The members copied and total are filled in by the copying process. + """ + self.source = source + self.sourcefs = sourcefs + self.destination = destination + self.exclude = None + self.excludeFile = None + self.copied = 0 + self.total = 0 + self.mountPoint = None + self.weight = 1 + self.condition = True + self.optional = False + + def is_file(self): + return self.sourcefs == "file" + + def do_count(self): + """ + Counts the number of files this entry has. + """ + # Need a name we can use like a global + class counter(object): + count = 0 + def cb_count(s): + counter.count += 1 + + if self.sourcefs == "squashfs": + libcalamares.utils.host_env_process_output(["unsquashfs", "-l", self.source], cb_count) + + elif self.sourcefs == "ext4": + libcalamares.utils.host_env_process_output(["find", self.mountPoint, "-type", "f"], cb_count) + + elif self.is_file(): + # Hasn't been mounted, copy directly; find handles both + # files and directories. + libcalamares.utils.host_env_process_output(["find", self.source, "-type", "f"], cb_count) + + self.total = counter.count + return self.total + + def do_mount(self, base): + """ + Mount given @p entry as loop device underneath @p base + + A *file* entry (e.g. one with *sourcefs* set to *file*) + is not mounted and just ignored. + + :param base: directory to place all the mounts in. + + :returns: None, but throws if the mount failed + """ + imgbasename = os.path.splitext( + os.path.basename(self.source))[0] + imgmountdir = os.path.join(base, imgbasename) + os.makedirs(imgmountdir, exist_ok=True) + + # This is where it *would* go (files bail out before actually mounting) + self.mountPoint = imgmountdir + + if self.is_file(): + return + + if os.path.isdir(self.source): + r = libcalamares.utils.mount(self.source, imgmountdir, "", "--bind") + elif os.path.isfile(self.source): + r = libcalamares.utils.mount(self.source, imgmountdir, self.sourcefs, "loop") + else: # self.source is a device + r = libcalamares.utils.mount(self.source, imgmountdir, self.sourcefs, "") + + if r != 0: + libcalamares.utils.debug("Failed to mount '{}' (fs={}) (target={})".format(self.source, self.sourcefs, imgmountdir)) + raise subprocess.CalledProcessError(r, "mount") + + +ON_POSIX = 'posix' in sys.builtin_module_names + + +def global_excludes(): + """ + List excludes for rsync. + """ + lst = [] + extra_mounts = libcalamares.globalstorage.value("extraMounts") + if extra_mounts is None: + extra_mounts = [] + + for extra_mount in extra_mounts: + mount_point = extra_mount["mountPoint"] + + if mount_point: + lst.extend(['--exclude', mount_point + '/']) + + return lst + +def file_copy(source, entry, progress_cb): + """ + Extract given image using rsync. + + :param source: Source file. This may be the place the entry's + image is mounted, or if it's a single file, the entry's source value. + :param entry: The UnpackEntry being copied. + :param progress_cb: A callback function for progress reporting. + Takes a number and a total-number. + """ + import time + + dest = entry.destination + + # `source` *must* end with '/' otherwise a directory named after the source + # will be created in `dest`: ie if `source` is "/foo/bar" and `dest` is + # "/dest", then files will be copied in "/dest/bar". + if not source.endswith("/") and not os.path.isfile(source): + source += "/" + + num_files_total_local = 0 + num_files_copied = 0 # Gets updated through rsync output + + args = ['rsync', '-aHAXSr', '--filter=-x trusted.overlay.*'] + args.extend(global_excludes()) + if entry.excludeFile: + args.extend(["--exclude-from=" + entry.excludeFile]) + if entry.exclude: + for f in entry.exclude: + args.extend(["--exclude", f]) + args.extend(['--progress', source, dest]) + + # last_num_files_copied trails num_files_copied, and whenever at least 107 more + # files (file_count_chunk) have been copied, progress is reported and + # last_num_files_copied is updated. The chunk size isn't "tidy" + # so that all the digits of the progress-reported number change. + # + file_count_chunk = 107 + + class counter(object): + last_num_files_copied = 0 + last_timestamp_reported = time.time() + last_total_reported = 0 + + def output_cb(line): + # rsync outputs progress in parentheses. Each line will have an + # xfer and a chk item (either ir-chk or to-chk) as follows: + # + # - xfer#x => Interpret it as 'file copy try no. x' + # - ir-chk=x/y, where: + # - x = number of files yet to be checked + # - y = currently calculated total number of files. + # - to-chk=x/y, which is similar and happens once the ir-chk + # phase (collecting total files) is over. + # + # If you're copying directory with some links in it, the xfer# + # might not be a reliable counter (for one increase of xfer, many + # files may be created). + m = re.findall(r'xfr#(\d+), ..-chk=(\d+)/(\d+)', line) + + if m: + # we've got a percentage update + num_files_remaining = int(m[0][1]) + num_files_total_local = int(m[0][2]) + # adjusting the offset so that progressbar can be continuesly drawn + num_files_copied = num_files_total_local - num_files_remaining + + now = time.time() + if (num_files_copied - counter.last_num_files_copied >= file_count_chunk) or (now - counter.last_timestamp_reported > 0.5): + counter.last_num_files_copied = num_files_copied + counter.last_timestamp_reported = now + counter.last_total_reported = num_files_total_local + progress_cb(num_files_copied, num_files_total_local) + + try: + returncode = libcalamares.utils.host_env_process_output(args, output_cb) + except subprocess.CalledProcessError as e: + returncode = e.returncode + + progress_cb(counter.last_num_files_copied, counter.last_total_reported) # Push towards 100% + + # Mark this entry as really done + entry.copied = entry.total + + # 23 is the return code rsync returns if it cannot write extended + # attributes (with -X) because the target file system does not support it, + # e.g., the FAT EFI system partition. We need -X because distributions + # using file system capabilities and/or SELinux require the extended + # attributes. But distributions using SELinux may also have SELinux labels + # set on files under /boot/efi, and rsync complains about those. The only + # clean way would be to split the rsync into one with -X and + # --exclude /boot/efi and a separate one without -X for /boot/efi, but only + # if /boot/efi is actually an EFI system partition. For now, this hack will + # have to do. See also: + # https://bugzilla.redhat.com/show_bug.cgi?id=868755#c50 + # for the same issue in Anaconda, which uses a similar workaround. + if returncode != 0 and returncode != 23: + libcalamares.utils.warning("rsync failed with error code {}.".format(returncode)) + return _("rsync failed with error code {}.").format(returncode) + + return None + + +class UnpackOperation: + """ + Extraction routine using unsquashfs. + + :param entries: + """ + + def __init__(self, entries): + self.entries = entries + self.entry_for_source = dict((x.source, x) for x in self.entries) + self.total_weight = sum([e.weight for e in entries]) + + def report_progress(self): + """ + Pass progress to user interface + """ + progress = float(0) + + current_total = 0 + current_done = 0 # Files count in the current entry + complete_count = 0 + complete_weight = 0 # This much weight already finished + for entry in self.entries: + if entry.total == 0: + # Total 0 hasn't counted yet + continue + if entry.total == entry.copied: + complete_weight += entry.weight + complete_count += 1 + else: + # There is at most *one* entry in-progress + current_total = entry.total + current_done = entry.copied + complete_weight += entry.weight * ( 1.0 * current_done ) / current_total + break + + if current_total > 0: + progress = ( 1.0 * complete_weight ) / self.total_weight + + global status + status = _("Unpacking image {}/{}, file {}/{}").format((complete_count+1), len(self.entries), current_done, current_total) + libcalamares.job.setprogress(progress) + + def run(self): + """ + Extract given image using unsquashfs. + + :return: + """ + global status + source_mount_path = tempfile.mkdtemp() + + try: + complete = 0 + for entry in self.entries: + status = _("Starting to unpack {}").format(entry.source) + libcalamares.job.setprogress( ( 1.0 * complete ) / len(self.entries) ) + entry.do_mount(source_mount_path) + entry.do_count() # Fill in the entry.total + + self.report_progress() + error_msg = self.unpack_image(entry, entry.mountPoint) + + if error_msg: + return (_("Failed to unpack image \"{}\"").format(entry.source), + error_msg) + complete += 1 + + return None + finally: + shutil.rmtree(source_mount_path, ignore_errors=True, onerror=None) + + + def unpack_image(self, entry, imgmountdir): + """ + Unpacks image. + + :param entry: + :param imgmountdir: + :return: + """ + def progress_cb(copied, total): + """ Copies file to given destination target. + + :param copied: + """ + entry.copied = copied + if total > entry.total: + entry.total = total + self.report_progress() + + try: + if entry.is_file(): + source = entry.source + else: + source = imgmountdir + + return file_copy(source, entry, progress_cb) + finally: + if not entry.is_file(): + subprocess.check_call(["umount", "-l", imgmountdir]) + + +def get_supported_filesystems_kernel(): + """ + Reads /proc/filesystems (the list of supported filesystems + for the current kernel) and returns a list of (names of) + those filesystems. + """ + PATH_PROCFS = '/proc/filesystems' + + if os.path.isfile(PATH_PROCFS) and os.access(PATH_PROCFS, os.R_OK): + with open(PATH_PROCFS, 'r') as procfile: + filesystems = procfile.read() + filesystems = filesystems.replace( + "nodev", "").replace("\t", "").splitlines() + return filesystems + + return [] + + +def get_supported_filesystems(): + """ + Returns a list of all the supported filesystems + (valid values for the *sourcefs* key in an item. + """ + return ["file"] + get_supported_filesystems_kernel() + + +def repair_root_permissions(root_mount_point): + """ + If the / of the system gets permission 777, change it down + to 755. Any other permission is left alone. This + works around standard behavior from squashfs where + permissions are (easily, accidentally) set to 777. + """ + existing_root_mode = os.stat(root_mount_point).st_mode & 0o777 + if existing_root_mode == 0o777: + try: + os.chmod(root_mount_point, 0o755) # Want / to be rwxr-xr-x + except OSError as e: + libcalamares.utils.warning("Could not set / to safe permissions: {}".format(e)) + # But ignore it + + +def extract_weight(entry): + """ + Given @p entry, a dict representing a single entry in + the *unpack* list, returns its weight (1, or whatever is + set if it is sensible). + """ + w = entry.get("weight", None) + if w: + try: + wi = int(w) + return wi if wi > 0 else 1 + except ValueError: + libcalamares.utils.warning("*weight* setting {!r} is not valid.".format(w)) + except TypeError: + libcalamares.utils.warning("*weight* setting {!r} must be number.".format(w)) + return 1 + + +def fetch_from_globalstorage(keys_list): + value = libcalamares.globalstorage.value(keys_list[0]) + if value is None: + return None + for key in keys_list[1:]: + if isinstance(value, dict) and key in value: + value = value[key] + else: + return None + return value + + +def run(): + """ + Unsquash filesystem. + """ + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if not root_mount_point: + libcalamares.utils.warning("No mount point for root partition") + return (_("No mount point for root partition"), + _("globalstorage does not contain a \"rootMountPoint\" key.")) + if not os.path.exists(root_mount_point): + libcalamares.utils.warning("Bad root mount point \"{}\"".format(root_mount_point)) + return (_("Bad mount point for root partition"), + _("rootMountPoint is \"{}\", which does not exist.".format(root_mount_point))) + + if libcalamares.job.configuration.get("unpack", None) is None: + libcalamares.utils.warning("No *unpack* key in job configuration.") + return (_("Bad unpackfs configuration"), + _("There is no configuration information.")) + + supported_filesystems = get_supported_filesystems() + + # Bail out before we start when there are obvious problems + # - unsupported filesystems + # - non-existent sources + # - missing tools for specific FS + for entry in libcalamares.job.configuration["unpack"]: + source = os.path.abspath(entry["source"]) + sourcefs = entry["sourcefs"] + optional = entry.get("optional", False) + + if sourcefs not in supported_filesystems: + libcalamares.utils.warning("The filesystem for \"{}\" ({}) is not supported by your current kernel".format(source, sourcefs)) + libcalamares.utils.warning(" ... modprobe {} may solve the problem".format(sourcefs)) + return (_("Bad unpackfs configuration"), + _("The filesystem for \"{}\" ({}) is not supported by your current kernel").format(source, sourcefs)) + if not os.path.exists(source): + if optional: + libcalamares.utils.warning("The source filesystem \"{}\" does not exist but is marked as optional, skipping".format(source)) + entry["condition"] = False + continue + else: + libcalamares.utils.warning("The source filesystem \"{}\" does not exist".format(source)) + return (_("Bad unpackfs configuration"), + _("The source filesystem \"{}\" does not exist").format(source)) + if sourcefs == "squashfs": + if shutil.which("unsquashfs") is None: + libcalamares.utils.warning("Failed to find unsquashfs") + + return (_("Bad unpackfs configuration"), + _("Failed to find unsquashfs, make sure you have the squashfs-tools package installed.") + + " " + _("Failed to unpack image \"{}\"").format(source)) + + unpack = list() + + is_first = True + for entry in libcalamares.job.configuration["unpack"]: + source = os.path.abspath(entry["source"]) + sourcefs = entry["sourcefs"] + destination = os.path.abspath(root_mount_point + entry["destination"]) + + condition = entry.get("condition", True) + if isinstance(condition, bool): + pass # 'condition' is already True or False + elif isinstance(condition, str): + keys = condition.split(".") + gs_value = fetch_from_globalstorage(keys) + if gs_value is None: + libcalamares.utils.warning("Condition key '{}' not found in global storage, assuming False".format(condition)) + condition = False + elif isinstance(gs_value, bool): + condition = gs_value + else: + libcalamares.utils.warning("Condition key '{}' is not a boolean, assuming True".format(condition)) + condition = True + else: + libcalamares.utils.warning("Invalid 'condition' value '{}', assuming True".format(condition)) + condition = True + + if not condition: + libcalamares.utils.debug("Skipping unpack of {} due to 'condition' being False".format(source)) + continue + + if not os.path.isdir(destination) and sourcefs != "file": + libcalamares.utils.warning(("The destination \"{}\" in the target system is not a directory").format(destination)) + if is_first: + return (_("Bad unpackfs configuration"), + _("The destination \"{}\" in the target system is not a directory").format(destination)) + else: + libcalamares.utils.debug(".. assuming that the previous targets will create that directory.") + + unpack.append(UnpackEntry(source, sourcefs, destination)) + # Optional settings + if entry.get("exclude", None): + unpack[-1].exclude = entry["exclude"] + if entry.get("excludeFile", None): + unpack[-1].excludeFile = entry["excludeFile"] + unpack[-1].weight = extract_weight(entry) + + is_first = False + + repair_root_permissions(root_mount_point) + try: + unpackop = UnpackOperation(unpack) + return unpackop.run() + finally: + repair_root_permissions(root_mount_point) diff --git a/airootfs/usr/lib/calamares/modules/unpackfs/module.desc b/airootfs/usr/lib/calamares/modules/unpackfs/module.desc new file mode 100644 index 0000000..2723c3c --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/unpackfs/module.desc @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# Syntax is YAML 1.2 +--- +type: "job" +name: "unpackfs" +interface: "python" +script: "main.py" +requiredModules: [ mount ] +weight: 12 diff --git a/airootfs/usr/lib/calamares/modules/unpackfs/runtests.sh b/airootfs/usr/lib/calamares/modules/unpackfs/runtests.sh new file mode 100644 index 0000000..2269b07 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/unpackfs/runtests.sh @@ -0,0 +1,37 @@ +#! /bin/sh +# +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Test preparation for unpackfs; since there's a bunch +# of fiddly bits than need to be present for the tests, +# do that in a script rather than entirely in CTest. +# +SRCDIR=$( dirname "$0" ) + +# For test 3 +mkdir /tmp/unpackfs-test-run-rootdir3 + +# For test 7 +mkdir /tmp/unpackfs-test-run-rootdir3/realdest + +# For test 9 +mkdir /tmp/unpackfs-test-run-rootdir3/smalldest +if test 0 = $( id -u ) ; then + mount -t tmpfs -o size=32M tmpfs /tmp/unpackfs-test-run-rootdir3/smalldest + dd if=/dev/zero of=/tmp/unpackfs-test-run-rootdir3/smalldest/bogus.zero bs=1M count=1 +fi + +# Run tests +sh "$SRCDIR/../testpythonrun.sh" unpackfs + +# Cleanup test 9 +if test 0 = $( id -u ) ; then + umount /tmp/unpackfs-test-run-rootdir3/smalldest +fi + +# Cleanup test 7 +rm -rf /tmp/unpackfs-test-run-rootdir3/realdest + +# Cleanup test 3 +rmdir /tmp/unpackfs-test-run-rootdir3 diff --git a/airootfs/usr/lib/calamares/modules/unpackfs/unpackfs.schema.yaml b/airootfs/usr/lib/calamares/modules/unpackfs/unpackfs.schema.yaml new file mode 100644 index 0000000..9dc53c4 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/unpackfs/unpackfs.schema.yaml @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/unpackfs +additionalProperties: false +type: object +properties: + unpack: + type: array + items: + type: object + additionalProperties: false + properties: + source: { type: string } + sourcefs: { type: string } + destination: { type: string } + excludeFile: { type: string } + exclude: { type: array, items: { type: string } } + weight: { type: integer, exclusiveMinimum: 0 } + optional: { type: boolean } + condition: + anyOf: + - type: boolean + - type: string + required: [ source , sourcefs, destination ] diff --git a/airootfs/usr/lib/calamares/modules/unpackfsc/libcalamares_job_unpackfsc.so b/airootfs/usr/lib/calamares/modules/unpackfsc/libcalamares_job_unpackfsc.so new file mode 100755 index 0000000..55883aa Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/unpackfsc/libcalamares_job_unpackfsc.so differ diff --git a/airootfs/usr/lib/calamares/modules/unpackfsc/module.desc b/airootfs/usr/lib/calamares/modules/unpackfsc/module.desc new file mode 100644 index 0000000..16ed114 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/unpackfsc/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "unpackfsc" +interface: "qtplugin" +load: "libcalamares_job_unpackfsc.so" diff --git a/airootfs/usr/lib/calamares/modules/users/libcalamares_viewmodule_users.so b/airootfs/usr/lib/calamares/modules/users/libcalamares_viewmodule_users.so new file mode 100755 index 0000000..febff2c Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/users/libcalamares_viewmodule_users.so differ diff --git a/airootfs/usr/lib/calamares/modules/users/module.desc b/airootfs/usr/lib/calamares/modules/users/module.desc new file mode 100644 index 0000000..9a14225 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/users/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "users" +interface: "qtplugin" +load: "libcalamares_viewmodule_users.so" diff --git a/airootfs/usr/lib/calamares/modules/usersq/libcalamares_viewmodule_usersq.so b/airootfs/usr/lib/calamares/modules/usersq/libcalamares_viewmodule_usersq.so new file mode 100755 index 0000000..f0cf848 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/usersq/libcalamares_viewmodule_usersq.so differ diff --git a/airootfs/usr/lib/calamares/modules/usersq/module.desc b/airootfs/usr/lib/calamares/modules/usersq/module.desc new file mode 100644 index 0000000..0c1458e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/usersq/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "usersq" +interface: "qtplugin" +load: "libcalamares_viewmodule_usersq.so" diff --git a/airootfs/usr/lib/calamares/modules/welcome/libcalamares_viewmodule_welcome.so b/airootfs/usr/lib/calamares/modules/welcome/libcalamares_viewmodule_welcome.so new file mode 100755 index 0000000..8377275 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/welcome/libcalamares_viewmodule_welcome.so differ diff --git a/airootfs/usr/lib/calamares/modules/welcome/module.desc b/airootfs/usr/lib/calamares/modules/welcome/module.desc new file mode 100644 index 0000000..4df257b --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/welcome/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "welcome" +interface: "qtplugin" +load: "libcalamares_viewmodule_welcome.so" diff --git a/airootfs/usr/lib/calamares/modules/welcomeq/libcalamares_viewmodule_welcomeq.so b/airootfs/usr/lib/calamares/modules/welcomeq/libcalamares_viewmodule_welcomeq.so new file mode 100755 index 0000000..8d15f9b Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/welcomeq/libcalamares_viewmodule_welcomeq.so differ diff --git a/airootfs/usr/lib/calamares/modules/welcomeq/module.desc b/airootfs/usr/lib/calamares/modules/welcomeq/module.desc new file mode 100644 index 0000000..3c3e82e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/welcomeq/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "viewmodule" +name: "welcomeq" +interface: "qtplugin" +load: "libcalamares_viewmodule_welcomeq.so" diff --git a/airootfs/usr/lib/calamares/modules/zfs/libcalamares_job_zfs.so b/airootfs/usr/lib/calamares/modules/zfs/libcalamares_job_zfs.so new file mode 100755 index 0000000..2d79607 Binary files /dev/null and b/airootfs/usr/lib/calamares/modules/zfs/libcalamares_job_zfs.so differ diff --git a/airootfs/usr/lib/calamares/modules/zfs/module.desc b/airootfs/usr/lib/calamares/modules/zfs/module.desc new file mode 100644 index 0000000..d44b55f --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/zfs/module.desc @@ -0,0 +1,7 @@ +# AUTO-GENERATED metadata file +# Syntax is YAML 1.2 +--- +type: "job" +name: "zfs" +interface: "qtplugin" +load: "libcalamares_job_zfs.so" diff --git a/airootfs/usr/lib/calamares/modules/zfshostid/main.py b/airootfs/usr/lib/calamares/modules/zfshostid/main.py new file mode 100644 index 0000000..702ba1e --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/zfshostid/main.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2022 Anke Boersma +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Calamares is Free Software: see the License-Identifier above. +# + +import os +import shutil + +import libcalamares +from libcalamares.utils import gettext_path, gettext_languages + + +import gettext +_ = gettext.translation("calamares-python", + localedir=libcalamares.utils.gettext_path(), + languages=libcalamares.utils.gettext_languages(), + fallback=True).gettext + + +def pretty_name(): + return _("Copying zfs generated hostid.") + + +def run(): + + zfs = libcalamares.globalstorage.value("zfsDatasets") + root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if zfs: + hostid_source = '/etc/hostid' + hostid_destination = '{!s}/etc/hostid'.format(root_mount_point) + + try: + shutil.copy2(hostid_source, hostid_destination) + except Exception as e: + libcalamares.utils.warning("Could not copy hostid") + + return None diff --git a/airootfs/usr/lib/calamares/modules/zfshostid/module.desc b/airootfs/usr/lib/calamares/modules/zfshostid/module.desc new file mode 100644 index 0000000..13fec35 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/zfshostid/module.desc @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +--- +type: "job" +name: "zfshostid" +interface: "python" +script: "main.py" +noconfig: true diff --git a/airootfs/usr/lib/calamares/modules/zfshostid/zfshostid.schema.yaml b/airootfs/usr/lib/calamares/modules/zfshostid/zfshostid.schema.yaml new file mode 100644 index 0000000..24774d0 --- /dev/null +++ b/airootfs/usr/lib/calamares/modules/zfshostid/zfshostid.schema.yaml @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2022 Anke Boersma +# SPDX-License-Identifier: GPL-3.0-or-later +--- +$schema: https://json-schema.org/schema# +$id: https://calamares.io/schemas/zfshostid +additionalProperties: false +type: object diff --git a/airootfs/usr/lib/cmake/Calamares/CMakeColors.cmake b/airootfs/usr/lib/cmake/Calamares/CMakeColors.cmake new file mode 100644 index 0000000..073c24d --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CMakeColors.cmake @@ -0,0 +1,43 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2014 Kevin Kofler +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +### +# +# Defines a handful of strings that, with normal xterm handling, +# will change colors in the output, so it's nicer to read. +if(NOT WIN32) + set(_use_color ON) + if("0" STREQUAL "$ENV{CLICOLOR}") + set(_use_color OFF) + endif() + if("0" STREQUAL "$ENV{CLICOLOR_FORCE}") + set(_use_color OFF) + endif() + if(NOT CMAKE_COLOR_MAKEFILE) + set(_use_color OFF) + endif() + + if(_use_color) + string(ASCII 27 Esc) + set(ColorReset "${Esc}[m") + set(ColorBold "${Esc}[1m") + set(Red "${Esc}[31m") + set(Green "${Esc}[32m") + set(Yellow "${Esc}[33m") + set(Blue "${Esc}[34m") + set(Magenta "${Esc}[35m") + set(Cyan "${Esc}[36m") + set(White "${Esc}[37m") + set(BoldRed "${Esc}[1;31m") + set(BoldGreen "${Esc}[1;32m") + set(BoldYellow "${Esc}[1;33m") + set(BoldBlue "${Esc}[1;34m") + set(BoldMagenta "${Esc}[1;35m") + set(BoldCyan "${Esc}[1;36m") + set(BoldWhite "${Esc}[1;37m") + endif() +endif() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAddBrandingSubdirectory.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAddBrandingSubdirectory.cmake new file mode 100644 index 0000000..7ae7da2 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAddBrandingSubdirectory.cmake @@ -0,0 +1,147 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# Support macros for creating Calamares branding components. +# +# Calamares branding components have two parts: +# - a branding.desc file that tells Calamares how to describe the product +# (e.g. strings like "Generic GNU/Linux") and the name of a QML file +# (the "slideshow") that is displayed during installation. +# - the QML files themselves, plus supporting images etc. +# +# Branding components can be created inside the Calamares source tree +# (there is one example the `default/` branding, which is also connected +# to the default configuration shipped with Calamares), but they can be +# built outside of, and largely independently of, Calamares by using +# these CMake macros. +# +# See the calamares-examples repository for more examples. +# +include( CMakeParseArguments) + +include( CMakeColors ) + +# Usage calamares_add_branding( [DIRECTORY ] [SUBDIRECTORIES ...]) +# +# Adds a branding component to the build: +# - the component's top-level files are copied into the build-dir; +# CMakeLists.txt is excluded from the glob. +# - the component's top-level files are installed into the component branding dir +# +# The branding component lives in if given, otherwise the +# current source directory. The branding component is installed +# with the given , which is usually the name of the +# directory containing the component, and which must match the +# *componentName* in `branding.desc`. +# +# If SUBDIRECTORIES are given, then those are copied (each one level deep) +# to the installation location as well, preserving the subdirectory name. +function( calamares_add_branding NAME ) + cmake_parse_arguments( _CABT "" "DIRECTORY" "SUBDIRECTORIES" ${ARGN} ) + if (NOT _CABT_DIRECTORY) + set(_CABT_DIRECTORY ".") + endif() + + set( SUBDIRECTORY ${_CABT_DIRECTORY} ) + set( _brand_dir ${_CABT_DIRECTORY} ) + + set( BRANDING_DIR share/calamares/branding ) + set( BRANDING_COMPONENT_DESTINATION ${BRANDING_DIR}/${NAME} ) + + foreach( _subdir "" ${_CABT_SUBDIRECTORIES} ) + file( GLOB BRANDING_COMPONENT_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/${_brand_dir} "${_brand_dir}/${_subdir}/*" ) + foreach( BRANDING_COMPONENT_FILE ${BRANDING_COMPONENT_FILES} ) + set( _subpath ${_brand_dir}/${BRANDING_COMPONENT_FILE} ) + if( NOT IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${_subpath} ) + set( _src ${CMAKE_CURRENT_SOURCE_DIR}/${_subpath} ) + set( _dst ${CMAKE_CURRENT_BINARY_DIR}/${_subpath} ) + if( ${_src} IS_NEWER_THAN ${_dst} ) + configure_file( ${_src} ${_dst} COPYONLY ) + endif() + + install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${_subpath} + DESTINATION ${BRANDING_COMPONENT_DESTINATION}/${_subdir}/ ) + endif() + endforeach() + endforeach() + + message( "-- ${BoldYellow}Found ${CALAMARES_APPLICATION_NAME} branding component: ${BoldRed}${NAME}${ColorReset}" ) + message( " ${Green}TYPE:${ColorReset} branding component" ) + message( " ${Green}BRANDING_COMPONENT_DESTINATION:${ColorReset} ${BRANDING_COMPONENT_DESTINATION}" ) +endfunction() + +# Usage calamares_add_branding_translations( [DIRECTORY ]) +# +# Adds the translations for a branding component to the build: +# - the component's lang/ directory is scanned for .ts files +# - the component's translations are installed into the component branding dir +# +# Translation files must be called calamares-_.ts . Optionally +# the lang/ dir is found in the given instead of the current source +# directory. +function( calamares_add_branding_translations NAME ) + cmake_parse_arguments( _CABT "" "DIRECTORY" "" ${ARGN} ) + if (NOT _CABT_DIRECTORY) + set(_CABT_DIRECTORY ".") + endif() + + set( SUBDIRECTORY ${_CABT_DIRECTORY} ) + set( _brand_dir ${_CABT_DIRECTORY} ) + + set( BRANDING_DIR share/calamares/branding ) + set( BRANDING_COMPONENT_DESTINATION ${BRANDING_DIR}/${NAME} ) + + file( GLOB BRANDING_TRANSLATION_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${SUBDIRECTORY}/lang/calamares-${NAME}_*.ts" ) + if ( BRANDING_TRANSLATION_FILES ) + qt_add_translation( QM_FILES ${BRANDING_TRANSLATION_FILES} ) + add_custom_target( branding-translation-${NAME} ALL DEPENDS ${QM_FILES} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/lang/ + COMMAND ${CMAKE_COMMAND} -E copy ${QM_FILES} ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/lang/ + ) + install( FILES ${QM_FILES} DESTINATION ${BRANDING_COMPONENT_DESTINATION}/lang/ ) + list( LENGTH BRANDING_TRANSLATION_FILES _branding_count ) + message( " ${Green}BRANDING_TRANSLATIONS:${ColorReset} ${_branding_count} language(s)" ) + endif() +endfunction() + +# Usage calamares_add_branding_subdirectory( [NAME ] [SUBDIRECTORIES ...]) +# +# Adds a branding component from a subdirectory: +# - if there is a CMakeLists.txt, use that (that CMakeLists.txt should +# call suitable calamares_add_branding() and other macros to install +# the branding component). +# - otherwise assume a "standard" setup with top-level files and a lang/ +# subdirectory for translations. +# +# If NAME is given, this is used instead of as the name of +# the branding component. This is needed if is more than +# one level deep, or to rename a component as it gets installed. +# +# If SUBDIRECTORIES are given, they are relative to , and are +# copied (one level deep) to the install location as well. +function( calamares_add_branding_subdirectory SUBDIRECTORY ) + cmake_parse_arguments( _CABS "" "NAME" "SUBDIRECTORIES" ${ARGN} ) + if (NOT _CABS_NAME) + set(_CABS_NAME "${SUBDIRECTORY}") + endif() + + if( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}/CMakeLists.txt" ) + add_subdirectory( ${SUBDIRECTORY} ) + elseif( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}/branding.desc" ) + calamares_add_branding( ${_CABS_NAME} DIRECTORY ${SUBDIRECTORY} SUBDIRECTORIES ${_CABS_SUBDIRECTORIES} ) + if( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}/lang" ) + calamares_add_branding_translations( ${_CABS_NAME} DIRECTORY ${SUBDIRECTORY} ) + endif() + else() + message( "-- ${BoldYellow}Warning:${ColorReset} tried to add branding component subdirectory ${BoldRed}${SUBDIRECTORY}${ColorReset} which has no branding.desc." ) + endif() + message( "" ) +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAddLibrary.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAddLibrary.cmake new file mode 100644 index 0000000..c7cc0f3 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAddLibrary.cmake @@ -0,0 +1,139 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# Support functions for building plugins. +# +# Usage: +# +# calamares_add_library( +# library-name +# EXPORT_MACRO macro-name +# TARGET_TYPE +# EXPORT export-name +# VERSION version +# SOVERSION version +# INSTALL_BINDIR dir +# RESOURCES resource-file +# SOURCES source-file... +# UI ui-file... +# LINK_LIBRARIES lib... +# LINK_PRIVATE_LIBRARIES lib... +# COMPILE_DEFINITIONS def... +# [NO_INSTALL] +# [NO_VERSION] +# ) +# +# The COMPILE_DEFINITIONS are set on the resulting module with a suitable +# flag (i.e. `-D`) so only state the name (optionally, also the value) +# without a `-D` prefixed to it. Pass in a CMake list as needed. +include( CMakeParseArguments ) +include( CalamaresAutomoc ) + +function(calamares_add_library) + # parse arguments (name needs to be saved before passing ARGN into the macro) + set(NAME ${ARGV0}) + set(options NO_INSTALL NO_VERSION) + set(oneValueArgs NAME EXPORT_MACRO TARGET_TYPE EXPORT VERSION SOVERSION INSTALL_BINDIR RESOURCES) + set(multiValueArgs SOURCES UI LINK_LIBRARIES LINK_PRIVATE_LIBRARIES COMPILE_DEFINITIONS) + cmake_parse_arguments(LIBRARY "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(LIBRARY_NAME ${NAME}) + + set(target ${LIBRARY_NAME}) + + # qt stuff + include_directories(${CMAKE_CURRENT_LIST_DIR}) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + + # add resources from current dir + if(LIBRARY_RESOURCES) + list(APPEND LIBRARY_SOURCES ${LIBRARY_RESOURCES}) + endif() + + # add target + if(LIBRARY_TARGET_TYPE STREQUAL "STATIC") + add_library(${target} STATIC ${LIBRARY_SOURCES}) + elseif(LIBRARY_TARGET_TYPE STREQUAL "MODULE") + add_library(${target} MODULE ${LIBRARY_SOURCES}) + elseif(LIBRARY_TARGET_TYPE STREQUAL "SHARED") + add_library(${target} SHARED ${LIBRARY_SOURCES}) + else() # default + message(FATAL_ERROR "Invalid library type '${LIBRARY_TARGET_TYPE}'") + endif() + + calamares_automoc(${target}) + if(LIBRARY_UI) + calamares_autouic(${target} ${LIBRARY_UI}) + endif() + if(LIBRARY_RESOURCES) + calamares_autorcc(${target} ${LIBRARY_RESOURCES}) + endif() + + if(LIBRARY_EXPORT_MACRO) + set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS ${LIBRARY_EXPORT_MACRO}) + endif() + + if(LIBRARY_COMPILE_DEFINITIONS) + set( _lib_definitions "${LIBRARY_EXPORT_MACRO}" ${LIBRARY_COMPILE_DEFINITIONS} ) + set_target_properties(${target} PROPERTIES COMPILE_DEFINITIONS "${_lib_definitions}") + endif() + + # add link targets + target_link_libraries(${target} + LINK_PUBLIC ${Calamares_LIBRARIES} + ${qtname}::Core + ${qtname}::Gui + ${qtname}::Widgets + ) + if(LIBRARY_LINK_LIBRARIES) + target_link_libraries(${target} LINK_PUBLIC ${LIBRARY_LINK_LIBRARIES}) + endif() + if(LIBRARY_LINK_PRIVATE_LIBRARIES) + target_link_libraries(${target} LINK_PRIVATE ${LIBRARY_LINK_PRIVATE_LIBRARIES}) + endif() + + # add soversion + if(NOT LIBRARY_NO_VERSION) + set_target_properties(${target} PROPERTIES VERSION ${LIBRARY_VERSION}) + + if(NOT LIBRARY_SOVERSION) + set(LIBRARY_SOVERSION ${LIBRARY_VERSION}) + endif() + + set_target_properties(${target} PROPERTIES SOVERSION ${LIBRARY_SOVERSION}) + endif() + + + if(NOT LIBRARY_INSTALL_BINDIR) + set(LIBRARY_INSTALL_BINDIR "${CMAKE_INSTALL_BINDIR}") + set(LIBRARY_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}") + else() + set(LIBRARY_INSTALL_LIBDIR "${LIBRARY_INSTALL_BINDIR}") + endif() + + # make installation optional, maybe useful for dummy plugins one day + if(NOT LIBRARY_NO_INSTALL) + include(GNUInstallDirs) + if(NOT LIBRARY_EXPORT) + install( TARGETS ${target} + RUNTIME DESTINATION ${LIBRARY_INSTALL_BINDIR} + LIBRARY DESTINATION ${LIBRARY_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${LIBRARY_INSTALL_LIBDIR} + ) + else() + install( TARGETS ${target} + EXPORT ${LIBRARY_EXPORT} + RUNTIME DESTINATION ${LIBRARY_INSTALL_BINDIR} + LIBRARY DESTINATION ${LIBRARY_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${LIBRARY_INSTALL_LIBDIR} + ) + endif() + endif() +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAddModuleSubdirectory.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAddModuleSubdirectory.cmake new file mode 100644 index 0000000..964dac0 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAddModuleSubdirectory.cmake @@ -0,0 +1,287 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# Function and support code for adding a Calamares module (either a Qt / C++ plugin, +# or a Python module, or whatever) to the build. +# +# # Usage +# +# The public API is one single function: +# +# - calamares_add_module_subdirectory(subdirectory [skiplistvar]) +# Adds a given *subdirectory* to the modules list, building the +# module that is there. The *subdirectory* must contain a `module.desc` +# (generally non-C++ modules) or a `CMakeLists.txt` (for C++ modules, +# or special cases). The module is assumed to be named after the +# (last component of) the subdirectory. +# +# If the module would be skipped (by the global SKIP_MODULES setting +# or a USE_* setting) or the module itself sets a reason to skip +# via the calamares_skip_module() function, the module is added to +# the list of skipped-modules in *skiplistvar*. If no variable is +# given, the reason is set in the parent scope variable +# SKIPPED_MODULES . Do **not** use SKIPPED_MODULES as the name of +# *skiplistvar*, things will get weird. +# +# Do note that the name of a module must be the same as the name of +# the directory containing it (as documented in src/modules/README.md). +# This applies to both C++ and Python modules, and allows the use of +# the subdirectory as a proxy for the module name inside. +# + +include( CalamaresAddTranslations ) +include( CalamaresCheckModuleSelection ) + +set( MODULE_DATA_DESTINATION share/calamares/modules ) + +# We look for Pylint (just once) so that unittests can be added that +# check the syntax / variables of Python modules. This should help +# avoid more typo's-in-releases. +if(BUILD_TESTING AND NOT PYLINT_COMMAND_SEARCHED) + set(PYLINT_COMMAND_SEARCHED TRUE) + find_program( + PYLINT_COMMAND + NAMES pylint3 pylint + PATHS $ENV{HOME}/.local/bin + ) +endif() + +function( _calamares_add_module_subdirectory_impl ) + set( SUBDIRECTORY ${ARGV0} ) + + # Set SKIPPED_MODULES here, so CMake-based modules have a + # parent scope to set it in; this function, in turn sets it + # in **its** parent scope. + set( SKIPPED_MODULES "" ) + set( MODULE_CONFIG_FILES "" ) + + # The module subdirectory may be given as a/b/c, but the module + # needs to be installed as "c", so we split off any intermediate + # directories. + # + # Compute _modulename (the last directory name) and _mod_dir + # (the full path to the module sources). + get_filename_component(_dirname "${SUBDIRECTORY}" DIRECTORY) + if( _dirname ) + # Remove the dirname and any leftover leading /s + string( REGEX REPLACE "^${_dirname}/*" "" _modulename "${SUBDIRECTORY}" ) + else() + set( _modulename ${SUBDIRECTORY} ) + endif() + # Strip any remaining / + string( REGEX REPLACE "/" "" _modulename "${_modulename}" ) + set( _mod_dir "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}" ) + + # Skip list check applies to all kinds of modules + calamares_check_skip( ${_modulename} SKIPPED_MODULES ) + if ( SKIPPED_MODULES ) + # If it's skipped by infrastucture, the message already includes the module + # name. We don't need to do any further checking. + set( SKIPPED_MODULES "${SKIPPED_MODULES}" PARENT_SCOPE ) + return() + endif() + + # If this subdirectory has a CMakeLists.txt, we add_subdirectory it... + if( EXISTS "${_mod_dir}/CMakeLists.txt" ) + add_subdirectory( ${SUBDIRECTORY} ) + file( GLOB MODULE_CONFIG_FILES RELATIVE ${_mod_dir} "${SUBDIRECTORY}/*.conf" ) + # Module has indicated it should be skipped, show that in + # the calling CMakeLists (which is src/modules/CMakeLists.txt normally). + if ( SKIPPED_MODULES ) + set( SKIPPED_MODULES ${SKIPPED_MODULES} PARENT_SCOPE ) + set( MODULE_CONFIG_FILES "" ) + else() + # The SKIPPED_MODULES may be set in the directory itself + get_directory_property( _skip DIRECTORY ${SUBDIRECTORY} DEFINITION SKIPPED_MODULES ) + if ( _skip ) + set( SKIPPED_MODULES ${_skip} PARENT_SCOPE ) + set( MODULE_CONFIG_FILES "" ) + endif() + endif() + if ( SKIPPED_MODULES ) + return() + endif() + # ...otherwise, we look for a module.desc. + elseif( EXISTS "${_mod_dir}/module.desc" ) + set( MODULES_DIR ${CMAKE_INSTALL_LIBDIR}/calamares/modules ) + set( MODULE_DESTINATION ${MODULES_DIR}/${_modulename} ) + + # Read module.desc, check that the interface type is supported. + # + # _mod_enabled boolean if the module should be built (only if the interface is supported) + # _mod_reason is a human-readable explanation why it isn't built + # _mod_testing boolean if the module should be added to the loadmodule tests + file(STRINGS "${_mod_dir}/module.desc" MODULE_INTERFACE REGEX "^interface") + if ( MODULE_INTERFACE MATCHES "pythonqt" ) + message( FATAL_ERROR "PythonQt is no longer supported" ) + set( _mod_enabled OFF ) + set( _mod_reason "No PythonQt support" ) + set( _mod_testing OFF ) + elseif ( MODULE_INTERFACE MATCHES "python" ) + set( _mod_enabled ${Calamares_WITH_PYTHON} ) + set( _mod_reason "No Python support" ) + set( _mod_testing ON ) # Will check syntax and imports, at least + elseif ( MODULE_INTERFACE MATCHES "qtplugin" ) + set( _mod_enabled OFF ) + set( _mod_reason "C++ modules must have a CMakeLists.txt instead" ) + set( _mod_testing OFF ) + elseif ( MODULE_INTERFACE MATCHES "process" ) + set( _mod_enabled ON ) + set( _mod_reason "" ) + set( _mod_testing OFF ) + else() + set( _mod_enabled OFF ) + set( _mod_reason "Unknown module interface '${MODULE_INTERFACE}'" ) + set( _mod_testing OFF ) + endif() + + if ( _mod_enabled ) + # We glob all the files inside the subdirectory, and we make sure they are + # synced with the bindir structure and installed. + file( GLOB MODULE_FILES RELATIVE ${_mod_dir} "${SUBDIRECTORY}/*" ) + foreach( MODULE_FILE ${MODULE_FILES} ) + if( NOT IS_DIRECTORY ${_mod_dir}/${MODULE_FILE} ) + configure_file( ${SUBDIRECTORY}/${MODULE_FILE} ${SUBDIRECTORY}/${MODULE_FILE} COPYONLY ) + + get_filename_component( FLEXT ${MODULE_FILE} EXT ) + if( "${FLEXT}" STREQUAL ".conf" ) + message(STATUS "Config ${MODULE_FILE}") + list( APPEND MODULE_CONFIG_FILES ${MODULE_FILE} ) + else() + message(STATUS "Non-Config ${MODULE_FILE}") + install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE} + DESTINATION ${MODULE_DESTINATION} ) + endif() + endif() + endforeach() + + message( "-- ${BoldYellow}Found ${CALAMARES_APPLICATION_NAME} module: ${BoldRed}${_modulename}${ColorReset}" ) + message( " ${Green}TYPE:${ColorReset} jobmodule" ) + message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" ) + if( MODULE_CONFIG_FILES ) + if (INSTALL_CONFIG) + message(" ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory and ${MODULE_DATA_DESTINATION}]") + foreach(_cf ${MODULE_CONFIG_FILES}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${_cf} DESTINATION ${MODULE_DATA_DESTINATION}) + endforeach() + else() + message(" ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory only]") + endif() + endif() + message( "" ) + # We copy over the lang directory, if any + if( IS_DIRECTORY "${_mod_dir}/lang" ) + install_calamares_gettext_translations( + ${SUBDIRECTORY} + SOURCE_DIR "${_mod_dir}/lang" + FILENAME ${SUBDIRECTORY}.mo + RENAME calamares-${SUBDIRECTORY}.mo + ) + endif() + else() + # Module disabled due to missing dependencies / unsupported interface + set( SKIPPED_MODULES "${SUBDIRECTORY} (${_mod_reason})" PARENT_SCOPE ) + endif() + else() + message( "-- ${BoldYellow}Warning:${ColorReset} tried to add module subdirectory ${BoldRed}${SUBDIRECTORY}${ColorReset} which has no CMakeLists.txt or module.desc." ) + message( "" ) + endif() + + # Check any config files for basic correctness + if ( BUILD_TESTING AND MODULE_CONFIG_FILES ) + set( _count 0 ) + foreach( _config_file ${MODULE_CONFIG_FILES} ) + set( _count_str "-${_count}" ) + if ( _count EQUAL 0 ) + set( _count_str "" ) + endif() + add_test( + NAME config-${SUBDIRECTORY}${_count_str} + COMMAND test_conf ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${_config_file} ) + math( EXPR _count "${_count} + 1" ) + endforeach() + endif() + + # Adding general tests + # + # Add a check that the module can be loaded. Since this calls exec(), the module + # may try to do things to the running system. Needs work to make that a + # safe thing to do. + # + # If the module has a tests/ subdirectory with *.global and *.job + # files (YAML files holding global and job-configurations for + # testing purposes) then those files are used to drive additional + # tests. The files must be numbered (starting from 1) for this to work; + # 1.global and 1.job together make the configuration for test 1. + # + # If the module has a tests/CMakeLists.txt while it doesn't have its + # own CMakeLists.txt (e.g. a Python module), then the subdirectory + # for tests/ is added on its own. + # + if ( BUILD_TESTING AND _mod_enabled AND _mod_testing ) + add_test( + NAME load-${SUBDIRECTORY} + COMMAND loadmodule ${SUBDIRECTORY} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + # Try it with the tests/ configurations shipped with the module + set( _count 1 ) + set( _testdir ${_mod_dir}/tests ) + while ( EXISTS "${_testdir}/${_count}.global" OR EXISTS "${_testdir}/${_count}.job" ) + set( _dash_g "" ) + set( _dash_j "" ) + if ( EXISTS "${_testdir}/${_count}.global" ) + set( _dash_g -g ${_testdir}/${_count}.global ) + endif() + if ( EXISTS "${_testdir}/${_count}.job" ) + set( _dash_j -j ${_testdir}/${_count}.job ) + endif() + add_test( + NAME load-${SUBDIRECTORY}-${_count} + COMMAND loadmodule ${_dash_g} ${_dash_j} ${SUBDIRECTORY} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + math( EXPR _count "${_count} + 1" ) + endwhile() + if ( EXISTS ${_testdir}/CMakeTests.txt AND NOT EXISTS ${_mod_dir}/CMakeLists.txt ) + include( ${_testdir}/CMakeTests.txt ) + endif() + if ( PYLINT_COMMAND AND MODULE_INTERFACE MATCHES "python" ) + # Python modules get an additional test via pylint; this + # needs to run at top-level because the ci/libcalamares directory + # contains API stubs. + # + # TODO: the entry point is assumed to be `main.py`, but that is + # configurable through module.desc + add_test( + NAME lint-${SUBDIRECTORY} + COMMAND env PYTHONPATH=ci: ${PYLINT_COMMAND} -E ${_mod_dir}/main.py + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + endif() + endif() +endfunction() + +function( calamares_add_module_subdirectory ) + set( SUBDIRECTORY ${ARGV0} ) + set( _ams_SKIP_LIST ${ARGV1} ) + + set( SKIPPED_MODULES "" ) + _calamares_add_module_subdirectory_impl( ${SUBDIRECTORY} ) + if ( SKIPPED_MODULES ) + if ( _ams_SKIP_LIST ) + list( APPEND ${_ams_SKIP_LIST} "${SKIPPED_MODULES}" ) + set( ${_ams_SKIP_LIST} "${${_ams_SKIP_LIST}}" PARENT_SCOPE ) + else() + set( SKIPPED_MODULES "${SKIPPED_MODULES}" PARENT_SCOPE ) + endif() + endif() +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAddPlugin.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAddPlugin.cmake new file mode 100644 index 0000000..5143b96 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAddPlugin.cmake @@ -0,0 +1,233 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# Convenience function for creating a C++ (qtplugin) module for Calamares. +# This function provides cmake-time feedback about the plugin, adds +# targets for compilation and boilerplate information, and creates +# a module.desc with standard values (if the module.desc file exists, +# that one is used instead, which happens only for unusual plugins). +# +# Usage: +# +# calamares_add_plugin( +# module-name +# TYPE +# EXPORT_MACRO macro-name +# SOURCES source-file... +# UI ui-file... +# LINK_LIBRARIES lib... +# LINK_PRIVATE_LIBRARIES lib... +# [COMPILE_DEFINITIONS def...] +# [RESOURCES resource-file] +# [REQUIRES module-name...] +# [NO_INSTALL] +# [NO_CONFIG] +# [SHARED_LIB] +# [EMERGENCY] +# [WEIGHT w] +# ) +# +# Function optional parameters: +# - COMPILE_DEFINITIONS +# Definitions are set on the resulting module with a suitable +# flag (i.e. `-D`) so only state the name (optionally, also the value) +# without a `-D` prefixed to it. +# - RESOURCES +# One (single!) filename for the RCC file for the plugin. +# - REQUIRES +# One or more names of modules which are added to the *requiredModules* +# key in the descriptor. See *Module Requirements* in the module +# documentation. +# - NO_INSTALL +# If this is set, the module is not installed by default; use this to +# build testing modules or unit-testing modules. +# - SHARED_LIB +# In unusual circumstances, this function is used to add a library +# rather than a normal Calamares module / plugin. +# - EMERGENCY +# If this is set, the module is marked as an *emergency* module in the +# descriptor. See *Emergency Modules* in the module documentation. +# - WEIGHT +# If this is set, writes an explicit weight into the module.desc; +# module weights are used in progress reporting. +# +# +# This function follows the global SKIP_MODULES and USE_* settings, so +# a plugin may be skipped -- then nothing will be built. In that case, +# SKIPPED_MODULES is set in the parent (i.e. caller's) scope with the +# reason why. This should rarely be a concern as AddModuleSubdirectory +# already handles skip-reasons and collects them for reporting. +# +# The target defined this way is called "calamares__", +# e.g. "calamares_viewmodule_packagechooserq". The function sets a variable +# in its **calling** scope, `_TARGET` with the full name +# of the target. + +include( CMakeParseArguments ) + +include( CalamaresAddLibrary ) +include( CalamaresCheckModuleSelection ) +include( CMakeColors ) + +function( calamares_add_plugin ) + # parse arguments ( name needs to be saved before passing ARGN into the macro ) + set( NAME ${ARGV0} ) + set( options NO_CONFIG NO_INSTALL SHARED_LIB EMERGENCY ) + set( oneValueArgs NAME TYPE EXPORT_MACRO RESOURCES WEIGHT ) + set( multiValueArgs SOURCES UI LINK_LIBRARIES LINK_PRIVATE_LIBRARIES COMPILE_DEFINITIONS REQUIRES ) + cmake_parse_arguments( PLUGIN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + set( PLUGIN_NAME ${NAME} ) + set( PLUGIN_DESTINATION ${CMAKE_INSTALL_LIBDIR}/calamares/modules/${PLUGIN_NAME} ) + set( PLUGIN_DESC_FILE module.desc ) + file( GLOB PLUGIN_CONFIG_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.conf" ) + set( PLUGIN_DATA_DESTINATION share/calamares/modules ) + set( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) + set( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) + set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" ) + + calamares_check_skip( ${NAME} _skip) + if ( _skip ) + set( SKIPPED_MODULES "${_skip}" PARENT_SCOPE ) + return() + endif() + + message( "-- ${BoldYellow}Found ${CALAMARES_APPLICATION_NAME} module: ${BoldRed}${PLUGIN_NAME}${ColorReset}" ) + message( " ${Green}TYPE:${ColorReset} ${PLUGIN_TYPE}" ) + message( " ${Green}LINK_LIBRARIES:${ColorReset} ${PLUGIN_LINK_LIBRARIES}" ) + message( " ${Green}LINK_PRIVATE_LIBRARIES:${ColorReset} ${PLUGIN_LINK_PRIVATE_LIBRARIES}" ) + message( " ${Green}PLUGIN_DESTINATION:${ColorReset} ${PLUGIN_DESTINATION}" ) + if( PLUGIN_CONFIG_FILES ) + if( PLUGIN_NO_CONFIG ) + message( FATAL_ERROR "${Red}NO_CONFIG${ColorReset} is set, with configuration ${Red}${PLUGIN_CONFIG_FILES}${ColorReset}" ) + endif() + set( _destination "(unknown)" ) + if(INSTALL_CONFIG AND NOT PLUGIN_NO_INSTALL) + set(_destination "${PLUGIN_DATA_DESTINATION}") + elseif( NOT PLUGIN_NO_INSTALL ) + set( _destination "[Build directory only]" ) + else() + set( _destination "[Skipping installation]" ) + endif() + message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${PLUGIN_CONFIG_FILES} => ${_destination}" ) + else() + if( NOT PLUGIN_NO_CONFIG ) + message( " ${Red}NO_CONFIG${ColorReset} should be set." ) + endif() + endif() + if( PLUGIN_RESOURCES ) + message( " ${Green}RESOURCES:${ColorReset} ${PLUGIN_RESOURCES}" ) + endif() + message( "" ) + + # create target name once for convenience + set( target "calamares_${PLUGIN_TYPE}_${PLUGIN_NAME}" ) + + # automatic library linkage + if(PLUGIN_TYPE STREQUAL "viewmodule") + list(APPEND PLUGIN_LINK_PRIVATE_LIBRARIES Calamares::calamaresui) + elseif(PLUGIN_TYPE STREQUAL "job") + list(APPEND PLUGIN_LINK_PRIVATE_LIBRARIES Calamares::calamares) + else() + message(FATAL_ERROR "Unknown plugin type ${PLUGIN_TYPE}") + endif() + + # determine target type + if( NOT ${PLUGIN_SHARED_LIB} ) + set( target_type "MODULE" ) + else() + set( target_type "SHARED" ) + endif() + + set( calamares_add_library_args + "${target}" + "EXPORT_MACRO" "${PLUGIN_EXPORT_MACRO}" + "TARGET_TYPE" "${target_type}" + "SOURCES" "${PLUGIN_SOURCES}" + ) + + if( PLUGIN_UI ) + list( APPEND calamares_add_library_args "UI" "${PLUGIN_UI}" ) + endif() + + if( PLUGIN_LINK_LIBRARIES ) + list( APPEND calamares_add_library_args "LINK_LIBRARIES" "${PLUGIN_LINK_LIBRARIES}" ) + endif() + + if( PLUGIN_LINK_PRIVATE_LIBRARIES ) + list( APPEND calamares_add_library_args "LINK_PRIVATE_LIBRARIES" "${PLUGIN_LINK_PRIVATE_LIBRARIES}" ) + endif() + + if( PLUGIN_COMPILE_DEFINITIONS ) + list( APPEND calamares_add_library_args "COMPILE_DEFINITIONS" ${PLUGIN_COMPILE_DEFINITIONS} ) + endif() + + if ( PLUGIN_NO_INSTALL ) + list( APPEND calamares_add_library_args "NO_INSTALL" ) + endif() + + list( APPEND calamares_add_library_args + "NO_VERSION" + "INSTALL_BINDIR" "${PLUGIN_DESTINATION}" + ) + + if( PLUGIN_RESOURCES ) + list( APPEND calamares_add_library_args "RESOURCES" "${PLUGIN_RESOURCES}" ) + endif() + + calamares_add_library( ${calamares_add_library_args} ) + + if ( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_DESC_FILE} ) + configure_file( ${PLUGIN_DESC_FILE} ${PLUGIN_DESC_FILE} COPYONLY ) + else() + set( _file ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_DESC_FILE} ) + set( _type ${PLUGIN_TYPE} ) + file( WRITE ${_file} "# AUTO-GENERATED metadata file\n# Syntax is YAML 1.2\n---\n" ) + file( APPEND ${_file} "type: \"${_type}\"\nname: \"${PLUGIN_NAME}\"\ninterface: \"qtplugin\"\nload: \"lib${target}.so\"\n" ) + if ( PLUGIN_REQUIRES ) + file( APPEND ${_file} "requiredModules:\n" ) + foreach( _r ${PLUGIN_REQUIRES} ) + file( APPEND ${_file} " - ${_r}\n" ) + endforeach() + endif() + if ( PLUGIN_EMERGENCY ) + file( APPEND ${_file} "emergency: true\n" ) + endif() + if ( PLUGIN_NO_CONFIG ) + file( APPEND ${_file} "noconfig: true\n" ) + endif() + if ( PLUGIN_WEIGHT ) + file( APPEND ${_file} "weight: ${PLUGIN_WEIGHT}\n" ) + endif() + endif() + + if ( NOT PLUGIN_NO_INSTALL ) + install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_DESC_FILE} + DESTINATION ${PLUGIN_DESTINATION} ) + + set( _warned_config OFF ) + foreach( PLUGIN_CONFIG_FILE ${PLUGIN_CONFIG_FILES} ) + if( ${CMAKE_CURRENT_SOURCE_DIR}/${PLUGIN_CONFIG_FILE} IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} ) + configure_file( ${PLUGIN_CONFIG_FILE} ${PLUGIN_CONFIG_FILE} COPYONLY ) + else() + message( " ${BoldYellow}Not updating${ColorReset} ${PLUGIN_CONFIG_FILE}" ) + set( _warned_config ON ) + endif() + if(INSTALL_CONFIG) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_CONFIG_FILE} DESTINATION ${PLUGIN_DATA_DESTINATION}) + endif() + endforeach() + if ( _warned_config ) + message( "" ) + endif() + endif() + + set(${NAME}_TARGET ${target} PARENT_SCOPE) +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAddTest.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAddTest.cmake new file mode 100644 index 0000000..984077e --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAddTest.cmake @@ -0,0 +1,56 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# Support functions for building Calamares tests. +# This extends KDE's ECM tests with some custom patterns. +# +# calamares_add_test( +# +# [GUI] +# [RESOURCES FILE] +# SOURCES +# ) + +include(CMakeParseArguments) +include(CalamaresAutomoc) + +function(calamares_add_test name) + set(options GUI) + set(oneValueArgs RESOURCES) + set(multiValueArgs SOURCES LIBRARIES DEFINITIONS) + cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(TEST_NAME ${name}) + + if(ECM_FOUND AND BUILD_TESTING) + ecm_add_test( + ${TEST_SOURCES} ${TEST_RESOURCES} + TEST_NAME + ${TEST_NAME} + LINK_LIBRARIES + Calamares::calamares + ${TEST_LIBRARIES} + ${qtname}::Core + ${qtname}::Test + ) + calamares_automoc( ${TEST_NAME} ) + # We specifically pass in the source directory of the test-being- + # compiled, so that it can find test-files in that source dir. + target_compile_definitions( + ${TEST_NAME} + PRIVATE -DBUILD_AS_TEST="${CMAKE_CURRENT_SOURCE_DIR}" ${TEST_DEFINITIONS} + ) + if(TEST_GUI) + target_link_libraries(${TEST_NAME} Calamares::calamaresui ${qtname}::Gui) + endif() + if(TEST_RESOURCES) + calamares_autorcc( ${TEST_NAME} ${TEST_RESOURCES} ) + endif() + endif() +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAddTranslations.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAddTranslations.cmake new file mode 100644 index 0000000..785d2fa --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAddTranslations.cmake @@ -0,0 +1,158 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# This file has not yet been documented for use outside of Calamares itself. + +include(CMakeParseArguments) +include(FeatureSummary) + +# The Gettext module is still old-fashioned, ALLCAPS variables +find_package( Gettext ) +set_package_properties( GETTEXT PROPERTIES + DESCRIPTION "GNU gettext (translation) tools." + URL "https://www.gnu.org/software/gettext/" + PURPOSE "Gettext is used in the translation of Python modules." + TYPE REQUIRED +) + +# Installs a directory containing language-code-labeled subdirectories with +# gettext data into the appropriate system directory. Allows renaming the +# .mo files during install to avoid namespace clashes. +# +# install_calamares_gettext_translations( +# NAME +# SOURCE_DIR path/to/lang +# FILENAME +# [RENAME ] +# ) +# +# For all of the (global) translation languages enabled for Calamares, +# try installing $SOURCE_DIR/$lang/LC_MESSAGES/.mo into the +# system gettext data directory (e.g. share/locale/), possibly renaming +# filename.mo to renamed.mo in the process. +function( install_calamares_gettext_translations ) + # parse arguments ( name needs to be saved before passing ARGN into the macro ) + set( NAME ${ARGV0} ) + set( oneValueArgs NAME SOURCE_DIR FILENAME RENAME ) + cmake_parse_arguments( TRANSLATION "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + if( NOT TRANSLATION_NAME ) + set( TRANSLATION_NAME ${NAME} ) + endif() + if( NOT TRANSLATION_FILENAME ) + set( TRANSLATION_FILENAME "${TRANSLATION_NAME}.mo" ) + endif() + if( NOT TRANSLATION_RENAME ) + set( TRANSLATION_RENAME "${TRANSLATION_FILENAME}" ) + endif() + string( REGEX REPLACE ".mo$" ".po" TRANSLATION_SOURCE_FILENAME "${TRANSLATION_FILENAME}" ) + + if ( GETTEXT_FOUND AND GETTEXT_MSGFMT_EXECUTABLE ) + message( STATUS "Installing gettext translations for ${TRANSLATION_NAME}") + message( STATUS " Installing ${TRANSLATION_FILENAME} from ${TRANSLATION_SOURCE_DIR}") + else() + message( WARNING "Gettext translations requested for ${TRANSLATION_NAME}, but gettext was not found." ) + return() + endif() + + set( TARGET_NAME calamares-gettext-translations-${NAME} ) + if( NOT TARGET "${TARGET_NAME}" ) + add_custom_target( "${TARGET_NAME}" ALL ) + endif() + + set( TRANSLATION_NAME "${NAME}" ) + foreach( lang ${CALAMARES_TRANSLATION_LANGUAGES} ) # Global + string( MAKE_C_IDENTIFIER "${TARGET_NAME}-${lang}" TARGET_SUBNAME ) + + set( lang_po "${TRANSLATION_SOURCE_DIR}/${lang}/LC_MESSAGES/${TRANSLATION_SOURCE_FILENAME}" ) + set( lang_mo_dir "${CMAKE_BINARY_DIR}/lang/${lang}/LC_MESSAGES" ) + set( lang_mo "${lang_mo_dir}/${TRANSLATION_RENAME}" ) + if( lang STREQUAL "en" ) + message( STATUS " Skipping ${TRANSLATION_NAME} translations for en_US" ) + else() + # We **don't** use the gettext macro's here because the source + # structure doesn't match: we are calling this once per language + # for all of Calamares's languages, while the gettext module + # expects it to be called once, for a given language source-dir. + # + # Using any of the gettext macros just gets us multiple rules + # for python.gmo, and it wants to use msgmerge, besides, which + # doesn't fit our Transifex workflow. + make_directory( ${lang_mo_dir} ) + add_custom_command( + OUTPUT ${lang_mo} + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} + ARGS -o ${lang_mo} ${lang_po} + MAIN_DEPENDENCY ${lang_po} + ) + add_custom_target( "${TARGET_SUBNAME}" DEPENDS ${lang_mo} ) + add_dependencies( "${TARGET_NAME}" "${TARGET_SUBNAME}" ) + install( + FILES ${lang_mo} + DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${lang}/LC_MESSAGES/ + ) + endif() + endforeach() +endfunction() + +set(_calamares_qrc_translations_qrc_source ${CMAKE_CURRENT_LIST_DIR}/i18n.qrc.in) # Needs to be set outside of function +function(calamares_qrc_translations basename) + set(options "") + set(oneValueArgs SUBDIRECTORY OUTPUT_VARIABLE) + set(multiValueArgs PREFIXES LANGUAGES) + cmake_parse_arguments(_qrt "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT _qrt_OUTPUT_VARIABLE) + message(FATAL_ERROR "No output variable") + endif() + if(NOT _qrt_PREFIXES) + set(_qrt_PREFIXES "${basename}") + endif() + if(NOT _qrt_LANGUAGES) + set(_qrt_LANGUAGES ${CALAMARES_TRANSLATION_LANGUAGES}) + endif() + if(NOT _qrt_SUBDIRECTORY) + set(_qrt_SUBDIRECTORY "") + endif() + + set(translations_qrc_infile ${CMAKE_CURRENT_BINARY_DIR}/${basename}.qrc) + set(translations_qrc_outfile ${CMAKE_CURRENT_BINARY_DIR}/qrc_${basename}.cxx) + + # Must use this variable name because of the @ substitution + set(calamares_i18n_qrc_content "") + set(calamares_i18n_ts_filelist "") + foreach(lang ${_qrt_LANGUAGES}) + foreach(tlsource ${_qrt_PREFIXES}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_qrt_SUBDIRECTORY}/${tlsource}_${lang}.ts") + string(APPEND calamares_i18n_qrc_content "${tlsource}_${lang}.qm\n") + list(APPEND calamares_i18n_ts_filelist "${CMAKE_CURRENT_SOURCE_DIR}/${_qrt_SUBDIRECTORY}/${tlsource}_${lang}.ts") + endif() + endforeach() + endforeach() + + configure_file(${_calamares_qrc_translations_qrc_source} ${translations_qrc_infile} @ONLY) + qt_add_translation(QM_FILES ${calamares_i18n_ts_filelist}) + + # Run the resource compiler (rcc_options should already be set) + add_custom_command( + OUTPUT ${translations_qrc_outfile} + COMMAND ${qtname}::rcc + ARGS + ${rcc_options} + --format-version 1 + -name ${basename} + -o ${translations_qrc_outfile} + ${translations_qrc_infile} + MAIN_DEPENDENCY ${translations_qrc_infile} + DEPENDS ${QM_FILES} + ) + + set(${_qrt_OUTPUT_VARIABLE} ${translations_qrc_outfile} PARENT_SCOPE) +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresAutomoc.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresAutomoc.cmake new file mode 100644 index 0000000..c7dbd72 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresAutomoc.cmake @@ -0,0 +1,57 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2019 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +# +### +# +# Helper function for doing automoc, autouic, autorcc on targets, +# and on the corresponding .ui or .rcc files. +# +# calamares_automoc(target) +# Sets AUTOMOC TRUE for a target. +# +# If the global variable CALAMARES_AUTOMOC_OPTIONS is set, uses that +# as well to set options passed to MOC. This can be used to add +# libcalamares/utils/moc-warnings.h file to the moc, which in turn +# reduces compiler warnings in generated MOC code. +# +# calamares_autouic(target [uifile ..]) +# Sets AUTOUIC TRUE for a target. +# +# If the global variable CALAMARES_AUTOUIC_OPTIONS is set, adds that +# to the options passed to uic for each of the named uifiles. +# +# calamares_autorcc(target [rcfile ..]) +# Sets AUTOUIC TRUE for a target. +# +# If the global variable CALAMARES_AUTORCC_OPTIONS is set, adds that +# to the options passed to rcc for each of the named rcfiles. + +function(calamares_automoc TARGET) + set_target_properties( ${TARGET} PROPERTIES AUTOMOC TRUE ) + if ( CALAMARES_AUTOMOC_OPTIONS ) + set_target_properties( ${TARGET} PROPERTIES AUTOMOC_MOC_OPTIONS "${CALAMARES_AUTOMOC_OPTIONS}" ) + endif() +endfunction() + +function(calamares_autouic TARGET) + set_target_properties( ${TARGET} PROPERTIES AUTOUIC TRUE ) + if ( CALAMARES_AUTOUIC_OPTIONS ) + foreach(S ${ARGN}) + set_property(SOURCE ${S} PROPERTY AUTOUIC_OPTIONS "${CALAMARES_AUTOUIC_OPTIONS}") + endforeach() + endif() +endfunction() + +function(calamares_autorcc TARGET) + set_target_properties( ${TARGET} PROPERTIES AUTORCC TRUE ) + if ( CALAMARES_AUTORCC_OPTIONS ) + foreach(S ${ARGN}) + set_property(SOURCE ${S} PROPERTY AUTORCC_OPTIONS "${CALAMARES_AUTORCC_OPTIONS}") + endforeach() + endif() +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresCheckModuleSelection.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresCheckModuleSelection.cmake new file mode 100644 index 0000000..5eeb0e9 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresCheckModuleSelection.cmake @@ -0,0 +1,116 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2017 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Calamares is Free Software: see the License-Identifier above. +# +### +# +# This module implements the "skip modules" part of configuring +# the Calamares repository or an external-modules repository. +# +# It should not be necessary to include() this module explicitly, +# since both AddPlugin and AddModuleSubdirectory do so implicitly. +# +# +# # Usage +# +# The public API is two functions: +# +# - calamares_skip_module(reason) +# A C++ module (or any that uses CMake) can call this macro to +# add *reason* to the list of skipped modules. Typically a module +# will pass in "modulename (why)" so that it is clear **which** +# module is skipped. This macro should be called at the top-level +# of a module's CMakeLists.txt and the module should then **not** +# call calamares_add_plugin(). +# - calamares_explain_skipped_modules(list...) +# This will print out all the module reasons (see above) that have +# been added to the given *listvar*. When AddModuleSubdirectory is +# used as the mechanism to add all the subdirectories in the repository +# that contain modules, with a consistent *listvar* setting, +# this will show all the modules that have been skipped. +# +# The internal API is one function: +# +# - calamares_check_skip(modulename outvar) +# Checks if the *modulename* has been listed in the global SKIP_MODULES +# variable (to skip specifically-named modules) or if there is a USE_* +# setting applicable to the module. If the module is skipped for this +# reason, a suitable entry is added to *outvar* as if +# calamares_skip_module() had been called. +# +# Best practice is to pick a variable to collect all of the skipped +# modules, and to pass the name of that variable to AddModuleSubdirectory +# in each call. After all subdirectories have been added, call +# calamares_explain_skipped_modules() with the value of that variable. + + +# Convenience function to indicate that a module has been skipped +# (optionally also why). Call this in the module's CMakeLists.txt +macro( calamares_skip_module ) + set( SKIPPED_MODULES ${SKIPPED_MODULES} ${ARGV} PARENT_SCOPE ) +endmacro() + +function( calamares_explain_skipped_modules ) + if ( ARGN ) + message( "${ColorReset}-- Skipped modules:" ) + foreach( SUBDIRECTORY ${ARGN} ) + message( "${ColorReset}-- Skipped ${BoldRed}${SUBDIRECTORY}${ColorReset}." ) + endforeach() + message( "" ) + endif() +endfunction() + +# Globally, SKIP_MODULES and USE_* affect what modules are built. +# Check if *modulename* should be skipped, and if so, set *outvar* to +# a human-readable reason for skipping it. +function( _calamares_check_skip_impl modulename outvar ) + # Globally-defined SKIP_MODULES may be space- or semicolon- separated + # so convert it to a list-variable. + string( REPLACE " " ";" SKIP_LIST "${SKIP_MODULES}" ) + + list( FIND SKIP_LIST "${modulename}" DO_SKIP ) + if( NOT DO_SKIP EQUAL -1 ) + set( ${outvar} "user request" PARENT_SCOPE ) + return() + endif() + + # Not skipped by the global check, see if it has an applicable USE_* + if( "${modulename}" MATCHES "^[a-zA-Z0-9_]+-" ) + # Split the name into - + string( REGEX REPLACE "-.*" "" _category "${modulename}" ) + string( REGEX REPLACE "^[^-]+-" "" _implementation "${modulename}" ) + else() + # Not a module to which USE_* applies + return() + endif() + + if( "${USE_${_category}}" STREQUAL "none" ) + set( ${outvar} "category ${_category} disabled" PARENT_SCOPE ) + return() + elseif( "${USE_${_category}}" STREQUAL "" ) + # Category not set at all or nonexistent + return() + endif() + + if ( "${USE_${_category}}" STREQUAL "${_implementation}" ) + # Matches, so accept this module + else() + set( ${outvar} "category ${_category} selects ${USE_${_category}}" PARENT_SCOPE ) + endif() +endfunction() + +# This is the public API;it calls the _impl version so that there +# is an extra intermediate scope for the subdirectory to write results into. +function( calamares_check_skip modulename outvar ) + set( _skip "" ) + _calamares_check_skip_impl( "${modulename}" _skip ) + if ( _skip ) + message( "${ColorReset}-- Skipping module ${BoldRed}${modulename} (${_skip})${ColorReset}." ) + message( "" ) + set( ${outvar} "${modulename} (${_skip})" PARENT_SCOPE ) + endif() +endfunction() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresConfig.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresConfig.cmake new file mode 100644 index 0000000..9a77f07 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresConfig.cmake @@ -0,0 +1,138 @@ +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +# Note that Calamares itself is GPL-3.0-or-later: the above license +# applies to **this** CMake file. +# +# Config file for the Calamares package +# +# The following IMPORTED targets are defined: +# - Calamares::calamares - the core library +# - Calamares::calamaresui - the UI (and QML) library +# +# For legacy use it defines the following variables: +# - Calamares_INCLUDE_DIRS - include directories for Calamares +# - Calamares_LIB_DIRS - library directories +# - Calamares_LIBRARIES - libraries to link against + + +####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() ####### +####### Any changes to this file will be overwritten by the next CMake run #### +####### The input file was CalamaresConfig.cmake.in ######## + +get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE) + +# Use original install prefix when loaded through a "/usr move" +# cross-prefix symbolic link such as /lib -> /usr/lib. +get_filename_component(_realCurr "${CMAKE_CURRENT_LIST_DIR}" REALPATH) +get_filename_component(_realOrig "/usr/lib/cmake/Calamares" REALPATH) +if(_realCurr STREQUAL _realOrig) + set(PACKAGE_PREFIX_DIR "/usr") +endif() +unset(_realOrig) +unset(_realCurr) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() + +#################################################################################### + +### Versioning and IMPORTED targets +# +# +include(${CMAKE_CURRENT_LIST_DIR}/CalamaresConfigVersion.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/CalamaresTargets.cmake) +if (NOT TARGET Calamares::calamares OR NOT TARGET Calamares::calamaresui) + message(FATAL_ERROR "Calamares found with missing CMake targets") +endif() + +# Need various CMake files that are installed alongside this one. +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +### Dependencies +# +# The libraries can depend on a variety of Qt and KDE Frameworks +# components, so accumulate them and find (just once). +# +macro(accumulate_deps outvar target namespace) + string(LENGTH ${namespace} _nslen) + get_target_property(_libs ${target} INTERFACE_LINK_LIBRARIES) + foreach(_lib ${_libs}) + if (_lib MATCHES ^${namespace}) + string(SUBSTRING ${_lib} ${_nslen} -1 _component) + list(APPEND ${outvar} ${_component}) + endif() + endforeach() +endmacro() + +set(Calamares_WITH_QT6 ON) +if(Calamares_WITH_QT6) + set(qtname "Qt6") + set(kfname "kf6") + message(STATUS "Calamares was built with Qt6 and KDE Frameworks 6") +else() + set(qtname "Qt5") + set(kfname "kf5") + message(STATUS "Calamares was built with Qt5 and KDE Frameworks 5 (legacy)") +endif() + +# Qt infrastructure for translations is required +set(qt_required Core Widgets LinguistTools) +accumulate_deps(qt_required Calamares::calamares ${qtname}::) +accumulate_deps(qt_required Calamares::calamaresui ${qtname}::) +find_package(${qtname} CONFIG REQUIRED ${qt_required}) + +set(kf_required "") +accumulate_deps(kf_required Calamares::calamares ${kfname}::) +accumulate_deps(kf_required Calamares::calamaresui ${kfname}::) +if(kf_required) + find_package(ECM ${ECM_VERSION} NO_MODULE) + if( ECM_FOUND ) + list(INSERT CMAKE_MODULE_PATH 0 ${ECM_MODULE_PATH}) + find_package(${kfname} REQUIRED COMPONENTS ${kf_required}) + endif() +endif() + +find_package(YAMLCPP REQUIRED) + +### Legacy support +# +# +set(Calamares_LIB_DIRS "${PACKAGE_PREFIX_DIR}/lib") +set(Calamares_INCLUDE_DIRS "${PACKAGE_PREFIX_DIR}/include") +set(Calamares_LIBRARIES Calamares::calamares) + +### CMake support +# +# +include(CalamaresAddBrandingSubdirectory) +include(CalamaresAddLibrary) +include(CalamaresAddModuleSubdirectory) +include(CalamaresAddPlugin) + +# These are feature-settings that affect consumers of Calamares +# libraries as well; without Python-support in the libs, for instance, +# there's no point in having a Python plugin. +# +# This list should match the one in libcalamares/CalamaresConfig.h, +# which is the C++-language side of the same configuration. +set(Calamares_WITH_PYTHON ON) +set(Calamares_WITH_PYBIND11 ON) +set(Calamares_WITH_BOOST_PYTHON ) +set(Calamares_WITH_QML ON) +set(Calamares_WITH_QT6 ON) diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresConfigVersion.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresConfigVersion.cmake new file mode 100644 index 0000000..bea8a00 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresConfigVersion.cmake @@ -0,0 +1,65 @@ +# This is a basic version file for the Config-mode of find_package(). +# It is used by write_basic_package_version_file() as input file for configure_file() +# to create a version-file which can be installed along a config.cmake file. +# +# The created file sets PACKAGE_VERSION_EXACT if the current version string and +# the requested version string are exactly the same and it sets +# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version, +# but only if the requested major version is the same as the current one. +# The variable CVF_VERSION must be set before calling configure_file(). + + +set(PACKAGE_VERSION "3.4.0") + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + + if("3.4.0" MATCHES "^([0-9]+)\\.") + set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}") + if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0) + string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}") + endif() + else() + set(CVF_VERSION_MAJOR "3.4.0") + endif() + + if(PACKAGE_FIND_VERSION_RANGE) + # both endpoints of the range must have the expected major version + math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1") + if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT))) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR + AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX) + OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX))) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + else() + if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR) + set(PACKAGE_VERSION_COMPATIBLE TRUE) + else() + set(PACKAGE_VERSION_COMPATIBLE FALSE) + endif() + + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() + endif() +endif() + + +# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") + return() +endif() + +# check that the installed version has the same 32/64bit-ness as the one which is currently searching: +if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") + math(EXPR installedBits "8 * 8") + set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") + set(PACKAGE_VERSION_UNSUITABLE TRUE) +endif() diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresTargets-release.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresTargets-release.cmake new file mode 100644 index 0000000..c44757c --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresTargets-release.cmake @@ -0,0 +1,31 @@ +#---------------------------------------------------------------- +# Generated CMake target import file for configuration "Release". +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Import target "Calamares::calamares" for configuration "Release" +set_property(TARGET Calamares::calamares APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Calamares::calamares PROPERTIES + IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE "Qt6::DBus;Python::Python;Qt6::Xml" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libcalamares.so.3.4.0" + IMPORTED_SONAME_RELEASE "libcalamares.so.3.4" + ) + +list(APPEND _cmake_import_check_targets Calamares::calamares ) +list(APPEND _cmake_import_check_files_for_Calamares::calamares "${_IMPORT_PREFIX}/lib/libcalamares.so.3.4.0" ) + +# Import target "Calamares::calamaresui" for configuration "Release" +set_property(TARGET Calamares::calamaresui APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Calamares::calamaresui PROPERTIES + IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE "KF6::CoreAddons" + IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libcalamaresui.so.3.4.0" + IMPORTED_SONAME_RELEASE "libcalamaresui.so.3.4" + ) + +list(APPEND _cmake_import_check_targets Calamares::calamaresui ) +list(APPEND _cmake_import_check_files_for_Calamares::calamaresui "${_IMPORT_PREFIX}/lib/libcalamaresui.so.3.4.0" ) + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) diff --git a/airootfs/usr/lib/cmake/Calamares/CalamaresTargets.cmake b/airootfs/usr/lib/cmake/Calamares/CalamaresTargets.cmake new file mode 100644 index 0000000..fc459a3 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/CalamaresTargets.cmake @@ -0,0 +1,123 @@ +# Generated by CMake + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8) + message(FATAL_ERROR "CMake >= 2.8.12 required") +endif() +if(CMAKE_VERSION VERSION_LESS "2.8.12") + message(FATAL_ERROR "CMake >= 2.8.12 required") +endif() +cmake_policy(PUSH) +cmake_policy(VERSION 2.8.12...4.0) +#---------------------------------------------------------------- +# Generated CMake target import file. +#---------------------------------------------------------------- + +# Commands may need to know the format version. +set(CMAKE_IMPORT_FILE_VERSION 1) + +# Protect against multiple inclusion, which would fail when already imported targets are added once more. +set(_cmake_targets_defined "") +set(_cmake_targets_not_defined "") +set(_cmake_expected_targets "") +foreach(_cmake_expected_target IN ITEMS Calamares::calamares Calamares::calamaresui) + list(APPEND _cmake_expected_targets "${_cmake_expected_target}") + if(TARGET "${_cmake_expected_target}") + list(APPEND _cmake_targets_defined "${_cmake_expected_target}") + else() + list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}") + endif() +endforeach() +unset(_cmake_expected_target) +if(_cmake_targets_defined STREQUAL _cmake_expected_targets) + unset(_cmake_targets_defined) + unset(_cmake_targets_not_defined) + unset(_cmake_expected_targets) + unset(CMAKE_IMPORT_FILE_VERSION) + cmake_policy(POP) + return() +endif() +if(NOT _cmake_targets_defined STREQUAL "") + string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}") + string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n") +endif() +unset(_cmake_targets_defined) +unset(_cmake_targets_not_defined) +unset(_cmake_expected_targets) + + +# Compute the installation prefix relative to this file. +get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +# Use original install prefix when loaded through a +# cross-prefix symbolic link such as /lib -> /usr/lib. +get_filename_component(_realCurr "${_IMPORT_PREFIX}" REALPATH) +get_filename_component(_realOrig "/usr/lib/cmake/Calamares" REALPATH) +if(_realCurr STREQUAL _realOrig) + set(_IMPORT_PREFIX "/usr/lib/cmake/Calamares") +endif() +unset(_realOrig) +unset(_realCurr) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH) +if(_IMPORT_PREFIX STREQUAL "/") + set(_IMPORT_PREFIX "") +endif() + +# Create imported target Calamares::calamares +add_library(Calamares::calamares SHARED IMPORTED) + +set_target_properties(Calamares::calamares PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/libcalamares" + INTERFACE_LINK_LIBRARIES "yamlcpp::yamlcpp;Qt6::Core;Qt6::Network;KF6::CoreAddons" +) + +# Create imported target Calamares::calamaresui +add_library(Calamares::calamaresui SHARED IMPORTED) + +set_target_properties(Calamares::calamaresui PROPERTIES + INTERFACE_LINK_LIBRARIES "Calamares::calamares;Qt6::Core;Qt6::Gui;Qt6::Widgets;Qt6::Svg;Qt6::QuickWidgets" +) + +# Load information for each installed configuration. +file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/CalamaresTargets-*.cmake") +foreach(_cmake_config_file IN LISTS _cmake_config_files) + include("${_cmake_config_file}") +endforeach() +unset(_cmake_config_file) +unset(_cmake_config_files) + +# Cleanup temporary variables. +set(_IMPORT_PREFIX) + +# Loop over all imported files and verify that they actually exist +foreach(_cmake_target IN LISTS _cmake_import_check_targets) + if(CMAKE_VERSION VERSION_LESS "3.28" + OR NOT DEFINED _cmake_import_check_xcframework_for_${_cmake_target} + OR NOT IS_DIRECTORY "${_cmake_import_check_xcframework_for_${_cmake_target}}") + foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}") + if(NOT EXISTS "${_cmake_file}") + message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file + \"${_cmake_file}\" +but this file does not exist. Possible reasons include: +* The file was deleted, renamed, or moved to another location. +* An install or uninstall procedure did not complete successfully. +* The installation package was faulty and contained + \"${CMAKE_CURRENT_LIST_FILE}\" +but not all the files it references. +") + endif() + endforeach() + endif() + unset(_cmake_file) + unset("_cmake_import_check_files_for_${_cmake_target}") +endforeach() +unset(_cmake_target) +unset(_cmake_import_check_targets) + +# This file does not depend on other imported targets which have +# been exported from the same project but in a separate export set. + +# Commands beyond this point should not need to know the version. +set(CMAKE_IMPORT_FILE_VERSION) +cmake_policy(POP) diff --git a/airootfs/usr/lib/cmake/Calamares/FindYAMLCPP.cmake b/airootfs/usr/lib/cmake/Calamares/FindYAMLCPP.cmake new file mode 100644 index 0000000..f8ea439 --- /dev/null +++ b/airootfs/usr/lib/cmake/Calamares/FindYAMLCPP.cmake @@ -0,0 +1,73 @@ +# === This file is part of Calamares - === +# +# SPDX-FileCopyrightText: 2014 Teo Mrnjavac +# SPDX-FileCopyrightText: 2020 Adriaan de Groot +# SPDX-License-Identifier: BSD-2-Clause +# +### +# +# Locate yaml-cpp +# +# This module defines +# YAMLCPP_FOUND, if false, do not try to link to yaml-cpp +# YAMLCPP_LIBRARY, where to find yaml-cpp +# YAMLCPP_INCLUDE_DIR, where to find yaml.h +# There is also one IMPORTED library target, +# yamlcpp::yamlcpp +# +# By default, the dynamic libraries of yaml-cpp will be found. To find the static ones instead, +# you must set the YAMLCPP_STATIC_LIBRARY variable to TRUE before calling find_package(YamlCpp ...). +# +# If yaml-cpp is not installed in a standard path, you can use the YAMLCPP_DIR CMake variable +# to tell CMake where yaml-cpp is. + +if(TARGET yamlcpp::yamlcpp) + return() +endif() + +# attempt to find static library first if this is set +if(YAMLCPP_STATIC_LIBRARY) + set(YAMLCPP_STATIC libyaml-cpp.a) +endif() + +# find the yaml-cpp include directory +find_path(YAMLCPP_INCLUDE_DIR yaml-cpp/yaml.h + PATH_SUFFIXES include + PATHS + ~/Library/Frameworks/yaml-cpp/include/ + /Library/Frameworks/yaml-cpp/include/ + /usr/local/include/ + /usr/include/ + /sw/yaml-cpp/ # Fink + /opt/local/yaml-cpp/ # DarwinPorts + /opt/csw/yaml-cpp/ # Blastwave + /opt/yaml-cpp/ + ${YAMLCPP_DIR}/include/) + +# find the yaml-cpp library +find_library(YAMLCPP_LIBRARY + NAMES ${YAMLCPP_STATIC} yaml-cpp + PATH_SUFFIXES lib64 lib + PATHS ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt + ${YAMLCPP_DIR}/lib) + +# handle the QUIETLY and REQUIRED arguments and set YAMLCPP_FOUND to TRUE if all listed variables are TRUE +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(YAMLCPP DEFAULT_MSG YAMLCPP_INCLUDE_DIR YAMLCPP_LIBRARY) +mark_as_advanced(YAMLCPP_INCLUDE_DIR YAMLCPP_LIBRARY) + +# Add an imported target +if( YAMLCPP_LIBRARY ) + add_library( yamlcpp::yamlcpp UNKNOWN IMPORTED ) + set_property( TARGET yamlcpp::yamlcpp PROPERTY IMPORTED_LOCATION ${YAMLCPP_LIBRARY} ) + if ( YAMLCPP_INCLUDE_DIR ) + set_property( TARGET yamlcpp::yamlcpp PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${YAMLCPP_INCLUDE_DIR} ) + endif() +endif() diff --git a/airootfs/usr/lib/libcalamares.so b/airootfs/usr/lib/libcalamares.so new file mode 120000 index 0000000..cb9e531 --- /dev/null +++ b/airootfs/usr/lib/libcalamares.so @@ -0,0 +1 @@ +libcalamares.so.3.4 \ No newline at end of file diff --git a/airootfs/usr/lib/libcalamares.so.3.4 b/airootfs/usr/lib/libcalamares.so.3.4 new file mode 120000 index 0000000..f6a9813 --- /dev/null +++ b/airootfs/usr/lib/libcalamares.so.3.4 @@ -0,0 +1 @@ +libcalamares.so.3.4.0 \ No newline at end of file diff --git a/airootfs/usr/lib/libcalamares.so.3.4.0 b/airootfs/usr/lib/libcalamares.so.3.4.0 new file mode 100755 index 0000000..c748dec Binary files /dev/null and b/airootfs/usr/lib/libcalamares.so.3.4.0 differ diff --git a/airootfs/usr/lib/libcalamaresui.so b/airootfs/usr/lib/libcalamaresui.so new file mode 120000 index 0000000..4afc515 --- /dev/null +++ b/airootfs/usr/lib/libcalamaresui.so @@ -0,0 +1 @@ +libcalamaresui.so.3.4 \ No newline at end of file diff --git a/airootfs/usr/lib/libcalamaresui.so.3.4 b/airootfs/usr/lib/libcalamaresui.so.3.4 new file mode 120000 index 0000000..7ee05e7 --- /dev/null +++ b/airootfs/usr/lib/libcalamaresui.so.3.4 @@ -0,0 +1 @@ +libcalamaresui.so.3.4.0 \ No newline at end of file diff --git a/airootfs/usr/lib/libcalamaresui.so.3.4.0 b/airootfs/usr/lib/libcalamaresui.so.3.4.0 new file mode 100755 index 0000000..4a3330e Binary files /dev/null and b/airootfs/usr/lib/libcalamaresui.so.3.4.0 differ diff --git a/airootfs/usr/local/bin/Installation_guide b/airootfs/usr/local/bin/Installation_guide new file mode 100755 index 0000000..58b97b0 --- /dev/null +++ b/airootfs/usr/local/bin/Installation_guide @@ -0,0 +1,5 @@ +#!/bin/sh +# +# SPDX-License-Identifier: GPL-3.0-or-later + +exec xdg-open 'https://wiki.archlinux.org/title/Installation_guide' diff --git a/airootfs/usr/local/bin/choose-mirror b/airootfs/usr/local/bin/choose-mirror new file mode 100755 index 0000000..d2349de --- /dev/null +++ b/airootfs/usr/local/bin/choose-mirror @@ -0,0 +1,29 @@ +#!/bin/bash +# +# SPDX-License-Identifier: GPL-3.0-or-later + +get_cmdline() { + local param + for param in $(/etc/pacman.d/mirrorlist < +# $2 +# $3 +unmute_and_set_level() { + [[ -n "$3" && -n "$2" && -n "$1" ]] || bugout + systemd-cat -t "livecdsound" printf "Setting: %s on card: %s to %s\n" "$2" "$1" "$3" + systemd-cat -t "livecdsound" amixer -c "$1" set "$2" "$3" unmute + return 0 +} + +# $1 +# $2 +mute_and_zero_level() { + [[ -n "$1" && -n "$2" ]] || bugout + systemd-cat -t "livecdsound" printf "Muting control: %s on card: %s\n" "$2" "$1" + systemd-cat -t "livecdsound" amixer -c "$1" set "$2" "0%" mute + return 0 +} + +# $1 +# $2 +# $3 "on" | "off" +switch_control() { + [[ -n "$3" && -n "$1" ]] || bugout + systemd-cat -t "livecdsound" printf "Switching control: %s on card: %s to %s\n" "$2" "$1" "$3" + systemd-cat -t "livecdsound" amixer -c "$1" set "$2" "$3" + return 0 +} + +# $1 +sanify_levels_on_card() { + unmute_and_set_level "$1" "Front" "80%" + unmute_and_set_level "$1" "Master" "80%" + unmute_and_set_level "$1" "Master Mono" "80%" + unmute_and_set_level "$1" "Master Digital" "80%" # E.g., cs4237B + unmute_and_set_level "$1" "Playback" "80%" + unmute_and_set_level "$1" "Headphone" "100%" + unmute_and_set_level "$1" "PCM" "80%" + unmute_and_set_level "$1" "PCM,1" "80%" # E.g., ess1969 + unmute_and_set_level "$1" "DAC" "80%" # E.g., envy24, cs46xx + unmute_and_set_level "$1" "DAC,0" "80%" # E.g., envy24 + unmute_and_set_level "$1" "DAC,1" "80%" # E.g., envy24 + unmute_and_set_level "$1" "Synth" "80%" + unmute_and_set_level "$1" "CD" "80%" + unmute_and_set_level "$1" "PC Speaker" "100%" + + mute_and_zero_level "$1" "Mic" + mute_and_zero_level "$1" "IEC958" # Ubuntu #19648 + + # Intel P4P800-MX + switch_control "$1" "Master Playback Switch" on + switch_control "$1" "Master Surround" on + + # Trident/YMFPCI/emu10k1: + unmute_and_set_level "$1" "Wave" "80%" + unmute_and_set_level "$1" "Music" "80%" + unmute_and_set_level "$1" "AC97" "80%" + + # DRC: + unmute_and_set_level "$1" "Dynamic Range Compression" "80%" + + # Required for HDA Intel (hda-intel): + unmute_and_set_level "$1" "Front" "80%" + + # Required for SB Live 7.1/24-bit (ca0106): + unmute_and_set_level "$1" "Analog Front" "80%" + + # Required at least for Via 823x hardware on DFI K8M800-MLVF Motherboard + switch_control "$1" "IEC958 Capture Monitor" off + + # Required for hardware allowing toggles for AC97 through IEC958, + # valid values are 0, 1, 2, 3. Needs to be set to 0 for PCM1. + unmute_and_set_level "$1" "IEC958 Playback AC97-SPSA" "0" + + # Required for newer Via hardware + unmute_and_set_level "$1" "VIA DXS,0" "80%" + unmute_and_set_level "$1" "VIA DXS,1" "80%" + unmute_and_set_level "$1" "VIA DXS,2" "80%" + unmute_and_set_level "$1" "VIA DXS,3" "80%" + + # Required on some notebooks with ICH4: + switch_control "$1" "Headphone Jack Sense" off + switch_control "$1" "Line Jack Sense" off + + # Some machines need one or more of these to be on; + # others need one or more of these to be off: + + switch_control "$1" "Audigy Analog/Digital Output Jack" on + switch_control "$1" "SB Live Analog/Digital Output Jack" on + + # D1984 -- Thinkpad T61/X61 + switch_control "$1" "Speaker" on + switch_control "$1" "Headphone" on + + # HDA-Intel w/ "Digital" capture mixer (See Ubuntu #193823) + unmute_and_set_level "$1" "Digital" "80%" + + return 0 +} + +# $1 | "all" +sanify_levels() { + local ttsdml_returnstatus=0 + local card + case "$1" in + all) + for card in $(echo_card_indices); do + sanify_levels_on_card "$card" || ttsdml_returnstatus=1 + done + ;; + *) + sanify_levels_on_card "$1" || ttsdml_returnstatus=1 + ;; + esac + return "$ttsdml_returnstatus" +} + +# List all cards that *should* be usable for PCM audio. In my experience, +# the console speaker (handled by the pcsp driver) isn't a suitable playback +# device, so we'll exclude it. +list_non_pcsp_cards() { + for card in $(echo_card_indices); do + local cardfile="/proc/asound/card${card}/id" + if [[ -r "$cardfile" && -f "$cardfile" && "$(cat "$cardfile")" != pcsp ]]; then + echo "$card" + fi + done +} + +# Properly initialize the sound card so that we have audio at boot. +unmute_all_cards() { + sanify_levels all +} + +is_numeric() { + local str="$1" + [[ "$str" =~ ^[0-9]+$ ]] +} + +set_default_card() { + local card="$1" + sed -e "s/%card%/$card/g" /etc/asound.conf +} + +play_on_card() { + local card="$1" file="$2" + aplay -q "-Dplughw:$card,0" "$file" +} + +# If there are multiple usable sound cards, prompt the user to choose one, +# using auditory feedback. +pick_a_card() { + set -f + usable_cards="$(list_non_pcsp_cards)" + num_usable_cards="$(wc -w <<<"$usable_cards")" + + if (( num_usable_cards == 1 )); then + systemd-cat -t "livecdsound" printf "Only one sound card is detected\n" + exit 0 + fi + systemd-cat -t "livecdsound" printf "multiple sound cards detected\n" + for card in "${usable_cards[@]}"; do + if ! is_numeric "$card"; then + continue + fi + play_on_card "$card" /usr/share/livecd-sounds/pick-a-card.wav & + done + wait + sleep 1 + for card in "${usable_cards[@]}"; do + if ! is_numeric "$card"; then + continue + fi + play_on_card "$card" /usr/share/livecd-sounds/beep.wav + if read -r -t 10; then + systemd-cat -t "livecdsound" printf "Selecting %s sound card as default\n" "$card" + set_default_card "$card" + break + fi + done +} + +if (( $# == 0 )); then + echo "error: No argument passed." + exit 1 +fi +while [[ "${1}" != "" ]]; do + case ${1} in + -h|--help) + usage + exit + ;; + -u|--unmute) + systemd-cat -t "livecdsound" printf "Unmuting all cards" + unmute_all_cards + ;; + -p|--pick) + pick_a_card + ;; + *) + echo "error: Unsupported argument" + usage + exit 1 + ;; + esac + shift +done diff --git a/airootfs/usr/local/share/livecd-sound/asound.conf.in b/airootfs/usr/local/share/livecd-sound/asound.conf.in new file mode 100644 index 0000000..3f9c7aa --- /dev/null +++ b/airootfs/usr/local/share/livecd-sound/asound.conf.in @@ -0,0 +1,3 @@ +Defaults node +defaults.ctl.card %card%; +defaults.pcm.card %card%; diff --git a/airootfs/usr/share/applications/calamares.desktop b/airootfs/usr/share/applications/calamares.desktop new file mode 100644 index 0000000..9a1416e --- /dev/null +++ b/airootfs/usr/share/applications/calamares.desktop @@ -0,0 +1,243 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=Install System +GenericName=System Installer +Keywords=calamares;system;installer; +TryExec=calamares +Exec=sh -c "pkexec calamares" +Comment=Calamares — System Installer +Icon=calamares +Terminal=false +StartupNotify=true +Categories=Qt;System; +X-AppStream-Ignore=true + +Name[ar]=تثبيت النظام +Icon[ar]=كالامارس +GenericName[ar]=مثبت النظام +Comment[ar]=كالامارس - مثبت النظام +Name[as]=চিছটেম ইনস্তল কৰক +Icon[as]=কেলামাৰেচ +GenericName[as]=চিছটেম ইনস্তলাৰ +Comment[as]=কেলামাৰেচ — চিছটেম​ ইনস্তলাৰ +Name[ast]=Instalar el sistema +Icon[ast]=calamares +GenericName[ast]=Instalador del sistema +Comment[ast]=Calamares — Instalador del sistema +Name[az]=Sistemi Quraşdırmaq +Icon[az]=calamares +GenericName[az]=Sistem Quraşdırıcısı +Comment[az]=Calamares Sistem Quraşdırıcısı +Name[az_AZ]=Sistemi quraşdırmaq +Icon[az_AZ]=calamares +GenericName[az_AZ]=Sistem quraşdırcısı +Comment[az_AZ]=Calamares — Sistem Quraşdırıcısı +Name[be]=Усталяваць сістэму +Icon[be]=calamares +GenericName[be]=Усталёўшчык сістэмы +Comment[be]=Calamares — усталёўшчык сістэмы +Name[bg]=Инсталирай системата +Icon[bg]=calamares +GenericName[bg]=Системен инсталатор +Comment[bg]=„Calamares“ – Системен инсталатор +Name[bn]=সিস্টেম ইনস্টল করুন +Icon[bn]=ক্যালামারেস +GenericName[bn]=সিস্টেম ইনস্টলার +Comment[bn]=ক্যালামারেস - সিস্টেম ইনস্টলার +Name[ca]=Instal·la el sistema +Icon[ca]=calamares +GenericName[ca]=Instal·lador de sistema +Comment[ca]=Calamares — Instal·lador de sistema +Name[cs_CZ]=Nainstalovat systém +Icon[cs_CZ]=calamares +GenericName[cs_CZ]=Instalátor systému +Comment[cs_CZ]=Calamares – instalátor operačních systémů +Name[da]=Installér system +Icon[da]=calamares +GenericName[da]=Systeminstallationsprogram +Comment[da]=Calamares — Systeminstallationsprogram +Name[de]=System installieren +Icon[de]=calamares +GenericName[de]=Installation des Betriebssystems +Comment[de]=Calamares - Installation des Betriebssystems +Name[el]=Εγκατάσταση συστήματος +Icon[el]=calamares +GenericName[el]=Εγκατάσταση συστήματος +Comment[el]=Calamares — Εγκατάσταση συστήματος +Name[en_GB]=Install System +Icon[en_GB]=calamares +GenericName[en_GB]=System Installer +Comment[en_GB]=Calamares — System Installer +Name[eo]=Instali Sistemo +Icon[eo]=calamares +GenericName[eo]=Sistema Instalilo +Comment[eo]=Calamares — Sistema Instalilo +Name[es]=Instalar el sistema +Icon[es]=calamares +GenericName[es]=Instalador del sistema +Comment[es]=Calamares — Instalador del sistema +Name[es_MX]=Instalar el Sistema +Icon[es_MX]=calamares +GenericName[es_MX]=Instalador del sistema +Comment[es_MX]=Calamares - Instalador del sistema +Name[es_PR]=Instalar el sistema +Name[et]=Paigalda süsteem +Icon[et]=calamares +GenericName[et]=Süsteemipaigaldaja +Comment[et]=Calamares — süsteemipaigaldaja +Name[eu]=Sistema instalatu +Icon[eu]=calamares +GenericName[eu]=Sistema instalatzailea +Comment[eu]=Calamares - sistema instalatzailea +Name[fa]=نصب سامانه +Icon[fa]=کالامارس +GenericName[fa]=نصب‌کننده سامانه +Comment[fa]=کالامارس — نصب‌کننده سامانه +Name[fi_FI]=Asenna järjestelmä +Icon[fi_FI]=calamares +GenericName[fi_FI]=Järjestelmän asennusohjelma +Comment[fi_FI]=Calamares — Järjestelmän asentaja +Name[fr]=Installer le système +Icon[fr]=calamares +GenericName[fr]=Installateur système +Comment[fr]=Calamares - Installateur système +Name[fur]=Instale il sisteme +Icon[fur]=calamares +GenericName[fur]=Program di instalazion dal sisteme +Comment[fur]=Calamares — Program di instalazion dal sisteme +Name[gl]=Instalación do Sistema +Icon[gl]=calamares +GenericName[gl]=Instalador de sistemas +Comment[gl]=Calamares — Instalador de sistemas +Name[he]=התקנת מערכת +Icon[he]=calamares +GenericName[he]=אשף התקנה +Comment[he]=Calamares - אשף התקנה +Name[hi]=सिस्टम इंस्टॉल करें +Icon[hi]=calamares +GenericName[hi]=सिस्टम इंस्टॉलर +Comment[hi]=Calamares — सिस्टम इंस्टॉलर +Name[hr]=Instaliraj sustav +Icon[hr]=calamares +GenericName[hr]=Instalacija sustava +Comment[hr]=Calamares — Instalacija sustava +Name[hu]=Rendszer telepítése +Icon[hu]=calamares +GenericName[hu]=Rendszertelepítő +Comment[hu]=Calamares – Rendszertelepítő +Name[id]=Instal Sistem +Icon[id]=calamares +GenericName[id]=Pemasang +Comment[id]=Calamares — Pemasang Sistem +Name[ie]=Installar li sistema +Icon[ie]=calamares +GenericName[ie]=Installator del sistema +Comment[ie]=Calamares — Installator del sistema +Name[is]=Setja upp kerfið +Icon[is]=calamares +GenericName[is]=Kerfis uppsetning +Comment[is]=Calamares — Kerfis uppsetning +Name[it_IT]=Installa il sistema +Icon[it_IT]=calamares +GenericName[it_IT]=Programma d'installazione del sistema +Comment[it_IT]=Calamares — Programma d'installazione del sistema +Name[ja]=システムをインストール +Icon[ja]=calamares +GenericName[ja]=システムインストーラー +Comment[ja]=Calamares — システムインストーラー +Name[ko]=시스템 설치 +Icon[ko]=깔라마레스 +GenericName[ko]=시스템 설치 관리자 +Comment[ko]=깔라마레스 — 시스템 설치 관리자 +Name[lt]=Įdiegti Sistemą +Icon[lt]=calamares +GenericName[lt]=Sistemos diegimas į kompiuterį +Comment[lt]=Calamares — Sistemos diegimo programa +Name[mk]=Инсталирај го системот +Icon[mk]=calamares +GenericName[mk]=Системен Инсталер +Comment[mk]=Calamares - Системен Инсталер +Name[ml]=സിസ്റ്റം ഇൻസ്റ്റാൾ ചെയ്യുക +Icon[ml]=കലാമാരേസ് +GenericName[ml]=സിസ്റ്റം ഇൻസ്റ്റാളർ +Comment[ml]=കലാമാരേസ് - സിസ്റ്റം ഇൻസ്റ്റാളർ +Name[nb]=Installer System +Icon[nb]=calamares +GenericName[nb]=Systeminstallatør +Comment[nb]=Calamares-systeminstallatør +Name[ne_NP]= सिस्टम इन्स्टल गर्नुहोस् +Icon[ne_NP]=Calamares +GenericName[ne_NP]=सिस्टम इन्स्टलर +Comment[ne_NP]=Calamares - सिस्टम इन्स्टलर +Name[nl]=Installeer systeem +Icon[nl]=calamares +GenericName[nl]=Installatieprogramma +Comment[nl]=Calamares — Installatieprogramma +Name[pl]=Zainstaluj system +Icon[pl]=calamares +GenericName[pl]=Instalator systemu +Comment[pl]=Calamares — Instalator systemu +Name[pt_BR]=Sistema de Instalação +Icon[pt_BR]=calamares +GenericName[pt_BR]=Instalador de Sistema +Comment[pt_BR]=Calamares — Instalador de Sistema +Name[pt_PT]=Instalar Sistema +Icon[pt_PT]=calamares +GenericName[pt_PT]=Instalador de Sistema +Comment[pt_PT]=Instalador de Sistema - Calamares +Name[ro]=Instalează sistemul +Icon[ro]=calamares +GenericName[ro]=Instalator de sistem +Comment[ro]=Calamares — Instalator de sistem +Name[ru]=Установить систему +Icon[ru]=calamares +GenericName[ru]=Установщик системы +Comment[ru]=Calamares - Установщик системы +Name[si]=පද්ධතිය ස්ථාපනය කරන්න +Icon[si]=කැලමරේස් +GenericName[si]=පද්ධති ස්ථාපකය +Comment[si]=Calamares - පද්ධති ස්ථාපකය +Name[sk]=Inštalovať systém +Icon[sk]=calamares +GenericName[sk]=Inštalátor systému +Comment[sk]=Calamares — Inštalátor systému +Name[sl]=Namesti sistem +Name[sq]=Instalo Sistemin +Icon[sq]=calamares +GenericName[sq]=Instalues Sistemi +Comment[sq]=Calamares — Instalues Sistemi +Name[sr]=Инсталирај систем +Icon[sr]=calamares +GenericName[sr]=Инсталатер система +Comment[sr]=Каламарес — инсталатер система +Name[sr@latin]=Instaliraj sistem +Name[sv]=Installera system +Icon[sv]=calamares +GenericName[sv]=Systeminstallerare +Comment[sv]=Calamares — Systeminstallerare +Name[tg]=Насбкунии низом +Icon[tg]=calamares +GenericName[tg]=Насбкунандаи низомӣ +Comment[tg]=Calamares — Насбкунандаи низомӣ +Name[th]=ติดตั้งระบบ +Name[tr_TR]=Sistemi Yükle +Icon[tr_TR]=calamares +GenericName[tr_TR]=Sistem Yükleyici +Comment[tr_TR]=Calamares — Sistem Yükleyici +Name[uk]=Встановити Систему +Icon[uk]=calamares +GenericName[uk]=Встановлювач системи +Comment[uk]=Calamares - Встановлювач системи +Name[vi]=Cài đặt hệ thống +Icon[vi]=calamares +GenericName[vi]=Bộ cài đặt hệ thống +Comment[vi]=Calamares — Bộ cài đặt hệ thống +Name[zh_CN]=安装系统 +Icon[zh_CN]=calamares +GenericName[zh_CN]=系统安装程序 +Comment[zh_CN]=Calamares — 系统安装程序 +Name[zh_TW]=安裝系統 +Icon[zh_TW]=calamares +GenericName[zh_TW]=系統安裝程式 +Comment[zh_TW]=Calamares ── 系統安裝程式 diff --git a/airootfs/usr/share/calamares/branding/default/banner.png b/airootfs/usr/share/calamares/branding/default/banner.png new file mode 100644 index 0000000..d1baeee Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/banner.png differ diff --git a/airootfs/usr/share/calamares/branding/default/banner.png.license b/airootfs/usr/share/calamares/branding/default/banner.png.license new file mode 100644 index 0000000..38aa361 --- /dev/null +++ b/airootfs/usr/share/calamares/branding/default/banner.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2020 Adriaan de Groot +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/airootfs/usr/share/calamares/branding/default/branding.desc b/airootfs/usr/share/calamares/branding/default/branding.desc new file mode 100644 index 0000000..eed9802 --- /dev/null +++ b/airootfs/usr/share/calamares/branding/default/branding.desc @@ -0,0 +1,239 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Product branding information. This influences some global +# user-visible aspects of Calamares, such as the product +# name, window behavior, and the slideshow during installation. +# +# Additional styling can be done using the stylesheet.qss +# file, also in the branding directory. +--- +componentName: default + + +### WELCOME / OVERALL WORDING +# +# These settings affect some overall phrasing and looks, +# which are most visible in the welcome page. + +# This selects between different welcome texts. When false, uses +# the traditional "Welcome to the %1 installer.", and when true, +# uses "Welcome to the Calamares installer for %1." This allows +# to distinguish this installer from other installers for the +# same distribution. +welcomeStyleCalamares: false + +# Should the welcome image (productWelcome, below) be scaled +# up beyond its natural size? If false, the image does not grow +# with the window but remains the same size throughout (this +# may have surprising effects on HiDPI monitors). +welcomeExpandingLogo: true + +### WINDOW CONFIGURATION +# +# The settings here affect the placement of the Calamares +# window through hints to the window manager and initial +# sizing of the Calamares window. + +# Size and expansion policy for Calamares. +# - "normal" or unset, expand as needed, use *windowSize* +# - "fullscreen", start as large as possible, ignore *windowSize* +# - "noexpand", don't expand automatically, use *windowSize* +windowExpanding: normal + +# Size of Calamares window, expressed as w,h. Both w and h +# may be either pixels (suffix px) or font-units (suffix em). +# e.g. "800px,600px" +# "60em,480px" +# This setting is ignored if "fullscreen" is selected for +# *windowExpanding*, above. If not set, use constants defined +# in CalamaresUtilsGui, 800x520. +windowSize: 800px,520px + +# Placement of Calamares window. Either "center" or "free". +# Whether "center" actually works does depend on the window +# manager in use (and only makes sense if you're not using +# *windowExpanding* set to "fullscreen"). +windowPlacement: center + +### PANELS CONFIGURATION +# +# Calamares has a main content area, and two panels (navigation +# and progress / sidebar). The panels can be controlled individually, +# or switched off. If both panels are switched off, the layout of +# the main content area loses its margins, on the assumption that +# you're doing something special. + +# Kind of sidebar (panel on the left, showing progress). +# - "widget" or unset, use traditional sidebar (logo, items) +# - "none", hide it entirely +# - "qml", use calamares-sidebar.qml from branding folder +# In addition, you **may** specify a side, separated by a comma, +# from the kind. Valid sides are: +# - "left" (if not specified, uses this) +# - "right" +# - "top" +# - "bottom" +# For instance, "widget,right" is valid; so is "qml", which defaults +# to putting the sidebar on the left. Also valid is "qml,top". +# While "widget,top" is valid, the widgets code is **not** flexible +# and results will be terrible. +sidebar: widget + +# Kind of navigation (button panel on the bottom). +# - "widget" or unset, use traditional navigation +# - "none", hide it entirely +# - "qml", use calamares-navigation.qml from branding folder +# In addition, you **may** specify a side, separated by a comma, +# from the kind. The same sides are valid as for *sidebar*, +# except the default is *bottom*. +navigation: widget + + +### STRINGS, IMAGES AND COLORS +# +# This section contains the "branding proper" of names +# and images, rather than global-look settings. + +# These are strings shown to the user in the user interface. +# There is no provision for translating them -- since they +# are names, the string is included as-is. +# +# The four Url strings are the Urls used by the buttons in +# the welcome screen, and are not shown to the user. Clicking +# on the "Support" button, for instance, opens the link supportUrl. +# If a Url is empty, the corresponding button is not shown. +# +# bootloaderEntryName is how this installation / distro is named +# in the boot loader (e.g. in the GRUB menu). +# +# These strings support substitution from /etc/os-release +# if KDE Frameworks 5.58 are available at build-time. When +# enabled, ${varname} is replaced by the equivalent value +# from os-release. All the supported var-names are in all-caps, +# and are listed on the FreeDesktop.org site, +# https://www.freedesktop.org/software/systemd/man/os-release.html +# Note that ANSI_COLOR and CPE_NAME don't make sense here, and +# are not supported (the rest are). Remember to quote the string +# if it contains substitutions, or you'll get YAML exceptions. +# +# The *Url* entries are used on the welcome page, and they +# are visible as buttons there if the corresponding *show* keys +# are set to "true" (they can also be overridden). +strings: + productName: "${NAME}" + shortProductName: Generic + version: 2023.3 LTS + shortVersion: 2023.3 + versionedName: Fancy GNU/Linux 2023.3 LTS "Venomous Vole" + shortVersionedName: FancyGL 2023.3 + bootloaderEntryName: FancyGL + productUrl: https://calamares.io/ + supportUrl: https://codeberg.org/Calamares/calamares/wiki + knownIssuesUrl: https://codeberg.org/Calamares/calamares/issues + releaseNotesUrl: https://calamares.io/news/ + donateUrl: https://docs.codeberg.org/improving-codeberg/donate/ + +# These images are loaded from the branding module directory. +# +# productBanner is an optional image, which if present, will be shown +# on the welcome page of the application, above the welcome text. +# It is intended to have a width much greater than height. +# It is displayed at 64px height (also on HiDPI). +# Recommended size is 64px tall, and up to 460px wide. +# productIcon is used as the window icon, and will (usually) be used +# by the window manager to represent the application. This image +# should be square, and may be displayed by the window manager +# as small as 16x16 (but possibly larger). +# productLogo is used as the logo at the top of the left-hand column +# which shows the steps to be taken. The image should be square, +# and is displayed at 80x80 pixels (also on HiDPI). +# productWallpaper is an optional image, which if present, will replace +# the normal solid background on every page of the application. +# It can be any size and proportion, +# and will be tiled to fit the entire window. +# For a non-tiled wallpaper, the size should be the same as +# the overall window, see *windowSize* above (800x520). +# productWelcome is shown on the welcome page of the application in +# the middle of the window, below the welcome text. It can be +# any size and proportion, and will be scaled to fit inside +# the window. Use `welcomeExpandingLogo` to make it non-scaled. +# Recommended size is 320x150. +# +# These filenames can also use substitutions from os-release (see above). +images: + # productBanner: "banner.png" + productIcon: "squid.png" + productLogo: "squid.png" + # productWallpaper: "wallpaper.png" + productWelcome: "languages.png" + +# Colors for text and background components. +# +# - SidebarBackground is the background of the sidebar +# - SidebarText is the (foreground) text color +# - SidebarBackgroundCurrent sets the background of the current step. +# Optional, and defaults to the application palette. +# - SidebarTextCurrent is the text color of the current step. +# +# These colors can **also** be set through the stylesheet, if the +# branding component also ships a stylesheet.qss. Then they are +# the corresponding CSS attributes of #sidebarApp. +style: + SidebarBackground: "#292F34" + SidebarText: "#FFFFFF" + SidebarTextCurrent: "#292F34" + SidebarBackgroundCurrent: "#D35400" + +### SLIDESHOW +# +# The slideshow is displayed during execution steps (e.g. when the +# installer is actually writing to disk and doing other slow things). + +# The slideshow can be a QML file (recommended) which can display +# arbitrary things -- text, images, animations, or even play a game -- +# during the execution step. The QML **is** abruptly stopped when the +# execution step is done, though, so maybe a game isn't a great idea. +# +# The slideshow can also be a sequence of images (not recommended unless +# you don't want QML at all in your Calamares). The images are displayed +# at a rate of 1 every 2 seconds during the execution step. +# +# To configure a QML file, list a single filename: +# slideshow: "show.qml" +# To configure images, like the filenames (here, as an inline list): +# slideshow: [ "/etc/calamares/slideshow/0.png", "/etc/logo.png" ] +slideshow: "show.qml" + +# There are two available APIs for a QML slideshow: +# - 1 (the default) loads the entire slideshow when the installation- +# slideshow page is shown and starts the QML then. The QML +# is never stopped (after installation is done, times etc. +# continue to fire). +# - 2 loads the slideshow on startup and calls onActivate() and +# onLeave() in the root object. After the installation is done, +# the show is stopped (first by calling onLeave(), then destroying +# the QML components). +# +# An image slideshow does not need to have the API defined. +slideshowAPI: 2 + + +# These options are to customize online uploading of logs to pastebins: +# - type : Defines the kind of pastebin service to be used. Currently +# it accepts two values: +# - none : disables the pastebin functionality +# - fiche : use fiche pastebin server +# - url : Defines the address of pastebin service to be used. +# Takes string as input. Important bits are the host and port, +# the scheme is not used. +# - sizeLimit : Defines maximum size limit (in KiB) of log file to be pasted. +# The option must be set, to have the log option work. +# Takes integer as input. If < 0, no limit will be forced, +# else only last (approximately) 'n' KiB of log file will be pasted. +# Please note that upload size may be slightly over the limit (due +# to last minute logging), so provide a suitable value. +uploadServer : + type : "fiche" + url : "http://termbin.com:9999" + sizeLimit : -1 diff --git a/airootfs/usr/share/calamares/branding/default/lang/calamares-default_ar.qm b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_ar.qm new file mode 100644 index 0000000..23eaa2a Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_ar.qm differ diff --git a/airootfs/usr/share/calamares/branding/default/lang/calamares-default_en.qm b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_en.qm new file mode 100644 index 0000000..ce6afd2 Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_en.qm differ diff --git a/airootfs/usr/share/calamares/branding/default/lang/calamares-default_eo.qm b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_eo.qm new file mode 100644 index 0000000..4d85aa1 Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_eo.qm differ diff --git a/airootfs/usr/share/calamares/branding/default/lang/calamares-default_fr.qm b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_fr.qm new file mode 100644 index 0000000..ae8e52e Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_fr.qm differ diff --git a/airootfs/usr/share/calamares/branding/default/lang/calamares-default_nl.qm b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_nl.qm new file mode 100644 index 0000000..5e26cc8 Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/lang/calamares-default_nl.qm differ diff --git a/airootfs/usr/share/calamares/branding/default/languages.png b/airootfs/usr/share/calamares/branding/default/languages.png new file mode 100644 index 0000000..5331652 Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/languages.png differ diff --git a/airootfs/usr/share/calamares/branding/default/languages.png.license b/airootfs/usr/share/calamares/branding/default/languages.png.license new file mode 100644 index 0000000..ea82645 --- /dev/null +++ b/airootfs/usr/share/calamares/branding/default/languages.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2015 Teo Mrnjavac +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/airootfs/usr/share/calamares/branding/default/show.qml b/airootfs/usr/share/calamares/branding/default/show.qml new file mode 100644 index 0000000..f4c50e6 --- /dev/null +++ b/airootfs/usr/share/calamares/branding/default/show.qml @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2015 Teo Mrnjavac + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +import QtQuick 2.0; +import calamares.slideshow 1.0; + +Presentation +{ + id: presentation + + function nextSlide() { + console.log("QML Component (default slideshow) Next slide"); + presentation.goToNextSlide(); + } + + Timer { + id: advanceTimer + interval: 1000 + running: presentation.activatedInCalamares + repeat: true + onTriggered: nextSlide() + } + + Slide { + + Image { + id: background + source: "squid.png" + width: 200; height: 200 + fillMode: Image.PreserveAspectFit + anchors.centerIn: parent + } + Text { + anchors.horizontalCenter: background.horizontalCenter + anchors.top: background.bottom + text: "This is a customizable QML slideshow.
"+ + "Distributions should provide their own slideshow and list it in
"+ + "their custom branding.desc file.
"+ + "To create a Calamares presentation in QML, import calamares.slideshow,
"+ + "define a Presentation element with as many Slide elements as needed." + wrapMode: Text.WordWrap + width: presentation.width + horizontalAlignment: Text.Center + } + } + + Slide { + centeredText: qsTr("This is a second Slide element.") + } + + Slide { + centeredText: qsTr("This is a third Slide element.") + } + + // When this slideshow is loaded as a V1 slideshow, only + // activatedInCalamares is set, which starts the timer (see above). + // + // In V2, also the onActivate() and onLeave() methods are called. + // These example functions log a message (and re-start the slides + // from the first). + function onActivate() { + console.log("QML Component (default slideshow) activated"); + presentation.currentSlide = 0; + } + + function onLeave() { + console.log("QML Component (default slideshow) deactivated"); + } + +} diff --git a/airootfs/usr/share/calamares/branding/default/squid.png b/airootfs/usr/share/calamares/branding/default/squid.png new file mode 100644 index 0000000..452e445 Binary files /dev/null and b/airootfs/usr/share/calamares/branding/default/squid.png differ diff --git a/airootfs/usr/share/calamares/branding/default/squid.png.license b/airootfs/usr/share/calamares/branding/default/squid.png.license new file mode 100644 index 0000000..cc08e1f --- /dev/null +++ b/airootfs/usr/share/calamares/branding/default/squid.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: 2014 Teo Mrnjavac +SPDX-License-Identifier: GPL-3.0-or-later diff --git a/airootfs/usr/share/calamares/branding/default/stylesheet.qss b/airootfs/usr/share/calamares/branding/default/stylesheet.qss new file mode 100644 index 0000000..5c36738 --- /dev/null +++ b/airootfs/usr/share/calamares/branding/default/stylesheet.qss @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: no + * SPDX-License-Identifier: CC0-1.0 + */ + +/* +A branding component can ship a stylesheet (like this one) +which is applied to parts of the Calamares user-interface. +In principle, all parts can be styled through CSS. +Missing parts should be filed as issues. + +The IDs are based on the object names in the C++ code. +You can use the Debug Dialog to find out object names: + - Open the debug dialog + - Choose tab *Tools* + - Click *Widget Tree* button +The list of object names is printed in the log. + +Documentation for styling Qt Widgets through a stylesheet +can be found at + https://doc.qt.io/qt-5/stylesheet-examples.html + https://doc.qt.io/qt-5/stylesheet-reference.html +In Calamares, styling widget classes is supported (e.g. +using `QComboBox` as a selector). + +This example stylesheet has all the actual styling commented out. +The examples are not exhaustive. + +*/ + +/*** Generic Widgets. + * + * You can style **all** widgets of a given class by selecting + * the class name. Some widgets have specialized sub-selectors. + */ + +/* +QPushButton { background-color: green; } +*/ + +/*** Main application window. + * + * The main application window has the sidebar, which in turn + * contains a logo and a list of items -- note that the list + * can **not** be styled, since it has its own custom C++ + * delegate code. + */ + +/* +#mainApp { } +#sidebarApp { } +#logoApp { } +*/ + +/*** Welcome module. + * + * There are plenty of parts, but the buttons are the most interesting + * ones (donate, release notes, ...). The little icon image can be + * styled through *qproperty-icon*, which is a little obscure. + * URLs can reference the QRC paths of the Calamares application + * or loaded via plugins or within the filesystem. There is no + * comprehensive list of available icons, though. + */ + +/* +QPushButton#aboutButton { qproperty-icon: url(:/data/images/release.svg); } +#donateButton, +#supportButton, +#releaseNotesButton, +#knownIssuesButton { qproperty-icon: url(:/data/images/help.svg); } +*/ + +/*** Partitioning module. + * + * Many moving parts, which you will need to experiment with. + */ + +/* +#bootInfoIcon { } +#bootInfoLable { } +#deviceInfoIcon { } +#defineInfoLabel { } +#scrollAreaWidgetContents { } +#partitionBarView { } +*/ + +/*** Licensing module. + * + * The licensing module paints individual widgets for each of + * the licenses. The item can be collapsed or expanded. + */ + +/* +#licenseItem { } +#licenseItemFullText { } +*/ diff --git a/airootfs/usr/share/calamares/modules/bootloader.conf b/airootfs/usr/share/calamares/modules/bootloader.conf new file mode 100644 index 0000000..2105f27 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/bootloader.conf @@ -0,0 +1,86 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Bootloader configuration. The bootloader is installed to allow +# the system to start (and pick one of the installed operating +# systems to run). +# +# Take note that Debian-derivatives that use unmodified GRUB EFI packages +# should specifically set *efiBootloaderId* to "debian" because that is +# hard-coded in `grubx64.efi`. +--- +# A variable from global storage which overrides the value of efiBootLoader +#efiBootLoaderVar: "packagechooser_bootloader" + +# Define which bootloader you want to use for EFI installations +# Possible options are 'grub', 'sb-shim', 'refind` and 'systemd-boot'. +efiBootLoader: "grub" + +# systemd-boot configuration files settings + +# kernelSearchPath is the path relative to the root of the install to search for kernels +# A kernel is identified by finding files which match regular expression, kernelPattern +kernelSearchPath: "/usr/lib/modules" +kernelPattern: "^vmlinuz.*" + +# loaderEntries is an array of options to add to loader.conf for systemd-boot +# please note that the "default" option is added programmatically +loaderEntries: + - "timeout 5" + - "console-mode keep" + +# systemd-boot and refind support custom kernel params +kernelParams: [ "quiet" ] + +# A list of kernel names that refind should accept as kernels +#refindKernelList: [ "linux","linux-lts","linux-zen","linux-hardened" ] + +# GRUB 2 binary names and boot directory +# Some distributions (e.g. Fedora) use grub2-* (resp. /boot/grub2/) names. +# These names are also used when using sb-shim, since that needs some +# GRUB functionality (notably grub-probe) to work. As needed, you may use +# complete paths like `/usr/bin/efibootmgr` for the executables. +# +grubInstall: "grub-install" +grubMkconfig: "grub-mkconfig" +grubCfg: "/boot/grub/grub.cfg" +grubProbe: "grub-probe" +efiBootMgr: "efibootmgr" + +# Optionally set the bootloader ID to use for EFI. This is passed to +# grub-install --bootloader-id. +# +# If not set here, the value from bootloaderEntryName from branding.desc +# is used, with problematic characters (space and slash) replaced. +# +# The ID is also used as a directory name within the EFI environment, +# and the bootloader is copied from /boot/efi/EFI// . When +# setting the option here, keep in mind that the name is sanitized +# (problematic characters, see above, are replaced). +# +# There are some special words possible at the end of *efiBootloaderId*: +# ${SERIAL} can be used to obtain a uniquely-numbered suffix +# that is added to the Id (yielding, e.g., `dirname1` or `dirname72`) +# ${RANDOM} can be used to obtain a unique 4-digit hex suffix +# ${PHRASE} can be used to obtain a unique 1-to-3-word suffix +# from a dictionary of space-themed words +# These words must be at the **end** of the *efiBootloaderId* value. +# There must also be at most one of them. If there is none, no suffix- +# processing is done and the *efiBootloaderId* is used unchanged. +# +# NOTE: Debian derivatives that use the unmodified Debian GRUB EFI +# packages may need to set this to "debian" because that is +# hard-coded in `grubx64.efi`. +# +# efiBootloaderId: "dirname" + +# Optionally install a copy of the GRUB EFI bootloader as the EFI +# fallback loader (either bootia32.efi or bootx64.efi depending on +# the system). This may be needed on certain systems (Intel DH87MC +# seems to be the only one). If you set this to false, take care +# to add another module to optionally install the fallback on those +# boards that need it. +installEFIFallback: true + +# Optionally install both BIOS and UEFI GRUB bootloaders. +installHybridGRUB: false diff --git a/airootfs/usr/share/calamares/modules/contextualprocess.conf b/airootfs/usr/share/calamares/modules/contextualprocess.conf new file mode 100644 index 0000000..e5c1c1d --- /dev/null +++ b/airootfs/usr/share/calamares/modules/contextualprocess.conf @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the contextual process job. +# +# Contextual processes are based on **global** configuration values. +# When a given global value (string) equals a given value, then +# the associated command is executed. +# +# Configuration consists of keys for global variable names (except +# *dontChroot* and *timeout*), and the sub-keys are strings to compare +# to the variable's value. If the variable has that particular value, the +# corresponding value (script) is executed. The top-level keys *dontChroot* +# and *timeout* are not global variable names. They have +# meaning just like in shellprocess.conf, that is they +# determine **where** the command runs and how long it has. +# +# The variable **may** contain dots, in which case the dot is used +# to select into maps inside global storage, e.g. +# +# - *firmwareType* is a simple global name +# - *branding.bootloader* is the *bootloader* value in the *branding* map +# +# Only a few global storage entries have well-defined sub-maps; +# branding is one of them, and *filesystem_use* is another. Note that +# variable names with dots **must** be quoted, or you will get a YAML error. +# +# +# You can check for an empty value with "". +# +# As a special case, the value-check "*" matches any value, but **only** +# if no other value-check matches. Use it as an *else* form for value- +# checks. Take care to put the asterisk in quotes. The value-check "*" +# **also** matches a literal asterisk as value; a confusing corner case +# is checking for an asterisk **and** having a wildcard match with +# different commands. This is currently not possible. +# +# Global configuration variables are not checked in a deterministic +# order, so do not rely on commands from one variable-check to +# always happen before (or after) checks on another +# variable. Similarly, the value-equality checks are not +# done in a deterministic order, but all of the value-checks +# for a given variable happen together. As a special case, the +# value-check for "*" (the *else* case) happens after all of the +# other value-checks, and only matches if none of the others do. +# +# The values after a value sub-keys are the same kinds of values +# as can be given to the *script* key in the shellprocess module. +# See shellprocess.conf for documentation on valid values and how +# variables are expanded in those commands. +--- +dontChroot: false +firmwareType: + efi: + - "-pkg remove efi-firmware" + - command: "-mkinitramfsrd -abgn" + timeout: 120 # This is slow + bios: "-pkg remove bios-firmware" + "": "/bin/false no-firmware-type-set" + "*": "/bin/false some-other-firmware-value" +"branding.shortVersion": + "2020.2": "/bin/false february" + "2019.4": "/bin/true april" diff --git a/airootfs/usr/share/calamares/modules/displaymanager.conf b/airootfs/usr/share/calamares/modules/displaymanager.conf new file mode 100644 index 0000000..d0a6a35 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/displaymanager.conf @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configure one or more display managers (e.g. SDDM) +# with a "best effort" approach. +# +# This module also sets up autologin, if the feature is enabled in +# globalstorage (where it would come from the users page). +--- +# The DM module attempts to set up all the DMs found in this list, in the +# precise order listed. The displaymanagers list can also be set in +# globalstorage, and in that case it overrides the setting here. +# +# If *sysconfigSetup* is set to *true* (see below, only relevant for +# openSUSE derivatives) then this list is ignored and only sysconfig +# is attempted. You can also list "sysconfig" in this list instead. +# +displaymanagers: + - slim + - sddm + - lightdm + - gdm + - mdm + - lxdm + - greetd + +# Enable the following settings to force a desktop environment +# in your displaymanager configuration file. This will attempt +# to configure the given DE (without checking if it is installed). +# The DM configuration for each potential DM may **or may not** +# support configuring a default DE, so the keys are mandatory +# but their interpretation is up to the DM configuration. +# +# Subkeys of *defaultDesktopEnvironment* are (all mandatory): +# - *executable* a full path to an executable +# - *desktopFile* a .desktop filename +# +# If this is **not** set, then Calamares will look for installed +# DE's and pick the first one it finds that is actually installed. +# +# If this **is** set, and the *executable* key doesn't point to +# an installed file, then the .desktop file's TryExec key is +# used instead. +# + +#defaultDesktopEnvironment: +# executable: "startkde" +# desktopFile: "plasma" + +#If true, try to ensure that the user, group, /var directory etc. for the +#display manager are set up correctly. This is normally done by the distribution +#packages, and best left to them. Therefore, it is disabled by default. +basicSetup: false + +# If true, setup autologin for openSUSE. This only makes sense on openSUSE +# derivatives or other systems where /etc/sysconfig/displaymanager exists. +# +# The preferred way to pick sysconfig is to just list it in the +# *displaymanagers* list (as the only one). +# +sysconfigSetup: false + +# Some DMs have specific settings. These can be customized here. +# +# greetd has configurable user and group; the user and group is created if it +# does not exist, and the user is set as default-session user. +# +# Some greeters for greetd (e.g gtkgreet or regreet) have support for a user's GTK CSS style to change appearance. +# +# lightdm has a list of greeters to look for, preferring them in order if +# they are installed (if not, picks the alphabetically first greeter that is installed). +# +greetd: + greeter_user: "tom_bombadil" + greeter_group: "wheel" + greeter_css_location: "/etc/greetd/style.css" +lightdm: + preferred_greeters: ["lightdm-greeter.desktop", "slick-greeter.desktop"] +sddm: + configuration_file: "/etc/sddm.conf" diff --git a/airootfs/usr/share/calamares/modules/finished.conf b/airootfs/usr/share/calamares/modules/finished.conf new file mode 100644 index 0000000..7abfb36 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/finished.conf @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the "finished" page, which is usually shown only at +# the end of the installation (successful or not). +--- +# DEPRECATED +# +# The finished page can hold a "restart system now" checkbox. +# If this is false, no checkbox is shown and the system is not restarted +# when Calamares exits. +# restartNowEnabled: true + +# DEPRECATED +# +# Initial state of the checkbox "restart now". Only relevant when the +# checkbox is shown by restartNowEnabled. +# restartNowChecked: false + +# Behavior of the "restart system now" button. +# +# There are four usable values: +# - never +# Does not show the button and does not restart. +# This matches the old behavior with restartNowEnabled=false. +# - user-unchecked +# Shows the button, defaults to unchecked, restarts if it is checked. +# This matches the old behavior with restartNowEnabled=true and restartNowChecked=false. +# - user-checked +# Shows the button, defaults to checked, restarts if it is checked. +# This matches the old behavior with restartNowEnabled=true and restartNowChecked=true. +# - always +# Shows the button, checked, but the user cannot change it. +# This is new behavior. +# +# The three combinations of legacy values are still supported. +restartNowMode: user-unchecked + +# If the checkbox is shown, and the checkbox is checked, then when +# Calamares exits from the finished-page it will run this command. +# If not set, falls back to "shutdown -r now". +restartNowCommand: "systemctl -i reboot" + +# When the last page is (successfully) reached, send a DBus notification +# to the desktop that the installation is done. This works only if the +# user as whom Calamares is run, can reach the regular desktop session bus. +notifyOnFinished: false diff --git a/airootfs/usr/share/calamares/modules/finishedq.conf b/airootfs/usr/share/calamares/modules/finishedq.conf new file mode 100644 index 0000000..ee226c3 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/finishedq.conf @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the "finishedq" page, which is usually shown only at +# the end of the installation (successful or not). +# +# See the documentation for the "finished" module for a full explanation +# of the configuration options; the description here applies primarily +# to the use that the QML makes of them. +--- +# Behavior of the "restart system now" button. +# +# The example QML for this module offers a "Restart Now" button, +# which the user can click on. It calls directly to the restart +# function. If the user closes the installer in some other way, +# (the "Done" button or close-window) a restart **might** happen: +# +# - never +# Do not restart (this will also block the "Restart Now" button, +# so it is not very useful) +# - user-unchecked +# Do not restart on other ways of closing the window. No checkbox +# is shown in the example QML, so there is no way for the user to +# express a choice -- except by clicking the "Restart Now" button. +# - user-checked +# Do restart on other ways of closing the window. This makes close +# and "Restart Now" do the same thing. No checkbox is shown by the QML, +# so the machine will **always** restart. +# - always +# Same as above. +# +# For the **specific** example QML included with this module, only +# *user-unchecked* really makes sense. +restartNowMode: user-unchecked +restartNowCommand: "systemctl -i reboot" +notifyOnFinished: false diff --git a/airootfs/usr/share/calamares/modules/fsresizer.conf b/airootfs/usr/share/calamares/modules/fsresizer.conf new file mode 100644 index 0000000..e58c398 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/fsresizer.conf @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Module that resizes a single FS to fill the entire (rest) of +# a device. This is used in OEM situations where an image is +# flashed onto an SD card (or similar) and used to boot a device, +# after which the FS should expand to fill the SD card. +# +# Example: a distro produces a 6GiB large image that is +# written to an 8GiB SD card; the FS should expand to take +# advantage of the unused 2GiB. The FS should expand much +# more if the same image is written to a 16GiB card. +--- + +# Which FS needs to be grown? Choose one way to identify it: +# - *fs* names a mount point which should already be mounted +# in the system. +# - *dev* names a device +fs: / +# dev: /dev/mmcblk0p1 + +# How much of the total remaining space should the FS use? +# The only sensible amount is "all of it". The value is +# in percent, so set it to 100. Perhaps a fixed size is +# needed (that would be weird though, since you don't know +# how big the card is), use MiB as suffix in that case. +# If missing, then it's assumed to be 0, and no resizing +# will happen. +# +# Percentages apply to **available space**. +size: 100% + +# Resizing might not be worth it, though. Set the minimum +# that it must grow; if it cannot grow that much, the +# resizing is skipped. Can be in percentage or absolute +# size, as above. If missing, then it's assumed to be 0, +# which means resizing is always worthwhile. +# +# If *atleast* is not zero, then the setting *required*, +# below, becomes relevant. +# +# Percentages apply to **total device size**. +#atleast: 1000MiB + +# When *atleast* is not zero, then the resize may be +# recommended (the default) or **required**. If the +# resize is required and cannot be carried out (because +# there's not enough space), then that is a fatal +# error for the installer. By default, resize is only +# recommended and it is not an error for no resize to be +# carried out. +required: false diff --git a/airootfs/usr/share/calamares/modules/fstab.conf b/airootfs/usr/share/calamares/modules/fstab.conf new file mode 100644 index 0000000..5c5c566 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/fstab.conf @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Creates /etc/fstab and /etc/crypttab in the target system. +# Also creates mount points for all the filesystems. +# +# When creating fstab entries for a filesystem, this module +# uses the options previously defined in the mount module +--- + +# Additional options added to each line in /etc/crypttab +crypttabOptions: luks +# For Debian and Debian-based distributions, change the above line to: +# crypttabOptions: luks,keyscript=/bin/cat + +# Options for handling /tmp in /etc/fstab +# Currently default (required) and ssd are supported +# The corresponding string can contain the following variables: +# tmpfs: true or tmpfs: false to either mount /tmp as tmpfs or not +# options: "" +# +# Example: +#tmpOptions: +# default: +# tmpfs: false +# options: "" +# ssd: +# tmpfs: true +# options: "defaults,noatime,mode=1777" +# +tmpOptions: + default: + tmpfs: false + options: "" + ssd: + tmpfs: true + options: "defaults,noatime,mode=1777" diff --git a/airootfs/usr/share/calamares/modules/grubcfg.conf b/airootfs/usr/share/calamares/modules/grubcfg.conf new file mode 100644 index 0000000..2212d27 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/grubcfg.conf @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Create, overwrite or update /etc/default/grub in the target system. +# +# Write lines to /etc/default/grub (in the target system) based +# on calculated values and the values set in the *defaults* key +# in this configuration file. +# +# Calculated values are: +# - GRUB_DISTRIBUTOR, branding module, *bootloaderEntryName* (this +# string is sanitized, and see also setting *keep_distributor*) +# - GRUB_ENABLE_CRYPTODISK, based on the presence of filesystems +# that use LUKS +# - GRUB_CMDLINE_LINUX_DEFAULT, adding LUKS setup and plymouth +# support to the kernel. + +--- +# If set to true, always creates /etc/default/grub from scratch even if the file +# already existed. If set to false, edits the existing file instead. +overwrite: false + +# If set to true, prefer to write files in /etc/default/grub.d/ +# rather than the single file /etc/default/grub. If this is set, +# Calamares will write /etc/default/grub.d/00calamares.cfg instead. +prefer_grub_d: false + +# If set to true, an **existing** setting for GRUB_DISTRIBUTOR is +# kept, not updated to the *bootloaderEntryName* from the branding file. +# Use this if the GRUB_DISTRIBUTOR setting in the file is "smart" in +# some way (e.g. uses shell-command substitution). +keep_distributor: false + +# The default kernel params that should always be applied. +# This is an array of strings. If it is unset, the default is +# `["quiet"]`. To avoid the default, explicitly set this key +# to an empty list, `[]`. +kernel_params: [ "quiet" ] + +# Default entries to write to /etc/default/grub if it does not exist yet or if +# we are overwriting it. +# +defaults: + GRUB_TIMEOUT: 5 + GRUB_DEFAULT: "saved" + GRUB_DISABLE_SUBMENU: true + GRUB_TERMINAL_OUTPUT: "console" + GRUB_DISABLE_RECOVERY: true + +# Set to true to force defaults to be used even when not overwriting +always_use_defaults: false diff --git a/airootfs/usr/share/calamares/modules/initcpio.conf b/airootfs/usr/share/calamares/modules/initcpio.conf new file mode 100644 index 0000000..d2a1268 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/initcpio.conf @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Run mkinitcpio(8) with the given preset value +--- +# This key defines the kernel to be loaded. +# It can have the following values: +# - the name of a single mkinitcpio preset +# - empty or unset +# - the literal string "all" +# +# If kernel is set to "all" or empty/unset then mkinitpio is called for all +# kernels. Otherwise it is called with a single preset with the value +# contained in kernel. +# +kernel: linux + +# Set this to true to turn off mitigations for lax file +# permissions on initramfs (which, in turn, can compromise +# your LUKS encryption keys, CVS-2019-13179). +# +# If your initramfs are stored in the EFI partition or another non-POSIX +# filesystem, this has no effect as the file permissions cannot be changed. +# In this case, ensure the partition is mounted securely. +# +be_unsafe: false diff --git a/airootfs/usr/share/calamares/modules/initcpiocfg.conf b/airootfs/usr/share/calamares/modules/initcpiocfg.conf new file mode 100644 index 0000000..a660393 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/initcpiocfg.conf @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The initcpiocfg module is responsible for the configuration of mkinitcpio.conf. Typically this +# module is used in conjunction with the initcpio module to generate the boot image when using mkinitcpio +--- +# +# Determines if the systemd versions of the hooks should be used. This is false by default. +# +# Please note that using the systemd hooks result in no access to the emergency recovery shell +useSystemdHook: false + +# +# Modifications to the standard list of hooks. +# +# There are three subkeys: +# - prepend, which puts hooks at the beginning of the +# list of hooks, in the order specified here, +# - append, which adds hooks at the end of the list of +# hooks, in the order specified here, +# - remove, which removes hooks from the list of hooks, +# wherever they may be. +# +# The example configuration here yields bogus, , bogus +# initially, and then removes that hook again. +# +hooks: + prepend: [ bogus ] + append: [ bogus ] + remove: [ bogus ] + +# +# In some cases, you may want to use a different source +# file than /etc/mkinitcpio.conf , e.g. because the live system +# does not match the target in a useful way. If unset or +# empty, defaults to /etc/mkinitcpio.conf +# +source: "/etc/mkinitcpio.conf" diff --git a/airootfs/usr/share/calamares/modules/initramfs.conf b/airootfs/usr/share/calamares/modules/initramfs.conf new file mode 100644 index 0000000..d2a1268 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/initramfs.conf @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Run mkinitcpio(8) with the given preset value +--- +# This key defines the kernel to be loaded. +# It can have the following values: +# - the name of a single mkinitcpio preset +# - empty or unset +# - the literal string "all" +# +# If kernel is set to "all" or empty/unset then mkinitpio is called for all +# kernels. Otherwise it is called with a single preset with the value +# contained in kernel. +# +kernel: linux + +# Set this to true to turn off mitigations for lax file +# permissions on initramfs (which, in turn, can compromise +# your LUKS encryption keys, CVS-2019-13179). +# +# If your initramfs are stored in the EFI partition or another non-POSIX +# filesystem, this has no effect as the file permissions cannot be changed. +# In this case, ensure the partition is mounted securely. +# +be_unsafe: false diff --git a/airootfs/usr/share/calamares/modules/keyboard.conf b/airootfs/usr/share/calamares/modules/keyboard.conf new file mode 100644 index 0000000..b4850b2 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/keyboard.conf @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# NOTE: you must have ckbcomp installed and runnable +# on the live system, for keyboard layout previews. +--- +# The name of the file to write X11 keyboard settings to +# The default value is the name used by upstream systemd-localed. +# Relative paths are assumed to be relative to /etc/X11/xorg.conf.d +xOrgConfFileName: "/etc/X11/xorg.conf.d/00-keyboard.conf" + +# The path to search for keymaps converted from X11 to kbd format. +# Common paths for this are: +# - /lib/kbd/keymaps/xkb +# - /usr/share/kbd/keymaps/xkb +# Leave this empty if the setting does not make sense on your distribution. +# +convertedKeymapPath: "/lib/kbd/keymaps/xkb" + +# Write keymap configuration to /etc/default/keyboard, usually +# found on Debian-related systems. +# Defaults to true if nothing is set. +#writeEtcDefaultKeyboard: true + +# Use the Locale1 service instead of directly managing configuration files. +# This is the modern mechanism for configuring the systemwide keyboard layout, +# and works on Wayland compositors to set the current layout. +# Defaults to false on X11 and true otherwise. +#useLocale1: true + +# 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 + # Configure keyboard when using Wayland with Gnome on Ubuntu 24.10+ + gnome: false diff --git a/airootfs/usr/share/calamares/modules/keyboardq.conf b/airootfs/usr/share/calamares/modules/keyboardq.conf new file mode 100644 index 0000000..d122f30 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/keyboardq.conf @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# NOTE: you must have ckbcomp installed and runnable +# on the live system, for keyboard layout previews. +--- +# The name of the file to write X11 keyboard settings to +# The default value is the name used by upstream systemd-localed. +# Relative paths are assumed to be relative to /etc/X11/xorg.conf.d +xOrgConfFileName: "/etc/X11/xorg.conf.d/00-keyboard.conf" + +# The path to search for keymaps converted from X11 to kbd format +# Leave this empty if the setting does not make sense on your distribution. +convertedKeymapPath: "/lib/kbd/keymaps/xkb" + +# Write keymap configuration to /etc/default/keyboard, usually +# found on Debian-related systems. +# Defaults to true if nothing is set. +#writeEtcDefaultKeyboard: true diff --git a/airootfs/usr/share/calamares/modules/license.conf b/airootfs/usr/share/calamares/modules/license.conf new file mode 100644 index 0000000..e32d499 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/license.conf @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration file for License viewmodule, Calamares +# Syntax is YAML 1.2 +--- +# Define a list of licenses which may / must be accepted before continuing. +# +# Each entry in this list has the following keys: +# - id Entry identifier, must be unique. Not user visible. YAML: string. +# - name Pretty name for the software product, user visible and untranslatable. YAML: string. +# - vendor Pretty name for the software vendor, user visible and untranslatable. YAML: string, optional, default is empty. +# - type Package type identifier for presentation, not user visible but affects user visible strings. YAML: string. +# values: driver, gpudriver, browserplugin, codec, package, software; optional, default is software. +# - required If set to true, the user cannot proceed without accepting this license. YAML: boolean, optional, default is false. +# - url A URL for the license; a remote URL is not shown in Calamares, but a link +# to the URL is provided, which opens in the default web browser. A local +# URL (i.e. file:///) assumes that the contents are HTML or plain text, and +# displays the license in-line. YAML: string, mandatory. +# - expand A boolean value only relevant for **local** URLs. If true, +# the license text is displayed in "expanded" form by +# default, rather than requiring the user to first open it up. +# YAML: boolean, optional, default is false. +entries: +- id: nvidia + name: Nvidia + vendor: Nvidia Corporation + type: driver + url: http://developer.download.nvidia.com/cg/Cg_3.0/license.pdf + required: false +- id: amd + name: Catalyst + vendor: "Advanced Micro Devices, Inc." + type: gpudriver + url: http://support.amd.com/en-us/download/eula + required: false +- id: flashplugin + name: Adobe Flash + vendor: Adobe Systems Incorporated + type: browserplugin + url: http://www.adobe.com/products/eulas/pdfs/PlatformClients_PC_WWEULA_Combined_20100108_1657.pdf + required: true +# This example uses a file: link. This example uses a relative link, which +# is relative to where you run Calamares. Assuming you run it from build/ +# as part of your testing, you'll get the LICENSE text for Calamares +# (which is the text of the GPLv3, not proprietary at all). +- id: mine_mine + name: Calamares Proprietary License + vendor: Calamares, Inc. + type: software + required: true + url: file:../LICENSE + expand: true diff --git a/airootfs/usr/share/calamares/modules/locale.conf b/airootfs/usr/share/calamares/modules/locale.conf new file mode 100644 index 0000000..4463f7a --- /dev/null +++ b/airootfs/usr/share/calamares/modules/locale.conf @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +# These settings are used to set your default system time zone. +# Time zones are usually located under /usr/share/zoneinfo and +# provided by the 'tzdata' package of your Distribution. +# +# Distributions using systemd can list available +# time zones by using the timedatectl command. +# timedatectl list-timezones +# +# The starting timezone (e.g. the pin-on-the-map) when entering +# the locale page can be set through keys *region* and *zone*. +# If either is not set, defaults to America/New_York. +# +# Note that useSystemTimezone and GeoIP settings can change the +# starting time zone. +# +region: "America" +zone: "New_York" + +# Instead of using *region* and *zone* specified above, +# you can use the system's notion of the timezone, instead. +# This can help if your system is automatically configured with +# a sensible TZ rather than chasing a fixed default. +# +# The default is false. +# +# useSystemTimezone: true + +# Should changing the system location (e.g. clicking around on the timezone +# map) immediately reflect the changed timezone in the live system? +# By default, installers (with a target system) do, and setup (e.g. OEM +# configuration) does not, but you can switch it on here (or off, if +# you think it's annoying in the installer). +# +# Note that not all systems support live adjustment. +# +# adjustLiveTimezone: true + +# System locales are detected in the following order: +# +# - /usr/share/i18n/SUPPORTED +# - localeGenPath (defaults to /etc/locale.gen if not set) +# - `locale -a` output +# +# Enable only when your Distribution is using a +# custom path for locale.gen +# +#localeGenPath: "/etc/locale.gen" + +# GeoIP based Language settings: Leave commented out to disable GeoIP. +# +# GeoIP needs a working Internet connection. +# This can be managed from `welcome.conf` by adding +# internet to the list of required conditions. (The welcome +# module can also do its own GeoIP lookups, independently +# of the lookup done here. The lookup in the welcome module +# is used to establish language; this one is for timezone). +# +# The configuration is in three parts: +# - a *style*, which can be "json" or "xml" depending on the +# kind of data returned by the service, and +# - a *url* where the data is retrieved, and +# - an optional *selector* +# to pick the right field out of the returned data (e.g. field +# name in JSON or element name in XML). +# +# The default selector (when the setting is blank) is picked to +# work with existing JSON providers (which use "time_zone") and +# Ubiquity's XML providers (which use "TimeZone"). +# +# If the service configured via *url* uses +# a different attribute name (e.g. "timezone") in JSON or a +# different element tag (e.g. "") in XML, set the +# selector to the name or tag to be used. +# +# In JSON: +# - if the string contains "." characters, this is used as a +# multi-level selector, e.g. "a.b" will select the timezone +# from data "{a: {b: "Europe/Amsterdam" } }". +# - each part of the string split by "." characters is used as +# a key into the JSON data. +# In XML: +# - all elements with the named tag (e.g. all TimeZone) elements +# from the document are checked; the first one with non-empty +# text value is used. +# Special case: +# - the *style* "fixed" is also supported. This ignores the data +# returned from the URL (but the URL must still be valid!) +# and just returns the value of the *selector*. +# +# An HTTP(S) request is made to *url*. The request should return +# valid data in a suitable format, depending on *style*; +# generally this includes a string value with the timezone +# in / format. For services that return data which +# does not follow the conventions of "suitable data" described +# below, *selector* may be used to pick different data. +# +# Suitable JSON data looks like +# ``` +# {"time_zone":"America/New_York"} +# ``` +# Suitable XML data looks like +# ``` +# Europe/Brussels +# ``` +# +# To accommodate providers of GeoIP timezone data with peculiar timezone +# naming conventions, the following cleanups are performed automatically: +# - backslashes are removed +# - spaces are replaced with _ +# +# To disable GeoIP checking, either comment-out the entire geoip section, +# or set the *style* key to an unsupported format (e.g. `none`). +# Also, note the analogous feature in src/modules/welcome/welcome.conf. +# +geoip: + style: "json" + url: "https://geoip.kde.org/v1/calamares" + selector: "" # leave blank for the default + +# For testing purposes, you could use *fixed* style, to see how Calamares +# behaves in a particular zone: +# +# geoip: +# style: "fixed" +# url: "https://geoip.kde.org/v1/calamares" # Still needs to be valid! +# selector: "America/Vancouver" # this is the selected zone +# diff --git a/airootfs/usr/share/calamares/modules/localeq.conf b/airootfs/usr/share/calamares/modules/localeq.conf new file mode 100644 index 0000000..bb2a7e8 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/localeq.conf @@ -0,0 +1,100 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +# This settings are used to set your default system time zone. +# Time zones are usually located under /usr/share/zoneinfo and +# provided by the 'tzdata' package of your Distribution. +# +# Distributions using systemd can list available +# time zones by using the timedatectl command. +# timedatectl list-timezones +# +# The starting timezone (e.g. the pin-on-the-map) when entering +# the locale page can be set through keys *region* and *zone*. +# If either is not set, defaults to America/New_York. +# +region: "America" +zone: "New_York" + + +# System locales are detected in the following order: +# +# - /usr/share/i18n/SUPPORTED +# - localeGenPath (defaults to /etc/locale.gen if not set) +# - 'locale -a' output +# +# Enable only when your Distribution is using an +# custom path for locale.gen +# +#localeGenPath: "PATH_TO/locale.gen" + +# GeoIP based Language settings: Leave commented out to disable GeoIP. +# +# GeoIP needs a working Internet connection. +# This can be managed from `welcome.conf` by adding +# internet to the list of required conditions. +# +# The configuration +# is in three parts: a *style*, which can be "json" or "xml" +# depending on the kind of data returned by the service, and +# a *url* where the data is retrieved, and an optional *selector* +# to pick the right field out of the returned data (e.g. field +# name in JSON or element name in XML). +# +# The default selector (when the setting is blank) is picked to +# work with existing JSON providers (which use "time_zone") and +# Ubiquity's XML providers (which use "TimeZone"). +# +# If the service configured via *url* uses +# a different attribute name (e.g. "timezone") in JSON or a +# different element tag (e.g. "") in XML, set this +# string to the name or tag to be used. +# +# In JSON: +# - if the string contains "." characters, this is used as a +# multi-level selector, e.g. "a.b" will select the timezone +# from data "{a: {b: "Europe/Amsterdam" } }". +# - each part of the string split by "." characters is used as +# a key into the JSON data. +# In XML: +# - all elements with the named tag (e.g. all TimeZone) elements +# from the document are checked; the first one with non-empty +# text value is used. +# +# +# An HTTP(S) request is made to *url*. The request should return +# valid data in a suitable format, depending on *style*; +# generally this includes a string value with the timezone +# in / format. For services that return data which +# does not follow the conventions of "suitable data" described +# below, *selector* may be used to pick different data. +# +# Note that this example URL works, but the service is shutting +# down in June 2018. +# +# Suitable JSON data looks like +# ``` +# {"time_zone":"America/New_York"} +# ``` +# Suitable XML data looks like +# ``` +# Europe/Brussels +# ``` +# +# To accommodate providers of GeoIP timezone data with peculiar timezone +# naming conventions, the following cleanups are performed automatically: +# - backslashes are removed +# - spaces are replaced with _ +# +# Legacy settings "geoipStyle", "geoipUrl" and "geoipSelector" +# in the top-level are still supported, but I'd advise against. +# +# To disable GeoIP checking, either comment-out the entire geoip section, +# or set the *style* key to an unsupported format (e.g. `none`). +# Also, note the analogous feature in src/modules/welcome/welcome.conf. +# +geoip: + style: "json" + url: "https://geoip.kde.org/v1/calamares" + selector: "" # leave blank for the default diff --git a/airootfs/usr/share/calamares/modules/luksbootkeyfile.conf b/airootfs/usr/share/calamares/modules/luksbootkeyfile.conf new file mode 100644 index 0000000..477d0e3 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/luksbootkeyfile.conf @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Luksbootkeyfile configuration. A key file is created for the +# LUKS encrypted devices. +--- +# Set Password-Based Key Derivation Function (PBKDF) algorithm +# for LUKS keyslot. +# +# There are three usable specific values: pbkdf2, argon2i or argon2id. +# There is one value equivalent to not setting it: default +# +# When not set (or explicitly set to "default"), the cryptsetup default is used +luks2Hash: default diff --git a/airootfs/usr/share/calamares/modules/luksopenswaphookcfg.conf b/airootfs/usr/share/calamares/modules/luksopenswaphookcfg.conf new file mode 100644 index 0000000..f1f03bb --- /dev/null +++ b/airootfs/usr/share/calamares/modules/luksopenswaphookcfg.conf @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Writes an openswap configuration with LUKS settings to the given path +--- +# Path of the configuration file to write (in the target system) +configFilePath: /etc/openswap.conf diff --git a/airootfs/usr/share/calamares/modules/machineid.conf b/airootfs/usr/share/calamares/modules/machineid.conf new file mode 100644 index 0000000..6a45234 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/machineid.conf @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Machine-ID and other random data on the target system. +# +# This module can create a number of "random" things on the target: +# - a systemd machine-id file (hence the name of the Calamares module) +# with a random UUID. +# - a dbus machine-id file (or, optionally, link to the one from systemd) +# - an entropy file +# +--- +# Whether to create /etc/machine-id for systemd. +# The default is *false*. +systemd: true +# If systemd is true, the kind of /etc/machine-id to create in the target +# - uuid (default) generates a UUID +# - systemd alias of uuid +# - blank creates the file but leaves it empty at 0 bytes +# - none alias of blank (use `systemd: false` if you don't want one at all) +# - literal-uninitialized creates the file and writes the string "uninitialized\n" +systemd-style: uuid + +# Whether to create /var/lib/dbus/machine-id for D-Bus. +# The default is *false*. +dbus: true +# Whether /var/lib/dbus/machine-id should be a symlink to /etc/machine-id +# (ignored if dbus is false, or if there is no /etc/machine-id to point to). +# The default is *false*. +dbus-symlink: true + +# Copy entropy from the host? If this is set to *true*, then +# any entropy file listed below will be copied from the host +# if it exists. Non-existent files will be generated from +# /dev/urandom . The default is *false*. +entropy-copy: false +# Which files to write (paths in the target). Each of these files is +# either generated from /dev/urandom or copied from the host, depending +# on the setting for *entropy-copy*, above. +entropy-files: + - /var/lib/urandom/random-seed + - /var/lib/systemd/random-seed + +# Whether to create an entropy file /var/lib/urandom/random-seed +# +# DEPRECATED: list the file in entropy-files instead. If this key +# exists and is set to *true*, a warning is printed and Calamares +# behaves as if `/var/lib/urandom/random-seed` is listed in *entropy-files*. +# +# entropy: false + +# Whether to create a symlink for D-Bus +# +# DEPRECATED: set *dbus-symlink* with the same meaning instead. +# +# symlink: false diff --git a/airootfs/usr/share/calamares/modules/mount.conf b/airootfs/usr/share/calamares/modules/mount.conf new file mode 100644 index 0000000..da95395 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/mount.conf @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Mount filesystems in the target (generally, before treating the +# target as a usable chroot / "live" system). Filesystems are +# automatically mounted from the partitioning module. Filesystems +# listed here are **extra**. The filesystems listed in *extraMounts* +# are mounted in all target systems. +--- +# Extra filesystems to mount. The key's value is a list of entries; each +# entry has five keys: +# - device The device node to mount +# - fs (optional) The filesystem type to use +# - mountPoint Where to mount the filesystem +# - options (optional) An array of options to pass to mount +# - efi (optional) A boolean that when true is only mounted for UEFI installs +# +# The device is not mounted if the mountPoint is unset or if the fs is +# set to unformatted. +# +extraMounts: + - device: proc + fs: proc + mountPoint: /proc + - device: sys + fs: sysfs + mountPoint: /sys + - device: /dev + mountPoint: /dev + options: [ bind ] + - device: tmpfs + fs: tmpfs + mountPoint: /run + - device: /run/udev + mountPoint: /run/udev + options: [ bind ] + - device: efivarfs + fs: efivarfs + mountPoint: /sys/firmware/efi/efivars + efi: true + +# Btrfs subvolumes to create if root filesystem is on btrfs volume. +# If *mountpoint* is mounted already to another partition, it is ignored. +# Separate subvolume for swapfile is handled separately and automatically. +# +# It is possible to prevent subvolume creation -- this is likely only relevant +# for the root (/) subvolume -- by giving an empty string as a subvolume +# name. In this case no subvolume will be created. +# +btrfsSubvolumes: + - mountPoint: / + subvolume: /@ + # As an alternative: + # + # subvolume: "" + - mountPoint: /home + subvolume: /@home + - mountPoint: /var/cache + subvolume: /@cache + - mountPoint: /var/log + subvolume: /@log + +# The name of the btrfs subvolume holding the swapfile. This only used when +# a swapfile is selected and the root filesystem is btrfs +# +btrfsSwapSubvol: /@swap + +# The mount options used to mount each filesystem. +# +# filesystem contains the name of the filesystem or on of three special +# values, "default", efi" and "btrfs_swap". The logic is applied in this manner: +# - If the partition is the EFI partition, the "efi" entry will be used +# - If the fs is btrfs and the subvolume is for the swapfile, +# the "btrfs_swap" entry is used +# - If the filesystem is an exact match for filesystem, that entry is used +# - If no match is found in the above, the default entry is used +# - If there is no match and no default entry, "defaults" is used +# - If the mountOptions key is not present, "defaults" is used +# +# Each filesystem entry contains 3 keys, all of which are optional +# options - An array of mount options that is used on all disk types +# ssdOptions - An array of mount options combined with options for ssds +# hddOptions - An array of mount options combined with options for hdds +# If combining these options results in an empty array, "defaults" is used +# +# Example 1 +# In this example, there are specific options for ext4 and btrfs filesystems, +# the EFI partition and the subvolume holding the btrfs swapfile. All other +# filesystems use the default entry. For the btrfs filesystem, there are +# additional options specific to hdds and ssds +# +# mountOptions: +# - filesystem: default +# options: [ defaults ] +# - filesystem: efi +# options: [ defaults, umask=0077 ] +# - filesystem: ext4 +# options: [ defaults ] +# - filesystem: btrfs +# options: [ defaults, compress=zstd:1 ] +# ssdOptions: [ discard=async ] +# hddOptions: [ autodefrag ] +# - filesystem: btrfs_swap +# options: [ defaults, noatime ] +# +# Example 2 +# In this example there is a single default used by all filesystems +# +# mountOptions: +# - filesystem: default +# options: [ defaults ] +# +mountOptions: + - filesystem: default + options: [ defaults ] + - filesystem: efi + options: [ defaults, umask=0077 ] + - filesystem: btrfs + options: [ defaults, compress=zstd:1 ] + - filesystem: btrfs_swap + options: [ defaults, noatime ] + + + + diff --git a/airootfs/usr/share/calamares/modules/netinstall.conf b/airootfs/usr/share/calamares/modules/netinstall.conf new file mode 100644 index 0000000..f185fc1 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/netinstall.conf @@ -0,0 +1,347 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +### Netinstall module +# +# The netinstall module allows distribution maintainers to ship minimal ISOs +# with only a basic set of preinstalled packages. At installation time, the +# user is presented with the choice to install groups of packages from a +# predefined list. +# +# Calamares will then use the *packages* module to install the packages. +# Without a *packages* module in the exec phase somewhere **after** +# this netinstall, nothing will actually get installed. The packages +# module must be correctly configured **and** the package manager must +# be runnable from within the installed system at the point where it +# is invoked, otherwise you'll get nothing. +# +# There are two basic deployment schemes: +# - static package lists; the packages do not change for this release. +# In this case, the package list file may be on the ISO-image itself +# as a separate file, **or** included in this configuration file. +# Either will do; separate file is easier to update independently +# of the Calamares configuration, while merged configurations use +# fewer files overall and are closer to self-documenting. +# - online package lists; the package list is fetched from a remote +# URL and handled otherwise like a static list. This can be useful +# if the package list needs updating during the lifetime of an ISO- +# image, e.g. packages are added or renamed. +# +# There is only one required key for this module, *groupsUrl*. +# +# This module supports multiple instances through the *label* key, +# which allows you to distinguish them in the UI. +--- +# The *groupsUrl* determines where the data for the netinstall groups-and- +# packages comes from. The value of the key may be: +# +# - a single string (this is treated as a list with just that string in it) +# - a list of strings +# +# Each string is treated as a URL (see below for special cases. The +# list is examined **in order** and each URL is tried in turn. The +# first URL to load successfully -- even if it yields 0 packages -- +# ends the process. This allows using a network URL and a (fallback) +# local URL for package lists, or for using multiple mirrors of +# netinstall data. +# +# The URL must point to a YAML file that follows the format described +# below at the key *groups* -- except for the special case URL "local". +# Note that the contents of the groups file is the **important** +# part of the configuration of this module. It specifies what +# groups and packages the user may select (and so what commands are to +# be run to install them). +# +# The format of the groups file is the same as the format of the +# *groups* key described below, **except** that a stand-alone +# groups file does not have to have the top-level *groups* key. +# (It **may** have one, though, for instance when you copy +# this configuration file to `netinstall.yaml` and key *groups* +# must have a list-of-groups as value; if the file does not have +# a top-level key *groups*, then the file must contain only a list of groups. +# +# Each item in the list *groupsUrl* may be: +# - A remote URL like `http://example.org/netinstall.php` +# - A local file URL like `file:///usr/share/calamares/netinstall.yaml` +# - The special-case literal string `local` +# +# Non-special case URLs are loaded as YAML; if the load succeeds, then +# they are interpreted like the *groups* key below. The special case +# `local` loads the data directly from **this** file. +# +groupsUrl: local + +# Alternate form: +# groupsUrl: [ local ] + +# Net-based package list, with fallback to local file +# groupsUrl: +# - http://example.com/calamares/netinstall.yaml +# - file:///etc/calamares/modules/netinstall.yaml + + + +# If the installation can proceed without netinstall (e.g. the Live CD +# can create a working installed system, but netinstall is preferred +# to bring it up-to-date or extend functionality) leave this set to +# false (the default). If set to true, the netinstall data is required. +# +# This only has an effect if the netinstall data cannot be retrieved, +# or is corrupt: having "required" set, means the install cannot proceed. +# For local or file: type *groupsUrl* settings, this setting is not +# really meaningful. +required: false + +# To support multiple instances of this module, +# some strings are configurable and translatable here. +# Sub-keys under *label* are used for the user interface. +# - *sidebar* This is the name of the module in the progress-tree / sidebar +# in Calamares. +# - *title* This is displayed above the list of packages. +# If no *sidebar* values are provided, defaults to "Package selection" +# and existing translations. If no *title* values are provided, no string +# is displayed. +# +# Translations are handled through `[ll]` notation, much like in +# `.desktop` files. The string associated with `key[ll]` is used for +# *key* when when the language *ll* (language-code, like *nl* or *en_GB* +# or *ja*) is used. +# +# The following strings are **already** known to Calamares and can be +# listed here in *untranslated* form (e.g. as value of *sidebar*) +# without bothering with the translations: they are picked up from +# the regular translation framework: +# - "Package selection" +# - "Office software" +# - "Office package" +# - "Browser software" +# - "Browser package" +# - "Web browser" +# - "Kernel" +# - "Services" +# - "Login" +# - "Desktop" +# - "Applications" +# - "Communication" +# - "Development" +# - "Office" +# - "Multimedia" +# - "Internet" +# - "Theming" +# - "Gaming" +# - "Utilities" +# Other strings should follow the translations format. +label: + sidebar: "Package selection" + # sidebar[nl]: "Pakketkeuze" + # sidebar[en_GB]: "Package choice" + # sidebar[ja]: "知りません" # "I don't know" + # title: "Office Package" + # title[nl]: "Kantoorsoftware" + +# If, and only if, *groupsUrl* is set to the literal string `local`, +# groups data is read from this file. The value of *groups* must be +# a list. Each item in the list is a group (of packages, or subgroups, +# or both). A standalone groups file contains just the list, +# (without the top-level *groups* key, or just the top-level *groups* +# key and with the list as its value, like in this file). +# +# Using `local` is recommended only for small static package lists. +# Here it is used for documentation purposes. +# +# +### Groups Format +# +# Each item in the list describes one group. The following keys are +# required for each group: +# +# - *name* of the group; short and human-readable. Shown in the first +# column of the UI. +# - *description* of the group; longer and human-readable. Shown in the +# second column of the UI. This is one of the things that visually +# distinguishes groups (with descriptions) from packages (without). +# - *packages*, a list of packages that belong to this group. +# The items of the *packages* list are actual package names +# as passed to the package manager (e.g. `qt5-creator-dev`). +# This list may be empty (e.g. if your group contains only +# subgroups). This key isn't **really** required, either -- +# one of *subgroups* or *packages* is. +# +# The following keys are **optional** for a group: +# +# - *hidden*: if true, do not show the group on the page. Defaults to false. +# - *selected*: if true, display the group as selected. Defaults to the +# parent group's value, if there is a parent group; top-level groups +# are set to true by default. +# - *critical*: if true, make the installation process fail if installing +# any of the packages in the group fails. Otherwise, just log a warning. +# Defaults to false. If not set in a subgroup (see below), inherits from +# the parent group. +# - *immutable*: if true, the state of the group (and all its subgroups) +# cannot be changed; no packages can be selected or deselected. No +# checkboxes are show for the group. Setting *immutable* to true +# really only makes sense in combination with *selected* set to true, +# so that the packages will be installed. (Setting a group to immutable +# can be seen as removing it from the user-interface.) +# - *noncheckable*: if true, the entire group cannot be selected or +# deselected by a single click. This does not affect any subgroups +# or child packages +# - *expanded*: if true, the group is shown in an expanded form (that is, +# not-collapsed) in the treeview on start. This only affects the user- +# interface. Only top-level groups are show expanded-initially. +# - *subgroups*: if present this follows the same structure as the top level +# groups, allowing sub-groups of packages to an arbitary depth. +# - *pre-install*: an optional command to run within the new system before +# the group's packages are installed. It will run before **each** package in +# the group is installed. +# - *post-install*: an optional command to run within the new system after +# the group's packages are installed. It will run after **each** package in +# the group is installed. +# +# If you set both *hidden* and *selected* for a top-level group, you are +# creating a "default" group of packages which will always be installed +# in the user's system. Hidden selected subgroups are installed if their +# parent is selected. Setting *hidden* to true without *selected*, or with +# *selected* set to false, is kind of pointless and will generate a warning. +# +# The *pre-install* and *post-install* commands are **not** passed to +# a shell; see the **packages** module configuration (i.e. `packages.conf`) +# for details. To use a full shell pipeline, call the shell explicitly. +# +# Non-critical groups are installed by calling the package manager +# individually, once for each package (and ignoring errors), while +# critical packages are installed in one single call to the package +# manager (and errors cause the installation to terminate). +# +# +# +# The *groups* key below contains some common patterns for packages +# and sub-groups, with documentation. + + +groups: + # This group is hidden, so the name and description are not really + # important. Since it is selected, these packages will be installed. + # It's non-critical, so they are installed one-by-one. + # + # This is a good approach for something you want up-to-date installed + # in the target system every time. + - name: "Default" + description: "Default group" + hidden: true + selected: true + critical: false + packages: + - base + - chakra-live-skel + # The Shells group contains only subgroups, no packages itself. + # The *critical* value is set for the subgroups that do not + # override it; *selected* is set to false but because one of + # the subgroups sets *selected* to true, the overall state of + # **this** group is partly-selected. + # + # Each of the sub-groups lists a bunch of packages that can + # be individually selected, so a user can pick (for instance) + # just one of the ZSH packages if they like. + - name: "Shells" + description: "Shells" + hidden: false + selected: false + critical: true + subgroups: + - name: "Bash" + description: "Bourne Again Shell" + selected: true + packages: + - bash + - bash-completion + - name: "Zsh" + description: "Zee shell, boss" + packages: + - zsh + - zsh-completion + - zsh-extensions + # The kernel group has no checkbox, because it is immutable. + # It can be (manually) expanded, and the packages inside it + # will be shown, also without checkboxes. This is a way to + # inform users that something will always be installed, + # sort of like a hidden+selected group but visible. + - name: "Kernel" + description: "Kernel bits" + hidden: false + selected: true + critical: true + immutable: true + packages: + - kernel + - kernel-debugsym + - kernel-nvidia + # *selected* defaults to true for top-level + - name: Communications + description: "Communications Software" + packages: + - ruqola + - konversation + - nheko + - quaternion + # Setting *selected* is supported. Here we also show off "rich" + # packages: ones with a package-name (for the package-manager) + # and a description (for the human). + - name: Editors + description: "Editing" + selected: false + packages: + - vi + - emacs + - nano + - name: kate-git + description: Kate (unstable) + - name: kate + description: KDE's text editor + # The "bare" package names can be intimidating, so you can use subgroups + # to provide human-readable names while hiding the packages themselves. + # This also allows you you group related packages -- suppose you feel + # that KDevelop should be installed always with PHP and Python support, + # but that support is split into multiple packages. + # + # So this subgroup (IDE) contains subgroups, one for each "package" + # we want to install. Each of those subgroups (Emacs, KDevelop) + # in turn contains **one** bogus subgroup, which then has the list + # of relevant packages. This extra-level-of-subgrouping allows us + # to list packages, while giving human-readable names. + # + # The name of the internal subgroup doesn't matter -- it is hidden + # from the user -- so we can give them all bogus names and + # descriptions, even the same name. Here, we use "Bogus". You + # can re-use the subgroup name, it doesn't really matter. + # + # Each internal subgroup is set to *hidden*, so it does not show up + # as an entry in the list, and it is set to *selected*, + # so that if you select its parent subgroup, the packages from + # the subgroup are selected with it and get installed. + - name: IDE + description: "Development Environment" + selected: false + subgroups: + - name: Emacs + description: LISP environment and editor + subgroups: + - name: Bogus + description: Bogus + hidden: true + selected: true + packages: + - emacs + - name: KDevelop + description: KDE's C++, PHP and Python environment + subgroups: + - name: Bogus + description: Bogus + hidden: true + selected: true + packages: + - kdevelop + - kdevelop-dev + - kdev-php + - kdev-python + diff --git a/airootfs/usr/share/calamares/modules/notesqml.conf b/airootfs/usr/share/calamares/modules/notesqml.conf new file mode 100644 index 0000000..c65f988 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/notesqml.conf @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The *notesqml* module can be used to display a QML file +# as an installer step. This is most useful for release-notes +# and similar somewhat-static content, but if you want to you +# can put SameGame in there as well. +# +# While the module compiles a QML file into a QRC for inclusion +# into the shared library, normal use will configure it with +# an external file, either from Calamares AppData directory or +# from the branding directory. +# +# --- +# +# QML modules can search for the QML inside the Qt resources +# (QRC) which are compiled into the module, or in the branding +# setup for Calamares, (or both of them, with branding taking +# precedence). This allows the module to ship a default UI and +# branding to optionally introduce a replacement file. +# +# Generally, leave the search method set to "both" because if +# you don't want to brand the UI, just don't ship a branding +# QML file for it. +# +# To support instanced QML modules, searches in the branding +# directory look for the full notesqml@instanceid name as well. +--- +# Search mode. Valid values are "both", "qrc" and "branding" +qmlSearch: both + +# Name of the QML file. If not set, uses the name of the instance +# of the module (e.g. if you list this module in `settings.conf` +# in the *instances* section, you get *id*, otherwise it would +# normally be "notesqml"). +# qmlFilename: notesqml + +# This is the name of the module in the progress-tree / sidebar +# in Calamares. To support multiple instances of the QML module, +# the name is configurable and translatable here. +qmlLabel: + notes: "Release Notes" + notes[nl]: "Opmerkingen" diff --git a/airootfs/usr/share/calamares/modules/oemid.conf b/airootfs/usr/share/calamares/modules/oemid.conf new file mode 100644 index 0000000..4fb14d9 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/oemid.conf @@ -0,0 +1,16 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# This is an OEM setup (phase-0) configuration file. +--- +# The batch-identifier is written to /var/log/installer/oem-id. +# This value is put into the text box as the **suggested** +# OEM ID. If ${DATE} is included in the identifier, then +# that is replaced by the current date in yyyy-MM-dd (ISO) format. +# +# It is ok for the identifier to be empty. +# +# The identifier is written to the file as UTF-8 (this will be no +# different from ASCII, for most inputs) and followed by a newline. +# If the identifier is empty, only a newline is written. +batch-identifier: neon-${DATE} diff --git a/airootfs/usr/share/calamares/modules/openrcdmcryptcfg.conf b/airootfs/usr/share/calamares/modules/openrcdmcryptcfg.conf new file mode 100644 index 0000000..911a4ef --- /dev/null +++ b/airootfs/usr/share/calamares/modules/openrcdmcryptcfg.conf @@ -0,0 +1,5 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +--- +configFilePath: /etc/conf.d/dmcrypt diff --git a/airootfs/usr/share/calamares/modules/packages.conf b/airootfs/usr/share/calamares/modules/packages.conf new file mode 100644 index 0000000..b9777f6 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/packages.conf @@ -0,0 +1,214 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The configuration for the package manager starts with the +# *backend* key, which picks one of the backends to use. +# In `main.py` there is a base class `PackageManager`. +# Implementations must subclass that and set a (class-level) +# property *backend* to the name of the backend (e.g. "dummy"). +# That property is used to match against the *backend* key here. +# +# You will have to add such a class for your package manager. +# It is fairly simple Python code. The API is described in the +# abstract methods in class `PackageManager`. Mostly, the only +# trick is to figure out the correct commands to use, and in particular, +# whether additional switches are required or not. Some package managers +# have more installer-friendly defaults than others, e.g., DNF requires +# passing --disablerepo=* -C to allow removing packages without Internet +# connectivity, and it also returns an error exit code if the package did +# not exist to begin with. +--- +# +# Which package manager to use, options are: +# - apk - Alpine Linux package manager +# - apt - APT frontend for DEB and RPM +# - dnf - DNF, the new RPM frontend +# - dnf5 - DNF5, the newer new RPM frontend +# - entropy - Sabayon package manager (is being deprecated) +# - luet - Sabayon package manager (next-gen) +# - packagekit - PackageKit CLI tool +# - pacman - Pacman +# - pamac - Manjaro package manager +# - portage - Gentoo package manager +# - yum - Yum RPM frontend +# - zypp - Zypp RPM frontend +# +# Not actually a package manager, but suitable for testing: +# - dummy - Dummy manager, only logs +# +backend: dummy + +# +# Often package installation needs an internet connection. +# Since you may allow system installation without a connection +# and want to offer OPTIONAL package installation, it's +# possible to have no internet, yet have this packages module +# enabled in settings. +# +# You can skip the whole module when there is no internet +# by setting "skip_if_no_internet" to true. +# +# You can run a package-manager specific update procedure +# before installing packages (for instance, to update the +# list of packages and dependencies); this is done only if there +# is an internet connection. +# +# Set "update_db" to 'true' for refreshing the database on the +# target system. On target installations, which got installed by +# unsquashing, a full system update may be needed. Otherwise +# post-installing additional packages may result in conflicts. +# Therefore set also "update_system" to 'true'. +# +skip_if_no_internet: false +update_db: true +update_system: false + +# pacman specific options +# +# *num_retries* should be a positive integer which specifies the +# number of times the call to pacman will be retried in the event of a +# failure. If it is missing, it will be set to 0. +# +# *disable_download_timeout* is a boolean that, when true, includes +# the flag --disable-download-timeout on calls to pacman. When missing, +# false is assumed. +# +# *needed_only* is a boolean that includes the pacman argument --needed +# when set to true. If missing, false is assumed. +pacman: + num_retries: 0 + disable_download_timeout: false + needed_only: false + +# +# List of maps with package operations such as install or remove. +# Distro developers can provide a list of packages to remove +# from the installed system (for instance packages meant only +# for the live system). +# +# A job implementing a distro specific logic to determine other +# packages that need to be installed or removed can run before +# this one. Distro developers may want to install locale packages +# or remove drivers not needed on the installed system. +# Such a job would populate a list of dictionaries in the global +# storage called "packageOperations" and that list is processed +# after the static list in the job configuration (i.e. the list +# that is in this configuration file). +# +# Allowed package operations are: +# - *install*, *try_install*: will call the package manager to +# install one or more packages. The install target will +# abort the whole installation if package-installation +# fails, while try_install carries on. Packages may be +# listed as (localized) names, or as (localized) package-data. +# See below for the description of the format. +# - *localInstall*: this is used to call the package manager +# to install a package from a path-to-a-package. This is +# useful if you have a static package archive on the install media. +# The *pacman* package manager is the only one to specially support +# this operation (all others treat this the same as *install*). +# - *remove*, *try_remove*: will call the package manager to +# remove one or more packages. The remove target will +# abort the whole installation if package-removal fails, +# while try_remove carries on. Packages may be listed as +# (localized) names. +# One additional key is recognized, to help netinstall out: +# - *source*: ignored, does get logged +# Any other key is ignored, and logged as a warning. +# +# There are two formats for naming packages: as a name or as package-data, +# which is an object notation providing package-name, as well as pre- and +# post-install scripts. +# +# Here are both formats, for installing vi. The first one just names the +# package for vi (using the naming of the installed package manager), while +# the second contains three data-items; the pre-script is run before invoking +# the package manager, and the post-script runs once it is done. +# +# - install +# - vi +# - package: vi +# pre-script: touch /tmp/installing-vi +# post-script: rm -f /tmp/installing-vi +# +# The pre- and post-scripts are optional, but you cannot leave both out +# if you do use the *package* key: using "package: vi" with neither script +# option will trick Calamares into trying to install a package named +# "package: vi", which is unlikely to work. +# +# The pre- and post-scripts are **not** executed by a shell unless you +# explicitly invoke `/bin/sh` in them. The command-lines are passed +# to exec(), which does not understand shell syntax. In other words: +# +# pre-script: ls | wc -l +# +# Will fail, because `|` is passed as a command-line argument to ls, +# as are `wc`, and `-l`. No shell pipeline is set up, and ls is likely +# to complain. Invoke the shell explicitly: +# +# pre-script: /bin/sh -c \"ls | wc -l\" +# +# The above note on shell-expansion applies to versions up-to-and-including +# Calamares 3.2.12, but will change in future. +# +# Any package name may be localized; this is used to install localization +# packages for software based on the selected system locale. By including +# the string `LOCALE` in the package name, the following happens: +# +# - if the system locale is English (any variety), then the package is not +# installed at all, +# - otherwise `$LOCALE` or `${LOCALE}` is replaced by the 'lower-cased' BCP47 +# name of the 'language' part of the selected system locale (not the +# country/region/dialect part), e.g. selecting "nl_BE" will use "nl" +# here. +# +# Take care that just plain `LOCALE` will not be replaced, so `foo-LOCALE` will +# be left unchanged, while `foo-$LOCALE` will be changed. However, `foo-LOCALE` +# **will** be removed from the list of packages (i.e. not installed), if +# English is selected. If a non-English locale is selected, then `foo-LOCALE` +# will be installed, unchanged (no language-name-substitution occurs). +# +# The following installs localizations for vi, if they are relevant; if +# there is no localization, installation continues normally. +# +# - install +# - vi-$LOCALE +# - package: vi-${LOCALE} +# pre-script: touch /tmp/installing-vi +# post-script: rm -f /tmp/installing-vi +# +# When installing packages, Calamares will invoke the package manager +# with a list of package names if it can; package-data prevents this because +# of the scripts that need to run. In other words, this: +# +# - install: +# - vi +# - binutils +# - package: wget +# pre-script: touch /tmp/installing-wget +# +# This will invoke the package manager three times, once for each package, +# because not all of them are simple package names. You can speed up the +# process if you have only a few pre-scripts, by using multiple install targets: +# +# - install: +# - vi +# - binutils +# - install: +# - package: wget +# pre-script: touch /tmp/installing-wget +# +# This will call the package manager once with the package-names "vi" and +# "binutils", and then a second time for "wget". When installing large numbers +# of packages, this can lead to a considerable time savings. +# +operations: + - install: + - vi + - vi-${LOCALE} + - wget + - binutils + - remove: + - vi + - wget + - binutils diff --git a/airootfs/usr/share/calamares/modules/partition.conf b/airootfs/usr/share/calamares/modules/partition.conf new file mode 100644 index 0000000..47e3328 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/partition.conf @@ -0,0 +1,397 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# + +# Options for EFI system partition. +# +# - *mountPoint* +# This setting specifies the mount point of the EFI system partition. Some +# distributions (Fedora, Debian, Manjaro, etc.) use /boot/efi, others (KaOS, +# etc.) use just /boot. +# +# Defaults to "/boot/efi", may be empty (but weird effects ensue) +# - *recommendedSize* +# This optional setting specifies the size of the EFI system partition. +# If nothing is specified, the default size of 300MiB will be used. +# When writing quantities here, M is treated as MiB, and if you really +# want one-million (10^6) bytes, use MB. +# - *minimumSize* +# This optional setting specifies the absolute minimum size of the EFI +# system partition. If nothing is specified, the *recommendedSize* +# is used instead. +# - *label* +# This optional setting specifies the name of the EFI system partition (see +# PARTLABEL; gpt only; requires KPMCore >= 4.2.0). +# If nothing is specified, the partition name is left unset. +# +# Going below the *recommended* size is allowed, but the user will +# get a warning that it might not work. Going below the *minimum* +# size is not allowed and the user will be told it will not work. +# +# Both quantities must be at least 32MiB, this is enforced by the EFI +# spec. If minimum is not specified, it defaults to the recommended +# size. Distros that allow more user latitude can set the minimum lower. +efi: + mountPoint: "/boot/efi" + recommendedSize: 300MiB + minimumSize: 32MiB + label: "EFI" + +# Deprecated alias of efi.mountPoint +# efiSystemPartition: "/boot/efi" + +# Deprecated alias of efi.recommendedSize +# efiSystemPartitionSize: 300MiB + +# Deprecated alias of efi.label +# efiSystemPartitionName: EFI + +# In autogenerated partitioning, allow the user to select a swap size? +# If there is exactly one choice, no UI is presented, and the user +# cannot make a choice -- this setting is used. If there is more than +# one choice, a UI is presented. +# +# Legacy settings *neverCreateSwap* and *ensureSuspendToDisk* correspond +# to values of *userSwapChoices* as follows: +# - *neverCreateSwap* is true, means [none] +# - *neverCreateSwap* is false, *ensureSuspendToDisk* is false, [small] +# - *neverCreateSwap* is false, *ensureSuspendToDisk* is true, [suspend] +# +# Autogenerated swap sizes are as follows: +# - *suspend*: Swap is always at least total memory size, +# and up to 4GiB RAM follows the rule-of-thumb 2 * memory; +# from 4GiB to 8 GiB it stays steady at 8GiB, and over 8 GiB memory +# swap is the size of main memory. +# - *small*: Follows the rules above, but Swap is at +# most 8GiB, and no more than 10% of available disk. +# In both cases, a fudge factor (usually 10% extra) is applied so that there +# is some space for administrative overhead (e.g. 8 GiB swap will allocate +# 8.8GiB on disk in the end). +# +# If *file* is enabled here, make sure to have the *fstab* module +# as well (later in the exec phase) so that the swap file is +# actually created. +userSwapChoices: + - none # Create no swap, use no swap + - small # Up to 4GB + - suspend # At least main memory size + # - reuse # Re-use existing swap, but don't create any (unsupported right now) + - file # To swap file instead of partition + +# This optional setting specifies the name of the swap partition (see +# PARTLABEL; gpt only; requires KPMCore >= 4.2.0). +# If nothing is specified, the partition name is left unset. +# swapPartitionName: swap + +# LEGACY SETTINGS (these will generate a warning) +# ensureSuspendToDisk: true +# neverCreateSwap: false + +# This setting specifies the LUKS generation (i.e LUKS1, LUKS2) used internally by +# cryptsetup when creating an encrypted partition. +# +# This option is set to luks1 by default, as grub doesn't support LUKS2 + Argon2id +# currently. On the other hand grub does support LUKS2 with PBKDF2 and could therefore be +# also set to luks2. Also there are some patches for grub and Argon2. +# See: https://aur.archlinux.org/packages/grub-improved-luks2-git +# +# Choices: luks1, luks2 (in addition, "luks" means "luks1") +# +# The default is luks1 +# +luksGeneration: luks1 + +# This setting determines if encryption should be allowed when using zfs. This +# setting has no effect unless zfs support is provided. +# +# This setting is to handle the fact that some bootloaders(such as grub) do not +# support zfs encryption. +# +# The default is true +# +# allowZfsEncryption: true + +# Correctly draw nested (e.g. logical) partitions as such. +drawNestedPartitions: false + +# Show/hide partition labels on manual partitioning page. +alwaysShowPartitionLabels: true + +# Allow manual partitioning. +# +# When set to false, this option hides the "Manual partitioning" button, +# limiting the user's choice to "Erase", "Replace" or "Alongside". +# This can be useful when using a custom partition layout we don't want +# the user to modify. +# +# If nothing is specified, manual partitioning is enabled. +#allowManualPartitioning: true + +# Show not encrypted boot partition warning. +# +# When set to false, this option does not show the +# "Boot partition not encrypted" warning when encrypting the +# root partition but not /boot partition. +# +# If nothing is specified, the warning is shown. +#showNotEncryptedBootMessage: true + +# Initial selection on the Choice page +# +# There are four radio buttons (in principle: erase, replace, alongside, manual), +# and you can pick which of them, if any, is initially selected. For most +# installers, "none" is the right choice: it makes the user pick something specific, +# rather than accidentally being able to click past an important choice (in particular, +# "erase" is a dangerous choice). +# +# The default is "none" +# +initialPartitioningChoice: none +# +# Similarly, some of the installation choices may offer a choice of swap; +# the available choices depend on *userSwapChoices*, above, and this +# setting can be used to pick a specific one. +# +# The default is "none" (no swap) if that is one of the enabled options, otherwise +# one of the items from the options. +initialSwapChoice: none + +# armInstall +# +# Leaves 16MB empty at the start of a drive when partitioning +# where usually the u-boot loader goes +# +# armInstall: false + +# Default partition table type, used when a "erase" disk is made. +# +# When erasing a disk, a new partition table is created on disk. +# In other cases, e.g. Replace and Alongside, as well as when using +# manual partitioning, this partition table exists already on disk +# and it is left unmodified. +# +# Possible values: gpt, msdos (or other names defined by KPMcore). +# Names are case-sensitive. +# +# If nothing is specified, Calamares defaults to "gpt" if system is +# efi or "msdos" otherwise. +# +# defaultPartitionTableType: msdos + +# Specify whether to create a partition table layout suitable for a hybrid +# (BIOS + EFI) bootloader installation. This will prepend both bios-boot and +# EFI system partitions to the partition layout, regardless of whether the +# booted system uses BIOS or EFI firmware. Defaults to false. +# +# createHybridBootloaderLayout: false + +# Requirement for partition table type +# +# Restrict the installation on disks that match the type of partition +# tables that are specified. +# +# Possible values: msdos, gpt (or other names defined by KPMcore). +# Names are case-sensitive. +# +# If nothing is specified, Calamares defaults to both "msdos" and "gpt". +# +# requiredPartitionTableType: gpt +# requiredPartitionTableType: +# - msdos +# - gpt + +# Default filesystem type, used when a "new" partition is made. +# +# When replacing a partition, the new filesystem type will be from the +# defaultFileSystemType value. In other cases, e.g. Erase and Alongside, +# as well as when using manual partitioning and creating a new +# partition, this filesystem type is pre-selected. Note that +# editing a partition in manual-creation mode will not automatically +# change the filesystem type to this default value -- it is not +# creating a new partition. +# +# Suggested values: ext2, ext3, ext4, reiser, xfs, jfs, btrfs +# If nothing is specified, Calamares defaults to "ext4". +# +# Names are case-sensitive and defined by KPMCore. +defaultFileSystemType: "ext4" + +# Selectable filesystem type, used when "erase" is done. +# +# When erasing the disk, the *defaultFileSystemType* is used (see +# above), but it is also possible to give users a choice: +# list suitable filesystems here. A drop-down is provided +# to pick which is the filesystems will be used. +# +# The value *defaultFileSystemType* is added to this list (with a warning) +# if not present; the default pick is the *defaultFileSystemType*. +# +# If not specified at all, uses *defaultFileSystemType* without a +# warning (this matches traditional no-choice-available behavior best). +# availableFileSystemTypes: ["ext4","f2fs"] + +# Per-directory filesystem restrictions. +# +# This optional setting specifies what filesystems the user can and cannot use +# for various directories and mountpoints when using manual partitioning. +# +# If nothing is specified, the only restriction enforced by default is that +# the EFI system partition must use the fat32 filesystem. +# +# Otherwise, the filesystem restrictions are defined as follow: +# +# directoryFilesystemRestrictions: +# - directory: "any" +# allowedFilesystemTypes: ["all"] +# - directory: "/" +# allowedFilesystemTypes: ["ext4","xfs","btrfs","jfs","f2fs"] +# - mountpoint: "efi" +# allowedFilesystemTypes: ["fat32"] +# onlyWhenMountpoint: true +# +# There can be any number of mountpoints listed, each entry having the +# following attributes: +# - mountpoint: mountpoint's full path +# or +# "any" to specify a global whitelist that applies to all +# mountpoints +# or +# "efi" to specify a whitelist specific to the EFI system +# partition, wherever that partition is located +# - allowedFilesystemTypes: the list of all filesystems valid for this +# mountpoint. If the list contains exactly one +# element, and that element is the special value +# "any", all filesystem types recognized by +# Calamares will be allowed. +# - onlyWhenMountpoint: Whether the restriction should apply only when the +# specified directory is a mountpoint. When set to +# true, Calamares will only enforce the listed +# restrictions when the user makes a separate partition +# for this directory and assigns the mountpoint +# accordingly. When set to false, Calamares will +# ensure this directory uses the specified filesystem +# even if the directory is part of a filesystem on a +# different mountpoint. Defaults to false. + +# The ClearMounts job unmounts / unmaps things before partitioning. +# Some special entries under /dev/mapper are excepted from this process. +# The example lists the three hard-coded exceptions which always apply +# (they don't need to be listed here). Add other names or wildcards (with +# a trailing '*') to this list if the live-ISO has additional mounts. +essentialMounts: [ "live-*", "control", "ventoy" ] + +# Show/hide LUKS related functionality in automated partitioning modes. +# Disable this if you choose not to deploy early unlocking support in GRUB2 +# and/or your distribution's initramfs solution. +# +# BIG FAT WARNING: +# +# This option is unsupported, as it cuts out a crucial security feature. +# Disabling LUKS and shipping Calamares without a correctly configured GRUB2 +# and initramfs is considered suboptimal use of the Calamares software. The +# Calamares team will not provide user support for any potential issue that +# may arise as a consequence of setting this option to false. +# It is strongly recommended that system integrators put in the work to support +# LUKS unlocking support in GRUB2 and initramfs/dracut/mkinitcpio/etc. +# For more information on setting up GRUB2 for Calamares with LUKS, see +# the Calamares website at https://calamares.io/docs/partitions/#luks . +# +# If nothing is specified, LUKS is enabled in automated modes. +#enableLuksAutomatedPartitioning: true + +# When enableLuksAutomatedPartitioning is true, this option will pre-check +# encryption checkbox. This option is only usefull to help people to not forget +# to cypher their disk when installing in enterprise (for example). +#preCheckEncryption: false + +# LVM support +# +# There is only one sub-key available, *enable* (defaults to true) +# which can be used to show (default) or hide the LVM buttons in the partitioning module. +lvm: + enable: true + +# Partition layout. +# +# This optional setting specifies a custom partition layout. +# +# If nothing is specified, the default partition layout is a single partition +# for root that uses 100% of the space and uses the filesystem defined by +# defaultFileSystemType. +# +# Note: the EFI system partition is prepended automatically to the layout if +# needed; the swap partition is appended to the layout if enabled (selections +# "small" or "suspend" in *userSwapChoices*). +# +# Otherwise, the partition layout is defined as follow: +# +# partitionLayout: +# - name: "rootfs" +# type: "4f68bce3-e8cd-4db1-96e7-fbcaf984b709" +# filesystem: "ext4" +# noEncrypt: false +# mountPoint: "/" +# size: 20% +# minSize: 500M +# maxSize: 10G +# attributes: 0xffff000000000003 +# - name: "home" +# type: "933ac7e1-2eb4-4f13-b844-0e14e2aef915" +# filesystem: "ext4" +# noEncrypt: false +# mountPoint: "/home" +# size: 3G +# minSize: 1.5G +# features: +# 64bit: false +# casefold: true +# - name: "data" +# filesystem: "fat32" +# mountPoint: "/data" +# features: +# sector-size: 4096 +# sectors-per-cluster: 128 +# size: 100% +# +# There can be any number of partitions, each entry having the following attributes: +# - name: filesystem label +# and +# partition name (gpt only; since KPMCore 4.2.0) +# - uuid: partition uuid (optional parameter; gpt only; requires KPMCore >= 4.2.0) +# - type: partition type (optional parameter; gpt only; requires KPMCore >= 4.2.0) +# - attributes: partition attributes (optional parameter; gpt only; requires KPMCore >= 4.2.0) +# - filesystem: filesystem type (optional parameter) +# - if not set at all, treat as "unformatted" +# - if "unformatted", no filesystem will be created +# - if "unknown" (or an unknown FS name, like "elephant") then the +# default filesystem type, or the user's choice, will be applied instead +# of "unknown" (e.g. the user might pick ext4, or xfs). +# - noEncrypt: whether this partition is exempt from encryption if enabled (optional parameter; default is false) +# - mountPoint: partition mount point (optional parameter; not mounted if unset) +# - size: partition size in bytes (append 'K', 'M' or 'G' for KiB, MiB or GiB) +# or +# % of the available drive space if a '%' is appended to the value +# - minSize: minimum partition size (optional parameter) +# - maxSize: maximum partition size (optional parameter) +# - features: filesystem features (optional parameter; requires KPMCore >= 4.2.0) +# name: boolean or integer or string + +# Checking for available storage +# +# This overlaps with the setting of the same name in the welcome module's +# requirements section. If nothing is set by the welcome module, this +# value is used instead. It is still a problem if there is no required +# size set at all, and the replace and resize options will not be offered +# if no required size is set. +# +# The value is in Gibibytes (GiB). +# +# BIG FAT WARNING: except for OEM-phase-0 use, you should be using +# the welcome module, **and** configure this value in +# `welcome.conf`, not here. +# requiredStorage: 3.5 +# Use GPT for UEFI (standard for modern VMs) +defaultPartitionTableType: "gpt" + +# Ensure the installer doesn't hang on "busy" disks +enableLuksAutomatedPartitioning: true diff --git a/airootfs/usr/share/calamares/modules/plasmalnf.conf b/airootfs/usr/share/calamares/modules/plasmalnf.conf new file mode 100644 index 0000000..105f247 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/plasmalnf.conf @@ -0,0 +1,83 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The Plasma Look-and-Feel module allows selecting a Plasma +# Look-and-Feel in the live- or host-system and switches the +# host Plasma session immediately to the chosen LnF; it +# can also write a LnF configuration to the target user / on +# the target system. +# +# This module should be used once in a view section (to get +# the UI) and once in the exec section (to apply the selection +# to the target user). It should come **after** the user module +# in exec, so that the target user has been created alrady. +--- +# Full path to the Plasma look-and-feel tool (CLI program +# for querying and applying Plasma themes). If this is not +# set, no LNF setting will happen. +lnftool: "/usr/bin/lookandfeeltool" + +# For systems where the user Calamares runs as (usually root, +# via either sudo or pkexec) has a clean environment, set this +# to the originating username; the lnftool will be run through +# "sudo -H -u " instead of directly. +# +# liveuser: "live" + +# If *showAll* is true, then all installed themes are shown in the +# UI for selection, even if they are not listed in *themes* (below). +# This allows selection of all themes even while not all of them are +# listed in *themes* -- which is useful to show screenshots for those +# you do have a screenshot for. If *themes* is empty or missing, +# the value of *showAll* is treated as `true`. +showAll: false + +# You can limit the list of Plasma look-and-feel themes by listing ids +# here. If this key is not present, all of the installed themes are listed. +# If the key is present, only installed themes that are **also** included +# in the list are shown (could be none!). See the *showAll* key, above, +# to change that. +# +# Themes may be listed by id, (e.g. fluffy-bunny, below) or as a theme +# and an image (e.g. breeze) which will be used to show a screenshot. +# Themes with no image set at all get a "missing screenshot" image; if the +# image file is not found, they get a color swatch based on the image name. +# +# The image may be an absolute path. If it is a relative path, though, +# it is searched in the current directory and in the branding directory +# (i.e. relative to the directory where your branding.desc lives). +# +# Valid forms of entries in the *themes* key: +# - A single string (unquoted), which is the theme id +# - A pair of *theme* and *image* keys, e.g. +# ``` +# - theme: fluffy-bunny.desktop +# image: "fluffy-screenshot.png" +# ``` +# +# The image screenshot is resized to 12x8 the current font size, with +# a minimum of 120x80 pixels. This allows the screenshot to scale up +# on HiDPI displays where the fonts are larger (in pixels). +themes: + - org.kde.fuzzy-pig.desktop + - theme: org.kde.breeze.desktop + image: "breeze.png" + - theme: org.kde.breezedark.desktop + image: "breeze-dark.png" + - org.kde.fluffy-bunny.desktop + +# You can pre-select one of the themes; it is not applied +# immediately, but its radio-button is switched on to indicate +# that that is the theme (that is most likely) currently in use. +# Do this only on Live images where you are reasonably sure +# that the user is not going to change the theme out from under +# themselves before running the installer. +# +# If this key is present, its value should be the id of the theme +# which should be pre-selected. If absent, empty, or the pre-selected +# theme is not found on the live system, no theme will be pre-selected. +# +# As a special setting, use "*", to try to find the currently- +# selected theme by reading the Plasma configuration. This requires +# KF5::Config at build- and run-time. +preselect: "*" diff --git a/airootfs/usr/share/calamares/modules/plymouthcfg.conf b/airootfs/usr/share/calamares/modules/plymouthcfg.conf new file mode 100644 index 0000000..ebe51d1 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/plymouthcfg.conf @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Plymouth Configuration Module +# +# This module can be used to setup the default plymouth theme to +# be used with your distribution +# +# You should only use this module if the plymouth package is installed +# on the build configurations of your distribution & the plymouth +# theme you want to configure is installed as well. If the unpacked +# filesystem configures a plymouth theme already, there is no need +# to change it here. +--- + + +# Leave this commented if you want to use the default theme +# shipped with your distribution configurations. Make sure that +# the theme exists in the themes directory of plymouth path. +# Debian / Ubuntu comes with themes "joy", "script", "softwaves", +# possibly others. Look in /usr/share/plymouth/themes for more. +# +# Specifying a non-existent theme will leave the plymouth +# configuration set to that theme. It is up to plymouth to +# deal with that. + +plymouth_theme: spinfinity + + + + diff --git a/airootfs/usr/share/calamares/modules/preservefiles.conf b/airootfs/usr/share/calamares/modules/preservefiles.conf new file mode 100644 index 0000000..75584f5 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/preservefiles.conf @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the preserve-files job +# +# The *files* key contains a list of files to preserve. Each element of +# the list should have one of these forms: +# +# - an absolute path (probably within the host system). This will be preserved +# as the same path within the target system (chroot). If, globally, +# *dontChroot* is true, then these items will be ignored (since the +# destination is the same as the source). +# - a map with a *dest* key. The *dest* value is a path interpreted in the +# target system (if the global *dontChroot* is true, then the host is the +# target as well). Relative paths are not recommended. There are two +# ways to select the source data for the file: +# - *from*, which must have one of the values, below; it is used to +# preserve files whose pathname is known to Calamares internally. +# - *src*, to refer to a path interpreted in the host system. Relative +# paths are not recommended, and are interpreted relative to where +# Calamares is being run. +# Exactly one of the two source keys (either *from* or *src*) must be set. +# +# Special values for the key *from* are: +# - *log*, for the complete log file (up to the moment the preservefiles +# module is run), +# - *config*, for a JSON dump of the contents of global storage. +# Note that this may contain sensitive information, and should be +# given restrictive permissions. +# +# A map with a *dest* key can have these additional fields: +# - *perm*, is a colon-separated tuple of :: +# where is in octal (e.g. 4777 for wide-open, 0400 for read-only +# by owner). If set, the file's ownership and permissions are set to +# those values within the target system; if not set, no permissions +# are changed. +# - *optional*, is a boolean; if this is set to `true` then failure to +# preserve the file will **not** be counted as a failure of the +# module, and installation will proceed. Set this for files that might +# not exist in the host system (e.g. nvidia configuration files that +# are created in some boot scenarios and not in others). +# +# The target path (*dest*) is modified by expanding variables in `${}`: +# - `ROOT` is replaced by the path to the target root (may be /). +# There is never any reason to use this, since the *dest* is already +# interpreted in the target system. +# - `USER` is replaced by the username entered by on the user +# page (may be empty, for instance if no user page is enabled) +# +# +# +files: + - from: log + dest: /var/log/Calamares.log + perm: root:wheel:600 + - from: log + dest: /home/${USER}/installation.log + optional: true + - from: config + dest: /var/log/Calamares-install.json + perm: root:wheel:600 +# - src: /var/log/nvidia.conf +# dest: /var/log/Calamares-nvidia.conf +# optional: true + +# The *perm* key contains a default value to apply to all files listed +# above that do not have a *perm* key of their own. If not set, +# root:root:0400 (highly restrictive) is used. +# +# perm: "root:root:0400" diff --git a/airootfs/usr/share/calamares/modules/rawfs.conf b/airootfs/usr/share/calamares/modules/rawfs.conf new file mode 100644 index 0000000..bbc3690 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/rawfs.conf @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the rawfs module: raw filesystem copy to a block device + +--- + +# To apply a custom partition layout, it has to be defined as a list of targets. +# +# For each target, the following attributes must be defined: +# * mountPoint: The mount point of the destination device on the installed system +# The corresponding block device will automatically be identified and used as the +# destination for the operation +# * source: The source filesystem; it can be the mount point of a locally (on the +# live system) mounted filesystem, a path to a disk image, or a block device +# * resize (optional): Expand the destination filesystem to fill the whole +# partition at the end of the operation; this works only with ext filesystems +# for now + +targets: + - mountPoint: / + source: / + - mountPoint: /home + source: /images/home.img + resize: true + - mountPoint: /data + source: /dev/mmcblk0p3 + +# To support testing, set the *bogus* key to true. No actual work is done, but the +# module's logic is exercised. + +# bogus: false diff --git a/airootfs/usr/share/calamares/modules/removeuser.conf b/airootfs/usr/share/calamares/modules/removeuser.conf new file mode 100644 index 0000000..cc086e7 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/removeuser.conf @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Removes a single user (with userdel) from the system. +# This is typically used in OEM setups or if the live user +# spills into the target system. +# +# The module never fails; if userdel fails, this is logged +# but the module still reports success and installation / setup +# continues as normal. +--- +# Username in the target system to be removed. +username: live diff --git a/airootfs/usr/share/calamares/modules/services-systemd.conf b/airootfs/usr/share/calamares/modules/services-systemd.conf new file mode 100644 index 0000000..330a94c --- /dev/null +++ b/airootfs/usr/share/calamares/modules/services-systemd.conf @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Systemd units manipulation. +# +# This module can perform actions using systemd units, +# (for example, enabling, disabling, or masking services, sockets, paths, etc.) +--- + +# There is one key for this module: *units*. Its value is a list of entries. +# Each entry has three keys: +# - *name* is the (string) name of the systemd unit that is being changed. +# Use quotes. You can use any valid systemd unit here (for example, +# "NetworkManager.service", "cups.socket", "lightdm", "gdm", etc.) +# - *action* is the (string) action that you want to perform over the unit +# (for example, "enable", "disable", "mask", "unmask", etc.). Please +# ensure that the action can actually run under chroot (otherwise it is +# pointless) +# - *mandatory* is a boolean option, which states whether the change +# must be done successfully. If systemd reports an error while changing +# a mandatory entry, the installation will fail. When mandatory is false, +# errors for that systemd unit are ignored. If mandatory +# is not specified, the default is false. +# +# The order of operations is the same as the order in which entries +# appear in the list + +# # This example enables NetworkManager.service (and fails if it can't), +# # disables cups.socket (and ignores failure). Then it enables the +# # graphical target (e.g. so that SDDM runs for login), and +# # finally masks pacman-init (an ArchLinux-only service). +# # +# units: +# - name: "NetworkManager.service" +# action: "enable" +# mandatory: true +# +# - name: "cups.socket" +# action: "disable" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified +# +# - name: "graphical.target" +# action: "enable" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified +# +# - name: "pacman-init.service" +# action: "mask" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified + +# By default, no changes are made. +units: [] diff --git a/airootfs/usr/share/calamares/modules/shellprocess.conf b/airootfs/usr/share/calamares/modules/shellprocess.conf new file mode 100644 index 0000000..709d2a8 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/shellprocess.conf @@ -0,0 +1,146 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the shell process job. +# +# Executes a list of commands found under the key *script*. +# If the top-level key *dontChroot* is true, then the commands +# are executed in the context of the live system, otherwise +# in the context of the target system. In all of the commands, +# the following variable expansions will take place: +# - `ROOT` is replaced by the root mount point of the **target** +# system from the point of view of the command (when run in the target +# system, e.g. when *dontChroot* is false, that will be `/`). +# - `USER` is replaced by the username, set on the user page. +# - `LANG` is replaced by the language chosen for the user-interface +# of Calamares, set on the welcome page. This may not reflect the +# chosen system language from the locale page. +# +# As a special case, variables of the form `gs[key]` where `key` is +# a dotted-keys string and `gs` is literally the letters `g` and `s`, +# use **any** value from Global Storage. For example, +# +# gs[branding.bootloader] +# +# This variable refers to the GS value stored in `bootloader` in the +# `branding` map. Examine the Debug window for information about the +# keys stored in GS. Only strings and integers are exposed this way, +# lists and other data types do not set any variable this way. +# +# Variables are written as `${var}`, e.g. `${ROOT}`. +# Write `$$` to get a shell-escaped `\$` in the shell command. +# It is not possible to get an un-escaped `$` in the shell command +# (either the command will fail because of undefined variables, or +# you get a shell-escaped `\$`). +# +# The (global) timeout for the command list can be set with +# the *timeout* key. The value is a time in seconds, default +# is 30 seconds if not set. The timeout **must** be tuned, either +# globally or per-command (see below in the description of *script*), +# to the load or expected running-time of the command. +# +# - Setting a timeout of 30 for a `touch` command is probably exessive +# - Setting a timeout of 1 for a `touch` command might be low, +# on a slow disk where touch needs to be loaded from CDROM +# - Setting a timeout of 30 for a 1GB download is definitely low +# - Setting a timeout of 3600 for a 1GB download is going to leave +# the user in uncertainty for a loooong time. +# +# The (global) verbosity of a command can be set to `true` or `false`. +# When set to `true`, command output is logged one line at a time. +# Otherwise the output is logged when the command completes. +# Line-at-a-time logging is appropriate for commands that take +# a long time to complete and produce their own (progress) output. +# +# If a command starts with "-" (a single minus sign), then the +# return value of the command following the - is ignored; otherwise, +# a failing command will abort the installation. This is much like +# make's use of - in a command. +# +# The value of *script* may be: +# - a single string; this is one command that is executed. +# - a single object (see below). +# - a list of items; these are executed one at a time, by +# separate shells (/bin/sh -c is invoked for each command). +# Each list item may be: +# - a single string; this is one command that is executed. +# - a single object, specifying a key *command* and (optionally) +# a key *timeout* to set the timeout for this specific +# command differently from the global setting. An optional +# key *environment* is a list of strings to put into the +# environment of the command. An optional key *verbose* +# overrides the global *verbose* setting in this file. +# +# Using a single object is not generally useful because the same effect +# can be obtained with a single string and a global timeout, except +# when the command needs environment-settings. When there are +# multiple commands to execute, one of them might have +# a different timeout than the others. +# +# The environment strings should all be "KEY='some value'" strings, +# as if they can be typed into the shell. Quoting the environment +# strings with "" in YAML is recommended. Adding the '' quotes ensures +# that the value will not be interpreted by the shell. Writing +# environment strings is the same as placing `export KEY='some value' ;` +# in front of the *command*. +# +# Calamares variable expansion is **also** done on the environment strings. +# Write `$$` to get a literal `$` in the shell command. +# +# To change the description of the job, set the *name* entries in *i18n*. +--- +# Set to true to run in host, rather than target system +dontChroot: false + +# Tune this for the commands you're actually running, or +# use the list-of-items form of commands to tune the timeout +# for each command individually. +timeout: 10 + +# This will copy the output from the command into the Calamares +# log file. No processing is done beyond log-each-line-separately, +# so this can introduce weirdness in the log if the script +# outputs e.g. escape codes. +# +# The default is `false`. This can also be set for each +# command individually. +verbose: false + +# Script may be a single string (because false returns an error exit +# code, this will trigger a failure in the installation): +# +# script: "/usr/bin/false" + +# Script may be a list of strings (because false returns an error exit +# code, **but** the command starts with a "-", the error exit is +# ignored and installation continues): +# +# script: +# - "-/usr/bin/false" +# - "/bin/ls" +# - "/usr/bin/true" + +# Script may be a list of items +# - if the touch command fails, it is ignored +# - there is nothing special about the invocation of true +# - the slowloris command has a different timeout from the other commands +# - the echo command logs its output line-by-line +script: + - "-touch ${ROOT}/tmp/thingy" + - "/usr/bin/true" + - command: "/usr/local/bin/slowloris" + timeout: 3600 + - command: "echo -e '\e[33;2mred\e[33;0m' ; echo second line" + verbose: true + +# You can change the description of the job (as it is displayed in the +# progress bar during installation) by defining an *i18n* key, which +# has a *name* field and optionally, translations as *name[lang]*. +# +# Without a translation here, the default name from the source code +# is used, "Shell Processes Job". +# +# i18n: +# name: "Shell process" +# name[nl]: "Schelpenpad" +# name[en_GB]: "Just a moment" diff --git a/airootfs/usr/share/calamares/modules/tracking.conf b/airootfs/usr/share/calamares/modules/tracking.conf new file mode 100644 index 0000000..fdbd355 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/tracking.conf @@ -0,0 +1,105 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Settings for various kinds of tracking that Distributions can +# enable. Distributions looking at tracking should be aware of +# the privacy (and hence communications) impact of that tracking, +# and are advised to consult the Mozilla and KDE policies on +# privacy and user tracking. +# +# There are three areas of tracking (-configuration) supported +# by Calamares It is up to individual Distributions to create +# suitable backends / configuration scripts for each. The +# different areas are: +# +# install: This is "phone home" functionality at the end of the +# install process. When enabled, it contacts the given +# URL. The URL can contain the special token $MACHINE, +# which is replaced by the machine-id of the installed +# system (if available, blank otherwise). +# +# machine: This enables machine-level tracking on a (semi-) +# continuous basis. It is meant to keep track of installed +# systems and their continued use / updating. +# +# user: This area enables user-level tracking, along the lines +# of the KDE User Telemetry Policy. It enables specific +# collection of data at a user- and application-level, +# possibly including actions done in an application. +# For the KDE environment, this enables user tracking +# with the appropriate framework, and the KDE User Telemetry +# policy applies. +# +# Each area has a key *enabled*. If the area is enabled, it is shown to +# the user. This defaults to false, which means no tracking would be +# configured or enabled by Calamares. +# +# Each area has a key *policy*, which is a Url to be opened when +# the user clicks on the corresponding Help button for an explanation +# of the details of that particular kind of tracking. If no policy +# is set, that tracking style is disabled. The example policy links +# go to Calamares' generic user manual (which is a terrible idea +# for a distribution: you have GDPR obligations under most of these +# tracking styles, so do your homework). +# +# Each area may have other configuration keys, depending on the +# area and how it needs to be configured. +# +# Globally, there are two other keys: +# +# policy: (optional) url about tracking settings for this distro. +# default: (optional) level to enable by default +# +--- +# This is the global policy; it is displayed as a link on the page. +# If blank or commented out, no link is displayed on the tracking +# page. You **must** provide policy links per-area as well. +policy: "https://calamares.io/docs/tracking#policy" + +# This is the default area to enable for tracking. If commented out, +# empty, or otherwise invalid, "none" is used, so no tracking by default. +# Setting an area here also checks the areas before it (install, machine, +# then user) by default -- subject to those areas being enabled at all. +# default: user + +# The install area has one specific configuration key: +# url: this URL (remember to include the protocol, and prefer https) +# is fetched (with a GET request, and the data discarded) at +# the end of the installation process. The following tokens +# are replaced in the url (possibly by blank strings, or by 0). +# - $CPU (cpu make and model) +# - $MEMORY (amount of main memory available) +# - $DISK (total amount of disk attached) +# Typically these are used as GET parameters, as in the example. +# +# Note that phone-home only works if the system has an internet +# connection; it is a good idea to require internet in the welcome +# module then. +install: + enabled: false + policy: "https://calamares.io/docs/tracking#policy" + url: "https://example.com/install.php?c=$CPU&m=$MEMORY" + +# The machine area has one specific configuration key: +# style: This string specifies what kind of tracking configuration +# needs to be done. See below for valid styles. +# +# Available styles: +# - *updatemanager* replaces the literal string "${MACHINE_ID}" with the contents of +# /etc/machine-id, in lines starting with "URI" in the file /etc/update-manager/meta-release +machine: + enabled: false + style: updatemanager + policy: "https://calamares.io/docs/tracking#policy" + +# The user area has one specific configuration key: +# style: This string specifies what kind of tracking configuration +# needs to be done. See below for valid styles. +# +# Available styles: +# - *kuserfeedback* sets up KUserFeedback tracking (applicable to the KDE +# Plasma Desktop) for each KUserFeedback area listed in *areas*. +user: + enabled: false + style: kuserfeedback + areas: [ PlasmaUserFeedback ] diff --git a/airootfs/usr/share/calamares/modules/umount.conf b/airootfs/usr/share/calamares/modules/umount.conf new file mode 100644 index 0000000..5842c87 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/umount.conf @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +### Umount Module +# +# This module represents the last part of the installation, the unmounting +# of partitions used for the install. After this, there is no regular way +# to modify the target system anymore. +# + +--- +# Setting emergency to true will make it so this module is still run +# when a prior module fails +emergency: true diff --git a/airootfs/usr/share/calamares/modules/unpackfs.conf b/airootfs/usr/share/calamares/modules/unpackfs.conf new file mode 100644 index 0000000..6282769 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/unpackfs.conf @@ -0,0 +1,123 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Unsquash / unpack a filesystem. Multiple sources are supported, and +# they may be squashed or plain filesystems. +# +# Configuration: +# +# from globalstorage: rootMountPoint +# from job.configuration: the path to where to mount the source image(s) +# for copying an ordered list of unpack mappings for image file <-> +# target dir relative to rootMountPoint. + +--- +# Each list item is unpacked, in order, to the target system. +# +# Each list item has the following **mandatory** attributes: +# - *source* path relative to the live / intstalling system to the image +# - *sourcefs* the type of the source files; valid entries are +# - `ext4` (copies the filesystem contents) +# - `squashfs` (unsquashes) +# - `file` (copies a file or directory) +# - (may be others if mount supports it) +# - *destination* path relative to rootMountPoint (so in the target +# system) where this filesystem is unpacked. It may be an +# empty string, which effectively is / (the root) of the target +# system. +# +# Each list item **optionally** can include the following attributes: +# - *exclude* is a list of values that is expanded into --exclude +# arguments for rsync (each entry in exclude gets its own --exclude). +# - *excludeFile* is a single file that is passed to rsync as an +# --exclude-file argument. This should be a full pathname +# inside the **host** filesystem. +# - *weight* is useful when the entries take wildly different +# times to unpack (e.g. with a squashfs, and one single file) +# and the total weight of this module should be distributed +# differently between the entries. (This is only relevant when +# there is more than one entry; by default all the entries +# have the same weight, 1) +# +# EXAMPLES +# +# Usually you list a filesystem image to unpack; you can use +# squashfs or an ext4 image. An empty destination is equivalent to "/", +# the root of the target system. The destination directory must exist +# in the target system. +# +# - source: "/path/to/filesystem.sqfs" +# sourcefs: "squashfs" +# destination: "" +# +# Multiple entries are unpacked in-order; if there is more than one +# item then only the first must exist beforehand -- it's ok to +# create directories with one unsquash and then to use those +# directories as a target from a second unsquash. +# +# - source: "/path/to/another/filesystem.img" +# sourcefs: "ext4" +# destination: "" +# - source: "/path/to/another/filesystem2.img" +# sourcefs: "ext4" +# destination: "/usr/lib/extra" +# +# You can list filesystem source paths relative to the Calamares run +# directory, if you use -d (this is only useful for testing, though). +# +# - source: ./example.sqfs +# sourcefs: squashfs +# destination: "" +# +# You can list individual files (copied one-by-one), or directories +# (the files inside this directory are copied directly to the destination, +# so no "dummycpp/" subdirectory is created in this example). +# Do note that the target directory must exist already (e.g. from +# extracting some other filesystem). +# +# - source: ../CHANGES +# sourcefs: file +# destination: "/tmp/derp" +# - source: ../src/modules/dummycpp +# sourcefs: file +# destination: "/tmp/derp" +# +# The *destination* and *source* are handed off to rsync, so the semantics +# of trailing slashes apply. In order to *rename* a file as it is +# copied, specify one single file (e.g. CHANGES) and a full pathname +# for its destination name, as in the example below. +# +# It is also possible to dynamically (conditionally) unpack a source by passing a boolean +# value for *condition*. This may be true or false (constant) or name a globalstorage +# value. Use '.' to separate parts of a globalstorage name if it is nested. +# +# This is used in e.g. stacked squashfses, where the user can select a specific +# install type. The default value of *condition* is true. +# +# - source: ./example.minimal.sqfs +# sourcefs: squashfs +# destination: "" +# condition: false +# - source: ./example.standard.sqfs +# sourcefs: squashfs +# destination: "" +# condition: exampleGlobalStorageVariable.subkey +# +# You may also wish to include optional squashfses, which may not exist at certain times +# depending on your image tooling. If an optional squashfs is not found, it is simply +# skipped. +# +# - source: ./example.standard.sqfs +# sourcefs: squashfs +# destination: "" +# - source: ./example.extras.sqfs +# sourcefs: squashfs +# destination: "" +# optional: true +unpack: + - source: "/run/archiso/bootmnt/arch/x86_64/airootfs.sfs" + sourcefs: "squashfs" + destination: "" + - source: "/run/archiso/bootmnt/arch/boot/x86_64/vmlinuz-linux" + sourcefs: "file" + destination: "/boot/vmlinuz-linux" diff --git a/airootfs/usr/share/calamares/modules/unpackfsc.conf b/airootfs/usr/share/calamares/modules/unpackfsc.conf new file mode 100644 index 0000000..b8780ac --- /dev/null +++ b/airootfs/usr/share/calamares/modules/unpackfsc.conf @@ -0,0 +1,74 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Unpack a filesystem. Supported ways to "pack" the filesystem are: +# - erofs +# Enhanced Read-Only File System is a relatively new filesystem, introduced +# in 2019 with Linux 5.4, which is optimised for runtime performance. EROFS +# has compression options similar to that of SquashFS, however, in +# direct head to head comparison, as of 2025, the EROFS compression +# algorithms are not as performant; it takes longer to pack a filesystem and +# the compression size is not quite as good. EROFS is under active +# development. +# . +# Wikipedia: https://en.wikipedia.org/wiki/EROFS +# Upstream documentation: https://erofs.docs.kernel.org/en/latest/ +# . +# erofs-utils contains mkfs.erofs, fsck.erofs and dump.erofs utilities +# which are required to pack, inspect and extract erofs files. +# - fsarchiver in *savedir/restdir* mode (directories, not block devices) +# - squashfs +# SquashFS is a compressed read-only file system for Linux that was +# introduced in 2002. It supports deduplication and has mature and +# performant compression algorithms. +# . +# Wikipedia: https://en.wikipedia.org/wiki/SquashFS +# Upstream documentation: https://github.com/plougher/squashfs-tools +# . +# squashfs-tools contains mksquashfs and unsquashfs utilities which are +# required to pack and extract squashfs files. +# +# Configuration: +# +# from globalstorage: rootMountPoint +# from job configuration: the item to unpack +# + +--- +# This module is configured a lot like the items in the *unpackfs* +# module, but with only **one** item. Use multiple instances for +# unpacking more than one filesystem. +# +# There are the following **mandatory** keys: +# - *source* path relative to the live / intstalling system to the image +# - *sourcefs* the type of the source files; valid entries are +# - `none` (this entry is ignored; kind of useless) +# - `erofs` +# - `fsarchiver` +# Aliases of this are `fsarchive`, `fsa` and `fsa-dir`. Uses +# fsarchiver in "restdir" mode. +# - `fsarchiver-block` +# Aliases of this are `fsa-block` and `fsa-fs`. Uses fsarchiver +# in "restfs" mode. +# - `squashfs` +# Aliases of this are `squash` and `unsquash`. +# - `tar` +# - *destination* path relative to rootMountPoint (so in the target +# system) where this filesystem is unpacked. It may be an +# empty string, which effectively is / (the root) of the target +# system. +# +# +# There are the following **optional** keys: +# - *condition* sets a dynamic condition on unpacking the item in +# this job. This may be true or false (constant) or name a globalstorage +# value. Use '.' to separate parts of a globalstorage name if it is nested. +# Remember to quote names. +# +# A condition is used in e.g. stacked squashfses, where the user can select +# a specific install type. The default value of *condition* is true. + +source: /data/rootfs.fsa +sourcefs: fsarchiver +destination: "/" +# condition: true diff --git a/airootfs/usr/share/calamares/modules/users.conf b/airootfs/usr/share/calamares/modules/users.conf new file mode 100644 index 0000000..0a9adf5 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/users.conf @@ -0,0 +1,312 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the one-user-system user module. +# +# Besides these settings, the users module also places the following +# keys into the Global Storage area, based on user input in the view step. +# +# - hostname +# - username +# - password (obscured) +# - autologinUser (if enabled, set to username) +# +# These Global Storage keys are set when the configuration for this module +# is read and when they are modified in the UI. +--- +### GROUPS CONFIGURATION +# +# The system has groups of uses. Some special groups must be +# created during installation. Optionally, there are special +# groups for users who can use sudo and for supporting autologin. + +# Used as default groups for the created user. +# Adjust to your Distribution defaults. +# +# Each entry in the *defaultGroups* list is either: +# - a string, naming a group; this is a **non**-system group +# which does not need to exist in the target system; if it +# does not exist, it will be created. +# - an entry with subkeys *name*, *must_exist* and *system*; +# if the group *must_exist* and does not, an error is thrown +# and the installation fails. +# +# The group is created if it does not exist, and it is +# created as a system group (GID < 1000) or user group +# (GID >= 1000) depending on the value of *system*. +defaultGroups: + - name: users + must_exist: true + system: true + - lp + - video + - network + - storage + - name: wheel + must_exist: false + system: true + - audio + - name: nopasswdlogin + must_exist: false + system: true + +# When *sudoersGroup* is set to a non-empty string, Calamares creates a +# sudoers file for the user. This file is located at: +# `/etc/sudoers.d/10-installer` +# Remember to add the (value of) *sudoersGroup* to *defaultGroups*. +# +# If your Distribution already sets up a group of sudoers in its packaging, +# remove this setting (delete or comment out the line below). Otherwise, +# the setting will be duplicated in the `/etc/sudoers.d/10-installer` file, +# potentially confusing users. +sudoersGroup: wheel + +# Some Distributions require a 'autologin' group for the user. +# Autologin causes a user to become automatically logged in to +# the desktop environment on boot. +# Disable when your Distribution does not require such a group. +# +# Remember to add the (value of) *autologinGroup* to *defaultGroups*. +autologinGroup: autologin + +# See also *user.nopasswd_group* for another group that is optionally added to the user + +### ROOT AND SUDO +# +# Some distributions have a root user enabled for login. Others +# rely entirely on sudo or similar mechanisms to raise privileges. + +# If set to `false` (the default), writes a sudoers file with `ALL=(ALL)` +# so that commands can be run as any user. If set to `true`, writes +# `ALL=(ALL:ALL)` so that any user and any group can be chosen. +sudoersConfigureWithGroup: false + +# Setting this to false, causes the root account to be disabled. +# When disabled, hides the "Use the same password for administrator" +# checkbox. Also hides the "Choose a password" and associated text-inputs. +setRootPassword: true + +# You can control the initial state for the 'reuse password for root' +# checkbox here. Possible values are: +# - true to check or +# - false to uncheck +# +# When checked, the user password is used for the root account too. +# +# NOTE: *doReusePassword* requires *setRootPassword* to be enabled. +doReusePassword: true + + +### PASSWORDS AND LOGIN +# +# Autologin is convenient for single-user systems, but depends on +# the location of the machine if it is practical. "Password strength" +# measures measures might improve security by enforcing hard-to-guess +# passwords, or might encourage a post-it-under-the-keyboard approach. +# Distributions are free to steer their users to one kind of password +# or another. Weak(er) passwords may be allowed, may cause a warning, +# or may be forbidden entirely. + +# Autologin choice can be display to the user. +# Possible values are: +# - true to display it +# - false to hide it +# By default, this value is set to true. +displayAutologin: true + +# You can control the initial state for the 'autologin checkbox' here. +# Possible values are: +# - true to check or +# - false to uncheck +# These set the **initial** state of the checkbox. +doAutologin: true + +# These are optional password-requirements that a distro can enforce +# on the user. The values given in this sample file set only very weak +# validation settings. +# +# Calamares itself supports two checks: +# - minLength +# - maxLength +# In this sample file, the values are set to -1 which means "no +# minimum", "no maximum". This allows any password at all. +# No effort is done to ensure that the checks are consistent +# (e.g. specifying a maximum length less than the minimum length +# will annoy users). +# +# Calamares supports password checking through libpwquality. +# The libpwquality check relies on the (optional) libpwquality library. +# The value for libpwquality is a list of configuration statements like +# those found in pwquality.conf. The statements are handed off to the +# libpwquality parser for evaluation. The check is ignored if +# libpwquality is not available at build time (generates a warning in +# the log). The Calamares password check rejects passwords with a +# score of < 40 with the given libpwquality settings. +# +# (additional checks may be implemented in CheckPWQuality.cpp and +# wired into UsersPage.cpp) +# +# To disable all password validations: +# - comment out the relevant 'passwordRequirements' keys below, +# or set minLength and maxLength to -1. +# - disable libpwquality at build-time. +# To allow all passwords, but provide warnings: +# - set both 'allowWeakPasswords' and 'allowWeakPasswordsDefault' to true. +# (That will show the box *Allow weak passwords* in the user- +# interface, and check it by default). +# - configure password-checking however you wish. +# To require specific password characteristics: +# - set 'allowWeakPasswords' to false (the default) +# - configure password-checking, e.g. with NIST settings + + +# These are very weak -- actually, none at all -- requirements +passwordRequirements: + minLength: -1 # Password at least this many characters + maxLength: -1 # Password at most this many characters + libpwquality: + - minlen=0 + - minclass=0 + +# These are "you must have a password, any password" -- requirements +# +# passwordRequirements: +# minLength: 1 + +# These are requirements the try to follow the suggestions from +# https://pages.nist.gov/800-63-3/sp800-63b.html , "Digital Identity Guidelines". +# Note that requiring long and complex passwords has its own cost, +# because the user has to come up with one at install time. +# Setting 'allowWeakPasswords' to false and 'doAutologin' to false +# will require a strong password and prevent (graphical) login +# without the password. It is likely to be annoying for casual users. +# +# passwordRequirements: +# minLength: 8 +# maxLength: 64 +# libpwquality: +# - minlen=8 +# - maxrepeat=3 +# - maxsequence=3 +# - usersubstr=4 +# - badwords=linux + +# You can control the visibility of the 'strong passwords' checkbox here. +# Possible values are: +# - true to show or +# - false to hide (default) +# the checkbox. This checkbox allows the user to choose to disable +# password-strength-checks. By default the box is **hidden**, so +# that you have to pick a password that satisfies the checks. +allowWeakPasswords: false +# You can control the initial state for the 'strong passwords' checkbox here. +# Possible values are: +# - true to uncheck or +# - false to check (default) +# the checkbox by default. Since the box is labeled to enforce strong +# passwords, in order to **allow** weak ones by default, the box needs +# to be unchecked. +allowWeakPasswordsDefault: false + + +# User settings +# +# The user can enter a username, but there are some other +# hidden settings for the user which are configurable in Calamares. +# +# Key *user* has the following sub-keys: +# +# - *shell* Shell to be used for the regular user of the target system. +# There are three possible kinds of settings: +# - unset (i.e. commented out, the default), act as if set to /bin/bash +# - empty (explicit), don't pass shell information to useradd at all +# and rely on a correct configuration file in /etc/default/useradd +# - set, non-empty, use that path as shell. No validation is done +# that the shell actually exists or is executable. +# - *forbidden_names* Login names that may not be used. This list always +# 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. 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`. +# +# You may write permissions as: +# - write "NNN" (three octal digits) or +# - write "oNNN" (small 'o' and three octal digits) or +# - write "rwxrwxrwx" (like the output of ls, with a - for unset bits) +# The following permissions mean the same thing: "o750", "rwxr-x---" . +# - *nopasswd_group* If set, **and** the user sets no password, then +# the user is added to this group as well. Whether "no password" is +# allowed depends on other settings in this module; distributions that +# require a specific group for "no password" login should set this +# **and** configure the group in the *defaultGroups* section, above. +# The default is unset; this example configuration is non-default. +user: + shell: /bin/bash + forbidden_names: [ root ] + home_permissions: "o700" + nopasswd_group: "nopasswdlogin" + + +# Hostname settings +# +# The user can enter a hostname; this is configured into the system +# in some way. There are settings for how a hostname is guessed (as +# a default / suggestion) and where (or how) the hostname is set in +# the target system. +# +# Key *hostname* has the following sub-keys: +# +# - *location* How the hostname is set in the target system: +# - *None*, to not set the hostname at all +# - *EtcFile*, to write to `/etc/hostname` directly +# - *Etc*, identical to above +# - *Hostnamed*, to use systemd hostnamed(1) over DBus +# - *Transient*, to remove `/etc/hostname` from the target +# The default is *EtcFile*. Setting this to *None* or *Transient* will +# hide the hostname field. +# - *writeHostsFile* Should /etc/hosts be written with a hostname for +# this machine (also adds localhost and some ipv6 standard entries). +# Defaults to *true*. +# - *template* Is a simple template for making a suggestion for the +# hostname, based on user data. The default is "${first}-${product}". +# This is used only if the hostname field is shown. KMacroExpander is +# used; write `${key}` where `key` is one of the following: +# - *first* User's first name (whatever is first in the User Name field, +# which is first-in-order but not necessarily a "first name" as in +# "given name" or "name by which you call someone"; beware of western bias) +# - *name* All the text in the User Name field. +# - *login* The login name (which may be suggested based on User Name) +# - *product* The hardware product, based on DMI data +# - *product2* The product as described by Qt +# - *cpu* CPU name +# - *host* Current hostname (which may be a transient hostname) +# Literal text in the template is preserved. Calamares tries to map +# `${key}` values to something that will fit in a hostname, but does not +# apply the same to literal text in the template. Do not use invalid +# characters in the literal text, or no suggeston will be done. +# - *forbidden_names* lists hostnames that may not be used. This list +# always contains "localhost", but may list others that are unsuitable +# or broken in special ways. +hostname: + location: EtcFile + writeHostsFile: true + template: "derp-${cpu}" + 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: + fullName: + # value: "OEM User" + editable: true + loginName: + # value: "oem" + editable: true diff --git a/airootfs/usr/share/calamares/modules/usersq.conf b/airootfs/usr/share/calamares/modules/usersq.conf new file mode 100644 index 0000000..ea171bd --- /dev/null +++ b/airootfs/usr/share/calamares/modules/usersq.conf @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# For documentation see Users Module users.conf +# +--- +# Used as default groups for the created user. +# Adjust to your Distribution defaults. +defaultGroups: + - users + - lp + - video + - network + - storage + - wheel + - audio + - lpadmin + +autologinGroup: autologin + +doAutologin: true + +sudoersGroup: wheel + +setRootPassword: true + +doReusePassword: true + +passwordRequirements: + minLength: -1 + maxLength: -1 + libpwquality: + - minlen=0 + - minclass=0 + +allowWeakPasswords: false + +allowWeakPasswordsDefault: false + +userShell: /bin/bash + +setHostname: EtcFile + +writeHostsFile: true diff --git a/airootfs/usr/share/calamares/modules/welcome.conf b/airootfs/usr/share/calamares/modules/welcome.conf new file mode 100644 index 0000000..29cd905 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/welcome.conf @@ -0,0 +1,138 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the welcome module. The welcome page +# displays some information from the branding file. +# Which parts it displays can be configured through +# the show* variables. +# +# In addition to displaying the welcome page, this module +# can check requirements for installation. +--- +# Display settings for various buttons on the welcome page. +# The URLs themselves come from `branding.desc`. Each button +# is show if the corresponding *show* setting +# here is "true". If the setting is "false", the button is hidden. +# Empty or not-set is interpreted as "false". +# +# TODO:3.3 Remove the URL fallback here; URLs only in `branding.desc` +# +# The setting can also be a full URL which will then be used +# instead of the one from the branding file. +showSupportUrl: true +showKnownIssuesUrl: true +showReleaseNotesUrl: false +# TODO:3.3 Move to branding, keep only a bool here +showDonateUrl: https://kde.org/community/donations/ + +# Requirements checking. These are general, generic, things +# that are checked. They may not match with the actual requirements +# imposed by other modules in the system. +requirements: + # Amount of available disk, in GiB. Floating-point is allowed here. + # Note that this does not account for *usable* disk, so it is possible + # to satisfy this requirement, yet have no space to install to. + requiredStorage: 5.5 + + # Amount of available RAM, in GiB. Floating-point is allowed here. + requiredRam: 1.0 + + # To check for internet connectivity, Calamares does a HTTP GET + # on this URL; on success (e.g. HTTP code 200) internet is OK. + # Use a privacy-respecting URL here, preferably in your own + # distribution domain. + # + # The URL is only used if "internet" is in the *check* list below. + internetCheckUrl: http://example.com + # + # This may be a single URL, or a list or URLs, in which case the + # URLs will be checked one-by-one; if any of them returns data, + # internet is assumed to be OK. This can be used to check via + # a number of places, where some domains may be down or blocked. + # + # To use a list of URLs, just use YAML list syntax (e.g. + # + # internetCheckUrl: + # - http://www.kde.org + # - http://www.freebsd.org + # + # or short-form + # + # internetCheckUrl: [ http://www.kde.org, http://www.freebsd.org ] + + # List conditions to check. Each listed condition will be + # probed in some way, and yields true or false according to + # the host system satisfying the condition. + # + # This sample file lists all the conditions that are known. + # + # Note that the last three checks are for testing-purposes only, + # and shouldn't be used in production (they are only available + # when building Calamares in development mode). There are five + # special checks: + # - *false* is a check that is always false (unsatisfied) + # - *true* is a check that is always true (satisfied) + # - *slow-false* takes 3 seconds, and then is false; use this one to + # show off the waiting-spinner before the first results come in + # - *slow-true* takes 3 seconds, and then is true + # - *snark* is a check that is only satisfied once it has been checked + # at least three times ("what I tell you three times is true"). + # Keep in mind that "true" and "false" are YAML keywords for + # boolean values, so should be quoted. + check: + - storage + - ram + - power + - internet + - root + - screen + - "false" + - slow-true + - snark + # List conditions that **must** be satisfied (from the list + # of conditions, above) for installation to proceed. + # If any of these conditions are not met, the user cannot + # continue past the welcome page. + required: + # - storage + - ram + # - root + +# GeoIP checking +# +# This can be used to pre-select a language based on the country +# the user is currently in. It *assumes* that there's internet +# connectivity, though. Configuration is like in the locale module, +# but remember to use a URL that returns full data **and** to +# use a selector that will pick the country, not the timezone. +# +# To disable GeoIP checking, either comment-out the entire geoip section, +# or set the *style* key to an unsupported format (e.g. `none`). +# Also, note the analogous feature in `src/modules/locale/locale.conf`, +# which is where you will find complete documentation. +# +# For testing, the *style* may be set to `fixed`, any URL that +# returns data (e.g. `http://example.com`) and then *selector* +# sets the data that is actually returned (e.g. "DE" to simulate +# the machine being in Germany). +# +# NOTE: the *selector* must pick the country code from the GeoIP +# data. Timezone, city, or other data will not be recognized. +# +geoip: + style: "none" + url: "https://geoip.kde.org/v1/ubiquity" # extended XML format + selector: "CountryCode" # blank uses default, which is wrong + +# User interface +# +# The "select language" icon is an international standard, but it +# might not theme very well with your desktop environment. +# Fill in an icon name (following FreeDesktop standards) to +# use that named icon instead of the usual one. +# +# Leave blank or unset to use the international standard. +# +# Known icons in this space are "set-language" and "config-language". +# +# languageIcon: set-language diff --git a/airootfs/usr/share/calamares/modules/welcomeq.conf b/airootfs/usr/share/calamares/modules/welcomeq.conf new file mode 100644 index 0000000..2efc514 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/welcomeq.conf @@ -0,0 +1,40 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration for the welcomeq module. +# +# The configuration for welcomeq is exactly the same +# as the welcome module, with the one exception of +# *qmlSearch* which governs QML loading. +# +# No documentation is given here: look in the welcome module. +--- +# Setting for QML loading: use QRC, branding, or both sources of files +qmlSearch: both + +# Everythin below here is documented in `welcome.conf` +showSupportUrl: true +showKnownIssuesUrl: true +showReleaseNotesUrl: true +# showDonateUrl: https://kde.org/community/donations/ + +requirements: + requiredStorage: 5.5 + requiredRam: 1.0 + internetCheckUrl: http://google.com + check: + - storage + - ram + - power + - internet + - root + - screen + required: + - ram + +geoip: + style: "none" + url: "https://geoip.kde.org/v1/ubiquity" # extended XML format + selector: "CountryCode" # blank uses default, which is wrong + +#languageIcon: languages diff --git a/airootfs/usr/share/calamares/modules/zfs.conf b/airootfs/usr/share/calamares/modules/zfs.conf new file mode 100644 index 0000000..e5a0aa3 --- /dev/null +++ b/airootfs/usr/share/calamares/modules/zfs.conf @@ -0,0 +1,45 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# The zfs module creates the zfs pools and datasets +# +# +# +--- +# The name to be used for the zpool +poolName: zpcala + +# A list of options that will be passed to zpool create +# +# Encryption options should generally not be added here since they will be added by +# selecting the encrypt disk option in the partition module +poolOptions: "-f -o ashift=12 -O mountpoint=none -O acltype=posixacl -O relatime=on" + +# A list of options that will be passed to zfs create when creating each dataset +# Do not include "canmount" or "mountpoint" as those are set below in the datasets array +datasetOptions: "-o compression=lz4 -o atime=off -o xattr=sa" + +# An array of datasets that will be created on the zpool mounted at / +# +# This default configuration is commonly used when support for booting more than one distro +# out of a single zpool is desired. If you decide to keep this default configuration, +# you should replace "distro" with an identifier that represents your distro. +datasets: + - dsName: ROOT + mountpoint: none + canMount: off + - dsName: ROOT/distro + mountpoint: none + canMount: off + - dsName: ROOT/distro/root + mountpoint: / + canMount: noauto + - dsName: ROOT/distro/home + mountpoint: /home + canMount: on + - dsName: ROOT/distro/varcache + mountpoint: /var/cache + canMount: on + - dsName: ROOT/distro/varlog + mountpoint: /var/log + canMount: on diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/BackButton.qml b/airootfs/usr/share/calamares/qml/calamares/slideshow/BackButton.qml new file mode 100644 index 0000000..4e420e0 --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/BackButton.qml @@ -0,0 +1,15 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +NavButton { + id: backButton + anchors.left: parent.left + visible: parent.currentSlide > 0 + isForward: false +} diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/ForwardButton.qml b/airootfs/usr/share/calamares/qml/calamares/slideshow/ForwardButton.qml new file mode 100644 index 0000000..7838fab --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/ForwardButton.qml @@ -0,0 +1,14 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +NavButton { + id: forwardButton + anchors.right: parent.right + visible: parent.currentSlide + 1 < parent.slides.length; +} diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/NavButton.qml b/airootfs/usr/share/calamares/qml/calamares/slideshow/NavButton.qml new file mode 100644 index 0000000..bdb2f40 --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/NavButton.qml @@ -0,0 +1,59 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* This is a navigation (arrow) button that fades in on hover, and + * which calls forward / backward navigation on the presentation it + * is in. It should be a child item of the presentation (not of a + * single slide). Use the ForwardButton or BackButton for a pre- + * configured instance that interacts with the presentation. + */ + +import QtQuick 2.5; + +Image { + id: fade + + property bool isForward : true + + width: 100 + height: 100 + anchors.verticalCenter: parent.verticalCenter + opacity: 0.3 + + OpacityAnimator { + id: fadeIn + target: fade + from: fade.opacity + to: 1.0 + duration: 500 + running: false + } + + OpacityAnimator { + id: fadeOut + target: fade + from: fade.opacity + to: 0.3 + duration: 250 + running: false + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: { fadeOut.running = false; fadeIn.running = true } + onExited: { fadeIn.running = false ; fadeOut.running = true } + onClicked: { + if (isForward) + fade.parent.goToNextSlide() + else + fade.parent.goToPreviousSlide() + } + } +} diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/Presentation.qml b/airootfs/usr/share/calamares/qml/calamares/slideshow/Presentation.qml new file mode 100644 index 0000000..7abc14f --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/Presentation.qml @@ -0,0 +1,238 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2017 Adriaan de Groot + * SPDX-FileCopyrightText: 2016 The Qt Company Ltd. + * SPDX-License-Identifier: LGPL-2.1-only + * + * 2017, Adriaan de Groot + * - added looping, keys-instead-of-shortcut + * 2018, Adriaan de Groot + * - make looping a property, drop the 'c' fade-key + * - drop navigation through entering a slide number + * (this and the 'c' key make sense in a *presentation* + * slideshow, not in a passive slideshow like Calamares) + * - remove quit key + * 2019, Adriaan de Groot + * - Support "V2" loading + * - Disable shortcuts until the content is visible in Calamares + * 2020, Adriaan de Groot + * - Updated to SPDX headers + */ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QML Presentation System. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Window 2.0 + +Item { + id: root + + property variant slides: [] + property int currentSlide: 0 + + property bool loopSlides: true + + property bool showNotes: false; + property bool allowDelay: true; + property alias mouseNavigation: mouseArea.enabled + property bool arrowNavigation: true + property bool keyShortcutsEnabled: true + + property color titleColor: textColor; + property color textColor: "black" + property string fontFamily: "Helvetica" + property string codeFontFamily: "Courier New" + + // This is set by the C++ part of Calamares when the slideshow + // becomes visible. You can connect it to a timer, or whatever + // else needs to start only when the slideshow becomes visible. + // + // It is used in this example also to keep the keyboard shortcuts + // enabled only while the slideshow is active. + property bool activatedInCalamares: false + + // Private API + property int _lastShownSlide: 0 + + Component.onCompleted: { + var slideCount = 0; + var slides = []; + for (var i=0; i 0) + root.slides[root.currentSlide].visible = true; + } + + function switchSlides(from, to, forward) { + from.visible = false + to.visible = true + return true + } + + onCurrentSlideChanged: { + switchSlides(root.slides[_lastShownSlide], root.slides[currentSlide], currentSlide > _lastShownSlide) + _lastShownSlide = currentSlide + // Always keep focus on the slideshow + root.focus = true + } + + function goToNextSlide() { + if (root.slides[currentSlide].delayPoints) { + if (root.slides[currentSlide]._advance()) + return; + } + if (currentSlide + 1 < root.slides.length) + ++currentSlide; + else if (loopSlides) + currentSlide = 0; // Loop at the end + } + + function goToPreviousSlide() { + if (currentSlide - 1 >= 0) + --currentSlide; + else if (loopSlides) + currentSlide = root.slides.length - 1 + } + + focus: true // Keep focus + + // Navigation through key events, too + Keys.onSpacePressed: goToNextSlide() + Keys.onRightPressed: goToNextSlide() + Keys.onLeftPressed: goToPreviousSlide() + + // navigate with arrow keys + Shortcut { sequence: StandardKey.MoveToNextLine; enabled: root.activatedInCalamares && root .arrowNavigation; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousLine; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToPreviousSlide() } + Shortcut { sequence: StandardKey.MoveToNextChar; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousChar; enabled: root.activatedInCalamares && root.arrowNavigation; onActivated: goToPreviousSlide() } + + // presentation-specific single-key shortcuts (which interfere with normal typing) + Shortcut { sequence: " "; enabled: root.activatedInCalamares && root.keyShortcutsEnabled; onActivated: goToNextSlide() } + + // standard shortcuts + Shortcut { sequence: StandardKey.MoveToNextPage; enabled: root.activatedInCalamares; onActivated: goToNextSlide() } + Shortcut { sequence: StandardKey.MoveToPreviousPage; enabled: root.activatedInCalamares; onActivated: goToPreviousSlide() } + + MouseArea { + id: mouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if (mouse.button == Qt.RightButton) + goToPreviousSlide() + else + goToNextSlide() + } + onPressAndHold: goToPreviousSlide(); //A back mechanism for touch only devices + } + + Window { + id: notesWindow; + width: 400 + height: 300 + + title: "QML Presentation: Notes" + visible: root.showNotes + + Flickable { + anchors.fill: parent + contentWidth: parent.width + contentHeight: textContainer.height + + Item { + id: textContainer + width: parent.width + height: notesText.height + 2 * notesText.padding + + Text { + id: notesText + + anchors.margins: 16 + + font.pixelSize: 16 + wrapMode: Text.WordWrap + + property string notes: root.slides[root.currentSlide].notes; + + onNotesChanged: { + var result = ""; + + var lines = notes.split("\n"); + var beginNewLine = false + for (var i=0; i 0) + result += " "; + result += line; + } + } + + if (result.length == 0) { + font.italic = true; + text = "no notes.." + } else { + font.italic = false; + text = result; + } + } + } + } + } + } +} diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/Slide.qml b/airootfs/usr/share/calamares/qml/calamares/slideshow/Slide.qml new file mode 100644 index 0000000..9cb9e73 --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/Slide.qml @@ -0,0 +1,206 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2012 Digia Plc and/or its subsidiary(-ies). + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QML Presentation System. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.5 + +Item { + /* + Slides can only be instantiated as a direct child of a Presentation {} as they rely on + several properties there. + */ + + id: slide + + property bool isSlide: true; + + property bool delayPoints: false; + property int _pointCounter: 0; + function _advance() { + if (!parent.allowDelay) + return false; + + _pointCounter = _pointCounter + 1; + if (_pointCounter < content.length) + return true; + _pointCounter = 0; + return false; + } + + property string title; + property variant content: [] + property string centeredText + property string writeInText; + property string notes; + + property real fontSize: parent.height * 0.05 + property real fontScale: 1 + + property real baseFontSize: fontSize * fontScale + property real titleFontSize: fontSize * 1.2 * fontScale + property real bulletSpacing: 1 + + property real contentWidth: width + + // Define the slide to be the "content area" + x: parent.width * 0.05 + y: parent.height * 0.2 + width: parent.width * 0.9 + height: parent.height * 0.7 + + property real masterWidth: parent.width + property real masterHeight: parent.height + + property color titleColor: parent.titleColor; + property color textColor: parent.textColor; + property string fontFamily: parent.fontFamily; + property int textFormat: Text.PlainText + + visible: false + + Text { + id: titleText + font.pixelSize: titleFontSize + text: title; + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.top + anchors.bottomMargin: parent.fontSize * 1.5 + font.bold: true; + font.family: slide.fontFamily + color: slide.titleColor + horizontalAlignment: Text.Center + z: 1 + } + + Text { + id: centeredId + width: parent.width + anchors.centerIn: parent + anchors.verticalCenterOffset: - parent.y / 3 + text: centeredText + horizontalAlignment: Text.Center + font.pixelSize: baseFontSize + font.family: slide.fontFamily + color: slide.textColor + wrapMode: Text.Wrap + } + + Text { + id: writeInTextId + property int length; + font.family: slide.fontFamily + font.pixelSize: baseFontSize + color: slide.textColor + + anchors.fill: parent; + wrapMode: Text.Wrap + + text: slide.writeInText.substring(0, length); + + NumberAnimation on length { + from: 0; + to: slide.writeInText.length; + duration: slide.writeInText.length * 30; + running: slide.visible && parent.visible && slide.writeInText.length > 0 + } + + visible: slide.writeInText != undefined; + } + + + Column { + id: contentId + anchors.fill: parent + + Repeater { + model: content.length + + Row { + id: row + + function decideIndentLevel(s) { return s.charAt(0) == " " ? 1 + decideIndentLevel(s.substring(1)) : 0 } + property int indentLevel: decideIndentLevel(content[index]) + property int nextIndentLevel: index < content.length - 1 ? decideIndentLevel(content[index+1]) : 0 + property real indentFactor: (10 - row.indentLevel * 2) / 10; + + height: text.height + (nextIndentLevel == 0 ? 1 : 0.3) * slide.baseFontSize * slide.bulletSpacing + x: slide.baseFontSize * indentLevel + visible: (!slide.parent.allowDelay || !delayPoints) || index <= _pointCounter + + Rectangle { + id: dot + anchors.baseline: text.baseline + anchors.baselineOffset: -text.font.pixelSize / 2 + width: text.font.pixelSize / 3 + height: text.font.pixelSize / 3 + color: slide.textColor + radius: width / 2 + opacity: text.text.length == 0 ? 0 : 1 + } + + Item { + id: space + width: dot.width * 1.5 + height: 1 + } + + Text { + id: text + width: slide.contentWidth - parent.x - dot.width - space.width + font.pixelSize: baseFontSize * row.indentFactor + text: content[index] + textFormat: slide.textFormat + wrapMode: Text.WordWrap + color: slide.textColor + horizontalAlignment: Text.AlignLeft + font.family: slide.fontFamily + } + } + } + } + +} diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/SlideCounter.qml b/airootfs/usr/share/calamares/qml/calamares/slideshow/SlideCounter.qml new file mode 100644 index 0000000..d5b2de7 --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/SlideCounter.qml @@ -0,0 +1,29 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2018 Adriaan de Groot + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Calamares is Free Software: see the License-Identifier above. + * + */ + +/* This control just shows a (non-translated) count of the slides + * in the slideshow in the format "n / total". + */ + +import QtQuick 2.5; + +Rectangle { + id: slideCounter + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 100 + height: 50 + + Text { + id: slideCounterText + anchors.centerIn: parent + //: slide counter, %1 of %2 (numeric) + text: qsTr("%L1 / %L2").arg(parent.parent.currentSlide + 1).arg(parent.parent.slides.length) + } +} diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/qmldir b/airootfs/usr/share/calamares/qml/calamares/slideshow/qmldir new file mode 100644 index 0000000..7b964b8 --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/qmldir @@ -0,0 +1,10 @@ +module calamares.slideshow + +Presentation 1.0 Presentation.qml +Slide 1.0 Slide.qml + +NavButton 1.0 NavButton.qml +ForwardButton 1.0 ForwardButton.qml +BackButton 1.0 BackButton.qml + +SlideCounter 1.0 SlideCounter.qml diff --git a/airootfs/usr/share/calamares/qml/calamares/slideshow/qmldir.license b/airootfs/usr/share/calamares/qml/calamares/slideshow/qmldir.license new file mode 100644 index 0000000..d2da9cf --- /dev/null +++ b/airootfs/usr/share/calamares/qml/calamares/slideshow/qmldir.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: no +SPDX-License-Identifier: CC0-1.0 diff --git a/airootfs/usr/share/calamares/settings.conf b/airootfs/usr/share/calamares/settings.conf new file mode 100644 index 0000000..5117322 --- /dev/null +++ b/airootfs/usr/share/calamares/settings.conf @@ -0,0 +1,236 @@ +# SPDX-FileCopyrightText: no +# SPDX-License-Identifier: CC0-1.0 +# +# Configuration file for Calamares +# +# This is the top-level configuration file for Calamares. +# It specifies what modules will be used, as well as some +# overall characteristics -- is this a setup program, or +# an installer. More specific configuration is devolved +# to the branding file (for the UI) and the individual +# module configuration files (for functionality). +--- +# Modules can be job modules (with different interfaces) and QtWidgets view +# modules. They could all be placed in a number of different paths. +# "modules-search" is a list of strings, each of these can either be a full +# path to a directory or the keyword "local". +# +# "local" means: +# - modules in $LIBDIR/calamares/modules, with +# - settings in SHARE/calamares/modules or /etc/calamares/modules. +# In debug-mode (e.g. calamares -d) "local" also adds some paths +# that make sense from inside the build-directory, so that you +# can build-and-run with the latest modules immediately. +# +# Strings other than "local" are taken as paths and interpreted +# relative to wherever Calamares is started. It is therefore **strongly** +# recommended to use only absolute paths here. This is mostly useful +# if your distro has forks of standard Calamares modules, but also +# uses some form of upstream packaging which might overwrite those +# forked modules -- then you can keep modules somewhere outside of +# the "regular" module tree. +# +# +# YAML: list of strings. +modules-search: [ local ] + +# Instances section. This section is optional, and it defines custom instances +# for modules of any kind. An instance entry has these keys: +# - *module* name, which matches the module name from the module descriptor +# (usually the name of the directory under `src/modules/`, but third- +# party modules may diverge. +# - *id* (optional) an identifier to distinguish this instance from +# all the others. If none is given, the name of the module is used. +# Together, the module and id form an instance key (see below). +# - *config* (optional) a filename for the configuration. If none is +# given, *module*`.conf` is used (e.g. `welcome.conf` for the welcome +# module) +# - *weight* (optional) In the *exec* phase of the sequence, progress +# is reported as jobs are completed. The jobs from a single module +# together contribute the full weight of that module. The overall +# progress (0 .. 100%) is divided up according to the weight of each +# module. Give modules that take a lot of time to complete, a larger +# weight to keep the overall progress moving along steadily. This +# weight overrides a weight given in the module descriptor. If no weight +# is given, uses the value from the module descriptor, or 1 if there +# isn't one there either. +# +# The primary goal of this mechanism is to allow loading multiple instances +# of the same module, with different configuration. If you don't need this, +# the instances section can safely be left empty. +# +# Module name plus instance name makes an instance key, e.g. +# "packagechooserq@licenseq", where "packagechooserq" is the module name (for the packagechooserq +# viewmodule) and "licenseq" is the instance name. In the *sequence* +# section below, use instance-keys to name instances (instead of just +# a module name, for modules which have only a single instance). +# +# Every module implicitly has an instance with the instance name equal +# to its module name, e.g. "welcome@welcome". In the *sequence* section, +# mentioning a module without a full instance key (e.g. "welcome") +# means that implicit module. +# +# An instance may specify its configuration file (e.g. `webview-home.conf`). +# The implicit instances all have configuration files named `.conf`. +# This (implict) way matches the source examples, where the welcome +# module contains an example `welcome.conf`. Specify a *config* for +# any module (also implicit instances) to change which file is used. +# +# For more information on running module instances, run Calamares in debug +# mode and check the Modules page in the Debug information interface. +# +# A module that is often used with instances is shellprocess, which will +# run shell commands specified in the configuration file. By configuring +# more than one instance of the module, multiple shell sessions can be run +# during install. +# +# YAML: list of maps of string:string key-value pairs. +#instances: +#- id: licenseq +# module: packagechooserq +# config: licenseq.conf + +# Sequence section. This section describes the sequence of modules, both +# viewmodules and jobmodules, as they should appear and/or run. +# +# A jobmodule instance key (or name) can only appear in an exec phase, whereas +# a viewmodule instance key (or name) can appear in both exec and show phases. +# There is no limit to the number of show or exec phases. However, the same +# module instance key should not appear more than once per phase, and +# deployers should take notice that the global storage structure is persistent +# throughout the application lifetime, possibly influencing behavior across +# phases. A show phase defines a sequence of viewmodules (and therefore +# pages). These viewmodules can offer up jobs for the execution queue. +# +# An exec phase displays a progress page (with brandable slideshow). This +# progress page iterates over the modules listed in the *immediately +# preceding* show phase, and enqueues their jobs, as well as any other jobs +# from jobmodules, in the order defined in the current exec phase. +# +# It then executes the job queue and clears it. If a viewmodule offers up a +# job for execution, but the module name (or instance key) isn't listed in the +# immediately following exec phase, this job will not be executed. +# +# YAML: list of lists of strings. +sequence: +- show: + - welcome +# - notesqml +# - packagechooserq@licenseq + - locale + - keyboard + - partition + - users +# - tracking + - summary +- exec: +# - dummycpp +# - dummyprocess +# - dummypython + - partition +# - zfs + - mount + - unpackfs + - machineid + - locale + - keyboard + - localecfg +# - luksbootkeyfile +# - luksopenswaphookcfg +# - dracutlukscfg + - fstab +# - plymouthcfg +# - zfshostid + - initcpiocfg + - initcpio + - users + - displaymanager + - networkcfg + - hwclock + - services-systemd +# - dracut +# - initramfs +# - grubcfg + - bootloader + - umount +- show: + - finished + +# A branding component is a directory, either in SHARE/calamares/branding or +# in /etc/calamares/branding (the latter takes precedence). The directory must +# contain a YAML file branding.desc which may reference additional resources +# (such as images) as paths relative to the current directory. +# +# A branding component can also ship a QML slideshow for execution pages, +# along with translation files. +# +# Only the name of the branding component (directory) should be specified +# here, Calamares then takes care of finding it and loading the contents. +# +# YAML: string. +branding: default + +# If this is set to true, Calamares will show an "Are you sure?" prompt right +# before each execution phase, i.e. at points of no return. If this is set to +# false, no prompt is shown. Default is false, but Calamares will complain if +# this is not explicitly set. +# +# YAML: boolean. +prompt-install: false + +# If this is set to true, Calamares will execute all target environment +# commands in the current environment, without chroot. This setting should +# only be used when setting up Calamares as a post-install configuration tool, +# as opposed to a full operating system installer. +# +# Some official Calamares modules are not expected to function with this +# setting. (e.g. partitioning seems like a bad idea, since that is expected to +# have been done already) +# +# Default is false (for a normal installer), but Calamares will complain if +# this is not explicitly set. +# +# YAML: boolean. +dont-chroot: false + +# If this is set to true, Calamares refers to itself as a "setup program" +# rather than an "installer". Defaults to the value of dont-chroot, but +# Calamares will complain if this is not explicitly set. +oem-setup: false + +# If this is set to true, the "Cancel" button will be disabled entirely. +# The button is also hidden from view. +# +# This can be useful if when e.g. Calamares is used as a post-install +# configuration tool and you require the user to go through all the +# configuration steps. +# +# Default is false, but Calamares will complain if this is not explicitly set. +# +# YAML: boolean. +disable-cancel: false + +# If this is set to true, the "Cancel" button will be disabled once +# you start the 'Installation', meaning there won't be a way to cancel +# the Installation until it has finished or installation has failed. +# +# Default is false, but Calamares will complain if this is not explicitly set. +# +# YAML: boolean. +disable-cancel-during-exec: false + +# If this is set to true, the "Next" and "Back" button will be hidden once +# you start the 'Installation'. +# +# Default is false, but Calamares will complain if this is not explicitly set. +# +# YAML: boolean. +hide-back-and-next-during-exec: false + +# If this is set to true, then once the end of the sequence has +# been reached, the quit (done) button is clicked automatically +# and Calamares will close. Default is false: the user will see +# that the end of installation has been reached, and that things are ok. +# +# +quit-at-end: false diff --git a/airootfs/usr/share/icons/hicolor/scalable/apps/calamares.svg b/airootfs/usr/share/icons/hicolor/scalable/apps/calamares.svg new file mode 100644 index 0000000..9082050 --- /dev/null +++ b/airootfs/usr/share/icons/hicolor/scalable/apps/calamares.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + diff --git a/airootfs/usr/share/locale/ar/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ar/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..674db75 Binary files /dev/null and b/airootfs/usr/share/locale/ar/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/as/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/as/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..bbf4317 Binary files /dev/null and b/airootfs/usr/share/locale/as/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ast/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ast/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..f68c204 Binary files /dev/null and b/airootfs/usr/share/locale/ast/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/az/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/az/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..d16421b Binary files /dev/null and b/airootfs/usr/share/locale/az/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/az_AZ/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/az_AZ/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..85ac0ac Binary files /dev/null and b/airootfs/usr/share/locale/az_AZ/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/be/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/be/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..6b3b4f5 Binary files /dev/null and b/airootfs/usr/share/locale/be/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/bg/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/bg/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..6fe579b Binary files /dev/null and b/airootfs/usr/share/locale/bg/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/bn/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/bn/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..349e395 Binary files /dev/null and b/airootfs/usr/share/locale/bn/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ca/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ca/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..c1a54eb Binary files /dev/null and b/airootfs/usr/share/locale/ca/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..64a7e19 Binary files /dev/null and b/airootfs/usr/share/locale/ca@valencia/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/cs_CZ/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/cs_CZ/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..5d934fe Binary files /dev/null and b/airootfs/usr/share/locale/cs_CZ/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/da/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/da/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..e8c747e Binary files /dev/null and b/airootfs/usr/share/locale/da/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/de/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/de/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..651b32e Binary files /dev/null and b/airootfs/usr/share/locale/de/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/el/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/el/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..33c632d Binary files /dev/null and b/airootfs/usr/share/locale/el/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/en_GB/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/en_GB/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..8b20901 Binary files /dev/null and b/airootfs/usr/share/locale/en_GB/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/eo/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/eo/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..6eecdff Binary files /dev/null and b/airootfs/usr/share/locale/eo/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/es/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/es/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..4cd23c3 Binary files /dev/null and b/airootfs/usr/share/locale/es/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/es_AR/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/es_AR/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..cb95ad9 Binary files /dev/null and b/airootfs/usr/share/locale/es_AR/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/es_MX/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/es_MX/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..a23cf60 Binary files /dev/null and b/airootfs/usr/share/locale/es_MX/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/et/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/et/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..7675f48 Binary files /dev/null and b/airootfs/usr/share/locale/et/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/eu/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/eu/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..ba834d2 Binary files /dev/null and b/airootfs/usr/share/locale/eu/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/fa/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/fa/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..fb4ed5a Binary files /dev/null and b/airootfs/usr/share/locale/fa/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/fi_FI/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/fi_FI/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..0aac77a Binary files /dev/null and b/airootfs/usr/share/locale/fi_FI/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/fr/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/fr/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..b2a9865 Binary files /dev/null and b/airootfs/usr/share/locale/fr/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/fur/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/fur/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..853ccbc Binary files /dev/null and b/airootfs/usr/share/locale/fur/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/gl/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/gl/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..f0aa3bd Binary files /dev/null and b/airootfs/usr/share/locale/gl/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/he/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/he/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..7865c57 Binary files /dev/null and b/airootfs/usr/share/locale/he/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/hi/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/hi/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..d34c46a Binary files /dev/null and b/airootfs/usr/share/locale/hi/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/hr/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/hr/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..2d45f8a Binary files /dev/null and b/airootfs/usr/share/locale/hr/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/hu/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/hu/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..d6cdca7 Binary files /dev/null and b/airootfs/usr/share/locale/hu/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/id/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/id/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..0308444 Binary files /dev/null and b/airootfs/usr/share/locale/id/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/is/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/is/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..8d7ed97 Binary files /dev/null and b/airootfs/usr/share/locale/is/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/it_IT/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/it_IT/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..f442e46 Binary files /dev/null and b/airootfs/usr/share/locale/it_IT/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ja/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ja/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..6397d08 Binary files /dev/null and b/airootfs/usr/share/locale/ja/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ka/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ka/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..d15704e Binary files /dev/null and b/airootfs/usr/share/locale/ka/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ko/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ko/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..4129f7f Binary files /dev/null and b/airootfs/usr/share/locale/ko/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/lt/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/lt/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..b6112cc Binary files /dev/null and b/airootfs/usr/share/locale/lt/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ml/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ml/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..b1027ed Binary files /dev/null and b/airootfs/usr/share/locale/ml/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/mr/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/mr/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..3037da4 Binary files /dev/null and b/airootfs/usr/share/locale/mr/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/nb/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/nb/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..e3c7ecb Binary files /dev/null and b/airootfs/usr/share/locale/nb/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/nl/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/nl/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..b1c47de Binary files /dev/null and b/airootfs/usr/share/locale/nl/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/oc/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/oc/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..842489f Binary files /dev/null and b/airootfs/usr/share/locale/oc/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/pl/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/pl/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..d3a10bf Binary files /dev/null and b/airootfs/usr/share/locale/pl/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/pt_BR/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/pt_BR/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..24517e5 Binary files /dev/null and b/airootfs/usr/share/locale/pt_BR/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/pt_PT/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/pt_PT/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..6715626 Binary files /dev/null and b/airootfs/usr/share/locale/pt_PT/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ro/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ro/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..79e5a8d Binary files /dev/null and b/airootfs/usr/share/locale/ro/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/ru/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/ru/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..07e6aa7 Binary files /dev/null and b/airootfs/usr/share/locale/ru/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/si/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/si/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..c033ab9 Binary files /dev/null and b/airootfs/usr/share/locale/si/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/sk/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/sk/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..3282291 Binary files /dev/null and b/airootfs/usr/share/locale/sk/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/sl/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/sl/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..86798fb Binary files /dev/null and b/airootfs/usr/share/locale/sl/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/sq/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/sq/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..3f517ed Binary files /dev/null and b/airootfs/usr/share/locale/sq/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/sr/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/sr/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..ee001ca Binary files /dev/null and b/airootfs/usr/share/locale/sr/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/sr@latin/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/sr@latin/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..81bccfc Binary files /dev/null and b/airootfs/usr/share/locale/sr@latin/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/sv/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/sv/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..1c53fce Binary files /dev/null and b/airootfs/usr/share/locale/sv/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/tg/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/tg/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..6eb72ed Binary files /dev/null and b/airootfs/usr/share/locale/tg/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/th/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/th/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..f355a8f Binary files /dev/null and b/airootfs/usr/share/locale/th/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/tr_TR/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/tr_TR/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..d9953e4 Binary files /dev/null and b/airootfs/usr/share/locale/tr_TR/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/uk/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/uk/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..7de2dfa Binary files /dev/null and b/airootfs/usr/share/locale/uk/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/vi/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/vi/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..5403c33 Binary files /dev/null and b/airootfs/usr/share/locale/vi/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/zh_CN/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/zh_CN/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..894a76a Binary files /dev/null and b/airootfs/usr/share/locale/zh_CN/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/locale/zh_TW/LC_MESSAGES/calamares-python.mo b/airootfs/usr/share/locale/zh_TW/LC_MESSAGES/calamares-python.mo new file mode 100644 index 0000000..8ca7981 Binary files /dev/null and b/airootfs/usr/share/locale/zh_TW/LC_MESSAGES/calamares-python.mo differ diff --git a/airootfs/usr/share/man/man8/calamares.8.gz b/airootfs/usr/share/man/man8/calamares.8.gz new file mode 100644 index 0000000..af25dd6 Binary files /dev/null and b/airootfs/usr/share/man/man8/calamares.8.gz differ diff --git a/airootfs/usr/share/polkit-1/actions/io.calamares.calamares.policy b/airootfs/usr/share/polkit-1/actions/io.calamares.calamares.policy new file mode 100644 index 0000000..614a546 --- /dev/null +++ b/airootfs/usr/share/polkit-1/actions/io.calamares.calamares.policy @@ -0,0 +1,25 @@ + + + + + + Calamares + https://calamares.io/ + + + Run Installer + Authentication is required to run the installation program + drive-harddisk + + no + no + auth_admin + + /usr/bin/calamares + true + + diff --git a/bootstrap_packages b/bootstrap_packages new file mode 100644 index 0000000..64966d0 --- /dev/null +++ b/bootstrap_packages @@ -0,0 +1,2 @@ +arch-install-scripts +base diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..b29f723 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#bin/bash +sudo rm -rf ~/archiso-work +sudo mkarchiso -v -w ~/archiso-work -o ~/archdisto/ . diff --git a/efiboot/loader/entries/01-archiso-linux.conf b/efiboot/loader/entries/01-archiso-linux.conf new file mode 100644 index 0000000..c35f663 --- /dev/null +++ b/efiboot/loader/entries/01-archiso-linux.conf @@ -0,0 +1,5 @@ +title Arch Linux install medium (%ARCH%, UEFI) +sort-key 01 +linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% diff --git a/efiboot/loader/entries/02-archiso-speech-linux.conf b/efiboot/loader/entries/02-archiso-speech-linux.conf new file mode 100644 index 0000000..48940b7 --- /dev/null +++ b/efiboot/loader/entries/02-archiso-speech-linux.conf @@ -0,0 +1,5 @@ +title Arch Linux install medium (%ARCH%, UEFI) with speech +sort-key 02 +linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +options archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on diff --git a/efiboot/loader/entries/03-archiso-memtest86+x64.conf b/efiboot/loader/entries/03-archiso-memtest86+x64.conf new file mode 100644 index 0000000..7a0ef5e --- /dev/null +++ b/efiboot/loader/entries/03-archiso-memtest86+x64.conf @@ -0,0 +1,4 @@ +title Memtest86+ +sort-key 03 +efi /boot/memtest86+/memtest.efi +architecture x64 diff --git a/efiboot/loader/loader.conf b/efiboot/loader/loader.conf new file mode 100644 index 0000000..06d4ac4 --- /dev/null +++ b/efiboot/loader/loader.conf @@ -0,0 +1,3 @@ +timeout 15 +default 01-archiso-linux.conf +beep on diff --git a/grub/grub.cfg b/grub/grub.cfg new file mode 100644 index 0000000..b879a67 --- /dev/null +++ b/grub/grub.cfg @@ -0,0 +1,112 @@ +# Load partition table and file system modules +insmod part_gpt +insmod part_msdos +insmod fat +insmod iso9660 +insmod ntfs +insmod ntfscomp +insmod exfat +insmod udf + +# Use graphics-mode output +if loadfont "${prefix}/fonts/unicode.pf2" ; then + insmod all_video + set gfxmode="auto" + terminal_input console + terminal_output console +fi + +# Enable serial console +insmod serial +insmod usbserial_common +insmod usbserial_ftdi +insmod usbserial_pl2303 +insmod usbserial_usbdebug +if serial --unit=0 --speed=115200; then + terminal_input --append serial + terminal_output --append serial +fi + +# Get a human readable platform identifier +if [ "${grub_platform}" == 'efi' ]; then + archiso_platform='UEFI' +elif [ "${grub_platform}" == 'pc' ]; then + archiso_platform='BIOS' +else + archiso_platform="${grub_cpu}-${grub_platform}" +fi + +# Set default menu entry +default=archlinux +timeout=15 +timeout_style=menu + + +# Menu entries + +menuentry "Arch Linux install medium (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { + set gfxpayload=keep + linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% + initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +} + +menuentry "Arch Linux install medium with speakup screen reader (%ARCH%, ${archiso_platform})" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' { + set gfxpayload=keep + linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on + initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +} + + +if [ "${grub_platform}" == 'efi' -a "${grub_cpu}" == 'x86_64' -a -f '/boot/memtest86+/memtest.efi' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class memtest --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest.efi + } +fi +if [ "${grub_platform}" == 'pc' -a -f '/boot/memtest86+/memtest' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class memtest --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest + } +fi +if [ "${grub_platform}" == 'efi' ]; then + if [ "${grub_cpu}" == 'x86_64' -a -f '/shellx64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellx64.efi + } + elif [ "${grub_cpu}" == 'i386' -a -f '/shellia32.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellia32.efi + } + elif [ "${grub_cpu}" == 'arm64' -a -f '/shellaa64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellaa64.efi + } + elif [ "${grub_cpu}" == 'riscv64' -a -f '/shellriscv64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellriscv64.efi + } + elif [ "${grub_cpu}" == 'loongarch64' -a -f '/shellloongarch64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellloongarch64.efi + } + fi + + menuentry 'UEFI Firmware Settings' --id 'uefi-firmware' { + fwsetup + } +fi + +menuentry 'System shutdown' --class shutdown --class poweroff { + echo 'System shutting down...' + halt +} + +menuentry 'System restart' --class reboot --class restart { + echo 'System rebooting...' + reboot +} + + +# GRUB init tune for accessibility +play 600 988 1 1319 4 diff --git a/grub/loopback.cfg b/grub/loopback.cfg new file mode 100644 index 0000000..1a48fc5 --- /dev/null +++ b/grub/loopback.cfg @@ -0,0 +1,85 @@ +# https://www.supergrubdisk.org/wiki/Loopback.cfg + +# Search for the ISO volume +search --no-floppy --set=archiso_img_dev --file "${iso_path}" +probe --set archiso_img_dev_uuid --fs-uuid "${archiso_img_dev}" + +# Get a human readable platform identifier +if [ "${grub_platform}" == 'efi' ]; then + archiso_platform='UEFI' +elif [ "${grub_platform}" == 'pc' ]; then + archiso_platform='BIOS' +else + archiso_platform="${grub_cpu}-${grub_platform}" +fi + +# Set default menu entry +default=archlinux +timeout=15 +timeout_style=menu + + +# Menu entries + +menuentry "Arch Linux install medium (%ARCH%, ${archiso_platform})" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { + set gfxpayload=keep + linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" + initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +} + +menuentry "Arch Linux install medium with speakup screen reader (%ARCH%, ${archiso_platform})" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' { + set gfxpayload=keep + linux /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux archisobasedir=%INSTALL_DIR% img_dev=UUID=${archiso_img_dev_uuid} img_loop="${iso_path}" accessibility=on + initrd /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +} + + +if [ "${grub_platform}" == 'efi' -a "${grub_cpu}" == 'x86_64' -a -f '/boot/memtest86+/memtest.efi' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class memtest --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest.efi + } +fi +if [ "${grub_platform}" == 'pc' -a -f '/boot/memtest86+/memtest' ]; then + menuentry 'Run Memtest86+ (RAM test)' --class memtest86 --class memtest --class gnu --class tool { + set gfxpayload=800x600,1024x768 + linux /boot/memtest86+/memtest + } +fi +if [ "${grub_platform}" == 'efi' ]; then + if [ "${grub_cpu}" == 'x86_64' -a -f '/shellx64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellx64.efi + } + elif [ "${grub_cpu}" == 'i386' -a -f '/shellia32.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellia32.efi + } + elif [ "${grub_cpu}" == 'arm64' -a -f '/shellaa64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellaa64.efi + } + elif [ "${grub_cpu}" == 'riscv64' -a -f '/shellriscv64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellriscv64.efi + } + elif [ "${grub_cpu}" == 'loongarch64' -a -f '/shellloongarch64.efi' ]; then + menuentry 'UEFI Shell' --class efi { + chainloader /shellloongarch64.efi + } + fi + + menuentry 'UEFI Firmware Settings' --id 'uefi-firmware' { + fwsetup + } +fi + +menuentry 'System shutdown' --class shutdown --class poweroff { + echo 'System shutting down...' + halt +} + +menuentry 'System restart' --class reboot --class restart { + echo 'System rebooting...' + reboot +} diff --git a/packages.x86_64 b/packages.x86_64 new file mode 100644 index 0000000..6bf6c82 --- /dev/null +++ b/packages.x86_64 @@ -0,0 +1,157 @@ +alsa-utils +amd-ucode +arch-install-scripts +archinstall +b43-fwcutter +base +bcachefs-tools +bind +bolt +brltty +broadcom-wl +btrfs-progs +clonezilla +cloud-init +cryptsetup +darkhttpd +ddrescue +dhcpcd +diffutils +dmidecode +dmraid +dnsmasq +dosfstools +e2fsprogs +edk2-shell +efibootmgr +espeakup +ethtool +exfatprogs +f2fs-tools +fatresize +foot-terminfo +fsarchiver +gpart +gpm +gptfdisk +grml-zsh-config +grub +hdparm +hyperv +intel-ucode +irssi +iw +iwd +jfsutils +kitty-terminfo +ldns +less +lftp +libfido2 +libusb-compat +linux +linux-atm +linux-firmware +linux-firmware-marvell +livecd-sounds +lsscsi +lvm2 +lynx +man-db +man-pages +mc +mdadm +memtest86+ +memtest86+-efi +mkinitcpio +mkinitcpio-archiso +mkinitcpio-nfs-utils +mmc-utils +modemmanager +mtools +nano +nbd +ndisc6 +nfs-utils +nilfs-utils +nmap +ntfs-3g +nvme-cli +open-iscsi +open-vm-tools +openconnect +openpgp-card-tools +openssh +openvpn +partclone +parted +partimage +pcsclite +ppp +pptpclient +pv +qemu-guest-agent +refind +reflector +rsync +rxvt-unicode-terminfo +screen +sdparm +sequoia-sq +sg3_utils +smartmontools +sof-firmware +squashfs-tools +sudo +syslinux +systemd-resolvconf +tcpdump +terminus-font +testdisk +tmux +tpm2-tools +tpm2-tss +udftools +usb_modeswitch +usbmuxd +usbutils +vim +virtualbox-guest-utils-nox +vpnc +wireless-regdb +wireless_tools +wpa_supplicant +wvdial +xdg-utils +xfsprogs +xl2tpd +zsh + +plasma +git +hyprpaper +kitty + +rofi +waybar +qt6-wayland +nvim +upower +qt5-svg +polkit-qt5 +hwinfo +swaybg +xdg-desktop-portal-hyprland +foot +polkit-kde-agent +xorg-xwayland +dolphin +cracklib +kpmcore +libpwquality +yaml-cpp +qt6-svg +python + +boost-libs +gparted diff --git a/pacman.conf b/pacman.conf new file mode 100644 index 0000000..2684a95 --- /dev/null +++ b/pacman.conf @@ -0,0 +1,108 @@ +# +# /etc/pacman.conf +# +# See the pacman.conf(5) manpage for option and repository directives + +# +# GENERAL OPTIONS +# +[options] +# The following paths are commented out with their default values listed. +# If you wish to use different paths, uncomment and update the paths. +#RootDir = / +#DBPath = /var/lib/pacman/ +#CacheDir = /var/cache/pacman/pkg/ +#LogFile = /var/log/pacman.log +#GPGDir = /etc/pacman.d/gnupg/ +#HookDir = /etc/pacman.d/hooks/ +HoldPkg = pacman glibc +#XferCommand = /usr/bin/curl -L -C - -f -o %o %u +#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u +#CleanMethod = KeepInstalled +Architecture = auto + +# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup +#IgnorePkg = +#IgnoreGroup = + +#NoUpgrade = +#NoExtract = + +# Misc options +#UseSyslog +#Color +#NoProgressBar +# We cannot check disk space from within a chroot environment +#CheckSpace +#VerbosePkgLists +ParallelDownloads = 5 +#DownloadUser = alpm +#DisableSandbox + +# By default, pacman accepts packages signed by keys that its local keyring +# trusts (see pacman-key and its man page), as well as unsigned packages. +SigLevel = Required DatabaseOptional +LocalFileSigLevel = Optional +#RemoteFileSigLevel = Required + +# NOTE: You must run `pacman-key --init` before first using pacman; the local +# keyring can then be populated with the keys of all official Arch Linux +# packagers with `pacman-key --populate archlinux`. + +# +# REPOSITORIES +# - can be defined here or included from another file +# - pacman will search repositories in the order defined here +# - local/custom mirrors can be added here or in separate files +# - repositories listed first will take precedence when packages +# have identical names, regardless of version number +# - URLs will have $repo replaced by the name of the current repo +# - URLs will have $arch replaced by the name of the architecture +# +# Repository entries are of the format: +# [repo-name] +# Server = ServerName +# Include = IncludePath +# +# The header [repo-name] is crucial - it must be present and +# uncommented to enable the repo. +# + +# The testing repositories are disabled by default. To enable, uncomment the +# repo name header and Include lines. You can add preferred servers immediately +# after the header, and they will be used before the default mirrors. + +#[core-testing] +#Include = /etc/pacman.d/mirrorlist + +[core] +Include = /etc/pacman.d/mirrorlist + +#[extra-testing] +#Include = /etc/pacman.d/mirrorlist + +[extra] +Include = /etc/pacman.d/mirrorlist + +# If you want to run 32 bit applications on your x86_64 system, +# enable the multilib repositories as required here. + +#[multilib-testing] +#Include = /etc/pacman.d/mirrorlist + +#[multilib] +#Include = /etc/pacman.d/mirrorlist + +# An example of a custom package repository. See the pacman manpage for +# tips on creating your own repositories. +#[custom] +#SigLevel = Optional TrustAll +#Server = file:///home/custompkgs + +[arcolinux_repo] +SigLevel = PackageOptional TrustAll +Server = https://arcolinux.github.io/$repo/$arch + +[localrepo] +SigLevel = Optional TrustAll +Server = file:///home/stuartdrew/archdisto/local_repo diff --git a/profiledef.sh b/profiledef.sh new file mode 100644 index 0000000..7278906 --- /dev/null +++ b/profiledef.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2034 + +iso_name="HyprArch" +iso_label="ARCH_$(date --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y%m)" +iso_publisher="HyprArch " +iso_application="Arch Linux Live/Rescue DVD" +iso_version="$(date --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y.%m.%d)" +install_dir="arch" +buildmodes=('iso') +bootmodes=('bios.syslinux' + 'uefi.systemd-boot') +pacman_conf="pacman.conf" +airootfs_image_type="squashfs" +airootfs_image_tool_options=('-comp' 'xz' '-Xbcj' 'x86' '-b' '1M' '-Xdict-size' '1M') +bootstrap_tarball_compression=('zstd' '-c' '-T0' '--auto-threads=logical' '--long' '-19') +boot_extended_size="1G" +file_permissions=( + ["/etc/shadow"]="0:0:400" + ["/root"]="0:0:750" + ["/root/.automated_script.sh"]="0:0:755" + ["/root/.gnupg"]="0:0:700" + ["/usr/local/bin/choose-mirror"]="0:0:755" + ["/usr/local/bin/Installation_guide"]="0:0:755" + ["/usr/local/bin/livecd-sound"]="0:0:755" + ["/etc/sddm.conf.d/autologin.conf"]="0:0:644" + ["/root/install-arch.desktop"]="0:0:755" + ["/usr/bin/calamares"]="0:0:755" +) diff --git a/syslinux/archiso_head.cfg b/syslinux/archiso_head.cfg new file mode 100644 index 0000000..671ab4e --- /dev/null +++ b/syslinux/archiso_head.cfg @@ -0,0 +1,28 @@ +SERIAL 0 115200 +UI vesamenu.c32 +MENU TITLE Arch Linux +MENU BACKGROUND splash.png + +MENU WIDTH 78 +MENU MARGIN 4 +MENU ROWS 7 +MENU VSHIFT 10 +MENU TABMSGROW 14 +MENU CMDLINEROW 14 +MENU HELPMSGROW 16 +MENU HELPMSGENDROW 29 + +# Refer to https://wiki.syslinux.org/wiki/index.php/Comboot/menu.c32 + +MENU COLOR border 30;44 #40ffffff #a0000000 std +MENU COLOR title 1;36;44 #9033ccff #a0000000 std +MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all +MENU COLOR unsel 37;44 #50ffffff #a0000000 std +MENU COLOR help 37;40 #c0ffffff #a0000000 std +MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std +MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std +MENU COLOR msg07 37;40 #90ffffff #a0000000 std +MENU COLOR tabmsg 31;40 #30ffffff #00000000 std + +MENU CLEAR +MENU IMMEDIATE diff --git a/syslinux/archiso_pxe-linux.cfg b/syslinux/archiso_pxe-linux.cfg new file mode 100644 index 0000000..872aae8 --- /dev/null +++ b/syslinux/archiso_pxe-linux.cfg @@ -0,0 +1,32 @@ +LABEL arch_nbd +TEXT HELP +Boot the Arch Linux install medium using NBD. +It allows you to install Arch Linux or perform system maintenance. +ENDTEXT +MENU LABEL Arch Linux install medium (%ARCH%, NBD) +LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% archiso_nbd_srv=${pxeserver} cms_verify=y +SYSAPPEND 3 + +LABEL arch_nfs +TEXT HELP +Boot the Arch Linux live medium using NFS. +It allows you to install Arch Linux or perform system maintenance. +ENDTEXT +MENU LABEL Arch Linux install medium (%ARCH%, NFS) +LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND archisobasedir=%INSTALL_DIR% archiso_nfs_srv=${pxeserver}:/run/archiso/bootmnt cms_verify=y +SYSAPPEND 3 + +LABEL arch_http +TEXT HELP +Boot the Arch Linux live medium using HTTP. +It allows you to install Arch Linux or perform system maintenance. +ENDTEXT +MENU LABEL Arch Linux install medium (%ARCH%, HTTP) +LINUX ::/%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD ::/%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND archisobasedir=%INSTALL_DIR% archiso_http_srv=http://${pxeserver}/ cms_verify=y +SYSAPPEND 3 diff --git a/syslinux/archiso_pxe.cfg b/syslinux/archiso_pxe.cfg new file mode 100644 index 0000000..b4c9a80 --- /dev/null +++ b/syslinux/archiso_pxe.cfg @@ -0,0 +1,5 @@ +INCLUDE archiso_head.cfg + +INCLUDE archiso_pxe-linux.cfg + +INCLUDE archiso_tail.cfg diff --git a/syslinux/archiso_sys-linux.cfg b/syslinux/archiso_sys-linux.cfg new file mode 100644 index 0000000..d945c7a --- /dev/null +++ b/syslinux/archiso_sys-linux.cfg @@ -0,0 +1,20 @@ +LABEL arch +TEXT HELP +Boot the Arch Linux install medium on BIOS. +It allows you to install Arch Linux or perform system maintenance. +ENDTEXT +MENU LABEL Arch Linux install medium (%ARCH%, BIOS) +LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% + +# Accessibility boot option +LABEL archspeech +TEXT HELP +Boot the Arch Linux install medium on BIOS with speakup screen reader. +It allows you to install Arch Linux or perform system maintenance with speech feedback. +ENDTEXT +MENU LABEL Arch Linux install medium (%ARCH%, BIOS) with ^speech +LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND archisobasedir=%INSTALL_DIR% archisosearchuuid=%ARCHISO_UUID% accessibility=on diff --git a/syslinux/archiso_sys.cfg b/syslinux/archiso_sys.cfg new file mode 100644 index 0000000..662482c --- /dev/null +++ b/syslinux/archiso_sys.cfg @@ -0,0 +1,8 @@ +INCLUDE archiso_head.cfg + +DEFAULT arch +TIMEOUT 150 + +INCLUDE archiso_sys-linux.cfg + +INCLUDE archiso_tail.cfg diff --git a/syslinux/archiso_tail.cfg b/syslinux/archiso_tail.cfg new file mode 100644 index 0000000..e84897c --- /dev/null +++ b/syslinux/archiso_tail.cfg @@ -0,0 +1,35 @@ +LABEL existing +TEXT HELP +Boot an existing operating system. +Press TAB to edit the disk and partition number to boot. +ENDTEXT +MENU LABEL Boot existing OS +COM32 chain.c32 +APPEND hd0 0 + +# https://www.memtest.org/ +LABEL memtest +MENU LABEL Run Memtest86+ (RAM test) +LINUX /boot/memtest86+/memtest + +# https://wiki.syslinux.org/wiki/index.php/Hdt_(Hardware_Detection_Tool) +LABEL hdt +MENU LABEL Hardware Information (HDT) +COM32 hdt.c32 +APPEND modules_alias=hdt/modalias.gz pciids=hdt/pciids.gz + +LABEL reboot +TEXT HELP +Reboot computer. +The computer's firmware must support APM. +ENDTEXT +MENU LABEL Reboot +COM32 reboot.c32 + +LABEL poweroff +TEXT HELP +Power off computer. +The computer's firmware must support APM. +ENDTEXT +MENU LABEL Power Off +COM32 poweroff.c32 diff --git a/syslinux/splash.png b/syslinux/splash.png new file mode 100644 index 0000000..64b959a Binary files /dev/null and b/syslinux/splash.png differ diff --git a/syslinux/syslinux.cfg b/syslinux/syslinux.cfg new file mode 100644 index 0000000..cbda72f --- /dev/null +++ b/syslinux/syslinux.cfg @@ -0,0 +1,11 @@ +DEFAULT select + +LABEL select +COM32 whichsys.c32 +APPEND -pxe- pxe -sys- sys -iso- sys + +LABEL pxe +CONFIG archiso_pxe.cfg + +LABEL sys +CONFIG archiso_sys.cfg diff --git a/yay b/yay new file mode 160000 index 0000000..1a09253 --- /dev/null +++ b/yay @@ -0,0 +1 @@ +Subproject commit 1a092538ebdfa373ddb641ab1fe564a2ed62877d