diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b7efcb4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "test/bats"] + path = test/bats + url = https://github.com/bats-core/bats-core.git +[submodule "test/test_helper/bats-support"] + path = test/test_helper/bats-support + url = https://github.com/bats-core/bats-support.git +[submodule "test/test_helper/bats-assert"] + path = test/test_helper/bats-assert + url = https://github.com/bats-core/bats-assert.git diff --git a/ReadMe.md b/ReadMe.md index e0bf756..793177c 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -2,7 +2,7 @@ # Docker real-time guide for `PREEMPT_RT` -Author: [Tobit Flatscher](https://github.com/2b-t) (August 2021 - April 2022) +Author: [Tobit Flatscher](https://github.com/2b-t) (August 2021 - July 2022) @@ -35,28 +35,30 @@ The manual set-up of `PREEMPT_RT` takes quite a while (see [`doc/realtime_basics ### 1.1 Installing `PREEMPT_RT` -The installation procedure either by compilation from source or from an existing [Debian package](https://packages.debian.org/buster/linux-image-rt-amd64) is lined out in [`doc/realtime_basics/PreemptRt.md`](./doc/realtime_basics/PreemptRt.md). The same procedure can also be performed with the provided scripts [`install_debian_preemptrt.sh`](./install_debian_preemptrt) and [`compile_kernel_preemptrt.sh`](./compile_kernel_preemptrt.sh). +The installation procedure either by compilation from source or from an existing [Debian package](https://packages.debian.org/buster/linux-image-rt-amd64) is lined out in [`doc/realtime_basics/PreemptRt.md`](./doc/realtime_basics/PreemptRt.md). The same procedure can also be performed with the provided scripts [`src/install_debian_preemptrt`](./src/install_debian_preemptrt) and [`src/compile_kernel_preemptrt`](./src/compile_kernel_preemptrt). -[`install_debian_preemptrt.sh`](./install_debian_preemptrt) checks online if there are already precompiled `PREEMPT_RT` packages available and lets you select a suiting version graphically, while [`compile_kernel_preemptrt.sh`](./compile_kernel_preemptrt.sh) compiles the kernel from scratch from you and installs it. Before using the scripts be sure to make them executable on your system with `$ sudo chmod +x install_debian_preemptrt.sh`. +[`src/install_debian_preemptrt`](./src/install_debian_preemptrt) checks online if there are already precompiled `PREEMPT_RT` packages available and lets you select a suiting version graphically, while [`src/compile_kernel_preemptrt`](./src/compile_kernel_preemptrt) compiles the kernel from scratch from you and installs it. Before using the scripts be sure to make them executable on your system with `$ sudo chmod +x install_debian_preemptrt`. #### 1.1.1 Installation from pre-compiled Debian package (recommended) -Start of by launching [`install_debian_preemptrt.sh`](./install_debian_preemptrt) and follow the installation instructions +Start of by launching [`src/install_debian_preemptrt`](./src/install_debian_preemptrt) and follow the installation instructions ```shell -$ ./install_debian_preemptrt.sh +$ cd src +$ ./install_debian_preemptrt ``` Afterwards you can reboot your system (be sure to select the correct kernel!) and should already be ready to go. You can check the kernel version with `$ uname -r` to verify that you are using the correct kernel and the installation was indeed successful. #### 1.1.2 Compilation of the kernel -If the installation above fails or for some good reason you have to compile the kernel yourself you can use the [`compile_kernel_preemptrt.sh`](./compile_kernel_preemptrt.sh) script. +If the installation above fails or for some good reason you have to compile the kernel yourself you can use the [`src/compile_kernel_preemptrt`](./src/compile_kernel_preemptrt) script. You can launch it in two different ways: ```shell -$ ./compile_kernel_preemptrt.sh +$ cd src +$ ./compile_kernel_preemptrt ``` will install the required dependencies and then open a dialog which lets you browse the possible versions and available options manually, reducing the copy and paste operations. @@ -64,7 +66,8 @@ will install the required dependencies and then open a dialog which lets you bro If you supply a correct real-time patch version from the list of available ones as an input argument, launching the command with superuser privileges it will download all files, patch the kernel, create a Debian package if no official one is available and install it automatically. ```shell -$ sudo ./compile_kernel_preemptrt.sh 5.10.78-rt55 +$ cd src +$ sudo ./compile_kernel_preemptrt 5.10.78-rt55 ``` This might be helpful for deploying a new kernel automatically on a remote system. The possible version numbers can be found at [here](https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/). diff --git a/compile_kernel_preemptrt.sh b/compile_kernel_preemptrt.sh deleted file mode 100755 index 3ed1e7b..0000000 --- a/compile_kernel_preemptrt.sh +++ /dev/null @@ -1,254 +0,0 @@ -#!/bin/bash -# Script for compiling a kernel with the PREEMPT_RT real-time patch with a simple graphical interface -# Tobit Flatscher - github.com/2b-t (2022) -# -# Usage: -# - '$ ./patch_kernel_preemprt.sh' and go through with a graphical user interface -# - Select a PREEMPT_RT version at https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/" -# and compile with '$ sudo ./patch_kernel_preemprt.sh 5.10.78-rt55' - - -# Install required dependencies -function install_dependencies { - echo "Installing dependencies..." - sudo apt-get install -y grep curl sed - sudo apt-get install -y build-essential bc ca-certificates gnupg2 libssl-dev lsb-release libelf-dev bison flex dwarves zstd libncurses-dev dpkg-dev - echo "Dependencies installed successfully!" -} - -# Get major versions by crawling website -function get_preemptrt_major_versions { - echo $(curl -Ls https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt | grep -o -P '(?<=href\=\")(\d\.\d*)+(?=/\">)') -} - -# Select the desired major version with a user dialog -function select_preemptrt_major_version { - local PREEMPTRT_MAJOR_VERSIONS=$(get_preemptrt_major_versions) - local DIALOG_PREEMPTRT_MAJOR_VERSIONS="" - for VER in ${PREEMPTRT_MAJOR_VERSIONS} - do - DIALOG_PREEMPTRT_MAJOR_VERSIONS="${DIALOG_PREEMPTRT_MAJOR_VERSIONS} ${VER} ${VER}" - done - CURRENT_KERNEL_VERSION=$(uname -r | sed 's/\.[^\.]*//2g') - echo $(dialog --no-tags --stdout --default-item ${CURRENT_KERNEL_VERSION} --menu "Select a major kernel version:" 30 40 10 ${DIALOG_PREEMPTRT_MAJOR_VERSIONS}) -} - -# Get the full versions by crawling website -function get_preemptrt_full_versions { - echo $(curl -Ls https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/${PREEMPT_RT_VER} | grep -o -P '(?<=href\=\"patch-).*(?=.patch.gz\">)') -} - -# Select the desired major version with a user dialog -function select_preemptrt_full_version { - local PREEMPTRT_FULL_VERSIONS=$(get_preemptrt_full_versions) - local DIALOG_PREEMPTRT_FULL_VERSIONS="" - for VER in ${PREEMPTRT_FULL_VERSIONS} - do - DIALOG_PREEMPTRT_FULL_VERSIONS="${DIALOG_PREEMPTRT_FULL_VERSIONS} ${VER} ${VER}" - done - echo $(dialog --no-tags --stdout --menu "Select the desired version of PREEMPT_RT:" 30 40 10 ${DIALOG_PREEMPTRT_FULL_VERSIONS}) -} - -# Reconstruct the corresponding kernel major version -function reconstruct_kernel_major_version { - local KERNEL_MAJOR_VERSION=$(echo "${PREEMPT_RT_VER}" | grep -o -P '^\s*(\d)+') - echo "$(curl -Ls https://www.kernel.org/pub/linux/kernel | grep -o -P "(?<=href\=\")(v${KERNEL_MAJOR_VERSION}.*)(?=/\">)")" -} - -# Download and extract the vanilla kernel -function download_and_extract_kernel { - echo "Downloading kernel '${KERNEL_VER_FULL}'..." - curl -SLO --fail https://www.kernel.org/pub/linux/kernel/${KERNEL_VER}/linux-${KERNEL_VER_FULL}.tar.xz - curl -SLO --fail https://www.kernel.org/pub/linux/kernel/${KERNEL_VER}/linux-${KERNEL_VER_FULL}.tar.sign - xz -d linux-${KERNEL_VER_FULL}.tar.xz -} - -# Download and extract the PREEMPT_RT patch -function download_and_extract_preemptrt { - echo "Downloading PREEMPT_RT patch '${PREEMPT_RT_VER_FULL}'..." - curl -SLO --fail https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/${PREEMPT_RT_VER}/patch-${PREEMPT_RT_VER_FULL}.patch.xz - curl -SLO --fail https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/${PREEMPT_RT_VER}/patch-${PREEMPT_RT_VER_FULL}.patch.sign - xz -d patch-${PREEMPT_RT_VER_FULL}.patch.xz -} - -# Sign the kernel and the patch -function sign_kernel_and_preemptrt { - echo "Signing keys..." - # Catch non-zero exit code despite "set -e", see https://stackoverflow.com/a/57189853 - if gpg2 --verify linux-${KERNEL_VER_FULL}.tar.sign; then - : - else - local KERNEL_KEY=$(gpg2 --verify linux-${KERNEL_VER_FULL}.tar.sign 2>&1 | grep -o -P '(?<=RSA key )(.*)') - gpg2 --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys ${KERNEL_KEY} - gpg2 --verify linux-${KERNEL_VER_FULL}.tar.sign - fi - - if gpg2 --verify patch-${PREEMPT_RT_VER_FULL}.patch.sign; then - : - else - local PREEMPT_KEY=$(gpg2 --verify patch-${PREEMPT_RT_VER_FULL}.patch.sign 2>&1 | grep -o -P '(?<=RSA key )(.*)') - gpg2 --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys ${PREEMPT_KEY} - gpg2 --verify patch-${PREEMPT_RT_VER_FULL}.patch.sign - fi -} - -# Apply the patch to the kernel -function apply_patch_to_kernel { - echo "Applying PREEMPT_RT patch '${PREEMPT_RT_VER_FULL}' to kernel '${KERNEL_VER_FULL}'..." - tar xf linux-${KERNEL_VER_FULL}.tar - cd linux-${KERNEL_VER_FULL} - patch -p1 < ../patch-${PREEMPT_RT_VER_FULL}.patch -} - -# Generate a new kernel configuration -function generate_new_kernel_configuration { - # Only option set in the configuration is full PREEMPT_RT, rest is left on default - echo "Generating kernel configuration" - echo "4" | make oldconfig -} - -# Function for finding a certain setting, replacing it or adding it if not existing -# @param $1 The name of the setting -# @param $2 its desired value -function find_and_replace_in_config { - local CONFIG_FILE=".config" - grep -E "$1=" $CONFIG_FILE && sed -i "s/$1=.*/$1=$2/" $CONFIG_FILE || echo $1"="$2 >> ${CONFIG_FILE} -} - -# Function for commenting out certain settings in the configuration files -# @param $1 The name of the setting to be commented out -function comment_out_in_config { - local CONFIG_FILE=".config" - sed -E "/$1/ s/^#*/#/" -i ${CONFIG_FILE} -} - -# Unsign the kernel configuration -function unsign_kernel_configuration { - echo "Forcing unsigned kernel..." - find_and_replace_in_config "CONFIG_SYSTEM_TRUSTED_KEYS" '""' - find_and_replace_in_config "CONFIG_SYSTEM_REVOCATION_KEYS" '""' -} - -# Select the installation modality -function select_installation_mode { - echo $(dialog --stdout --default-item "Debian package" --menu "Select the desired installation mode:" 30 40 10 "Debian" "Debian package" "Classic" "Install directly") -} - -# Generate a debian package for easier installation -function generate_preemptrt_kernel_debian_package { - echo "Generating Debian package..." - sudo make -j$(nproc) deb-pkg -} - -# Function for deciding whether to install the kernel now or install it later -function select_install_now { - dialog --stdout --title "Install Debian package" --yesno "Want to install the Debian package now" 7 60 - echo $? -} - -# Install PREEMPT_RT from the Debian package -function install_preemptrt_kernel_debian_package { - echo "Installing Debian package..." - sudo dpkg -i ../linux-image-${PREEMPT_RT_VER_FULL}_${PREEMPT_RT_VER_FULL}-1_$(dpkg --print-architecture).deb -} - -# Install the kernel directly without creating a Debian package first -function install_preemptrt_kernel_directly { - echo "Installing in the classic way..." - sudo make -j$(nproc) - sudo make modules_install -j$(nproc) - sudo make install -j$(nproc) -} - -# Install and let the user decide the kernel etc. -function install_kernel_interactive { - { - sudo apt-get install -y dialog - install_dependencies - } || { - echo "Warning: Could not install dependencies. Installation might fail!" - } - - # Get patch version via dialog - local PREEMPT_RT_VER=$(select_preemptrt_major_version) # e.g. 5.10 - local PREEMPT_RT_VER_FULL=$(select_preemptrt_full_version) # e.g. 5.10.78-rt55 - local KERNEL_VER=$(reconstruct_kernel_major_version) # e.g. v5.x - local KERNEL_VER_FULL=$(echo "${PREEMPT_RT_VER_FULL}" | sed 's/-rt.*//g') # e.g. 5.10.78 - - # Download and extract the files for kernel and patch - download_and_extract_kernel - download_and_extract_preemptrt - sign_kernel_and_preemptrt - - # Apply patch to kernel - apply_patch_to_kernel - generate_new_kernel_configuration - unsign_kernel_configuration - - # Choose between Debian package and installing directly - local INSTALLATION_MODE=$(select_installation_mode) - if [ "${INSTALLATION_MODE}" == "Debian" ] - then - generate_preemptrt_kernel_debian_package - INSTALL_NOW=$(select_install_now) - if [ "${INSTALL_NOW}" -eq 0 ] - then - install_preemptrt_kernel_debian_package - echo "Done: Installation with Debian package completed!" - else - echo "Done: Debian package generated!" - fi - else - install_preemptrt_kernel_directly - echo "Done: Classic installation complete!" - fi -} - -# Installs the kernel as Debian package from a given command-line argument without interactive choices -function install_kernel_noninteractive { - { - install_dependencies - } || { - echo "Warning: Could not install dependencies. Installation might fail!" - } - - local PREEMPT_RT_VER_FULL=$1 # e.g. 5.10.78-rt55 - local KERNEL_VER_FULL=$(echo "${PREEMPT_RT_VER_FULL}" | sed 's/-rt.*//g') # e.g. 5.10.78 - local PREEMPT_RT_VER=$(echo "${KERNEL_VER_FULL}" | sed 's/\.[^\.]*//2g') # e.g. 5.10 - local KERNEL_VER="v"$(echo "${PREEMPT_RT_VER}" | sed -n 's/\(.*\)[.]\(.*\)/\1.x/p') # e.g. v5.x - - download_and_extract_kernel - download_and_extract_preemptrt - sign_kernel_and_preemptrt - apply_patch_to_kernel - generate_new_kernel_configuration - unsign_kernel_configuration - generate_preemptrt_kernel_debian_package - install_preemptrt_kernel_debian_package - echo "Done: Installation with Debian package completed!" -} - - -# Install the kernel either in an interactive or non-interactive way depending if an input argument is given -function main { - set -e # Exit immediately in case of failure - if [ "$#" -eq 0 ] - then - install_kernel_interactive - elif [ "$#" -eq 1 ] && [ "${EUID}" -eq 0 ] - then - install_kernel_noninteractive $@ - else - echo "Error: Could not run installation script!" - echo "Either launch it as: " - echo " - '$ ./patch_kernel_preemprt.sh'" - echo " - '$ sudo ./patch_kernel_preemprt.sh 5.10.78-rt55'" - echo "For the available PREEMPT_RT versions see: " - echo "see https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/" - exit 1 - fi - exit 0 -} - -main $@ - diff --git a/doc/realtime_basics/PreemptRt.md b/doc/realtime_basics/PreemptRt.md index db78d2f..c616fd6 100644 --- a/doc/realtime_basics/PreemptRt.md +++ b/doc/realtime_basics/PreemptRt.md @@ -14,11 +14,11 @@ The `PREEMPT_RT` kernels are known for causing a headache with the **official Nv The installation of `PREEMPT_RT` can be performed either by installing the patch from an existing **Debian package** or by re-compiling the kernel yourself. While the first option should be preferred for having a more detailed control over the kernel set-up latter might still be desired or might be your only options in rare cases of lacking compatibility. -In the main folder of this I have provided two **scripts `install_debian_preemptrt.sh` and `compile_kernel_preemptrt.sh`** which is able to install either from an existing Debian package or by recompiling the kernel automatically. This means it should not be necessary to perform this steps manually. Nonetheless I will leave this here as a reference! +In the main folder of this I have provided two **scripts `src/install_debian_preemptrt` and `src/compile_kernel_preemptrt`** which is able to install either from an existing Debian package or by recompiling the kernel automatically. This means it should not be necessary to perform this steps manually. Nonetheless I will leave this here as a reference! #### 1.2.1 Installation from Debian package (recommended) -The installation from a Debian package is way **simpler** than the recompilation listed below but is at the same time **less flexible** as it is only available for a limited number of kernels and kernel configurations. You might not be able to make the Debian package work with your particular system and might have to re-compile the kernel anyways. Nonetheless it is **highly advised** that you follow these simple steps before turning to a full re-compilation of the kernel. The script that already performs the steps for you is `install_debian_preemptrt.sh`. +The installation from a Debian package is way **simpler** than the recompilation listed below but is at the same time **less flexible** as it is only available for a limited number of kernels and kernel configurations. You might not be able to make the Debian package work with your particular system and might have to re-compile the kernel anyways. Nonetheless it is **highly advised** that you follow these simple steps before turning to a full re-compilation of the kernel. The script that already performs the steps for you is `src/install_debian_preemptrt`. Have a look at the search results resulting from [this query on package.debian.org](https://packages.debian.org/search?keywords=linux-image-rt-amd64) (potentially changing the architecture!) and see if you can find a kernel close to yours, e.g. [this one](https://packages.debian.org/bullseye/linux-image-rt-amd64). If you can find one click on the architecture `amd64` under `Download linux-image-rt-amd64` on the bottom and select a geographically suiting mirror and save the image in a location of your choice. @@ -33,7 +33,7 @@ Jump to section 2.1.3 and then try to reboot. In case it does not work you will #### 1.2.2 Re-compilation of the kernel -The re-compilation of the kernel is described in the [official Ubuntu installation guide](https://help.ubuntu.com/lts/installation-guide/amd64/install.en.pdf#page=98) as well as on the [Franka Emika installation guide](https://frankaemika.github.io/docs/installation_linux.html#setting-up-the-real-time-kernel) page but [might depend on the precise version](https://stackoverflow.com/a/51709420). In case you are running into issues you might have to consider [this](https://askubuntu.com/a/1338150) and [this](https://askubuntu.com/a/1329625) post. As already mentioned the script `compile_kernel_preemptrt.sh` does the steps that are listed in this section. Therefore it should not be necessary for you to do the following steps manually! +The re-compilation of the kernel is described in the [official Ubuntu installation guide](https://help.ubuntu.com/lts/installation-guide/amd64/install.en.pdf#page=98) as well as on the [Franka Emika installation guide](https://frankaemika.github.io/docs/installation_linux.html#setting-up-the-real-time-kernel) page but [might depend on the precise version](https://stackoverflow.com/a/51709420). In case you are running into issues you might have to consider [this](https://askubuntu.com/a/1338150) and [this](https://askubuntu.com/a/1329625) post. As already mentioned the script `src/compile_kernel_preemptrt` does the steps that are listed in this section. Therefore it should not be necessary for you to do the following steps manually! Start by installing the Debian packages required for the re-compilation. For a Debian-based Linux distribution this can be done conveniently with: diff --git a/docker/Dockerfile b/docker/Dockerfile index 072c392..80db712 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -17,6 +17,11 @@ RUN apt-get update && apt-get install -y \ python3 \ python3-distutils \ gnuplot-qt +# For unit-testing bash +RUN apt-get update && apt-get install -y \ + bats \ + dialog \ + xdotool # Fetch most recent version of rt-tests and mklatencyplot RUN cd ${WS_DIR} \ diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index f0cdb9d..1d32782 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -7,4 +7,6 @@ services: tty: true privileged: true network_mode: host - + volumes: + - ../src:/benchmark/src + - ../test:/benchmark/test diff --git a/install_debian_preemptrt.sh b/install_debian_preemptrt.sh deleted file mode 100755 index 056a869..0000000 --- a/install_debian_preemptrt.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# This script searches https://packages.debian.org/ for kernels with the PREEMPT_RT patch and allows the user to choose a desired version interactively -# This should be preferred over the re-compilation of the Linux kernel -# Tobit Flatscher - github.com/2b-t (2022) - - -# Install the kernel from the available pre-compiled packages online -function main { - set -e # Exit immediately in case of failure - sudo apt-get install -y dialog - sudo apt-get install -y coreutils curl grep dpkg-dev - - # Select Debian version - POSSIBLE_DEBIAN_VERSIONS=$(cat /etc/debian_version | tr / " ") - local DIALOG_POSSIBLE_DEBIAN_VERSIONS="" - for VER in ${POSSIBLE_DEBIAN_VERSIONS} - do - PREEMPTRT_FILE=$(curl -Ls https://packages.debian.org/${VER}/$(dpkg --print-architecture)/linux-image-rt-$(dpkg --print-architecture)/download | grep -o -P '(?<=

