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 + +}