From 5c7e392c9545a77ad92970809f67f1bd5d4ab288 Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 16:08:40 +0200 Subject: [PATCH 1/8] Add software install via USB config tool --- assets/femtofox-config.txt | 4 + .../usr/local/bin/femto-set-time.sh | 15 ++++ .../usr/local/bin/femto-usb-config-tool.sh | 74 ++++++++++++++++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/assets/femtofox-config.txt b/assets/femtofox-config.txt index 01e4d3c..4eab8f9 100644 --- a/assets/femtofox-config.txt +++ b/assets/femtofox-config.txt @@ -18,6 +18,10 @@ ## Options are "enable" and "disable" #act_led="disable" +### Software control +## Software package names are filenames (without .sh suffix) in /usr/local/bin/packages +## Multiple packages can be installed by separating them with commas +#software_install="packageA,packageB" ### Meshtastic settings diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-set-time.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-set-time.sh index 51b29d8..2ca6bd8 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-set-time.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-set-time.sh @@ -23,6 +23,19 @@ set_timezone() { echo "Setting time zone..." ln -f -s /usr/share/zoneinfo/$1 /etc/localtime >/dev/null 2>&1 dpkg-reconfigure --frontend noninteractive tzdata >/dev/null 2>&1 + if [ ! -f /usr/share/zoneinfo/$1 >/dev/null 2>&1 ]; then + echo -e "Invalid timezone: $1" + return 1 + fi + desired_offset=$(TZ=$1 date +"%:z") + current_offset=$(date +"%:z") + if [ "$desired_offset" != "$current_offset" ]; then + echo -e "FAILED to set timezone to $1. Expected GMT offset: $desired_offset, but got: $current_offset" + return 1 + else + echo "Set timezone to $current_offset" + return 0 + fi } set_timestamp() { @@ -46,9 +59,11 @@ while getopts ":t:T:h" opt; do case ${opt} in t) # Option -t (timezone) set_timezone "$OPTARG" + [ $? -eq 1 ] && exit 1 ;; T) # Option -t (timestamp) set_timestamp "$OPTARG" + [ $? -eq 1 ] && exit 1 || exit 0 ;; h) # Option -h (help) echo -e "$help" diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh index 5bf0574..36d18b5 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh @@ -20,6 +20,15 @@ if grep -qE '^first_boot=true' /etc/femto.conf; then fi exit_script() { + if [[ -n "$partial_failure" ]]; then + log_message "\033[0;31mPartial failure in usb-config-tool...\033[0m" + for _ in {1..2}; do #boot code + blink "1" && sleep 0.5 + done + for _ in {1..2}; do #boot code + blink "0.25" && sleep 0.5 + done + fi if [ ! -z "$usb_path" ]; then #if usb path is populated if ! df -T /mnt/usb 2>/dev/null | grep -qw 'ntfs'; then log_message "USB configuration script complete. Copying femtofox-config.log to USB drive." @@ -130,8 +139,9 @@ if [ -f "$mount_point/femtofox-config.txt" ]; then meshtastic_private_key) meshtastic_private_key=$(escape_sed "$value") ;; meshtastic_admin_key) meshtastic_admin_key=$(escape_sed "$value") ;; dont_run_if_log_exists) dont_run_if_log_exists=$(escape_sed "$value") ;; + software_install) software_install=$(escape_sed "$value") ;; esac - done < <(grep -E '^(act_led|wifi_ssid|wifi_psk|wifi_country|meshtastic_lora_radio|timezone|meshtastic_url|meshtastic_legacy_admin|meshtastic_public_key|meshtastic_private_key|meshtastic_admin_key|dont_run_if_log_exists)=' "$usb_config") + done < <(grep -E '^(act_led|wifi_ssid|wifi_psk|wifi_country|meshtastic_lora_radio|timezone|meshtastic_url|meshtastic_legacy_admin|meshtastic_public_key|meshtastic_private_key|meshtastic_admin_key|dont_run_if_log_exists|software_install)=' "$usb_config") # Check if the log exits and if the dont_run_if_log_exists line is set in the script if $log_exists && [[ $dont_run_if_log_exists = "true" ]]; then @@ -173,6 +183,13 @@ if [ -f "$mount_point/femtofox-config.txt" ]; then found_config="true" update_wifi="true" fi + + if [ "$update_wifi" = true ]; then #if wifi config found, restart wifi + log_message "Making changes to wifi settings and restarting wifi." + wifi_command="$wifi_command -r" + eval $wifi_command 2>&1 | tee -a /tmp/femtofox-config.log + log_message "wpa_supplicant.conf updated and wifi restarted. Enabling Meshtastic wifi setting." + fi #get meshtastic_lora_radio model, if specified, and copy appropriate yaml to /etc/meshtasticd/config.d/ if [[ -n "$meshtastic_lora_radio" ]]; then @@ -214,12 +231,18 @@ if [ -f "$mount_point/femtofox-config.txt" ]; then radio="none" ;; *) - log_message "Invalid LoRa radio name: $meshtastic_lora_radio, ignoring." + log_message "\033[0;31mInvalid LoRa radio name: $meshtastic_lora_radio, ignoring.\033[0m" + partial_failure=true ;; esac if [[ -n "$radio" ]]; then # if a radio was found femto-meshtasticd-config.sh -l "$radio" -s 2>&1 | tee -a /tmp/femtofox-config.log # set the radio and restart the service - log_message "Set LoRa radio to $meshtastic_lora_radio, restarting Meshtasticd." + if [ $? -eq 0 ]; then + log_message "Set LoRa radio to $meshtastic_lora_radio, restarting Meshtasticd." + else + log_message "\033[0;31mSeting LoRa radio to $meshtastic_lora_radio FAILED, restarting Meshtasticd.\033[0m" | tee -a /tmp/femtofox-config.log + partial_failure=true + fi fi fi @@ -227,6 +250,10 @@ if [ -f "$mount_point/femtofox-config.txt" ]; then timezone=$(echo "$timezone" | sed 's/\\//g') log_message "Updating system timezone to $timezone." femto-set-time.sh -t "$timezone" + if [ $? -eq 1 ]; then + partial_failure=true + log_message "\033[0;31mSeting timezone to $timezone FAILED.\033[0m" | tee -a /tmp/femtofox-config.log + fi found_config="true" fi @@ -274,25 +301,54 @@ if [ -f "$mount_point/femtofox-config.txt" ]; then while IFS='=' read -r key value; do log_message "Meshtastic CLI command found." femto-meshtasticd-config.sh -m "$value" 3 "Meshtastic CLI command" 2>&1 | tee -a /tmp/femtofox-config.log + if [ $? -eq 1 ]; then + partial_failure=true + log_message "\033[0;31mMeshtastic CLI command \`meshtastic --host $value\` FAILED.\033[0m" | tee -a /tmp/femtofox-config.log + fi found_config=true done < <(grep '^meshtastic_cli=' "$usb_config" | sed -E 's/^meshtastic_cli=["]?(.*[^"])["]?$/meshtastic_cli=\1/') - if [ "$found_config" = true ]; then #if we found a config file containing valid data - if [ "$update_wifi" = true ]; then #if wifi config found, restart wifi - log_message "Making changes to wifi settings and restarting wifi." - wifi_command="$wifi_command -r" - eval $wifi_command 2>&1 | tee -a /tmp/femtofox-config.log - log_message "wpa_supplicant.conf updated and wifi restarted. Enabling Meshtastic wifi setting." + # install software packages from software_install= + package_dir="/usr/local/bin/packages" + IFS=',' read -ra packages <<< "$software_install" + for package in "${packages[@]}"; do + if [[ -f "/usr/local/bin/packages/${package}.sh" ]]; then + found_config=true + log_message "Installing $($package_dir/${package}.sh -N) in non-interactive mode..." | tee -a /tmp/femtofox-config.log + set -o pipefail + $package_dir/${package}.sh -xi | tee -a /tmp/femtofox-config.log + exit_status=$? + set +o pipefail + if [ $exit_status -eq 0 ]; then + log_message "\033[0;32mInstallation of $($package_dir/${package}.sh -N) successful!\033[0m" + else + log_message "\033[0;31mInstallation of $($package_dir/${package}.sh -N) unsuccessful!\033[0m" + partial_failure=true + fi + else + log_message "\033[0;31mSoftware package $package_dir/${package}.sh does not exist, ignoring...\033[0m" + partial_failure=true fi + done + + if [ "$found_config" = true ]; then #if we found a config file containing valid data if [ "$meshtastic_url" != "" ]; then log_message "Connecting to Meshtastic radio and submitting $meshtastic_url" femto-meshtasticd-config.sh -q "$meshtastic_url" 2>&1 | tee -a /tmp/femtofox-config.log + if [ $? -eq 1 ]; then + partial_failure=true + log_message "\033[0;31mSetting Meshtastic URL to $meshtastic_url FAILED.\033[0m" | tee -a /tmp/femtofox-config.log + fi fi if [ "$meshtastic_security_command" != "femto-meshtasticd-config.sh" ]; then log_message "Connecting to Meshtastic radio and submitting $meshtastic_security_command" eval "$meshtastic_security_command" 2>&1 | tee -a /tmp/femtofox-config.log + if [ $? -eq 1 ]; then + partial_failure=true + log_message "\033[0;31mSetting Meshtastic security settings FAILED.\033[0m" | tee -a /tmp/femtofox-config.log + fi fi for _ in {1..10}; do #do our successful config boot code From 119b4ef03965b56c630b4a4410d3e70ab89a6610 Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 16:16:07 +0200 Subject: [PATCH 2/8] Update femtofox-config.txt --- assets/femtofox-config.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/femtofox-config.txt b/assets/femtofox-config.txt index 4eab8f9..fc3e24d 100644 --- a/assets/femtofox-config.txt +++ b/assets/femtofox-config.txt @@ -19,8 +19,8 @@ #act_led="disable" ### Software control -## Software package names are filenames (without .sh suffix) in /usr/local/bin/packages -## Multiple packages can be installed by separating them with commas +## Software package names are filenames (without .sh suffix) in /usr/local/bin/packages. Multiple packages can be installed by separating them with commas. +## Some software (such as Samba) normally requires user interaction to complete. In this case, a message will display on serial console and in the USB config log warning that you will need to run the "initialization" option in the software menu for that package. Internet connectivity is required for software installation. #software_install="packageA,packageB" ### Meshtastic settings From 3fe798408282f28dad39872434f83c02081184e7 Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 16:36:55 +0200 Subject: [PATCH 3/8] Update femtofox-config.txt --- assets/femtofox-config.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/femtofox-config.txt b/assets/femtofox-config.txt index fc3e24d..90b1b92 100644 --- a/assets/femtofox-config.txt +++ b/assets/femtofox-config.txt @@ -18,9 +18,9 @@ ## Options are "enable" and "disable" #act_led="disable" -### Software control -## Software package names are filenames (without .sh suffix) in /usr/local/bin/packages. Multiple packages can be installed by separating them with commas. -## Some software (such as Samba) normally requires user interaction to complete. In this case, a message will display on serial console and in the USB config log warning that you will need to run the "initialization" option in the software menu for that package. Internet connectivity is required for software installation. +### Software installation +## Software package names are filenames (without .sh suffix) in /usr/local/bin/packages +## Multiple packages can be installed by separating them with commas #software_install="packageA,packageB" ### Meshtastic settings From 94ae7aaa1bcd4080fe8703d0e0e0f84f780110ba Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 16:41:17 +0200 Subject: [PATCH 4/8] Update femto-usb-config-tool.sh --- .../rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh index 36d18b5..560a135 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh @@ -14,7 +14,7 @@ log_message() { echo "$(date +"%Y-%m-%d %H:%M:%S") $1" >> /tmp/femtofox-config.log } -if grep -qE '^first_boot=true' /etc/femto.conf; then +if systemctl is-enabled femto-runonce &>/dev/null; then log_message "First boot, skipping USB Configuration Tool." exit 0 fi From dd420e146e7e191ea7c44e75c3f46070ea4cc39d Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 19:16:12 +0200 Subject: [PATCH 5/8] Add retry to usb-config-tool software installation Will retry up to 5 times --- .../usr/local/bin/femto-usb-config-tool.sh | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh index 560a135..c9c8cc1 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-usb-config-tool.sh @@ -310,18 +310,26 @@ if [ -f "$mount_point/femtofox-config.txt" ]; then # install software packages from software_install= package_dir="/usr/local/bin/packages" + export HOME=/root IFS=',' read -ra packages <<< "$software_install" for package in "${packages[@]}"; do if [[ -f "/usr/local/bin/packages/${package}.sh" ]]; then found_config=true log_message "Installing $($package_dir/${package}.sh -N) in non-interactive mode..." | tee -a /tmp/femtofox-config.log - set -o pipefail - $package_dir/${package}.sh -xi | tee -a /tmp/femtofox-config.log - exit_status=$? - set +o pipefail - if [ $exit_status -eq 0 ]; then - log_message "\033[0;32mInstallation of $($package_dir/${package}.sh -N) successful!\033[0m" - else + for i in {1..5}; do + set -o pipefail + $package_dir/${package}.sh -xi | tee -a /tmp/femtofox-config.log + last_install_attempt_state=${PIPESTATUS[0]} + set +o pipefail + if [ $last_install_attempt_state -eq 0 ]; then + log_message "\033[0;32mInstallation of $($package_dir/${package}.sh -N) successful!\033[0m" + break + fi + [ $i = 5 ] && log_message "Installation failed... Is internet connected?" && break + log_message "Installation failed ($((i+1))/5). Retrying..." + sleep 4 + done + if [ $last_install_attempt_state -ne 0 ]; then log_message "\033[0;31mInstallation of $($package_dir/${package}.sh -N) unsuccessful!\033[0m" partial_failure=true fi From b04080efb57291a4b4498b16352d7a2232e4231b Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 19:17:00 +0200 Subject: [PATCH 6/8] Prevent Boot Complete LED boot code until USB-config is done --- .../etc/systemd/system/femto-boot-complete.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/systemd/system/femto-boot-complete.service b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/systemd/system/femto-boot-complete.service index 85e6d55..2e47bf2 100644 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/systemd/system/femto-boot-complete.service +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/systemd/system/femto-boot-complete.service @@ -1,7 +1,7 @@ [Unit] Description=Boot Complete DefaultDependencies=no -After=multi-user.target +After=multi-user.target femto-usb-config-tool.service [Service] ExecStart=/usr/local/bin/femto-boot-complete.sh From e74b48af6b681770ea3347e9b963d45fb2153ebf Mon Sep 17 00:00:00 2001 From: nagumii Date: Wed, 26 Feb 2025 20:07:15 +0200 Subject: [PATCH 7/8] Increment foxbuntu version --- foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/foxbuntu-release | 2 +- .../out/rootfs_uclibc_rv1106/usr/local/bin/femto-runonce.sh | 4 ++-- .../out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/foxbuntu-release b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/foxbuntu-release index 3f697f5..f2aa180 100644 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/foxbuntu-release +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/etc/foxbuntu-release @@ -1,6 +1,6 @@ major=1 minor=0 patch=0 -hotfix= +hotfix=RC3 # major, minor and patch must be numbers and populated. hotfix must be letters and can be blank. diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-runonce.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-runonce.sh index d38262a..0968a57 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-runonce.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-runonce.sh @@ -109,11 +109,11 @@ log_message "Enabling meshtasticd service" systemctl enable meshtasticd #generate SSH keys -log_message "Generating new SSH encryption keys. This can take a couple minutes..." +log_message "Generating new SSH encryption keys. This can take a few minutes..." femto-utils.sh -E #generate ttyd SSL keys -log_message "Generating new Web Terminal (ttyd) SSL encryption keys. This can take a couple minutes..." +log_message "Generating new Web Terminal (ttyd) SSL encryption keys. This can take a few minutes..." /usr/local/bin/packages/ttyd.sh -k log_message "Enabling Web Terminal (ttyd) service..." systemctl enable ttyd diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh index ad3fe08..ad38079 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh @@ -284,7 +284,7 @@ while getopts ":harsl:ipcnoSEC:R:v" opt; do replace_colors "$OPTARG" ;; v) # get foxbuntu version) - echo "Foxbuntu v$(grep -oP 'major=\K[0-9]+' /etc/foxbuntu-release).$(grep -oP 'minor=\K[0-9]+' /etc/foxbuntu-release).$(grep -oP 'patch=\K[0-9]+' /etc/foxbuntu-release)$(grep -oP 'hotfix=\K[a-z]+' /etc/foxbuntu-release)" + echo "Foxbuntu v$(grep -oP 'major=\K[0-9]+' /etc/foxbuntu-release).$(grep -oP 'minor=\K[0-9]+' /etc/foxbuntu-release).$(grep -oP 'patch=\K[0-9]+' /etc/foxbuntu-release)$(grep -oP 'hotfix=\K\S+' /etc/foxbuntu-release)" ;; \?) # Unknown argument) echo -e "Unknown argument $1.\n$help" From 81309bf271e28cb35cd751330a2e54bedda133ea Mon Sep 17 00:00:00 2001 From: nagumii Date: Thu, 27 Feb 2025 13:34:45 +0200 Subject: [PATCH 8/8] Minor fix for system info > OS info --- .../out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh index ad38079..2a0cb78 100755 --- a/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh +++ b/foxbuntu/sysdrv/out/rootfs_uclibc_rv1106/usr/local/bin/femto-utils.sh @@ -92,10 +92,11 @@ Mnted drives:$mounted_drives" } os_info() { - local os_version="Foxbuntu v$(grep -oP 'major=\K[0-9]+' /etc/foxbuntu-release).$(grep -oP 'minor=\K[0-9]+' /etc/foxbuntu-release)$(output=$(grep -o 'patch=[1-9][0-9]*' /etc/foxbuntu-release | cut -d= -f2) && [ -n "$output" ] && echo ".$output")$(grep -oP 'hotfix=\K[a-z]+' /etc/foxbuntu-release) ($(lsb_release -d | awk -F'\t' '{print $2}') $(lsb_release -c | awk -F'\t' '{print $2}'))" + local os_version="$(femto-utils.sh -v) ($(lsb_release -d | awk -F'\t' '{print $2}') $(lsb_release -c | awk -F'\t' '{print $2}'))" local system_uptime="$(uptime -p | awk '{$1=""; print $0}' | sed -e 's/ day\b/d/g' -e 's/ hour\b/h/g' -e 's/ hours\b/h/g' -e 's/ minute\b/m/g' -e 's/ minutes\b/m/g' | sed 's/,//g')" local kernel_active_modules="$(lsmod | awk 'NR>1 {print $1}' | tr '\n' ' ' && echo)" local kernel_boot_modules="$(modules=$(sed -n '6,$p' /etc/modules | sed ':a;N;$!ba;s/\n/, /g;s/, $//'); [ -z "$modules" ] && echo "none" || echo "$modules")" + local kernel_modules_blacklist="$(femto-kernel-modules.sh -y | sed 's/\x1B\[[0-9;]*m//g')" #remove underline local ttyd_enabled="$(femto-utils.sh -C "ttyd")" local logging_enabled="$(logging "check" | sed 's/\x1b\[[0-9;]*m//g')" local act_led="$(femto-utils.sh -a "check" | sed -r 's/\x1B\[[0-9;]*[mK]//g')" #remove color from output @@ -107,6 +108,7 @@ Uptime:$system_uptime\n\ System time:$(date)\n\ K mods active:$kernel_active_modules\n\ K boot mods:$kernel_boot_modules\n\ +K mod blcklst: $kernel_modules_blacklist Web terminal:$ttyd_enabled\n\ Logging:$logging_enabled\n\ Activity LED:$act_led"