#! /usr/bin/env bash # # Copyright 2021 Northern.tech AS # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Default that can be overridden by providing this method in a # configuration file passed with '--config' function platform_modify() { true } PLATFORM_MODIFY_HOOKS=(platform_modify) function user_local_modify() { true } USER_LOCAL_MODIFY_HOOKS=(user_local_modify) function overlay_modify() { true } OVERLAY_MODIFY_HOOKS=(overlay_modify) function trap_exit() { echo "mender-convert-modify has finished. Cleaning up..." sudo umount -f work/boot sudo umount -f work/rootfs } function trap_term() { true } trap trap_term INT TERM trap trap_exit EXIT echo "Running $(basename $0): $@" source modules/bootstrap.sh source modules/disk.sh source modules/probe.sh source modules/deb.sh source modules/testscfg.sh source modules/grub.sh # The mender_convert_config is always used and provides all the defaults declare -a configs=("configs/mender_convert_config") while (("$#")); do case "$1" in -o | --overlay) overlays+=("${2}") shift 2 ;; -c | --config) configs+=("${2}") shift 2 ;; -d | --disk-image) disk_image="${2}" shift 2 ;; *) log_fatal "Sorry, but the provided option is not supported: $1" ;; esac done # Note the use of %q formatting here. This is a bash feature to add # proper quoting to the strings so that spaces and special characters # will be treated properly. Primarily for supporting spaces in # pathnames and avoid splitting those into multiple parameters. source modules/config.sh $(printf "%q " "${configs[@]}") boot_part=$(disk_boot_part) root_part=$(disk_root_part) # Sysfs device paths boot_part_device=$(disk_boot_part_device) data_part_device=$(disk_data_part_device) root_part_a_device=$(disk_root_part_a_device) root_part_b_device=$(disk_root_part_b_device) # Create mount points mkdir -p work/boot mkdir -p work/rootfs sudo mount ${boot_part} work/boot sudo mount ${root_part} work/rootfs log_info "Installing Mender client and related files" if [ "${MENDER_CLIENT_INSTALL}" = "y" ]; then log_info "Installing Mender client version ${MENDER_CLIENT_VERSION}" deb_get_and_install_pacakge mender-client "${MENDER_CLIENT_VERSION}" # Save installed client version for tests in Yocto variable format testscfg_add "PREFERRED_VERSION_mender-client" "$(echo ${DEB_NAME} | sed -r 's/.*_([0-9]+\.[0-9]+\.[0-9]+).*/\1/')" fi if [ "${MENDER_ENABLE_SYSTEMD}" == "y" ]; then run_and_log_cmd "sudo ln -sf /lib/systemd/system/mender-client.service \ work/rootfs/etc/systemd/system/multi-user.target.wants/mender-client.service" fi if [ "${MENDER_ADDON_CONNECT_INSTALL}" = "y" ]; then log_info "Installing Mender Connect addon" deb_get_and_install_pacakge mender-connect "${MENDER_ADDON_CONNECT_VERSION}" run_and_log_cmd "sudo ln -sf /lib/systemd/system/mender-connect.service \ work/rootfs/etc/systemd/system/multi-user.target.wants/mender-connect.service" fi if [ "${MENDER_ADDON_CONFIGURE_INSTALL}" = "y" ]; then log_info "Installing Mender Configure addon" deb_get_and_install_pacakge mender-configure "${MENDER_ADDON_CONFIGURE_VERSION}" "true" fi # Do this unconditionally even if not installing add-ons. The reason is that if # the Debian package is installed later, this folder has to preexist. It is part # of the rootfs setup, independently of the software. log_info "Creating state folder in the data partition for Mender add-ons" run_and_log_cmd "sudo mkdir -p work/rootfs/var/lib" run_and_log_cmd "sudo mkdir -p work/rootfs/data/mender-configure" run_and_log_cmd "sudo ln -sf /data/mender-configure work/rootfs/var/lib" run_and_log_cmd "sudo mkdir -p work/rootfs/data/mender-monitor" run_and_log_cmd "sudo ln -sf /data/mender-monitor work/rootfs/var/lib" if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then # Check for known U-Boot problems in all files on the boot partition. check_for_broken_uboot_uefi_support work/boot if [ "$MENDER_GRUB_D_INTEGRATION" = y ] || ( [ "$MENDER_GRUB_D_INTEGRATION" = auto ] \ && supports_grub_d_and_efi "work/boot" "work/rootfs" ); then # No need to install Grub, use the one already present, and only install # our tools. log_info "Not installing GRUB EFI bootloader, relying on platform provided one." log_info "Installing Mender GRUB tools..." grub_install_grub_editenv_binary log_info "Generating grub config using update-grub..." grub_create_grub_config grub_install_grub_d_config else log_info "Installing GRUB EFI bootloader..." grub_install_mender_grub log_info "Generating the mender-grub config..." grub_create_grub_config grub_install_standalone_grub_config fi fi run_and_log_cmd "sudo mkdir -p work/rootfs/data/mender" run_and_log_cmd "sudo mkdir -p work/rootfs/var/lib" run_and_log_cmd "sudo ln -sf /data/mender work/rootfs/var/lib" log_info "Using root device A in mender.conf: $root_part_a_device" log_info "Using root device B in mender.conf: $root_part_b_device" cat <<- EOF > work/mender.conf.data { "RootfsPartA": "${root_part_a_device}", "RootfsPartB": "${root_part_b_device}" } EOF run_and_log_cmd "sudo cp work/mender.conf.data work/rootfs/data/mender/mender.conf" run_and_log_cmd "sudo chmod 600 work/rootfs/data/mender/mender.conf" if [ -f input/resources/mender.conf ]; then log_info "Installing the local mender.conf file" run_and_log_cmd "mkdir -p work/rootfs/etc/mender" run_and_log_cmd "cp input/resources/mender.conf work/rootfs/etc/mender" run_and_log_cmd "sudo chmod 600 work/rootfs/etc/mender/mender.conf" else log_warn "No mender.conf file found in input/resources. Have you remembered to run the bootstrap script?" fi if [ -z "${MENDER_DEVICE_TYPE}" ]; then # Observed systems who do not have this file, e.g images generated with mkosi if [ -f work/rootfs/etc/hostname ]; then device_type=$(cat work/rootfs/etc/hostname) else device_type="default" fi else device_type="${MENDER_DEVICE_TYPE}" fi run_and_log_cmd "echo 'device_type=${device_type}' > work/device_type" run_and_log_cmd "sudo install -m 0444 work/device_type work/rootfs/data/mender/" if [ "${MENDER_CLIENT_INSTALL}" = "y" ]; then run_and_log_cmd "echo 'artifact_name=${MENDER_ARTIFACT_NAME}' \ | sudo tee work/rootfs/etc/mender/artifact_info" fi log_info "Creating state scripts version file." case "${MENDER_CLIENT_VERSION}" in 1*) VERSION_STRING='2' ;; *) VERSION_STRING='3' ;; esac run_and_log_cmd "sudo mkdir -p work/rootfs/etc/mender/scripts/" run_and_log_cmd "echo -n ${VERSION_STRING} | sudo tee work/rootfs/etc/mender/scripts/version" log_info "Installing a custom /etc/fstab (see ${MENDER_CONVERT_LOG_FILE} for more info)" if [ "${MENDER_GRUB_EFI_INTEGRATION}" == "y" ]; then boot_part_mountpoint="/boot/efi" else boot_part_mountpoint="/uboot" fi run_and_log_cmd "sudo mkdir -p work/rootfs/${boot_part_mountpoint}" if [ "${MENDER_DATA_PART_GROWFS}" == "y" ]; then MENDER_DATA_PART_FSTAB_OPTS="${MENDER_DATA_PART_FSTAB_OPTS},x-systemd.growfs" fi if echo ${MENDER_ROOT_PART_FSTAB_OPTS} | tr ',' '\n' | egrep -q "^ro$"; then if ! echo "${MENDER_ROOT_PART_MKFS_OPTS}" | fgrep -q -- "^64bit"; then log_warn "EXT4 64bits feature is known to create unstable checksums on read-only file systems, add \"-O ^64bit\" to MENDER_ROOT_PART_MKFS_OPTS to remove it" fi if ! echo "${MENDER_ROOT_PART_MKFS_OPTS}" | fgrep -q -- "^has_journal"; then log_warn "EXT4 journal feature is known to create unstable checksums on read-only file systems, add \"-O ^has_journal\" to MENDER_ROOT_PART_MKFS_OPTS to remove it" fi fi log_info "Using boot partition device in fstab: $boot_part_device" log_info "Using data partition device in fstab: $data_part_device" fstab=$(sed '/\/boot/d' work/rootfs/etc/fstab) fstab=$(sed '/.*\s\/\s.*/d' <<< "$fstab") cat <<- EOF > work/rootfs/etc/fstab ${boot_part_device} ${boot_part_mountpoint} auto ${MENDER_BOOT_PART_FSTAB_OPTS} 0 0 /dev/root / auto ${MENDER_ROOT_PART_FSTAB_OPTS} 1 ${MENDER_ROOT_PART_FS_PASSNO} ${data_part_device} /data auto ${MENDER_DATA_PART_FSTAB_OPTS} 0 0 # entries kept from original fstab $fstab EOF # # Make sure to re-label rootfs when selinux is in enforcing mode # e.g. CentOS8 after conversion cannot start login shell due selinux # inspired by: https://forums.centos.org/viewtopic.php?t=48714 # if [ -f work/rootfs/etc/selinux/config ]; then grep -r 'SELINUX=Enforcing' work/rootfs/etc/selinux/config || true if [ $? -eq 0 ]; then log_info "Selinux is in enforcing mode. Enable autorelabel" touch work/rootfs/.autorelabel fi fi log_info "Performing platform specific modifications (if any)" for hook in "${PLATFORM_MODIFY_HOOKS[@]}"; do log_info "Running hook: $hook" eval $hook done log_info "Performing user/local specific modifications (if any)" for hook in "${USER_LOCAL_MODIFY_HOOKS[@]}"; do log_info "Running hook: $hook" eval $hook done for overlay in "${overlays[@]}"; do log_info "Applying rootfs overlay: ${overlay}" run_and_log_cmd "sudo rsync --archive --keep-dirlinks --verbose ${overlay}/ work/rootfs/" done log_info "Performing overlay specific modifications (if any)" for hook in "${OVERLAY_MODIFY_HOOKS[@]}"; do log_info "Running hook: $hook" eval $hook done