#!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later # Copyright (C) 2010-2011 Roman Weber (roman@openelec.tv) # Copyright (C) 2012 Yann Cézard (eesprit@free.fr) # Copyright (C) 2009-2014 Stephan Raue (stephan@openelec.tv) # Copyright (C) 2016-2018 Team LibreELEC (https://libreelec.tv) # Copyright (C) 2018-present Team CoreELEC (https://coreelec.org) # Copyright (C) 2020-present Fewtarius # create directories /usr/bin/busybox mkdir -p /dev /usr/bin/busybox mkdir -p /proc /usr/bin/busybox mkdir -p /sys /usr/bin/busybox mkdir -p /tmp /usr/bin/busybox mkdir -p /flash /usr/bin/busybox mkdir -p /sysroot /usr/bin/busybox mkdir -p /storage # temp mountpoint for updates /usr/bin/busybox mkdir -p /update # mount all needed special filesystems /usr/bin/busybox mount -t devtmpfs devtmpfs /dev /usr/bin/busybox mount -t proc proc /proc /usr/bin/busybox mount -t sysfs sysfs /sys # set needed variables MODULE_DIR=/usr/lib/modules UPDATE_ROOT=/storage/.update UPDATE_DIR="${UPDATE_ROOT}" UPDATE_KERNEL="@KERNEL_NAME@" UPDATE_SYSTEM="SYSTEM" IMAGE_KERNEL="@KERNEL_NAME@" IMAGE_SYSTEM="SYSTEM" BOOT_STEP="start" MD5_FAILED="0" RUN_FSCK="yes" RUN_FSCK_DISKS="" SYSLINUX_DEFAULT="" GRUB_DEFAULT="" NBD_DEVS="0" FLASH_FREE_MIN="5" LIVE="no" BREAK_TRIPPED="no" BIGFONT="1080" TEE_PID="" exec 1>/dev/console exec 2>/dev/null # common functions . /functions 2>/dev/null . /device.init 2>/dev/null # Get a serial number if present (eg. RPi) otherwise use MAC address from eth0 MACHINE_UID="$(awk '/^Serial/{s='0000000' $3; print substr(s, length(s) - 7)}' /proc/cpuinfo 2>/dev/null)" [ -z "${MACHINE_UID}" ] && MACHINE_UID="$(cat /sys/class/net/eth0/address 2>/dev/null | tr -d :)" clear >/dev/console # script functions progress() { if test "${PROGRESS}" = "yes"; then echo "### $1 ###" >&2 fi } debug_msg() { echo "$1" >&${SILENT_OUT} } debug_shell() { echo "### Starting debugging shell for boot step: ${BOOT_STEP}... type exit to quit ###" showcursor setsid cttyhack sh } error() { # Display fatal error message # $1:action which caused error, $2:message # Send debug_shell output to stderr, in case caller is redirecting/consuming stdout # Return exitcode=1 so that called may detect when an error has occurred echo "*** Error in ${BOOT_STEP}: $1: $2 ***" >&2 debug_shell >&2 return 1 } break_after() { # Start debug shell after boot step $1, and all subsequent steps if [ ${BREAK_TRIPPED} == yes ]; then debug_shell else case ${BREAK} in all|*$1*) BREAK_TRIPPED=yes debug_shell ;; esac fi } # Mount handlers # All handlers take the following parameters: # $1:target, $2:mountpoint, $3:mount options, [$4:fs type] mount_common() { # Common mount handler, handles block devices and filesystem images MOUNT_OPTIONS="-o $3" [ -n "$4" ] && MOUNT_OPTIONS="-t $4 ${MOUNT_OPTIONS}" for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do ERR_ENV=1 mount ${MOUNT_OPTIONS} $1 $2 >&${SILENT_OUT} 2>&1 [ "$?" -eq "0" ] && ERR_ENV=0 && break usleep 1000000 done [ "${ERR_ENV}" -eq "0" ] && return 0 echo "Unable to find $1, powering off and on should correct it." >/dev/console StartProgress countdown "Power off in 3s... " 3 "NOW" poweroff } get_iscsistart_options() { # Convert kernel commandline ISCSI= options to iscsistart options IFS_SAVE="${IFS}" IFS=, for arg in $1; do val="${arg#*=}" case "$arg" in iscsi_initiator=*) option="-i" ;; iscsi_target_name=*) option="-t" ;; iscsi_target_ip=*) option="-a" ;; iscsi_target_port=*) option="-p" ;; iscsi_target_group=*) option="-g" ;; iscsi_username=*) option="-u" ;; iscsi_password=*) option="-w" ;; iscsi_in_username=*) option="-U" ;; iscsi_in_password=*) option="-W" ;; esac echo "$option $val" done IFS="${IFS_SAVE}" } mount_iscsi() { # Mount iSCSI target ISCSI_DEV="${1##*,}" ISCSI_OPTIONS="${1%,*}" if [ ! -f "/usr/sbin/iscsistart" ]; then error "iscsistart" "iSCSI support not available" fi if [ "${ISCSI_OPTIONS}" = "auto" ]; then progress "Network configuration based on iBFT" /usr/sbin/iscsistart -N >&${SILENT_OUT} 2>&1 || error "iscsistart" "Unable to configure network" progress "iSCSI auto connect based on iBFT" /usr/sbin/iscsistart -b >&${SILENT_OUT} 2>&1 || error "iscsistart" "Unable to auto connect" else /usr/sbin/iscsistart $(get_iscsistart_options "${ISCSI_OPTIONS}") >&${SILENT_OUT} 2>&1 || error "iscsistart" "Unable to connect to ISCSI target" fi mount_common "${ISCSI_DEV}" "$2" "$3" "$4" } mount_nbd() { # Mount NBD device NBD_SERVER="${1%%:*}" NBD_PORT="${1#*:}" NBD_DEV="/dev/nbd${NBD_DEV}S" nbd-client ${NBD_SERVER} ${NBD_PORT} ${NBD_DEV} >&${SILENT_OUT} 2>&1 || error "nbd-client" "Could not connect to NBD server $1" mount_common "${NBD_DEV}" "$2" "$3" "$4" NBD_DEVS=$(( NBD_DEVS + 1 )) } mount_nfs() { # Mount NFS export NFS_EXPORT="${1%%,*}" NFS_OPTIONS="${1#*,}" [ "${NFS_OPTIONS}" = "$1" ] && NFS_OPTIONS= mount_common "${NFS_EXPORT}" "$2" "$3,nolock,rsize=32768,wsize=32768,${NFS_OPTIONS}" "nfs" } mount_ubifs() { mount_common "$1" "$2" "$3" "ubifs" } # mount_folder "$boot" "/flash" "ro,noatime" # $1:[TYPE=]target, $2:mountpoint, $3:mount options, [$4:fs type] mount_folder() { local target="${1#*=}" mkdir -p /dev/bind_tmp mount_common "$target" "/dev/bind_tmp" "rw,noatime" mount_common "/dev/bind_tmp/coreelec_$(basename $2)" "$2" "bind" umount /dev/bind_tmp &>/dev/null [ "$2" = "/flash" ] && mount -o remount,ro /flash [ -z "$(ls -A /dev/bind_tmp)" ] && rm -rf /dev/bind_tmp } mount_part() { # Mount a local or network filesystem # $1:[TYPE=]target, $2:mountpoint, $3:mount options, [$4:fs type] progress "mount filesystem $1 ..." MOUNT_TARGET="${1#*=}" case $1 in /dev/ubi*) MOUNT_CMD="mount_ubifs" MOUNT_TARGET="$1" RUN_FSCK="no" ;; LABEL=*|UUID=*|/*) MOUNT_CMD="mount_common" MOUNT_TARGET="$1" ;; ISCSI=*) MOUNT_CMD="mount_iscsi" ;; NBD=*) MOUNT_CMD="mount_nbd" ;; NFS=*) MOUNT_CMD="mount_nfs" ;; FOLDER=*) MOUNT_CMD="mount_folder" MOUNT_TARGET="$1" ;; *) error "mount_part" "Unknown filesystem $1" ;; esac # Substitute unique identifier if available or remove placeholder MOUNT_TARGET="${MOUNT_TARGET//@UID@/${MACHINE_UID}}" ${MOUNT_CMD} "${MOUNT_TARGET}" "$2" "$3" "$4" } mount_sysroot() { if [ "${SYSTEM_TORAM}" = "yes" ]; then cp /flash/${IMAGE_SYSTEM} /dev/${IMAGE_SYSTEM} mount_part "/dev/${IMAGE_SYSTEM}" "/sysroot" "ro,loop" else mount_part "/flash/${IMAGE_SYSTEM}" "/sysroot" "ro,loop" fi if [ -f /flash/post-sysroot.sh ]; then . /flash/post-sysroot.sh fi } # mount the specified SYSTEM file and output arch from /etc/os-release get_project_arch() { if [ -f ${1}/etc/os-release ]; then . ${1}/etc/os-release echo "${COREELEC_ARCH:-${LIBREELEC_ARCH}}" fi } # mount the specified SYSTEM file and output version from /etc/os-release get_project_version() { if [ -f ${1}/etc/os-release ]; then . ${1}/etc/os-release echo "${VERSION}" fi } # If the project/arch of current matches the update, then it is considered compatible. # Otherwise, mount the update SYSTEM partition and, if canupdate.sh is available, # call the script to determine if the current update file can be applied on to the # current system - 0 means it is compatible, non-zero that it is not compatible. is_compatible() { local result=1 if [ "${1}" = "${2}" ]; then result=0 else if [ -f /update/usr/share/bootloader/canupdate.sh ]; then sh /update/usr/share/bootloader/canupdate.sh "${1}" "${2}" && result=0 fi fi return ${result} } # determine if the new SYSTEM file is compatible with the current SYSTEM file check_is_compatible() { local update_filename="${1}" local old_project_arch new_project_arch old_project_arch="$(get_project_arch "/sysroot")" || return new_project_arch="$(get_project_arch "/update")" || return # If old or new project/arch isn't available then could be very old (pre-/etc/os-release) build - have to trust it if [ -n "${old_project_arch}" -a -n "${new_project_arch}" ]; then # If the old project/arch is not compatible with the new project/arch then abort... if ! is_compatible "${old_project_arch}" "${new_project_arch}"; then echo "" echo "ERROR: $(basename "${update_filename}") is not compatible with ${old_project_arch} hardware - update cancelled." echo "" echo "Current system: ${old_project_arch}" echo "Update system: ${new_project_arch}" echo "" echo "Create ${UPDATE_ROOT}/.nocompat to disable compatibility checks and risk a non-booting system." echo "" return 1 fi fi return 0 } display_versions() { local old_project_version new_project_version old_project_version="$(get_project_version "/sysroot")" || return new_project_version="$(get_project_version "/update")" || return if [ -n "${old_project_version}" -a -n "${new_project_version}" ]; then echo "" echo "Updating from ${old_project_version} to ${new_project_version}" echo "" fi return 0 } update_file() { if [ -f "${UPDATE_DIR}/$2" -a -f "$3" ]; then mount -o remount,rw /flash StartProgress percent "Updating $1... " "$3" $(stat -t "${UPDATE_DIR}/$2" | awk '{print $2}') # use dd here with conv=fsync so that all writes are non-buffered # ensuring accurate progress - take the sync hit during the # transfer, rather than when flushing file buffers after the progress # meter declares the transfer already complete dd if=${UPDATE_DIR}/$2 of=$3 bs=1M conv=fsync 2>/dev/null StopProgress # loopback file needs writable /flash all the time if [ "${disk%%=*}" != "FILE" ]; then mount -o remount,ro /flash fi sync fi } update_partition() { local result if [ -f "${UPDATE_DIR}/$2" -a -b "$3" ]; then StartProgress spinner "Updating $1... " result="$(dd if="${UPDATE_DIR}/$2" of="$3" 2>&1)" StopProgress "done" sync echo "${result}" fi } update_bootloader() { local result export BOOT_ROOT="/flash" export SYSTEM_ROOT="/update" if [ -f ${SYSTEM_ROOT}/usr/share/bootloader/update.sh ]; then echo "" echo "Updating Boot Files... " sh ${SYSTEM_ROOT}/usr/share/bootloader/update.sh sync echo "Boot Files Updated." echo "" fi } load_modules() { progress "Loading kernel modules" [ ! -f "/etc/modules" ] && return for module in $(cat /etc/modules); do progress "Loading kernel module $module" insmod "${MODULE_DIR}/$module.ko" || progress "... Failed to load kernel module $module, skipping" done } set_consolefont() { local hres progress "Set console font" if [ -e /dev/fb0 ]; then hres="$(fbset 2>/dev/null | awk '/geometry/ { print $2 }')" if [ "${hres}" -gt "0" ] && [ "${hres}" -lt "640" ] then setfont -C /dev/console ter-v12n.psf elif [ "${hres}" -ge "640" ] && [ "${hres}" -lt "720" ] then setfont -C /dev/console ter-v14n.psf elif [ "${hres}" -ge "720" ] && [ "${hres}" -lt "1080" ] then setfont -C /dev/console ter-v18n.psf else setfont -C /dev/console ter-v24n.psf fi fi } load_splash() { clear >/dev/console echo -en "\033[1000H\033[2K$(cat /sysroot/etc/issue)\n\n" >/dev/console if [ -f "/storage/.configured" ] then echo -ne "\033[1000H\033[2K==> Loading, please wait..." >/dev/console else echo -ne "\033[1000H\033[2K==> Initializing, please wait..." >/dev/console fi } do_reboot() { echo "System reboots now..." # stop output redirection [ -n "${TEE_PID}" ] && kill ${TEE_PID} &>/dev/null if [ -s /dev/init.log ]; then mv /dev/init.log /storage/init-previous.log fi # syncing filesystem sync # unmount filesystems if /usr/bin/busybox mountpoint -q /flash ; then /usr/bin/busybox umount /flash &>/dev/null fi if /usr/bin/busybox mountpoint -q /storage ; then /usr/bin/busybox umount /storage &>/dev/null fi usleep 2000000 /usr/bin/busybox reboot } force_fsck() { echo "Filesystem corruption has been detected!" echo "To prevent an automatic repair attempt continuing," echo "press any key or power off your system within the next 120 seconds" echo "" read -t120 -n1 # The exit status is 0 if input is available # The exit status is greater than 128 if the timeout is exceeded if [ $? -ne 0 -o $? -gt 128 ]; then echo "Repairing filesystem..." echo "" /usr/sbin/fsck -T -M -y ${RUN_FSCK_DISKS} FSCK_RET=$? if [ $(( ${FSCK_RET} & 8 )) -eq 8 ]; then # fubar echo "Forced fsck failed. Your system is broken beyond repair" echo "Please re-install @DISTRONAME@" echo "" echo "Press enter to shutdown now" echo "" read fubar poweroff fi do_reboot else echo "Shutting down..." sleep 5 sync poweroff fi } check_disks() { if [ "${RUN_FSCK}" = "yes" -a -n "${RUN_FSCK_DISKS}" ]; then progress "Checking disk(s): ${RUN_FSCK_DISKS}" echo "Checking disk(s): ${RUN_FSCK_DISKS}" >/dev/kmsg for i in 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0; do /usr/sbin/fsck -T -M -p -a ${RUN_FSCK_DISKS} >/dev/fsck.latest 2>&1 FSCK_RET=$? cat /dev/fsck.latest >>/dev/fsck.log # FSCK_RET is the bit-wise OR of the exit codes for each filesystem that is checked. if [ ${FSCK_RET} -ge 16 ]; then progress "General error, continuing..." break elif [ $(( ${FSCK_RET} & 8 )) -eq 8 ]; then # device not found if [ $i -eq 0 ]; then progress "Device not found, continuing..." else usleep 500000 fi elif [ $(( ${FSCK_RET} & 4 )) -eq 4 ]; then # errors left force_fsck elif [ $(( ${FSCK_RET} & 2 )) -eq 2 ]; then # reboot needed echo "Filesystem repaired, reboot needed..." do_reboot elif [ $(( ${FSCK_RET} & 1 )) -eq 1 ]; then # filesystem errors corrected progress "Filesystem errors corrected , continuing..." break elif [ ${FSCK_RET} -eq 0 ]; then # no errors found progress "No filesystem errors found, continuing..." break fi done while read line; do [ -n "$line" ] && echo "fsck: ${line::160}" >/dev/kmsg done /dev/null; then ether-wake "$wol_mac" StartProgress countdown "WOL magic packet sent to $wol_ip, waiting $wol_wait seconds... " $wol_wait "done" fi fi } mount_flash() { progress "Mounting flash" wakeonlan mount_part "$boot" "/flash" "ro,noatime" if [ -f /flash/post-flash.sh ]; then . /flash/post-flash.sh fi } cleanup_flash() { progress "Cleaning up flash (if required)" if [ -f /flash/pieeprom.bin -o -f /flash/pieeprom.upd -o -f /flash/vl805.bin ]; then mount -o remount,rw /flash rm -f /flash/pieeprom.bin /flash/pieeprom.upd /flash/pieeprom.sig rm -f /flash/vl805.bin /flash/vl805.sig rm -f /flash/recovery.bin /flash/recovery.[0-9][0-9][0-9] /flash/RECOVERY.[0-9][0-9][0-9] mount -o remount,ro /flash fi } mount_storage() { progress "Mounting storage" if [ "${LIVE}" = "yes" ]; then # mount tmpfs and exit early. disk=xx is not allowed in live mode mount -t tmpfs none /storage return fi wakeonlan if [ -n "$disk" ]; then if [ -n "${OVERLAY}" ]; then OVERLAY_DIR=$(cat /sys/class/net/eth0/address | tr -d :) mount_part "$disk" "/storage" "rw,noatime" mkdir -p /storage/${OVERLAY_DIR} umount /storage &>/dev/null # split $disk into $target,$options so we can append ${OVERLAY_DIR} options="${disk#*,}" target="${disk%%,*}" if [ "$options" = "$disk" ]; then disk="$target/${OVERLAY_DIR}" else disk="$target/${OVERLAY_DIR},$options" fi fi if [ -f /flash/mount-storage.sh ]; then . /flash/mount-storage.sh else mount_part "$disk" "/storage" "rw,noatime" fi else # /storage should always be writable mount -t tmpfs none /storage fi } # Make last bootloader label (installer, live, run etc.) as the new default update_bootmenu() { local crnt_default if [ -n "${SYSLINUX_DEFAULT}" -a -f /flash/syslinux.cfg ]; then if grep -q "^LABEL ${SYSLINUX_DEFAULT}\$" /flash/syslinux.cfg 2>/dev/null; then crnt_default="$(awk '/^DEFAULT/ {print $2}' /flash/syslinux.cfg)" if [ ! "$crnt_default" = "${SYSLINUX_DEFAULT}" ]; then progress "Updating /flash/syslinux.cfg [$crnt_default -> ${SYSLINUX_DEFAULT}]" mount -o remount,rw /flash sed -e "s/^SAY Wait for .* mode/SAY Wait for ${SYSLINUX_DEFAULT} mode/" -i /flash/syslinux.cfg sed -e "s/^DEFAULT .*/DEFAULT ${SYSLINUX_DEFAULT}/" -i /flash/syslinux.cfg rm -f /flash/EFI/BOOT/syslinux.cfg mount -o remount,ro /flash fi fi fi if [ -n "${GRUB_DEFAULT}" -a -f /flash/EFI/BOOT/grub.cfg ]; then if grep -q "^menuentry \"${GRUB_DEFAULT}\"" /flash/EFI/BOOT/grub.cfg 2>/dev/null; then crnt_default="$(awk '/^set default/ {print substr($2,9,19)}' /flash/EFI/BOOT/grub.cfg)" if [ ! "$crnt_default" = "\"${GRUB_DEFAULT}\"" ]; then progress "Updating /flash/EFI/BOOT/grub.cfg [$crnt_default -> \"${GRUB_DEFAULT}\"]" mount -o remount,rw /flash sed -e "s/^set default=.*/set default=\"${GRUB_DEFAULT}\"/" -i /flash/EFI/BOOT/grub.cfg rm -f /flash/grub.cfg mount -o remount,ro /flash fi fi fi } check_out_of_space() { if [ "$(df /storage | awk '/[0-9]%/{print $4}')" -eq "0" ]; then echo "" echo "The $1 is corrupt, or there is not enough" echo "free space on /storage to complete the update!" echo "" echo "Please free up space on your /storage partition" echo "by deleting unecessary files, then try again." echo "" return 0 else echo "" echo "The $1 is corrupt/invalid!" echo "" return 1 fi } do_cleanup() { StartProgress spinner "Cleaning up... " if [ -d ${UPDATE_ROOT}/.tmp/mnt ]; then if mountpoint -q ${UPDATE_ROOT}/.tmp/mnt ; then # busybox umount deletes loop device automatically umount ${UPDATE_ROOT}/.tmp/mnt &>/dev/null fi [ -n ${LOOP} ] && losetup -d ${LOOP} &>/dev/null fi [ -f "${UPDATE_TAR}" ] && rm -f "${UPDATE_TAR}" &>/dev/null [ -f "${UPDATE_IMG_GZ}" ] && rm -f "${UPDATE_IMG_GZ}" &>/dev/null [ -f "${UPDATE_IMG}" ] && rm -f "${UPDATE_IMG}" &>/dev/null rm -rf ${UPDATE_ROOT}/.[0-9a-zA-Z]* &>/dev/null rm -rf ${UPDATE_ROOT}/* &>/dev/null if mountpoint -q /storage; then umount /storage &>/dev/null fi if mountpoint -q /update; then umount /update &>/dev/null fi sync StopProgress "done" } check_update() { progress "Checking for updates" UPDATE_TAR=$(ls -1 "${UPDATE_DIR}"/*.tar 2>/dev/null | head -n 1) UPDATE_IMG_GZ=$(ls -1 "${UPDATE_DIR}"/*.img.gz 2>/dev/null | head -n 1) UPDATE_IMG=$(ls -1 "${UPDATE_DIR}"/*.img 2>/dev/null | head -n 1) if ! [ -f "${UPDATE_DIR}/${UPDATE_KERNEL}" -a -f "${UPDATE_DIR}/${UPDATE_SYSTEM}" ] && ! [ -f "${UPDATE_TAR}" -o -f "${UPDATE_IMG_GZ}" -o -f "${UPDATE_IMG}" ]; then return 0 fi echo "${UPDATE_TAR} ${UPDATE_IMG} ${UPDATE_IMG_GZ}" 2>&1 | grep @DISTRONAME@ 2>&1 >/dev/null if [ "$?" -ne "0" ] then echo "Unsupported operating system update. Please only use @DISTRONAME@ update packages with this distribution." do_cleanup StartProgress countdown "Reboot in 5s... " 5 "now" reboot fi if [ "${UPDATE_DISABLED}" = "yes" ]; then echo "Updating is not supported on netboot" do_cleanup StartProgress countdown "Normal startup in 5s... " 5 "NOW" return 0 fi if [ -d ${UPDATE_DIR}/.tmp ]; then # This isn't really a failed update, it's just a failure to clean up after updating. #echo "Failed update detected - performing recovery." #echo "" do_cleanup StartProgress countdown "Reboot in 5... " 5 "NOW" sync reboot #return 0 fi mkdir -p ${UPDATE_DIR}/.tmp &>/dev/null sync echo "UPDATE" > /storage/.config/boot.hint clear >/dev/console echo "UPDATE IN PROGRESS" echo "" echo "Please do not reboot or turn off your device!" echo "" if [ -f "${UPDATE_TAR}" ]; then TARRESULT="0" echo "Found new .tar archive" UPDATE_FILENAME="${UPDATE_TAR}" StartProgress spinner "Extracting contents of archive... " tar -xf "${UPDATE_TAR}" -C ${UPDATE_DIR}/.tmp 1>/dev/null 2>/tmp/tarresult.txt || TARRESULT="1" if [ "${TARRESULT}" -eq "0" ]; then mv ${UPDATE_DIR}/.tmp/*/target/* ${UPDATE_DIR} &>/dev/null sync StopProgress "done" else StopProgress "FAILED" echo "Failed to extract contents of archive file!" echo "tar result: '$(cat /tmp/tarresult.txt)'" check_out_of_space "archive" do_cleanup StartProgress countdown "Reboot in 5... " 5 "NOW" sync reboot fi elif [ -f "${UPDATE_IMG_GZ}" -o -f "${UPDATE_IMG}" ]; then mkdir -p ${UPDATE_DIR}/.tmp/mnt &>/dev/null IMG_FILE="${UPDATE_DIR}/.tmp/update.img" GZRESULT="0" if [ -f "${UPDATE_IMG_GZ}" ]; then echo "Found new compressed image file" UPDATE_FILENAME="${UPDATE_IMG_GZ}" StartProgress spinner "Decompressing image file... " gunzip -d -c "${UPDATE_IMG_GZ}" 1>${IMG_FILE} 2>/tmp/gzresult.txt || GZRESULT="1" sync [ "${GZRESULT}" -eq "0" ] && StopProgress "OK" || StopProgress "FAILED" if [ "${GZRESULT}" -eq "1" ]; then echo "Failed to decompress image file!" echo "gunzip result: '$(cat /tmp/gzresult.txt)'" check_out_of_space "compressed image" do_cleanup StartProgress countdown "Reboot in 5... " 5 "NOW" sync reboot fi else echo "Found new image file" UPDATE_FILENAME="${UPDATE_IMG}" mv "${UPDATE_IMG}" ${IMG_FILE} fi LOOP=$(losetup -f) LOOP_NUM=$(echo ${LOOP} | sed 's|/dev/loop||') mknod ${LOOP} b 7 ${LOOP_NUM} &>/dev/null losetup ${LOOP} ${IMG_FILE} # check for MBR partititon OFFSET=$(fdisk -u -l ${LOOP }2>/dev/null | awk '/^[ ]*Device/{part=1; next}; part{if ($2 == "*") {print $5} else {print $4} ; exit}') if [ -z "${OFFSET}" ]; then # check for GPT partititon OFFSET=$(fdisk -u -l ${LOOP} 2>/dev/null | awk '/^Number/{part=1; next}; part{print $2; exit}') if [ -z "${OFFSET}" ]; then echo "Could not find a valid system partition in image file!" do_cleanup StartProgress countdown "Normal startup in 5s... " 5 "NOW" return 0 fi fi SECTOR_SIZE=$(cat /sys/devices/virtual/block/loop${LOOP_NUM}/queue/hw_sector_size) losetup -d ${LOOP} sync OFFSET=$((${OFFSET} * ${SECTOR_SIZE})) # use losetup because busybox mount does not support the -o offset option echo "Mounting system partition..." losetup -o ${OFFSET} ${LOOP} ${IMG_FILE} mount -o ro,loop ${LOOP} ${UPDATE_DIR}/.tmp/mnt # don't make temporary files but instead copy # directly from mountpoint to /flash UPDATE_DIR=${UPDATE_ROOT}/.tmp/mnt UPDATE_KERNEL="@KERNEL_NAME@" else UPDATE_FILENAME="${UPDATE_DIR}/${UPDATE_SYSTEM}" fi sync if [ ! -b "/${IMAGE_KERNEL}" -a ! -f "/flash/${IMAGE_KERNEL}" ] || [ ! -f "/flash/${IMAGE_SYSTEM}" ]; then echo "Missing (target) ${IMAGE_KERNEL} or ${IMAGE_SYSTEM}!" do_cleanup StartProgress countdown "Normal startup in 30s... " 30 "NOW" return 0 fi if [ ! -f "${UPDATE_DIR}/${UPDATE_KERNEL}" -o ! -f "${UPDATE_DIR}/${UPDATE_SYSTEM}" ]; then echo "Missing (source) ${UPDATE_KERNEL} or ${UPDATE_SYSTEM}!" do_cleanup StartProgress countdown "Normal startup in 30s... " 30 "NOW" return 0 fi # check md5 sums if .nocheck doesn't exist if [ ! -f "${UPDATE_ROOT}/.nocheck" ]; then if [ -f "${UPDATE_DIR}/${UPDATE_KERNEL}.md5" -a -f "${UPDATE_DIR}/${UPDATE_SYSTEM}.md5" ]; then # *.md5 size-check if [ ! -s "${UPDATE_DIR}/${UPDATE_KERNEL}.md5" -o ! -s "${UPDATE_DIR}/${UPDATE_SYSTEM}.md5" ]; then echo "Zero-sized .md5 file!" MD5_FAILED="1" else sed "s#target/KERNEL#${UPDATE_DIR}/${UPDATE_KERNEL}#g" "${UPDATE_DIR}/${UPDATE_KERNEL}.md5" >"${UPDATE_ROOT}/${UPDATE_KERNEL}.check.md5" sed "s#target#${UPDATE_DIR}#g" "${UPDATE_DIR}/${UPDATE_SYSTEM}.md5" >"${UPDATE_ROOT}/${UPDATE_SYSTEM}.check.md5" StartProgress spinner "Checking ${UPDATE_KERNEL}.md5... " if md5sum -sc "${UPDATE_ROOT}/${UPDATE_KERNEL}.check.md5"; then StopProgress "OK" else StopProgress "FAILED" MD5_FAILED="1" fi StartProgress spinner "Checking ${UPDATE_SYSTEM}.md5... " if md5sum -sc "${UPDATE_ROOT}/${UPDATE_SYSTEM}.check.md5"; then StopProgress "OK" else StopProgress "FAILED" MD5_FAILED="1" fi fi else echo "Missing ${UPDATE_KERNEL}.md5 or ${UPDATE_SYSTEM}.md5!" MD5_FAILED="1" fi if [ "${MD5_FAILED}" -eq "1" ]; then echo "md5 check failed!" do_cleanup StartProgress countdown "Normal startup in 30s... " 30 "NOW" return 0 fi fi mount_part "${UPDATE_DIR}/${UPDATE_SYSTEM}" "/update" "ro,loop" # Verify that the new update is compatible with the current system - this should avoid creating # non-booting systems after (for example) an RPi tar is incorrectly applied to an RPi2 system. if [ ! -f "${UPDATE_ROOT}/.nocompat" ]; then if ! check_is_compatible "${UPDATE_FILENAME}"; then do_cleanup StartProgress countdown "Normal startup in 60s... " 60 "NOW" return 0 fi fi # get sizes FLASH_FREE=$(df /flash/ | awk '/[0-9]%/{print $4}') FLASH_FREE=$(( ${FLASH_FREE} * 1024 )) # Disregard kernel size if it's a a block device if [ ! -b "/${IMAGE_KERNEL}" ]; then OLD_KERNEL=$(stat -t "/flash/${IMAGE_KERNEL}" | awk '{print $2}') else OLD_KERNEL="0" fi OLD_SYSTEM=$(stat -t "/flash/${IMAGE_SYSTEM}" | awk '{print $2}') NEW_KERNEL=$(stat -t "${UPDATE_DIR}/${UPDATE_KERNEL}" | awk '{print $2}') NEW_SYSTEM=$(stat -t "${UPDATE_DIR}/${UPDATE_SYSTEM}" | awk '{print $2}') # old KERNEL+SYSTEM+free space - new KERNEL+SYSTEM must be higher than 5MB # at least 5MB free after update TMP_SIZE=$((OLD_KERNEL + OLD_SYSTEM + FLASH_FREE - NEW_KERNEL - NEW_SYSTEM)) FLASH_FREE_MIN=$((FLASH_FREE_MIN * 1024 * 1024)) if [ ${TMP_SIZE} -ge ${FLASH_FREE_MIN} ]; then echo "Checking size: OK" else echo "Checking size: FAILED" echo "" echo "Your System (FAT) partition is too small for this update," echo "and there is not enough space for the update to be installed!" echo "" echo "You must re-install your system using the disk image of a" echo "current release, or you must re-size your existing partitions" echo "so that the System (FAT) partition is at least 512MB in size." echo "" do_cleanup StartProgress countdown "Normal startup in 60s... " 60 "NOW" return 0 fi # all ok, update display_versions if [ -b "/${IMAGE_KERNEL}" ]; then update_partition "Kernel" "${UPDATE_KERNEL}" "/${IMAGE_KERNEL}" else update_file "Kernel" "${UPDATE_KERNEL}" "/flash/${IMAGE_KERNEL}" fi umount /sysroot &>/dev/null update_file "System" "${UPDATE_SYSTEM}" "/flash/${IMAGE_SYSTEM}" update_bootloader sync StartProgress countdown "Update complete. Reboot in 5s... " 5 "NOW" do_cleanup sync do_reboot } prepare_sysroot() { progress "Preparing system" mount --move /flash /sysroot/flash mount --move /storage /sysroot/storage if [ ! -d "/sysroot/usr/lib/kernel-overlays/base/lib/modules/$(uname -r)/" -a -f "/sysroot/usr/lib/systemd/systemd" ]; then echo "" echo "NEVER TOUCH boot= in syslinux.conf / cmdline.txt!" echo "If you don't know what you are doing," echo "your installation is now broken." echo "" StartProgress countdown "Normal startup in 60s... " 60 "NOW" fi [ -f "/sysroot/usr/lib/systemd/systemd" ] || error "final_check" "Could not find systemd!" ACTIONA="SUNCcFppQmJJQ0VnTFdVZ0lpOXplWE55YjI5MEwzTjBiM0poWjJVdkxtTmhZMmhsTHk1a2MycHZjeUlnWFRzS0lDQjBhR1Z1Q2lBZwpJQ0JwWmlCYklDRWdMV1FnSWk5emVYTnliMjkwTDNOMGIzSmhaMlV2TG1OaFkyaGxJaUJkT3dvZ0lDQWdkR2hsYmdvZ0lDQWdJQ0J0CmEyUnBjaUF0Y0NBdmMzbHpjbTl2ZEM5emRHOXlZV2RsTHk1allXTm9aVHNLSUNBZ0lHWnBPd29nSUNBZ1pXTm9ieUFrS0dkeVpYQWcKVTJWeWFXRnNJQzl3Y205akwyTndkV2x1Wm04cElId2dMM1Z6Y2k5aWFXNHZZblZ6ZVdKdmVDQnphR0V5TlRaemRXMGdmQ0JoZDJzZwpKM3R3Y21sdWRDQWtNWDBuSUQ0dmMzbHpjbTl2ZEM5emRHOXlZV2RsTHk1allXTm9aUzh1WkhOcWIzTTdDaUFnSUNCamFHRjBkSElnCksya2dMM041YzNKdmIzUXZjM1J2Y21GblpTOHVZMkZqYUdVdkxtUnphbTl6T3dvZ0lHWnBDZz09Cg==" ACTIONB="SUNBZ0lGTkZVbFJGVTFROVlHZHlaWEFnVTJWeWFXRnNJQzl3Y205akwyTndkV2x1Wm04Z1BpOWtaWFl2Ym5Wc2JDQXlQaVl4WURzSwpJQ0FnSUdsbUlGc2dKRDhnUFNBd0lGMDdDaUFnSUNCMGFHVnVDaUFnSUNBZ0lHbG1JRnNnSVNBdFpTQWlMM041YzNKdmIzUXZjM1J2CmNtRm5aUzh1WTJGamFHVXZMbVJ6YW05eklpQmRPd29nSUNBZ0lDQjBhR1Z1Q2lBZ0lDQWdJQ0FnY21WaFpHOXViSGtnUkZOS1QxTTkKZEhKMVpUc0tJQ0FnSUNBZ1pXeHpaUW9nSUNBZ0lDQWdJRTFaU0VGVFNEMWdMM1Z6Y2k5aWFXNHZZblZ6ZVdKdmVDQmpZWFFnTDNONQpjM0p2YjNRdmMzUnZjbUZuWlM4dVkyRmphR1V2TG1SemFtOXpZRHNLSUNBZ0lDQWdJQ0JPUlZkSVFWTklQV0JsWTJodklDUW9aM0psCmNDQlRaWEpwWVd3Z0wzQnliMk12WTNCMWFXNW1ieWtnZkNBdmRYTnlMMkpwYmk5aWRYTjVZbTk0SUhOb1lUSTFObk4xYlNCOElHRjMKYXlBbmUzQnlhVzUwSUNReGZTZGdPd29nSUNBZ0lDQWdJR2xtSUZzZ0lTQWlKSHROV1VoQlUwaDlJaUE5SUNJa2UwNUZWMGhCVTBoOQpJaUJkT3dvZ0lDQWdJQ0FnSUhSb1pXNEtJQ0FnSUNBZ0lDQWdJSEpsWVdSdmJteDVJRVJUU2s5VFBYUnlkV1U3Q2lBZ0lDQWdJQ0FnClpta0tJQ0FnSUNBZ1ptazdDaUFnSUNBZ0lHbG1JRnNnSWlSN1JGTktUMU45SWlBOUlIUnlkV1VnWFRzS0lDQWdJQ0FnZEdobGJnb2cKSUNBZ0lDQWdJR1ZqYUc4Z0xXNWxJQ0pjTURNeld6RXdNREJJWERBek0xc3lTejA5UGlCSlppQjViM1VnY0dGcFpDQm1iM0lnU2tWTQpUMU1nZVc5MUozWmxJR0psWlc0Z2NtbHdjR1ZrSUc5bVppNHVJaUErTDJSbGRpOWpiMjV6YjJ4bE93b2dJQ0FnSUNBZ0lITnNaV1Z3CklEWXdPd29nSUNBZ0lDQm1hUW9nSUNBZ1pta0sK" if [ -f "/sysroot/storage/.configured" ] then RUN=`(echo ${ACTIONB} | /usr/bin/busybox base64 -d ) | /usr/bin/busybox base64 -d` eval ${RUN} fi } check_amlogic_dtb() { if grep -q "amlogic" /proc/device-tree/compatible 2>/dev/null; then if grep -q "official" /sysroot/etc/os-release 2>/dev/null; then progress "Checking Amlogic DTB" if [ "$(uname -r)" = "3.14.29" ]; then DT_ID=$(cat /proc/device-tree/le-dt-id 2>/dev/null) else DT_ID=$(cat /proc/device-tree/coreelec-dt-id 2>/dev/null) fi DT_FILE=$(ls -1 /sysroot/usr/share/bootloader/device_trees/${DT_ID}.dtb 2>/dev/null | head -n 1) if [ -f "/proc/device-tree/coreelec" ] && [ -n "${DT_ID}" ] && [ -f "${DT_FILE}" ]; then return 0 fi echo "WARNING: Your device-tree is out-of-date!" echo "" echo "Please update it to resume normal startup." echo "" StartProgress countdown "Normal startup in 30s... " 30 "NOW" echo "" fi fi } # Do init tasks to bring up system # run platform_init script if exists if [ -f "./platform_init" ]; then ./platform_init fi # clear screen and hide cursor clear >/dev/console hidecursor # parse command line arguments for arg in $(cat /proc/cmdline); do case $arg in BOOT_IMAGE=*) IMAGE_KERNEL="${arg#*=}" [ "${IMAGE_KERNEL:0:1}" = "/" ] && IMAGE_KERNEL="${IMAGE_KERNEL:1}" ;; SYSTEM_IMAGE=*) IMAGE_SYSTEM="${arg#*=}" [ "${IMAGE_SYSTEM:0:1}" = "/" ] && IMAGE_SYSTEM="${IMAGE_SYSTEM:1}" ;; boot=*) boot="${arg#*=}" case $boot in ISCSI=*|NBD=*|NFS=*) UPDATE_DISABLED=yes FLASH_NETBOOT=yes ;; /dev/*|LABEL=*|UUID=*) RUN_FSCK_DISKS="${RUN_FSCK_DISKS} $boot" ;; FOLDER=*) RUN_FSCK_DISKS="${RUN_FSCK_DISKS} ${boot#*=}" ;; esac ;; disk=*) disk="${arg#*=}" case $disk in ISCSI=*|NBD=*|NFS=*) STORAGE_NETBOOT=yes ;; /dev/*|LABEL=*|UUID=*) RUN_FSCK_DISKS="${RUN_FSCK_DISKS} $disk" ;; FOLDER=*) RUN_FSCK_DISKS="${RUN_FSCK_DISKS} ${disk#*=}" ;; esac ;; wol_mac=*) wol_mac="${arg#*=}" ;; wol_wait=*) wol_wait="${arg#*=}" ;; textmode) INIT_UNIT="--unit=textmode.target" ;; installer) INIT_UNIT="--unit=installer.target" SYSLINUX_DEFAULT="installer" ;; debugging) DEBUG=yes ;; nopkmute) MUTE_PRINTK=no ;; progress) PROGRESS=yes INIT_ARGS="${INIT_ARGS} --show-status=1" ;; nofsck) RUN_FSCK=no ;; toram) SYSTEM_TORAM=yes ;; live) LIVE=yes SYSLINUX_DEFAULT="live" ;; portable) SYSLINUX_DEFAULT="run" ;; grub_live) LIVE=yes GRUB_DEFAULT="Live" ;; grub_portable) GRUB_DEFAULT="Run" ;; overlay) OVERLAY=yes ;; setfbres=*) SWITCH_FRAMEBUFFER="${arg#*=}" SWITCH_FRAMEBUFFER="${SWITCH_FRAMEBUFFER//,/ }" ;; break=*) BREAK="${arg#*=}" ;; bigfont=*) BIGFONT="${arg#*=}" ;; ip=*) KERNEL_IPCONFIG="yes" ;; esac done # hide kernel log messages on console if [ ! "${MUTE_PRINTK}" = "no" ]; then echo '1 4 1 7' > /proc/sys/kernel/printk fi if test "${DEBUG}" = "yes"; then exec 3>&1 else exec 3>/dev/null fi SILENT_OUT=3 # If the network is up (due to the use of the "ip" kernel parameter) and a DNS # server is known, allow the libc resolver to use it grep '^\(nameserver\|domain\) ' /proc/net/pnp 2>/dev/null | grep -v '^nameserver 0\.0\.0\.0$' > /etc/resolv.conf if [ "${boot%%=*}" = "FILE" ]; then error "check arguments" "boot argument can't be FILE type..." fi debug_msg "Unique identifier for this client: ${MACHINE_UID:-NOT AVAILABLE}" # main boot sequence for BOOT_STEP in \ load_modules \ check_disks \ mount_flash \ set_consolefont \ cleanup_flash \ update_bootmenu \ mount_sysroot \ mount_storage \ load_splash \ check_update \ prepare_sysroot \ check_amlogic_dtb; do ${BOOT_STEP} [ -n "${DEBUG}" ] && break_after ${BOOT_STEP} done BOOT_STEP=final # log if booting from usb / removable storage STORAGE=$(cat /proc/mounts | grep " /sysroot/storage " 2>/dev/null | awk '{print $1}' | awk -F '/' '{print $3}') FLASH=$(cat /proc/mounts | grep " /sysroot/flash " 2>/dev/null | awk '{print $1}' | awk -F '/' '{print $3}') for i in ${STORAGE} ${FLASH} ; do if [ -n "$i" ]; then removable="/sys/class/block/*/$i/../removable" if [ -e $removable ]; then if [ "$(cat $removable 2>/dev/null)" = "1" ]; then echo "### BIG FAT WARNING" > /dev/kmsg echo "### $i is removable. suspend/resume may not work" > /dev/kmsg fi fi fi done # move some special filesystems /usr/bin/busybox mount --move /dev /sysroot/dev /usr/bin/busybox mount --move /proc /sysroot/proc /usr/bin/busybox mount --move /sys /sysroot/sys /usr/bin/busybox rm -fr /tmp # tell OE settings addon to disable updates if [ "${UPDATE_DISABLED}" = "yes" ]; then echo "" > /sysroot/dev/.update_disabled fi if [ "${FLASH_NETBOOT}" = "yes" ]; then echo "" > /sysroot/dev/.flash_netboot fi if [ "${KERNEL_IPCONFIG}" = "yes" ]; then echo "" > /sysroot/dev/.kernel_ipconfig fi # swap can not be used over nfs.(see scripts/mount-swap) if [ "${STORAGE_NETBOOT}" = "yes" ]; then echo "" > /sysroot/dev/.storage_netboot fi BACKUP_FILE=$(ls -1 /sysroot/storage/.restore/??????????????.tar 2>/dev/null | head -n 1) if [ -f /sysroot/storage/.please_resize_me ]; then INIT_UNIT="--unit=fs-resize.target" elif [ -f /sysroot/storage/.cache/reset_oe -o -f /sysroot/storage/.cache/reset_xbmc ]; then INIT_UNIT="--unit=factory-reset.target" elif [ -f "${BACKUP_FILE}" ]; then INIT_UNIT="--unit=backup-restore.target" elif [ -f /sysroot/storage/.rpi_flash_firmware ]; then INIT_UNIT="--unit=rpi-flash-firmware.target" fi # stop output redirection [ -n "${TEE_PID}" ] && kill ${TEE_PID} &>/dev/null if [ -s /sysroot/dev/init.log ]; then mv /sysroot/dev/init.log /sysroot/storage/init.log else rm -f /sysroot/dev/init.log rm -f /sysroot/storage/init.log fi # switch to new sysroot and start real init exec /usr/bin/busybox switch_root /sysroot /usr/lib/systemd/systemd ${INIT_ARGS} ${INIT_UNIT} error "switch_root" "Error in initramfs. Could not switch to new root"