#! /bin/sh # vim: set tabstop=4 syntax=sh : # SPDX-License-Identifier: GPL-2.0-or-later WITH exception (do not use in other places) for functions imported from YourFritz shell framework yf_bootmanager_version="0.8.6" yf_bootmanager_timestamp=202301141521 ####################################################################################################### # # # extension for the 'restart' page of AVM's FRITZ!OS GUI to provide a selection between the two # # installed systems on devices with 'dual boot' support # # # ###################################################################################################VER# # # # bootmanager, version 0.8.6 # # # # This script is a part of the YourFritz project from https://github.com/PeterPawn/YourFritz. # # # ###################################################################################################CPY# # # # Copyright (C) 2014-2023 P.Haemmerlein (peterpawn@yourfritz.de) # # # ###################################################################################################LIC# # # # This project 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 2 of the # # License, or (at your option) any later version. # # # # This program 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 under http://www.gnu.org/licenses/gpl-2.0.html for more details. # # # ####################################################################################################### # # # "FRITZ!Box" and "FRITZ!" are registered word marks and "AVM" is a registered word and figurative # # mark of: # # # # AVM Computersysteme Vertriebs GmbH, 10559, Berlin, DE. # # # ####################################################################################################### # # # This script implements all actions necessary to switch between the two installed system versions on # # AVM router devices with 'dual-boot' capabilities and - optionally - change the 'branding' value, if # # supported by device and installed firmware. # # # # It should work now with more chipsets used by AVM nowadays. # # # ####################################################################################################### # # # With update to 0.8.3, support for more devices has been added and AVM's models using the FIT format # # for firmware images will be handled now correctly - assuming, that the 'bootslotctl' tool by AVM is # # available for these models. Currently there's no support to detect the inactive system and its # # version and/or modification date/source - there's no code included to dissect an existing FIT image # # from either FIT partition. # # # # This version is able to detect various manners to overwrite the used 'branding' value, even if the # # device doesn't support permanent changes to the original value from EVA's configuration. # # If a supported method was detected ('YF_CHANGE_OEM' and 'yf_custom_environment' are available and # # a fixed 'export OEM=' will be detected, too) and it offers a chance to change the branding, # # the feature gets activated ... otherwise no changes to the branding value are supported. # # # ####################################################################################################### # # # There are seven operations supported as first argument (at least one parameter is always required): # # # # is_supported - returns true (exit code 0) or false (exit code 1), if the device doesn't support the # # dual-boot selection # # is_blocked - check the used semaphore file, if there's already a running instance # # switch_to - switches to the 'running' or 'alternative' system (2nd parameter) and (optionally) # # sets the OEM name to the 3rd parameter (which isn't checked any further) # # get_values - provide a list of relevant values for implementers of other interfaces, without the # # needs to implement the full logic once again # # - if the second parameter is 'nocache', an existing cache file (where those data is # # stored otherwise - ready for output on next call) gets ignored # # debug - this operation is intended only for diagnose, it shows some settings and their # # current values on STDOUT to provide information about the current system # # show_eva_cfg - show content of EVA configuration area (for debug purposes) # # clear_cache - remove saved data from an earlier 'get_values' call without 'nocache' argument; if # # the second argument is 'with_eva_config', any results from an earlier check of EVA's # # configuration area are removed, too, what leads to a delay on next call which builds # # new data # # # ####################################################################################################### # # # The script tries to detect the framework (if any), which was used to modify the original firmware. # # To find this, it checks for the YourFritz version file, the Freetz version file and the "modfs" # # version file in this order and the first file found is assumed to be a mark of the used framework. # # # ####################################################################################################### # # # To test implementation for various aspects (EVA configuration, FIT image handling, loop device # # driver download), some environment values (BM_DEBUG_{FIT|EVA|LOOP}=1) are supported. With other # # values (see script code below) it's possible to use specified image files instead of data read from # # current device. Those settings are for TEST PURPOSES only - don't use them, if you don't understand # # their meanings and their impact on flow of execution. # # # ####################################################################################################### # # # constants # # # ####################################################################################################### # # # common values # # # ####################################################################################################### readonly messages_file="${0}.msg" readonly tmpfs="/var" readonly procfs="/proc" readonly devtmpfs="/dev" readonly sysfs="/sys" readonly tmp="$tmpfs/tmp" readonly null="$devtmpfs/null" readonly zeros="$devtmpfs/zero" readonly timer="$procfs/timer_list" readonly data_cache_file="$tmp/bootmanager.data" readonly boot_cache_file="$tmp/bootmanager.boot" readonly device_cache_file="$tmp/bootmanager.dev" readonly inactive_fit_cache_file="$tmp/bootmanager.inactive.fit" readonly urlader_environment="$procfs/sys/urlader/environment" readonly system_select_name="linux_fs_start" readonly active_name="active" readonly inactive_name="inactive" readonly branding_name="firmware_version" readonly kernel_args="kernel_args" readonly kernel_args1="kernel_args1" readonly flash_size="flashsize" readonly proc_mtd="$procfs/mtd" readonly proc_devices="$procfs/devices" readonly tffs_device_name="tffs" # shellcheck disable=SC2034 readonly mtd_prefix="mtd" # shellcheck disable=SC2034 readonly mtd_mask="${mtd_prefix}%u" readonly mtdblock_prefix="$devtmpfs/mtdblock" readonly mtdblock_mask="${mtdblock_prefix}%u" readonly emmcblock_prefix="$devtmpfs/mmcblk0" # shellcheck disable=SC2034 readonly emmcblock_mask="${emmcblock_prefix}p%u" readonly proc_cpuinfo="$procfs/cpuinfo" readonly proc_mountinfo="$procfs/self/mountinfo" readonly urlader_name="urlader" readonly kernel_name="kernel" readonly fit_partition_name="fit" readonly fit_partition_name_mask="$fit_partition_name%u" readonly fit_mtd_name_mask="fit%u" readonly bootslot_tool="/sbin/bootslotctl" readonly filesystem_name="filesystem" readonly reserved_prefix="reserved" readonly alt_root_mount_name="alt_root" readonly alt_root_image_name="alt_root_image" readonly defaults_dir="etc/default.%s" readonly sysfs_block_dir="$sysfs/block" readonly loop_driver_name="loop" readonly backing_file_name="backing_file" readonly disk_by_label="$devtmpfs/disk/by-partlabel" readonly tmpdir="${TMPDIR:-$tmp}" readonly avm_rcconf="etc/init.d/rc.conf" readonly nas_base="$tmpfs/media/ftp" readonly semafile="$tmpfs/run/bootmanager.sem" readonly fdt_base="$procfs/device-tree" readonly fdt_chosen="$fdt_base/chosen" readonly fdt_model="model" readonly fdt_compatible="compatible" readonly yf_change_oem_marker="# YF_CHANGE_OEM" readonly yf_change_oem_script="etc/init.d/yf_change_oem.sh" readonly yf_custom_environment_script="yf_custom_environment\\.sh" readonly yf_custom_environment_locations="\$path/etc/boot.d \$path/etc/init.d \$path/etc/inittab \$wrapper/etc/inittab" readonly yf_signature_key_modulus="00b4074ef52e5c44f3a1eca1bd8eb3b5f217b82eaa23641adf50ca5bb98f3538ac706a5bb5f15def942bc352f3cd1cdf4ba1f47bc66863d95ecc7576d4f6d6729d396056e8fdf5d2ff5998ff33559104e07934e0c944d764bb881708ed73c0df0b948a9659de65c9940a9f537fcd1450bcdffe5a76f6b8d298ef89c3ce895f9361" readonly yf_signature_key_exponent="010001" readonly yf_loop_repository_url="https://raw.githubusercontent.com/PeterPawn/yf_bin/yf_bm_loop_driver/loop-drivers/YourFritz-loop-driver-archive.image" readonly uname="uname" readonly avm_downloader="/usr/bin/tr069fwupdate" readonly avm_pubkey_sources="/etc/avm_firmware_public_key[1-9] /etc/plugin_global_key.pem" ####################################################################################################### # # # various sources for version numbers # # # ####################################################################################################### readonly yourfritz_version_file="etc/.yourfritz_version" readonly freetz_version_file="etc/.freetz-version" readonly freetz_ng_version_file="etc/.freetz-ng-version" readonly modfs_version_file="etc/.modfs_version" readonly avm_version_file_old="etc/version" readonly avm_version_file_old_prefix="FIRMWARE_" readonly avm_version_file="etc/init.d/rc.conf" readonly avm_version_file_prefix="CONFIG_" readonly avm_version_main_components="VERSION SUBVERSION BUILDNUMBER" readonly avm_version_major_name="CONFIG_VERSION_MAJOR" readonly avm_version_date="DATE" readonly avm_product_name="CONFIG_PRODUKT" readonly version_files="$yourfritz_version_file $freetz_ng_version_file $freetz_version_file $modfs_version_file" readonly modified_by="YourFritz Freetz-NG Freetz modfs -" ####################################################################################################### # # # GRX5, VR9 and IPQ (only 'raw flash' based devices) common settings # # # ####################################################################################################### # shellcheck disable=SC2034 readonly mtd_filesystem_name_active="${filesystem_name}" # shellcheck disable=SC2034 readonly mtd_filesystem_name_inactive="${reserved_prefix}-${filesystem_name}" # shellcheck disable=SC2034 readonly mtd_kernel_name_active="${kernel_name}" # shellcheck disable=SC2034 readonly mtd_kernel_name_inactive="${reserved_prefix}-${kernel_name}" ####################################################################################################### # # # MIPS specific 'cpuinfo' mask # # # ####################################################################################################### readonly mips_cpuinfo_check='s|^system type[ \t]*: \([^ \t]*\).*$|\1|p' ####################################################################################################### # # # IPQ4019/IPQ501x/IPQ807x specific settings # # # ####################################################################################################### readonly ipq_cpuinfo_check="s|^model name[ \t]*: ARMv7 Processor rev [45] (v7l)\$|IPQ|p;tq;b;:q q" readonly ipq8074_eva_config_name="0:CONFIG" readonly emmc_mask="emmc_size" readonly bcm963_mask="BCM963" readonly nvram_name="nvram" ####################################################################################################### # # # VR9 specific settings # # # ####################################################################################################### readonly wrapper_mount_name="wrapper" readonly vr9_wrapper_dir="/wrapper" readonly vr9_rootfs_name="/filesystem_core.squashfs" readonly vr9_image_source_active="${vr9_wrapper_dir}${vr9_rootfs_name}" readonly vr9_image_source_inactive="mount:%s:${vr9_rootfs_name}" ####################################################################################################### # # # Puma6 / Puma7 specific settings # # # ####################################################################################################### readonly puma_helper="/etc/puma6_helper.sh" readonly puma_partitions="/proc/avm_partitions" # shellcheck disable=SC2034 readonly puma_filesystem_name_active="${filesystem_name}_ATOM" # shellcheck disable=SC2034 readonly puma_filesystem_name_inactive="${filesystem_name}_${reserved_prefix}_ATOM" # shellcheck disable=SC2034 readonly puma_kernel_name_active="${kernel_name}_ATOM" # shellcheck disable=SC2034 readonly puma_kernel_name_inactive="${kernel_name}_${reserved_prefix}_ATOM" ####################################################################################################### # # # sub-functions # # # ####################################################################################################### # # # imported from framework - it's not allowed to copy the functions below (up to the next comment box) # # to other works # # # ####################################################################################################### __yf_ansi_sgr() { printf -- '\033[%sm' "$1"; } __yf_ansi_bold__="$(__yf_ansi_sgr 1)" __yf_ansi_yellow__="$(__yf_ansi_sgr 33)" __yf_ansi_bright_red__="$(__yf_ansi_sgr 91)" __yf_ansi_bright_green__="$(__yf_ansi_sgr 92)" __yf_ansi_reset__="$(__yf_ansi_sgr 0)" __yf_get_script_lines() { sed -n -e "/^#*${1}#\$/,/^#\{20\}.*#\$/p" -- "$0" | \ sed -e '1d;$d' | \ sed -e 's|# \(.*\) *#$|\1|' | \ sed -e 's|^#*#$|--|p' | \ sed -e '$d' | \ sed -e 's| *$||' } __yf_show_script_name() { [ -n "$1" ] && printf -- '%s' "$1" printf -- '%s' "${0#*/}" [ -n "$1" ] && printf -- "%s" "${__yf_ansi_reset__}" } __yf_show_license() { __yf_get_script_lines 'LIC'; } __yf_show_version() { printf -- "\n%s%s%s, " "${__yf_ansi_bold__}" "$(__yf_get_script_lines 'VER' | sed -n -e "2s|^\([^,]*\),.*|\1|p")" "${__yf_ansi_reset__}" v_display="$(__yf_get_script_lines 'VER' | sed -n -e "2s|^[^,]*, \(.*\)|\1|p")" printf -- "%s\n" "$v_display" } __yf_show_copyright() { __yf_get_script_lines 'CPY'; } boot_manager_usage() { __yf_show_version __yf_show_copyright __yf_show_license printf -- "\n" printf -- "%sbootmanager%s - collect data and set values for yf_bootmanager project\n\n" "${__yf_ansi_bold__}" "${__yf_ansi_reset__}" printf -- "See https://github.com/PeterPawn/YourFritz/tree/main/bootmanager for further info.\n\n" printf -- "Usage: %s \n\n" "$0" printf -- " is one of:\n\n" printf -- "is_supported\n" printf -- "is_blocked\n" printf -- "switch_to { running | alternative } [ branding ]\n" printf -- "get_values [ no_cache ]\n" printf -- "clear_cache [ with_eva_config ]\n" printf -- "debug [ verbose ]\n" printf -- "show_eva_cfg\n\n" printf -- "Please check the 'header' of this script file for further info regarding those commands or.\n" printf -- "the URL mentioned above.\n" printf -- "\n" } ####################################################################################################### # # # end of functions imported from YF framework # # # ####################################################################################################### # # # (safe) detection of Puma6 and Puma7 devices, only ATOM core supported - runs in a sub-shell # # # ####################################################################################################### is_puma_based_device() ( [ -f "$puma_helper" ] || exit 1 # shellcheck disable=SC1090 . "$puma_helper" if type is_puma7 2>"$null" 1>&2; then [ "$IS_ATOM" = "y" ] || exit 1 else is_puma_atom || exit 1 fi exit 0 ) ####################################################################################################### # # # provide a shellcheck compatible use of printf with variable mask values # # # ####################################################################################################### printf_ss() ( mask="$1" shift # shellcheck disable=SC2059 printf -- "$(printf -- '%s' "$mask")" "$@" ) ####################################################################################################### # # # handle timestamps and format time-span values according to precision # # # ####################################################################################################### # shellcheck disable=SC2015 timestamp() { [ -f "$timer" ] && [ "$shell_32bit_arithmetics" = "0" ] && sed -n -e "s|^now at \([0-9]*\).*|\1|p" "$timer" || date +%s; } duration() { [ -f "$timer" ] && [ "$shell_32bit_arithmetics" = "0" ] && printf -- "%u.%09u" "$(( ( $2 - $1 ) / 1000000000 ))" "$(( ( $2 - $1 ) % 1000000000 ))" || printf -- "%u" "$(( $2 - $1 ))"; } ####################################################################################################### # # # get major device ID for specified driver name # # # ####################################################################################################### get_major_device_id() ( sed -n -e "s|^[ ]\{0,2\}\([0-9]\{1,3\}\) $1|\1|p" "$proc_devices"; ) ####################################################################################################### # # # get TFFS major device ID from /proc/devices # # # ####################################################################################################### get_tffs_major() ( get_major_device_id "$tffs_device_name"; ) ####################################################################################################### # # # get a value from urlader environment # # # ####################################################################################################### get_environment() ( sed -n -e "s|^$1[ \t]\(.*\)\$|\1|p" "${2:-$urlader_environment}" 2>"$null"; ) ####################################################################################################### # # # set a new value on urlader environment and verify successful change # # # ####################################################################################################### set_environment_value() ( printf -- "%s\t%s\n" "$1" "$2" >"$urlader_environment" grep -q "^$1[[:space]]$2\$" "$urlader_environment" 2>"$null" ) ####################################################################################################### # # # common helper functions for EVA configuration and FIT image functions # # # ####################################################################################################### hex2bin() { hex2bin_ro() { i=1; h=1; z=0 # shellcheck disable=SC2034 while read -r p _ o; do [ "$i" -lt "$p" ] && return 1 i=$(( i + 1 )) if [ "$o" -ge 60 ] && [ "$o" -le 67 ]; then c=$(( o - 60 )) elif [ "$o" -ge 70 ] && [ "$o" -le 71 ]; then c=$(( o - 62 )) elif [ "$o" -ge 101 ] && [ "$o" -le 106 ]; then c=$(( o - 91 )) elif [ "$o" -ge 141 ] && [ "$o" -le 146 ]; then c=$(( o - 131 )) else return 1 fi if [ $h -eq 0 ]; then v=$(( v + c )) if [ $v -eq 0 ]; then z=$(( z + 1 )) else if [ $z -gt 0 ]; then dd if="$zeros" bs=$z count=1 2>"$null" z=0 fi printf -- "%b" "\0$(( v >> 6 ))$(( ( v >> 3 ) & 7 ))$(( v & 7 ))" fi h=1 else v=$(( c * 16 )) h=0 fi done [ $z -gt 0 ] && dd if="$zeros" bs=$z count=1 2>"$null" return 0 } if [ -n "$1" ]; then printf -- "%s" "$1" | hex2bin return $? fi cmp -l -- "$zeros" - 2>"$null" | hex2bin_ro return $? } bin2dec_be() ( bin2dec_be_ro() { i=1; l=0; v=0; s=-8; ff=0 while read -r p _ rt; do if [ "$ff" -eq 1 ]; then v=$(( v * 256 )) ff=255 v=$(( v + ff )) i=$(( i + 1 )) ff=0 fi while [ "$i" -lt "$p" ]; do v=$(( v * 256 )) i=$(( i + 1 )) done if [ "$rt" = 377 ] && [ $ff -eq 0 ]; then ff=1 continue fi v=$(( v * 256 )) rt=$(( 0$rt )) v=$(( v + rt )) i=$(( p + 1 )) done printf -- "%d" $v } ( cat; printf -- "%b" "\377" ) | cmp -l -- "$zeros" - 2>"$null" | bin2dec_be_ro return 0 ) swap_byte_order() ( swap_byte_order_ro() { v1=0; v2=0; v3=0; v4=0 while read -r p _ rt; do [ "$p" -gt 5 ] && exit 1 [ "$p" -eq 5 ] && [ "$rt" -ne 0377 ] && exit 1 if [ "$p" -eq 5 ]; then for i in $v4 $v3 $v2 $v1; do printf -- "%b" "\0$(printf -- "%o" "$i")" done exit 0 fi eval "v$p"=$(( 0$rt )) done exit 1 } ( cat; printf -- "%b" "\377" ) | cmp -l -- "$zeros" - 2>"$null" | swap_byte_order_ro ) native_byte_order() ( if [ "$(dd if=/proc/self/exe bs=1 count=1 skip=5 2>"$null")" = "$(printf -- "\001")" ]; then swap_byte_order else cat - 2>"$null" fi ) native_byte_order_evacfg() { # EVA configuration on Puma devices uses BE byte order, don't swap for those models if [ "$is_puma_device" = "0" ] || [ -n "$BM_DEBUG_EVA_LE" ]; then native_byte_order else cat - 2>"$null" fi } get_data() ( dd if="$1" bs="$3" count=$(( ( $2 / $3 ) + 1 )) skip=1 2>"$null" | dd bs=1 count="$2" 2>"$null"; ) string_from_file() ( strlen() { s=1 while read -r p l _; do [ "$p" -ne "$s" ] && printf -- "%u\n" "$(( s - 1 ))" && return s=$(( s + 1 )) done printf -- "%u\n" "$(( s - 1 ))" } l="$(get_data "$1" 256 "$2" | cmp -l -- - "$zeros" 2>"$null" | strlen)" [ -n "$l" ] && get_data "$1" "$l" "$2" ) ####################################################################################################### # # # time-optimized copying of data with offset and known size (using 'dd') # # # ####################################################################################################### copy_optimized() ( ind() { i="$1"; while [ "$i" -gt 0 ]; do printf ">> "; i=$(( i - 1 )); done; } lvl=$(( ${5:-0} + 1 )) def_bsz=$(( 1024 * 1024 )) bsz=$(( ${4:-$def_bsz} )) [ $(( bsz & ( bsz - 1 ) )) -gt 0 ] && bsz=$def_bsz cnt=$(( $3 )) [ $cnt -le 0 ] && exit 0 off=$(( $2 )) [ $off -lt 0 ] && exit 1 if [ $cnt -lt $bsz ]; then if [ $(( off % bsz )) -ne 0 ]; then if [ $bsz -gt 1024 ]; then [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(1) copy_optimized %s %u %u %u\n" "$(ind "$lvl")" "$1" $off $(( cnt & 1023 )) 1024 1>&2 copy_optimized "$1" $off $(( cnt & 1023 )) 1024 $lvl shft=$(( 1024 - ( off % 1024 ) )) cnt=$(( cnt - shft )) off=$(( off + shft )) [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(2) copy_optimized %s %u %u %u\n" "$(ind "$lvl")" "$1" $off $cnt 1024 1>&2 copy_optimized "$1" $off $cnt 1024 $lvl exit 0 fi of=$(( off )) s=1 while [ $(( of & 1 )) -eq 0 ]; do s=$(( s * 2 )); of=$(( of / 2 )); done skp=$(( off / s )) cnt=$(( cnt / s )) r=0 else s=1 c=$(( cnt & ~1023 )) while [ $(( c % 2 )) -eq 0 ] && [ $c -gt 0 ]; do s=$(( s * 2 )); c=$(( c / 2 )); done skp=$(( off / s )) [ $s -eq 1 ] && r=0 || r=$(( cnt & 1023 )) cnt=$(( cnt / s )) rb=1024 fi [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(1) dd if=%s bs=%s skip=%s count=%s\n" "$(ind "$lvl")" "$1" "$s" "$skp" "$cnt" 1>&2 if dd if="$1" bs="$s" skip="$skp" count="$cnt" 2>"$null"; then if [ $r -ne 0 ]; then [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(3) copy_optimized %s %u %u %u\n" "$(ind "$lvl")" "$1" $(( ( skp + cnt ) * s )) $r $rb 1>&2 copy_optimized "$1" $(( ( skp + cnt ) * s )) $r $rb $lvl fi exit 0 else exit 1 fi else if [ $(( off % bsz )) -ne 0 ]; then [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(4) copy_optimized %s %u %u %u\n" "$(ind "$lvl")" "$1" $off $(( bsz - ( off % bsz ) )) $bsz 1>&2 copy_optimized "$1" $off $(( bsz - ( off % bsz ) )) $bsz $lvl shft=$(( bsz - ( off % bsz ) )) off=$(( off + shft )) cnt=$(( cnt - shft )) [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(5) copy_optimized %s %u %u %u\n" "$(ind "$lvl")" "$1" $off $cnt $bsz 1>&2 copy_optimized "$1" $off $cnt $bsz $lvl else s=$(( off / bsz )) c=$(( cnt / bsz )) [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(2) dd if=%s bs=%s skip=%s count=%s\n" "$(ind "$lvl")" "$1" "$bsz" "$s" "$c" 1>&2 if dd if="$1" bs="$bsz" skip="$s" count="$c" 2>"$null"; then if [ $cnt -gt $(( c * bsz )) ]; then shft=$(( c * bsz )) off=$(( off + shft )) cnt=$(( cnt - shft )) [ -n "$BM_DEBUG_FIT" ] && printf -- "%s(6) copy_optimized %s %u %u %u\n" "$(ind "$lvl")" "$1" $off $cnt $bsz 1>&2 copy_optimized "$1" $off $cnt $bsz $lvl fi exit 0 else exit 1 fi fi fi ) ####################################################################################################### # # # find offset of rootfs image in the specified source with AVM's FIT image format # # # ####################################################################################################### find_rootfs_in_fit_image() ( fdt32_align() { [ $(( $1 % 4 )) -gt 0 ] && printf -- "%u\n" $(( ( $1 + fdt32_size ) & ~3 )) || printf -- "%u\n" "$1"; } get_fdt32_be() ( get_data "$1" 4 "$2" | bin2dec_be; ) get_fdt32_le() ( get_data "$1" 4 "$2" | swap_byte_order | bin2dec_be; ) get_string() { n="$(printf -- "__fdt_string_%u" "$2")" f="$(set | sed -n -e "s|^\($n=\).*|\1|p")" if [ -z "$f" ]; then v="$(string_from_file "$1" "$2")" printf -- "%s=\"%s\"\n%s=\"\$%s\"\n" "$n" "$v" "$3" "$n" else printf -- "%s=\"\$%s\"\n" "$3" "$n" fi } is_printable_string() ( ro() { i=0 while read -r p l _; do i=$(( i + 1 )) [ "$p" -gt "$i" ] && [ "$i" -eq 1 ] && return 1 [ "$l" -lt 040 ] && return 1 [ "$l" -gt 0176 ] && return 1 done [ "$i" -eq 0 ] && [ "$1" -eq 1 ] && return 1; [ "$i" -lt $(( $1 - 1 )) ] && return 1 return 0 } get_data "$1" "$3" "$2" | cmp -l -- - "$zeros" 2>"$null" | ro "$3" return $? ) entry() ( img="$1" offset="$2" parent="$3" level="$4" filesystem_found=0 ramdisk_found=0 data=$(get_fdt32_be "$img" "$offset") # shellcheck disable=SC2050 while [ 1 -eq 1 ]; do case "$data" in ("$fdt_begin_node") name_off="$(( offset + fdt32_size ))" eval "$(get_string "$img" $name_off "name")" [ -z "$name" ] && name="/" offset=$(fdt32_align $(( offset + fdt32_size + ${#name} + 1 )) ) eval "$(entry "$img" "$offset" "$name" "$(( level + 1 ))" 5>&1)" offset="$next_offset" ;; ("$fdt_end_node") offset=$(( offset + fdt32_size )) { [ -n "$fs_node_name" ] && printf -- "fs_node_name=\"%s\" fs_offset=%u fs_size=%u " "$fs_node_name" "$fs_offset" "$fs_size" # shellcheck disable=SC2154 [ -n "$rd_node_name" ] && printf -- "rd_node_name=\"%s\" rd_offset=%u rd_size=%u " "$rd_node_name" "$rd_offset" "$rd_size" printf -- "next_offset=%u" "$offset" } 1>&5 if [ "$filesystem_found" = "1" ] && [ "$type_found" = "$filesystem_type" ] && [ -n "$data_found" ]; then printf -- " fs_size=%u fs_offset=%u fs_node_name=\"%s\"" "$fs_size" "$fs_offset" "$parent" 1>&5 fi if [ "$ramdisk_found" = "1" ]; then if [ "$fs_size" -eq 0 ] && [ "$rd_size" -lt "$new_rd_size" ]; then printf -- " rd_size=%u rd_offset=%u rd_node_name=\"%s\"" "$new_rd_size" "$new_rd_offset" "$parent" 1>&5 fi fi exit 0 ;; ("$fdt_prop") value_size="$(get_fdt32_be "$img" $(( offset + fdt32_size )))" name_off="$(( fdt_start + fdt_off_dt_strings + $(get_fdt32_be "$img" $(( offset + ( 2 * fdt32_size ) )) ) ))" eval "$(get_string "$img" $name_off "name")" data_offset=$(( offset + 3 * fdt32_size )) if [ "$value_size" -gt 512 ]; then [ "$name" = "$data_name" ] && file_size="$value_size" && file_offset="$data_offset" elif is_printable_string "$img" "$data_offset" "$value_size"; then eval "$(get_string "$img" $(( offset + 3 * fdt32_size )) "str")" if [ "$name" = "$filesystem_indicator" ]; then # shellcheck disable=SC2154 if [ -n "$(expr "$str" : ".*\($filesystem_indicator_marker\).*")" ]; then filesystem_found=1 fs_size="$file_size" fs_offset="$file_offset" fs_node_name="$parent" fi elif [ "$name" = "$type_name" ]; then type_found="$str" if [ "$type_found" = "$ramdisk_type" ]; then ramdisk_found=1 new_rd_size="$file_size" new_rd_offset="$file_offset" fi fi fi offset=$(fdt32_align $(( data_offset + value_size )) ) ;; ("$fdt_nop") offset=$(( offset + fdt32_size )) ;; ("$fdt_end") offset=$(( offset + fdt32_size )) break ;; esac data=$(get_fdt32_be "$img" "$offset") done { [ -n "$fs_node_name" ] && printf -- "fs_node_name=\"%s\" fs_offset=%u fs_size=%u " "$fs_node_name" "$fs_offset" "$fs_size" [ -n "$rd_node_name" ] && printf -- "rd_node_name=\"%s\" rd_offset=%u rd_size=%u " "$rd_node_name" "$rd_offset" "$rd_size" printf -- "next_offset=%u\n" "$offset" } 1>&5 ) # shellcheck disable=SC2015 dev_info() ( [ -z "$2" ] && udevadm info -q all -n "$1" || { udevadm info -q all -n "$1" | sed -n -e "s|^E: $2=\(.*\)|\1|p"; } ) mtd_type() ( [ "$(dev_info "$1" DEVTYPE)" = "mtd" ] && cat "/sys$(dev_info "$1" DEVPATH)/type" && exit 0 || exit 1; ) nand_pagesize() ( [ "$(dev_info "$1" DEVTYPE)" = "mtd" ] && [ "$(mtd_type "$1")" = "nand" ] && cat "/sys$(dev_info "$1" DEVPATH)/subpagesize" && exit 0 || exit 1; ) get_real_name() ( if command -v realpath 2>"$null" 1>&2; then realpath "$1" 2>"$null" elif command -v readlink 2>"$null" 1>&2; then readlink -f "$1" 2>"$null" else p="$1" l="$(ls -dl "$p")" t="${l#*" $p -> "}" d="$(pwd)" # shellcheck disable=SC2164 cd "${p%/*}" 2>"$null" # shellcheck disable=SC2164 cd -P "${t%/*}" 2>"$null" r="$(pwd)/${t##*/}" # shellcheck disable=SC2164 cd "$d" 2>"$null" printf -- "%s\n" "$r" fi ) copy_image() ( ! command -v udevadm 2>"$null" 1>&2 && printf -- "Missing 'udevadm' utility.\a\n" 1>&2 && exit 1 [ -f "$1" ] && type="file" || type="$(dev_info "$1" DEVTYPE)" [ "$type" = "mtd" ] && type="$(mtd_type "$1")" case "$type" in ("file"|"partition"|"nor") dd if="$1" of="$2" bs="$3" count=1 2>"$null" ;; ("nand") ! command -v nanddump 2>"$null" 1>&2 && printf -- "Missing 'nanddump' utility.\a\n" 1>&2 && exit 1 pagesize="$(nand_pagesize "$1")" "$(command -v nanddump 2>"$null")" --bb skipbad "$1" 2>"$null" | dd of="$2" bs="$pagesize" count="$(( $3 / pagesize + 1 ))" 2>"$null" && dd if="$null" of="$2" bs="$3" seek=1 2>"$null" ;; ("disk") mtd="$(get_real_name "/sys$(dev_info "$1" DEVPATH)/device")" mtdname="${mtd##*/}" copy_image "$devtmpfs/$mtdname" "$2" "$3" ;; (*) printf -- "Unable to detect device type of FIT image source (%s) or this type (%s) is unsupported.\a\n" "$1" "$([ -z "$type" ] && printf -- "(unknown)" || printf "%s\n" "$type")" 1>&2 exit 1 ;; esac ) filesystem_indicator="avm,kernel-args" filesystem_indicator_marker="mtdparts_ext=" data_name="data" type_name="type" filesystem_type="filesystem" ramdisk_type="ramdisk" fdt_begin_node=1 fdt_end_node=2 fdt_prop=3 fdt_nop=4 fdt_end=9 fdt32_size=4 fs_copy="$2" next_offset=0 if [ -n "$BM_DEBUG_FIT_IMAGE" ]; then img="$BM_DEBUG_FIT_IMAGE" else img="$1" fi magic="$(dd if="$img" bs=4 count=1 2>"$null" | bin2dec_be)" [ "$magic" -lt 0 ] && magic=$(( magic & 0xffffffff )) if ! [ "$magic" = "218164734" ]; then printf -- "Invalid magic value (0x%08x) found at offset 0x%02x.\a\n" "$magic" "0" 1>&2 exit 1 fi [ "$2" = "check" ] && exit 0 next_offset=$(( next_offset + fdt32_size )) payload_size="$(get_fdt32_le "$img" "$next_offset")" td="$tempdir" [ -z "$td" ] && td="/tmp" tmpimg="$td/fit-image-$$" trap '[ -f "$tmpimg" ] && rm -f "$tmpimg" 2>/dev/null' EXIT copy_image "$img" "$tmpimg" "$(( payload_size + 64 + 8 + 8 ))" || exit 1 img="$tmpimg" next_offset=$(( next_offset + fdt32_size + 64 )) fdt_magic="$(get_fdt32_be "$img" "$next_offset")" if { [ "$fdt_magic" -gt 0 ] && [ "$fdt_magic" -ne 3490578157 ]; } || { [ "$fdt_magic" -lt 0 ] && [ "$fdt_magic" -ne -804389139 ]; }; then printf "Invalid FDT magic found.\a\n" 1>&2 && exit 1 fi fdt_start=$next_offset next_offset=$(( next_offset + 2 * fdt32_size )) fdt_off_dt_struct="$(get_fdt32_be "$img" "$next_offset")" next_offset=$(( next_offset + fdt32_size )) fdt_off_dt_strings="$(get_fdt32_be "$img" "$next_offset")" fs_size=0 fs_offset=0 rd_size=0 fs_offset=0 fs_node_name="" rd_node_name="" next_offset=$(( fdt_start + fdt_off_dt_struct )) eval "$(entry "$img" "$next_offset" "-" 0 5>&1)" if [ "$fs_size" -gt 0 ]; then printf -- "rootfs_type=squashfs rootfs_offset=%u rootfs_size=%u\n" "$fs_offset" "$fs_size" [ -n "$fs_copy" ] && copy_optimized "$img" "$fs_offset" "$fs_size" >"$tempdir/$fs_copy" elif [ "$rd_size" -gt 0 ]; then printf -- "rootfs_type=ramdísk rootfs_offset=%u rootfs_size=%u\n" "$rd_offset" "$rd_size" [ -n "$fs_copy" ] && copy_optimized "$img" "$rd_offset" "$rd_size" >"$tempdir/$fs_copy" else printf -- "No rootfs candicates found.\a\n" 1>&2 && exit 1 fi ) ####################################################################################################### # # # check urlader configuration of host device # # # ####################################################################################################### check_urlader_configuration() ( msg() { [ "$verbose" -eq 1 ] || return; printf_ss "$@" 1>&2; } append() { cat - >>"$ut/config_header"; } # shellcheck disable=SC2015 env() { [ -n "$BM_DEBUG_EVA_ENV" ] && get_environment "$1" "$BM_DEBUG_EVA_ENV" || get_environment "$1"; } fsize() { wc -c <"$1" 2>"$null" | sed -n -e "s|^\([0-9]*\).*\$|\1|p"; } chkgrep() { [ "$(printf -- "\n\000ABCDEFGH\000\r\n12345678ABC\000\n" | grep -ao "ABC" 2>"$null")" = "$(printf -- "ABC\nABC\n")" ] && return 0 || return 1; } chkcfg() { _f="$1" v="$(dd if="$_f" bs=4 count=1 skip=$(( 0x8C / 4 )) 2>"$null" | native_byte_order_evacfg | bin2dec_be)" [ "$(( v & ( ( 1 << 16 ) - 1 ) ))" = "25609" ] || return 1 _kv=0 dd if="$zeros" of="$ut/8zeros" bs=8 count=1 2>"$null" # shellcheck disable=SC2050 while [ 1 -eq 1 ]; do dd if="$_f" bs=8 count=1 skip=$(( ( 0x98 / 8 ) + _kv )) 2>"$null" | cmp -s -- - "$ut/8zeros" 2>"$null" && break _kv=$(( _kv + 1 )) [ "$_kv" -gt 256 ] && return 1 done _dp=$(dd if="$_f" bs=4 count=1 skip=$(( 0x94 / 4 )) 2>"$null" | native_byte_order_evacfg | bin2dec_be) _do=$(( ( ( 0x98 / 8 ) + _kv + 1 ) * 8 )) _cb=$(( _dp - _do )) _e=0 while [ "$_e" -lt "$_kv" ]; do _vp=$(dd if="$_f" bs=4 count=1 skip=$(( ( 0x98 / 4 ) + ( _e * 2 ) )) 2>"$null" | native_byte_order_evacfg | bin2dec_be) _np=$(dd if="$_f" bs=4 count=1 skip=$(( ( 0x98 / 4 ) + ( _e * 2 ) + 1 )) 2>"$null" | native_byte_order_evacfg | bin2dec_be) [ "$_vp" -lt "$_cb" ] && return 1 [ "$_np" -lt "$_cb" ] && return 1 [ $(( _vp - _cb )) -gt 1023 ] && return 1 [ $(( _np - _cb )) -gt 1023 ] && return 1 _e=$(( _e + 1 )) done grep -q -ao "00:04:0E:FF:FF:01" "$_f" 2>"$null" 1>&2 && return 1 printf -- "%u\n" "$_kv" return 0 } geteva() ( if ! [ -d "$procfs" ]; then msg "'%s' missing.\n" "$procfs" exit 1 fi case "$system_type" in ("IPQ8074") cat "$disk_by_label/$ipq8074_eva_config_name" 2>"$null" ;; ("BCM963") evacfg="$(sed -n -e "s|^\(mtd[0-9]\?\):\([ \t]\+[0-9a-fA-F]*\)\{2\}[ \t]\?\"$nvram_name\"\$|\1|p" "$proc_mtd")" cat "$devtmpfs/$evacfg" 2>"$null" ;; (*) if ! [ -f "$proc_mtd" ]; then msg "'%s' missing.\n" "$proc_mtd" exit 1 fi urlader="$(sed -n -e "s|^\(mtd[0-9]\?\):\([ \t]\+[0-9a-fA-F]*\)\{2\}[ \t]\?\"$urlader_name\"\$|\1|p" "$proc_mtd")" if [ -z "$urlader" ]; then msg "'%s' partition not found.\n" "$urlader_name" exit 1 fi if ! [ -c "$devtmpfs/$urlader" ]; then msg "'%s' is not a character device.\n" "$devtmpfs/$urlader" exit 1 fi cat "$devtmpfs/$urlader" 2>"$null" ;; esac ) sblk() ( h=$(( $3 / 2 )) s=$(( $4 / h )) mo="$(dd if="$2" count=1 bs="$3" skip=$(( $4 / $3 )) 2>"$null" | grep -ao "$1" 2>"$null")" [ -z "$mo" ] && printf -- "(not found)" && exit 1 if ! dd if="$2" count=1 bs="$h" skip="$s" 2>"$null" | grep -q -ao "$1" 2>"$null"; then if [ "$(dd if="$2" count=1 bs="$h" skip=$(( s + 1 )) 2>"$null" | grep -ao "$1" 2>"$null")" = "$mo" ]; then lwr=1 else printf -- "%u\n" "$(( $4 + h - 16 ))" exit 0 fi else lwr=0 fi if [ "$h" -le 16 ]; then [ "$lwr" -eq 0 ] && printf -- "%u\n" "$(( s * h ))" || printf -- "%u\n" "$(( ( s + 1 ) * h ))" exit 0 fi sblk "$1" "$2" "$(( h ))" "$(( $4 + ( lwr * h ) ))" ) fndo() ( f="$1"; l="$2"; ch="$3" fo="$(sblk "$l" "$f" "$(fsize "$f")" 0)" [ "$fo" = "(not found)" ] && printf -- "%s" "$fo" && exit 1 s=$(( fo - ( fo % 4096 ) - 4096 )) [ "$s" -lt 0 ] && s=0 dd if="$f" of="$ut/try_here" bs=4096 count=2 skip=$(( s / 4096 )) 2>"$null" sz=$(( $(fsize "$ch") / 4 )) fnd=0 o=$(( fo - s )) while [ "$o" -ge 0 ]; do if dd if="$ut/try_here" bs=4 skip=$(( o / 4 )) count="$sz" 2>"$null" | cmp -s -- - "$ch" 2>"$null"; then fnd=1 break fi o=$(( o - 16 )) done if [ "$fnd" -eq 1 ]; then [ "$BM_DEBUG_EVA" = "1" ] && msg "Configuration area candidate found at offset: %u (%#x)\n" "$(( s + o ))" "$(( s + o ))" dd if="$f" of="$ut/check_this" bs=16 count=64 skip=$(( ( s + o ) / 16 )) 2>"$null" if chkcfg "$ut/check_this" 1>"$null"; then [ "$BM_DEBUG_EVA" = "1" ] && msg "Configuration area found at offset: %u (%#x)\n" "$(( s + o ))" "$(( s + o ))" printf -- "%u\n" "$(( s + o ))" exit 0 else [ "$BM_DEBUG_EVA" = "1" ] && msg "Configuration area candidate ignored at offset: %u (%#x)\n" "$(( s + o ))" "$(( s + o ))" fi fi dd if="$zeros" of="$f" bs=16 count=1 seek=$(( fo / 16 )) conv=notrunc 2>"$null" fndo "$f" "$l" "$ch" ) adjust_eva_config_size() ( f="$1" s="$(fsize "$1")" o="$s" b=1 while [ "$s" -ge 16 ]; do if [ $(( s & 1 )) -eq 1 ]; then dd if="$zeros" of="$1" bs="$b" seek=$(( o / b )) count=1 2>"$null" adjust_eva_config_size "$1" fi s=$(( s >> 1 )) b=$(( b << 1 )) done ) env_name="$1" trap 'rm -rf "$ut" 2>$null' EXIT if ! command -v grep 2>"$null" 1>&2; then msg "Missing command '%s'.\n" "grep" exit 1 fi if ! chkgrep; then msg "The found '%s' command behaves unexpected.\n" "grep" exit 1 fi ut="$tempdir/urlader" rm -rf "$ut" 2>"$null" mkdir -p "$ut" 2>"$null" if [ -n "$BM_DEBUG_EVA_IMAGE" ]; then cat "$BM_DEBUG_EVA_IMAGE" >"$ut/dump" else geteva >"$ut/dump" || exit 1 fi adjust_eva_config_size "$ut/dump" memsize="" # shellcheck disable=SC2034 mtd0="" maca="" wlan_key="" for value in memsize mtd0 maca wlan_key; do env_value="$(env "$value")" if [ -z "$env_value" ]; then msg "Missing '%s' value from urlader environment.\n" "$value" else [ -n "$BM_DEBUG_EVA" ] && msg "Read '%s' value from urlader environment: %s\n" "$value" "$env_value" fi eval $value="$env_value" done [ -z "$memsize" ] && exit 1 touch "$ut/config_header" 2>"$null" printf -- "\000\000\000\003" | native_byte_order_evacfg | append [ "$is_puma_device" = "1" ] && [ -z "$BM_DEBUG_EVA_CONFIG" ] && [ -z "$BM_DEBUG_EVA_ENV" ] && memsize=$(( memsize / 2 )) hex2bin "$(printf -- "%08x" "$memsize")" | native_byte_order_evacfg | append printf -- "\000\000\000\000" | append i=0 while [ $i -lt 16 ]; do mtd=$(printf -- "mtd%u" "$i") i=$(( i + 1 )) val="$(env "$mtd")" [ -n "$BM_DEBUG_EVA" ] && msg "Read '%s' value from urlader environment: %s\n" "$mtd" "$([ -z "$val" ] && printf -- "(empty)" || printf -- "%s" "$val")" if [ -z "$val" ]; then printf -- "\000\000\000\000\000\000\000\000" | append else start="$(expr "$val" : "\(0[xX][0-9a-fA-F]*\),.*")" end="$(expr "$val" : "[^,]*,\(0[xX][0-9a-fA-F]*\)")" size=$(( end - start )) hex2bin "$(printf -- "%08x" "$start")" | native_byte_order_evacfg | append hex2bin "$(printf -- "%08x" "$size")" | native_byte_order_evacfg | append fi done found=0 if [ -z "$BM_DEBUG_EVA_CONFIG" ]; then if [ -n "$wlan_key" ] && grep -q -ao "$wlan_key" "$ut/dump" 2>"$null" 1>&2; then co="$(fndo "$ut/dump" "$wlan_key" "$ut/config_header")" [ "$co" = "(not found)" ] && unset co || found=1 fi if [ "$found" -eq 0 ]; then [ -z "$maca" ] && msg "No usable anchor value found, processing aborted.\a\n" && exit 1 [ -n "$wlan_key" ] && msg "Unable to find value of WLAN key (%s) in dump file, try again with maca value (%s).\n" "$wlan_key" "$maca" if grep -q -ao "$maca" "$ut/dump" 2>"$null" 1>&2; then co="$(fndo "$ut/dump" "$maca" "$ut/config_header")" [ "$co" = "(not found)" ] && unset co if [ -z "$co" ]; then msg "Unable to locate configuration area in urlader dump file.\n" exit 1 fi found=1 fi fi [ $found -eq 0 ] && exit 1 dd if="$ut/dump" of="$ut/config" bs=1 count=1024 skip="$co" 2>"$null" rm -f "$ut/dump" "$ut/try_here" "$ut/check_this" "$ut/config_header" "$ut/8zeros" 2>"$null" [ -n "$BM_DEBUG_EVA" ] && ! [ "$co" = "(not found)" ] && msg "EVA configuration found at offset: %u (%#x)\n" "$co" "$co" else cp "$BM_DEBUG_EVA_CONFIG" "$ut/config" fi kvs=$(( 0x98 / 8 )) kvc=0 dd if="$zeros" of="$ut/8zeros" bs=8 count=1 2>"$null" # shellcheck disable=SC2050 while [ 1 -eq 1 ]; do dd if="$ut/config" bs=8 count=1 skip=$(( kvs )) 2>"$null" | cmp -s -- - "$ut/8zeros" 2>"$null" && break kvc=$(( kvc + 1 )) kvs=$(( kvs + 1 )) [ "$kvc" -gt 256 ] && exit 1 done defptr=$(dd if="$ut/config" bs=4 count=1 skip=$(( 0x94 / 4 )) 2>"$null" | native_byte_order_evacfg | bin2dec_be) defoff=$(( ( kvs + 1 ) * 8 )) cb=$(( defptr - defoff )) kvs=$(( 0x98 / 4 )) ent=0 dh=0 while [ "$ent" -lt "$kvc" ]; do kv="$(dd if="$ut/config" bs=4 count=1 skip=$(( kvs )) 2>"$null" | native_byte_order_evacfg | bin2dec_be)" kn="$(dd if="$ut/config" bs=4 count=1 skip=$(( kvs + 1 )) 2>"$null" | native_byte_order_evacfg | bin2dec_be)" nm="$(string_from_file "$ut/config" "$(( kn - cb ))")" val="$(string_from_file "$ut/config" "$(( kv - cb ))")" kvs=$(( kvs + 2 )) ent=$(( ent + 1 )) if [ -z "$env_name" ]; then if [ "$dh" -eq 0 ]; then printf -- ">>>> fixed values from EVA configuration <<<<\n" 1>&2 dh=1 fi printf -- "%s=%s\n" "$nm" "$val" 1>&2 elif [ "$nm" = "$env_name" ]; then msg "'%s' found in fixed values area.\n" "$env_name" printf -- "%s\n" "$val" exit 2 fi done coff="$defoff" dh=0 while ! [ "$(dd if="$ut/config" bs=1 count=1 skip="$coff" 2>"$null" | base64)" = "/w==" ]; do nm="$(string_from_file "$ut/config" "$coff")" coff=$(( coff + ${#nm} + 1 )) val="$(string_from_file "$ut/config" "$coff")" coff=$(( coff + ${#val} + 1 )) if [ -z "$env_name" ]; then if [ "$dh" -eq 0 ]; then printf -- ">>> default values from EVA configuration <<<\n" 1>&2 dh=1 fi printf -- "%s=%s\n" "$nm" "$val" 1>&2 elif [ "$nm" = "$env_name" ]; then msg "'%s' found in defaults values area.\n" "$env_name" printf -- "%s\n" "$val" exit 0 fi done exit 1 ) ####################################################################################################### # # # check for and get (if any) a fixed branding value from specified system root # # # # $1 - system root directory # # # ####################################################################################################### get_fixed_branding() ( path="${1:-/}${1:+/}$avm_rcconf" b="$(sed -n -e "s|^export OEM\(=\(['\"]\?\)\(.*\)\2\)\?\$|\3|p" "$path")" printf -- '%s\n' "$b" [ -z "$b" ] && exit 1 || exit 0 ) ####################################################################################################### # # # check, whether the preparations for 'YF_CHANGE_OEM' are found in specified system # # # # $1 - system root directory # # # ####################################################################################################### check_oem_kernel_cmdline() ( path="${1:-/}${1:+/}" grep -q "^$yf_change_oem_marker\$" "$path$avm_rcconf" 2>"$null" || exit 1 grep -q "$yf_change_oem_script" "$path$avm_rcconf" 2>"$null" || exit 1 exit 0 ) ####################################################################################################### # # # check, whether the preparations for 'yf_custom_environment' are found in specified system # # # # $1 - system root directory # # # ####################################################################################################### check_custom_environment() ( if [ "$system_type" = "VR9" ]; then if is_active_mountpoint "$tempdir/$wrapper_mount_name"; then # shellcheck disable=SC2034 wrapper="$tempdir/$wrapper_mount_name" altsys=1 else # shellcheck disable=SC2034 wrapper="$vr9_wrapper_dir" altsys=0 fi fi path="${1:-/}${1:+/}" path="${path%*/}" for dir in $(eval printf -- "%s\\\\n" "$yf_custom_environment_locations"); do if [ -d "$dir" ]; then recursive="-r" suffix="/*" else recursive="" suffix="" fi # shellcheck disable=SC2086 if grep -q $recursive -- "$yf_custom_environment_script" "$dir"$suffix 2>"$null"; then # script called from somewhere # shellcheck disable=SC2086,SC2013 for fn in $(grep -l $recursive -- "$yf_custom_environment_script" "$dir"$suffix 2>"$null" | sed -n -e 1p); do v="$(grep -- "$yf_custom_environment_script" "$fn" 2>"$null" | sed -n -e "s|.*[ \t]\([^ \t]*$yf_custom_environment_script\).*|\1|p")" [ "$system_type" = "VR9" ] && [ "$altsys" -eq 1 ] && v="$tempdir$v" [ -f "$v" ] && exit 0 done fi done exit 1 ) ####################################################################################################### # # # check, whether the host system supports branding changes # # # # checks in order of relevance: # # - check for a 'fixed branding' using 'export OEM=' from /etc/init.d/rc.conf # # - check for a 'semi-fixed branding' using 'oem=' from kernel command line (requires a marker # # in file /etc/init.d/rc.conf -> see script 'add_change_oem.sh' in 'bootmanager' folder of # # YourFritz repository) # # - check for existence of 'yf_custom_environment.sh' in any directory from current PATH value; if # # it's found, the presence of the used TFFS minor node is checked next (it has to contain at least # # one line) and if this condition is met, too, in a last step the '/etc/inittab' file is checked, # # whether a call to the shell script above exists here # # - as a last resort the configuration area from AVM's bootloader (EVA) will be located, dissected # # and checked, whether the 'firmware_version' setting is located in the table of 'immutable # # settings' # # - if the check above results in an 'immutable value' (in this case, any changes to the setting will # # be ignored/reset on next system boot), no changes to the current branding are supported # # # ####################################################################################################### change_branding_support() { get_fixed_branding "$1" >"$null" && printf -- "fixed_export" && exit 0 check_oem_kernel_cmdline "$1" && printf -- "kernel_cmdline" && exit 0 check_custom_environment "$1" && printf -- "custom_environment" && exit 0 if [ -n "$2" ] && { [ "$2" = "immutable" ] || [ "$2" = "changeable" ]; }; then printf -- "%s\n" "$2" [ "$2" = "changeable" ] && exit 0 || exit 1 else if [ -n "$device_branding_is_changeable" ]; then if [ "$device_branding_is_changeable" = "true" ]; then printf -- "changeable" exit 0 else printf -- "immutable" exit 1 fi else device_branding_is_changeable="false" printf -- "immutable" exit 1 fi fi } ####################################################################################################### # # # get branding from environment variables 'kernel_args' or 'kernel_args1' # # # # Caution: read values from urlader environment and NOT from current kernel command line # # # ####################################################################################################### get_oem_kernel_cmdline() ( kargs="$(get_environment "$kernel_args") $(get_environment "$kernel_args1")" for kv in $kargs; do if [ -n "$(expr "$kv" : "\([Oo][Ee][Mm]\)=.*")" ]; then printf -- "%s\n" "$kv" | sed -e "s|^\([^=]*\)=\(['\"]\?\)\(.*\)\2\$|\3|" exit 0 fi done exit 1 ) ####################################################################################################### # # # replace an existing 'oem=' entry from 'kernel_args' or 'kernel_args1' or append a new one, # # if it doesn't exist yet # # # ####################################################################################################### set_oem_kernel_cmdline() ( replaced=0 for var in $kernel_args1 $kernel_args; do line="" for kv in $(get_environment "$var"); do if [ -n "$(expr "$kv" : "\([Oo][Ee][Mm]\)=.*")" ]; then kv="$(printf -- "oem=%s" "$1")" replaced=1 else line="$line${line:+ }$kv" fi done if [ "$replaced" -eq 1 ]; then set_environment_value "$var" "$line" exit $? fi done set_environment_value "$var" "$line oem=$1" exit $? ) ####################################################################################################### # # # get branding from custom environment stored in a TFFS node # # # ####################################################################################################### get_custom_env_branding() ( minor="${YF_CUSTOM_ENVIRONMENT_TFFS_MINOR:-80}" [ -z "$minor" ] && exit 1 major="$(get_tffs_major)" mknod "$tempdir/tffs_node" c "$major" "$minor" || exit 1 cat "$tempdir/tffs_node" >"$tempdir/tffsnode" 2>"$null" rm -f "$tempdir/tffs_node" 2>"$null" trap 'rm $tempdir/tffsnode' EXIT branding="$(sed -n -e "s|^${branding_name}[ \t]\(.*\)|\1|p" "$tempdir/tffsnode")" if [ -n "$branding" ]; then printf -- "%s\n" "$branding" exit 0 fi exit 1 ) ####################################################################################################### # # # remove an existing 'firmware_version ' entry from custom environment file, if it exists, and # # append a new entry containing the wanted setting # # # ####################################################################################################### set_custom_env_branding() ( minor="${YF_CUSTOM_ENVIRONMENT_TFFS_MINOR:-80}" [ -z "$minor" ] && exit 1 major="$(get_tffs_major)" mknod "$tempdir/tffs_node" c "$major" "$minor" || exit 1 sed -e "/^$branding_name/d" "$tempdir/tffs_node" >"$tempdir/tffsnode" 2>"$null" printf -- "%s\t%s\n" "$branding_name" "$1" >>"$tempdir/tffsnode" cat "$tempdir/tffsnode" >"$tempdir/tffs_node" grep -q "^${branding_name}[[:space:]]$1\$" "$tempdir/tffs_node" 2>"$null" && rc=0 || rc=1 rm -f "$tempdir/tffs_node" "$tempdir/tffsnode" 2>"$null" exit $rc ) ####################################################################################################### # # # get branding for the specified system # # # # $1 - root of system to use, empty string (or "/") for running system # # $2 - value of 'change_branding_support' function, if it's missing, a new call to function will be # # made # # # ####################################################################################################### get_system_branding() ( [ -z "$2" ] && cbs="$(change_branding_support "$1")" || cbs="$2" case "$cbs" in ("fixed_export") get_fixed_branding "$1" exit $? ;; ("kernel_cmdline") get_oem_kernel_cmdline || printf -- "(unknown)" exit $? ;; ("custom_environment") get_custom_env_branding || printf -- "(unknown)" exit $? ;; (*) printf -- "%s\n" "$OEM" exit 0 ;; esac ) ####################################################################################################### # # # replace branding, if necessary # # # # $1 - wanted branding # # $2 - current branding for newly selected system # # $3 - output of change_branding_support for the newly selected system # # # ####################################################################################################### change_branding() ( [ "$1" = "$2" ] && exit 0 case "$3" in ("changeable") set_environment_value "$branding_name" "$1" exit $? ;; ("kernel_cmdline") set_oem_kernel_cmdline "$1" exít $? ;; ("custom_environment") set_custom_env_branding "$1" exit $? ;; ("fixed_export" | "immutable") exit 1 ;; (*) exit 1 ;; esac ) ####################################################################################################### # # # get branding from urlader environment # # # ####################################################################################################### get_device_branding() ( get_environment "$branding_name" ) ####################################################################################################### # # # read a single value/line from specified FDT node/property # # # ####################################################################################################### get_fdt_value() ( [ -f "$fdt_base/$1" ] && sed -n -e "1p" "$fdt_base/$1" || exit 1 ) ####################################################################################################### # # # get 'compatible' info from current FDT # # # ####################################################################################################### get_fdt_compatible_value() ( v="$(get_fdt_value "$fdt_compatible" | sed -n -e "s|\([^,]*\),\(.*\)|\2|p" | sed -e "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/")" [ -n "$v" ] && printf -- "%s\n" "$v" ) ####################################################################################################### # # # find system type or model name # # # # Supported models/chipsets: # # # # (x) all VR9- and AR10-based devices with two slots for a system (kernel + filesystem) # # (x) all GRX5-based devices (no single slot devices known so far) # # (x) all Puma6 and Puma7 devices (no single slot devices found yet) # # ( ) IPQ4019-based devices (7520 and 7530 - single slot devices use IPQ4018) # # (x) IPQ8074-based devices with eMMC flash and using FIT images (4060, FR6000, 5590) # # ( ) BRCMA9 devices with nand flash (7581, 7582 - BCM963 family) # # (X) BRCMA9 devices with nand flash and using FIT image format (7530ax - BCM963 family) # # (x) IPQ5010/IPQ5018-based devices with nand flash and using FIT image format (7510, FR1200ax) # # (X) PRX300-based devices with NAND flash and using FIT image format (5530) # # # ####################################################################################################### get_system_type() ( # Puma6 and Puma7 devices with '/proc/avm_partitions' pseudo file, platform selection based on AVM's script 'puma6_helper.sh' is_puma_based_device && printf -- "%s\n" "Puma6/7" && exit 0 compatible="$(get_fdt_compatible_value)" # VR9, GRX5 and PRX300 based devices with partitions in '/proc/mtd', identified by content of '/proc/cpuinfo' type="$(sed -n -e "$mips_cpuinfo_check" "$proc_cpuinfo")" if [ -n "$type" ]; then printf -- '%s\n' "$type" && exit 0 fi # ARM-based devices with Qualcomm chipsets in 32-bit mode, selected by content of '/proc/cpuinfo' and further properties type="$(sed -n -e "$ipq_cpuinfo_check" "$proc_cpuinfo")" if [ -n "$type" ] && [ "$type" = "IPQ" ]; then fsize="$(get_environment "$flash_size")" # IPQ8074 (Hawkeye family) with eMMC storage using FIT image format, partitions accessible via '/dev/disk/by-partlabel/fit[01]' if [ -n "$(expr "$fsize" : ".*${emmc_mask}=\([^ ]*\).*")" ]; then printf -- "%s\n" "IPQ8074" && exit 0 # BCM963 using kernel and filesystem partitions OR FIT images in NAND flash elif [ "$(expr "$compatible" : "\($bcm963_mask\).*")" = "$bcm963_mask" ]; then printf -- "%s\n" "BCM963" && exit 0 # IPQ501x using FIT format in NAND flash elif [ "$compatible" = "IPQ5018" ]; then printf -- "%s\n" "IPQ5018" && exit 0 # IPQ4019 with NAND flash and partitions in '/proc/mtd' else printf -- '%s\n' "IPQ4019" && exit 0 fi fi # unknown device type printf -- '%s\n' "undetected" return 1 ) ####################################################################################################### # # # find partition number with given name # # # ####################################################################################################### get_mtd_number() { sed -n -e "s|^mtd\([0-9]*\): [^ ]* [^ ]* \"$1\"\$|\1|p" "$proc_mtd" } ####################################################################################################### # # # FIT image devices with eMMC flash: find partition with specified name # # # ####################################################################################################### get_emmc_partition() ( [ -z "$1" ] && exit 1 if [ -L "$disk_by_label/$1" ]; then command -v realpath 2>"$null" 1>&2 && realpath "$disk_by_label/$1" 2>"$null" && exit 0 command -v readlink 2>"$null" 1>&2 && readlink -f "$disk_by_label/$1" 2>"$null" && exit 0 p="$disk_by_label/$1" l="$(ls -dl "$p")" t="${l#*" $p -> "}" d="$(pwd)" # shellcheck disable=SC2164 cd "${p%/*}" 2>"$null" # shellcheck disable=SC2164 cd -P "${t%/*}" 2>"$null" r="$(pwd)/${t##*/}" # shellcheck disable=SC2164 cd "$d" 2>"$null" printf -- "%s\n" "$r" exit 0 fi exit 1 ) ####################################################################################################### # # # GRX5/VR9: locate the MTD partition for kernel or filesystem for the active or alternative system # # # ####################################################################################################### get_mtd_partition() ( [ "$2" = "$active_name" ] || [ "$2" = "$inactive_name" ] || exit 1 if [ "$1" = "$kernel_name" ] || [ "$1" = "$filesystem_name" ]; then printf_ss "$mtdblock_mask" "$(eval get_mtd_number "$(printf -- "\${mtd_%s_name_%s}" "$1" "$2")")" exit 0 fi exit 1 ) ####################################################################################################### # # # check whether this device uses FIT image format for firmware # # # ####################################################################################################### is_fit_image_device() ( [ -n "$BM_DEBUG_FIT" ] && exit 0 case "$system_type" in ("IPQ8074"|"BCM963"|"IPQ5018"|"PRX300") if [ -d "$disk_by_label" ]; then [ -z "$(get_emmc_partition "$(printf_ss "$fit_partition_name_mask" "0")")" ] && exit 1 [ -z "$(get_emmc_partition "$(printf_ss "$fit_partition_name_mask" "1")")" ] && exit 1 else [ -z "$(get_mtd_number "$(printf_ss "$fit_mtd_name_mask" "0")")" ] && exit 1 [ -z "$(get_mtd_number "$(printf_ss "$fit_mtd_name_mask" "1")")" ] && exit 1 fi if [ "$system_type" = "IPQ5018" ] && [ -n "$(get_mtd_partition "$filesystem_name" "$inactive_name")" ]; then # FIT image, but a 'reserved_filesystem' partition, too exit 1 fi [ -x "$bootslot_tool" ] && exit 0 ;; (*) exit 1 ;; esac ) ####################################################################################################### # # # extract 'etc' subdirectory (more isn't needed yet) from ramdisk in a FIT image # # # ####################################################################################################### extract_rootfs_ramdisk_etc() ( if ! command -v cpio 2>"$null" 1>&2 && busybox --list | grep -q "cpio"; then # cpio exists as applet, but there's no symlink for it ln -s "$(command -v busybox 2>"$null")" "$tempdir/cpio" cpio="$tempdir/cpio" elif cpio="$(command -v cpio 2>"$null")"; then : else printf -- "Missing 'cpio' command.\a\n" 1>&2 exit 1 fi mp="$tempdir/$alt_root_mount_name" mkdir -p "$mp" 2>"$null" || exit 1 cwd="$(pwd)" cd "$mp" || exit 1 gzip -d <"$1" | "$cpio" -i -d -m 'etc/*' 2>"$null" cd "$cwd" || exit 1 [ -d "$mp/etc" ] && printf -- "%s\n" "$mp" && exit 0 exit 1 ) ####################################################################################################### # # # check, whether the YourFritz public key is already available and if it's missing, overload one of # # AVM's key files with the key # # # ####################################################################################################### install_yourfritz_key() ( found="" for file in $avm_pubkey_sources; do [ -f "$file" ] || continue key="$(sed -n -e 1p "$file")" [ "$key" = "$yf_signature_key_modulus" ] && exit 0 found="$file $found" done [ -n "$1" ] && exit 1 printf -- "%s\n%s" "$yf_signature_key_modulus" "$yf_signature_key_exponent" >"$tempdir/pubkey" for file in $found; do [ -n "$BM_DEBUG_LOOP" ] && printf -- "Installing YourFritz key as %s\n" "$file" 1>&2 mount -o bind "$tempdir/pubkey" "$file" 2>"$null" && break done install_yourfritz_key check && exit 0 exit 1 ) ####################################################################################################### # # # remove a bind-mounted YourFritz key, if any # # # ####################################################################################################### uninstall_yourfritz_key() ( tgt="$(sed -n -e "s|^\([0-9:]* \)\{3\}[^ ]*${tempdir#/var}/pubkey \([^ ]*\) r.*|\2|p" "$proc_mountinfo")" [ -n "$tgt" ] && [ -n "$BM_DEBUG_LOOP" ] && printf -- "Uninstalling YourFritz key from %s\n" "$tgt" 1>&2 [ -n "$tgt" ] && umount "$tgt" ) ####################################################################################################### # # # check, whether loop device support is present - and try as hard as possible to find a way to load # # an appropriate kernel module, if it's not usable 'out of the box' # # # # If this function returns an exit code of 0, processing that needs the loopback driver for block # # devices may continue; otherwise it should be stopped without further attempts. # # # ####################################################################################################### is_loop_device_supported() ( show_avm_log_files() ( exec 1>&2 if [ -f /var/tmp/firmware_stream_result ]; then printf -- "AVM: firmware_stream_result\n" cat /var/tmp/firmware_stream_result fi if [ -f /var/tmp/fwsign.log ]; then printf -- "AVM: fwsign.log\n" cat /var/tmp/fwsign.log fi if [ -d /var/packet ]; then printf -- "AVM: /var/packet\n" find /var/packet -exec ls -l '{}' \; fi if [ -f /var/tmp/install_out.log ] && [ -s /var/tmp/install_out.log ]; then printf -- "AVM: install_out.log\n" cat /var/tmp/install_out.log fi if [ -f /var/tmp/install_error.log ] && [ -s /var/tmp/install_error.log ]; then printf -- "AVM: install_error.log\n" cat /var/tmp/install_error.log fi ) check_whether_module_loaded() { [ -n "$(get_major_device_id "$loop_driver_name")" ] && return 0 || return 1; } check_whether_module_loaded && exit 0 # already loaded [ -n "$BM_DEBUG_LOOP" ] && printf -- "Trying to load loop driver for block devices\n" 1>&2 modprobe "$loop_driver_name" 2>"$null" check_whether_module_loaded && exit 0 # had to be loaded only find "/lib/modules/$("$uname" -r)" /var \( ! -path "$nas_base/*" -a -name "${loop_driver_name}*.ko" \) | \ while read -r lkm; do if [ -n "$BM_DEBUG_LOOP" ]; then printf -- "Trying to load %s\n" "$lkm" 1>&2 insmod "$lkm" else insmod "$lkm" 2>"$null" fi check_whether_module_loaded && break # present, but not in any dependencies file -> insmod needed instead of modprobe done check_whether_module_loaded && exit 0 [ -n "$BM_NO_DYNAMIC_LOADING" ] && exit 1 # no dynamic loading of loop device drivers from yourfritz.de [ -x "$avm_downloader" ] || exit 1 # no tr069fwupdate found install_yourfritz_key || exit 1 trap 'uninstall_yourfritz_key' EXIT [ -n "$BM_DEBUG_LOOP" ] && printf -- "Loading loop device drivers from %s\n" "$yf_loop_repository_url" 1>&2 "$avm_downloader" packet "$yf_loop_repository_url" && rc=0 || rc=1 [ -n "$BM_DEBUG_LOOP" ] && show_avm_log_files [ "$rc" -ne 0 ] && exit $rc if [ -n "$BM_DEBUG_LOOP" ]; then insmod "/var/$loop_driver_name.ko" else insmod "/var/$loop_driver_name.ko" 2>"$null" fi check_whether_module_loaded && exit 0 # downloaded from repository and finally loaded as module exit 1 ) ####################################################################################################### # # # find rootfs offset in a FIT image partition and mount the found filesystem # # # ####################################################################################################### mount_fit_alternative_system() ( is_loop_device_supported || exit 1 mp="$tempdir/$alt_root_mount_name" mkdir -p "$mp" 2>"$null" || exit 1 mount -t squashfs "$1" "$mp" 2>"$null" && printf -- "%s\n" "$mp" && exit 0 exit 1 ) ####################################################################################################### # # # VR9: locate the MTD partition for kernel or the image name of root filesystem for the active or # # alternative system # # # ####################################################################################################### get_vr9_image() ( [ "$1" = "$filesystem_name" ] || exit 1 if [ "$2" = "$active_name" ]; then printf -- '%s' "$vr9_image_source_active" exit 0 elif [ "$2" = "$inactive_name" ]; then eval local_name="\${mtd_${1}_name_${2}}" printf_ss "$vr9_image_source_inactive" "$(printf_ss "$mtdblock_mask" "$(eval get_mtd_number "$(printf -- "\${mtd_%s_name_%s}" "$1" "$2")")")" exit 0 fi exit 1 ) ####################################################################################################### # # # Puma6/7: locate the eMMC partition for filesystem or kernel of the active or alternative system # # # ####################################################################################################### get_puma_partition() ( [ "$2" = "$active_name" ] || [ "$2" = "$inactive_name" ] || exit 1 if [ "$1" = "$kernel_name" ] || [ "$1" = "$filesystem_name" ]; then eval partname="\$puma_${1}_name_${2}" # shellcheck disable=SC2154 sed -n -e "s|^$partname=\(.*\)|\1|p" "$puma_partitions" exit 0 fi exit 1 ) ####################################################################################################### # # # locate partition/device name for a FIT partition on devices using this feature # # # ####################################################################################################### get_fit_partition() ( [ -z "$1" ] && exit 1 i=$(get_mtd_number "$1") [ -n "$i" ] && printf_ss "$mtdblock_mask\n" "$i" && exit 0 v="$(get_emmc_partition "$1")" [ -n "$v" ] && printf -- "%s\n" "$v" && exit 0 exit 1 ) ####################################################################################################### # # # locate the flash device (MTD or eMMC) for filesystem or kernel of the active or alternative system # # # ####################################################################################################### get_partition() ( if [ "$(expr "$1" : "\($fit_partition_name\)[01]")" = "$fit_partition_name" ]; then get_fit_partition "$1" exit $? fi rc=0 [ "$1" = "$kernel_name" ] || [ "$1" = "$filesystem_name" ] || exit 1 [ "$2" = "$active_name" ] || [ "$2" = "$inactive_name" ] || exit 1 if [ "$is_puma_device" = "1" ]; then get_puma_partition "$1" "$2" rc=$? else if [ "$system_type" = "VR9" ] || [ "$system_type" = "AR10" ]; then if [ "$1" = "$kernel_name" ]; then get_mtd_partition "$1" "$2" else get_vr9_image "$1" "$2" fi rc=$? elif [ "$system_type" = "GRX500" ] || [ "$system_type" = "GRX350" ] || [ "$system_type" = "IPQ4019" ] || [ "$system_type" = "IPQ5018" ]; then get_mtd_partition "$1" "$2" rc=$? else rc=1 fi fi exit $rc ) ####################################################################################################### # # # get the size of a MTD or eMMC partition # # # ####################################################################################################### get_partition_size() ( if [ "$is_puma_device" = "1" ]; then symname="$(sed -n -e "s|\(.*\)=$1|\1|p" "$puma_partitions")" size="$(sed -n -e "s|${symname}_SIZE=\(.*\)|\1|p" "$puma_partitions")" else part="$(expr "$1" : "[^0-9]*\(.*\)")" size="$(sed -n -e "s|^mtd${part}: \([^ ]*\) .*|0x\1|p" "$proc_mtd")" fi [ "${#size}" -gt 2 ] && printf -- '%d\n' $(( size )) && exit 0 exit 1 ) ####################################################################################################### # # # check, if a list contains an item and return its index (based on 1) # # # # $1 - the item to look for (needle) # # $2 - the haystack # # # ####################################################################################################### index_of_item() ( i=0 for item in $2; do i=$(( i + 1 )) [ "$1" = "$item" ] && printf -- '%u' "$i" && exit 0 done exit 1 ) ####################################################################################################### # # # get the count of words in the specified string # # # # $* - items to count # # # ####################################################################################################### count_of_items() ( # shellcheck disable=SC2048,SC2086 set -- $* printf -- '%u' $# ) ####################################################################################################### # # # create a locked section using a semaphore file, cleanup older orphaned locks automatically # # # # $1 - operation (get or release) # # $2 - maximum wait time for 'get' # # # ####################################################################################################### spinlock() ( { [ "$1" = "get" ] || [ "$1" = "release" ]; } || exit 1 [ -z "$spinlock_pid" ] && exit 1 set -C if [ "$1" = "get" ]; then maxwait=$(( $2 )) i=0 while [ $i -le $maxwait ]; do while [ -f "$semafile" ]; do pid="$(cat "$semafile")" if [ -d "/proc/$pid" ]; then sleep 1 i=$(( i + 1 )) [ $i -ge $maxwait ] && exit 1 else if [ -d "/proc" ]; then rm "$semafile" 2>"$null" fi fi done printf -- '%d' "$spinlock_pid" >"$semafile" pid="$(cat "$semafile")" [ -n "$pid" ] && [ "$pid" = "$spinlock_pid" ] && exit 0 done exit 1 else [ -f "$semafile" ] || exit 0 pid="$(cat "$semafile")" { [ -z "$pid" ] || [ "$pid" != "$spinlock_pid" ]; } && exit 0 rm -f "$semafile" 2>"$null" exit 0 fi ) ####################################################################################################### # # # compare two strings and return -1, 0 or 1 for lower, equal and higher # # # # $1 - left item to compare # # $2 - right item to compare # # # ####################################################################################################### compare_strings() ( t="$tempdir/cs_$$" while [ -d "$t" ]; do t="${t}_$$" done mkdir -p "$t" 2>"$null" trap 'rm -r "$t" 2>$null' EXIT HUP INT printf -- '%s\377' "$1" >"$t/left" printf -- '%s\377' "$2" >"$t/right" cmp -l "$t/left" "$t/right" 2>"$null" >"$t/out" read -r pos left right 2>"$null" <"$t/out" if [ -z "$left" ] || [ -z "$right" ]; then printf -- '%d' "0" else [ "$left" -lt "$right" ] && printf -- '%d' "-1" || printf -- '%d' "1" fi ) ####################################################################################################### # # # return maximum value of numbers specified as parameters # # # ####################################################################################################### max() ( m=0 while [ -n "$1" ]; do [ "$1" -gt "$m" ] && m="$1" shift done printf -- "%d\n" "$m" ) ####################################################################################################### # # # get next system to boot from environment # # # ####################################################################################################### get_system_switch() ( [ -n "$2" ] && sed -n -e "s/^$system_select_name\t[01]\(.*\)\$/\1/p" "$1" && return sed -n -e "s/^$system_select_name\t\([01]\).*\$/\1/p" "${1:-$urlader_environment}" ) ####################################################################################################### # # # check, whether the specified partition/device contains a magic value of AVM's FIT image format # # # # $1 - (block) device to check # # # ####################################################################################################### is_fit_image_present() ( find_rootfs_in_fit_image "$1" "check" ) ####################################################################################################### # # # check the first 16 byte of the specified partition - if they're all 0xFF or 0x00, there's usually # # no kernel (and no alternative system) installed # # # # $1 - kernel (block) device to check # # # ####################################################################################################### is_kernel_present() ( __read_bytes() { while read -r line left right; do [ "$left" -ne 377 ] && [ "$left" -ne 0 ] && return 0 done return 1 } dd if="$1" bs=16 count=1 2>"$null" | cmp -l - $zeros 2>"$null" | __read_bytes ) ####################################################################################################### # # # check, if the system to start next is also the running one # # # # - if a FDT exists, the started system may be found there # # - if a saved environment exists, we take the value from there and treat it as the original one # # - if neither of the places above contains a valid value, the partition of 'kernel' type with the # # lowest device index is assumed to be from system selector value 0 # # # ####################################################################################################### is_switched() ( if [ -d "$fdt_chosen" ]; then ! [ "$(sed -n -e "s|^\([01]\).*|\1|p" "$fdt_chosen/$system_select_name")" = "$(get_system_switch)" ] else if [ -f "$boot_cache_file" ]; then ! [ "$(get_system_switch)" = "$(get_system_switch "$boot_cache_file")" ] else if is_fit_image_device; then [ -n "$BM_DEBUG_FIT" ] && exit 0 ! [ "$(get_system_switch)" = "$("$bootslot_tool" get_active)" ] else k1="$(get_partition "$kernel_name" "$active_name")" k2="$(get_partition "$kernel_name" "$inactive_name")" c=$(compare_strings "$k1" "$k2") [ "$c" -eq -1 ] && current=0 || current=1 ! [ "$(get_system_switch)" = "$current" ] fi fi fi ) ####################################################################################################### # # # switch system selection variable to alternative value # # # ####################################################################################################### switch_system() ( if [ -x "$bootslot_tool" ] && ! is_switched; then "$bootslot_tool" activate_other exit $? fi printf -- '%s\t%d\n' "$system_select_name" $(( ( $(get_system_switch) + 1 ) % 2 )) >"$urlader_environment" exit 0 ) ####################################################################################################### # # # switch next boot time selection to specified system, if action is really needed # # # # $1 - 'running' / 'alternative' to switch to the specified system based on the running version # # # ####################################################################################################### switch_system_to() ( rc=1 if is_switched; then [ "$1" = "running" ] && switch_system && rc=0 else [ "$1" = "alternative" ] && switch_system && rc=0 fi if [ -n "$2" ]; then if [ -f "$data_cache_file" ]; then if [ "$1" = "running" ]; then cbs_name="active_change_branding_support" brnd_name="active_branding" else cbs_name="inactive_change_branding_support" brnd_name="inactive_branding" fi cbs="$(sed -n -e "s|^$cbs_name=\(['\"]\?\)\(.*\)\1\$|\2|p" "$data_cache_file")" brnd="$(sed -n -e "s|^$brnd_name=\(['\"]\?\)\(.*\)\1\$|\2|p" "$data_cache_file")" if [ -n "$cbs" ]; then change_branding "$2" "$brnd" "$cbs" && exit 0 fi fi exit 1 fi exit $rc ) ####################################################################################################### # # # get product name value from AVM's rc.conf file # # # # $1 - system root directory # # # ####################################################################################################### get_product_name() ( sed -n -e "s|^[ \t]*export $avm_product_name=\"\?\([^\"]*\)\"\?\$|\1|p" "${1:-/}${1:+/}$avm_version_file" | sed -n -e \$p ) ####################################################################################################### # # # get major version from AVM's rc.conf file # # # # $1 - system root directory # # # ####################################################################################################### get_version_major() ( sed -n -e "s|^[ \t]*export $avm_version_major_name=\"\?\([^\"]*\)\"\?\$|\1|p" "${1:-/}${1:+/}$avm_version_file" | sed -n -e \$p ) ####################################################################################################### # # # get the contained brandings from the directory with system defaults # # # # $1 - system root directory # # $2 - output of 'change_branding_support' for system from $1; if empty, function will be called # # (possibly again/multiple times) to get data # # # ####################################################################################################### get_brandings() ( [ -z "$2" ] && cbs="$(change_branding_support "$1")" || cbs="$2" if [ "$cbs" = "fixed_export" ]; then get_fixed_branding "$1" exit 0 fi if [ -z "$1" ] || [ "$1" = "/" ]; then eval PRODUKT="\$$avm_product_name" else PRODUKT="$(get_product_name "$1")" fi path="$(printf -- '%s%s%s' "${1:-/}" "${1:+/}" "$(printf_ss "$defaults_dir" "$PRODUKT")")" space="" for name in "$path"/*; do if [ -d "$name" ]; then printf -- "%s%s" "$space" "${name##*/}" space=" " fi done printf -- "\n" ) ####################################################################################################### # # # retrieve version values from AVM's bootslotctl tool # # # ####################################################################################################### #get_fit_version_values() #( # [ -z "$1" ] && sys="active" || sys="other" # "$bootslot_tool" "get_fw_version" "$("$bootslot_tool" "get_$sys")" #) ####################################################################################################### # # # extract firmware variables from AVM's firmware info source # # # # $1 - info source file # # $2 - values prefix in this file # # # ####################################################################################################### get_version_main_values() ( for v in $avm_version_main_components; do sed -n -e "s|^[ \t]*export $2$v=\"\?\([^\"]*\)\"\?\$|$v=\"\1\"|p" "$1" | sed -n -e \$p done ) ####################################################################################################### # # # extract firmware variables from AVM's firmware new info source # # # # $1 - system root directory # # # ####################################################################################################### get_new_version_main_values() ( path="${1:-/}${1:+/}$avm_version_file" prefix="$avm_version_file_prefix" get_version_main_values "$path" "$prefix" ) ####################################################################################################### # # # extract firmware variables from AVM's firmware old info source # # # # $1 - system root directory # # # ####################################################################################################### get_old_version_main_values() ( path="${1:-/}${1:+/}$avm_version_file_old" prefix="$avm_version_file_old_prefix" get_version_main_values "$path" "$prefix" ) ####################################################################################################### # # # check, if version info is specified in the older or newer place # # # # $1 - system root directory # # # ####################################################################################################### has_old_version_info() ( path="${1:-/}${1:+/}$avm_version_file_old" for v in $avm_version_main_components; do [ -z "$(sed -n -e "s|^[ \t]*export $avm_version_file_old_prefix$v=\"\?\([^\"]*\)\"\?\$|\1|p" "$path")" ] \ && exit 1 || exit 0 done ) ####################################################################################################### # # # get system version from specified system root # # # # $1 - system root directory # # # ####################################################################################################### get_version_values() ( if has_old_version_info "$1"; then get_old_version_main_values "$1" else get_new_version_main_values "$1" fi ) ####################################################################################################### # # # get system version from specified system root # # # # $1 - system root directory # # # ####################################################################################################### get_system_version() ( eval "$(get_version_values "$1")" [ -z "$SUBVERSION" ] && [ -n "$BUILDNUMBER" ] && SUBVERSION="-$BUILDNUMBER" has_old_version_info "$1" && printf -- '%s%s' "$VERSION" "$SUBVERSION" || printf -- '%s.%s%s' "$(get_version_major "$1")" "$VERSION" "$SUBVERSION" ) ####################################################################################################### # # # parse date value with format from AVM's firmware (/etc/version) to a UNIX timestamp # # # # $1 - source string # # # ####################################################################################################### parse_date_value() ( command -v date 2>"$null" 1>&2 || printf -- '%s' "$1" date --help 2>&1 | grep -q BusyBox 2>"$null" || printf -- '%s' "$1" eval "$(printf -- '%s\n' "$1" | sed -n -e "s|\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\) \([0-9]*\):\([0-9]*\):\([0-9]*\)|day=\1 month=\2 year=\3 hour=\4 minutes=\5 seconds=\6|p")" # shellcheck disable=SC2154 date +%s -d "$year$month$day$hour$minutes.$seconds" ) ####################################################################################################### # # # initialize text snippets from external message definition file (bootmanager.msg) # # # ####################################################################################################### get_localized() ( clang="$1" id="$2" line="$(sed -n -e "s|^[ \t]*[Ll]anguages \(.*\)|\1|p" "$messages_file")" [ -z "$line" ] && exit 1 # shellcheck disable=SC2086 set -- $line if ! [ "$clang" = "$1" ]; then deflang="$1" shift while [ -n "$1" ]; do if [ "$1" = "$clang" ]; then usedLanguage="$1" break fi shift done [ -z "$1" ] && usedLanguage="$deflang" else usedLanguage="$1" fi sed -n -e "s/^\($usedLanguage\|any\):\($id\)=\(.*\)/\3/p" "$messages_file" ) ####################################################################################################### # # # get localized date string # # # ####################################################################################################### get_localized_datetime_mask() ( mask="$(get_localized "${Language:-de}" "datetime")" [ -z "$mask" ] && mask="%c" printf -- "%s" "$mask" ) ####################################################################################################### # # # get localized date string # # # ####################################################################################################### get_localized_date() ( mask="$1" date "+$mask" -d @"$2" ) ####################################################################################################### # # # get system build date from specified root, it's a file modification time in the newer versions # # # # $1 - system root directory # # # ####################################################################################################### get_system_date() ( path="${1:-/}${1:+/}$avm_version_file_old" if has_old_version_info "$1"; then d="$(sed -n -e "s|^[ \t]*export $avm_version_file_old_prefix$avm_version_date=\"\?\([^\"]*\)\"\?\$|\1|p" "$path")" v=$(parse_date_value "$d") else v=$(stat -c %Y "$path") fi printf -- '%d' "$v" ) ####################################################################################################### # # # check, if the specified directory is an active mount-point for any filesystem # # # ####################################################################################################### is_active_mountpoint() ( line="$(sed -n -e "\|^[0-9]\+ [0-9]\+ [0-9:]* [^ ]* ${1:-/} .* - .*|p" "$proc_mountinfo")" [ ${#line} -eq 0 ] && exit 1 || exit 0 ) ####################################################################################################### # # # find an active mountpoint with the specified device # # # ####################################################################################################### find_mountpoint_for_device() ( line="$(sed -n -e "s|^[0-9]\+ [0-9]\+ [0-9:]* [^ ]* \(.*\) .* - [^ ]* $1 .*|\1|p" "$proc_mountinfo")" [ ${#line} -eq 0 ] || printf -- '%s' "$line" [ ${#line} -eq 0 ] && exit 1 || exit 0 ) ####################################################################################################### # # # find the device for an active mountpoint # # # ####################################################################################################### find_device_of_mountpoint() ( line="$(sed -n -e "s|^[0-9]\+ [0-9]\+ [0-9:]* [^ ]* $1 .* - [^ ]* \([^ ]*\) .*|\1|p" "$proc_mountinfo")" [ ${#line} -eq 0 ] || printf -- '%s' "$line" [ ${#line} -eq 0 ] && exit 1 || exit 0 ) ####################################################################################################### # # # find device's major and minor ID for the specified mountpoint # # # ####################################################################################################### get_ids_of_mountpoint() ( line="$(sed -n -e "s|^[0-9]\+ [0-9]\+ *\([0-9]*:[0-9]*\) [^ ]* ${1:-/} .* - [^ ]* \([^ ]*\) .*|\1|p" "$proc_mountinfo")" [ ${#line} -eq 0 ] || printf -- '%s' "$line" [ ${#line} -eq 0 ] && exit 1 || exit 0 ) ####################################################################################################### # # # check, if the specified mountpoint is from a loop device # # # ####################################################################################################### is_loopdevice_mountpoint() ( loop_major="$(get_major_device_id "$loop_driver_name")" [ ${#loop_major} -eq 0 ] && exit 1 dev="$(get_ids_of_mountpoint "$1")" [ ${#dev} -eq 0 ] && exit 1 [ "${dev%:*}" = "$loop_major" ] && exit 0 || exit 1 ) ####################################################################################################### # # # get file name of mounted image for a loopback device # # # ####################################################################################################### get_loopdevice_backingfile() ( if is_loopdevice_mountpoint "${1:-/}"; then dev="$(get_ids_of_mountpoint "${1:-/}")" [ -z "$dev" ] && exit 1 for device in "$sysfs_block_dir"/*; do if [ "$(cat "$device/dev" 2>"$null")" = "$dev" ]; then file="$(cat "$device/$loop_driver_name/$backing_file_name" 2>"$null")" [ "${#file}" -gt 0 ] && printf -- '%s\n' "$file" && exit 0 fi done fi exit 1 ) ####################################################################################################### # # # mount alternative system' root filesystem somewhere # # # # CGI calls have only /bin:/usr/bin in the search PATH, the old value is not important, changing the # # PATH should be local to this function and its children, even if it's "export"ed here. # # # ####################################################################################################### mount_alternative_system() ( export PATH=/sbin:/usr/sbin:/bin:/usr/bin src="$(get_partition "$filesystem_name" "$inactive_name")" mp="$tempdir/$alt_root_mount_name" mkdir -p "$mp" 2>"$null" || exit 1 if ! [ "${src#mount:}" = "$src" ]; then # shellcheck disable=SC2046 set -- $(printf -- '%s' "$src" | sed -e "s|:| |g") device="$2" file="$3" else unset device file="$src" fi if [ -n "$device" ]; then wr="$tempdir/$wrapper_mount_name" mkdir -p "$wr" 2>"$null" || exit 1 wre="$(find_mountpoint_for_device "$device")" # shellcheck disable=SC2181 if [ $? -ne 0 ]; then mount -t yaffs2 -o ro "$device" "$wr" 2>"$null" || exit 1 else wr="$wre" fi fi mount -t squashfs -o ro "$wr$file" "$mp" 2>"$null" || exit 1 printf -- '%s' "$mp" exit 0 ) ####################################################################################################### # # # dismount the alternative root filesystem # # # ####################################################################################################### dismount_alternative_system() ( is_active_mountpoint "$tempdir/$alt_root_mount_name" || exit 0 umount -d "$tempdir/$alt_root_mount_name" 2>"$null" || exit 1 rmdir "$tempdir/$alt_root_mount_name" 2>"$null" if [ -L "$tempdir/losetup" ]; then losetup="$tempdir/losetup" else if command -v losetup 2>"$null" 1>&2; then losetup="$(command -v losetup 2>"$null")" else unset losetup fi fi if [ -n "$losetup" ]; then "$losetup" -a | grep -q "$tempdir/" 2>"$null" && \ "$losetup" -d "$("$losetup" -a | sed -n -e "s|^\($devtmpfs/$loop_driver_name.*\):.*$tempdir/.*|\1|p")" fi [ -f "$tempdir/$alt_root_image_name" ] && rm -f "$tempdir/$alt_root_image_name" 2>"$null" is_active_mountpoint "$tempdir/$wrapper_mount_name" || exit 0 umount -d "$tempdir/$wrapper_mount_name" 2>"$null" || exit 1 rmdir "$tempdir/$wrapper_mount_name" 2>"$null" exit 0 ) ####################################################################################################### # # # guess modification source # # # # $1 - mounted system root directory (optional) # # # ####################################################################################################### get_modified_by() ( base="${1:-}" i=1 for f in $version_files; do [ -f "$base/$f" ] && break i=$(( i + 1 )) done # shellcheck disable=SC2086 set -- $modified_by eval printf -- '%s' "\$$i" ) ####################################################################################################### # # # convert binary value (from STDIN) to decimal representation # # # ####################################################################################################### bin2dec() ( bin2dec_read_octal() { e="$1" i=1 v=0 s=-8 ff=0 while read -r pos left right; do if [ $ff -eq 1 ]; then [ "$e" -eq 0 ] && v=$(( v * 256 )) || s=$(( s + 8 )) [ "$e" -eq 0 ] && ff=255 || ff=$(( 255 << s )) v=$(( v + ff )) i=$(( i + 1 )) ff=0 fi while [ $i -lt "$pos" ]; do # insert zeros for missing bytes [ "$e" -eq 0 ] && v=$(( v * 256 )) || s=$(( s + 8 )) i=$(( i + 1 )) done if [ "$right" = 377 ] && [ $ff -eq 0 ]; then ff=1 continue fi [ "$e" -eq 0 ] && v=$(( v * 256 )) || s=$(( s + 8 )) [ "$e" -eq 0 ] && right=$(( 0$right )) || right=$(( 0$right << s )) v=$(( v + right )) i=$(( pos + 1 )) done printf -- '%d' $v } [ "$(dd if=/proc/self/exe bs=1 count=1 skip=5 2>"$null" | base64)" = "AQ==" ] && e=1 || e=0 { cat; printf -- '%b' "\377"; } | cmp -l -- $zeros - 2>"$null" | bin2dec_read_octal $e return 0 ) ####################################################################################################### # # # get mkfs_time value from SquashFS superblock # # # # $1 - mounted system root directory # # # ####################################################################################################### get_mkfs_time() ( base="${1:-}" if is_loopdevice_mountpoint "${base:-/}"; then src="$(get_loopdevice_backingfile "${base:-/}")" [ "${#src}" -gt 0 ] && len=$(stat -c %s "$src") || len=0 elif [ -z "$1" ] || [ "$1" = "/" ]; then # root of running system needs /proc/mtd src="$(get_partition "$filesystem_name" "$active_name")" [ "${#src}" -gt 0 ] && len=$(get_partition_size "$src") || len=0 else src="$(find_device_of_mountpoint "$1")" [ "${#src}" -gt 0 ] && len=$(get_partition_size "$src") || len=0 fi if [ "${#src}" -gt 0 ]; then v="$(dd if="$src" bs=4 count=1 skip=2 2>"$null" | bin2dec)" if [ "${#v}" -gt 0 ]; then if [ "$v" -gt "$len" ]; then printf -- '%s\n' "$v" && exit 0 fi fi fi exit 1 ) ####################################################################################################### # # # get date of last modification from specified system root # # - take the date and time from any existing modification file or # # - take date and time from SquashFS image, if it's a file (mounted via a loop device) # # # # $1 - mounted system root directory (optional) # # # ####################################################################################################### get_modified_date() ( command -v stat 2>"$null" 1>&2 || exit 1 v="$(stat -c %Y -- /proc/self/exe 2>"$null")" # shellcheck disable=SC2181 [ $? -ne 0 ] && exit 1 [ "$(expr "$v" : "\([0-9]*\)")" = "$v" ] || exit 1 base="${1:-}" unset v for f in $version_files; do if [ -f "$base/$f" ]; then v="$(stat -c %Y -- "$base/$f")" break fi done if [ -z "$v" ]; then v="$(get_mkfs_time "${base:-/}")" # try to find valid mkfs_time first fi if [ -z "$v" ]; then if is_loopdevice_mountpoint "${base:-/}"; then file="$(get_loopdevice_backingfile "${base:-/}")" v="$(stat -c %Y -- "$file")" d="$(get_system_date "$1")" if [ -n "$d" ] && [ -n "$v" ]; then [ "$d" -lt "$v" ] && delta=$(( v - d )) || delta=$(( d - v )) # no abs() function available [ $delta -lt 600 ] && exit 1 # within ten minutes, assume no further modifications fi else v="$(stat -c %Y -- "$base/$avm_rcconf")" fi fi [ -z "$v" ] && exit 1 printf -- "%u\n" "$v" exit 0 ) ####################################################################################################### # # # cleanup any mounted images and remove the temporary directory and semaphore file, if it's owned by # # this instance to avoid deadlocks # # # ####################################################################################################### cleanup() { [ -z "$tempdir" ] && return is_active_mountpoint "$tempdir/$alt_root_mount_name" && umount -d "$tempdir/$alt_root_mount_name" 2>"$null" is_active_mountpoint "$tempdir/$wrapper_mount_name" && umount -d "$tempdir/$wrapper_mount_name" 2>"$null" rm -r "$tempdir" 2>"$null" [ -z "$spinlock_pid" ] && return [ -f "$semafile" ] || return [ "$(cat $semafile 2>"$null")" = "$spinlock_pid" ] && rm -f "$semafile" 2>"$null" } ####################################################################################################### # # # collect data from active and inactive system for further processing # # # ####################################################################################################### collect_data() { datetime_mask="$(get_localized_datetime_mask "${Language:-de}")" running_version="$(get_system_version)" running_date_epoch="$(get_system_date)" running_date="$(get_localized_date "$datetime_mask" "$running_date_epoch")" running_fsdate_epoch="$(get_modified_date)" running_fsdate="$(get_localized_date "$datetime_mask" "$running_fsdate_epoch")" running_modified="$(get_modified_by)" readonly system_type running_version running_date_epoch running_date running_fsdate_epoch running_fsdate running_modified running_cbs="$(change_branding_support)" running_branding="$(get_system_branding "" "$running_cbs")" running_brandings="$(get_brandings "" "$running_cbs")" current_system_switch=$(get_system_switch) readonly running_cbs running_branding running_brandings current_system_switch alternative_is_missing=1 if ! is_fit_image_device; then if [ "$system_type" = "IPQ5018" ]; then is_fit_image_present "$(get_partition "$(printf_ss "$fit_partition_name_mask" "$("$bootslot_tool" "get_other")")")" && present=1 || present=0 else is_kernel_present "$(get_partition "$kernel_name" "$inactive_name")" && present=1 || present=0 fi if [ "$present" -eq 1 ]; then alternative_is_missing=0 spinlock_pid=$$ spinlock get 10 && mp="$(printf -- '%s' "$(mount_alternative_system)")" if [ -n "$mp" ]; then alternative_version="$(get_system_version "$mp")" readonly alternative_version if [ -n "$alternative_version" ]; then alternative_date_epoch="$(get_system_date "$mp")" alternative_date="$(get_localized_date "$datetime_mask" "$alternative_date_epoch")" alternative_fsdate_epoch="$(get_modified_date "$mp")" alternative_fsdate="$(get_localized_date "$datetime_mask" "$alternative_fsdate_epoch")" alternative_modified="$(get_modified_by "$mp")" readonly alternative_date_epoch alternative_date alternative_fsdate_epoch alternative_fsdate alternative_modified alternative_cbs="$(change_branding_support "$mp" "$running_cbs")" alternative_branding="$(get_system_branding "$mp" "$alternative_cbs")" alternative_brandings="$(get_brandings "$mp" "$alternative_cbs")" readonly alternative_cbs alternative_branding alternative_brandings current_system_switch fi dismount_alternative_system fi spinlock release fi else if [ -z "$BM_DEBUG_FIT_IMAGE" ]; then fp="$(printf_ss "$fit_partition_name_mask" "$("$bootslot_tool" get_other)")" fpd="$(get_partition "$fp")" else fpd="$BM_DEBUG_FIT_IMAGE" [ -z "$fpd" ] && exit 1 fi eval "$(find_fit_inactive_rootfs_parameters "$fpd")" readonly alternative_is_missing alternative_version readonly alternative_date_epoch alternative_date alternative_fsdate_epoch alternative_fsdate alternative_modified readonly alternative_cbs alternative_branding alternative_brandings current_system_switch fi } ####################################################################################################### # # # implement 'get_values' action, if no cached data exists # # # ####################################################################################################### get_values() { printf -- "active_version=\"%s\"\n" "$running_version" printf -- "active_date_epoch=\"%s\"\n" "$running_date_epoch" printf -- "active_date=\"%s\"\n" "$running_date" printf -- "active_modified_by=\"%s\"\n" "$running_modified" printf -- "active_modified_at_epoch=\"%s\"\n" "$running_fsdate_epoch" printf -- "active_modified_at=\"%s\"\n" "$running_fsdate" printf -- "active_brandings=\"%s\"\n" "$running_brandings" printf -- "active_branding=\"%s\"\n" "$running_branding" printf -- "active_change_branding_support=%s\n" "$running_cbs" if [ $alternative_is_missing -eq 1 ]; then printf -- "inactive_version=\"%s\"\n" "missing" else printf -- "inactive_version=\"%s\"\n" "$alternative_version" printf -- "inactive_date_epoch=\"%s\"\n" "$alternative_date_epoch" printf -- "inactive_date=\"%s\"\n" "$alternative_date" printf -- "inactive_modified_by=\"%s\"\n" "$alternative_modified" if ! [ "$alternative_modified" = "-" ]; then printf -- "inactive_modified_at_epoch=\"%s\"\n" "$alternative_fsdate_epoch" printf -- "inactive_modified_at=\"%s\"\n" "$alternative_fsdate" fi printf -- "inactive_brandings=\"%s\"\n" "$alternative_brandings" printf -- "inactive_branding=\"%s\"\n" "$alternative_branding" printf -- "inactive_change_branding_support=%s\n" "$alternative_cbs" fi # # the value of 'switch_branding_support' is kept stable for older frontends, but it may be removed # in future versions # if [ "$running_cbs" = "immutable" ] && [ "$alternative_cbs" = "immutable" ]; then cb_support="false" elif [ "$running_cbs" = "fixed_export" ] && ! [ "$alternative_cbs" = "fixed_export" ]; then cb_support="running_fixed" elif ! [ "$running_cbs" = "fixed_export" ] && [ "$alternative_cbs" = "fixed_export" ]; then cb_support="alternative_fixed" elif [ "$running_cbs" = "fixed_export" ] && [ "$alternative_cbs" = "fixed_export" ]; then cb_support="both_fixed" else cb_support="true" fi # old name of 'active_branding', while only 'fixed_export', 'changeable' and 'immutable' were considered printf -- "current_branding=%s\n" "$running_branding" printf -- "device_branding=%s\n" "$device_branding" printf -- "device_branding_changeable=%s\n" "$device_branding_is_changeable" printf -- "switch_branding_support=%s\n" "$cb_support" printf -- "current_switch_value=%s\n" "$current_system_switch" printf -- "system_is_switched=%s\n" "$(is_switched && printf -- '%s' "true" || printf -- '%s' "false")" printf -- "bootmanager_version=\"%s-%s\"\n" "$yf_bootmanager_version" "$yf_bootmanager_timestamp" [ -n "$error_description" ] && printf -- "error_description=\"%s\"\n" "$error_description" [ "$error_code" -ne 0 ] && printf -- "error_code=%u\n" "$error_code" } ####################################################################################################### # # # check dual-boot support # # # ####################################################################################################### is_dualboot_supported() ( [ -z "$(get_system_switch)" ] && exit 1 is_fit_image_device && [ -x "$bootslot_tool" ] && exit 0 [ -n "$BM_DEBUG_FIT" ] && exit 0 [ -z "$(get_partition "$kernel_name" "$inactive_name")" ] && exit 1 [ -z "$(get_partition "$filesystem_name" "$inactive_name")" ] && exit 1 exit 0 ) ####################################################################################################### # # # save initial environment at first call # # # ####################################################################################################### save_environment() ( [ -f "$boot_cache_file" ] || cat "$urlader_environment" >"$boot_cache_file" 2>"$null"; ) ####################################################################################################### # # # display debug line with optional colorization # # # ####################################################################################################### debug() ( if [ "$color" -eq 1 ]; then line_start="$(printf -- "\033[32;1m")" line_end="$(printf -- "\033[0m")" header_end="" case "$2" in ("red") c=31 ;; ("green") c=32 ;; ("yellow") c=33 ;; ("blue") c=34 ;; (*) c=37 ;; esac value_start="$(printf -- "\033[%s;1m" "$c")" else line_start="" line_end="" header_end="" value_start="" fi mask="$(printf -- "%s%s%s%s%s%s\\\\n" "$line_start" "$1" "$header_end" "$value_start" "$3" "$line_end")" shift 3 # shellcheck disable=SC2059 printf -- "$mask" "$@" ) ####################################################################################################### # # # display delimiter line to format verbose debug output # # # ####################################################################################################### debug_delim() ( if [ "$color" -eq 1 ]; then c="$1" v="$2" shift 2 left="$(dd if=$zeros bs=1 count=$(( ( max_delim - ${#1} ) / 2 )) 2>"$null" | tr '\000' '>')" right="$(dd if=$zeros bs=1 count=$(( max_delim - ${#1} - ${#left} )) 2>"$null" | tr '\000' '<')" mask="$(printf -- "{white}%s%s {%s}%s {white}%s%s{reset}" ">>>>>>>>>>" "$left" "$c" "$v" "$right" "<<<<<<<<<<")" # shellcheck disable=SC2059 printf -- "$mask\n" "$@" | colorize else shift 2 printf -- ">>>>>>>>>> %s <<<<<<<<<<\n" "$*" | \ sed -e "s|{[^}]}||g" fi ) ####################################################################################################### # # # display delimiter line to format verbose debug output # # # ####################################################################################################### colorize() ( [ "$color" -eq 0 ] && cat - && exit color() { printf -- "\033[%s;1m" "$1"; } red="$(color 31)" red_bg="$(color "37;101")" yellow="$(color 33)" green="$(color 32)" blue="$(color 34)" white="$(color 37)" reset="$(printf -- "\033[0m")" sed -e "s|{red}|$red|g" \ -e "s|{red_bg}|$red_bg|g" \ -e "s|{yellow}|$yellow|g" \ -e "s|{green}|$green|g" \ -e "s|{blue}|$blue|g" \ -e "s|{white}|$white|g" \ -e "s|{reset}|$reset|g" ) ####################################################################################################### # # # get data from inactive slot, if a FIT image is used # # # ####################################################################################################### get_inactive_slot_system_info() { if [ "$rootfs_type" = "squashfs" ]; then spinlock_pid=$$ spinlock get 10 && mp="$(printf -- '%s' "$(mount_fit_alternative_system "$1")")" else mp="$(extract_rootfs_ramdisk_etc "$1")" fi if [ -n "$mp" ]; then printf -- "%s=%s\n" "alternative_is_missing" "0" alternative_version="$(get_system_version "$mp")" printf -- "%s=\"%s\"\n" "alternative_version" "$alternative_version" if [ -n "$alternative_version" ]; then alternative_date_epoch="$(get_system_date "$mp")" printf -- "%s=%s\n" "alternative_date_epoch" "$alternative_date_epoch" printf -- "%s=\"%s\"\n" "alternative_date" "$(get_localized_date "$datetime_mask" "$alternative_date_epoch")" alternative_fsdate_epoch="$(get_modified_date "$mp")" printf -- "%s=%s\n" "alternative_fsdate_epoch" "$alternative_fsdate_epoch" printf -- "%s=\"%s\"\n" "alternative_fsdate" "$(get_localized_date "$datetime_mask" "$alternative_fsdate_epoch")" printf -- "%s=\"%s\"\n" "alternative_modified" "$(get_modified_by "$mp")" printf -- "%s=%s\n" "alternative_cbs" "$(change_branding_support "$mp" "$running_cbs")" printf -- "%s=%s\n" "alternative_branding" "$(get_system_branding "$mp" "$alternative_cbs")" printf -- "%s=\"%s\"\n" "alternative_brandings" "$(get_brandings "$mp" "$alternative_cbs")" fi if [ "$rootfs_type" = "squashfs" ]; then dismount_alternative_system else rm -r "$mp" 2>"$null" fi else printf -- "%s=%s\n" "alternative_is_missing" "1" fi [ "$rootfs_type" = "squashfs" ] && spinlock release } ####################################################################################################### # # # detect root filesystem from FIT image in inactive slot # # # ####################################################################################################### find_fit_inactive_rootfs_parameters() { if ! [ -f "$inactive_fit_cache_file" ]; then time1=$(timestamp) find_rootfs_in_fit_image "$1" "fscopy-$$" >"$inactive_fit_cache_file" retc=$? time2=$(timestamp) [ -n "$BM_DEBUG_FIT" ] && printf -- "FIT image rootfs offset lookup lasted %s seconds.\n" "$(duration "$time1" "$time2")" 1>&2 # shellcheck disable=SC1090 . "$inactive_fit_cache_file" if ! [ -f "$tempdir/fscopy-$$" ]; then printf -- "File with copy of filesystem image is missing.\n" 1>&2 printf -- "%s=%u\n" "alternative_is_missing" "1" >>"$inactive_fit_cache_file" printf -- "%s=\"\"\n" "alternative_version" >>"$inactive_fit_cache_file" error_description="Unable to mount inactive filesystem from FIT image." error_code=$(( error_code & 2 )) else get_inactive_slot_system_info "$tempdir/fscopy-$$" >>"$inactive_fit_cache_file" rm -f "$tempdir/fscopy-$$" 2>"$null" fi else [ -n "$BM_DEBUG_FIT" ] && printf -- "inactive slot system info loaded from cache file for FIT image\n" 1>&2 fi cat "$inactive_fit_cache_file" } ####################################################################################################### # # # load device configuration findings from cache file, if it exists # # # ####################################################################################################### load_device_configuration() { out_dev_config() { printf -- "device_cbs=%s device_branding=%s device_branding_is_changeable=%s\n" "$1" "$2" "$3" } if ! [ -f "$device_cache_file" ]; then printf -- "undetectable=%s\n" "$(get_environment "$branding_name")" >"$device_cache_file" time1=$(timestamp) device_branding="$(check_urlader_configuration "$branding_name")" retc=$? time2=$(timestamp) # shellcheck disable=SC2181 [ -z "$device_branding" ] && device_branding="$(get_environment "$branding_name")" if [ "$retc" -eq 0 ]; then device_branding_is_changeable="true" printf -- "changeable=%s\n" "$device_branding" >"$device_cache_file" elif [ "$retc" -eq 2 ]; then device_branding_is_changeable="false" printf -- "immutable=%s\n" "$device_branding" >"$device_cache_file" else device_branding_is_changeable="false" fi [ -n "$BM_DEBUG_EVA" ] && printf -- "EVA device configuration lookup lasted %s seconds.\n" "$(duration "$time1" "$time2")" 1>&2 && \ cat "$device_cache_file" 1>&2 else device_cbs="$(sed -n -e "s;^\(immutable\|changeable\|undetectable\)=.*\$;\1;p" "$device_cache_file")" device_branding="$(sed -n -e "s;^\(immutable\|changeable\|undetectable\)=\(.*\)\$;\2;p" "$device_cache_file")" [ "$device_cbs" = "changeable" ] && device_branding_is_changeable="true" || device_branding_is_changeable="false" [ -n "$BM_DEBUG_EVA" ] && printf -- "Device configuration info loaded from cache file.\n" 1>&2 && cat "$device_cache_file" 1>&2 fi out_dev_config "$device_cbs" "$device_branding" "$device_branding_is_changeable" } ####################################################################################################### # # # prepare a temporary directory and a clean exit routine # # # ####################################################################################################### tempdir="$tmpdir/$$_$(date +%s)" readonly tempdir mkdir -p "$tempdir" 2>"$null" trap cleanup HUP EXIT INT TERM rc=127 ####################################################################################################### # # # check parameters and call requested function # # # ####################################################################################################### save_environment verbose=0 system_type="" is_puma_device=0 [ "$(( 0x80000000 ))" = "-2147483648" ] && shell_32bit_arithmetics=1 || shell_32bit_arithmetics=0 error_description="" error_code=0 case "$1" in ("is_supported") system_type="$(get_system_type)" is_dualboot_supported && rc=0 || rc=1 ;; ("is_blocked") [ -f $semafile ] && rc=1 || rc=0 ;; ("debug") shift verbose=0 while [ -n "$1" ]; do if [ "$1" = "verbose" ]; then verbose=1 elif [ "$1" = "color" ]; then color=1 elif [ "$1" = "nocolor" ]; then color=0 fi shift done if [ -z "$color" ] && [ -t 1 ]; then color=1 fi debug "yf_bootmanager version = " "yellow" "%s-%s" "$yf_bootmanager_version" "$yf_bootmanager_timestamp" system_type="$(get_system_type)" [ "$system_type" = "Puma6/7" ] && is_puma_device=1 || is_puma_device=0 delim_1="debug output of bootmanager script" delim_2="running system" delim_3="alternative system" delim_4="device configuration from EVA loader" max_delim=$(max ${#delim_1} ${#delim_2} ${#delim_3}) set | grep "^delim_[0-9]*=" 2>"$null" | \ while read -r ln; do lv="$(expr "$ln" : "[^=]*='\(.*\)'")" [ "${#lv}" -gt "$max_delim" ] && max_delim=${#lv} done datetime_mask="$(get_localized_datetime_mask "${Language:-de}")" # shellcheck disable=SC2046 [ "$verbose" -eq 1 ] && debug_delim "red" "%s" "$delim_4" eval "$(load_device_configuration)" [ "$verbose" -eq 1 ] && debug_delim "red" "%s" "$delim_1" rootfs_type="" rootfs_offset=0 rootfs_size=0 debug "system type = " "yellow" "%s" "$system_type" if [ -d "$fdt_base" ]; then model="$(get_fdt_value "$fdt_model")" [ -n "$model" ] && debug "model = " "yellow" "%s" "$(get_fdt_value "$fdt_model")" chipset="$(get_fdt_value "$fdt_compatible" | sed -n -e "s|\([^,]*\),\(.*\)|\1|p")" [ -n "$chipset" ] && debug "chipset manufacturer = " "yellow" "%s" "$chipset" comp="$(get_fdt_compatible_value)" [ -n "$comp" ] && debug "compatible = " "yellow" "%s" "$comp" fi debug "system selector = " "yellow" "%u" "$(get_system_switch)" debug "system is switched = " "yellow" "%s" "$(is_switched && printf -- '%s' "true" || printf -- '%s' "false")" debug "device branding = " "yellow" "%s" "$device_branding" debug "device branding is changeable = " "yellow" "%s" "$device_branding_is_changeable" debug "current branding = " "yellow" "%s" "$OEM" if ! is_fit_image_device; then if [ "$system_type" = "IPQ5018" ]; then debug "system in inactive slot is installed = " "yellow" "%s" "$(is_fit_image_present "$(get_partition "$(printf_ss "$fit_partition_name_mask" "$("$bootslot_tool" "get_other")")")" \ && printf -- '%s' "true" || printf -- '%s' "false")" [ "$verbose" -eq 1 ] && debug_delim "red" "%s" "$delim_2" else debug "inactive system is installed = " "yellow" "%s" "$(is_kernel_present "$(get_partition "$kernel_name" "$inactive_name")" && printf -- '%s' "true" || printf -- '%s' "false")" [ "$verbose" -eq 1 ] && debug_delim "red" "%s" "$delim_2" debug "active kernel = " "yellow" "%s" "$(get_partition "$kernel_name" "$active_name")" fi debug "active filesystem = " "yellow" "%s" "$(get_partition "$filesystem_name" "$active_name")" debug "active system version = " "yellow" "%s" "$(get_system_version)" else if [ -z "$BM_DEBUG_FIT_IMAGE" ]; then fp="$(printf_ss "$fit_partition_name_mask" "$("$bootslot_tool" get_other)")" fpd="$(get_partition "$fp")" else fpd="$BM_DEBUG_FIT_IMAGE" [ -z "$fpd" ] && printf -- "Missing FIT image source for FIT debugging.\a\n" 1>&2 && exit 1 fi fscopyname="fscopy-$$" eval "$(find_fit_inactive_rootfs_parameters "$fpd" "$fscopyname")" debug "inactive slot contains a FIT image = " "yellow" "%s" "$([ -n "$rootfs_type" ] && printf -- '%s' "true" || printf -- '%s' "false")" if [ -z "$BM_DEBUG_FIT_IMAGE" ]; then [ "$verbose" -eq 1 ] && debug_delim "red" "%s" "$delim_2" debug "active slot = " "yellow" "%s" "$(get_partition "$(printf_ss "$fit_partition_name_mask" "$("$bootslot_tool" get_active)")")" debug "active system version = " "yellow" "%s" "$(get_system_version)" fi fi if [ -z "$BM_DEBUG_FIT_IMAGE" ]; then value="$(get_system_date)" debug "active system date = " "yellow" "$(printf -- "%%s {green}(epoch: {yellow}%%s{green})" | colorize)" "$(get_localized_date "$datetime_mask" "$value")" "$value" modby="$(get_modified_by)" if [ "$modby" = "-" ]; then debug "active system modification source = " "yellow" "%s" "(unmodified or unknown)" else value="$(get_modified_date)" debug "active system modification date = " "yellow" "$(printf -- "%%s {green}(epoch: {yellow}%%s{green})" | colorize)" "$(get_localized_date "$datetime_mask" "$value")" "$value" debug "active system modification source = " "yellow" "%s" "$modby" fi cbs="$(change_branding_support)" debug "brandings supported on active system = " "yellow" "%s" "$(get_brandings "" "$cbs")" if ! [ "$cbs" = "changeable" ]; then debug "branding used by active system = " "yellow" "$(printf -- "%%s {green}({yellow}%%s{green})" | colorize)" "$(get_system_branding "" "$cbs")" "$cbs" fi fi [ "$verbose" -eq 1 ] && debug_delim "red" "%s" "$delim_3" if ! is_fit_image_device; then if ! [ "$system_type" = "IPQ5018" ]; then debug "inactive kernel = " "yellow" "%s" "$(get_partition "$kernel_name" "$inactive_name")" fi debug "inactive filesystem = " "yellow" "%s" "$(get_partition "$filesystem_name" "$inactive_name")" if [ "$system_type" = "IPQ5018" ]; then is_fit_image_present "$(get_partition "$(printf_ss "$fit_partition_name_mask" "$("$bootslot_tool" "get_other")")")" && present=1 || present=0 else is_kernel_present "$(get_partition "$kernel_name" "$inactive_name")" && present=1 || present=0 fi if [ "$present" -eq 1 ]; then spinlock_pid=$$ spinlock get 10 && mp="$(printf -- '%s' "$(mount_alternative_system)")" [ "$verbose" -eq 1 ] && debug "" "blue" "%s" "$(printf -- "inactive filesystem mounted on %s" "$mp")" if [ -n "$mp" ]; then debug "inactive system version = " "yellow" "%s" "$(get_system_version "$mp")" value="$(get_system_date "$mp")" debug "inactive system date = " "yellow" "$(printf -- "%%s {green}(epoch: {yellow}%%s{green})" | colorize)" "$(get_localized_date "$datetime_mask" "$value")" "$value" altmodby="$(get_modified_by "$mp")" if [ "$altmodby" = "-" ]; then debug "inactive system modification source = " "yellow" "%s" "(unmodified or unknown)" else value="$(get_modified_date "$mp")" debug "inactive system modification date = " "yellow" "$(printf -- "%%s {green}(epoch: {yellow}%%s{green})" | colorize)" "$(get_localized_date "$datetime_mask" "$value")" "$value" debug "inactive system modification source = " "yellow" "%s" "$altmodby" fi altcbs="$(change_branding_support "$mp")" debug "brandings supported on inactive system = " "yellow" "%s" "$(get_brandings "$mp" "$altcbs")" if ! [ "$altcbs" = "changeable" ]; then debug "branding used by inactive system = " "yellow" "$(printf -- "%%s {green}({yellow}%%s{green})" | colorize)" "$(get_system_branding "$mp" "$altcbs")" "$altcbs" fi dismount_alternative_system [ "$verbose" -eq 1 ] && debug "" "blue" "%s" "inactive filesystem dismounted" fi else debug "" "red" "%s" "inactive filesystem could not be mounted" fi spinlock release else debug "inactive slot = " "yellow" "%s" "$fpd" if [ -n "$rootfs_type" ]; then debug "rootfs_type = " "yellow" "%s" "$rootfs_type" debug "rootfs_offset = " "yellow" "%u (%#x)" "$rootfs_offset" "$rootfs_offset" debug "rootfs_size = " "yellow" "%u (%#x)" "$rootfs_size" "$rootfs_size" if ! [ "$alternative_is_missing" = "1" ]; then debug "inactive system version = " "yellow" "%s" "$alternative_version" value="$alternative_date_epoch" debug "inactive system date = " "yellow" "$(printf -- "%%s {green}(epoch: {yellow}%%s{green})" | colorize)" "$(get_localized_date "$datetime_mask" "$value")" "$value" altmodby="$alternative_modified" if [ "$altmodby" = "-" ]; then debug "inactive system modification source = " "yellow" "%s" "(unmodified or unknown)" else value="$alternative_fsdate_epoch" debug "inactive system modification date = " "yellow" "$(printf -- "%%s {green}(epoch: {yellow}%%s{green})" | colorize)" "$(get_localized_date "$datetime_mask" "$value")" "$value" debug "inactive system modification source = " "yellow" "%s" "$altmodby" fi altcbs="$alternative_cbs" debug "brandings supported on inactive system = " "yellow" "%s" "$alternative_brandings" if ! [ "$altcbs" = "changeable" ]; then debug "branding used by inactive system = " "yellow" "$(printf -- "%%s {green}({yellow}%%s{green})" | colorize)" "$alternative_branding" "$altcbs" fi else debug "" "red" "%s" "Unable to mount inactive filesystem and to extract data" fi else debug "" "red" "%s" "FIT image not found for inactive slot" fi fi [ -n "$error_description" ] && debug "saved error description = " "red" "%s" "$error_description" [ "$error_code" -ne 0 ] && debug "stored error code = " "red" "%s" "$error_code" rc=0 ;; ("show_eva_cfg") verbose=1 system_type="$(get_system_type)" [ "$system_type" = "Puma6/7" ] && is_puma_device=1 || is_puma_device=0 check_urlader_configuration exit 0 ;; ("get_values") system_type="$(get_system_type)" [ "$system_type" = "Puma6/7" ] && is_puma_device=1 || is_puma_device=0 # shellcheck disable=SC2046 eval $(load_device_configuration) if is_dualboot_supported; then if [ "$2" = "nocache" ] || ! [ -f "$data_cache_file" ]; then spinlock_pid=$$ collect_data if [ "$2" = "nocache" ]; then get_values else get_values >"$data_cache_file" fi fi [ "$2" = "nocache" ] || cat "$data_cache_file" 2>"$null" rc=0 else rc=1 fi ;; ("clear_cache") [ -f "$data_cache_file" ] && rm "$data_cache_file" 2>"$null" shift while [ -n "$1" ]; do [ "$1" = "with_eva_config" ] && [ -f "$device_cache_file" ] && rm "$device_cache_file" 2>"$null" shift done rc=0 ;; ("switch_to") # shellcheck disable=SC2046 eval $(load_device_configuration) switch_system_to "$2" "$3" rc=$? ;; ("-h" | "--help") boot_manager_usage rc=0 ;; (*) if [ -z "$1" ]; then boot_manager_usage 1>&2 rc=1 else printf -- "Unknown operation '%s'.\n" "$1" 1>&2 rc=1 fi ;; esac ####################################################################################################### # # # finish and regular exit # # # ####################################################################################################### exit $rc ####################################################################################################### # # # end of script # # # #######################################################################################################