Browse Source

Merge pull request #183 from filberg/master

Make kernel upgrades easier (both builds and flashing)
SolidHal 3 years ago
parent
commit
0c43abf765
2 changed files with 443 additions and 0 deletions
  1. 150 0
      scripts/InstallScripts/FlashKernelPart.sh
  2. 293 0
      scripts/LibKernelUpgrade.sh

+ 150 - 0
scripts/InstallScripts/FlashKernelPart.sh

@@ -0,0 +1,150 @@
+#!/bin/sh
+
+# This file is part of PrawnOS (http://www.prawnos.com)
+# Copyright (c) 2018 Hal Emmerich <hal@halemmerich.com>
+# Copyright (c) 2020 Fil Bergamo <fil@filberg.eu>
+
+# PrawnOS is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2
+# as published by the Free Software Foundation.
+
+# PrawnOS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with PrawnOS.  If not, see <https://www.gnu.org/licenses/>.
+
+# -----------------------------------------------
+#              STATIC CONFIGURATION
+# -----------------------------------------------
+#
+# eMMC device name
+emmc_devname=mmcblk2
+#
+# Blank kernel image
+blnk=./blank_kernel
+#
+# Actual kernel image (.kpart)
+kimg=./vmlinux.kpart
+#
+# GPT partition type UUID for "ChromeOS kernel"
+ptype_kernel="FE3A2A5D-4F32-41A7-B725-ACCC3285A309"
+#
+# Kernel partition number
+pnum_kernel=1
+# -----------------------------------------------
+
+# function die():
+# print an error message and exit with the provided code
+# $1 = error message
+# $2 = exit code
+die()
+{
+    redtxt='\033[0;31m'
+    printf "$redtxt$1\n"
+    exit $2
+}
+
+# function get_partition_type_uuid()
+# print the UUID of the gpt partition type
+# for the given partition number on the given device
+# $1 = device (e.g. /dev/mmcblk2)
+# $2 = partition number
+get_partition_type_uuid()
+{
+    cgpt show -i "$2" -t -n -q "$1" 2>/dev/null
+}
+
+# function get_root_partition()
+# print the actual physical device
+# currently mounted as "/"
+get_root_partition()
+{
+    rootfs=$(findmnt / -n -o SOURCE)
+
+    if echo "$rootfs" | grep -q "/dev/mapper" ;then
+	# root filesystem is on luks volume
+	# let's find the physical device behind it
+	rootfs=$(cryptsetup status "$rootfs" | grep "device: " | cut -d ' ' -f 5)
+    fi
+    echo "$rootfs"
+}
+
+
+# ------------- BEGIN SCRIPT EXECUTION --------------------
+
+set -e
+
+# Check root or sudo
+[ ! $(id -u) = 0 ] &&
+    die "Please run this script with sudo, or as root" 1
+
+rootfs=$(get_root_partition)
+devname=$(lsblk -no pkname $rootfs | head -n 1)
+devtype=
+kpart=
+
+case "$devname" in
+    $emmc_devname)
+	devtype="Internal eMMC"
+	kpart=/dev/${devname}p$pnum_kernel
+	;;
+    sd*)
+	devtype="USB Stick"
+	kpart=/dev/${devname}$pnum_kernel
+	;;
+    mmcblk[0-9])
+	devtype="External SD card"
+	kpart=/dev/${devname}p$pnum_kernel
+	;;
+    *)
+	die "FAILED to recognize booted device type $devname" 127
+esac
+
+# Check that the selected partition is effectively of type "kernel"
+ptype=$(get_partition_type_uuid /dev/$devname $pnum_kernel)
+[ ! "$ptype" = "$ptype_kernel" ] &&
+    die "FATAL ERROR: unexpected partitioning scheme found. Partition # $pnum_kernel is NOT the kernel partition!" 255
+
+# Check that the needed kernel images exist
+[ ! -e "$kimg" ] &&
+    die "ERROR: cannot find kernel image at $kimg !" 127
+
+[ ! -e "$blnk" ] &&
+    die "ERROR: cannot find blank image at $blnk !" 127
+
+
+# Prompt for user's confirmation
+echo "
+----------------------------
+ /!\\ !!! CAUTION !!! /!\\
+----------------------------
+This will flash a new kernel image onto the running device's kernel partition: $kpart
+The detected running device is: $devtype.
+DO NOT shutdown or reboot before completing the process!
+"
+printf "%s" "Are you REALLY sure you want to continue??? [y/N] "
+
+read ans
+
+[ "$ans" != "y" ] &&
+       [ "$ans" != "Y" ] &&
+       die "Aborted by user. Kernel untouched." 1
+
+# put the kernel in the kernel partition
+dd if="$blnk" of="$kpart" conv=notrunc ||
+    die "FAILED to flash blank kernel on $kpart!" 255
+
+dd if="$kimg" of="$kpart" conv=notrunc ||
+    die "FAILED to flash kernel image on $kpart!" 255
+
+echo "
+The new kernel image has been successfully flashed onto $kpart.
+It's now safe to reboot."
+
+# TODO: install modules. -----------------------------------
+# Right now, there's no easy way to do that on the running machine
+# -----------------------------------------------------------------
+# make -C build/linux-$KVER ARCH=arm INSTALL_MOD_PATH=$outmnt modules_install

