diff --git a/CHANGES-3.3 b/CHANGES-3.3 index 74bb0718c..f187e1320 100644 --- a/CHANGES-3.3 +++ b/CHANGES-3.3 @@ -24,8 +24,11 @@ This release contains contributions from (alphabetically by first name): - Branding entries use ${var} instead of @{var} for substitutions, in line with all the other substitution mechanisms used from C++ core. See documentation in `branding.desc`. - - Boost::Python requires at least version 1.72 - - KDE Frameworks must be version 5.58 or later + - Boost::Python requires at least version 1.72. + - KDE Frameworks must be version 5.58 or later. + - The `INSTALL_CONFIG` option has been removed. If you are installing + the example configuration files from the Calamares repository, just + stop. That was never a good idea, and you should keep your configs elsewhere. ## Modules ## - *dracut* added a configurable kernel name. (thanks Anke) @@ -35,7 +38,9 @@ This release contains contributions from (alphabetically by first name): - *netinstall* now has a new *noncheckable* option for groups, which prevent it a group from being checked/uncheckd as a whole. You can still check individual items **in** the group though. (thanks Shivanand) -- *partition* can now pick LUKS or LUKS2. (thanks Jeremy) + - *partition* can now pick LUKS or LUKS2. (thanks Jeremy) + - *zfs* creates a hostid through zgenhostid. + - *zfshostid* new module to copy zfs generated /etc/hostid # 3.3.0-alpha2 (2022-08-23) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04fbf687a..86e8175f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,7 +73,6 @@ set(CALAMARES_SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") ### OPTIONS # -option(INSTALL_CONFIG "Install configuration files" OFF) option(INSTALL_POLKIT "Install Polkit configuration" ON) option(INSTALL_COMPLETION "Install shell completions" OFF) # When adding WITH_* that affects the ABI offered by libcalamares, @@ -542,7 +541,6 @@ add_subdirectory(src) add_feature_info(Python ${WITH_PYTHON} "Python job modules") add_feature_info(Qml ${WITH_QML} "QML UI support") -add_feature_info(Config ${INSTALL_CONFIG} "Install Calamares configuration") add_feature_info(Polkit ${INSTALL_POLKIT} "Install Polkit files") add_feature_info(KCrash ${BUILD_KF5Crash} "Crash dumps via KCrash") @@ -587,10 +585,6 @@ install( ### Miscellaneous installs # # -if(INSTALL_CONFIG) - install(FILES settings.conf DESTINATION share/calamares) -endif() - if(INSTALL_POLKIT) install(FILES com.github.calamares.calamares.policy DESTINATION "${POLKITQT-1_POLICY_FILES_INSTALL_DIR}") endif() @@ -607,7 +601,9 @@ install(FILES calamares.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/application install(FILES man/calamares.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/) -# uninstall target +### Uninstall +# +# configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @@ -617,6 +613,17 @@ configure_file( add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +### Developer convenience +# +# The module support files -- .desc files, .conf files -- are copied into the build +# directory so that it is possible to run `calamares -d` from there. Copy the +# top-level settings.conf as well, into the build directory. +if( settings.conf IS_NEWER_THAN ${CMAKE_BINARY_DIR}/settings.conf ) + configure_file(settings.conf ${CMAKE_BINARY_DIR}/settings.conf COPYONLY) +endif() + + + ### CMAKE SUMMARY REPORT # get_directory_property(SKIPPED_MODULES DIRECTORY src/modules DEFINITION LIST_SKIPPED_MODULES) diff --git a/CMakeModules/CalamaresAddModuleSubdirectory.cmake b/CMakeModules/CalamaresAddModuleSubdirectory.cmake index e0eed223e..55cf440ce 100644 --- a/CMakeModules/CalamaresAddModuleSubdirectory.cmake +++ b/CMakeModules/CalamaresAddModuleSubdirectory.cmake @@ -153,12 +153,10 @@ function( _calamares_add_module_subdirectory_impl ) get_filename_component( FLEXT ${MODULE_FILE} EXT ) if( "${FLEXT}" STREQUAL ".conf" ) - if( INSTALL_CONFIG ) - install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE} - DESTINATION ${MODULE_DATA_DESTINATION} ) - endif() + 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() @@ -169,12 +167,7 @@ function( _calamares_add_module_subdirectory_impl ) message( " ${Green}TYPE:${ColorReset} jobmodule" ) message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" ) if( MODULE_CONFIG_FILES ) - if ( INSTALL_CONFIG ) - set( _destination "${MODULE_DATA_DESTINATION}" ) - else() - set( _destination "[Build directory only]" ) - endif() - message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => ${_destination}" ) + message( " ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory only]" ) endif() message( "" ) # We copy over the lang directory, if any diff --git a/CMakeModules/CalamaresAddPlugin.cmake b/CMakeModules/CalamaresAddPlugin.cmake index 7e2a3f583..a86ee6e71 100644 --- a/CMakeModules/CalamaresAddPlugin.cmake +++ b/CMakeModules/CalamaresAddPlugin.cmake @@ -104,10 +104,7 @@ function( calamares_add_plugin ) 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 ) - # Not INSTALL_CONFIG + if( NOT PLUGIN_NO_INSTALL ) set( _destination "[Build directory only]" ) else() set( _destination "[Skipping installation]" ) @@ -210,17 +207,12 @@ function( calamares_add_plugin ) 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} OR INSTALL_CONFIG ) + 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( "" ) diff --git a/README.md b/README.md index 570adf575..35a75453b 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ [![Current issue](https://img.shields.io/badge/issue-in_progress-FE9B48)](https://github.com/calamares/calamares/labels/hacking%3A%20in-progress) [![GitHub release](https://img.shields.io/github/release/calamares/calamares.svg)](https://github.com/calamares/calamares/releases) -[![GitHub Build Status](https://img.shields.io/github/workflow/status/calamares/calamares/ci?label=GH%20build)](https://github.com/calamares/calamares/actions?query=workflow%3Aci) -[![GitHub license](https://img.shields.io/github/license/calamares/calamares.svg)](https://github.com/calamares/calamares/blob/calamares/LICENSES/GPL-3.0-or-later.txt) +[![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/calamares/calamares/push.yml)](https://github.com/calamares/calamares/actions?query=workflow%3Aci) +[![GitHub license](https://img.shields.io/badge/license-Multiple-green)](https://github.com/calamares/calamares/tree/calamares/LICENSES) | [Report a Bug](https://github.com/calamares/calamares/issues/new) | [Translate](https://www.transifex.com/projects/p/calamares/) | [Contribute](CONTRIBUTING.md) | [Matrix: #calamares:kde.org](https://webchat.kde.org/#/room/%23calamares:kde.org) | [IRC: Libera.Chat #calamares](https://kiwiirc.com/client/irc.libera.chat/#calamares) | [Wiki](https://github.com/calamares/calamares/wiki) | diff --git a/settings.conf b/settings.conf index 8f4d60213..f4cbd05ca 100644 --- a/settings.conf +++ b/settings.conf @@ -140,6 +140,7 @@ sequence: # - luksopenswaphookcfg # - dracutlukscfg # - plymouthcfg +# - zfshostid - initcpiocfg - initcpio - users diff --git a/src/modules/displaymanager/displaymanager.conf b/src/modules/displaymanager/displaymanager.conf index c5c0e0b78..18e956f27 100644 --- a/src/modules/displaymanager/displaymanager.conf +++ b/src/modules/displaymanager/displaymanager.conf @@ -59,3 +59,11 @@ basicSetup: false # *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. +greetd: + greeter_user: "tom_bombadil" + greeter_group: "wheel" diff --git a/src/modules/displaymanager/displaymanager.schema.yaml b/src/modules/displaymanager/displaymanager.schema.yaml index 7e4c5f54c..2f1da9d56 100644 --- a/src/modules/displaymanager/displaymanager.schema.yaml +++ b/src/modules/displaymanager/displaymanager.schema.yaml @@ -20,3 +20,10 @@ properties: 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 } + additionalProperties: false + diff --git a/src/modules/displaymanager/main.py b/src/modules/displaymanager/main.py index f908bc441..608b443ad 100644 --- a/src/modules/displaymanager/main.py +++ b/src/modules/displaymanager/main.py @@ -828,7 +828,7 @@ class DMgreetd(DisplayManager): 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 -s -- gtkgreet" + self.config_data['default_session']['command'] = "cage -d -s -- gtkgreet" 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 @@ -983,6 +983,11 @@ def run(): # 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() diff --git a/src/modules/dracut/dracut.conf b/src/modules/dracut/dracut.conf index 98d847d9e..ba1a7b08c 100644 --- a/src/modules/dracut/dracut.conf +++ b/src/modules/dracut/dracut.conf @@ -5,6 +5,6 @@ --- # Dracut defaults to setting initramfs-.img # If you want to specify another filename for the resulting image, -# set a custom kernel name, including the path +# set a custom name, including the path # -# kernelName: /boot/initramfs-linux.img +initramfsName: /boot/initramfs-freebsd.img diff --git a/src/modules/dracut/dracut.schema.yaml b/src/modules/dracut/dracut.schema.yaml index 503b1a4f0..d6008e1bf 100644 --- a/src/modules/dracut/dracut.schema.yaml +++ b/src/modules/dracut/dracut.schema.yaml @@ -6,4 +6,4 @@ $id: https://calamares.io/schemas/dracut additionalProperties: false type: object properties: - kernelName: { type: string } + initramfsName: { type: string } diff --git a/src/modules/dracut/main.py b/src/modules/dracut/main.py index 071406580..85e6f3e7f 100644 --- a/src/modules/dracut/main.py +++ b/src/modules/dracut/main.py @@ -12,9 +12,10 @@ # # Calamares is Free Software: see the License-Identifier above. # +import subprocess import libcalamares -from libcalamares.utils import check_target_env_call +from libcalamares.utils import target_env_process_output import gettext @@ -34,12 +35,20 @@ def run_dracut(): :return: """ - kernelName = libcalamares.job.configuration['kernelName'] + try: + initramfs_name = libcalamares.job.configuration['initramfsName'] + target_env_process_output(['dracut', '-f', initramfs_name]) + except KeyError: + try: + target_env_process_output(['dracut', '-f']) + except subprocess.CalledProcessError as cpe: + libcalamares.utils.warning(f"Dracut failed with output: {cpe.output}") + return cpe.returncode + except subprocess.CalledProcessError as cpe: + libcalamares.utils.warning(f"Dracut failed with output: {cpe.output}") + return cpe.returncode - if not kernelName: - return check_target_env_call(['dracut', '-f']) - else: - return check_target_env_call(['dracut', '-f', '{}'.format(kernelName)]) + return 0 def run(): @@ -50,7 +59,6 @@ def run(): :return: """ return_code = run_dracut() - if return_code != 0: - return (_("Failed to run dracut on the target"), - _("The exit code was {}").format(return_code)) + return (_("Failed to run dracut"), + _(f"Dracut failed to run on the target with return code: {return_code}")) diff --git a/src/modules/grubcfg/grubcfg.conf b/src/modules/grubcfg/grubcfg.conf index 2d58e5dc2..189d4c2a9 100644 --- a/src/modules/grubcfg/grubcfg.conf +++ b/src/modules/grubcfg/grubcfg.conf @@ -46,3 +46,6 @@ defaults: 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/src/modules/grubcfg/grubcfg.schema.yaml b/src/modules/grubcfg/grubcfg.schema.yaml index d4d8b5ddb..c04c0b79f 100644 --- a/src/modules/grubcfg/grubcfg.schema.yaml +++ b/src/modules/grubcfg/grubcfg.schema.yaml @@ -19,4 +19,5 @@ properties: GRUB_DISABLE_SUBMENU: { type: boolean, default: true } GRUB_TERMINAL_OUTPUT: { type: string } GRUB_DISABLE_RECOVERY: { type: boolean, default: true } - required: [ GRUB_TIMEOUT, GRUB_DEFAULT, GRUB_TERMINAL_OUTPUT ] + required: [ GRUB_TIMEOUT, GRUB_DEFAULT ] + always_use_defaults: { type: boolean, default: false } diff --git a/src/modules/grubcfg/main.py b/src/modules/grubcfg/main.py index c9a863ffd..d325766f6 100644 --- a/src/modules/grubcfg/main.py +++ b/src/modules/grubcfg/main.py @@ -14,6 +14,7 @@ # import libcalamares +import fileinput import os import re @@ -85,6 +86,30 @@ def get_zfs_root(): 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 + """ + for line in fileinput.input(default_grub, inplace=True): + 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 one of the keys we care about + if key in grub_config_items.keys(): + print(f"{key}={grub_config_items[key]}") + else: + print(line) + else: + print(line) + + def modify_grub_default(partitions, root_mount_point, distributor): """ Configures '/etc/default/grub' for hibernation and plymouth. @@ -103,7 +128,7 @@ def modify_grub_default(partitions, root_mount_point, distributor): :return: """ default_grub = get_grub_config_path(root_mount_point) - distributor_replace = distributor.replace("'", "'\\''") + distributor = distributor.replace("'", "'\\''") dracut_bin = libcalamares.utils.target_env_call( ["sh", "-c", "which dracut"] ) @@ -125,7 +150,7 @@ def modify_grub_default(partitions, root_mount_point, distributor): zfs_root_path = None for partition in partitions: - if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs"): + if partition["mountPoint"] in ("/", "/boot") and partition["fs"] in ("btrfs", "f2fs", "zfs"): no_save_default = True break @@ -190,63 +215,46 @@ def modify_grub_default(partitions, root_mount_point, distributor): if swap_outer_mappername: kernel_params.append(f"resume=/dev/mapper/{swap_outer_mappername}") - if "overwrite" in libcalamares.job.configuration: - overwrite = libcalamares.job.configuration["overwrite"] - else: - overwrite = False + overwrite = libcalamares.job.configuration.get("overwrite", False) - distributor_line = f"GRUB_DISTRIBUTOR='{distributor_replace}'" - kernel_cmd = f'GRUB_CMDLINE_LINUX_DEFAULT="{" ".join(kernel_params)}"' - have_kernel_cmd = False - have_distributor_line = 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 i in range(len(lines)): - if lines[i].startswith("#GRUB_CMDLINE_LINUX_DEFAULT"): - lines[i] = kernel_cmd - have_kernel_cmd = True - elif lines[i].startswith("GRUB_CMDLINE_LINUX_DEFAULT"): - regex = re.compile(r"^GRUB_CMDLINE_LINUX_DEFAULT\s*=\s*") - line = regex.sub("", lines[i]) - line = line.lstrip() - line = line.lstrip("\"") - line = line.lstrip("'") - line = line.rstrip() - line = line.rstrip("\"") - line = line.rstrip("'") - existing_params = line.split() + 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] + existing_param_name = existing_param.split("=")[0].strip() - # the only ones we ever add - if existing_param_name not in [ - "quiet", "resume", "splash"]: + # 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) - lines[i] = kernel_cmd - have_kernel_cmd = True - elif (lines[i].startswith("#GRUB_DISTRIBUTOR") - or lines[i].startswith("GRUB_DISTRIBUTOR")): - if libcalamares.job.configuration.get("keep_distributor", False): - lines[i] = distributor_line - have_distributor_line = True - else: - # We're not updating because of *keep_distributor*, but if - # this was a comment line, then it's still not been set. - have_distributor_line = have_distributor_line or not lines[i].startswith("#") - # If btrfs or f2fs is used, don't save default - if no_save_default and lines[i].startswith("GRUB_SAVEDEFAULT="): - lines[i] = "#GRUB_SAVEDEFAULT=\"true\"" - else: - lines = [] + 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 value.__class__.__name__ == "bool": + for key, value in libcalamares.job.configuration["defaults"].items(): + if isinstance(value, bool): if value: escaped_value = "true" else: @@ -254,19 +262,20 @@ def modify_grub_default(partitions, root_mount_point, distributor): else: escaped_value = str(value).replace("'", "'\\''") - lines.append(f"{key}='{escaped_value}'") + grub_config_items[key] = f"'{escaped_value}'" - if not have_kernel_cmd: - lines.append(kernel_cmd) - - if not have_distributor_line: - lines.append(distributor_line) + 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: - lines.append("GRUB_ENABLE_CRYPTODISK=y") + grub_config_items["GRUB_ENABLE_CRYPTODISK"] = "y" - with open(default_grub, 'w') as grub_file: - grub_file.write("\n".join(lines) + "\n") + 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 diff --git a/src/modules/initcpiocfg/main.py b/src/modules/initcpiocfg/main.py index fb557a479..5fd9d93ae 100644 --- a/src/modules/initcpiocfg/main.py +++ b/src/modules/initcpiocfg/main.py @@ -147,6 +147,7 @@ def find_initcpio_features(partitions, root_mount_point): "base", "udev", "autodetect", + "kms", "modconf", "block", "keyboard", diff --git a/src/modules/mount/main.py b/src/modules/mount/main.py index 49ffc9627..32da36dd0 100644 --- a/src/modules/mount/main.py +++ b/src/modules/mount/main.py @@ -294,13 +294,13 @@ def mount_partition(root_mount_point, partition, partitions, mount_options, moun # Mount the subvolumes swap_subvol = libcalamares.job.configuration.get("btrfsSwapSubvol", "/@swap") for s in btrfs_subvolumes: - mount_option = "subvol={}".format(s['subvolume']) if s['subvolume'] == swap_subvol: - mount_option += "," + get_mount_options("btrfs_swap", mount_options, partition) + mount_option_no_subvol = get_mount_options("btrfs_swap", mount_options, partition) else: - mount_option += "," + get_mount_options(fstype, mount_options, partition) + mount_option_no_subvol = get_mount_options(fstype, mount_options, partition) + mount_option = f"subvol={s['subvolume']},{mount_option_no_subvol}" subvolume_mountpoint = mount_point[:-1] + s['mountPoint'] - mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option}) + mount_options_list.append({"mountpoint": s['mountPoint'], "option_string": mount_option_no_subvol}) if libcalamares.utils.mount(device, subvolume_mountpoint, fstype, diff --git a/src/modules/partition/Config.cpp b/src/modules/partition/Config.cpp index 8bdb4643d..3a5e80a7c 100644 --- a/src/modules/partition/Config.cpp +++ b/src/modules/partition/Config.cpp @@ -242,7 +242,7 @@ Config::setSwapChoice( Config::SwapChoice c ) void Config::setEraseFsTypeChoice( const QString& choice ) { - QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr ); + const QString canonicalChoice = PartUtils::canonicalFilesystemName( choice, nullptr ); if ( canonicalChoice != m_eraseFsTypeChoice ) { m_eraseFsTypeChoice = canonicalChoice; @@ -250,6 +250,17 @@ Config::setEraseFsTypeChoice( const QString& choice ) } } +void +Config::setReplaceFilesystemChoice( const QString& filesystemName ) +{ + const QString canonicalChoice = PartUtils::canonicalFilesystemName( filesystemName, nullptr ); + if ( canonicalChoice != m_replaceFileSystemChoice ) + { + m_replaceFileSystemChoice = canonicalChoice; + Q_EMIT replaceModeFilesystemChanged( canonicalChoice ); + } +} + bool Config::acceptPartitionTableType( PartitionTable::TableType tableType ) const { @@ -281,8 +292,8 @@ fillGSConfigurationEFI( Calamares::GlobalStorage* gs, const QVariantMap& configu gs->insert( "efiSystemPartitionSize_i", part_size.toBytes() ); // Assign long long int to long unsigned int to prevent compilation warning - size_t unsigned_part_size = part_size.toBytes(); - if ( unsigned_part_size != PartUtils::efiFilesystemMinimumSize() ) + auto byte_part_size = part_size.toBytes(); + if ( byte_part_size != PartUtils::efiFilesystemMinimumSize() ) { cWarning() << "EFI partition size" << sizeString << "has been adjusted to" << PartUtils::efiFilesystemMinimumSize() << "bytes"; @@ -367,7 +378,9 @@ Config::fillConfigurationFSTypes( const QVariantMap& configurationMap ) Q_ASSERT( !m_eraseFsTypes.isEmpty() ); Q_ASSERT( m_eraseFsTypes.contains( fsRealName ) ); m_eraseFsTypeChoice = fsRealName; + m_replaceFileSystemChoice = fsRealName; Q_EMIT eraseModeFilesystemChanged( m_eraseFsTypeChoice ); + Q_EMIT replaceModeFilesystemChanged( m_replaceFileSystemChoice ); } void diff --git a/src/modules/partition/Config.h b/src/modules/partition/Config.h index 9dfbf8360..e59ee6887 100644 --- a/src/modules/partition/Config.h +++ b/src/modules/partition/Config.h @@ -31,6 +31,9 @@ class Config : public QObject Q_PROPERTY( QString eraseModeFilesystem READ eraseFsType WRITE setEraseFsTypeChoice NOTIFY eraseModeFilesystemChanged ) + Q_PROPERTY( QString replaceModeFilesystem READ replaceModeFilesystem WRITE setReplaceFilesystemChoice NOTIFY + replaceModeFilesystemChanged ) + Q_PROPERTY( bool allowManualPartitioning READ allowManualPartitioning CONSTANT FINAL ) public: @@ -131,6 +134,9 @@ public: */ QString eraseFsType() const { return m_eraseFsTypeChoice; } + /// @brief Currently-selected FS type for *replace* mode + QString replaceModeFilesystem() const { return m_replaceFileSystemChoice; } + /** @brief Configured default FS type (for other modes than erase) * * This is not "Unknown" or "Unformatted" @@ -159,17 +165,20 @@ public Q_SLOTS: void setSwapChoice( int ); ///< Translates a button ID or so to SwapChoice void setSwapChoice( SwapChoice ); void setEraseFsTypeChoice( const QString& filesystemName ); ///< See property eraseModeFilesystem + void setReplaceFilesystemChoice( const QString& filesystemName ); Q_SIGNALS: void installChoiceChanged( InstallChoice ); void swapChoiceChanged( SwapChoice ); void eraseModeFilesystemChanged( const QString& ); + void replaceModeFilesystemChanged( const QString& ); private: /** @brief Handle FS-type configuration, for erase and default */ void fillConfigurationFSTypes( const QVariantMap& configurationMap ); EraseFsTypesSet m_eraseFsTypes; QString m_eraseFsTypeChoice; + QString m_replaceFileSystemChoice; FileSystem::Type m_defaultFsType; SwapChoiceSet m_swapChoices; diff --git a/src/modules/partition/gui/ChoicePage.cpp b/src/modules/partition/gui/ChoicePage.cpp index b81a8bf66..739310354 100644 --- a/src/modules/partition/gui/ChoicePage.cpp +++ b/src/modules/partition/gui/ChoicePage.cpp @@ -287,6 +287,13 @@ ChoicePage::setupChoices() m_eraseFsTypesChoiceComboBox, &QComboBox::currentTextChanged, m_config, &Config::setEraseFsTypeChoice ); connect( m_config, &Config::eraseModeFilesystemChanged, this, &ChoicePage::onActionChanged ); m_eraseButton->addOptionsComboBox( m_eraseFsTypesChoiceComboBox ); + + // Also offer it for "replace + auto* box = new QComboBox; + box->addItems( m_config->eraseFsTypes() ); + connect( box, &QComboBox::currentTextChanged, m_config, &Config::setReplaceFilesystemChoice ); + connect( m_config, &Config::replaceModeFilesystemChanged, this, &ChoicePage::onActionChanged ); + m_replaceButton->addOptionsComboBox( box ); } m_itemsLayout->addWidget( m_alongsideButton ); @@ -302,13 +309,8 @@ ChoicePage::setupChoices() m_itemsLayout->addStretch(); -#if ( QT_VERSION < QT_VERSION_CHECK( 5, 15, 0 ) ) - auto buttonSignal = QOverload< int, bool >::of( &QButtonGroup::buttonToggled ); -#else - auto buttonSignal = &QButtonGroup::idToggled; -#endif connect( m_grp, - buttonSignal, + &QButtonGroup::idToggled, this, [ this ]( int id, bool checked ) { @@ -859,8 +861,8 @@ ChoicePage::doReplaceSelectedPartition( const QModelIndex& current ) selectedDevice(), selectedPartition, { gs->value( "defaultPartitionType" ).toString(), - gs->value( "defaultFileSystemType" ).toString(), - m_config->luksFileSystemType(), + m_config->replaceModeFilesystem(), + gs->value( "luksFileSystemType" ).toString(), m_encryptWidget->passphrase() } ); Partition* homePartition = findPartitionByPath( { selectedDevice() }, *homePartitionPath ); @@ -1740,5 +1742,7 @@ ChoicePage::shouldShowEncryptWidget( Config::InstallChoice choice ) const // If there are any choices for FS, check it's not ZFS because that doesn't // support the kind of encryption we enable here. const bool suitableFS = m_eraseFsTypesChoiceComboBox ? m_eraseFsTypesChoiceComboBox->currentText() != "zfs" : true; - return ( choice == InstallChoice::Erase ) && m_enableEncryptionWidget && suitableFS; + const bool suitableChoice + = choice == InstallChoice::Erase || choice == InstallChoice::Alongside || choice == InstallChoice::Replace; + return suitableChoice && m_enableEncryptionWidget && suitableFS; } diff --git a/src/modules/umount/umount.conf b/src/modules/umount/umount.conf index 9fb922740..5842c8780 100644 --- a/src/modules/umount/umount.conf +++ b/src/modules/umount/umount.conf @@ -11,4 +11,4 @@ --- # Setting emergency to true will make it so this module is still run # when a prior module fails -emergency: false +emergency: true diff --git a/src/modules/zfs/ZfsJob.cpp b/src/modules/zfs/ZfsJob.cpp index 921645f0a..c840da846 100644 --- a/src/modules/zfs/ZfsJob.cpp +++ b/src/modules/zfs/ZfsJob.cpp @@ -241,6 +241,13 @@ ZfsJob::exec() } } + // Generate the zfs hostid file + auto i = system->runCommand( { "zgenhostid" }, std::chrono::seconds( 3 ) ); + if ( i.getExitCode() != 0 ) + { + cWarning() << "Failed to create /etc/hostid"; + } + // Create the zpool ZfsResult zfsResult; if ( encrypt ) diff --git a/src/modules/zfshostid/main.py b/src/modules/zfshostid/main.py new file mode 100644 index 000000000..702ba1ede --- /dev/null +++ b/src/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/src/modules/zfshostid/module.desc b/src/modules/zfshostid/module.desc new file mode 100644 index 000000000..13fec35be --- /dev/null +++ b/src/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/src/modules/zfshostid/zfshostid.schema.yaml b/src/modules/zfshostid/zfshostid.schema.yaml new file mode 100644 index 000000000..24774d01a --- /dev/null +++ b/src/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