diff --git a/bin/armbian-configng b/bin/armbian-configng
index 60de3f083..f035f0d18 100755
--- a/bin/armbian-configng
+++ b/bin/armbian-configng
@@ -1,6 +1,7 @@
#!/bin/bash
tput init
+clear
#
# Language-based variable assignment for script directory path
# This serves as a Rosetta Stone for developers,
@@ -13,15 +14,32 @@ trap "exit" INT TERM
script_dir="$(dirname "$0")"
# Define the lib directory one level up from the script directory
-lib_dir="$script_dir/../lib/armbian-configng"
-doc_dir="$script_dir/../share/doc/armbian-configng"
-json_file="$lib_dir/config.ng.jobs.json"
-
-#
-# Load The Bash procedure Objects
-json_data=$(<"$json_file")
-
-#
+lib_dir="$script_dir/../lib/armbian-config"
+doc_dir="$script_dir/../share/doc/armbian-config"
+json_file="$lib_dir/config.jobs.json"
+modules_dir="$lib_dir/modules"
+
+ # Load The Bash procedure Objects
+json_data=$(jq '
+ walk(
+ # Process only objects
+ if type == "object" then
+ # Check if the object has a "status" key and if "status" is "Disabled"
+ if has("status") and .status == "Disabled" then
+ empty # Exclude this object from the output
+ else
+ . # Otherwise, keep the object as it is
+ end
+ else
+ . # For non-object elements, leave them unchanged
+ end
+ )
+' "$json_file")
+
+
+
+# Check if help or doc otherwise run sudo -E
+[[ $EUID != 0 && "$1" != "--help" && "$1" != "--doc" ]] && exec sudo -E "$0" "$@"
# 'whiptail' is a simple dialog box utility that works well with Bash. It doesn't have all the features of some other dialog box utilities, but it does everything we need for this script.
[[ -x "$(command -v whiptail)" ]] && DIALOG="whiptail"
@@ -30,28 +48,36 @@ json_data=$(<"$json_file")
declare -A module_options
#
-# Load configng core functions and module options array
+# Load config core functions and module options array
+
-source "$lib_dir/config.ng.functions.sh"
+
+source "$lib_dir/config.core.functions.sh"
set_runtime_variables
-echo "Loaded Runtime variables..." #| show_infobox ;
-#set_newt_colors 2
-echo "Loaded Dialog..." #| show_infobox ;
-source "$lib_dir/config.ng.docs.sh"
-echo "Loaded Docs..." #| show_infobox ;
-source "$lib_dir/config.ng.system.sh"
-echo "Loaded System helpers..." #| show_infobox ;
-source "$lib_dir/config.ng.network.sh"
-echo "Loaded Network helpers..." #| show_infobox ;
-source "$lib_dir/config.ng.software.sh"
-echo "Loaded Software helpers..." #| show_infobox ;
-#
-# Loads the variables from beta armbian-config for runtime handling
+source "$lib_dir/config.core.runtime.sh"
+# Function to source modules
+source_modules() {
+ # Iterate over all config.*.sh files in the lib_dir
+ for file in "$modules_dir/"config.*.sh; do
+ # Check if the file exists and is a regular file
+ if [[ -f "$file" ]]; then
+ echo "Sourcing: $(basename "$file")"
+ source "$file"
+ else
+ echo "Warning: $(basename "$file") does not exist or is not a file."
+ fi
+ done
-source "$lib_dir/config.ng.runtime.sh"
-echo "Loaded Runtime conditions..." #| show_infobox ;
+ tput reset # Soft reset, clears screen but keeps scrollback
-clear
+}
+
+
+
+# Call the function to source the modules
+source_modules
+# keep the scrollback
+tput reset
case "$1" in
"--help")
@@ -61,7 +87,7 @@ case "$1" in
exit 0
fi
- echo "Usage: armbian-configng --[option]
+ echo "Usage: armbian-config --[option]
Options:
--help [catagory] Use [catagory] to filter specific menu options.
--cmd [option] Run a command from the menu (simple)
@@ -69,9 +95,9 @@ case "$1" in
--doc Generate the README.md file
Examples:
- armbian-configng --help [cmd||System||Software||Network||Localisation]
- armbian-configng --cmd help
- armbian-configng --api help
+ armbian-config --help [cmd||System||Software||Network||Localisation]
+ armbian-config --cmd help
+ armbian-config --api help
"
exit 0
;;
@@ -118,51 +144,9 @@ case "$1" in
"$option" "$args"
exit 0
;;
-"main=help" | "main=Help")
- see_cli_legacy
- echo ""
- exit 0
- ;;
-"main="*)
- declare -A main_map
- main_map=(
- # map name to menu category
- ["System"]="S"
- ["Software"]="I"
- ["Network"]="N"
- ["Localisation"]="L"
- )
- main_value="${1#main=}"
- main_value="${main_map[$main_value]}"
-
- if [ -z "$main_value" ]; then
- echo "Error: Invalid List $1"
- exit 1
- fi
- declare -A select_map
- # map name to menu number
- select_map=(
- ["Headers"]="04"
- ["Headers_install"]="04"
- ["Headers_remove"]="05"
- ["Firmware"]="06"
- ["Nightly"]="07"
- )
- select_value="${2#selection=}"
- select_value="${select_map[$select_value]}"
- if [ -z "$select_value" ]; then
- echo "Error: Invalid Option $2"
- exit 1
- fi
- echo "$main_value""$select_value"
- execute_command "$main_value""$select_value"
- exit 0
- ;;
*)
- if [[ $EUID != 0 ]]; then
- echo -e "error: Exiting \nTry: 'sudo armbian-config'\n or: 'armbian-config --help' for More info\n\n"
- exit 0
- fi
+ #
+
;;
esac
diff --git a/lib/armbian-config/config.core.functions.sh b/lib/armbian-config/config.core.functions.sh
new file mode 100644
index 000000000..9085098b6
--- /dev/null
+++ b/lib/armbian-config/config.core.functions.sh
@@ -0,0 +1,767 @@
+#!/bin/bash
+
+module_options+=(
+ ["check_desktop,author"]="Igor Pecovnik"
+ ["check_desktop,ref_link"]=""
+ ["check_desktop,feature"]="check_desktop"
+ ["check_desktop,desc"]="Migrated procedures from Armbian config."
+ ["check_desktop,example"]="check_desktop"
+ ["check_desktop,status"]="Active"
+ ["check_desktop,doc_link"]=""
+)
+#
+# read desktop parameters
+#
+function check_desktop() {
+
+ DISPLAY_MANAGER=""
+ DESKTOP_INSTALLED=""
+ check_if_installed nodm && DESKTOP_INSTALLED="nodm"
+ check_if_installed lightdm && DESKTOP_INSTALLED="lightdm"
+ check_if_installed lightdm && DESKTOP_INSTALLED="gnome"
+ [[ -n $(service lightdm status 2> /dev/null | grep -w active) ]] && DISPLAY_MANAGER="lightdm"
+ [[ -n $(service nodm status 2> /dev/null | grep -w active) ]] && DISPLAY_MANAGER="nodm"
+ [[ -n $(service gdm status 2> /dev/null | grep -w active) ]] && DISPLAY_MANAGER="gdm"
+
+}
+
+module_options+=(
+ ["check_if_installed,author"]="Igor Pecovnik"
+ ["check_if_installed,ref_link"]=""
+ ["check_if_installed,feature"]="check_if_installed"
+ ["check_if_installed,desc"]="Migrated procedures from Armbian config."
+ ["check_if_installed,example"]="check_if_installed nano"
+ ["check_if_installed,status"]="Active"
+)
+#
+# check dpkg status of $1 -- currently only 'not installed at all' case caught
+#
+function check_if_installed() {
+
+ local DPKG_Status="$(dpkg -s "$1" 2> /dev/null | awk -F": " '/^Status/ {print $2}')"
+ if [[ "X${DPKG_Status}" = "X" || "${DPKG_Status}" = *deinstall* || "${DPKG_Status}" = *not-installed* ]]; then
+ return 1
+ else
+ return 0
+ fi
+
+}
+
+module_options+=(
+ ["update_skel,author"]="Igor Pecovnik"
+ ["update_skel,ref_link"]=""
+ ["update_skel,feature"]="update_skel"
+ ["update_skel,desc"]="Update the /etc/skel files in users directories"
+ ["update_skel,example"]="update_skel"
+ ["update_skel,status"]="Active"
+)
+#
+# check dpkg status of $1 -- currently only 'not installed at all' case caught
+#
+function update_skel() {
+
+ getent passwd |
+ while IFS=: read -r username x uid gid gecos home shell; do
+ if [ ! -d "$home" ] || [ "$username" == 'root' ] || [ "$uid" -lt 1000 ]; then
+ continue
+ fi
+ tar -C /etc/skel/ -cf - . | su - "$username" -c "tar --skip-old-files -xf -"
+ done
+
+}
+
+module_options+=(
+ ["qr_code,author"]="Igor Pecovnik"
+ ["qr_code,ref_link"]=""
+ ["qr_code,feature"]="qr_code"
+ ["qr_code,desc"]="Show or generate QR code for Google OTP"
+ ["qr_code,example"]="qr_code generate"
+ ["qr_code,status"]="Active"
+)
+#
+# check dpkg status of $1 -- currently only 'not installed at all' case caught
+#
+function qr_code() {
+
+ clear
+ if [[ "$1" == "generate" ]]; then
+ google-authenticator -t -d -f -r 3 -R 30 -W -q
+ cp /root/.google_authenticator /etc/skel
+ update_skel
+ fi
+ export TOP_SECRET=$(head -1 /root/.google_authenticator)
+ qrencode -m 2 -d 9 -8 -t ANSI256 "otpauth://totp/test?secret=$TOP_SECRET"
+ echo -e '\nScan QR code with your OTP application on mobile phone\n'
+ read -n 1 -s -r -p "Press any key to continue"
+
+}
+
+module_options+=(
+ ["is_package_manager_running,author"]="Igor Pecovnik"
+ ["is_package_manager_running,ref_link"]=""
+ ["is_package_manager_running,feature"]="is_package_manager_running"
+ ["is_package_manager_running,desc"]="Migrated procedures from Armbian config."
+ ["is_package_manager_running,example"]="is_package_manager_running"
+ ["is_package_manager_running,status"]="Active"
+)
+#
+# check if package manager is doing something
+#
+function is_package_manager_running() {
+
+ if ps -C apt-get,apt,dpkg > /dev/null; then
+ [[ -z $scripted ]] && echo -e "\nPackage manager is running in the background. \n\nCan't install dependencies. Try again later." | show_infobox
+ return 0
+ else
+ return 1
+ fi
+
+}
+
+module_options+=(
+ ["set_runtime_variables,author"]="Igor Pecovnik"
+ ["set_runtime_variables,ref_link"]=""
+ ["set_runtime_variables,feature"]="set_runtime_variables"
+ ["set_runtime_variables,desc"]="Run time variables Migrated procedures from Armbian config."
+ ["set_runtime_variables,example"]="set_runtime_variables"
+ ["set_runtime_variables,status"]="Active"
+)
+#
+# gather info about the board and start with loading menu variables
+#
+function set_runtime_variables() {
+
+ missing_dependencies=()
+
+ # Check if whiptail is available and set DIALOG
+ if [[ -z "$DIALOG" ]]; then
+ missing_dependencies+=("whiptail")
+ fi
+
+ # Check if jq is available
+ if ! [[ -x "$(command -v jq)" ]]; then
+ missing_dependencies+=("jq")
+ fi
+
+ # If any dependencies are missing, print a combined message and exit
+ if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
+ if is_package_manager_running; then
+ sudo apt install ${missing_dependencies[*]}
+ fi
+ fi
+
+ # Determine which network renderer is in use for NetPlan
+ if systemctl is-active NetworkManager 1> /dev/null; then
+ NETWORK_RENDERER=NetworkManager
+ else
+ NETWORK_RENDERER=networkd
+ fi
+
+ DIALOG_CANCEL=1
+ DIALOG_ESC=255
+
+ # we have our own lsb_release which does not use Python. Others shell install it here
+ if [[ ! -f /usr/bin/lsb_release ]]; then
+ if is_package_manager_running; then
+ sleep 3
+ fi
+ debconf-apt-progress -- apt-get update
+ debconf-apt-progress -- apt -y -qq --allow-downgrades --no-install-recommends install lsb-release
+ fi
+
+ [[ -f /etc/armbian-release ]] && source /etc/armbian-release && ARMBIAN="Armbian $VERSION $IMAGE_TYPE"
+ DISTRO=$(lsb_release -is)
+ DISTROID=$(lsb_release -sc)
+ KERNELID=$(uname -r)
+ [[ -z "${ARMBIAN// /}" ]] && ARMBIAN="$DISTRO $DISTROID"
+ DEFAULT_ADAPTER=$(ip -4 route ls | grep default | tail -1 | grep -Po '(?<=dev )(\S+)')
+ LOCALIPADD=$(ip -4 addr show dev $DEFAULT_ADAPTER | awk '/inet/ {print $2}' | cut -d'/' -f1)
+ BACKTITLE="Contribute: https://github.com/armbian/configng"
+ TITLE="Armbian configuration utility"
+ [[ -z "${DEFAULT_ADAPTER// /}" ]] && DEFAULT_ADAPTER="lo"
+
+ # detect desktop
+ check_desktop
+
+}
+
+
+module_options+=(
+ ["connect_bt_interface,author"]="Igor Pecovnik"
+ ["connect_bt_interface,ref_link"]=""
+ ["connect_bt_interface,feature"]="connect_bt_interface"
+ ["connect_bt_interface,desc"]="Migrated procedures from Armbian config."
+ ["connect_bt_interface,example"]="connect_bt_interface"
+ ["connect_bt_interface,status"]="Active"
+)
+#
+# connect to bluetooth device
+#
+function connect_bt_interface() {
+
+ IFS=$'\r\n'
+ GLOBIGNORE='*'
+ show_infobox <<< "\nDiscovering Bluetooth devices ... "
+ BT_INTERFACES=($(hcitool scan | sed '1d'))
+
+ local LIST=()
+ for i in "${BT_INTERFACES[@]}"; do
+ local a=$(echo ${i[0]//[[:blank:]]/} | sed -e 's/^\(.\{17\}\).*/\1/')
+ local b=${i[0]//$a/}
+ local b=$(echo $b | sed -e 's/^[ \t]*//')
+ LIST+=("$a" "$b")
+ done
+
+ LIST_LENGTH=$((${#LIST[@]} / 2))
+ if [ "$LIST_LENGTH" -eq 0 ]; then
+ BT_ADAPTER=${WLAN_INTERFACES[0]}
+ show_message <<< "\nNo nearby Bluetooth devices were found!"
+ else
+ exec 3>&1
+ BT_ADAPTER=$(whiptail --title "Select interface" \
+ --clear --menu "" $((6 + ${LIST_LENGTH})) 50 $LIST_LENGTH "${LIST[@]}" 2>&1 1>&3)
+ exec 3>&-
+ if [[ $BT_ADAPTER != "" ]]; then
+ show_infobox <<< "\nConnecting to $BT_ADAPTER "
+ BT_EXEC=$(
+ expect -c 'set prompt "#";set address '$BT_ADAPTER';spawn bluetoothctl;expect -re $prompt;send "disconnect $address\r";
+ sleep 1;send "remove $address\r";sleep 1;expect -re $prompt;send "scan on\r";sleep 8;send "scan off\r";
+ expect "Controller";send "trust $address\r";sleep 2;send "pair $address\r";sleep 2;send "connect $address\r";
+ send_user "\nShould be paired now.\r";sleep 2;send "quit\r";expect eof'
+ )
+ echo "$BT_EXEC" > /tmp/bt-connect-debug.log
+ if [[ $(echo "$BT_EXEC" | grep "Connection successful") != "" ]]; then
+ show_message <<< "\nYour device is ready to use!"
+ else
+ show_message <<< "\nError connecting. Try again!"
+ fi
+ fi
+ fi
+
+}
+
+# Start of config ng
+
+module_options+=(
+ ["set_colors,author"]="Joey Turner"
+ ["set_colors,ref_link"]=""
+ ["set_colors,feature"]="set_colors"
+ ["set_colors,desc"]="Change the background color of the terminal or dialog box"
+ ["set_colors,example"]="set_colors 0-7"
+ ["set_colors,doc_link"]=""
+ ["set_colors,status"]="Active"
+)
+#
+# Function to set the tui colors
+#
+function set_colors() {
+ local color_code=$1
+
+ if [ "$DIALOG" = "whiptail" ]; then
+ set_newt_colors "$color_code"
+ #echo "color code: $color_code" | show_infobox ;
+ elif [ "$DIALOG" = "dialog" ]; then
+ set_term_colors "$color_code"
+ else
+ echo "Invalid dialog type"
+ return 1
+ fi
+}
+
+#
+# Function to set the colors for newt
+#
+function set_newt_colors() {
+ local color_code=$1
+ case $color_code in
+ 0) color="black" ;;
+ 1) color="red" ;;
+ 2) color="green" ;;
+ 3) color="yellow" ;;
+ 4) color="blue" ;;
+ 5) color="magenta" ;;
+ 6) color="cyan" ;;
+ 7) color="white" ;;
+ 8) color="black" ;;
+ 9) color="red" ;;
+ *) return ;;
+ esac
+ export NEWT_COLORS="root=,$color"
+}
+
+#
+# Function to set the colors for terminal
+#
+function set_term_colors() {
+ local color_code=$1
+ case $color_code in
+ 0) color="\e[40m" ;; # black
+ 1) color="\e[41m" ;; # red
+ 2) color="\e[42m" ;; # green
+ 3) color="\e[43m" ;; # yellow
+ 4) color="\e[44m" ;; # blue
+ 5) color="\e[45m" ;; # magenta
+ 6) color="\e[46m" ;; # cyan
+ 7) color="\e[47m" ;; # white
+ *)
+ echo "Invalid color code"
+ return 1
+ ;;
+ esac
+ echo -e "$color"
+}
+
+#
+# Function to reset the colors
+#
+function reset_colors() {
+ echo -e "\e[0m"
+}
+
+module_options+=(
+ ["parse_menu_items,author"]="Gunjan Gupta"
+ ["parse_menu_items,ref_link"]=""
+ ["parse_menu_items,feature"]="parse_menu_items"
+ ["parse_menu_items,desc"]="Parse json to get list of desired menu or submenu items"
+ ["parse_menu_items,example"]="parse_menu_items 'menu_options_array'"
+ ["parse_menu_items,doc_link"]=""
+ ["parse_menu_items,status"]="Active"
+)
+#
+# Function to parse the menu items
+#
+parse_menu_items() {
+ local -n options=$1
+ while IFS= read -r id; do
+ IFS= read -r description
+ IFS= read -r condition
+ # If the condition field is not empty and not null, run the function specified in the condition
+ if [[ -n $condition && $condition != "null" ]]; then
+ # If the function returns a truthy value, add the menu item to the menu
+ if eval $condition; then
+ options+=("$id" " - $description")
+ fi
+ else
+ # If the condition field is empty or null, add the menu item to the menu
+ options+=("$id" " - $description ")
+ fi
+ done < <(echo "$json_data" | jq -r '.menu[] | '${parent_id:+".. | objects | select(.id==\"$parent_id\") | .sub[]? |"}' "\(.id)\n\(.description)\n\(.condition)"' || exit 1)
+}
+
+module_options+=(
+ ["generate_top_menu,author"]="Joey Turner"
+ ["generate_top_menu,ref_link"]=""
+ ["generate_top_menu,feature"]="generate_top_menu"
+ ["generate_top_menu,desc"]="Build the main menu from a object"
+ ["generate_top_menu,example"]="generate_top_menu 'json_data'"
+ ["generate_top_menu,doc_link"]=""
+ ["generate_top_menu,status"]="Active"
+)
+#
+# Function to generate the main menu from a JSON object
+#
+generate_top_menu() {
+ local json_data="$1"
+ local status="$ARMBIAN $KERNELID ($DISTRO $DISTROID)"
+ local backtitle="$BACKTITLE"
+
+
+ while true; do
+ local menu_options=()
+
+ parse_menu_items menu_options
+
+ local OPTION=$($DIALOG --backtitle "$backtitle" --title "$TITLE" --menu "$status" 0 80 9 "${menu_options[@]}" \
+ --ok-button Select --cancel-button Exit 3>&1 1>&2 2>&3)
+ local exitstatus=$?
+
+ if [ $exitstatus = 0 ]; then
+ [ -z "$OPTION" ] && break
+ [[ -n "$debug" ]] && echo "$OPTION"
+ generate_menu "$OPTION"
+ fi
+ done
+}
+
+module_options+=(
+ ["generate_menu,author"]="Tearran"
+ ["generate_menu,ref_link"]=""
+ ["generate_menu,feature"]="generate_menu"
+ ["generate_menu,desc"]="Generate a submenu from a parent_id"
+ ["generate_menu,example"]="generate_menu 'parent_id'"
+ ["generate_menu,doc_link"]=""
+ ["generate_menu,status"]="Active"
+)
+#
+# Function to generate the submenu
+#
+function generate_menu() {
+ local parent_id="$1"
+ local top_parent_id="$2"
+ local backtitle="$BACKTITLE"
+ local status=""
+
+ while true; do
+ # Get the submenu options for the current parent_id
+ local submenu_options=()
+ parse_menu_items submenu_options
+
+ local OPTION=$($DIALOG --backtitle "$BACKTITLE" --title "$top_parent_id $parent_id" --menu "$status" 0 80 9 "${submenu_options[@]}" \
+ --ok-button Select --cancel-button Back 3>&1 1>&2 2>&3)
+
+ local exitstatus=$?
+
+ if [ $exitstatus = 0 ]; then
+ [ -z "$OPTION" ] && break
+
+ # Check if the selected option has a submenu
+ local submenu_count=$(jq -r --arg id "$OPTION" '.menu[] | .. | objects | select(.id==$id) | .sub? | length' "$json_file")
+ submenu_count=${submenu_count:-0} # If submenu_count is null or empty, set it to 0
+ if [ "$submenu_count" -gt 0 ]; then
+ # If it does, generate a new menu for the submenu
+ [[ -n "$debug" ]] && echo "$OPTION"
+ generate_menu "$OPTION" "$parent_id"
+ else
+ # If it doesn't, execute the command
+ [[ -n "$debug" ]] && echo "$OPTION"
+ execute_command "$OPTION"
+ fi
+ fi
+ done
+}
+
+module_options+=(
+ ["execute_command,author"]="Joey Turner"
+ ["execute_command,ref_link"]=""
+ ["execute_command,feature"]="execute_command"
+ ["execute_command,desc"]="Needed by generate_menu"
+ ["execute_command,example"]=""
+ ["execute_command,doc_link"]=""
+ ["execute_command,status"]="Active"
+)
+#
+# Function to execute the command
+#
+function execute_command() {
+ local id=$1
+
+ # Extract commands
+ local commands=$(jq -r --arg id "$id" '
+ .menu[] |
+ .. |
+ objects |
+ select(.id == $id) |
+ .command[]?' "$json_file")
+
+ # Check if a prompt exists
+ local prompt=$(jq -r --arg id "$id" '
+ .menu[] |
+ .. |
+ objects |
+ select(.id == $id) |
+ .prompt?' "$json_file")
+
+ # If a prompt exists, display it and wait for user confirmation
+ if [[ "$prompt" != "null" && $INPUTMODE != "cmd" ]]; then
+ get_user_continue "$prompt" process_input
+ fi
+
+ # Execute each command
+ for command in "${commands[@]}"; do
+ [[ -n "$debug" ]] && echo "$command"
+ eval "$command"
+ done
+}
+
+module_options+=(
+ ["show_message,author"]="Joey Turner"
+ ["show_message,ref_link"]=""
+ ["show_message,feature"]="show_message"
+ ["show_message,desc"]="Display a message box"
+ ["show_message,example"]="show_message <<< 'hello world' "
+ ["show_message,doc_link"]=""
+ ["show_message,status"]="Active"
+)
+#
+# Function to display a message box
+#
+function show_message() {
+ # Read the input from the pipe
+ input=$(cat)
+
+ # Display the "OK" message box with the input data
+ if [[ $DIALOG != "bash" ]]; then
+ $DIALOG --title "$TITLE" --msgbox "$input" 0 0
+ else
+ echo -e "$input"
+ read -p -r "Press [Enter] to continue..."
+ fi
+}
+
+module_options+=(
+ ["show_infobox,author"]="Joey Turner"
+ ["show_infobox,ref_link"]=""
+ ["show_infobox,feature"]="show_infobox"
+ ["show_infobox,desc"]="pipeline strings to an infobox "
+ ["show_infobox,example"]="show_infobox <<< 'hello world' ; "
+ ["show_infobox,doc_link"]=""
+ ["show_infobox,status"]="Active"
+)
+#
+# Function to display an infobox with a message
+#
+function show_infobox() {
+ export TERM=ansi
+ local input
+ local BACKTITLE="$BACKTITLE"
+ local -a buffer # Declare buffer as an array
+ if [ -p /dev/stdin ]; then
+ while IFS= read -r line; do
+ buffer+=("$line") # Add the line to the buffer
+ # If the buffer has more than 10 lines, remove the oldest line
+ if ((${#buffer[@]} > 18)); then
+ buffer=("${buffer[@]:1}")
+ fi
+ # Display the lines in the buffer in the infobox
+
+ TERM=ansi $DIALOG --title "$TITLE" --infobox "$(printf "%s\n" "${buffer[@]}")" 16 90
+ sleep 0.5
+ done
+ else
+
+ input="$1"
+ TERM=ansi $DIALOG --title "$TITLE" --infobox "$input" 6 80
+ fi
+ echo -ne '\033[3J' # clear the screen
+}
+
+module_options+=(
+ ["show_menu,author"]="Joey Turner"
+ ["show_menu,ref_link"]=""
+ ["show_menu,feature"]="show_menu"
+ ["show_menu,desc"]="Display a menu from pipe"
+ ["show_menu,example"]="show_menu <<< armbianmonitor -h ; "
+ ["show_menu,doc_link"]=""
+ ["show_menu,status"]="Active"
+)
+#
+#
+#
+show_menu() {
+
+ # Get the input and convert it into an array of options
+ inpu_raw=$(cat)
+ # Remove the lines before -h
+ input=$(echo "$inpu_raw" | sed 's/-\([a-zA-Z]\)/\1/' | grep '^ [a-zA-Z] ' | grep -v '\[')
+ options=()
+ while read -r line; do
+ package=$(echo "$line" | awk '{print $1}')
+ description=$(echo "$line" | awk '{$1=""; print $0}' | sed 's/^ *//')
+ options+=("$package" "$description")
+ done <<< "$input"
+
+ # Display the menu and get the user's choice
+ [[ $DIALOG != "bash" ]] && choice=$($DIALOG --title "$TITLE" --menu "Choose an option:" 0 0 9 "${options[@]}" 3>&1 1>&2 2>&3)
+
+ # Check if the user made a choice
+ if [ $? -eq 0 ]; then
+ echo "$choice"
+ else
+ exit 0
+ fi
+
+}
+
+module_options+=(
+ ["get_user_continue,author"]="Joey Turner"
+ ["get_user_continue,ref_link"]=""
+ ["get_user_continue,feature"]="get_user_continue"
+ ["get_user_continue,desc"]="Display a Yes/No dialog box and process continue/exit"
+ ["get_user_continue,example"]="get_user_continue 'Do you wish to continue?' process_input"
+ ["get_user_continue,doc_link"]=""
+ ["get_user_continue,status"]="Active"
+)
+#
+# Function to display a Yes/No dialog box
+#
+function get_user_continue() {
+ local message="$1"
+ local next_action="$2"
+
+ if $($DIALOG --yesno "$message" 10 80 3>&1 1>&2 2>&3); then
+ $next_action
+ else
+ $next_action "No"
+ fi
+}
+
+menu_options+=(
+ ["get_user_continue,author"]="Joey Turner"
+ ["get_user_continue,ref_link"]=""
+ ["get_user_continue,feature"]="process_input"
+ ["get_user_continue,desc"]="used to process the user's choice paired with get_user_continue"
+ ["get_user_continue,example"]="get_user_continue 'Do you wish to continue?' process_input"
+ ["get_user_continue,status"]="Active"
+ ["get_user_continue,doc_link"]=""
+)
+#
+# Function to process the user's choice paired with get_user_continue
+#
+function process_input() {
+ local input="$1"
+ if [ "$input" = "No" ]; then
+ return 1
+ fi
+}
+
+module_options+=(
+ ["get_user_continue_secure,author"]="Joey Turner"
+ ["get_user_continue_secure,ref_link"]=""
+ ["get_user_continue_secure,feature"]="get_user_continue_secure"
+ ["get_user_continue_secure,desc"]="Secure version of get_user_continue"
+ ["get_user_continue_secure,example"]="get_user_continue_secure 'Do you wish to continue?' process_input"
+ ["get_user_continue_secure,doc_link"]=""
+ ["get_user_continue_secure,status"]="Active"
+)
+#
+# Secure version of get_user_continue
+#
+function get_user_continue_secure() {
+ local message="$1"
+ local next_action="$2"
+
+ # Define a list of allowed functions
+ local allowed_functions=("process_input" "other_function")
+ # Check if the next_action is in the list of allowed functions
+ found=0
+ for func in "${allowed_functions[@]}"; do
+ if [[ "$func" == "$next_action" ]]; then
+ found=1
+ break
+ fi
+ done
+
+ if [[ "$found" -eq 1 ]]; then
+ if $($DIALOG --yesno "$message" 10 80 3>&1 1>&2 2>&3); then
+ $next_action
+ else
+ $next_action "No"
+ fi
+ else
+ echo "Error: Invalid function"
+
+ exit 1
+ fi
+}
+
+module_options+=(
+ ["see_ping,author"]="Joey Turner"
+ ["see_ping,ref_link"]=""
+ ["see_ping,feature"]="see_ping"
+ ["see_ping,desc"]="Check the internet connection with fallback DNS"
+ ["see_ping,example"]="see_ping"
+ ["see_ping,doc_link"]=""
+ ["see_ping,status"]="Active"
+)
+#
+# Function to check the internet connection
+#
+function see_ping() {
+ # List of servers to ping
+ servers=("1.1.1.1" "8.8.8.8")
+
+ # Check for internet connection
+ for server in "${servers[@]}"; do
+ if ping -q -c 1 -W 1 $server > /dev/null; then
+ echo "Internet connection: Present"
+ break
+ else
+ echo "Internet connection: Failed"
+ sleep 1
+ fi
+ done
+
+ if [[ $? -ne 0 ]]; then
+ read -n -r 1 -s -p "Warning: Configuration cannot work properly without a working internet connection. \
+ Press CTRL C to stop or any key to ignore and continue."
+ fi
+
+}
+
+module_options+=(
+ ["see_current_apt,author"]="Joey Turner"
+ ["see_current_apt,ref_link"]=""
+ ["see_current_apt,feature"]="see_current_apt"
+ ["see_current_apt,desc"]="Check when apt list was last updated and suggest updating or update"
+ ["see_current_apt,example"]="see_current_apt || see_current_apt update"
+ ["see_current_apt,doc_link"]=""
+ ["see_current_apt,status"]="Active"
+)
+#
+# Function to check when the package list was last updated
+#
+see_current_apt() {
+ # Number of seconds in a day
+ local update_apt="$1"
+ local day=86400
+ local ten_minutes=600
+ # Get the current date as a Unix timestamp
+ local now=$(date +%s)
+
+ # Get the timestamp of the most recently updated file in /var/lib/apt/lists/
+ local update=$(stat -c %Y /var/lib/apt/lists/* 2>/dev/null | sort -n | tail -1)
+
+ # Check if the update timestamp was found
+ if [[ -z "$update" ]]; then
+ echo "No package lists found."
+ return 1 # No package lists exist
+ fi
+
+ # Calculate the number of seconds since the last update
+ local elapsed=$((now - update))
+
+ # Check if any apt-related processes are running
+ if ps -C apt-get,apt,dpkg > /dev/null; then
+ echo "A package manager is currently running."
+ export running_pkg="true"
+ return 1 # The processes are running
+ else
+ export running_pkg="false"
+ fi
+
+ # Check if the package list is up-to-date
+ if ((elapsed < ten_minutes)); then
+ [[ "$update_apt" != "update" ]] && echo "The package lists are up-to-date."
+ return 0 # The package lists are up-to-date
+ else
+ [[ "$update_apt" != "update" ]] && echo "Update the package lists." # Suggest updating
+ [[ "$update_apt" == "update" ]] && apt_install_wrapper apt-get update
+ return 0 # The package lists are not up-to-date
+ fi
+}
+
+
+module_options+=(
+ ["sanitize_input,author"]=""
+ ["sanitize_input,ref_link"]=""
+ ["sanitize_input,feature"]="sanitize_input"
+ ["sanitize_input,desc"]="sanitize input cli"
+ ["sanitize_input,example"]="sanitize_input"
+ ["sanitize_input,status"]="Pending Review"
+ ["sanitize_input,doc_link"]=""
+)
+#
+# sanitize input cli
+#
+sanitize_input() {
+ local sanitized_input=()
+ for arg in "$@"; do
+ if [[ $arg =~ ^[a-zA-Z0-9_=]+$ ]]; then
+ sanitized_input+=("$arg")
+ else
+ echo "Invalid argument: $arg"
+ exit 1
+ fi
+ done
+ echo "${sanitized_input[@]}"
+}
diff --git a/lib/armbian-config/config.core.runtime.sh b/lib/armbian-config/config.core.runtime.sh
new file mode 100644
index 000000000..d85391fd8
--- /dev/null
+++ b/lib/armbian-config/config.core.runtime.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+# This script is used to dynamically modify a JSON structure that represents a menu in the Armbian configuration tool.
+# It performs several checks, such as checking if certain packages are installed and determining the network protocol used.
+# Based on these checks, it appends information to the descriptions of menu and submenu items, and shows or hides certain submenu items.
+# The modified JSON structure is stored in the variable 'json_data'.
+
+set_colors 2 # Set the color to green
+
+# Dynamically updates a JSON menu structure based on system checks.
+
+#
+# Initialize variables
+system_info="$(uname -m)"
+locale_setting="$LANG"
+installed_software="$(see_current_apt)"
+held_packages=$(apt-mark showhold)
+
+module_options+=(
+ ["update_json_data,author"]="Joey Turner"
+ ["update_json_data,ref_link"]=""
+ ["update_json_data,feature"]="update_json_data"
+ ["update_json_data,desc"]="Update JSON data with system information"
+ ["update_json_data,example"]="update_json_data"
+ ["update_json_data,status"]="review"
+ ["update_json_data,doc_link"]=""
+
+)
+#
+# Update JSON data with system information
+update_json_data() {
+ json_data=$(echo "$json_data" | jq --arg key "$1" --arg value "$2" \
+ '(.menu[] | select(.id == $key).description) += " (" + $value + ")"')
+}
+
+module_options+=(
+ ["update_submenu_data,author"]="Joey Turner"
+ ["update_submenu_data,ref_link"]=""
+ ["update_submenu_data,feature"]="update_submenu_data"
+ ["update_submenu_data,desc"]="Update submenu descriptions based on conditions"
+ ["update_submenu_data,example"]="update_submenu_data"
+ ["update_submenu_data,status"]="review"
+ ["update_submenu_data,doc_link"]=""
+)
+#
+# Update submenu descriptions based on conditions
+update_submenu_data() {
+ json_data=$(echo "$json_data" | jq --arg key "$1" --arg subkey "$2" --arg value "$3" \
+ '(.menu[] | select(.id==$key).sub[] | select(.id == $subkey).description) += " (" + $value + ")"')
+}
+
+#
+# Check if network adapter is IPv6 or IPv4
+network_adapter="$DEFAULT_ADAPTER"
+
+#
+# Main menu updates
+update_json_data "System" "$system_info"
+update_json_data "Network" "$network_adapter"
+update_json_data "Localisation" "$locale_setting"
+update_json_data "Software" "$installed_software"
+
+# Conditional submenu updates based on network type
+if [ "$network_adapter" = "IPv6" ]; then
+ update_submenu_data "Network" "N08" "IPV6"
+else
+ update_submenu_data "Network" "N08" "IPV4"
+fi
diff --git a/lib/armbian-config/config.jobs.json b/lib/armbian-config/config.jobs.json
new file mode 100644
index 000000000..45ecb1b13
--- /dev/null
+++ b/lib/armbian-config/config.jobs.json
@@ -0,0 +1,1008 @@
+{
+ "menu": [
+ {
+ "id": "System",
+ "description": "System wide and admin settings",
+ "sub": [
+ {
+ "id": "S01",
+ "description": "Enable Armbian kernel/firmware upgrades",
+ "prompt": "This will enable Armbian kernel upgrades?\nWould you like to continue?",
+ "command": [
+ "armbian_fw_manipulate unhold"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "[[ -n \"$(apt-mark showhold)\" ]]"
+ },
+ {
+ "id": "S02",
+ "description": "Disable Armbian kernel upgrades",
+ "prompt": "Disable Armbian kernel/firmware upgrades\nWould you like to continue?",
+ "command": [
+ "armbian_fw_manipulate hold"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "[[ -z \"$(apt-mark showhold)\" ]]"
+ },
+ {
+ "id": "S03",
+ "description": "Edit the boot environment",
+ "prompt": "This will open /boot/armbianEnv.txt file to edit\nCTRL+S to save\nCTLR+X to exit\nwould you like to continue?",
+ "command": [
+ "nano /boot/armbianEnv.txt"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ },
+ {
+ "id": "S04",
+ "description": "Install Linux headers",
+ "command": [
+ "Headers_install"
+ ],
+ "status": "Preview",
+ "doc_link": "https://github.com/armbian/config/wiki#System",
+ "src_reference": "https://github.com/armbian/config/blob/master/debian-config-jobs#L160",
+ "author": "https://github.com/Tearran",
+ "condition": "! are_headers_installed"
+ },
+ {
+ "id": "S05",
+ "description": "Remove Linux headers",
+ "command": [
+ "Headers_remove"
+ ],
+ "status": "Preview",
+ "doc_link": "https://github.com/armbian/config/wiki#System",
+ "src_reference": "https://github.com/armbian/config/blob/master/debian-config-jobs#L160",
+ "author": "https://github.com/Tearran",
+ "condition": "are_headers_installed"
+ },
+ {
+ "id": "S06",
+ "description": "Install to internal storage",
+ "command": [
+ "armbian-install"
+ ],
+ "status": "Preview",
+ "doc_link": "https://github.com/armbian/config/wiki#System",
+ "src_reference": "",
+ "author": "https://github.com/igorpecovnik",
+ "condition": "[[ -n $(ls /sbin/armbian-install) ]]"
+ },
+ {
+ "id": "S07.1",
+ "description": "Manage SSH login options",
+ "sub": [
+ {
+ "id": "S07",
+ "description": "Disable root login",
+ "command": [
+ "sed -i \"s|^#\\?PermitRootLogin.*|PermitRootLogin no|\" /etc/ssh/sshd_config",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q '^PermitRootLogin yes' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S08",
+ "description": "Enable root login",
+ "command": [
+ "sed -i \"s/^#\\?PermitRootLogin.*/PermitRootLogin yes/\" /etc/ssh/sshd_config",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q '^PermitRootLogin no' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S09",
+ "description": "Disable password login",
+ "command": [
+ "sed -i \"s/^#\\?PasswordAuthentication.*/PasswordAuthentication no/\" /etc/ssh/sshd_config",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q 'PasswordAuthentication yes' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S10",
+ "description": "Enable password login",
+ "command": [
+ "sed -i \"s/^#\\?PasswordAuthentication.*/PasswordAuthentication yes/\" /etc/ssh/sshd_config",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q 'PasswordAuthentication no' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S11",
+ "description": "Disable Public key authentication login",
+ "command": [
+ "sed -i \"s/^#\\?PubkeyAuthentication.*/PubkeyAuthentication no/\" /etc/ssh/sshd_config",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q 'PubkeyAuthentication yes' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S12",
+ "description": "Enable Public key authentication login",
+ "command": [
+ "sed -i \"s/^#\\?PubkeyAuthentication.*/PubkeyAuthentication yes/\" /etc/ssh/sshd_config",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q 'PubkeyAuthentication no' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S13",
+ "description": "Disable OTP authentication",
+ "command": [
+ "clear",
+ "! check_if_installed libpam-google-authenticator && ! check_if_installed qrencode || debconf-apt-progress -- apt-get -y purge libpam-google-authenticator qrencode",
+ "sed -i \"s/^#\\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/\" /etc/ssh/sshd_config || sed -i \"0,/KbdInteractiveAuthentication/s//ChallengeResponseAuthentication yes/\" /etc/ssh/sshd_config",
+ "sed -i '/^auth required pam_google_authenticator.so nullok/ d' /etc/pam.d/sshd",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q 'ChallengeResponseAuthentication yes' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S14",
+ "description": "Enable OTP authentication",
+ "command": [
+ "check_if_installed libpam-google-authenticator || debconf-apt-progress -- apt-get -y install libpam-google-authenticator",
+ "check_if_installed qrencode || debconf-apt-progress -- apt-get -y install qrencode",
+ "sed -i \"s/^#\\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication yes/\" /etc/ssh/sshd_config",
+ "sed -i $'/KbdInteractiveAuthentication/{iChallengeResponseAuthentication yes\\n:a;n;ba}' /etc/ssh/sshd_config || sed -n -i '/password updating/{p;:a;N;/@include common-password/!ba;s/.*\\n/auth required pam_google_authenticator.so nullok\\nauth required pam_permit.so\\n/};p' /etc/pam.d/sshd",
+ "[ ! -f /root/.google_authenticator ] && qr_code generate",
+ "systemctl restart sshd.service 2>/dev/null | systemctl restart ssh.service 2>/dev/null"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed libpam-google-authenticator || ! check_if_installed qrencode || grep -q '^ChallengeResponseAuthentication no' /etc/ssh/sshd_config || ! grep -q 'ChallengeResponseAuthentication' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S15",
+ "description": "Generate new OTP authentication QR code",
+ "command": [
+ "qr_code generate"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q '^ChallengeResponseAuthentication yes' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S16",
+ "description": "Show OTP authentication QR code",
+ "command": [
+ "qr_code"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Igor Pecovnik",
+ "condition": "grep -q '^ChallengeResponseAuthentication yes' /etc/ssh/sshd_config && [ -f /root/.google_authenticator ]"
+ },
+ {
+ "id": "S30",
+ "description": "Disable last login banner",
+ "command": [
+ "sed -i \"s/^#\\?PrintLastLog.*/PrintLastLog no/\" /etc/ssh/sshd_config",
+ "systemctl restart ssh.service "
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q '^PrintLastLog yes' /etc/ssh/sshd_config"
+ },
+ {
+ "id": "S31",
+ "description": "Enable last login banner",
+ "command": [
+ "sed -i \"s/^#\\?PrintLastLog.*/PrintLastLog yes/\" /etc/ssh/sshd_config",
+ "systemctl restart ssh.service "
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "grep -q '^PrintLastLog no' /etc/ssh/sshd_config"
+ }
+ ]
+ },
+ {
+ "id": "S17",
+ "description": "Change shell system wide to BASH",
+ "command": [
+ "export BASHLOCATION=$(grep /bash$ /etc/shells | tail -1)",
+ "sed -i \"s|^SHELL=.*|SHELL=${BASHLOCATION}|\" /etc/default/useradd",
+ "sed -i \"s|^DSHELL=.*|DSHELL=${BASHLOCATION}|\" /etc/adduser.conf",
+ "apt_install_wrapper apt-get -y purge armbian-zsh zsh-common zsh tmux",
+ "update_skel",
+ "awk -F'[/:]' '{if ($3 >= 1000 && $3 != 65534 || $3 == 0) print $1}' /etc/passwd | xargs -L1 chsh -s $(grep /bash$ /etc/shells | tail -1)"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "https://github.com/igorpecovnik",
+ "condition": "[[ $(cat /etc/passwd | grep \"^root:\" | rev | cut -d\":\" -f1 | cut -d\"/\" -f1| rev) == \"zsh\" ]]"
+ },
+ {
+ "id": "S18",
+ "description": "Change shell system wide to ZSH",
+ "command": [
+ "export ZSHLOCATION=$(grep /zsh$ /etc/shells | tail -1)",
+ "sed -i \"s|^SHELL=.*|SHELL=${ZSHLOCATION}|\" /etc/default/useradd",
+ "sed -i \"s|^DSHELL=.*|DSHELL=${ZSHLOCATION}|\" /etc/adduser.conf",
+ "apt_install_wrapper apt-get -y install armbian-zsh zsh-common zsh tmux",
+ "update_skel",
+ "awk -F'[/:]' '{if ($3 >= 1000 && $3 != 65534 || $3 == 0) print $1}' /etc/passwd | xargs -L1 chsh -s $(grep /zsh$ /etc/shells | tail -1)"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "https://github.com/igorpecovnik",
+ "condition": "[[ $(cat /etc/passwd | grep \"^root:\" | rev | cut -d\":\" -f1 | cut -d\"/\" -f1| rev) == \"bash\" ]]"
+ },
+ {
+ "id": "S19",
+ "description": "Switch to rolling release",
+ "prompt": "This will switch to rolling releases\n\nwould you like to continue?",
+ "command": [
+ "set_rolling"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "https://github.com/armbian/config/blob/master/debian-config-jobs#L1446",
+ "author": "Igor Pecovnik",
+ "condition": "grep -q 'apt.armbian.com' /etc/apt/sources.list.d/armbian.list && [[ -z \"$(apt-mark showhold)\" ]]"
+ },
+ {
+ "id": "S20",
+ "description": "Switch to stable release",
+ "prompt": "This will switch to stable releases\n\nwould you like to continue?",
+ "command": [
+ "set_stable"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "https://github.com/armbian/config/blob/master/debian-config-jobs#L1446",
+ "author": "Igor Pecovnik",
+ "condition": "grep -q 'beta.armbian.com' /etc/apt/sources.list.d/armbian.list && [[ -z \"$(apt-mark showhold)\" ]]"
+ },
+ {
+ "id": "S21",
+ "description": "Enable read only filesystem",
+ "prompt": "This will enable Armbian read-only filesystem. Reboot is mandatory?\n\nWould you like to continue?",
+ "command": [
+ "manage_overlayfs enable"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Igor Pecovnik",
+ "condition": "modinfo overlay > /dev/null 2>&1 && [[ -z $(findmnt -k /media/root-ro | tail -1) ]] && [[ \"${DISTRO}\"=Ubuntu ]]"
+ },
+ {
+ "id": "S22",
+ "description": "Disable read only filesystem",
+ "prompt": "This will disable Armbian read-only filesystem. Reboot is mandatory?\n\nWould you like to continue?",
+ "command": [
+ "manage_overlayfs disable"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Igor Pecovnik",
+ "condition": "command -v overlayroot-chroot > /dev/null 2>&1 && findmnt -k /media/root-ro | tail -1 | grep -w /media/root-ro > /dev/null 2>&1"
+ },
+ {
+ "id": "S23",
+ "description": "Adjust welcome screen (motd)",
+ "command": [
+ "adjust_motd"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "[ -f /etc/default/armbian-motd ]"
+ },
+ {
+ "id": "S24",
+ "description": "Install alternative kernels",
+ "prompt": "Switching between kernels might change functionality of your device. \n\nIt might fail to boot!",
+ "command": [
+ "switch_kernels"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Igor Pecovnik",
+ "condition": ""
+ },
+ {
+ "id": "S25",
+ "description": "Distribution upgrades",
+ "condition": "[ -f /etc/armbian-distribution-status ] && release_upgrade rolling verify || release_upgrade stable verify",
+ "sub": [
+ {
+ "id": "S26",
+ "description": "Upgrade to latest stable / LTS",
+ "prompt": "Release upgrade is irriversible operation which upgrades all packages. \n\nResoulted upgrade might break your build beyond repair!",
+ "command": [
+ "release_upgrade stable"
+ ],
+ "status": "Active",
+ "author": "Igor Pecovnik",
+ "condition": "[ -f /etc/armbian-distribution-status ] && release_upgrade stable verify"
+ },
+ {
+ "id": "S27",
+ "description": "Upgrade to rolling unstable",
+ "prompt": "Release upgrade is irriversible operation which upgrades all packages. \n\nResoulted upgrade might break your build beyond repair!",
+ "command": [
+ "release_upgrade rolling"
+ ],
+ "status": "Active",
+ "author": "Igor Pecovnik",
+ "condition": "[ -f /etc/armbian-distribution-status ] && release_upgrade rolling verify"
+ }
+ ]
+ },
+ {
+ "id": "S28",
+ "description": "Manage device tree overlays",
+ "command": [
+ "manage_dtoverlays"
+ ],
+ "status": "Active",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Gunjan Gupta",
+ "condition": "[ -n $OVERLAY_DIR ] && [ -n $BOOT_SOC ]"
+ }
+ ]
+ },
+ {
+ "id": "Network",
+ "description": "Fixed and wireless network settings",
+ "sub": [
+ {
+ "id": "N01",
+ "description": "Configure network interfaces",
+ "sub": [
+ {
+ "id": "N02",
+ "description": "Add / change interface",
+ "command": [
+ "network_config armbian"
+ ],
+ "status": "Preview",
+ "author": "Igor Pecovnik",
+ "condition": ""
+ },
+ {
+ "id": "N03",
+ "description": "Revert to Armbian defaults",
+ "command": [
+ "default_network_config"
+ ],
+ "status": "Preview",
+ "author": "Igor Pecovnik",
+ "condition": ""
+ },
+ {
+ "id": "N04",
+ "description": "Show configuration",
+ "command": [
+ "show_message <<< \"$(netplan get all)\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Igor Pecovnik",
+ "condition": "[[ -f /etc/netplan/armbian.yaml ]]"
+ },
+ {
+ "id": "N06",
+ "description": "Show active status",
+ "command": [
+ "show_message <<< \"$(netplan status --all)\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "Igor Pecovnik",
+ "condition": "[ -f /etc/netplan/armbian.yaml ] && [ netplan status 2>/dev/null ]"
+ }
+ ]
+ },
+ {
+ "id": "N15",
+ "description": "Install Bluetooth support",
+ "command": [
+ "see_current_apt ",
+ "debconf-apt-progress -- apt-get -y install bluetooth bluez bluez-tools",
+ "check_if_installed xserver-xorg && debconf-apt-progress -- apt-get -y --no-install-recommends install pulseaudio-module-bluetooth blueman"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed bluetooth && ! check_if_installed bluez && ! check_if_installed bluez-tools"
+ },
+ {
+ "id": "N16",
+ "description": "Remove Bluetooth support",
+ "command": [
+ "see_current_apt ",
+ "debconf-apt-progress -- apt-get -y remove bluetooth bluez bluez-tools",
+ "check_if_installed xserver-xorg && debconf-apt-progress -- apt-get -y remove pulseaudio-module-bluetooth blueman",
+ "debconf-apt-progress -- apt -y -qq autoremove"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed bluetooth || check_if_installed bluez || check_if_installed bluez-tools"
+ },
+ {
+ "id": "N17",
+ "description": "Bluetooth Discover",
+ "prompt": "This will enable bluetooth and discover devices\n\nWould you like to continue?",
+ "command": [
+ "connect_bt_interface"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed bluetooth || check_if_installed bluez || check_if_installed bluez-tools"
+ },
+ {
+ "id": "N18",
+ "description": "Toggle system IPv6/IPv4 internet protocol",
+ "prompt": "This will toggle your internet protocol\nWould you like to continue?",
+ "command": [
+ "toggle_ipv6 | show_infobox"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ }
+ ]
+ },
+ {
+ "id": "Localisation",
+ "description": "Localisation",
+ "sub": [
+ {
+ "id": "L00",
+ "description": "Change Global timezone (WIP)",
+ "command": [
+ "dpkg-reconfigure tzdata"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ },
+ {
+ "id": "L01",
+ "description": "Change Locales reconfigure the language and character set",
+ "command": [
+ "dpkg-reconfigure locales",
+ "source /etc/default/locale ; sed -i \"s/^LANGUAGE=.*/LANGUAGE=$LANG/\" /etc/default/locale",
+ "export LANGUAGE=$LANG"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ },
+ {
+ "id": "L02",
+ "description": "Change Keyboard layout",
+ "command": [
+ "dpkg-reconfigure keyboard-configuration ; setupcon ",
+ "update-initramfs -u"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ },
+ {
+ "id": "L03",
+ "description": "Change APT mirrors",
+ "prompt": "This will change the APT mirrors\nWould you like to continue?",
+ "command": [
+ "get_user_continue \"This is only a frontend test\" process_input"
+ ],
+ "status": "Disabled",
+ "author": ""
+ },
+ {
+ "id": "L04",
+ "description": "Change System Hostname",
+ "command": [ "change_system_hostname" ],
+ "status": "Preview",
+ "author": ""
+ }
+ ]
+ },
+ {
+ "id": "Software",
+ "description": "Run/Install 3rd party applications",
+ "sub": [
+ {
+ "id": "Desktops",
+ "description": "Install Desktop Environments",
+ "sub": [
+ {
+ "id": "SW02",
+ "description": "Install XFCE desktop",
+ "command": [
+ "install_de \"xfce\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": ""
+ },
+ {
+ "id": "SW03",
+ "description": "Install Gnome desktop",
+ "command": [
+ "install_de \"gnome\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": ""
+ },
+ {
+ "id": "SW04",
+ "description": "Install i3-wm desktop",
+ "command": [
+ "install_de \"i3-wm\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": ""
+ },
+ {
+ "id": "SW05",
+ "description": "Install Cinnamon desktop",
+ "command": [
+ "install_de \"cinnamon\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": ""
+ },
+ {
+ "id": "SW06",
+ "description": "Install kde-neon desktop",
+ "command": [
+ "install_de \"kde-neon\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": ""
+ }
+ ]
+ },
+ {
+ "id": "Netconfig",
+ "description": "Network tools",
+ "sub": [
+ {
+ "id": "SW08",
+ "description": "Install realtime console network usage monitor (nload)",
+ "command": [
+ "get_user_continue \"This operation will install nload.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y install nload"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed nload"
+ },
+ {
+ "id": "SW09",
+ "description": "Remove realtime console network usage monitor (nload)",
+ "command": [
+ "get_user_continue \"This operation will purge nload.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y purge nload"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed nload"
+ },
+ {
+ "id": "SW10",
+ "description": "Install bandwidth measuring tool (iperf3)",
+ "command": [
+ "get_user_continue \"This operation will install iperf3.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y install iperf3"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed iperf3"
+ },
+ {
+ "id": "SW11",
+ "description": "Remove bandwidth measuring tool (iperf3)",
+ "command": [
+ "get_user_continue \"This operation will purge iperf3.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y purge iperf3"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed iperf3"
+ },
+ {
+ "id": "SW12",
+ "description": "Install IP LAN monitor (iptraf-ng)",
+ "command": [
+ "get_user_continue \"This operation will install iptraf-ng.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y install iptraf-ng"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed iptraf-ng"
+ },
+ {
+ "id": "SW13",
+ "description": "Remove IP LAN monitor (iptraf-ng)",
+ "command": [
+ "get_user_continue \"This operation will purge nload.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y purge iptraf-ng"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed iptraf-ng"
+ },
+ {
+ "id": "SW14",
+ "description": "Install hostname broadcast via mDNS (avahi-daemon)",
+ "command": [
+ "get_user_continue \"This operation will install avahi-daemon and add configuration files.\nDo you wish to continue?\" process_input",
+ "check_if_installed avahi-daemon",
+ "debconf-apt-progress -- apt-get -y install avahi-daemon libnss-mdns",
+ "cp /usr/share/doc/avahi-daemon/examples/sftp-ssh.service /etc/avahi/services/",
+ "cp /usr/share/doc/avahi-daemon/examples/ssh.service /etc/avahi/services/",
+ "service avahi-daemon restart"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed avahi-daemon"
+ },
+ {
+ "id": "SW15",
+ "description": "Remove hostname broadcast via mDNS (avahi-daemon)",
+ "command": [
+ "get_user_continue \"This operation will purge avahi-daemon \nDo you wish to continue?\" process_input",
+ "check_if_installed avahi-daemon",
+ "systemctl stop avahi-daemon avahi-daemon.socket",
+ "debconf-apt-progress -- apt-get -y purge avahi-daemon"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed avahi-daemon"
+ }
+ ]
+ },
+ {
+ "id": "DevTools",
+ "description": "Development",
+ "sub": [
+ {
+ "id": "SW17",
+ "description": "Install tools for cloning and managing repositories (git)",
+ "command": [
+ "get_user_continue \"This operation will install git.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y install git"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed git"
+ },
+ {
+ "id": "SW18",
+ "description": "Remove tools for cloning and managing repositories (git)",
+ "command": [
+ "get_user_continue \"This operation will remove git.\n\nDo you wish to continue?\" process_input",
+ "debconf-apt-progress -- apt-get -y purge git"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed git"
+ }
+ ]
+ },
+ {
+ "id": "Benchy",
+ "description": "System benchmaking and diagnostics",
+ "command": [
+ "see_monitoring"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "[ -f /usr/bin/armbianmonitor ]"
+ },
+ {
+ "id": "Containers",
+ "description": "Containerlization and Virtual Machines",
+ "sub": [
+ {
+ "id": "SW25",
+ "description": "Install Docker Minimal",
+ "prompt": "This operation will install Docker Minimal.\nWould you like to continue?",
+ "command": [
+ "install_docker"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed docker-ce"
+ },
+ {
+ "id": "SW26",
+ "description": "Install Docker Engine",
+ "prompt": "This operation will install Docker Engine.\nWould you like to continue?",
+ "command": [
+ "install_docker engine"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed docker-compose-plugin"
+ },
+ {
+ "id": "SW27",
+ "description": "Remove Docker",
+ "prompt": "This operation will purge Docker.\nWould you like to continue?",
+ "command": [
+ "apt_install_wrapper apt -y purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed docker-ce"
+ },
+ {
+ "id": "SW28",
+ "description": "Purge all Docker images, containers, and volumes",
+ "prompt": "This operation will delete all Docker images, containers, and volumes.\nWould you like to continue?",
+ "command": [
+ "rm -rf /var/lib/docker",
+ "rm -rf /var/lib/containerd"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed docker-ce && [ -d /var/lib/docker ]"
+ }
+ ]
+ },
+ {
+ "id": "Media",
+ "description": "Media Servers and Editors",
+ "sub": [
+ {
+ "id": "SW21",
+ "description": "Install Plex Media server",
+ "prompt": "This operation will install Plex Media server.\nWould you like to continue?",
+ "command": [
+ "install_plexmediaserver"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed plexmediaserver"
+ },
+ {
+ "id": "SW22",
+ "description": "Remove Plex Media server",
+ "prompt": "This operation will purge Plex Media server.\nWould you like to continue?",
+ "command": [
+ "apt_install_wrapper apt-get -y purge plexmediaserver",
+ "sed -i '/plexmediaserver.gpg/s/^/#/g' /etc/apt/sources.list.d/plexmediaserver.list"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed plexmediaserver"
+ },
+ {
+ "id": "SW23",
+ "description": "Install Emby server",
+ "prompt": "This operation will install Emby server.\nWould you like to continue?",
+ "command": [
+ "install_embyserver"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed emby-server"
+ },
+ {
+ "id": "SW24",
+ "description": "Remove Emby server",
+ "prompt": "This operation will purge Emby server.\nWould you like to continue?",
+ "command": [
+ "apt_install_wrapper apt -y purge emby-server"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed emby-server"
+ }
+ ]
+ },
+ {
+ "id": "Management",
+ "description": "Remote Management tools",
+ "sub": [
+ {
+ "id": "M00",
+ "description": "Install Cockpit web-based management tool",
+ "prompt": "This operation will install Cockpit.\ncockpit cockpit-ws cockpit-system cockpit-storaged\nWould you like to continue?",
+ "command": [
+ "see_current_apt update",
+ "apt_install_wrapper apt -y install cockpit cockpit-ws cockpit-system cockpit-storaged "
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "! check_if_installed cockpit"
+ },
+ {
+ "id": "M01",
+ "description": "Purge Cockpit web-based management tool",
+ "prompt": "This operation will purge Cockpit.\nWould you like to continue?",
+ "command": [
+ "apt_install_wrapper apt -y purge cockpit"
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed cockpit"
+ },
+ {
+ "id": "M02",
+ "description": "Start Cockpit Service",
+ "command": [
+ "sudo systemctl enable --now cockpit.socket | show_infobox "
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed cockpit && ! systemctl is-enabled cockpit.socket > /dev/null 2>&1"
+ },
+ {
+ "id": "M03",
+ "description": "Stop Cockpit Service",
+ "command": [
+ "systemctl stop cockpit cockpit.socket",
+ "systemctl disable cockpit.socket | show_infobox "
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": "",
+ "condition": "check_if_installed cockpit && systemctl is-enabled cockpit.socket > /dev/null 2>&1"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "id": "Help",
+ "description": "About this app",
+ "sub": [
+ {
+ "id": "H00",
+ "description": "About This system. (WIP)",
+ "command": [
+ "show_message <<< \"This app is to help execute procedures to configure your system\n\nSome options may not work on manually modified systems\""
+ ],
+ "status": "Preview",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ },
+ {
+ "id": "H02",
+ "description": "List of Config function(WIP)",
+ "command": [
+ "show_message <<< see_use"
+ ],
+ "status": "Disabled",
+ "doc_link": "",
+ "src_reference": "",
+ "author": ""
+ }
+ ]
+ }
+ ]
+}
diff --git a/lib/armbian-config/modules/config.docs.sh b/lib/armbian-config/modules/config.docs.sh
new file mode 100644
index 000000000..d86595bc4
--- /dev/null
+++ b/lib/armbian-config/modules/config.docs.sh
@@ -0,0 +1,560 @@
+#!/bin/bash
+
+# This file is part of Armbian configuration utility.
+
+module_options+=(
+ ["generate_readme,author"]="Joey Turner"
+ ["generate_readme,ref_link"]="#L17"
+ ["generate_readme,feature"]="generate_readme"
+ ["generate_readme,desc"]="Generate Document files."
+ ["generate_readme,example"]="generate_readme"
+ ["generate_readme,status"]="review"
+ ["generate_readme,doc_link"]=""
+)
+#
+# Function to generate the README.md file
+#
+function generate_readme() {
+
+ # Get the current date
+ local current_date=$(date)
+ # setup doc folders
+ #mkdir -p "$script_dir/../share/doc/armbian-configng"
+
+ echo -e "Sorting data\nUpdating documentation" # current_date ;
+
+ cat << EOF > "$script_dir/../DOCUMENTATION.md"
+
+# Armbian Configuration Utility
+
+Utility for configuring your board, adjusting services, and installing applications. It comes with Armbian by default.
+
+To start the Armbian configuration utility, use the following command:
+~~~
+sudo armbian-config
+~~~
+
+$(see_full_list)
+
+## Install
+Armbian installation
+~~~
+sudo apt install armbian-config
+~~~
+
+3rd party Debian based distributions
+~~~
+{
+ sudo wget https://apt.armbian.com/armbian.key -O key
+ sudo gpg --dearmor < key | sudo tee /usr/share/keyrings/armbian.gpg > /dev/null
+ sudo chmod go+r /usr/share/keyrings/armbian.gpg
+ sudo echo "deb [arch=\$(dpkg --print-architecture) signed-by=/usr/share/keyrings/armbian.gpg] http://apt.armbian.com \$(lsb_release -cs) main \$(lsb_release -cs)-utils \$(lsb_release -cs)-desktop" | sudo tee /etc/apt/sources.list.d/armbian.list
+ sudo apt update
+ sudo apt install armbian-config
+}
+~~~
+
+***
+
+## CLI options
+Command line options.
+
+Use:
+~~~
+armbian-config --help
+~~~
+
+Outputs:
+~~~
+$(see_cmd_list)
+~~~
+
+## Legacy options
+Backward Compatible options.
+
+Use:
+~~~
+armbian-config main=Help
+~~~
+
+Outputs:
+~~~
+$(see_cli_legacy)
+~~~
+
+***
+
+## Development
+
+Development is divided into three sections:
+
+Click for more info:
+
+
+Jobs / JSON Object
+
+A list of the jobs defined in the Jobs file.
+~~~
+$(see_jq_menu_list)
+~~~
+
+
+
+
+Jobs API / Helper Functions
+
+These helper functions facilitate various operations related to job management, such as creation, updating, deletion, and listing of jobs, acting as a practical API for developers.
+
+$(see_function_table_md)
+
+
+
+
+
+
+Runtime / Board Statuses
+
+(WIP)
+
+This section outlines the runtime environment to check configurations and statuses for dynamically managing jobs based on JSON data.
+
+(WIP)
+
+
+
+
+## Testing and contributing
+
+
+Get Development
+
+Install the dependencies:
+~~~
+sudo apt install git jq whiptail
+~~~
+
+Get Development and contribute:
+~~~
+{
+git clone https://github.com/armbian/configng
+cd configng
+./armbian-configng --help
+}
+~~~
+
+Install and test Development deb:
+~~~
+{
+ sudo apt install whiptail
+ latest_release=\$(curl -s https://api.github.com/repos/armbian/configng/releases/latest)
+ deb_url=\$(echo "\$latest_release" | jq -r '.assets[] | select(.name | endswith(".deb")) | .browser_download_url')
+ curl -LO "\$deb_url"
+ deb_file=\$(echo "\$deb_url" | awk -F"/" '{print \$NF}')
+ sudo dpkg -i "\$deb_file"
+ sudo dpkg --configure -a
+ sudo apt --fix-broken install
+}
+~~~
+
+
+
+EOF
+
+}
+
+module_options+=(
+ ["serve_doc,author"]="Joey Turner"
+ ["serve_doc,ref_link"]=""
+ ["serve_doc,feature"]="serve_doc"
+ ["serve_doc,desc"]="Serve the edit and debug server."
+ ["serve_doc,example"]="serve_doc"
+ ["serve_doc,status"]="active"
+ ["serve_doc,doc_link"]=""
+)
+#
+# Function to serve the edit and debug server
+#
+function serve_doc() {
+ if [[ "$(id -u)" == "0" ]]; then
+ echo "Red alert! not for sudo user"
+ exit 1
+ fi
+ if [[ -z $CODESPACES ]]; then
+ # Start the Python server in the background
+ python3 -m http.server > /tmp/config.log 2>&1 &
+ local server_pid=$!
+ local input=("
+ Starting server...
+ Server PID: $server_pid
+
+ Press [Enter] to exit"
+ )
+
+ $DIALOG --title "Message Box" --msgbox "$input" 0 0
+
+ # Stop the server
+ kill "$server_pid"
+ else
+ echo "Info:GitHub Codespace"
+ exit 0
+ fi
+}
+
+module_options+=(
+ ["see_use,author"]="Joey Turner"
+ ["see_use,ref_link"]=""
+ ["see_use,feature"]="see_use"
+ ["see_use,desc"]="Show the usage of the functions."
+ ["see_use,example"]="see_use"
+ ["see_use,status"]="review"
+ ["see_use,doc_link"]=""
+)
+#
+# Function to parse the key-pairs (WIP)
+#
+function see_use() {
+ mod_message="Usage: \n\n"
+ # Iterate over the options
+ for key in "${!module_options[@]}"; do
+ # Split the key into function_name and type
+ IFS=',' read -r function_name type <<< "$key"
+ # If the type is 'long', append the option to the help message
+ if [[ "$type" == "feature" ]]; then
+ mod_message+="${module_options["$function_name,feature"]} - ${module_options["$function_name,desc"]}\n"
+ mod_message+=" ${module_options["$function_name,example"]}\n\n"
+ fi
+ done
+
+ echo -e "$mod_message"
+}
+
+module_options+=(
+ ["generate_json_options,author"]="Joey Turner"
+ ["generate_json_options,ref_link"]=""
+ ["generate_json_options,feature"]="generate_json"
+ ["generate_json_options,desc"]="Generate JSON-like object file."
+ ["generate_json_options,example"]="generate_json"
+ ["generate_json_options,status"]="review"
+ ["generate_json_options,doc_link"]=""
+)
+#
+# Function to generate a JSON-like object file
+#
+function generate_json_options() {
+ echo -e "{\n\"configng-helpers\" : ["
+ features=()
+ for key in "${!module_options[@]}"; do
+ if [[ $key == *",feature" ]]; then
+ features+=("${module_options[$key]}")
+ fi
+ done
+
+ for index in "${!features[@]}"; do
+ feature=${features[$index]}
+ desc_key="${feature},desc"
+ example_key="${feature},example"
+ author_key="${feature},author"
+ ref_key="${feature},ref_link"
+ status_key="${feature},status"
+ doc_key="${feature},doc_link"
+ author="${module_options[$author_key]}"
+ ref_link="${module_options[$ref_key]}"
+ status="${module_options[$status_key]}"
+ doc_link="${module_options[$doc_key]}"
+ desc="${module_options[$desc_key]}"
+ example="${module_options[$example_key]}"
+ echo " {"
+ echo " \"id\": \"$feature\","
+ echo " \"Author\": \"$author\","
+ echo " \"src_reference\": \"$ref_link\","
+ echo " \"description\": \"$desc\","
+ echo " \"command\": [ \"$example\" ]",
+ echo " \"status\": \"$status\","
+ echo " \"doc_link\": \"$doc_link\""
+ if [ $index -ne $((${#features[@]} - 1)) ]; then
+ echo " },"
+ else
+ echo " }"
+ fi
+ done
+ echo "]"
+ echo "}"
+}
+
+module_options+=(
+ ["generate_svg,author"]="Joey Turner"
+ ["generate_svg,ref_link"]=""
+ ["generate_svg,feature"]="generate_svg"
+ ["generate_svg,desc"]="Generate 'Armbian CPU logo' SVG for document file."
+ ["generate_svg,example"]="generate_svg"
+ ["generate_svg,status"]="review"
+ ["generate_svg,doc_link"]=""
+)
+#
+# This function is used to generate a armbian CPU logo
+#
+function generate_svg() {
+
+ cat << EOF
+
+
+EOF
+
+}
+
+module_options+=(
+ ["generate_jobs_from_json,author"]="Joey Turner"
+ ["generate_jobs_from_json,ref_link"]=""
+ ["generate_jobs_from_json,feature"]="generate_jobs_from_json"
+ ["generate_jobs_from_json,desc"]="Generate jobs from JSON file."
+ ["generate_jobs_from_json,example"]="generate_jobs_from_json"
+ ["generate_jobs_from_json,status"]="review"
+ ["generate_jobs_from_json,doc_link"]=""
+)
+#
+# This function is used to generate jobs links Table from JSON file.
+#
+function see_jobs_from_json_md() {
+
+ echo -e "\n"
+
+ # Use jq to parse the JSON
+ menu_items=$(jq -r '.menu | length' "$json_file")
+
+ for ((i = 0; i < $menu_items; i++)); do
+ cat=$(jq -r ".menu[$i].id" "$json_file")
+ description=$(jq -r ".menu[$i].description" "$json_file")
+ #echo -e "## $cat\n"
+ #echo -e "$description\n"
+ echo -e "| "$cat" | ID | Description | Documents | Status |"
+ echo -e "|:------ | :-- | :---------- | --------: | ------:|"
+
+ sub_items=$(jq -r ".menu[$i].sub | length" "$json_file")
+
+ for ((j = 0; j < $sub_items; j++)); do
+ id=$(jq -r ".menu[$i].sub[$j].id" "$json_file")
+ id_link=$(jq -r ".menu[$i].sub[$j].id" "$json_file" | tr '[:upper:]' '[:lower:]')
+ description=$(jq -r ".menu[$i].sub[$j].description" "$json_file")
+ command=$(jq -r ".menu[$i].sub[$j].command" "$json_file")
+ status=$(jq -r ".menu[$i].sub[$j].status" "$json_file")
+ doc_link=$(jq -r ".menu[$i].sub[$j].doc_link" "$json_file")
+
+ # Check if src_reference and doc_link are null
+ [ -z "$doc_link" ] && doc_link="#$id_link" || doc_link="[Document]($doc_link)"
+
+ echo -e "| | $id | $description | $doc_link | $status |"
+
+ done
+ echo -e "\n"
+ done
+
+}
+
+function see_full_list() {
+
+ # Use jq to parse the JSON into markdown
+ menu_items=$(jq -r '.menu | length' "$json_file")
+
+ for ((i = 0; i < $menu_items; i++)); do
+ cat=$(jq -r ".menu[$i].id" "$json_file")
+ description=$(jq -r ".menu[$i].description" "$json_file")
+
+ echo -e "- ## **$cat** "
+ #echo "$description"
+
+ sub_items=$(jq -r ".menu[$i].sub | length" "$json_file")
+
+ for ((j = 0; j < $sub_items; j++)); do
+ id=$(jq -r ".menu[$i].sub[$j].id" "$json_file")
+ id_link=$(jq -r ".menu[$i].sub[$j].id" "$json_file" | tr '[:upper:]' '[:lower:]')
+ description=$(jq -r ".menu[$i].sub[$j].description" "$json_file")
+ command=$(jq -r ".menu[$i].sub[$j].command" "$json_file")
+ status=$(jq -r ".menu[$i].sub[$j].status" "$json_file")
+ doc_link=$(jq -r ".menu[$i].sub[$j].doc_link" "$json_file")
+
+ # Check if src_reference and doc_link are null
+ [ -z "$doc_link" ] && doc_link="#$id_link" || doc_link="$doc_link"
+
+ echo -e " - **$id** - $description"
+ # echo -e " - Status: [$status]($doc_link)"
+
+ done
+ echo -e "\n"
+ done
+
+}
+
+module_options+=(
+ ["see_function_table_md,author"]="Joey Turner"
+ ["see_function_table_md,ref_link"]=""
+ ["see_function_table_md,feature"]="see_function_table_md"
+ ["see_function_table_md,desc"]="Generate this markdown table of all module_options"
+ ["see_function_table_md,example"]="see_function_table_md"
+ ["see_function_table_md,status"]="review"
+ ["see_function_table_md,doc_link"]=""
+)
+#
+# This function is used to generate a markdown table from the module_options array
+#
+function see_function_table_md() {
+ mod_message="| Description | Example | Credit |\n"
+ mod_message+="|:----------- | ------- |:------:|\n"
+ # Iterate over the options
+ for key in "${!module_options[@]}"; do
+ # Split the key into function_name and type
+ IFS=',' read -r function_name type <<< "$key"
+ # If the type is 'feature', append the option to the help message
+ if [[ "$type" == "feature" ]]; then
+ status=${module_options["$function_name,status"]}
+ ref_link=${module_options["$function_name,ref_link"]}
+ doc_link=${module_options["$function_name,doc_link"]}
+ ref_link_md=$([[ -n "$ref_link" ]] && echo "[Source]($ref_link)" || echo "X")
+ doc_link_md=$([[ -n "$doc_link" ]] && echo "[Document]($doc_link)" || echo "X")
+ status_md=$([[ -z "$ref_link" ]] && echo "source link Needed" || ([[ (-n "$ref_link" && -n "$doc_link") ]] && echo "Review" || echo "$status"))
+ mod_message+="| ${module_options["$function_name,desc"]} | ${module_options["$function_name,example"]} | ${module_options["$function_name,author"]} \n"
+ fi
+ done
+
+ echo -e "$mod_message"
+}
+
+module_options+=(
+ ["see_jq_menu_list,author"]="Joey Turner"
+ ["see_jq_menu_list,ref_link"]=""
+ ["see_jq_menu_list,feature"]="see_jq_menu_list"
+ ["see_jq_menu_list,desc"]="Generate a markdown list json objects using jq."
+ ["see_jq_menu_list,example"]="see_jq_menu_list"
+ ["see_jq_menu_list,status"]="review"
+ ["see_jq_menu_list,doc_link"]=""
+)
+#
+# This function is used to generate a markdown list from the json object using jq.
+#
+function see_jq_menu_list() {
+ jq -r '
+ .menu[] |
+ .sub[] |
+ "### " + .id + "\n\n" +
+ (.description // "No description available") + "\n\nJobs:\n\n~~~\n" +
+ ((.command // ["No commands available"]) | join("\n")) +
+ "\n~~~\n"
+' $json_file
+}
+
+module_options+=(
+ ["see_cmd_list,author"]="Joey Turner"
+ ["see_cmd_list,ref_link"]=""
+ ["see_cmd_list,feature"]="see_cmd_list"
+ ["see_cmd_list,desc"]="Generate a Help message for cli commands."
+ ["see_cmd_list,example"]="see_cmd_list [catagory]"
+ ["see_cmd_list,status"]="review"
+ ["see_cmd_list,doc_link"]=""
+)
+#
+# See command options
+#
+see_cmd_list() {
+ local help_menu="$1"
+
+ if [[ -n "$help_menu" && "$help_menu" != "cmd" ]]; then
+ echo "$json_data" | jq -r --arg menu "$help_menu" '
+ def recurse_menu(menu; level):
+ menu | .id as $id | .description as $desc |
+ if has("sub") then
+ if level == 0 then
+ "\n \($id) - \($desc)\n" + (.sub | map(recurse_menu(. ; level + 1)) | join("\n"))
+ elif level == 1 then
+ " \($id) - \($desc)\n" + (.sub | map(recurse_menu(. ; level + 1)) | join("\n"))
+ else
+ " \($id) - \($desc)\n" + (.sub | map(recurse_menu(. ; level + 1)) | join("\n"))
+ end
+ else
+ if level == 0 then
+ " --cmd \($id) - \($desc)"
+ elif level == 1 then
+ " --cmd \($id) - \($desc)"
+ else
+ "\t--cmd \($id) - \($desc)"
+ end
+ end;
+
+ # Find the correct menu if $menu is passed, otherwise show all
+ if $menu == "" then
+ .menu | map(recurse_menu(. ; 0)) | join("\n")
+ else
+ .menu | map(select(.id == $menu) | recurse_menu(. ; 0)) | join("\n")
+ end
+ '
+ elif [[ -z "$1" || "$1" == "cmd" ]]; then
+ echo "$json_data" | jq -r --arg menu "$help_menu" '
+ def recurse_menu(menu; level):
+ menu | .id as $id | .description as $desc |
+ if has("sub") then
+ if level == 0 then
+ "\n \($id) - \($desc)\n" + (.sub | map(recurse_menu(. ; level + 1)) | join("\n"))
+ elif level == 1 then
+ " \($id) - \($desc)\n" + (.sub | map(recurse_menu(. ; level + 1)) | join("\n"))
+ else
+ " \($id) - \($desc)\n" + (.sub | map(recurse_menu(. ; level + 1)) | join("\n"))
+ end
+ else
+ if level == 0 then
+ " --cmd \($id) - \($desc)"
+ elif level == 1 then
+ " --cmd \($id) - \($desc)"
+ else
+ "\t--cmd \($id) - \($desc)"
+ end
+ end;
+ .menu | map(recurse_menu(. ; 0)) | join("\n")
+ '
+
+ else
+ echo "nope"
+ fi
+}
+
+
+module_options+=(
+ ["see_cli_legacy,author"]="Joey Turner"
+ ["see_cli_legacy,ref_link"]=""
+ ["see_cli_legacy,feature"]="see_cli_legacy"
+ ["see_cli_legacy,desc"]="Generate a Help message legacy cli commands."
+ ["see_cli_legacy,example"]="see_cli_legacy"
+ ["see_cli_legacy,status"]="review"
+ ["see_cli_legacy,doc_link"]=""
+)
+function see_cli_legacy() {
+ local script_name=$(basename "$0")
+ cat << EOF
+Legacy Options (Backward Compatible)
+Please use 'armbian-config --help' for more information.
+
+Usage: $script_name main=[arguments] selection=[options]
+
+EOF
+
+cat << EOF
+ $script_name main=System selection=Headers - Install headers:
+ $script_name main=System selection=Headers_remove - Remove headers:
+EOF
+
+ # TODO Migrate following features
+
+ # $script_name main=System selection=Firmware - Update, upgrade and reboot:
+ # $script_name main=System selection=Nightly - Switch to nightly builds:
+ # $script_name main=System selection=Stable - Switch to stable builds:
+ # $script_name main=System selection=Default - Install default desktop:
+ # $script_name main=System selection=ZSH - Change to ZSH:
+ # $script_name main=System selection=BASH - Change to BASH:
+ # $script_name main=System selection=Stable - Change to stable repository [branch=dev]:
+ # $script_name main=System selection=Nightly - Change to nightly repository [branch=dev]:
+ # $script_name main=Software selection=Source_install - Install kernel source:
+ # $script_name main=Software selection=Source_remove - Remove kernel source:
+ # $script_name main=Software selection=Avahi - Install Avahi mDNS/DNS-SD daemon:
+
+}
diff --git a/lib/armbian-config/modules/config.network.sh b/lib/armbian-config/modules/config.network.sh
new file mode 100644
index 000000000..f05d9d370
--- /dev/null
+++ b/lib/armbian-config/modules/config.network.sh
@@ -0,0 +1,472 @@
+#!/bin/bash
+
+module_options+=(
+ ["check_ip_version,author"]="Joey Turner"
+ ["check_ip_version,ref_link"]=""
+ ["check_ip_version,feature"]="check_ip_version"
+ ["check_ip_version,desc"]="Check if a domain is reachable via IPv4 and IPv6"
+ ["check_ip_version,example"]="check_ip_version google.com"
+ ["check_ip_version,status"]="review"
+ ["check_ip_version,doc_link"]=""
+)
+#
+#
+#
+check_ip_version() {
+ domain=${1:-armbian.com}
+
+ if ping -c 1 $domain > /dev/null 2>&1; then
+ echo "IPv4"
+ elif ping6 -c 1 $domain > /dev/null 2>&1; then
+ echo "IPv6"
+ else
+ echo "Unreachable"
+ fi
+}
+
+module_options+=(
+ ["toggle_ipv6,author"]="Joey Turner"
+ ["toggle_ipv6,ref_link"]=""
+ ["toggle_ipv6,feature"]="toggle_ipv6"
+ ["toggle_ipv6,desc"]="Toggle IPv6 on or off"
+ ["toggle_ipv6,example"]="toggle_ipv6"
+ ["toggle_ipv6,status"]="review"
+ ["toggle_ipv6,doc_link"]=""
+)
+#
+# Function to toggle IPv6 on or off
+#
+toggle_ipv6() {
+ # Check if IPv6 is currently enabled
+ if sysctl net.ipv6.conf.all.disable_ipv6 | grep -q 0; then
+ # If IPv6 is enabled, disable it
+ echo "Disabling IPv6..."
+ sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
+ sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
+ sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
+ echo "IPv6 is now disabled."
+ # Confirm that IPv6 is disabled
+ if sysctl net.ipv6.conf.all.disable_ipv6 | grep -q 1; then
+ check_ip_version google.com
+ else
+ check_ip_version google.com
+ fi
+ else
+ # If IPv6 is disabled, enable it
+ echo "Enabling IPv6..."
+ sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
+ sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0
+ sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
+ echo "IPv6 is now enabled."
+ # Confirm that IPv6 is enabled
+ if sysctl net.ipv6.conf.all.disable_ipv6 | grep -q 0; then
+ check_ip_version google.com
+ else
+ check_ip_version google.com
+ fi
+ fi
+
+ # Now call the function with a domain name
+
+}
+
+module_options+=(
+ ["see_ping,author"]="Joey Turner"
+ ["see_ping,ref_link"]="https://github.com/Tearran/configng/blob/main/config.ng.functions.sh#632"
+ ["see_ping,feature"]="see_ping"
+ ["see_ping,desc"]="Check the internet connection with fallback DNS"
+ ["see_ping,example"]="see_ping"
+ ["see_ping,doc_link"]=""
+ ["see_ping,status"]="review"
+)
+#
+# Function to check the internet connection
+#
+function see_ping() {
+ # List of servers to ping
+ servers=("1.1.1.1" "8.8.8.8")
+
+ # Check for internet connection
+ for server in "${servers[@]}"; do
+ if ping -q -c 1 -W 1 $server > /dev/null; then
+ echo "Internet connection: Present"
+ break
+ else
+ echo "Internet connection: Failed"
+ sleep 1
+ fi
+ done
+
+ if [[ $? -ne 0 ]]; then
+ read -n -r 1 -s -p "Warning: Configuration cannot work properly without a working internet connection. \
+ Press CTRL C to stop or any key to ignore and continue."
+ fi
+
+}
+
+module_options+=(
+ ["default_network_config,author"]="Igor Pecovnik"
+ ["default_network_config,ref_link"]=""
+ ["default_network_config,feature"]="default_network_config"
+ ["default_network_config,desc"]="Revert network config back to Armbian defaults"
+ ["default_network_config,example"]="default_network_config"
+ ["default_network_config,doc_link"]=""
+ ["default_network_config,status"]="review"
+)
+#
+# Function to revert network configuration to Armbian defaults
+#
+function default_network_config() {
+
+ local yamlfile=10-dhcp-all-interfaces
+
+ # store current configs to temporal folder
+ store_netplan_config
+
+ get_user_continue "This action might disconnect you from network.\n\nAre you sure network was configured correctly?" process_input
+ if [[ $? == 0 ]]; then
+ # remove all configs
+ rm -f /etc/netplan/*.yaml
+ # disable hostapd
+ systemctl stop hostapd 2> /dev/null
+ systemctl disable hostapd 2> /dev/null
+ # reset netplan config
+ netplan set --origin-hint ${yamlfile} renderer=${NETWORK_RENDERER}
+ netplan set --origin-hint ${yamlfile} ethernets.all-eth-interfaces.dhcp4=true
+ netplan set --origin-hint ${yamlfile} ethernets.all-eth-interfaces.dhcp6=true
+ netplan set --origin-hint ${yamlfile} ethernets.all-eth-interfaces.match.name=e*
+
+ # exceptions
+ if [[ "${NETWORK_RENDERER}" == "NetworkManager" ]]; then
+ # uninstall packages
+ apt_install_wrapper apt-get -y purge hostapd
+ netplan apply
+ nmcli con down br0
+ else
+ # uninstall packages
+ apt_install_wrapper apt-get -y purge hostapd networkd-dispatcher
+ # drop and delete bridge interface in case its there
+ if [[ -n $(ip link show type bridge) ]]; then
+ ip link set br0 down
+ brctl delbr br0
+ networkctl reconfigure br0
+ fi
+ # remove networkd-dispatcher hook
+ rm -f /etc/networkd-dispatcher/carrier.d/armbian-ap
+ netplan apply
+ fi
+ else
+ restore_netplan_config
+ fi
+}
+
+module_options+=(
+ ["default_wireless_network_config,author"]="Igor Pecovnik"
+ ["default_wireless_network_config,ref_link"]=""
+ ["default_wireless_network_config,feature"]="default_wireless_network_config"
+ ["default_wireless_network_config,desc"]="Stop hostapd, clean config"
+ ["default_wireless_network_config,example"]="default_wireless_network_config"
+ ["default_wireless_network_config,doc_link"]=""
+ ["default_wireless_network_config,status"]="review"
+)
+function default_wireless_network_config(){
+
+ # defaul yaml file
+ local yamlfile=${1:-armbian}
+ local adapter=${2:-wlan0}
+
+ # remove wifi from netplan
+ if [[ -f /etc/netplan/${yamlfile}.yaml ]]; then
+ sed -i -e 'H;x;/^\( *\)\n\1/{s/\n.*//;x;d;}' -e 's/.*//;x;/'$adapter':/{s/^\( *\).*/ \1/;x;d;}' /etc/netplan/${yamlfile}.yaml
+ sed -i -e 'H;x;/^\( *\)\n\1/{s/\n.*//;x;d;}' -e 's/.*//;x;/- '$adapter'/{s/^\( *\).*/ \1/;x;d;}' /etc/netplan/${yamlfile}.yaml
+ sed -i -e 'H;x;/^\( *\)\n\1/{s/\n.*//;x;d;}' -e 's/.*//;x;/wifis:/{s/^\( *\).*/ \1/;x;d;}' /etc/netplan/${yamlfile}.yaml
+ fi
+
+ # remove networkd-dispatcher hook
+ rm -f /etc/networkd-dispatcher/carrier.d/armbian-ap
+ # remove network-manager dispatcher hook
+ rm -f /etc/NetworkManager/dispatcher.d/armbian-ap
+
+ # hostapd needs more cleaning
+ if systemctl is-active hostapd 1> /dev/null; then
+ systemctl stop hostapd 2> /dev/null
+ systemctl disable hostapd 2> /dev/null
+ fi
+
+ # apply config
+ netplan apply
+
+ # exceptions
+ if [[ "${NETWORK_RENDERER}" == "NetworkManager" ]]; then
+ # uninstall packages
+ apt_install_wrapper apt-get -y --no-install-recommends purge hostapd
+ systemctl restart NetworkManager
+ else
+ # uninstall packages
+ apt_install_wrapper apt-get -y --no-install-recommends purge hostapd networkd-dispatcher
+ brctl delif br0 $adapter 2> /dev/null
+ networkctl reconfigure br0
+ fi
+
+}
+
+
+module_options+=(
+ ["network_config,author"]="Igor Pecovnik"
+ ["network_config,ref_link"]=""
+ ["network_config,feature"]="network_config"
+ ["network_config,desc"]="Netplan wrapper"
+ ["network_config,example"]="network_config"
+ ["network_config,doc_link"]=""
+ ["network_config,status"]="review"
+)
+#
+# Function to select network adapter
+#
+function network_config() {
+
+ # defaul yaml file
+ local yamlfile=${1:-armbian}
+
+ # store current configs to temporal folder
+ store_netplan_config
+
+ LIST=()
+ HIDE_IP_PATTERN="^dummy0|^lo|^docker|^virbr|^br"
+ for f in /sys/class/net/*; do
+ interface=$(basename $f)
+ if [[ $interface =~ $HIDE_IP_PATTERN ]]; then
+ continue
+ else
+ [[ $interface == w* ]] && devicetype="wifi" || devicetype="wired"
+ QUERY=$(ip -br addr show dev $interface | awk '{ print $1, " " , ($3==""?"unassigned":$3)"['$devicetype']" }')
+ [[ -n $QUERY ]] && LIST+=($QUERY)
+ fi
+ done
+ LIST_LENGTH=$((${#LIST[@]} / 2))
+ adapter=$($DIALOG --title "Select interface" --menu "" $((${LIST_LENGTH} + 8)) 60 $((${LIST_LENGTH})) "${LIST[@]}" 3>&1 1>&2 2>&3)
+ if [[ -n $adapter && $? == 0 ]]; then
+ #
+ # Wireless networking
+ #
+ if [[ "$adapter" == w* ]]; then
+
+ LIST=()
+ if systemctl is-active --quiet service hostapd; then
+ LIST+=("stop" "Disable access point")
+ else
+ LIST=("sta" "Connect to access point")
+ LIST+=("ap" "Become an access point")
+ fi
+ LIST_LENGTH=$((${#LIST[@]} / 2))
+ wifimode=$($DIALOG --title "Select wifi mode" --menu "" $((${LIST_LENGTH} + 8)) 60 $((${LIST_LENGTH})) "${LIST[@]}" 3>&1 1>&2 2>&3)
+ case $wifimode in
+ stop)
+ # disable hostapd and cleanup config
+ default_wireless_network_config "${yamlfile}" "${adapter}"
+ ;;
+
+ sta)
+ ip link set ${adapter} up
+ default_wireless_network_config "${yamlfile}" "${adapter}"
+ LIST=()
+ LIST=($(iw dev ${adapter} scan 2> /dev/null | grep 'SSID\|^BSS' | cut -d" " -f2 | sed "s/(.*//g" | xargs -n2 -d'\n' | awk '{print $2,$1}'))
+ sleep 1
+ LIST_LENGTH=$((${#LIST[@]} / 2))
+ if [[ ${#LIST[@]} == 0 ]]; then
+ restore_netplan_config
+ else
+ SELECTED_SSID=$($DIALOG --title "Select SSID" --menu "rf" $((${LIST_LENGTH} + 6)) 50 $((${LIST_LENGTH})) "${LIST[@]}" 3>&1 1>&2 2>&3)
+ if [[ -n $SELECTED_SSID ]]; then
+ SELECTED_PASSWORD=$($DIALOG --title "Enter new password for $SELECTED_SSID" --passwordbox "" 7 50 3>&1 1>&2 2>&3)
+ if [[ -n $SELECTED_PASSWORD ]]; then
+ # connect to AP
+ netplan set --origin-hint ${yamlfile} renderer=${NETWORK_RENDERER}
+ netplan set --origin-hint ${yamlfile} wifis.$adapter.access-points."${SELECTED_SSID//./\\.}".password=${SELECTED_PASSWORD}
+ netplan set --origin-hint ${yamlfile} wifis.$adapter.dhcp4=true
+ netplan set --origin-hint ${yamlfile} wifis.$adapter.dhcp6=true
+ show_message <<< "$(netplan get all)"
+ $DIALOG --title " Changing network settings " --yes-button "Yes" --no-button "Cancel" --yesno \
+ "This action might disconnect you from network.\n\nAre you sure network was configured correctly?" 9 50
+ if [[ $? = 0 ]]; then
+ netplan apply
+ else
+ restore_netplan_config
+ fi
+ fi
+ fi
+ fi
+ ;;
+
+ ap)
+ ip link set ${adapter} up
+ default_wireless_network_config "${yamlfile}" "${adapter}"
+ ! check_if_installed hostapd && apt_install_wrapper apt-get -y --no-install-recommends install hostapd networkd-dispatcher
+ SELECTED_SSID=$($DIALOG --title "Enter SSID for AP" --inputbox "" 7 50 3>&1 1>&2 2>&3)
+ if [[ -n "${SELECTED_SSID}" && $? == 0 ]]; then
+ SELECTED_PASSWORD=$($DIALOG --title "Enter new password for $SELECTED_SSID" --passwordbox "" 7 50 3>&1 1>&2 2>&3)
+ if [[ -n "${SELECTED_PASSWORD}" && $? == 0 ]]; then
+ # start bridged AP
+ netplan set --origin-hint ${yamlfile} renderer=${NETWORK_RENDERER}
+ netplan set --origin-hint ${yamlfile} ethernets.$adapter.dhcp4=no
+ netplan set --origin-hint ${yamlfile} ethernets.$adapter.dhcp6=no
+ netplan set --origin-hint ${yamlfile} bridges.br0.interfaces='['$adapter']'
+ cat <<- EOF > "/etc/hostapd/hostapd.conf"
+ interface=$adapter
+ driver=nl80211
+ ssid=$SELECTED_SSID
+ hw_mode=g
+ channel=7
+ wmm_enabled=0
+ macaddr_acl=0
+ auth_algs=1
+ ignore_broadcast_ssid=0
+ wpa=2
+ wpa_passphrase=$SELECTED_PASSWORD
+ wpa_key_mgmt=WPA-PSK
+ wpa_pairwise=TKIP
+ rsn_pairwise=CCMP
+ EOF
+ netplan apply
+ # Start hostapd services
+ systemctl unmask hostapd 2>/dev/null
+ systemctl enable hostapd 2>/dev/null
+ systemctl start hostapd 2>/dev/null
+ # Sometimes it fails to add to the bridge
+ brctl addif br0 $adapter 2>/dev/null
+ # Add hooks to hack wrong if type
+ if [[ "${NETWORK_RENDERER}" == "NetworkManager" ]]; then
+ mkdir -p /etc/NetworkManager/dispatcher.d
+ cat <<- EOF > "/etc/NetworkManager/dispatcher.d/armbian-ap"
+ #!/bin/bash
+ # Added by armbian-config
+ interface=\$1
+ status=\$2
+ case "\$status" in
+ up)
+ if [[ "\$interface" == "br0" ]]; then
+ service hostapd restart
+ brctl addif br0 $adapter
+ fi
+ ;;
+ down)
+ if [[ "\$interface" == "br0" ]]; then
+ brctl delif br0 $adapter
+ fi
+ ;;
+ esac
+ EOF
+ chmod +x /etc/NetworkManager/dispatcher.d/armbian-ap
+ else
+ # workarounding bug in netplan for failing to add wireless adaptor to bridge
+ # this might not be needed on all versions
+ mkdir -p /etc/networkd-dispatcher/carrier.d/
+ cat <<- EOF > "/etc/networkd-dispatcher/carrier.d/armbian-ap"
+ #!/bin/sh
+ brctl addif br0 $adapter
+ netplan apply
+ exit 0
+ EOF
+ chmod +x /etc/networkd-dispatcher/carrier.d/armbian-ap
+ fi
+ fi
+ fi
+ ;;
+
+ *)
+ echo -n "unknown"
+ exit
+ ;;
+ esac
+
+ else
+
+ #
+ # Wired networking
+ #
+
+ # remove default configuration
+ rm -f /etc/netplan/10-dhcp-all-interfaces.yaml
+
+ LIST=("dhcp" "Auto IP assigning")
+ LIST+=("static" "Set IP manually")
+ wiredmode=$($DIALOG --title "Select IP mode" --menu "" $((${LIST_LENGTH} + 8)) 60 $((${LIST_LENGTH})) "${LIST[@]}" 3>&1 1>&2 2>&3)
+ if [[ "${wiredmode}" == "dhcp" && $? == 0 ]]; then
+ [[ -f /etc/netplan/${yamlfile}.yaml ]] && sed -i -e 'H;x;/^\( *\)\n\1/{s/\n.*//;x;d;}' -e 's/.*//;x;/bridges/{s/^\( *\).*/ \1/;x;d;}' /etc/netplan/${yamlfile}.yaml
+ netplan set --origin-hint ${yamlfile} renderer=${NETWORK_RENDERER}
+ netplan set --origin-hint ${yamlfile} ethernets.$adapter.dhcp4=no
+ netplan set --origin-hint ${yamlfile} ethernets.$adapter.dhcp6=no
+ netplan set --origin-hint ${yamlfile} bridges.br0.interfaces='['$adapter']'
+ netplan set --origin-hint ${yamlfile} bridges.br0.dhcp4=yes
+ netplan set --origin-hint ${yamlfile} bridges.br0.dhcp6=yes
+ show_message <<< "$(netplan get all)"
+ $DIALOG --title " Changing network settings " --yes-button "Yes" --no-button "Cancel" --yesno \
+ "This action might disconnect you from network.\n\nAre you sure network was configured correctly?" 9 50
+ if [[ $? = 0 ]]; then
+ # apply NetPlan
+ netplan apply
+ [[ "${NETWORK_RENDERER}" == "NetworkManager" ]] && systemctl restart NetworkManager;
+ else
+ restore_netplan_config
+ fi
+ elif [[ "${wiredmode}" == "static" ]]; then
+ local ips=()
+ for f in /sys/class/net/*; do
+ local intf=$(basename $f)
+ # skip unwanted
+ if [[ $intf =~ ^dummy0|^lo|^docker|^virbr ]]; then
+ continue
+ else
+ local tmp=$(ip -4 addr show dev $intf | grep -v "$intf:avahi" | awk '/inet/ {print $2}' | uniq)
+ [[ -n $tmp ]] && ips+=("$tmp")
+ fi
+ done
+ #address=${ips[@]}
+ address=${ips[0]} # use only 1st one
+ [[ -z "${address}" ]] && address="1.2.3.4/5"
+ # clean values from config
+ [[ -f /etc/netplan/${yamlfile}.yaml ]] && sed -i -e 'H;x;/^\( *\)\n\1/{s/\n.*//;x;d;}' -e 's/.*//;x;/bridges/{s/^\( *\).*/ \1/;x;d;}' /etc/netplan/${yamlfile}.yaml
+ address=$($DIALOG --title "Enter IP for $adapter" --inputbox "\nValid format: $address" 9 40 "$address" 3>&1 1>&2 2>&3)
+ if [[ -n $address && $? == 0 ]]; then
+ defaultroute=$(ip route show default | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]" | head -1 | xargs)
+ defaultroute=$($DIALOG --title "Enter IP for default route" --inputbox "\nValid format: $defaultroute" 9 40 "$defaultroute" 3>&1 1>&2 2>&3)
+ if [[ -n $defaultroute && $? == 0 ]]; then
+ nameservers="9.9.9.9,1.1.1.1"
+ nameservers=$($DIALOG --title "Enter DNS server" --inputbox "\nValid format: $nameservers" 9 40 "$nameservers" 3>&1 1>&2 2>&3)
+ else
+ restore_netplan_config
+ fi
+ if [[ -n $nameservers && $? == 0 ]]; then
+ netplan set --origin-hint ${yamlfile} renderer=${NETWORK_RENDERER}
+ netplan set --origin-hint ${yamlfile} ethernets.$adapter.dhcp4=no
+ netplan set --origin-hint ${yamlfile} ethernets.$adapter.dhcp6=no
+ netplan set --origin-hint ${yamlfile} bridges.br0.interfaces='['$adapter']'
+ netplan set --origin-hint ${yamlfile} bridges.br0.addresses='['$address']'
+ netplan set --origin-hint ${yamlfile} bridges.br0.routes='[{"to":"0.0.0.0/0", "via": "'$defaultroute'","metric":200}]'
+ netplan set --origin-hint ${yamlfile} bridges.br0.nameservers.addresses='['$nameservers']'
+ else
+ restore_netplan_config
+ fi
+ if [[ $? == 0 ]]; then
+ show_message <<< "$(netplan get all)"
+ $DIALOG --title " Changing network settings " --yes-button "Yes" --no-button "Cancel" --yesno \
+ "This action might disconnect you from network.\n\nAre you sure network was configured correctly?" 9 50
+ if [[ $? = 0 ]]; then
+ # apply NetPlan
+ netplan apply
+ [[ "${NETWORK_RENDERER}" == "NetworkManager" ]] && systemctl restart NetworkManager;
+ else
+ restore_netplan_config
+ fi
+ fi
+ else
+ restore_netplan_config
+ fi
+ else
+ restore_netplan_config
+ fi
+ fi
+ else
+ restore_netplan_config
+ fi
+}
diff --git a/lib/armbian-config/modules/config.software.sh b/lib/armbian-config/modules/config.software.sh
new file mode 100644
index 000000000..4f72b0352
--- /dev/null
+++ b/lib/armbian-config/modules/config.software.sh
@@ -0,0 +1,110 @@
+module_options+=(
+ ["see_monitoring,author"]="Joey Turner"
+ ["see_monitoring,ref_link"]=""
+ ["see_monitoring,feature"]="see_monitoring"
+ ["see_monitoring,desc"]="Menu for armbianmonitor features"
+ ["see_monitoring,example"]="see_monitoring"
+ ["see_monitoring,status"]="review"
+ ["see_monitoring,doc_link"]=""
+)
+#
+# @decrition generate a menu for armbianmonitor
+#
+function see_monitoring() {
+ if [ -f /usr/bin/htop ]; then
+ choice=$(armbianmonitor -h | grep -Ev '^\s*-c\s|^\s*-M\s' | show_menu)
+
+ armbianmonitor -$choice
+
+ else
+ echo "htop is not installed"
+ fi
+}
+
+module_options+=(
+ ["update_skel,author"]="Kat Schwarz"
+ ["update_skel,ref_link"]=""
+ ["update_skel,feature"]="install_plexmediaserver"
+ ["update_skel,desc"]="Install plexmediaserver from repo using apt"
+ ["update_skel,example"]="install_plexmediaserver"
+ ["update_skel,status"]="Active"
+)
+#
+# Install plexmediaserver using apt
+#
+install_plexmediaserver() {
+ if [ ! -f /etc/apt/sources.list.d/plexmediaserver.list ]; then
+ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/plexmediaserver.gpg] https://downloads.plex.tv/repo/deb public main" | sudo tee /etc/apt/sources.list.d/plexmediaserver.list > /dev/null 2>&1
+ else
+ sed -i "/downloads.plex.tv/s/^#//g" /etc/apt/sources.list.d/plexmediaserver.list > /dev/null 2>&1
+ fi
+ # Note: for compatibility with existing source file in some builds format must be gpg not asc
+ # and location must be /usr/share/keyrings
+ wget -qO- https://downloads.plex.tv/plex-keys/PlexSign.key | gpg --dearmor | sudo tee /usr/share/keyrings/plexmediaserver.gpg > /dev/null 2>&1
+ apt_install_wrapper apt-get update
+ apt_install_wrapper apt-get -y install plexmediaserver
+ $DIALOG --msgbox "To test that Plex Media Server has installed successfully\nIn a web browser go to http://localhost:32400/web or \nhttp://127.0.0.1:32400/web on this computer." 9 70
+}
+
+module_options+=(
+ ["update_skel,author"]="Kat Schwarz"
+ ["update_skel,ref_link"]=""
+ ["update_skel,feature"]="install_embyserver"
+ ["update_skel,desc"]="Download a embyserver deb file from a URL and install using apt"
+ ["update_skel,example"]="install_embyserver"
+ ["update_skel,status"]="Active"
+)
+#
+# Download a deb file from a URL and install using wget and apt with dialog progress bars
+#
+install_embyserver() {
+ URL=$(curl -s https://api.github.com/repos/MediaBrowser/Emby.Releases/releases/latest |
+ grep "/emby-server-deb.*$(dpkg --print-architecture).deb" | cut -d : -f 2,3 | tr -d '"')
+ cd ~/
+ wget -O "emby-server.deb" $URL 2>&1 | stdbuf -oL awk '/[.] +[0-9][0-9]?[0-9]?%/ { print substr($0,63,3) }' |
+ $DIALOG --gauge "Please wait\nDownloading ${URL##*/}" 8 70 0
+ apt_install_wrapper apt-get -y install ~/emby-server.deb
+ unlink emby-server.deb
+ $DIALOG --msgbox "To test that Emby Server has installed successfully\nIn a web browser go to http://localhost:8096 or \nhttp://127.0.0.1:8096 on this computer." 9 70
+}
+
+module_options+=(
+ ["update_skel,author"]="Kat Schwarz"
+ ["update_skel,ref_link"]=""
+ ["update_skel,feature"]="install_docker"
+ ["update_skel,desc"]="Install docker from a repo using apt"
+ ["update_skel,example"]="install_docker engine"
+ ["update_skel,status"]="Active"
+)
+#
+# Install Docker from repo using apt
+# Setup sources list and GPG key then install the app. If you want a full desktop then $1=desktop
+#
+install_docker() {
+ # Check if repo for distribution exists.
+ URL="https://download.docker.com/linux/${DISTRO,,}/dists/$DISTROID"
+ if wget --spider "${URL}" 2> /dev/null; then
+ # Add Docker's official GPG key:
+ wget -qO - https://download.docker.com/linux/${DISTRO,,}/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/docker.gpg > /dev/null
+ if [[ $? -eq 0 ]]; then
+ # Add the repository to Apt sources:
+ cat <<- EOF > "/etc/apt/sources.list.d/docker.list"
+ deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] https://download.docker.com/linux/${DISTRO,,} $DISTROID stable
+ EOF
+ apt_install_wrapper apt-get update
+ # Install docker
+ if [ "$1" = "engine" ]; then
+ apt_install_wrapper apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
+ else
+ apt_install_wrapper apt-get -y install docker-ce docker-ce-cli containerd.io
+ fi
+ systemctl enable docker.service > /dev/null 2>&1
+ systemctl enable containerd.service > /dev/null 2>&1
+ $DIALOG --msgbox "To test that Docker has installed successfully\nrun the following command: docker run hello-world" 9 70
+ fi
+ else
+ $DIALOG --msgbox "ERROR ! ${DISTRO} $DISTROID distribution not found in repository!" 7 70
+ fi
+}
+
+
diff --git a/lib/armbian-config/modules/config.system.kernel.sh b/lib/armbian-config/modules/config.system.kernel.sh
new file mode 100644
index 000000000..fda74ab0e
--- /dev/null
+++ b/lib/armbian-config/modules/config.system.kernel.sh
@@ -0,0 +1,247 @@
+#!/bin/bash
+
+
+
+module_options+=(
+ ["are_headers_installed,author"]="Gunjan Gupta"
+ ["are_headers_installed,ref_link"]=""
+ ["are_headers_installed,feature"]="are_headers_installed"
+ ["are_headers_installed,desc"]="Check if kernel headers are installed"
+ ["are_headers_installed,example"]="are_headers_installed"
+ ["are_headers_installed,status"]="Pending Review"
+ ["are_headers_installed,doc_link"]=""
+)
+#
+# @description Install kernel headers
+#
+function are_headers_installed() {
+ if [[ -f /etc/armbian-release ]]; then
+ PKG_NAME="linux-headers-${BRANCH}-${LINUXFAMILY}"
+ else
+ PKG_NAME="linux-headers-$(uname -r | sed 's/'-$(dpkg --print-architecture)'//')"
+ fi
+
+ check_if_installed ${PKG_NAME}
+ return $?
+}
+
+menu_options+=(
+ ["get_headers_kernel,author"]="Igor Pecovnik"
+ ["get_headers_kernel,ref_link"]=""
+ ["get_headers_kernel,feature"]="get_headers_install"
+ ["get_headers_kernel,desc"]="Migrated procedures from Armbian config."
+ ["get_headers_kernel,example"]="get_headers_install"
+ ["get_headers_kernel,status"]="Active"
+ ["get_headers_kernel,doc_link"]=""
+)
+#
+# install kernel headers
+#
+function get_headers_install() {
+
+ if [[ -f /etc/armbian-release ]]; then
+ INSTALL_PKG="linux-headers-${BRANCH}-${LINUXFAMILY}"
+ else
+ INSTALL_PKG="linux-headers-$(uname -r | sed 's/'-$(dpkg --print-architecture)'//')"
+ fi
+
+ debconf-apt-progress -- apt-get -y install ${INSTALL_PKG} || exit 1
+
+}
+
+
+module_options+=(
+ ["Headers_install,author"]="Joey Turner"
+ ["Headers_install,ref_link"]=""
+ ["Headers_install,feature"]="Headers_install"
+ ["Headers_install,desc"]="Install kernel headers"
+ ["Headers_install,example"]="is_package_manager_running"
+ ["Headers_install,status"]="Pending Review"
+ ["Headers_install,doc_link"]=""
+)
+#
+# @description Install kernel headers
+#
+function Headers_install() {
+ if ! is_package_manager_running; then
+ if [[ -f /etc/armbian-release ]]; then
+ INSTALL_PKG="linux-headers-${BRANCH}-${LINUXFAMILY}"
+ else
+ INSTALL_PKG="linux-headers-$(uname -r | sed 's/'-$(dpkg --print-architecture)'//')"
+ fi
+ debconf-apt-progress -- apt-get -y install ${INSTALL_PKG}
+ fi
+}
+
+
+module_options+=(
+ ["set_header_remove,author"]="Igor Pecovnik"
+ ["set_header_remove,ref_link"]=""
+ ["set_header_remove,feature"]="set_header_remove"
+ ["set_header_remove,desc"]="Migrated procedures from Armbian config."
+ ["set_header_remove,example"]="set_header_remove"
+ ["set_header_remove,doc_link"]=""
+ ["set_header_remove,status"]="Active"
+ ["set_header_remove,doc_ink"]=""
+)
+#
+# remove kernel headers
+#
+function set_header_remove() {
+
+ REMOVE_PKG="linux-headers-*"
+ if [[ -n $(dpkg -l | grep linux-headers) ]]; then
+ debconf-apt-progress -- apt-get -y purge ${REMOVE_PKG}
+ rm -rf /usr/src/linux-headers*
+ else
+ debconf-apt-progress -- apt-get -y install ${INSTALL_PKG}
+ fi
+ # cleanup
+ apt clean
+ debconf-apt-progress -- apt -y autoremove
+
+}
+
+
+module_options+=(
+ ["Headers_remove,author"]="Joey Turner"
+ ["Headers_remove,ref_link"]="https://github.com/armbian/config/blob/master/debian-config-jobs#L160"
+ ["Headers_remove,feature"]="Headers_remove"
+ ["Headers_remove,desc"]="Remove Linux headers"
+ ["Headers_remove,example"]="Headers_remove"
+ ["Headers_remove,status"]="Pending Review"
+ ["Headers_remove,doc_link"]="https://github.com/armbian/config/wiki#System"
+)
+#
+# @description Remove Linux headers
+#
+function Headers_remove() {
+ if ! is_package_manager_running; then
+ REMOVE_PKG="linux-headers-*"
+ if [[ -n $(dpkg -l | grep linux-headers) ]]; then
+ debconf-apt-progress -- apt-get -y purge ${REMOVE_PKG}
+ rm -rf /usr/src/linux-headers*
+ else
+ debconf-apt-progress -- apt-get -y install ${INSTALL_PKG}
+ fi
+ # cleanup
+ apt clean
+ debconf-apt-progress -- apt -y autoremove
+ fi
+}
+
+
+module_options+=(
+ ["armbian_fw_manipulate,author"]="Igor Pecovnik"
+ ["armbian_fw_manipulate,ref_link"]=""
+ ["armbian_fw_manipulate,feature"]="armbian_fw_manipulate"
+ ["armbian_fw_manipulate,desc"]="freeze/unhold/reinstall armbian related packages."
+ ["armbian_fw_manipulate,example"]="armbian_fw_manipulate unhold|freeze|reinstall"
+ ["armbian_fw_manipulate,status"]="Active"
+)
+#
+# freeze/unhold/reinstall armbian firmware packages
+#
+armbian_fw_manipulate() {
+
+ local function=$1
+ local version=$2
+ local branch=$3
+
+ [[ -n $version ]] && local version="=${version}"
+
+ # fallback to $BRANCH
+ [[ -z "${branch}" ]] && branch="${BRANCH}"
+ [[ -z "${branch}" ]] && branch="current" # fallback in case we switch to very old BSP that have no such info
+
+ # packages to install
+ local armbian_packages=(
+ "linux-u-boot-${BOARD}-${branch}"
+ "linux-image-${branch}-${LINUXFAMILY}"
+ "linux-dtb-${branch}-${LINUXFAMILY}"
+ "armbian-zsh"
+ "armbian-bsp-cli-${BOARD}-${branch}"
+ )
+
+ # reinstall headers only if they were previously installed
+ if are_headers_installed; then
+ local armbian_packages+="linux-headers-${branch}-${LINUXFAMILY}"
+ fi
+
+ local packages=""
+ for pkg in "${armbian_packages[@]}"; do
+ if [[ "${function}" == reinstall ]]; then
+ local pkg_search=$(apt search "$pkg" 2> /dev/null | grep "^$pkg")
+ if [[ $? -eq 0 && -n "${pkg_search}" ]]; then
+ if [[ "${pkg_search}" == *$version* ]] ; then
+ packages+="$pkg${version} ";
+ else
+ packages+="$pkg ";
+ fi
+ fi
+ else
+ if check_if_installed $pkg; then
+ packages+="$pkg${version} "
+ fi
+ fi
+ done
+ for pkg in "${packages[@]}"; do
+ case $function in
+ unhold) apt-mark unhold ${pkg} | show_infobox ;;
+ hold) apt-mark hold ${pkg} | show_infobox ;;
+ reinstall)
+ apt_install_wrapper apt-get -y --simulate --download-only --allow-change-held-packages --allow-downgrades install ${pkg}
+ if [[ $? == 0 ]]; then
+ apt_install_wrapper apt-get -y purge "linux-u-boot-*" "linux-image-*" "linux-dtb-*" "linux-headers-*" "armbian-zsh-*" "armbian-bsp-cli-*" # remove all branches
+ apt_install_wrapper apt-get -y --allow-change-held-packages install ${pkg}
+ apt_install_wrapper apt-get -y autoremove
+ apt_install_wrapper apt-get -y clean
+ else
+ exit 1
+ fi
+
+
+ ;;
+ *) return ;;
+ esac
+ done
+}
+
+module_options+=(
+ ["switch_kernels,author"]="Igor"
+ ["switch_kernels,ref_link"]=""
+ ["switch_kernels,feature"]=""
+ ["switch_kernels,desc"]="Switching to alternative kernels"
+ ["switch_kernels,example"]=""
+ ["switch_kernels,status"]="Active"
+)
+#
+# @description Switch between alternative kernels
+#
+function switch_kernels() {
+
+ # we only allow switching kerneles that are in the test pool
+ [[ -z "${KERNEL_TEST_TARGET}" ]] && KERNEL_TEST_TARGET="legacy,current,edge"
+ local kernel_test_target=$(for x in ${KERNEL_TEST_TARGET//,/ }; do echo "linux-image-$x-${LINUXFAMILY}"; done;)
+ local installed_kernel_version=$(dpkg -l | grep '^ii' | grep linux-image | awk '{print $2"="$3}')
+ # just in case current is not installed
+ [[ -n ${installed_kernel_version} ]] && local grep_current_kernel=" | grep -v ${installed_kernel_version}"
+ local search_exec="apt-cache show ${kernel_test_target} | grep -E \"Package:|Version:|version:|family\" | grep -v \"Config-Version\" | sed -n -e 's/^.*: //p' | sed 's/\.$//g' | xargs -n3 -d'\n' | sed \"s/ /=/\" $grep_current_kernel"
+ IFS=$'\n'
+ local LIST=()
+ for line in $(eval ${search_exec}); do
+ LIST+=($(echo $line | awk -F ' ' '{print $1 " "}') $(echo $line | awk -F ' ' '{print "v"$2}'))
+ done
+ unset IFS
+ local list_length=$((${#LIST[@]} / 2))
+ if [ "$list_length" -eq 0 ]; then
+ dialog --backtitle "$BACKTITLE" --title " Warning " --msgbox "\nNo other kernels available!" 7 32
+ else
+ local target_version=$(whiptail --separate-output --title "Select kernel" --menu "ed" $((${list_length} + 7)) 80 $((${list_length})) "${LIST[@]}" 3>&1 1>&2 2>&3)
+ if [ $? -eq 0 ] && [ -n "${target_version}" ]; then
+ local branch=${target_version##*image-}
+ armbian_fw_manipulate "reinstall" "${target_version/*=/}" "${branch%%-*}"
+ fi
+ fi
+}
+
diff --git a/lib/armbian-config/modules/config.system.sh b/lib/armbian-config/modules/config.system.sh
new file mode 100644
index 000000000..ec51c2dfd
--- /dev/null
+++ b/lib/armbian-config/modules/config.system.sh
@@ -0,0 +1,450 @@
+#!/bin/bash
+
+module_options+=(
+ ["apt_install_wrapper,author"]="igorpecovnik"
+ ["apt_install_wrapper,ref_link"]=""
+ ["apt_install_wrapper,feature"]="Install wrapper"
+ ["apt_install_wrapper,desc"]="Install wrapper"
+ ["apt_install_wrapper,example"]="apt_install_wrapper apt-get -y purge armbian-zsh"
+ ["apt_install_wrapper,status"]="Active"
+)
+#
+# @description Use TUI / GUI for apt install if exists
+#
+function apt_install_wrapper() {
+
+ if [ -t 0 ]; then
+ debconf-apt-progress -- "$@"
+ else
+ # Terminal not defined - proceed without TUI
+ "$@"
+ fi
+}
+
+module_options+=(
+ ["change_system_hostname,author"]="igorpecovnik"
+ ["change_system_hostname,ref_link"]=""
+ ["change_system_hostname,feature"]="Change hostname"
+ ["change_system_hostname,desc"]="change_system_hostname"
+ ["change_system_hostname,example"]="change_system_hostname"
+ ["change_system_hostname,status"]="Active"
+)
+#
+# @description Change system hostname
+#
+function change_system_hostname() {
+ local new_hostname=$($DIALOG --title "Enter new hostnane" --inputbox "" 7 50 3>&1 1>&2 2>&3)
+ [ $? -eq 0 ] && [ -n "${new_hostname}" ] && hostnamectl set-hostname "${new_hostname}"
+}
+
+module_options+=(
+ ["release_upgrade,author"]="Igor Pecovnik"
+ ["release_upgrade,ref_link"]=""
+ ["release_upgrade,feature"]="Upgrade upstream distribution release"
+ ["release_upgrade,desc"]="Upgrade to next stable or rolling release"
+ ["release_upgrade,example"]="release_upgrade stable verify"
+ ["release_upgrade,status"]="Active"
+)
+#
+# Upgrade distribution
+#
+release_upgrade(){
+
+ local upgrade_type=$1
+ local verify=$2
+
+ local distroid=${DISTROID}
+
+ if [[ "${upgrade_type}" == stable ]]; then
+ local filter=$(grep "supported" /etc/armbian-distribution-status | cut -d"=" -f1)
+ elif [[ "${upgrade_type}" == rolling ]]; then
+ local filter=$(grep "eos\|csc" /etc/armbian-distribution-status | cut -d"=" -f1 | sed "s/sid/testing/g")
+ else
+ local filter=$(cat /etc/armbian-distribution-status | cut -d"=" -f1)
+ fi
+
+ local upgrade=$(for j in $filter; do
+ for i in $(grep "^${distroid}" /etc/armbian-distribution-status | cut -d";" -f2 | cut -d"=" -f2 | sed "s/,/ /g"); do
+ if [[ $i == $j ]]; then
+ echo $i
+ fi
+ done
+ done | tail -1)
+
+ if [[ -z "${upgrade}" ]]; then
+ return 1;
+ elif [[ -z "${verify}" ]]; then
+ [[ -f /etc/apt/sources.list.d/ubuntu.sources ]] && sed -i "s/$distroid/$upgrade/g" /etc/apt/sources.list.d/ubuntu.sources
+ [[ -f /etc/apt/sources.list.d/debian.sources ]] && sed -i "s/$distroid/$upgrade/g" /etc/apt/sources.list.d/debian.sources
+ [[ -f /etc/apt/sources.list ]] && sed -i "s/$distroid/$upgrade/g" /etc/apt/sources.list
+ [[ "${upgrade}" == "testing" ]] && upgrade="sid" # our repo and everything is tied to sid
+ [[ -f /etc/apt/sources.list.d/armbian.list ]] && sed -i "s/$distroid/$upgrade/g" /etc/apt/sources.list.d/armbian.list
+ apt_install_wrapper apt-get -y update
+ apt_install_wrapper apt-get -y -o Dpkg::Options::="--force-confold" upgrade --without-new-pkgs
+ apt_install_wrapper apt-get -y -o Dpkg::Options::="--force-confold" full-upgrade
+ apt_install_wrapper apt-get -y --purge autoremove
+ fi
+}
+
+module_options+=(
+ ["install_de,author"]="Igor Pecovnik"
+ ["install_de,ref_link"]=""
+ ["install_de,feature"]="install_de"
+ ["install_de,desc"]="Install DE"
+ ["install_de,example"]="install_de"
+ ["install_de,status"]="Active"
+)
+#
+# Install desktop
+#
+function install_de() {
+
+ # get user who executed this script
+ if [ $SUDO_USER ]; then local user=$SUDO_USER; else local user=$(whoami); fi
+
+ #debconf-apt-progress --
+ apt-get update
+ #debconf-apt-progress --
+ apt-get -o Dpkg::Options::="--force-confold" -y --install-recommends install armbian-${DISTROID}-desktop-$1 # armbian-bsp-desktop-${BOARD}-${BRANCH}
+
+ # clean apt cache
+ apt-get -y clean
+
+ # add user to groups
+ for additionalgroup in sudo netdev audio video dialout plugdev input bluetooth systemd-journal ssh; do
+ usermod -aG ${additionalgroup} ${user} 2> /dev/null
+ done
+
+ # Recreating Synaptic search index
+ update-apt-xapian-index -u
+
+ # set up profile sync daemon on desktop systems
+ which psd > /dev/null 2>&1
+ if [[ $? -eq 0 && -z $(grep overlay-helper /etc/sudoers) ]]; then
+ echo "${user} ALL=(ALL) NOPASSWD: /usr/bin/psd-overlay-helper" >> /etc/sudoers
+ touch /home/${user}/.activate_psd
+ fi
+
+ # update skel
+ update_skel
+
+ # desktops has different default login managers
+ case "$1" in
+ gnome)
+ # gdm3
+ ;;
+ *)
+ # lightdm
+ mkdir -p /etc/lightdm/lightdm.conf.d
+ echo "[Seat:*]" > /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
+ echo "autologin-user=${username}" >> /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
+ echo "autologin-user-timeout=0" >> /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
+ echo "user-session=xfce" >> /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
+ ln -s /lib/systemd/system/lightdm.service /etc/systemd/system/display-manager.service > /dev/null 2>&1
+ service lightdm start > /dev/null 2>&1
+ ;;
+ esac
+ exit
+}
+
+module_options+=(
+ ["update_skel,author"]="Igor Pecovnik"
+ ["update_skel,ref_link"]=""
+ ["update_skel,feature"]="update_skel"
+ ["update_skel,desc"]="Update the /etc/skel files in users directories"
+ ["update_skel,example"]="update_skel"
+ ["update_skel,status"]="Active"
+)
+#
+# check dpkg status of $1 -- currently only 'not installed at all' case caught
+#
+function update_skel() {
+
+ getent passwd |
+ while IFS=: read -r username x uid gid gecos home shell; do
+ if [ ! -d "$home" ] || [ "$username" == 'root' ] || [ "$uid" -lt 1000 ]; then
+ continue
+ fi
+ tar -C /etc/skel/ -cf - . | su - "$username" -c "tar --skip-old-files -xf -"
+ done
+
+}
+
+module_options+=(
+ ["qr_code,author"]="Igor Pecovnik"
+ ["qr_code,ref_link"]=""
+ ["qr_code,feature"]="qr_code"
+ ["qr_code,desc"]="Show or generate QR code for Google OTP"
+ ["qr_code,example"]="qr_code generate"
+ ["qr_code,status"]="Active"
+)
+#
+# check dpkg status of $1 -- currently only 'not installed at all' case caught
+#
+function qr_code() {
+
+ clear
+ if [[ "$1" == "generate" ]]; then
+ google-authenticator -t -d -f -r 3 -R 30 -W -q
+ cp /root/.google_authenticator /etc/skel
+ update_skel
+ fi
+ export TOP_SECRET=$(head -1 /root/.google_authenticator)
+ qrencode -m 2 -d 9 -8 -t ANSI256 "otpauth://totp/test?secret=$TOP_SECRET"
+ echo -e '\nScan QR code with your OTP application on mobile phone\n'
+ read -n 1 -s -r -p "Press any key to continue"
+
+}
+
+module_options+=(
+ ["set_stable,author"]="Tearran"
+ ["set_stable,ref_link"]="https://github.com/armbian/config/blob/master/debian-config-jobs#L1446"
+ ["set_stable,feature"]="set_stable"
+ ["set_stable,desc"]="Set Armbian to stable release"
+ ["set_stable,example"]="set_stable"
+ ["set_stable,status"]="Active"
+)
+#
+# @description Set Armbian to stable release
+#
+function set_stable() {
+
+ if ! grep -q 'apt.armbian.com' /etc/apt/sources.list.d/armbian.list; then
+ sed -i "s/http:\/\/[^ ]*/http:\/\/apt.armbian.com/" /etc/apt/sources.list.d/armbian.list
+ apt_install_wrapper apt-get update
+ armbian_fw_manipulate "reinstall"
+ fi
+}
+
+module_options+=(
+ ["set_rolling,author"]="Tearran"
+ ["set_rolling,ref_link"]="https://github.com/armbian/config/blob/master/debian-config-jobs#L1446"
+ ["set_rolling,feature"]="set_rolling"
+ ["set_rolling,desc"]="Set Armbian to rolling release"
+ ["set_rolling,example"]="set_rolling"
+ ["set_rolling,status"]="Active"
+)
+#
+# @description Set Armbian to rolling release
+#
+function set_rolling() {
+
+ if ! grep -q 'beta.armbian.com' /etc/apt/sources.list.d/armbian.list; then
+ sed -i "s/http:\/\/[^ ]*/http:\/\/beta.armbian.com/" /etc/apt/sources.list.d/armbian.list
+ apt_install_wrapper apt-get update
+ armbian_fw_manipulate "reinstall"
+ fi
+}
+
+module_options+=(
+ ["manage_overlayfs,author"]="igorpecovnik"
+ ["manage_overlayfs,ref_link"]=""
+ ["manage_overlayfs,feature"]="overlayfs"
+ ["manage_overlayfs,desc"]="Set Armbian root filesystem to read only"
+ ["manage_overlayfs,example"]="manage_overlayfs enable|disable"
+ ["manage_overlayfs,status"]="Active"
+)
+#
+# @description set/unset Armbian root filesystem to read only
+#
+function manage_overlayfs() {
+
+ if [[ "$1" == "enable" ]]; then
+ debconf-apt-progress -- apt-get -o Dpkg::Options::="--force-confold" -y install overlayroot cryptsetup cryptsetup-bin
+ [[ ! -f /etc/overlayroot.conf ]] && cp /etc/overlayroot.conf.dpkg-new /etc/overlayroot.conf
+ sed -i "s/^overlayroot=.*/overlayroot=\"tmpfs\"/" /etc/overlayroot.conf
+ sed -i "s/^overlayroot_cfgdisk=.*/overlayroot_cfgdisk=\"enabled\"/" /etc/overlayroot.conf
+ else
+ overlayroot-chroot rm /etc/overlayroot.conf > /dev/null 2>&1
+ debconf-apt-progress -- apt-get -y purge overlayroot cryptsetup cryptsetup-bin
+ fi
+ # reboot is mandatory
+ reboot
+}
+
+module_options+=(
+ ["toggle_ssh_lastlog,author"]="tearran"
+ ["toggle_ssh_lastlog,ref_link"]=""
+ ["toggle_ssh_lastlog,feature"]="toggle_ssh_lastlog"
+ ["toggle_ssh_lastlog,desc"]="Toggle SSH lastlog"
+ ["toggle_ssh_lastlog,example"]="toggle_ssh_lastlog"
+ ["toggle_ssh_lastlog,status"]="Active"
+)
+#
+# @description Toggle SSH lastlog
+#
+function toggle_ssh_lastlog() {
+
+ if ! grep -q '^#\?PrintLastLog ' "${SDCARD}/etc/ssh/sshd_config"; then
+ # If PrintLastLog is not found, append it with the value 'yes'
+ echo 'PrintLastLog no' >> "${SDCARD}/etc/ssh/sshd_config"
+ sudo service ssh restart
+ else
+ # If PrintLastLog is found, toggle between 'yes' and 'no'
+ sed -i '/^#\?PrintLastLog /
+{
+ s/PrintLastLog yes/PrintLastLog no/;
+ t;
+ s/PrintLastLog no/PrintLastLog yes/
+}' "${SDCARD}/etc/ssh/sshd_config"
+ sudo service ssh restart
+ fi
+
+}
+
+module_options+=(
+ ["adjust_motd,author"]="igorpecovnik"
+ ["adjust_motd,ref_link"]=""
+ ["adjust_motd,feature"]="Adjust motd"
+ ["adjust_motd,desc"]="Adjust welcome screen (motd)"
+ ["adjust_motd,example"]=""
+ ["adjust_motd,status"]="Active"
+)
+#
+# @description Toggle message of the day items
+#
+function adjust_motd() {
+
+ # show motd description
+ motd_desc() {
+ case $1 in
+ clear)
+ echo "Clear screen on login"
+ ;;
+ header)
+ echo "Show header with logo"
+ ;;
+ sysinfo)
+ echo "Display system information"
+ ;;
+ tips)
+ echo "Show Armbian team tips"
+ ;;
+ commands)
+ echo "Show recommended commands"
+ ;;
+ *)
+ echo "No description"
+ ;;
+ esac
+ }
+
+ # read status
+ function motd_status() {
+ source /etc/default/armbian-motd
+ if [[ $MOTD_DISABLE == *$1* ]]; then
+ echo "OFF"
+ else
+ echo "ON"
+ fi
+ }
+
+ LIST=()
+ for v in $(grep THIS_SCRIPT= /etc/update-motd.d/* | cut -d"=" -f2 | sed "s/\"//g"); do
+ LIST+=("$v" "$(motd_desc $v)" "$(motd_status $v)")
+ done
+
+ INLIST=($(grep THIS_SCRIPT= /etc/update-motd.d/* | cut -d"=" -f2 | sed "s/\"//g"))
+ CHOICES=$($DIALOG --separate-output --nocancel --title "Adjust welcome screen" --checklist "" 11 50 5 "${LIST[@]}" 3>&1 1>&2 2>&3)
+ INSERT="$(echo "${INLIST[@]}" "${CHOICES[@]}" | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ' | sed 's/ *$//')"
+ # adjust motd config
+ sed -i "s/^MOTD_DISABLE=.*/MOTD_DISABLE=\"$INSERT\"/g" /etc/default/armbian-motd
+ clear
+ find /etc/update-motd.d/. -type f -executable | sort | bash
+ echo "Press any key to return to armbian-config"
+ read
+}
+
+module_options+=(
+["manage_dtoverlays,author"]="Gunjan Gupta"
+["manage_dtoverlays,ref_link"]=""
+["manage_dtoverlays,feature"]="dtoverlays"
+["manage_dtoverlays,desc"]="Enable/disable device tree overlays"
+["manage_dtoverlays,example"]="manage_dtoverlays"
+["manage_dtoverlays,status"]="Active"
+)
+#
+# @description Enable/disable device tree overlays
+#
+function manage_dtoverlays () {
+ # check if user agree to enter this area
+ local changes="false"
+ local overlayconf="/boot/armbianEnv.txt"
+ while true; do
+ local options=()
+ j=0
+ available_overlays=$(ls -1 ${OVERLAY_DIR}/*.dtbo | sed "s#^${OVERLAY_DIR}/##" | sed 's/.dtbo//g' | grep $BOOT_SOC | tr '\n' ' ')
+ for overlay in ${available_overlays}; do
+ local status="OFF"
+ grep '^fdt_overlays' ${overlayconf} | grep -qw ${overlay} && status=ON
+ options+=( "$overlay" "" "$status")
+ done
+ selection=$($DIALOG --title "Manage devicetree overlays" --cancel-button "Back" \
+ --ok-button "Save" --checklist "\nUse to toggle functions and save them.\nExit when you are done.\n " \
+ 0 0 0 "${options[@]}" 3>&1 1>&2 2>&3)
+ exit_status=$?
+ case $exit_status in
+ 0)
+ changes="true"
+ newoverlays=$(echo $selection | sed 's/"//g')
+ sed -i "s/^fdt_overlays=.*/fdt_overlays=$newoverlays/" ${overlayconf}
+ if ! grep -q "^fdt_overlays" ${overlayconf}; then echo "fdt_overlays=$newoverlays" >> ${overlayconf}; fi
+ sync
+ ;;
+ 1)
+ if [[ "$changes" == "true" ]]; then
+ $DIALOG --title " Reboot required " --yes-button "Reboot" \
+ --no-button "Cancel" --yesno "A reboot is required to apply the changes. Shall we reboot now?" 7 34
+ if [[ $? = 0 ]]; then
+ reboot
+ fi
+ fi
+ break
+ ;;
+ 255)
+ ;;
+ esac
+ done
+}
+
+module_options+=(
+["store_netplan_config,author"]="Igor Pecovnik"
+["store_netplan_config,ref_link"]=""
+["store_netplan_config,feature"]="Storing netplan config to tmp"
+["store_netplan_config,desc"]=""
+["store_netplan_config,example"]=""
+["store_netplan_config,status"]="Active"
+)
+#
+# @description Storing Netplan configuration to temp folder
+#
+function store_netplan_config () {
+
+ # store current configs to temporal folder
+ restore_netplan_config_folder=$(mktemp -d /tmp/XXXXXXXXXX)
+ rsync --quiet /etc/netplan/* ${restore_netplan_config_folder}/ 2>/dev/null
+ trap restore_netplan_config 1 2 3 6
+
+}
+
+module_options+=(
+["store_netplan_config,author"]="Igor Pecovnik"
+["store_netplan_config,ref_link"]=""
+["store_netplan_config,feature"]="Storing netplan config to tmp"
+["store_netplan_config,desc"]=""
+["store_netplan_config,example"]=""
+["store_netplan_config,status"]="Active"
+)
+#
+# @description Restoring Netplan configuration from temp folder
+#
+restore_netplan_config() {
+
+ echo "Restoring NetPlan configs" | show_infobox
+ # just in case
+ if [[ -n ${restore_netplan_config_folder} ]]; then
+ rm -f /etc/netplan/*
+ rsync -ar ${restore_netplan_config_folder}/. /etc/netplan
+ fi
+
+}