Download Page for )(linux-image-rt.*)(?=<\/kbd>)') - DIALOG_POSSIBLE_DEBIAN_VERSIONS="${DIALOG_POSSIBLE_DEBIAN_VERSIONS} ${VER} ${PREEMPTRT_FILE}" - done - DEBIAN_VERSION=$(dialog --stdout --menu "Select the desired PREEMPT_RT kernel version:" 30 70 10 ${DIALOG_POSSIBLE_DEBIAN_VERSIONS}) - - # Select download server - DOWNLOAD_SERVERS=$(curl -Ls https://packages.debian.org/${DEBIAN_VERSION}/$(dpkg --print-architecture)/linux-image-rt-$(dpkg --print-architecture)/download | grep -o -P '(?<=
  • )') - local DIALOG_DOWNLOAD_SERVERS="" - for VER in ${DOWNLOAD_SERVERS} - do - DIALOG_DOWNLOAD_SERVERS="${DIALOG_DOWNLOAD_SERVERS} ${VER} ${VER}" - done - local DOWNLOAD_FILE_LOCATION=$(dialog --no-tags --stdout --menu "Select a download server:" 30 70 10 ${DIALOG_DOWNLOAD_SERVERS}) - - # Download the Debian package - DOWNLOADED_FILE=$(echo "${DOWNLOAD_FILE_LOCATION}" | rev | cut -d'/' -f 1 | rev) - echo "Downloading Debian package ${DOWNLOADED_FILE} from ${DOWNLOAD_FILE_LOCATION}..." - curl -SLO --fail ${DOWNLOAD_FILE_LOCATION} - - # Install Debian package - echo "Installing Debian package ${DOWNLOADED_FILE}..." - sudo dpkg -i ${DOWNLOADED_FILE} - sudo apt-get install -f - echo "Done: Installation from Debian package completed!" - exit 0 -} - -main $@ - diff --git a/src/compile_kernel_preemptrt b/src/compile_kernel_preemptrt new file mode 100755 index 0000000..9f6f27a --- /dev/null +++ b/src/compile_kernel_preemptrt @@ -0,0 +1,104 @@ +#!/bin/bash +# Script for compiling a kernel with the PREEMPT_RT real-time patch with a simple graphical interface +# Tobit Flatscher - github.com/2b-t (2022) +# +# Usage: +# - '$ ./compile_kernel_preemptrt' and go through with a graphical user interface +# - Select a PREEMPT_RT version at https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/" +# and compile with '$ sudo ./compile_kernel_preemptrt 5.10.78-rt55' + + +function install_kernel_interactive() { + declare desc="Install the kernel with a graphic user menu and let the user decide which version to choose" + { + info_msg "Installing dependencies..." + sudo apt-get install -y dialog + install_dependencies + success_msg "Dependencies installed successfully!" + } || { + warning_msg "Warning: Could not install dependencies. Installation might fail!" + } + + local PREEMPTRT_FULL_VERSION=$(select_preemptrt) + local KERNEL_FULL_VERSION=$(extract_kernel_full_version "${PREEMPTRT_FULL_VERSION}") + + info_msg "Downloading and extracting kernel and patch..." + local KERNEL_FILE=$(download_and_extract_kernel "${KERNEL_FULL_VERSION}") + local PREEMPTRT_FILE=$(download_and_extract_preemptrt "${PREEMPTRT_FULL_VERSION}") + success_msg "Kernel and patch downloaded and extracted successfully!" + + info_msg "Applying patch to kernel..." + apply_patch_to_kernel "${PREEMPTRT_FILE}" "${KERNEL_FILE}" + generate_new_kernel_configuration + success_msg "Patch applied successfully!" + info_msg "Forcing unsigned kernel..." + unsign_kernel_configuration + + local INSTALLATION_MODE=$(select_installation_mode) + if [ "${INSTALLATION_MODE}" == "Debian" ] + then + generate_preemptrt_kernel_debian_package + IS_INSTALL_NOW=$(select_install_now) + if [ "${IS_INSTALL_NOW}" -eq 0 ] + then + install_preemptrt_kernel_debian_package "${PREEMPTRT_FULL_VERSION}" + success_msg "Done: Installation with Debian package completed!" + else + warning_msg "Done: Debian package generated successfully but installation skipped!" + fi + else + install_preemptrt_kernel_directly + success_msg "Done: Classic installation complete!" + fi +} + +function install_kernel_noninteractive() { + declare desc="Install the PREEMPT_RT kernel by patching a kernel and installing it as a Debian package from a given command-line argument (e.g. 5.10.78-rt55) without interactive choices" + { + info_msg "Installing dependencies..." + install_dependencies + success_msg "Dependencies installed successfully!" + } || { + warning_msg "Warning: Could not install dependencies. Installation might fail!" + } + + local PREEMPTRT_FULL_VERSION=$1 + local KERNEL_FULL_VERSION=$(extract_kernel_full_version "${PREEMPTRT_FULL_VERSION}") + + local KERNEL_FILE=$(download_and_extract_kernel "${KERNEL_FULL_VERSION}") + local PREEMPTRT_FILE=$(download_and_extract_preemptrt "${PREEMPTRT_FULL_VERSION}") + apply_patch_to_kernel "${PREEMPTRT_FILE}" "${KERNEL_FILE}" + generate_new_kernel_configuration + unsign_kernel_configuration + generate_preemptrt_kernel_debian_package + install_preemptrt_kernel_debian_package "${PREEMPTRT_FULL_VERSION}" + success_msg "Done: Installation with Debian package completed!" +} + + +function main() { + declare desc="Install the kernel either in an interactive or non-interactive way depending if an input argument is given" + local CURRENT_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd) + local LIB_OUTPUT="${CURRENT_PATH}/lib_output.sh" + local LIB_INSTALL_DEBIAN="${CURRENT_PATH}/lib_compile_kernel.sh" + source "${LIB_OUTPUT}" + source "${LIB_INSTALL_DEBIAN}" + + set -e # Exit immediately in case of failure + if [ "$#" -eq 0 ]; then + install_kernel_interactive + elif [ "$#" -eq 1 ] && [ "${EUID}" -eq 0 ]; then + install_kernel_noninteractive $@ + else + error_msg "Error: Could not run installation script!" + info_msg "Either launch it as: " + info_msg " - '$ ./compile_kernel_preemptrt'" + info_msg " - '$ sudo ./compile_kernel_preemptrt 5.10.78-rt55'" + info_msg "For the available PREEMPT_RT versions see: " + info_msg "see https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/" + exit 1 + fi + exit 0 +} + +main $@ diff --git a/src/install_debian_preemptrt b/src/install_debian_preemptrt new file mode 100755 index 0000000..db0ecf0 --- /dev/null +++ b/src/install_debian_preemptrt @@ -0,0 +1,41 @@ +#!/bin/bash +# This script searches https://packages.debian.org/ for kernels with the PREEMPT_RT patch and allows the user to choose a desired version interactively +# This should be preferred over the re-compilation of the Linux kernel +# Tobit Flatscher - github.com/2b-t (2022) +# +# Usage: +# - '$ ./install_debian_preemptrt' + + +function main() { + declare desc="Install the PREEMPT_RT-patched kernel from the available pre-compiled Debian packages available online" + set -e + + local CURRENT_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd) + local LIB_OUTPUT="${CURRENT_PATH}/lib_output.sh" + local LIB_INSTALL_DEBIAN="${CURRENT_PATH}/lib_install_debian.sh" + source "${LIB_OUTPUT}" + source "${LIB_INSTALL_DEBIAN}" + + info_msg "Installing dependencies..." + install_dependencies + success_msg "Dependencies installed successfully!" + + local DEBIAN_VERSION=$(select_debian_version) + local DOWNLOAD_LOCATION=$(select_download_location "${DEBIAN_VERSION}") + local DOWNLOADED_FILE=$(download_file "${DOWNLOAD_LOCATION}") + success_msg "Downloaded Debian package '${DOWNLOADED_FILE}' from '${DOWNLOAD_LOCATION}'..." + + local IS_INSTALL_NOW=$(select_install_now) + if [ "${IS_INSTALL_NOW}" -eq 0 ]; then + info_msg "Installing Debian package '${DOWNLOADED_FILE}'..." + install_debian_pkg "${DOWNLOADED_FILE}" + success_msg "Done: Installation from Debian package '${DOWNLOADED_FILE}' completed!" + else + warning_msg "Done: Debian package '${DOWNLOADED_FILE}' downloaded successfully but installation skipped!" + fi + + exit 0 +} + +main $@ diff --git a/src/lib_compile_kernel.sh b/src/lib_compile_kernel.sh new file mode 100755 index 0000000..0930a1d --- /dev/null +++ b/src/lib_compile_kernel.sh @@ -0,0 +1,225 @@ +#!/bin/bash +# Library for compiling a kernel with the PREEMPT_RT real-time patch using a simple graphical interface +# Tobit Flatscher - github.com/2b-t (2022) + + +function is_valid_url() { + declare desc="Check if a given url exists or not" + local POTENTIAL_URL=$1 + if curl --head --silent --fail "${POTENTIAL_URL}" > /dev/null; then + echo "true" + else + echo "false" + fi +} + +function remove_right_of_dot() { + declare desc="Remove contents right of dot and return remaining string left of it" + local INPUT_STRING=$1 + echo "${INPUT_STRING%.*}" +} + +function install_dependencies() { + declare desc="Install the missing dependencies for the PREEMPT_RT compilation from source" + sudo apt-get install -y grep curl sed + sudo apt-get install -y build-essential bc ca-certificates gnupg2 libssl-dev lsb-release libelf-dev bison flex dwarves zstd libncurses-dev dpkg-dev +} + +function get_preemptrt_minor_versions() { + declare desc="Get the major and minor PREEMPT_RT versions by crawling website (e.g. 5.10 ...)" + echo $(curl -Ls https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt | grep -o -P '(?<=href\=\")(\d\.\d*)+(?=/\">)') +} + +function get_current_kernel_version() { + declare desc="Get the version of the currently used kernel (e.g. 5.10)" + echo $(uname -r | sed 's/\.[^\.]*//2g') +} + +function select_preemptrt_minor_version() { + declare desc="Select the desired major and minor version with a user dialog" + local PREEMPTRT_MINOR_VERSIONS=$(get_preemptrt_minor_versions) + local DIALOG_PREEMPTRT_MINOR_VERSIONS="" + for VER in ${PREEMPTRT_MINOR_VERSIONS}; do + DIALOG_PREEMPTRT_MINOR_VERSIONS="${DIALOG_PREEMPTRT_MINOR_VERSIONS} ${VER} ${VER}" + done + local CURRENT_KERNEL_VERSION=$(get_current_kernel_version) + echo $(dialog --keep-tite --no-tags --stdout --default-item ${CURRENT_KERNEL_VERSION} --menu "Select a major kernel version:" 30 40 10 ${DIALOG_PREEMPTRT_MINOR_VERSIONS}) +} + +function get_preemptrt_full_versions() { + declare desc="Get the full PREEMPT_RT versions (major.minor.patch.rt-rtpatch) from major and minor version by crawling website (e.g. 5.10.78-rt55 ...)" + local PREEMPTRT_MINOR_VERSION=$1 + echo $(curl -Ls https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/${PREEMPTRT_MINOR_VERSION} | grep -o -P '(?<=href\=\"patch-).*(?=.patch.gz\">)') +} + +function select_preemptrt_full_version() { + declare desc="Select the desired full version (e.g. 5.10.78-rt55) with a user dialog given the corresponding minor version (e.g. 5.10)" + local PREEMPTRT_MINOR_VERSION=$1 + local PREEMPTRT_FULL_VERSIONS=$(get_preemptrt_full_versions "${PREEMPTRT_MINOR_VERSION}") + local DIALOG_PREEMPTRT_FULL_VERSIONS="" + for VER in ${PREEMPTRT_FULL_VERSIONS}; do + DIALOG_PREEMPTRT_FULL_VERSIONS="${DIALOG_PREEMPTRT_FULL_VERSIONS} ${VER} ${VER}" + done + echo $(dialog --keep-tite --no-tags --stdout --menu "Select the desired version of PREEMPT_RT:" 30 40 10 ${DIALOG_PREEMPTRT_FULL_VERSIONS}) +} + +function select_preemptrt() { + declare desc="Select the desired full version (e.g. 5.10.78-rt55) with a user dialog by first selecting the corresponding minor version (e.g. 5.10)" + local PREEMPTRT_MINOR_VERSION=$(select_preemptrt_minor_version) + echo $(select_preemptrt_full_version "${PREEMPTRT_MINOR_VERSION}") +} + +function extract_kernel_full_version() { + declare desc="Extract the full kernel version (major.minor.patch, e.g. 5.10.78) from the full PREEMPT_RT patch version (e.g. 5.10.78-rt55)" + local PREEMPTRT_FULL_VERSION=$1 + echo "${PREEMPTRT_FULL_VERSION}" | sed 's/-rt.*//g' +} + +function extract_kernel_minor_version() { + declare desc="Extract the kernel minor version (major.minor, e.g. 5.10) from the full kernel version (e.g. 5.10.78)" + local KERNEL_FULL_VERSION=$1 + echo "${KERNEL_FULL_VERSION}" | sed 's/\.[^\.]*//2g' +} + +function reconstruct_kernel_major_tag() { + declare desc="Extract the kernel major tag (vmajor.x, e.g. v5.x) from the minor kernel version (e.g. 5.10)" + local KERNEL_MINOR_VERION=$1 + local KERNEL_MAJOR_VERSION=$(echo "${KERNEL_MINOR_VERSION}" | grep -o -P '^\s*(\d)+') + echo "$(curl -Ls https://www.kernel.org/pub/linux/kernel | grep -o -P "(?<=href\=\")(v${KERNEL_MAJOR_VERSION}.*)(?=/\">)")" +} + +function get_kernel_link() { + declare desc="Get the link where the kernel can be downloaded from" + local KERNEL_MAJOR_TAG=$1 + local KERNEL_FULL_VERSION=$2 + echo "https://www.kernel.org/pub/linux/kernel/${KERNEL_MAJOR_TAG}/linux-${KERNEL_FULL_VERSION}.tar.xz" +} + +function get_kernel_signature_link() { + declare desc="Get the link where the kernel signature can be downloaded from" + local KERNEL_MAJOR_TAG=$1 + local KERNEL_FULL_VERSION=$2 + echo "https://www.kernel.org/pub/linux/kernel/${KERNEL_MAJOR_TAG}/linux-${KERNEL_FULL_VERSION}.tar.sign" +} + +function get_preemptrt_link() { + declare desc="Get the link where the PREEMPT_RT-patch can be downloaded from" + local KERNEL_MINOR_VERSION=$1 + local PREEMPTRT_FULL_VERSION=$2 + echo "https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/${KERNEL_MINOR_VERSION}/patch-${PREEMPTRT_FULL_VERSION}.patch.xz" +} + +function get_preemptrt_signature_link() { + declare desc="Get the link where the PREEMPT_RT-patch signature can be downloaded from" + local KERNEL_MINOR_VERSION=$1 + local PREEMPTRT_FULL_VERSION=$2 + echo "https://mirrors.edge.kernel.org/pub/linux/kernel/projects/rt/${KERNEL_MINOR_VERSION}/patch-${PREEMPTRT_FULL_VERSION}.patch.sign" +} + +function sign_file() { + declare desc="Sign a given file" + local UNSIGNED_FILE=$1 + gpg2 --verify "${UNSIGNED_FILE}" + if [ $? -ne 0 ] + then + local RECEIVED_KEY=$(gpg2 --verify "${UNSIGNED_FILE}" 2>&1 | grep -o -P '(?<=RSA key )(.*)') + gpg2 --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys "${RECEIVED_KEY}" + gpg2 --verify "${UNSIGNED_FILE}" + fi +} + +function download_and_extract_kernel() { + declare desc="Download and extract the vanilla kernel and sign it" + local KERNEL_FULL_VERSION=$1 + local KERNEL_MINOR_VERSION=$(extract_kernel_minor_version "${KERNEL_FULL_VERSION}") + local KERNEL_MAJOR_TAG=$(reconstruct_kernel_major_tag "${KERNEL_MINOR_VERSION}") + local KERNEL_DOWNLOAD_LINK=$(get_kernel_link "${KERNEL_MAJOR_TAG}" "${KERNEL_FULL_VERSION}") + local KERNEL_SIGNATURE_DOWNLOAD_LINK=$(get_kernel_signature_link "${KERNEL_MAJOR_TAG}" "${KERNEL_FULL_VERSION}") + curl -SLO --fail "${KERNEL_DOWNLOAD_LINK}" + curl -SLO --fail "${KERNEL_SIGNATURE_DOWNLOAD_LINK}" + xz -d "linux-${KERNEL_FULL_VERSION}.tar.xz" + sign_file "linux-${KERNEL_FULL_VERSION}.tar.sign" + echo "linux-${KERNEL_FULL_VERSION}.tar" +} + +function download_and_extract_preemptrt() { + declare desc="Download and extract the PREEMPT_RT patch and sign it" + local PREEMPTRT_FULL_VERSION=$1 + local KERNEL_FULL_VERSION=$(extract_kernel_full_version "${PREEMPTRT_FULL_VERSION}") + local PREEMPTRT_MINOR_VERSION=$(extract_kernel_minor_version "${KERNEL_FULL_VERSION}") + local PREEMPTRT_DOWNLOAD_LINK=$(get_preemptrt_link "${PREEMPTRT_MINOR_VERSION}" "${PREEMPTRT_FULL_VERSION}") + local PREEMPTRT_SIGNATURE_DOWNLOAD_LINK=$(get_preemptrt_signature_link "${PREEMPTRT_MINOR_VERSION}" "${PREEMPTRT_FULL_VERSION}") + curl -SLO --fail "${PREEMPTRT_DOWNLOAD_LINK}" + curl -SLO --fail "${PREEMPTRT_SIGNATURE_DOWNLOAD_LINK}" + xz -d "patch-${PREEMPTRT_FULL_VERSION}.patch.xz" + sign_file "patch-${PREEMPTRT_FULL_VERSION}.patch.sign" + echo "patch-${PREEMPTRT_FULL_VERSION}.patch" +} + +function apply_patch_to_kernel() { + declare desc="Apply the PREEMPT_RT patch to the kernel" + local PREEMPTRT_FILE=$1 + local KERNEL_FILE=$2 + tar xf "${KERNEL_FILE}" + local KERNEL_FOLDER=$(remove_right_of_dot "${KERNEL_FILE}") + cd "${KERNEL_FOLDER}" + patch -p1 < "../${PREEMPTRT_FILE}" +} + +function generate_new_kernel_configuration() { + declare desc="Generate a new kernel configuration" + # Only option set in the configuration is full PREEMPT_RT, rest is left on default + echo "4" | make oldconfig +} + +function find_and_replace_in_config() { + declare desc="Add or replace (if exists) given setting in the configuration file" + local KERNEL_CONFIG_FILE=".config" + local SETTING_NAME=$2 + local DESIRED_VALUE=$3 + grep -E "${SETTING_NAME}=" "${KERNEL_CONFIG_FILE}" && sed -i "s/${SETTING_NAME}=.*/${SETTING_NAME}=${DESIRED_VALUE}/" "${KERNEL_CONFIG_FILE}" || echo "${SETTING_NAME}=${DESIRED_VALUE}" >> "${KERNEL_CONFIG_FILE}" +} + +function comment_out_in_config() { + declare desc="Comment out certain setting in the configuration file" + local KERNEL_CONFIG_FILE=$1 + local SETTING_NAME=$2 + sed -E "/${SETTING_NAME}/ s/^#*/#/" -i ${KERNEL_CONFIG_FILE} +} + +function unsign_kernel_configuration() { + declare desc="Force unsigned kernel configuration" + local KERNEL_CONFIG_FILE=".config" + find_and_replace_in_config "${KERNEL_CONFIG_FILE}" "CONFIG_SYSTEM_TRUSTED_KEYS" '""' + find_and_replace_in_config "${KERNEL_CONFIG_FILE}" "CONFIG_SYSTEM_REVOCATION_KEYS" '""' +} + +function select_installation_mode() { + declare desc="Select installation modality" + echo $(dialog --keep-tite --stdout --default-item "Debian package" --menu "Select the desired installation mode:" 0 0 5 "Debian" "Debian package" "Classic" "Install directly") +} + +function generate_preemptrt_kernel_debian_package() { + declare desc="Generate Debian package for easier installation and uninstallation" + sudo make -j$(nproc) deb-pkg +} + +function select_install_now() { + declare desc="Decide whether to install the patched kernel from Debian package now or install it later on" + dialog --keep-tite --stdout --title "Install Debian package" --yesno "Want to install the Debian package now" 0 0 + echo $? +} + +function install_preemptrt_kernel_debian_package() { + declare desc="Install the PREEMPT_RT-patched kernel from Debian package" + local PREEMPTRT_FULL_VERSION=$1 + local ARCHITECTURE=$(dpkg --print-architecture) + sudo dpkg -i "../linux-image-${PREEMPTRT_FULL_VERSION}_${PREEMPTRT_FULL_VERSION}-1_${ARCHITECTURE}.deb" +} + +function install_preemptrt_kernel_directly() { + declare desc="Install the PREEMPT_RT-patched kernel directly from source with Make" + sudo make -j$(nproc) + sudo make modules_install -j$(nproc) + sudo make install -j$(nproc) +} diff --git a/src/lib_install_debian.sh b/src/lib_install_debian.sh new file mode 100755 index 0000000..cac512f --- /dev/null +++ b/src/lib_install_debian.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# This library holds functions for searching https://packages.debian.org/ for kernels with the PREEMPT_RT patch and installing it in an interactive way +# Tobit Flatscher - github.com/2b-t (2022) + + +function install_dependencies() { + declare desc="Install the missing dependencies for the PREEMPT_RT installation from a Debian package" + sudo apt-get install -y grep curl sed + sudo apt-get install -y build-essential bc ca-certificates gnupg2 libssl-dev lsb-release libelf-dev bison flex dwarves zstd libncurses-dev dpkg-dev +} + +function get_debian_versions() { + declare desc="Get the possible Debian versions of the kernel" + echo $(cat /etc/debian_version | tr / " ") +} + +function get_preemptrt_file() { + declare desc="Get the PREEMPT_RT filename for the given Debian distribution by crawling the website" + local DEBIAN_VERSION=$1 + echo $(curl -Ls https://packages.debian.org/${DEBIAN_VERSION}/$(dpkg --print-architecture)/linux-image-rt-$(dpkg --print-architecture)/download | grep -o -P '(?<=

    Download Page for )(linux-image-rt.*)(?=<\/kbd>)') +} + +function select_debian_version() { + declare desc="Select the Debian version from a list of given Debian versions" + local POSSIBLE_DEBIAN_VERSIONS=$(get_debian_versions) + local DIALOG_POSSIBLE_DEBIAN_VERSIONS="" + for VER in ${POSSIBLE_DEBIAN_VERSIONS}; do + local PREEMPTRT_FILE=$(get_preemptrt_file "$VER") + DIALOG_POSSIBLE_DEBIAN_VERSIONS="${DIALOG_POSSIBLE_DEBIAN_VERSIONS} ${VER} ${PREEMPTRT_FILE}" + done + echo $(dialog --keep-tite --stdout --menu "Select the desired PREEMPT_RT kernel version:" 0 0 4 ${DIALOG_POSSIBLE_DEBIAN_VERSIONS}) +} + +function get_download_locations() { + declare desc="Get a list of available download servers for the PREEMPT_RT Debian package given the Debian version by crawling the website" + local DEBIAN_VERSION=$1 + local ARCHITECTURE=$(dpkg --print-architecture) + echo $(curl -Ls https://packages.debian.org/${DEBIAN_VERSION}/${ARCHITECTURE}/linux-image-rt-${ARCHITECTURE}/download | grep -o -P '(?<=
  • )') +} + +function select_download_location() { + declare desc="Select the desired download server from a list of given download servers" + local DEBIAN_VERSION=$1 + local DOWNLOAD_SERVERS=$(get_download_locations "${DEBIAN_VERSION}") + local DIALOG_DOWNLOAD_SERVERS="" + for SERVER in ${DOWNLOAD_SERVERS}; do + DIALOG_DOWNLOAD_SERVERS="${DIALOG_DOWNLOAD_SERVERS} ${SERVER} ${SERVER}" + done + echo $(dialog --keep-tite --no-tags --stdout --menu "Select a download server:" 0 0 5 ${DIALOG_DOWNLOAD_SERVERS}) +} + +function extract_filename { + declare desc="Extract the filename from a given PREEMPT_RT download link" + local DOWNLOAD_LINK=$1 + echo "${DOWNLOAD_LINK}" | rev | cut -d'/' -f 1 | rev +} + +function download_file() { + declare desc="Downloads a given file and returns the filename" + local DOWNLOAD_LOCATION=$1 + curl -SLO --fail "${DOWNLOAD_LOCATION}" + echo $(extract_filename "${DOWNLOAD_LOCATION}") +} + +function select_install_now() { + declare desc="Select if the Debian package should be installed now or the installation should be performed at a later point" + dialog --keep-tite --stdout --title "Install Debian package" --yesno "Want to install the Debian package now?" 0 0 + echo $? +} + +function install_debian_pkg { + declare desc="Install a Debian package given by its file location" + local ${DEBIAN_PKG}=$1 + sudo dpkg -i ${DEBIAN_PKG} + sudo apt-get install -f +} diff --git a/src/lib_output.sh b/src/lib_output.sh new file mode 100755 index 0000000..ab40a32 --- /dev/null +++ b/src/lib_output.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Bash library containing different output utility functions +# Tobit Flatscher - github.com/2b-t (2022) + + +function _msg() { + declare desc="Output text in colour to the console" + local COLOUR=$1 + local CLEAR="\033[0m" + printf "${COLOUR}%s${CLEAR} \\n" "${*:2}" 1>&2 +} + +function error_msg() { + declare desc="Output errors in red to the console" + local RED="\033[0;31m" + _msg ${RED} ${*} +} + +function warning_msg() { + declare desc="Output warnings in yellow to the console" + local YELLOW="\033[1;33m" + _msg ${YELLOW} ${*} +} + +function info_msg() { + declare desc="Output information in white to the console" + local WHITE="\033[1;37m" + _msg ${WHITE} ${*} +} + +function success_msg() { + declare desc="Output success in green to the console" + local GREEN="\033[0;32m" + _msg ${GREEN} ${*} +} + diff --git a/test/bats b/test/bats new file mode 160000 index 0000000..325b211 --- /dev/null +++ b/test/bats @@ -0,0 +1 @@ +Subproject commit 325b2118e7a9956d222b918c1dadcd12e2737f13 diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert new file mode 160000 index 0000000..ffe84ea --- /dev/null +++ b/test/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit ffe84ea5dd43b568851549b3e241db150c12929c diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support new file mode 160000 index 0000000..3c8fadc --- /dev/null +++ b/test/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit 3c8fadc5097c9acfc96d836dced2bb598e48b009 diff --git a/test/test_lib_compile_kernel.bats b/test/test_lib_compile_kernel.bats new file mode 100755 index 0000000..b14d53f --- /dev/null +++ b/test/test_lib_compile_kernel.bats @@ -0,0 +1,119 @@ +#!/usr/bin/env bats +# Unit-tests for Bash library for compiling PREEMPT_RT kernel from source +# Tobit Flatscher - github.com/2b-t (2022) +# +# Usage: +# - '$ ./test_lib_compile_kernel.bats' + + +setup() { + load "test_helper/bats-support/load" + load "test_helper/bats-assert/load" + local DIR="$( cd "$( dirname "${BATS_TEST_FILENAME}" )" >/dev/null 2>&1 && pwd )" + local TEST_FILE="${DIR}/../src/lib_compile_kernel.sh" + source "${TEST_FILE}" +} + +@test "Test is_valid_url" { + declare desc="Test if a given URL is valid" + local URL="https://www.google.com/" + local IS_URL=$(is_valid_url "${URL}") + assert_equal "${IS_URL}" "true" +} + +@test "Test remove_right_of_dot" { + declare desc="Test if the text right of the dot is removed correctly" + local INITIAL_STRING="a.b.c" + local EXPECTED_RESULT_STRING="a.b" + local RESULT_STRING=$(remove_right_of_dot "${INITIAL_STRING}") + assert_equal "${RESULT_STRING}" "${EXPECTED_RESULT_STRING}" +} + +@test "Test get_preemptrt_minor_versions" { + declare desc="Test if detected PREEMPT_RT major and minor versions respect the version numbering" + local PREEMPTRT_MINOR_VERSIONS=$(get_preemptrt_minor_versions) + assert_not_equal "${PREEMPTRT_MINOR_VERSIONS}" "" + for PREEMPTRT_MINOR_VERSION in ${PREEMPTRT_MINOR_VERSIONS}; do + assert_regex "${PREEMPTRT_MINOR_VERSION}" "^[0-9]+\.[0-9]+(\.[0-9]+)?$" + done +} + +@test "Test get_current_kernel_version" { + declare desc="Test if the detected kernel version respects the version numbering" + local KERNEL_VERSION=$(get_current_kernel_version) + assert_not_equal "${KERNEL_VERSION}" "" + assert_regex "${KERNEL_VERSION}" "^[0-9]+\.[0-9]+(\.[0-9]+)?$" +} + +@test "Test select_preemptrt_minor_version" { + declare desc="Test if the selected PREEMPT_RT patch respects the version numbering" + (sleep 2 && xdotool key Return) & local PREEMPTRT_MINOR_VERSION=$(select_preemptrt_minor_version) + assert_not_equal "${PREEMPTRT_MINOR_VERSION}" "" + assert_regex "${PREEMPTRT_MINOR_VERSION}" "^[0-9]+\.[0-9]+(\.[0-9]+)?$" +} + +@test "Test get_preemptrt_full_versions" { + declare desc="Test if the full PREEMPT_RT version can be fetched from major and minor version and respects the version numbering" + local PREEMPTRT_MINOR_VERSION="5.11" + local PREEMPTRT_FULL_VERSIONS=$(get_preemptrt_full_versions "${PREEMPTRT_MINOR_VERSION}") + assert_not_equal "${PREEMPTRT_FULL_VERSIONS}" "" + for PREEMPTRT_FULL_VERSION in ${PREEMPTRT_FULL_VERSIONS}; do + assert_regex "${PREEMPTRT_FULL_VERSION}" "^[0-9]+\.[0-9]+(\.[0-9]+)+\-rt[0-9]+$" + done +} + +@test "Test select_preemptrt_full_version" { + declare desc="Test if the full PREEMPT_RT version can be selected from major and minor version and respects the version numbering" + local PREEMPTRT_MINOR_VERSION="5.11" + (sleep 2 && xdotool key Return) & local PREEMPTRT_FULL_VERSION=$(select_preemptrt_full_version "${PREEMPTRT_MINOR_VERSION}") + assert_not_equal "${PREEMPTRT_FULL_VERSION}" "" + assert_regex "${PREEMPTRT_FULL_VERSION}" "^[0-9]+\.[0-9]+(\.[0-9]+)+\-rt[0-9]+$" +} + +@test "Test extract_kernel_full_version" { + declare desc="Test if the full kernel version can be correctly constructed from the full real-time patch version" + local PREEMPTRT_FULL_VERSION="5.10.78-rt55" + local KERNEL_FULL_VERSION=$(extract_kernel_full_version "${PREEMPTRT_FULL_VERSION}") + assert_not_equal "${KERNEL_FULL_VERSION}" "" + assert_regex "${KERNEL_FULL_VERSION}" "^[0-9]+\.[0-9]+\.[0-9]+$" +} + +@test "Test extract_kernel_minor_version" { + declare desc="Test if the full minor version can be correctly constructed from the full kernel version" + local KERNEL_FULL_VERSION="5.10.78" + local KERNEL_MINOR_VERSION=$(extract_kernel_minor_version "${KERNEL_FULL_VERSION}") + assert_not_equal "${KERNEL_MINOR_VERSION}" "" + assert_regex "${KERNEL_MINOR_VERSION}" "^[0-9]+\.[0-9]+$" +} + +@test "Test reconstruct_kernel_major_tag" { + declare desc="Test if the kernel major version can be correctly constructed from the kernel minor version" + local KERNEL_MINOR_VERSION="5.10" + local KERNEL_MAJOR_VERSION=$(reconstruct_kernel_major_tag "${KERNEL_MINOR_VERSION}") + assert_not_equal "${KERNEL_MAJOR_VERSION}" "" + assert_regex "${KERNEL_MAJOR_VERSION}" "^v[0-9]+\.x$" +} + +@test "Test valid download links" { + declare desc="Test if valid download links can be generated by simply following the set-up routine with default values" + local PREEMPTRT_MINOR_VERSIONS="5.10" + local PREEMPTRT_MINOR_VERSION=$(echo ${PREEMPTRT_MINOR_VERSIONS} | cut --delimiter " " --fields 1) + local PREEMPTRT_FULL_VERSIONS=$(get_preemptrt_full_versions "${PREEMPTRT_MINOR_VERSION}") + local PREEMPTRT_FULL_VERSION=$(echo ${PREEMPTRT_FULL_VERSIONS} | cut --delimiter " " --fields 1) + local KERNEL_FULL_VERSION=$(extract_kernel_full_version "${PREEMPTRT_FULL_VERSION}") + local KERNEL_MINOR_VERSION=$(extract_kernel_minor_version "${KERNEL_FULL_VERSION}") + local KERNEL_MAJOR_TAG=$(reconstruct_kernel_major_tag "${KERNEL_MINOR_VERSION}") + local KERNEL_DOWNLOAD_LINK=$(get_kernel_link "${KERNEL_MAJOR_TAG}" "${KERNEL_FULL_VERSION}") + local KERNEL_SIGNATURE_DOWNLOAD_LINK=$(get_kernel_signature_link "${KERNEL_MAJOR_TAG}" "${KERNEL_FULL_VERSION}") + local PREEMPTRT_DOWNLOAD_LINK=$(get_preemptrt_link "${KERNEL_MINOR_VERSION}" "${PREEMPTRT_FULL_VERSION}") + local PREEMPTRT_SIGNATURE_DOWNLOAD_LINK=$(get_preemptrt_signature_link "${KERNEL_MINOR_VERSION}" "${PREEMPTRT_FULL_VERSION}") + # Check if download links exist + local IS_KERNEL_DOWNLOAD_LINK_VALID=$(is_valid_url "${KERNEL_DOWNLOAD_LINK}") + assert_equal "${IS_KERNEL_DOWNLOAD_LINK_VALID}" "true" + local IS_KERNEL_SIGNATURE_DOWNLOAD_LINK_VALID=$(is_valid_url "${KERNEL_SIGNATURE_DOWNLOAD_LINK}") + assert_equal "${IS_KERNEL_SIGNATURE_DOWNLOAD_LINK_VALID}" "true" + local IS_PREEMPTRT_DOWNLOAD_LINK_VALID=$(is_valid_url "${PREEMPTRT_DOWNLOAD_LINK}") + assert_equal "${IS_PREEMPTRT_DOWNLOAD_LINK_VALID}" "true" + local IS_PREEMPTRT_SIGNATURE_DOWNLOAD_LINK_VALID=$(is_valid_url "${PREEMPTRT_SIGNATURE_DOWNLOAD_LINK}") + assert_equal "${IS_PREEMPTRT_SIGNATURE_DOWNLOAD_LINK_VALID}" "true" +} diff --git a/test/test_lib_install_debian.bats b/test/test_lib_install_debian.bats new file mode 100755 index 0000000..a20fbc1 --- /dev/null +++ b/test/test_lib_install_debian.bats @@ -0,0 +1,56 @@ +#!/usr/bin/env bats +# Unit-tests for Bash library for installing PREEMPT_RT kernel from a Debian package +# Tobit Flatscher - github.com/2b-t (2022) +# +# Usage: +# - '$ ./test_lib_install_debian.bats' + + +setup() { + load "test_helper/bats-support/load" + load "test_helper/bats-assert/load" + local DIR="$( cd "$( dirname "${BATS_TEST_FILENAME}" )" >/dev/null 2>&1 && pwd )" + local TEST_FILE="${DIR}/../src/lib_install_debian.sh" + source "${TEST_FILE}" +} + +@test "Test get_debian_versions" { + declare desc="Test if valid Debian version is detected" + local DEBIAN_VERSIONS=$(get_debian_versions) + assert_not_equal "${DEBIAN_VERSIONS}" "" + assert_regex "${DEBIAN_VERSIONS}" "^([a-z]( )?)+$" +} + +@test "Test get_preemptrt_file" { + declare desc="Test if a valid Debian file is returned for the given Debian version" + local DEBIAN_VERSION="bullseye" + local PREEMPTRT_FILE=$(get_preemptrt_file "${DEBIAN_VERSION}") + assert_regex "${PREEMPTRT_FILE}" "^(linux-image-rt-).+(\.deb)$" +} + +@test "Test select_debian_version" { + declare desc="Test if select Debian version dialog returns a single option only" + (sleep 2 && xdotool key B && xdotool key Return) & DEBIAN_VERSION=$(select_debian_version) + assert_regex "${DEBIAN_VERSION}" "^([a-z])+$" +} + +@test "Test get_download_locations" { + declare desc="Test if a valid hyperlink is returned for the given Debian version" + local DEBIAN_VERSION="bullseye" + local DOWNLOAD_LOCATION=$(get_download_locations "${DEBIAN_VERSION}") + assert_regex "${DOWNLOAD_LOCATION}" "^(http://).+(\.deb)$" +} + +@test "Test select_download_location" { + declare desc="Test if select download location dialog returns a single option only" + local DEBIAN_VERSION="bullseye" + (sleep 1 && xdotool key Return) & local DOWNLOAD_LOCATION=$(select_download_location "${DEBIAN_VERSION}") + assert_regex "${DOWNLOAD_LOCATION}" "^(http://).+(\.deb)$" +} + +@test "Test extract_filename" { + declare desc="Test if filename is extracted correctly from hyperlink" + local DOWNLOAD_LOCATION="http://ftp.us.debian.org/debian/pool/main/l/linux-signed-amd64/linux-image-rt-amd64_5.10.127-1_amd64.deb" + local DOWNLOADED_FILE=$(extract_filename "${DOWNLOAD_LOCATION}") + assert_regex "${DOWNLOADED_FILE}" "^(linux-image-rt-).+(\.deb)$" +} diff --git a/test/test_lib_output.bats b/test/test_lib_output.bats new file mode 100755 index 0000000..b8e5e23 --- /dev/null +++ b/test/test_lib_output.bats @@ -0,0 +1,39 @@ +#!/usr/bin/env bats +# Unit-tests for Bash output library +# Tobit Flatscher - github.com/2b-t (2022) +# +# Usage: +# - '$ ./test_lib_output.bats' + + +setup() { + load "test_helper/bats-support/load" + load "test_helper/bats-assert/load" + local DIR="$( cd "$( dirname "${BATS_TEST_FILENAME}" )" >/dev/null 2>&1 && pwd )" + local TEST_FILE="${DIR}/../src/lib_output.sh" + source "${TEST_FILE}" +} + +@test "Test error_msg" { + declare desc="Test if error message contains the desired string" + run error_msg "error" + assert_output --partial "error" +} + +@test "Test warning_msg" { + declare desc="Test if warning message contains the desired string" + run warning_msg "warning" + assert_output --partial "warning" +} + +@test "Test info_msg" { + declare desc="Test if info message contains the desired string" + run info_msg "info" + assert_output --partial "info" +} + +@test "Test success_msg" { + declare desc="Test if success message contains the desired string" + run success_msg "success" + assert_output --partial "success" +}