+ 293 - 0
scripts/LibKernelUpgrade.sh

@@ -0,0 +1,293 @@
+#!/bin/bash
+
+# This file is part of PrawnOS (http://www.prawnos.com)
+# Copyright (c) 2018 Hal Emmerich <hal@halemmerich.com>
+# Copyright (c) 2020 Fil Bergamo <fil@filberg.eu>
+
+# PrawnOS is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2
+# as published by the Free Software Foundation.
+
+# PrawnOS is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with PrawnOS.  If not, see <https://www.gnu.org/licenses/>.
+
+
+# ------------------------------------------------------
+#                 STATIC CONFIGURATION
+# ------------------------------------------------------
+#
+# Upstream url to be checked for source updates
+# Gets variable-expanded by filling in the version string "w.x.y"
+# through funcion expand_version_pattern()
+# Here, VER means the main version number,
+#       MAJ the major revision number,
+#       MIN the minor revision number
+# (e.g. "LATEST-VER.MAJ.N" + becomes "LATEST-5.4.N" for "5.4.43")
+src_url="https://linux-libre.fsfla.org/pub/linux-libre/releases/LATEST-VER.MAJ.N"
+#
+# Timeout, in seconds, for remote operations (rsyc, wget..)
+remote_timeout=15
+#
+# Number of retires after a remote operation times out
+remote_retries=2
+#
+# Seconds to wait before retrying failed remote operations
+remote_wait_retry=5
+#
+# The tool to be used to search for updates
+# can only choose between "rsync" and "wget"
+remote_tool="wget"
+#
+# The naming pattern of the actual source archive
+# as found in ${src_url}/${src_tar_pattern}
+# follows the same variable expansion as $src_url
+src_tar_pattern="linux-libre-VER.MAJ.MIN-gnu.tar.lz"
+#
+# Then naming pattern of the signature for the source archive
+src_tar_sig_pattern="${src_tar_pattern}.sign"
+#
+# The log file path (leave empty to disable logging)
+logfile=
+# ------------------------------------------------------
+
+log()
+{
+    [ ! -z "$logfile" ] &&
+	echo "$(date +'%Y-%m-%d %H:%M:%S') $1" >> $logfile
+}
+
+print_err()
+{
+    printf "\033[0;31m%s\n" "[ERR] $1" 1>&2
+    log "[ERR] $1"
+}
+
+print_warn()
+{
+    echo "[WRN] $1" 1>&2
+    log "[WRN] $1"
+}
+
+print_msg()
+{
+    echo "[INF] $1"
+    log "[INF] $1"
+}
+
+die()
+{
+    print_err "$1"
+    [ $# -gt 1 ] && exit $2
+    exit 255
+}
+
+# function expand_version_pattern()
+# Replace VER, MAJ, MIN in the given pattern
+#   with the given 'w', 'x' and 'y' values.
+# $1 = pattern
+# $2 = main version number ('w')
+# $3 = major revision number ('x')
+# $4 = minor revision number ('y')
+expand_version_pattern()
+{
+    expanded=${1//VER/$2}
+    expanded=${expanded//MAJ/$3}
+    echo ${expanded//MIN/$4}
+}
+
+
+# function get_running_kver()
+# Print the upstream version of the currently running kernel
+# i.e. < w.x[.y] > without any trailing distro-specific version
+get_running_kver()
+{
+    uname -r | cut -d '-' -f 1
+}
+
+
+# function split_version_string()
+# Split a 'w.x.y' version string into separate values.
+# Set $PRAWNOS_KVER_VER with the main version number ('w').
+# Set $PRAWNOS_KVER_MAJ wiht the major revision number ('x').
+# Set $PRAWNOS_KVER_MIN with the minor revision number ('y').
+# Return 0 on success, return 1 on failure.
+# $1 = version string in the 'w.x.y' format
+split_version_string()
+{
+#    ver=$1
+#    IFS='.' read -ra vscheme <<< "$ver"
+#    PRAWNOS_KVER_VER=${vscheme[0]}
+#    PRAWNOS_KVER_MAJ=${vscheme[1]}
+#    PRAWNOS_KVER_MIN=${vscheme[2]}
+
+    anynum="[0-9]{1,}"
+    match_ver="($anynum)\.($anynum)\.($anynum)"
+
+    if [[ $target =~ $search_fmt ]];then
+	PRAWNOS_KVER_VER=${BASH_REMATCH[1]}
+	PRAWNOS_KVER_MAJ=${BASH_REMATCH[2]}
+	PRAWNOS_KVER_MIN=${BASH_REMATCH[3]}
+	return 0
+    else
+	return 1
+    fi
+}
+
+# function rsync_list_remote()
+# List contents of remote directory via rsync.
+# Return rsync's exit code.
+# $1 = remote rsync url to be listed
+rsync_list_remote()
+{
+    rsync --timeout=$remote_timeout --list-only "$1" 2>&1
+    ec=$?
+
+    # rsync exit codes 30, 35 = timeout
+    case $ec in
+	30|35)
+	    print_warn "rsync timeout while connecting to remote server"
+	    if [ $remote_retries -lt 1 ];then
+		return $ec
+	    fi
+	    export remote_retries=$(( remote_retries - 1 ))
+	    print_warn "Waiting $remote_wait_retry seconds before retrying"
+	    sleep $remote_wait_retry
+	    rsync_list_remote $@
+	    return $?
+	    ;;
+	*)
+	    return $ec
+	    ;;
+    esac
+}
+
+# function rsync_search_url_pattern()
+# Search $src_url for the presence of a file
+#   that matches $src_tar_pattern.
+# Print the bare file name matching the search.
+# $1 = main version number ('w')
+# $2 = major revision number ('x')
+rsync_search_url_pattern()
+{
+    file_pattern=$(expand_version_pattern \
+		       "$src_url/$src_tar_pattern" $1 $2 "*")
+
+    print_msg "Attempting to list remote file pattern $file_pattern..."
+    file=$(rsync_list_remote $file_pattern)
+    ec=$?
+    if [ $ec -ne 0 ];then
+	print_err "Failed to list remote file pattern"
+	print_err "Output from rsync:"
+	print_err "$file"
+	return $ec
+    fi
+
+    fields=( $file )
+    echo ${fields[-1]}
+}
+
+# function wget_search_url_pattern()
+# Search $src_url for an 'href' pointing to a file
+#   that matches $src_tar_pattern.
+# Print the bare file name matching the search.
+# $1 = main version number ('w')
+# $2 = major revision number ('x')
+wget_search_url_pattern()
+{
+    baseurl=$(expand_version_pattern "$src_url" "$1" "$2")
+    outfile=$(tempfile)
+
+    wget --timeout=$remote_timeout \
+	 --tries=$(( $remote_retries + 1 )) \
+	 --output-document="$outfile" \
+	 "$baseurl"
+
+    ec=$?
+    if [ $? -ne 0 ];then
+	print_err "Failed to download url with wget."
+	return $ec
+    fi
+
+    search_string=$(expand_version_pattern $src_tar_pattern "$1" "$2" "*")
+
+    grep -o "href=\"$search_string\"" "$outfile" | grep -o "$search_string"
+
+    rm "$outfile"
+}
+
+# function parse_value_from_template()
+# Match "$target" against "$template" and print
+#   the first occurrence in "$target"
+#   that matches the "$placeholder" part in "$template"
+# i.e. parse "$placeholder" out of "$target" using "$template"
+# $1 = template
+# $2 = target
+# $3 = placeholder
+# $4 = matching pattern
+parse_value_from_template()
+{
+    template=$1
+    target=$2
+    phold=$3
+
+    # replace $phold with a matching group in $template
+    search_fmt=$(sed "s/$phold/($4)/g" <<< $template)
+
+    [[ $target =~ $search_fmt ]] &&
+	echo ${BASH_REMATCH[1]}
+}
+
+# function parse_version_from_filename()
+# Extract the version string from the given filename
+#   and print it to stdout
+# $1 = filename matching $src_tar_pattern
+parse_version_from_filename()
+{
+    if [ -z "$1" ];then
+	print_err "Failed parsing version: empty file name received;"
+	return 1
+    fi
+
+    anynum="[0-9]{1,}"
+
+    set -x
+    template_ver=$(expand_version_pattern "$src_tar_pattern" "VER" "$anynum" "$anynum")
+    template_maj=$(expand_version_pattern "$src_tar_pattern" "$anynum" "MAJ" "$anynum")
+    template_min=$(expand_version_pattern "$src_tar_pattern" "$anynum" "$anynum" "MIN")
+
+    ver=$(parse_value_from_template "$template_ver" "$1" "VER" "$anynum")
+    maj=$(parse_value_from_template "$template_maj" "$1" "MAJ" "$anynum")
+    min=$(parse_value_from_template "$template_min" "$1" "MIN" "$anynum")
+
+    echo "${ver}.${maj}.${min}"
+}
+
+# function get_latest_src_kver()
+# Check the upstream url for the latest source release.
+# Print the latest version available in the format 'w.x.y'
+# $1 = main version number ('w')
+# $2 = major revision number ('x')
+get_latest_src_kver()
+{
+    case $remote_tool in
+	 wget)
+	     fname=$(wget_search_url_pattern $1 $2)
+	     ;;
+	 rsync)
+	     fname=$(rsync_search_url_pattern $1 $2)
+	     ;;
+	 *)
+	     print_err "Unknown remote tool: $remote_tool"
+	     return 1
+    esac
+
+    echo $fname
+
+    parse_version_from_filename "$fname"
+}
+