MOTD: deterministic spacing, tidy and compact output
This commit is contained in:
parent
db842095bc
commit
b21d883fb1
@ -12,10 +12,20 @@
|
||||
THIS_SCRIPT="clear"
|
||||
MOTD_DISABLE=""
|
||||
|
||||
[[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd
|
||||
# Clear runtime MOTD state
|
||||
MOTD_RUN_DIR="/run/armbian-motd"
|
||||
if [[ -d "$MOTD_RUN_DIR" ]]; then
|
||||
rm -f "${MOTD_RUN_DIR}/".*.printed 2>/dev/null
|
||||
else
|
||||
mkdir -p "$MOTD_RUN_DIR" 2>/dev/null
|
||||
fi
|
||||
|
||||
for f in $MOTD_DISABLE; do
|
||||
[[ $f == $THIS_SCRIPT ]] && exit 0
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
# Optional overrides
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
# Clear screen
|
||||
|
||||
@ -2,237 +2,166 @@
|
||||
#
|
||||
# Copyright (c) Authors: https://www.armbian.com/authors
|
||||
#
|
||||
# This file is licensed under the terms of the GNU General Public
|
||||
# License version 2. This program is licensed "as is" without any
|
||||
# warranty of any kind, whether express or implied.
|
||||
# Licensed under the GNU General Public License, version 2.
|
||||
# Distributed “as is” without any warranty of any kind.
|
||||
|
||||
# DO NOT EDIT THIS FILE but add config options to /etc/default/armbian-motd
|
||||
# any changes will be lost on board support package update
|
||||
# DO NOT EDIT THIS FILE.
|
||||
# To customize behavior, override options in /etc/default/armbian-motd.
|
||||
# Any local edits here will be lost during board support package updates.
|
||||
|
||||
set -o pipefail
|
||||
|
||||
THIS_SCRIPT="header"
|
||||
MOTD_DISABLE=""
|
||||
HIDE_IP_PATTERN="^dummy0|^lo|^docker|^hassio|^br-|^veth|^vnet|^virbr"
|
||||
HIDE_LOCAL_IPV6="true"
|
||||
|
||||
# Read image configuration
|
||||
[[ -f /etc/armbian-image-release ]] && . /etc/armbian-image-release
|
||||
VENDORTEMP="${VENDOR}"
|
||||
_motd_mark_after_header() {
|
||||
local base="/run/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || base="${XDG_RUNTIME_DIR:-/tmp}/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || return 0
|
||||
local stamp="${base}/.after_header.printed"
|
||||
[[ -e "$stamp" ]] || { echo ""; : >"$stamp"; }
|
||||
}
|
||||
|
||||
# Read update configuration
|
||||
[[ -f /etc/armbian-release ]] && . /etc/armbian-release
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
# Keep the VENDOR from image if its defined there
|
||||
[[ -n "${VENDORTEMP}" && "${VENDORTEMP}" != "${VENDOR}" ]] && VENDOR="${VENDORTEMP}"
|
||||
# --- Load metadata from image and release files ---
|
||||
safe_source /etc/armbian-image-release
|
||||
VENDORTEMP="${VENDOR:-}"
|
||||
safe_source /etc/armbian-release
|
||||
[[ -n "${VENDORTEMP}" && "${VENDORTEMP}" != "${VENDOR:-}" ]] && VENDOR="${VENDORTEMP}"
|
||||
[[ -n "${VENDORPRETTYNAME:-}" ]] && VENDOR="${VENDORPRETTYNAME}"
|
||||
|
||||
# If VENDORPRETTYNAME is defined, used that
|
||||
[[ -n ${VENDORPRETTYNAME} ]] && VENDOR="${VENDORPRETTYNAME}"
|
||||
# --- Distribution and upgrade information ---
|
||||
DISTRIBUTION_CODENAME=""
|
||||
DISTRIBUTION_ID=""
|
||||
upgrade=""
|
||||
|
||||
if [[ -f /etc/armbian-distribution-status ]]; then
|
||||
. /etc/armbian-distribution-status
|
||||
# Find a way that works
|
||||
[[ -f /etc/lsb-release ]] && DISTRIBUTION_CODENAME=$(grep CODENAME /etc/lsb-release | cut -d"=" -f2)
|
||||
[[ -f /etc/lsb-release ]] && DISTRIBUTION_ID=$(grep DISTRIB_ID /etc/lsb-release | cut -d"=" -f2)
|
||||
[[ -z "${DISTRIBUTION_CODENAME}" && -f /etc/os-release ]] && DISTRIBUTION_CODENAME=$(grep VERSION_CODENAME /etc/os-release | cut -d"=" -f2)
|
||||
[[ -z "${DISTRIBUTION_ID}" && -f /etc/os-release ]] && DISTRIBUTION_ID=$(grep "^ID" /etc/os-release | cut -d"=" -f2)
|
||||
[[ -z "${DISTRIBUTION_CODENAME}" && -x /usr/bin/lsb_release ]] && DISTRIBUTION_CODENAME=$(/usr/bin/lsb_release -c | cut -d":" -f2 | tr -d "\t")
|
||||
[[ -z "${DISTRIBUTION_ID}" && -x /usr/bin/lsb_release ]] && DISTRIBUTION_ID=$(/usr/bin/lsb_release -i | cut -d":" -f2 | tr -d "\t")
|
||||
# Read Armbian distribution status
|
||||
DISTRIBUTION_STATUS=$(grep "^${DISTRIBUTION_CODENAME}" /etc/armbian-distribution-status | cut -d"=" -f2 | cut -d";" -f1)
|
||||
# Prefer lsb-release (safe to source; no VERSION conflict)
|
||||
if [[ -f /etc/lsb-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/lsb-release
|
||||
DISTRIBUTION_CODENAME="${DISTRIB_CODENAME:-${DISTRIBUTION_CODENAME}}"
|
||||
DISTRIBUTION_ID="${DISTRIB_ID:-${DISTRIBUTION_ID}}"
|
||||
fi
|
||||
|
||||
# Read upgrade possibilities on stable channel
|
||||
filter=$(grep "supported" /etc/armbian-distribution-status | cut -d"=" -f1)
|
||||
upgrade=$(for j in ${filter}; do
|
||||
for i in $(grep "^${DISTRIBUTION_CODENAME}" /etc/armbian-distribution-status | cut -d";" -f2 | cut -d"=" -f2 | sed "s/,/ /g"); do
|
||||
if [[ "${i}" == "${j}" ]]; then
|
||||
echo "${i}"
|
||||
fi
|
||||
# If still missing, read only the keys we need from os-release (do NOT source it)
|
||||
if [[ -z "${DISTRIBUTION_CODENAME}" || -z "${DISTRIBUTION_ID}" ]]; then
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
DISTRIBUTION_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{sub(/^"/,"",$2); sub(/"$/,"",$2); print $2}' /etc/os-release)"
|
||||
DISTRIBUTION_ID="$(awk -F= '/^ID=/{sub(/^"/,"",$2); sub(/"$/,"",$2); print $2}' /etc/os-release)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If still missing, fallback to lsb_release command
|
||||
if [[ -z "${DISTRIBUTION_CODENAME}" || -z "${DISTRIBUTION_ID}" ]]; then
|
||||
if command -v lsb_release >/dev/null 2>&1; then
|
||||
DISTRIBUTION_CODENAME="$(lsb_release -c | awk -F: '{gsub(/\t/,"",$2); print $2}')"
|
||||
DISTRIBUTION_ID="$(lsb_release -i | awk -F: '{gsub(/\t/,"",$2); print $2}')"
|
||||
fi
|
||||
fi
|
||||
|
||||
DISTRIBUTION_STATUS="$(awk -F'[=;]' -v c="${DISTRIBUTION_CODENAME}" '$1==c{print $2; exit}' /etc/armbian-distribution-status)"
|
||||
filter=$(awk -F= '/supported/{print $1}' /etc/armbian-distribution-status)
|
||||
current_line="$(awk -F';' -v c="${DISTRIBUTION_CODENAME}" '$1==c{print $2; exit}' /etc/armbian-distribution-status)"
|
||||
candidates="$(printf '%s\n' "${current_line#*=}" | tr ',' ' ')"
|
||||
for j in $filter; do
|
||||
for i in $candidates; do
|
||||
if [[ "$i" == "$j" ]]; then upgrade="$i"; fi
|
||||
done
|
||||
done | tail -1)
|
||||
done
|
||||
fi
|
||||
|
||||
[[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
function get_wan_address() {
|
||||
curl -4 --max-time 2 -s https://ipv4.whatismyip.akamai.com/
|
||||
} # get wan ip address
|
||||
# --- Kernel and system info ---
|
||||
KERNELID="$(uname -r)"
|
||||
|
||||
function get_wan6_address() {
|
||||
curl -6 --max-time 2 -s https://ipv6.whatismyip.akamai.com/
|
||||
} # get wan ip6 address
|
||||
_motd_mark_after_header
|
||||
|
||||
function get_ip_addresses() {
|
||||
local ipv4s=()
|
||||
local ipv4
|
||||
local address4
|
||||
local ipv6s=()
|
||||
local ipv6
|
||||
local address6
|
||||
local intf
|
||||
local f
|
||||
|
||||
for f in /sys/class/net/*; do
|
||||
intf=$(basename "${f}")
|
||||
# match only interface names
|
||||
if [[ ${intf} =~ ${HIDE_IP_PATTERN} ]]; then
|
||||
continue
|
||||
else
|
||||
# List all IP addresses without reordering or duplicates
|
||||
ipv4=$(ip -4 addr show dev "${intf}")
|
||||
ipv4=$(echo "${ipv4}" | grep -v "${intf}:avahi")
|
||||
ipv4=$(echo "${ipv4}" | awk '/inet/ {print $2}')
|
||||
ipv4=$(echo "${ipv4}" | cut -d'/' -f1)
|
||||
ipv4=$(echo "${ipv4}" | awk '!x[$0]++')
|
||||
ipv6=$(ip -6 addr show dev "${intf}")
|
||||
if [[ "${HIDE_LOCAL_IPV6}" == true ]]; then
|
||||
ipv6=$(ip -6 addr show dev "${intf}" | grep -v fe80)
|
||||
fi
|
||||
ipv6=$(echo "${ipv6}" | grep -v "${intf}:avahi")
|
||||
ipv6=$(echo "${ipv6}" | awk '/inet6/ {print $2}')
|
||||
ipv6=$(echo "${ipv6}" | cut -d'/' -f1)
|
||||
ipv6=$(echo "${ipv6}" | awk '!x[$0]++')
|
||||
|
||||
for address4 in ${ipv4}; do
|
||||
ipv4s+=("${address4}")
|
||||
done
|
||||
for address6 in ${ipv6}; do
|
||||
ipv6s+=("${address6}")
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${ipv4s[*]}|${ipv6s[*]}"
|
||||
} # get_ip_addresses
|
||||
|
||||
# Read Armbian kernel version
|
||||
KERNELID=$(uname -r)
|
||||
# Get other variables
|
||||
ip_address=$(get_ip_addresses &)
|
||||
wan_ip_address=$(get_wan_address &)
|
||||
wan_ip6_address=$(get_wan6_address &)
|
||||
|
||||
# Get access point info
|
||||
if systemctl is-active --quiet service hostapd && [[ -f /etc/hostapd/hostapd.conf ]]; then
|
||||
. /etc/hostapd/hostapd.conf
|
||||
fi
|
||||
|
||||
# Display software vendor logo
|
||||
if [[ -n "${VENDORCOLOR}" ]]; then
|
||||
echo -e "\e[38;2;${VENDORCOLOR}m$(figlet -f small " ${VENDOR}")\e[0m"
|
||||
# --- Vendor banner ---
|
||||
if [[ -n "${VENDORCOLOR:-}" ]]; then
|
||||
if command -v figlet >/dev/null 2>&1; then
|
||||
printf '\e[38;2;%sm%s\e[0m\n' "${VENDORCOLOR}" "$(figlet -f small " ${VENDOR}")"
|
||||
else
|
||||
printf '\e[38;2;%sm### %s ###\e[0m\n' "${VENDORCOLOR}" "${VENDOR}"
|
||||
fi
|
||||
else
|
||||
echo -e "\e[1;91m$(figlet -f small " ${VENDOR}")\e[0m"
|
||||
if command -v figlet >/dev/null 2>&1; then
|
||||
printf '\e[1;91m%s\e[0m\n' "$(figlet -f small " ${VENDOR}")"
|
||||
else
|
||||
printf '\e[1;91m### %s ###\e[0m\n' "${VENDOR}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Read RPI model from cpuinfo
|
||||
if [[ ${BOARD:-} == rpi4b ]]; then
|
||||
BOARD_NAME=$(tr '\0' '\n' < /proc/device-tree/model | sed -E 's/ Rev [0-9.]+$//')
|
||||
# --- Board name and version line ---
|
||||
# Keep BOARD_NAME from Armbian files; only special-case RPi4 model tweak.
|
||||
if [[ "${BOARD:-}" == "rpi4b" ]]; then
|
||||
BOARD_NAME="$(tr '\0' '\n' < /proc/device-tree/model | sed -E 's/ Rev [0-9.]+$//')"
|
||||
fi
|
||||
|
||||
# Display version, board, and kernel version
|
||||
[[ ${VERSION} == *trunk* ]] && VERSION=$(echo -e ${VERSION} | cut -d"." -f1-2 | sed "s/\$/ rolling/")
|
||||
echo -e " \e[0;92mv${VERSION}\x1B[0m for ${BOARD_NAME} running Armbian Linux \e[0;92m${KERNELID^}\x1B[0m"
|
||||
# Preserve Armbian VERSION; if it ends with "trunk", show "rolling"
|
||||
if [[ "${VERSION:-}" == *trunk* ]]; then
|
||||
VERSION="$(printf '%s' "${VERSION}" | cut -d'.' -f1-2)"
|
||||
VERSION="${VERSION} rolling"
|
||||
fi
|
||||
|
||||
# render image and board type
|
||||
printf ' \e[0;92mv%s\x1B[0m for %s running Armbian Linux \e[0;92m%s\x1B[0m\n' \
|
||||
"${VERSION}" "${BOARD_NAME}" "${KERNELID}"
|
||||
|
||||
# --- Hardware support status ---
|
||||
HARDWARE_STATUS=""
|
||||
if [[ "${IMAGE_TYPE:-}" != "stable" ]]; then
|
||||
[[ "${IMAGE_TYPE}" == "user-built" ]] && HARDWARE_STATUS="\e[0;91mDIY\x1B[0m (custom image)\x1B[0m"
|
||||
[[ "${IMAGE_TYPE}" == "nightly" ]] && HARDWARE_STATUS="\e[0;91mfor advanced users\x1B[0m (rolling release)\x1B[0m"
|
||||
case "${IMAGE_TYPE:-}" in
|
||||
"user-built") HARDWARE_STATUS=$'\e[0;91mDIY\x1B[0m (custom image)\x1B[0m' ;;
|
||||
"nightly") HARDWARE_STATUS=$'\e[0;91mfor advanced users\x1B[0m (rolling release)\x1B[0m' ;;
|
||||
esac
|
||||
else
|
||||
[[ "${BOARD_TYPE:-}" == "csc" || "${BOARD_TYPE}" == "tvb" ]] && HARDWARE_STATUS="\e[0;91mDIY (community maintained)\x1B[0m"
|
||||
[[ "${BOARD_TYPE}" == "wip" ]] && HARDWARE_STATUS="\e[0;91mfor advanced users\x1B[0m (work in progress)\x1B[0m"
|
||||
[[ "${BOARD_TYPE}" == "eos" ]] && HARDWARE_STATUS="\e[0;91mend of life\x1B[0m"
|
||||
case "${BOARD_TYPE:-}" in
|
||||
"csc"|"tvb") HARDWARE_STATUS=$'\e[0;91mDIY (community maintained)\x1B[0m' ;;
|
||||
"wip") HARDWARE_STATUS=$'\e[0;91mfor advanced users\x1B[0m (work in progress)\x1B[0m' ;;
|
||||
"eos") HARDWARE_STATUS=$'\e[0;91mend of life\x1B[0m' ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# render distribution status
|
||||
if [[ ${DISTRIBUTION_STATUS} == supported ]]; then
|
||||
DISTRO_STATUS="\e[0;92mstable\e[0m (${DISTRIBUTION_CODENAME})"
|
||||
elif [[ ${DISTRIBUTION_STATUS} == eos ]]; then
|
||||
DISTRO_STATUS="\e[0;91mend of life\e[0m (${DISTRIBUTION_CODENAME})"
|
||||
else
|
||||
DISTRO_STATUS="\e[0;93mrolling\e[0m (${DISTRIBUTION_CODENAME})"
|
||||
fi
|
||||
# --- Distribution status line ---
|
||||
case "${DISTRIBUTION_STATUS:-}" in
|
||||
supported) DISTRO_STATUS=$'\e[0;92mstable\e[0m' ;;
|
||||
eos) DISTRO_STATUS=$'\e[0;91mend of life\e[0m' ;;
|
||||
*) DISTRO_STATUS=$'\e[0;93mrolling\e[0m' ;;
|
||||
esac
|
||||
[[ -n "${DISTRIBUTION_CODENAME}" ]] && DISTRO_STATUS+=" (${DISTRIBUTION_CODENAME})"
|
||||
|
||||
# read packages update status
|
||||
[[ -f /var/cache/apt/archives/updates.number ]] && . /var/cache/apt/archives/updates.number
|
||||
if [[ ${NUM_UPDATES:-} -gt 0 ]]; then
|
||||
if apt-mark showhold | grep -q linux-image 2> /dev/null; then
|
||||
# --- Updates status ---
|
||||
if [[ -f /var/cache/apt/archives/updates.number ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /var/cache/apt/archives/updates.number
|
||||
fi
|
||||
UPDATE_STATUS=""
|
||||
if [[ "${NUM_UPDATES:-0}" -gt 0 ]]; then
|
||||
if apt-mark showhold 2>/dev/null | grep -q '^linux-image'; then
|
||||
UPDATE_STATUS="Kernel upgrade \e[0;91mdisabled\e[0m"
|
||||
else
|
||||
UPDATE_STATUS="Kernel upgrade \e[0;92menabled\e[0m"
|
||||
fi
|
||||
UPDATE_STATUS+=" and \e[1;92m${NUM_UPDATES}\e[0m package"
|
||||
# Cosmetic is important
|
||||
[[ ${NUM_UPDATES} -gt 1 ]] && UPDATE_STATUS+="s"
|
||||
[[ "${NUM_UPDATES}" -gt 1 ]] && UPDATE_STATUS+="s"
|
||||
UPDATE_STATUS+=" available for upgrade\e[0m "
|
||||
fi
|
||||
|
||||
# read running Docker containers if any
|
||||
if systemctl is-active docker > /dev/null; then
|
||||
CONTAINERS_STATUS=$(docker ps --format "{{.Names}}" | tr '\n' ',' | sed 's/,/, /g' | sed 's/, $//')
|
||||
printf '\n'
|
||||
|
||||
# --- Display summary ---
|
||||
if [[ -n "${DISTRO_STATUS:-}" ]]; then
|
||||
[[ -n "${upgrade}" ]] && DISTRO_STATUS+=", possible distro upgrade (${upgrade})"
|
||||
printf ' Packages: %s %b\n' "${DISTRIBUTION_ID^}" "${DISTRO_STATUS}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
[[ -n "${UPDATE_STATUS}" ]] && printf ' Updates: %b\n' "${UPDATE_STATUS}"
|
||||
[[ -n "${HARDWARE_STATUS}" ]] && printf ' Support: %b\n' "${HARDWARE_STATUS}"
|
||||
|
||||
# Display packages status
|
||||
if [[ -n ${DISTRO_STATUS} ]]; then
|
||||
if [[ -n "${upgrade}" ]]; then
|
||||
DISTRO_STATUS+=", possible distro upgrade (${upgrade})"
|
||||
fi
|
||||
echo -e " Packages: ${DISTRIBUTION_ID^} ${DISTRO_STATUS}"
|
||||
fi
|
||||
|
||||
# Display available updates
|
||||
if [[ -n ${UPDATE_STATUS} ]]; then
|
||||
echo -e " Updates: ${UPDATE_STATUS}"
|
||||
fi
|
||||
|
||||
# Display hardware support status
|
||||
if [[ -n ${HARDWARE_STATUS} ]]; then
|
||||
echo -e " Support: ${HARDWARE_STATUS}"
|
||||
fi
|
||||
|
||||
# Display IP addresses
|
||||
IFS='|' read -r ipv4s ipv6s <<< "${ip_address}"
|
||||
# remove WAN IPv4 from the list of all IPv4
|
||||
if [[ "${ipv4s}" == *${wan_ip_address}* ]]; then
|
||||
ipv4s=$(echo $ipv4s | sed "s/"${wan_ip_address}"//g" | xargs )
|
||||
fi
|
||||
if [[ -n ${ipv4s} || -n ${wan_ip_address} ]]; then
|
||||
all_ip_address=" IPv4: "
|
||||
if [[ -n ${ipv4s} ]]; then
|
||||
all_ip_address+=" \x1B[93m(LAN)\x1B[0m "
|
||||
fi
|
||||
all_ip_address+="\x1B[92m${ipv4s// /, }\x1B[0m "
|
||||
if [[ -n ${wan_ip_address} ]]; then
|
||||
all_ip_address+="\x1B[93m(WAN)\x1B[0m \x1B[95m${wan_ip_address}\x1B[0m "
|
||||
fi
|
||||
echo -e "${all_ip_address}"
|
||||
fi
|
||||
# remove WAN IPv6 from the list of all IPv6
|
||||
if [[ -n "${wan_ip6_address}" && "${ipv6s}" == *${wan_ip6_address}* ]]; then
|
||||
ipv6s=$(echo $ipv6s | sed "s/"${wan_ip6_address}"//g" | xargs )
|
||||
fi
|
||||
|
||||
if [[ -n ${ipv6s} || -n ${wan_ip6_address} ]]; then
|
||||
all_ip6_address=" IPv6: "
|
||||
if [[ -n ${ipv6s} ]]; then
|
||||
all_ip6_address+="\x1B[96m${ipv6s// /, }\x1B[0m "
|
||||
fi
|
||||
if [[ -n ${wan_ip6_address} ]]; then
|
||||
all_ip6_address+="\x1B[93m(WAN)\x1B[0m \x1B[95m${wan_ip6_address}\x1B[0m "
|
||||
fi
|
||||
echo -e "${all_ip6_address}"
|
||||
fi
|
||||
|
||||
# Display running docker containers
|
||||
if [[ -n "${CONTAINERS_STATUS}" ]]; then
|
||||
echo -e " Containers: \x1B[92m${CONTAINERS_STATUS}\x1B[0m"
|
||||
fi
|
||||
|
||||
# Display hostapd
|
||||
if [[ -n ${ssid} ]]; then
|
||||
echo -e " WiFi AP: SSID: (\x1B[91m${ssid}\x1B[0m), $(iw dev "${interface:-}" info | grep channel | xargs)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
# ensure zero exit when executed directly
|
||||
(return 0 2>/dev/null) || exit 0
|
||||
74
packages/bsp/common/etc/update-motd.d/15-ap-info
Executable file
74
packages/bsp/common/etc/update-motd.d/15-ap-info
Executable file
@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
# Print WiFi Access Point info for Armbian MOTD.
|
||||
# Detects active hostapd instances, parses SSID + interface, shows channel via `iw`.
|
||||
|
||||
set -o pipefail
|
||||
|
||||
# one-time spacer after header (marks that something printed before Performance)
|
||||
_motd_mark_after_header() {
|
||||
# prefer /run, fallback to user runtime or /tmp (works when not root)
|
||||
local base="/run/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || base="${XDG_RUNTIME_DIR:-/tmp}/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || return 0
|
||||
local stamp="${base}/.after_header.printed"
|
||||
[[ -e "$stamp" ]] || { echo ""; : >"$stamp"; }
|
||||
}
|
||||
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
THIS_SCRIPT="ap-info"
|
||||
MOTD_DISABLE=""
|
||||
|
||||
# Optional overrides
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
# helper: pretty-print one AP line
|
||||
print_ap_line() {
|
||||
local ssid="$1" iface="$2"
|
||||
local chan
|
||||
if command -v iw >/dev/null 2>&1; then
|
||||
# Grab the whole "channel ..." line from iw and use it as-is
|
||||
chan="$(iw dev "$iface" info 2>/dev/null \
|
||||
| sed -n 's/^[[:space:]]*channel[[:space:]]\+\(.*\)$/\1/p' \
|
||||
| head -1)"
|
||||
fi
|
||||
printf ' WiFi AP: SSID: (\x1B[91m%s\x1B[0m), channel %s\n' \
|
||||
"$ssid" "${chan:-n/a}"
|
||||
}
|
||||
|
||||
# detect active hostapd services
|
||||
if systemctl is-active --quiet hostapd.service \
|
||||
|| systemctl list-units 'hostapd@*.service' --state=active >/dev/null 2>&1; then
|
||||
|
||||
# collect hostapd configs (classic and templated), skip non-existing
|
||||
confs=()
|
||||
for p in /etc/hostapd/hostapd.conf /etc/hostapd/hostapd-*.conf; do
|
||||
[[ -f "$p" ]] && confs+=("$p")
|
||||
done
|
||||
|
||||
# print at most one line per iface+ssid combo
|
||||
declare -A _ap_printed=()
|
||||
first=1
|
||||
for cfg in "${confs[@]}"; do
|
||||
ssid="$(grep -E '^[[:space:]]*ssid[[:space:]]*=' "$cfg" | sed -E 's/.*=//;s/^[[:space:]]+|[[:space:]]+$//')"
|
||||
iface="$(grep -E '^[[:space:]]*interface[[:space:]]*=' "$cfg" | sed -E 's/.*=//;s/^[[:space:]]+|[[:space:]]+$//')"
|
||||
|
||||
# fall back to iw to discover iface if config lacks it
|
||||
[[ -z "$iface" ]] && iface="$(iw dev 2>/dev/null | awk '/Interface/ {print $2; exit}')"
|
||||
|
||||
key="${iface}|${ssid}"
|
||||
[[ -z "$ssid" || -z "$iface" || -n "${_ap_printed[$key]+x}" ]] && continue
|
||||
|
||||
# print exactly one spacer before the first AP line
|
||||
if (( first )); then _motd_mark_after_header; first=0; fi
|
||||
|
||||
print_ap_line "$ssid" "$iface"
|
||||
_ap_printed[$key]=1
|
||||
done
|
||||
fi
|
||||
|
||||
# ensure zero exit when executed directly
|
||||
(return 0 2>/dev/null) || exit 0
|
||||
112
packages/bsp/common/etc/update-motd.d/20-ip-info
Executable file
112
packages/bsp/common/etc/update-motd.d/20-ip-info
Executable file
@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
# Print LAN/WAN IPv4 and IPv6 lines for Armbian MOTD.
|
||||
# Standalone: discovers addresses itself and prints formatted output.
|
||||
|
||||
set -o pipefail
|
||||
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
THIS_SCRIPT="ip-info"
|
||||
MOTD_DISABLE=""
|
||||
|
||||
# Defaults (can be overridden by /etc/default/armbian-motd if present)
|
||||
HIDE_IP_PATTERN='^dummy0|^lo|^docker|^hassio|^br-|^veth|^vnet|^virbr'
|
||||
HIDE_LOCAL_IPV6="true"
|
||||
|
||||
# Optional overrides
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
# one-time spacer after header (marks that something printed before Performance)
|
||||
_motd_mark_after_header() {
|
||||
local base="/run/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || base="${XDG_RUNTIME_DIR:-/tmp}/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || return 0
|
||||
local stamp="${base}/.after_header.printed"
|
||||
[[ -e "$stamp" ]] || { echo ""; : >"$stamp"; }
|
||||
}
|
||||
|
||||
get_wan_address() { curl -4 -fsS --max-time 2 https://ipv4.whatismyip.akamai.com/ 2>/dev/null || true; }
|
||||
get_wan6_address() { curl -6 -fsS --max-time 2 https://ipv6.whatismyip.akamai.com/ 2>/dev/null || true; }
|
||||
|
||||
get_lan_ips() {
|
||||
# echo "<ipv4s space-separated>|<ipv6s space-separated>"
|
||||
local ipv4s=() ipv6s=() intf
|
||||
|
||||
for p in /sys/class/net/*; do
|
||||
intf="${p##*/}"
|
||||
[[ "$intf" =~ $HIDE_IP_PATTERN ]] && continue
|
||||
|
||||
# IPv4 (order preserved, unique)
|
||||
mapfile -t add4 < <(ip -4 -o addr show dev "$intf" 2>/dev/null \
|
||||
| awk '$3=="inet"{print $4}' | cut -d/ -f1 || true)
|
||||
for a in "${add4[@]}"; do
|
||||
[[ " ${ipv4s[*]} " == *" $a "* ]] || ipv4s+=("$a")
|
||||
done
|
||||
|
||||
# IPv6 (prefer scope global when hiding link-local)
|
||||
if [[ "${HIDE_LOCAL_IPV6}" == "true" ]]; then
|
||||
mapfile -t add6 < <(ip -6 -o addr show dev "$intf" scope global 2>/dev/null \
|
||||
| awk '$3=="inet6"{print $4}' | cut -d/ -f1 || true)
|
||||
else
|
||||
mapfile -t add6 < <(ip -6 -o addr show dev "$intf" 2>/dev/null \
|
||||
| awk '$3=="inet6"{print $4}' | cut -d/ -f1 || true)
|
||||
fi
|
||||
for a in "${add6[@]}"; do
|
||||
[[ " ${ipv6s[*]} " == *" $a "* ]] || ipv6s+=("$a")
|
||||
done
|
||||
done
|
||||
|
||||
printf '%s|%s\n' "${ipv4s[*]}" "${ipv6s[*]}"
|
||||
}
|
||||
|
||||
print_ip_lines() {
|
||||
local ipv4s="$1" ipv6s="$2" wan4="$3" wan6="$4"
|
||||
# print a single blank line before the first post-header section
|
||||
_motd_spacer() {
|
||||
local tag="${1:-after-header}"
|
||||
local dir="/run/armbian-motd"; local stamp="$dir/.${tag}.printed"
|
||||
mkdir -p "$dir" 2>/dev/null
|
||||
[[ -e "$stamp" ]] || { echo ""; : > "$stamp"; }
|
||||
}
|
||||
|
||||
# Drop WAN duplicates from LAN lists
|
||||
if [[ -n "$wan4" && " $ipv4s " == *" $wan4 "* ]]; then
|
||||
ipv4s="$(printf '%s\n' $ipv4s | awk -v w="$wan4" '$0!=w' | paste -sd' ' -)"
|
||||
fi
|
||||
if [[ -n "$wan6" && " $ipv6s " == *" $wan6 "* ]]; then
|
||||
ipv6s="$(printf '%s\n' $ipv6s | awk -v w="$wan6" '$0!=w' | paste -sd' ' -)"
|
||||
fi
|
||||
|
||||
# IPv4 line
|
||||
if [[ -n "$ipv4s" || -n "$wan4" ]]; then
|
||||
_motd_mark_after_header
|
||||
local line=" IPv4: "
|
||||
[[ -n "$ipv4s" ]] && line+=" \x1B[93m(LAN)\x1B[0m \x1B[92m${ipv4s// /, }\x1B[0m "
|
||||
[[ -n "$wan4" ]] && line+="\x1B[93m(WAN)\x1B[0m \x1B[95m${wan4}\x1B[0m "
|
||||
printf '%b\n' "$line"
|
||||
fi
|
||||
|
||||
# IPv6 line
|
||||
if [[ -n "$ipv6s" || -n "$wan6" ]]; then
|
||||
_motd_mark_after_header
|
||||
local line=" IPv6: "
|
||||
[[ -n "$ipv6s" ]] && line+="\x1B[96m${ipv6s// /, }\x1B[0m "
|
||||
[[ -n "$wan6" ]] && line+="\x1B[93m(WAN)\x1B[0m \x1B[95m${wan6}\x1B[0m "
|
||||
printf '%b\n' "$line"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local combo ipv4s ipv6s wan4 wan6
|
||||
combo="$(get_lan_ips)"
|
||||
IFS='|' read -r ipv4s ipv6s <<< "$combo"
|
||||
wan4="$(get_wan_address)"
|
||||
wan6="$(get_wan6_address)"
|
||||
print_ip_lines "$ipv4s" "$ipv6s" "$wan4" "$wan6"
|
||||
}
|
||||
|
||||
# If sourced, don't run main; if executed, run.
|
||||
(return 0 2>/dev/null) || main
|
||||
53
packages/bsp/common/etc/update-motd.d/25-containers-info
Executable file
53
packages/bsp/common/etc/update-motd.d/25-containers-info
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Print running container names for Armbian MOTD (Docker).
|
||||
# Standalone: discovers containers itself and prints a formatted line,
|
||||
# identical to the original header behavior.
|
||||
|
||||
set -o pipefail
|
||||
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
THIS_SCRIPT="containers-info"
|
||||
MOTD_DISABLE=""
|
||||
|
||||
# Optional overrides
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
_motd_spacer() {
|
||||
local tag="${1:-after-header}"
|
||||
local dir="/run/armbian-motd"; local stamp="$dir/.${tag}.printed"
|
||||
mkdir -p "$dir" 2>/dev/null
|
||||
[[ -e "$stamp" ]] || { echo ""; : > "$stamp"; }
|
||||
}
|
||||
|
||||
# one-time spacer after header (marks that something printed before Performance)
|
||||
_motd_mark_after_header() {
|
||||
local base="/run/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || base="${XDG_RUNTIME_DIR:-/tmp}/armbian-motd"
|
||||
mkdir -p "$base" 2>/dev/null || return 0
|
||||
local stamp="${base}/.after_header.printed"
|
||||
[[ -e "$stamp" ]] || { echo ""; : >"$stamp"; }
|
||||
}
|
||||
|
||||
|
||||
list_docker_names() {
|
||||
# Only show names if Docker is active and CLI is present
|
||||
if systemctl is-active --quiet docker && command -v docker >/dev/null 2>&1; then
|
||||
docker ps --format '{{.Names}}' 2>/dev/null | paste -sd', ' -
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local names
|
||||
names="$(list_docker_names)"
|
||||
if [[ -n "$names" ]]; then
|
||||
_motd_mark_after_header
|
||||
printf ' Containers: \x1B[92m%s\x1B[0m\n' "$names"
|
||||
fi
|
||||
}
|
||||
|
||||
# If sourced, don't run main; if executed, run.
|
||||
(return 0 2>/dev/null) || { main; exit 0; }
|
||||
@ -7,186 +7,236 @@
|
||||
# warranty of any kind, whether express or implied.
|
||||
#
|
||||
|
||||
# DO NOT EDIT THIS FILE but add config options to /etc/default/armbian-motd
|
||||
# generate system information
|
||||
# DO NOT EDIT THIS FILE — use /etc/default/armbian-motd for overrides.
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
THIS_SCRIPT="sysinfo"
|
||||
|
||||
# don't change default values - they are lost on upgrades. Use /etc/default/armbian-motd for your values
|
||||
|
||||
# Defaults (override in /etc/default/armbian-motd)
|
||||
MOTD_DISABLE=""
|
||||
PRIMARY_INTERFACE="eth0"
|
||||
PRIMARY_DIRECTION="rx"
|
||||
STORAGE=/dev/sda1
|
||||
PRIMARY_DIRECTION="rx" # rx | tx | both | (anything else to disable)
|
||||
STORAGE="/dev/sda1"
|
||||
|
||||
CPU_TEMP_LIMIT=60
|
||||
# Temperature offset in Celcius degrees
|
||||
CPU_TEMP_OFFSET=0
|
||||
CPU_TEMP_OFFSET=0 # °C offset
|
||||
HDD_TEMP_LIMIT=60
|
||||
AMB_TEMP_LIMIT=40
|
||||
|
||||
[[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd
|
||||
|
||||
for f in $MOTD_DISABLE; do
|
||||
[[ $f == $THIS_SCRIPT ]] && exit 0
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "$f" == "$THIS_SCRIPT" ]] && exit 0
|
||||
done
|
||||
|
||||
# don't edit below here
|
||||
# Optional vendor helpers (ambienttemp, batteryinfo, getboardtemp)
|
||||
if [[ -r /usr/lib/armbian/armbian-allwinner-battery ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /usr/lib/armbian/armbian-allwinner-battery
|
||||
fi
|
||||
# Provide safe no-op stubs if functions are missing
|
||||
command -v ambienttemp >/dev/null 2>&1 || ambienttemp() { echo ""; }
|
||||
command -v batteryinfo >/dev/null 2>&1 || batteryinfo() { :; }
|
||||
command -v getboardtemp >/dev/null 2>&1 || getboardtemp() { board_temp=""; }
|
||||
|
||||
# Include functions:
|
||||
# getboardtemp()
|
||||
# batteryinfo()
|
||||
# ambienttemp()
|
||||
source /usr/lib/armbian/armbian-allwinner-battery
|
||||
|
||||
function display() {
|
||||
# $1=name $2=value $3=red_limit $4=minimal_show_limit $5=unit $6=after $7=acs/desc{
|
||||
# battery red color is opposite, lower number
|
||||
if [[ "$1" == "Battery" ]]; then local great="<"; else local great=">"; fi
|
||||
if [[ -n "$2" && "$2" -gt "0" && (( "${2%.*}" -ge "$4" )) ]]; then
|
||||
printf "%-14s" "$1:"
|
||||
if awk "BEGIN{exit ! ($2 $great $3)}"; then echo -ne "\e[0;91m $2"; else echo -ne "\e[0;92m $2"; fi
|
||||
printf "%-1s\x1B[0m" "$5"
|
||||
printf "%-11s\t" "$6"
|
||||
display() {
|
||||
# $1=name $2=value $3=red_limit $4=min_show_limit $5=unit $6=after
|
||||
# Battery uses inverse threshold (lower is worse)
|
||||
local name="$1" val="$2" red="$3" min="$4" unit="$5" after="$6"
|
||||
[[ -z "$val" ]] && return 0
|
||||
if awk -v v="$val" -v m="$min" -v inv="$name" 'BEGIN{
|
||||
if (v+0 <= 0) exit 1;
|
||||
if (inv=="Battery") exit !(v+0 >= m+0); else exit !(v+0 >= m+0)
|
||||
}'; then
|
||||
printf "%-14s" "${name}:"
|
||||
if awk -v v="$val" -v r="$red" -v inv="$name" 'BEGIN{
|
||||
if (inv=="Battery") exit !(v+0 < r+0); else exit !(v+0 > r+0)
|
||||
}'; then
|
||||
printf "\e[0;91m %s" "$val"
|
||||
else
|
||||
printf "\e[0;92m %s" "$val"
|
||||
fi
|
||||
printf "%s\x1B[0m" "$unit"
|
||||
printf "%-11s\t" "$after"
|
||||
return 1
|
||||
fi
|
||||
} # display
|
||||
return 0
|
||||
}
|
||||
|
||||
function storage_info() {
|
||||
# storage info
|
||||
RootInfo=$(df -h /)
|
||||
root_usage=$(awk '/\// {print $(NF-1)}' <<<${RootInfo} | sed 's/%//g')
|
||||
root_total=$(awk '/\// {print $(NF-4)}' <<<${RootInfo})
|
||||
StorageInfo=$(df -h $STORAGE 2>/dev/null | grep $STORAGE)
|
||||
if [[ -n "${StorageInfo}" && ${RootInfo} != *$STORAGE* ]]; then
|
||||
storage_usage=$(awk '/\// {print $(NF-1)}' <<<${StorageInfo} | sed 's/%//g')
|
||||
storage_total=$(awk '/\// {print $(NF-4)}' <<<${StorageInfo})
|
||||
if [[ -n "$(command -v smartctl)" ]]; then
|
||||
DISK="${STORAGE::-1}"
|
||||
storage_temp+=$(smartctl -l scttempsts $DISK 2> /dev/null | grep -i 'Current Temperature:' | awk '{print $(NF-1)}')
|
||||
storage_info() {
|
||||
# Root filesystem
|
||||
local rootInfo storageInfo
|
||||
rootInfo="$(df -hP /)"
|
||||
root_usage="$(awk 'NR==2{gsub(/%/,"",$5); print $5}' <<<"$rootInfo")"
|
||||
root_total="$(awk 'NR==2{print $2}' <<<"$rootInfo")"
|
||||
|
||||
# Optional extra storage (only if it exists and is not the root FS)
|
||||
if df -hP "$STORAGE" &>/dev/null; then
|
||||
storageInfo="$(df -hP "$STORAGE" | awk 'NR==2')"
|
||||
if [[ -n "$storageInfo" ]] && ! grep -q " $STORAGE " <<<"$rootInfo"; then
|
||||
storage_usage="$(awk '{gsub(/%/,"",$5); print $5}' <<<"$storageInfo")"
|
||||
storage_total="$(awk '{print $2}' <<<"$storageInfo")"
|
||||
if command -v smartctl >/dev/null 2>&1; then
|
||||
local disk="${STORAGE%[0-9]}"
|
||||
storage_temp="$(smartctl -l scttempsts "$disk" 2>/dev/null \
|
||||
| awk -F': *' '/Current Temperature:/ {print $(NF-1)}')"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
} # storage_info
|
||||
}
|
||||
|
||||
# query various systems and send some stuff to the background for overall faster execution.
|
||||
# Works only with ambienttemp and batteryinfo since A20 is slow enough :)
|
||||
amb_temp=$(ambienttemp &)
|
||||
# Collect sensor and storage data
|
||||
amb_temp="$(ambienttemp)"
|
||||
batteryinfo
|
||||
storage_info
|
||||
getboardtemp
|
||||
critical_load=80
|
||||
|
||||
# get uptime, logged in users and load in one take
|
||||
UPTIME=$(LC_ALL=C uptime)
|
||||
UPT1=${UPTIME#*'up '}
|
||||
UPT2=${UPT1%'user'*}
|
||||
users=${UPT2//*','}
|
||||
users=${users//' '}
|
||||
time=${UPT2%','*}
|
||||
time=${time//','}
|
||||
time=$(echo $time | xargs)
|
||||
load=${UPTIME#*'load average: '}
|
||||
load=${load//','}
|
||||
load=$(echo $load | cut -d" " -f1)
|
||||
[[ $load == 0.0* ]] && load=0.10
|
||||
cpucount=$(grep -c processor /proc/cpuinfo)
|
||||
# Uptime, users, and load
|
||||
users="$(who 2>/dev/null | wc -l | tr -d ' ')"
|
||||
time="$(uptime -p 2>/dev/null | sed -e 's/^up //')"
|
||||
[[ -z "$time" ]] && time="$(LC_ALL=C uptime | sed -E 's/.*up ([^,]+), .*/\1/')"
|
||||
|
||||
load=$(awk '{printf("%.0f",($1/$2) * 100)}' <<< "$load $cpucount")
|
||||
cpucount="$(grep -c ^processor /proc/cpuinfo 2>/dev/null || echo 1)"
|
||||
read -r load1 _ < /proc/loadavg
|
||||
[[ "$load1" == 0.0* ]] && load1="0.10"
|
||||
load="$(awk -v l="$load1" -v c="$cpucount" 'BEGIN{printf("%.0f",(l/c)*100)}')"
|
||||
|
||||
# memory and swap
|
||||
mem_info=$(LC_ALL=C free -w 2>/dev/null | grep "^Mem" || LC_ALL=C free | grep "^Mem")
|
||||
memory_usage=$(awk '{printf("%.0f",(($2-($4+$6+$7))/$2) * 100)}' <<<${mem_info})
|
||||
mem_info=$(echo $mem_info | awk '{print $2}')
|
||||
memory_total=$(( mem_info / 1024 ))
|
||||
swap_info=$(LC_ALL=C free -m | grep "^Swap")
|
||||
swap_usage=$( (awk '/Swap/ { printf("%3.0f", $3/$2*100) }' <<<${swap_info} 2>/dev/null || echo 0) | tr -c -d '[:digit:]')
|
||||
swap_total=$(awk '{print $(2)}' <<<${swap_info})
|
||||
# Memory / swap
|
||||
read -r mem_total_kb _ < <(awk '/MemTotal:/ {print $2" "$3; exit}' /proc/meminfo)
|
||||
mem_total_mb="$(( mem_total_kb / 1024 ))"
|
||||
mem_used_kb="$(awk '
|
||||
/MemTotal:/ {t=$2}
|
||||
/MemFree:/ {f=$2}
|
||||
/Buffers:/ {b=$2}
|
||||
/^Cached:/ {c=$2}
|
||||
END{printf("%d", t-f-b-c)}
|
||||
' /proc/meminfo)"
|
||||
memory_usage="$(awk -v u="$mem_used_kb" -v t="$mem_total_kb" 'BEGIN{printf("%.0f", (u/t)*100)}')"
|
||||
|
||||
# display info
|
||||
swap_total_mb="$(awk '/SwapTotal:/ {print int($2/1024)}' /proc/meminfo)"
|
||||
swap_used_mb="$(awk '/SwapFree:/ {sf=$2} /SwapTotal:/ {st=$2} END{print int((st-sf)/1024)}' /proc/meminfo)"
|
||||
if [[ "${swap_total_mb:-0}" -gt 0 ]]; then
|
||||
swap_usage="$(awk -v u="$swap_used_mb" -v t="$swap_total_mb" 'BEGIN{printf("%3.0f", (u/t)*100)}')"
|
||||
else
|
||||
swap_total_mb=0
|
||||
swap_usage=0
|
||||
fi
|
||||
|
||||
# draw a line to start this section
|
||||
# --- spacing before Performance ---
|
||||
# Add a blank line only if something printed before (e.g., IP, containers, AP)
|
||||
if [[ -e /run/armbian-motd/.after_header.printed ]] \
|
||||
|| [[ -e "${XDG_RUNTIME_DIR:-/tmp}/armbian-motd/.after_header.printed" ]]; then
|
||||
echo ""
|
||||
fi
|
||||
printf "\e[0;90m Performance: \x1B[0m\n\n"
|
||||
|
||||
#echo ""
|
||||
printf "\e[0;90m Performance: \x1B[0m"
|
||||
echo "" # fixed newline
|
||||
display " Load" "${load%% *}" "$critical_load" "0" "%" ""
|
||||
printf " Uptime: \x1B[92m%s\x1B[0m\t" "${time:-unknown}"
|
||||
display " Local users" "${users:-0}" "3" "2" "" ""
|
||||
echo ""
|
||||
display " Load" "${load%% *}" "${critical_load}" "0" "%" ""
|
||||
|
||||
printf " Uptime: \x1B[92m%s\x1B[0m\t" "$time"
|
||||
display " Local users" "${users##* }" "3" "2" ""
|
||||
echo "" # fixed newline
|
||||
if [[ ${memory_total} -gt 1000 ]]; then
|
||||
memory_total=$(awk '{printf("%.2f",$1/1024)}' <<<${memory_total})"G"
|
||||
# Humanize totals
|
||||
if (( mem_total_mb > 1000 )); then
|
||||
memory_total="$(awk -v m="$mem_total_mb" 'BEGIN{printf("%.2fG", m/1024)}')"
|
||||
else
|
||||
memory_total+="M"
|
||||
memory_total="${mem_total_mb}M"
|
||||
fi
|
||||
|
||||
if [[ ${swap_total} -gt 500 ]]; then
|
||||
swap_total=$(awk '{printf("%.2f",$1/1024)}' <<<${swap_total})"G"
|
||||
if (( swap_total_mb > 500 )); then
|
||||
swap_total_disp="$(awk -v s="$swap_total_mb" 'BEGIN{printf("%.2fG", s/1024)}')"
|
||||
else
|
||||
swap_total+="M"
|
||||
swap_total_disp="${swap_total_mb}M"
|
||||
fi
|
||||
|
||||
display " Memory usage" "$memory_usage" "70" "0" "%" " of ${memory_total}"
|
||||
display " Zram usage" "$swap_usage" "75" "0" "%" " of ${swap_total}"
|
||||
echo "" # fixed newline
|
||||
display " CPU temp" "$board_temp" $CPU_TEMP_LIMIT "0" "°C" ""
|
||||
display " Ambient temp" "$amb_temp" $AMB_TEMP_LIMIT "0" "°C" ""
|
||||
display " Usage of /" "$root_usage" "90" "1" "%" " of $root_total $overlay_root"
|
||||
if command -v overlayroot-chroot > /dev/null 2>&1 && findmnt -k /media/root-ro | tail -1 | grep -w /media/root-ro > /dev/null 2>&1; then
|
||||
echo -ne "\x1B[91m(read only rootfs)\x1B[0m"
|
||||
display " Memory usage" "$memory_usage" "70" "0" "%" " of ${memory_total}"
|
||||
display " Zram usage" "$swap_usage" "75" "0" "%" " of ${swap_total_disp}"
|
||||
echo ""
|
||||
|
||||
# Temperatures & root usage
|
||||
# Apply optional CPU temp offset if both numeric
|
||||
if [[ -n "${board_temp:-}" ]] && awk -v t="$board_temp" -v o="$CPU_TEMP_OFFSET" 'BEGIN{exit !(t ~ /^-?[0-9.]+$/ && o ~ /^-?[0-9.]+$/)}'; then
|
||||
board_temp="$(awk -v t="$board_temp" -v o="$CPU_TEMP_OFFSET" 'BEGIN{printf("%.1f", t+o)}')"
|
||||
fi
|
||||
echo "" # fixed newline
|
||||
|
||||
# Truncate temperature decimals (e.g. 37.6 → 37)
|
||||
cpu_temp_int=$(printf '%.0f\n' "${board_temp%%.*}")
|
||||
amb_temp_int=$(printf '%.0f\n' "${amb_temp%%.*}")
|
||||
|
||||
display " CPU temp" "$cpu_temp_int" "$CPU_TEMP_LIMIT" "0" "°C" ""
|
||||
display " Ambient temp" "$amb_temp_int" "$AMB_TEMP_LIMIT" "0" "°C" ""
|
||||
display " Usage of /" "${root_usage:-0}" "90" "1" "%" " of ${root_total} ${overlay_root}"
|
||||
|
||||
if command -v overlayroot-chroot >/dev/null 2>&1 && findmnt -k /media/root-ro | tail -1 | grep -qw /media/root-ro; then
|
||||
echo -ne "\x1B[91m(read only rootfs)\x1B[0m"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Optional secondary storage / disk temp / battery
|
||||
a=0
|
||||
display " storage /" "$storage_usage" "90" "1" "%" " of $storage_total" ; a=$((a+$?))
|
||||
display " storage temp" "$storage_temp" $HDD_TEMP_LIMIT "0" "°C" "" ; a=$((a+$?))
|
||||
display " Battery" "$battery_percent" "20" "1" "%" "$status_battery_text" ; a=$((a+$?))
|
||||
(( $a > 0 )) && echo "" # new line only if some value is displayed
|
||||
display " storage /" "${storage_usage:-0}" "90" "1" "%" " of ${storage_total:-}" ; a=$((a+$?))
|
||||
display " storage temp" "${storage_temp:-0}" "$HDD_TEMP_LIMIT" "0" "°C" "" ; a=$((a+$?))
|
||||
display " Battery" "${battery_percent:-0}" "20" "1" "%" "${status_battery_text:-}" ; a=$((a+$?))
|
||||
(( a > 0 )) && echo ""
|
||||
|
||||
line=0
|
||||
if [[ -n "$PRIMARY_INTERFACE" ]] && vnstat -i "$PRIMARY_INTERFACE" &> /dev/null; then
|
||||
traffic=$(LC_ALL=C vnstat -i $PRIMARY_INTERFACE --oneline | cut -d";" -f4,5)
|
||||
traffic_rx=$(echo $traffic | cut -d";" -f1,1 | sed -r 's/([0-9]+\.[0-9]{1})[0-9]*/\1/' | cut -d" " -f1 | cut -d"." -f1)
|
||||
traffic_rx_unit=$(echo $traffic | cut -d";" -f1,1 | sed -r 's/([0-9]+\.[0-9]{1})[0-9]*/\1/' | cut -d" " -f2)
|
||||
traffic_tx=$(echo $traffic | cut -d";" -f2,2 | sed -r 's/([0-9]+\.[0-9]{1})[0-9]*/\1/' | cut -d" " -f1 | cut -d"." -f1)
|
||||
traffic_tx_unit=$(echo $traffic | cut -d";" -f2,2 | sed -r 's/([0-9]+\.[0-9]{1})[0-9]*/\1/' | cut -d" " -f2)
|
||||
[[ "$traffic" == *"Not enough"* ]] && { traffic_tx="n/a "; traffic_rx="n/a "; }
|
||||
case $PRIMARY_DIRECTION in
|
||||
tx)
|
||||
display " TX today" "$traffic_tx" 500 0 "" "$traffic_tx_unit" ""
|
||||
line=$((line+1))
|
||||
;;
|
||||
rx)
|
||||
display " RX today" "$traffic_rx" 500 0 "" "$traffic_rx_unit" ""
|
||||
line=$((line+1))
|
||||
;;
|
||||
both)
|
||||
display " TX today" "$traffic_tx" 500 0 "" "$traffic_tx_unit" ""
|
||||
display " RX today" "$traffic_rx" 500 0 "" "$traffic_rx_unit" ""
|
||||
line=$((line+1))
|
||||
;;
|
||||
*) #off or whatever
|
||||
;;
|
||||
esac
|
||||
# vnStat daily traffic (if available)
|
||||
if [[ -n "$PRIMARY_INTERFACE" ]] && command -v vnstat >/dev/null 2>&1; then
|
||||
if vnstat -i "$PRIMARY_INTERFACE" &>/dev/null; then
|
||||
# Use C locale so decimals are '.' and units are ASCII.
|
||||
line="$(LC_ALL=C vnstat -i "$PRIMARY_INTERFACE" --oneline 2>/dev/null)"
|
||||
# Fields 4 and 5 are today's RX;TX totals. They may be like "205.88KiB" or "205.88 KiB".
|
||||
rx_raw="$(cut -d';' -f4 <<<"$line")"
|
||||
tx_raw="$(cut -d';' -f5 <<<"$line")"
|
||||
|
||||
# Normalize: extract integer value (truncate decimals) and unit; ensure exactly one space.
|
||||
norm_val() { sed -E 's/^([0-9]+)(\.[0-9]+)?([[:space:]]*)([A-Za-z]+).*/\1/'; }
|
||||
norm_unit() { sed -E 's/^([0-9]+)(\.[0-9]+)?([[:space:]]*)([A-Za-z]+).*/\4/'; }
|
||||
|
||||
traffic_rx_value="$(printf '%s' "$rx_raw" | norm_val)"
|
||||
traffic_rx_unit="$( printf '%s' "$rx_raw" | norm_unit)"
|
||||
traffic_tx_value="$(printf '%s' "$tx_raw" | norm_val)"
|
||||
traffic_tx_unit="$( printf '%s' "$tx_raw" | norm_unit)"
|
||||
|
||||
# Handle "Not enough" (no data yet)
|
||||
if [[ "$rx_raw$tx_raw" == *"Not enough"* ]]; then
|
||||
traffic_rx_value="n/a"; traffic_rx_unit=""
|
||||
traffic_tx_value="n/a"; traffic_tx_unit=""
|
||||
fi
|
||||
|
||||
case "$PRIMARY_DIRECTION" in
|
||||
tx)
|
||||
display " TX today" "$traffic_tx_value" 500 0 " ${traffic_tx_unit}" "" ;;
|
||||
rx)
|
||||
display " RX today" "$traffic_rx_value" 500 0 " ${traffic_rx_unit}" "" ;;
|
||||
both)
|
||||
display " TX today" "$traffic_tx_value" 500 0 " ${traffic_tx_unit}" ""
|
||||
display " RX today" "$traffic_rx_value" 500 0 " ${traffic_rx_unit}" "" ;;
|
||||
*) : ;;
|
||||
esac
|
||||
echo "" # force newline after vnStat output
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $(command -v zpool) ]]; then
|
||||
zpoolstatus="$(zpool status </dev/stdin 2>&1 )"
|
||||
if [[ -n $(echo $zpoolstatus | grep 'not loaded') ]]; then
|
||||
:
|
||||
elif [[ -n $(echo $zpoolstatus | grep 'degraded\|OFFLINE') ]]; then
|
||||
# ZFS pool summary
|
||||
if command -v zpool >/dev/null 2>&1; then
|
||||
zpoolstatus="$(zpool status 2>&1 || true)"
|
||||
if grep -q 'not loaded' <<<"$zpoolstatus"; then
|
||||
: # no line
|
||||
elif grep -Eq 'degraded|OFFLINE' <<<"$zpoolstatus"; then
|
||||
printf " ZFS pool: "
|
||||
echo -ne "\e[0;91mDegraded\x1B[0m"
|
||||
line=$((line+1))
|
||||
elif [[ -n $(echo $zpoolstatus | grep 'no pools available') ]]; then
|
||||
echo ""
|
||||
elif grep -q 'no pools available' <<<"$zpoolstatus"; then
|
||||
printf " ZFS pool: "
|
||||
echo -ne "n/a"
|
||||
line=$((line+1))
|
||||
echo ""
|
||||
else
|
||||
printf " ZFS pool: "
|
||||
echo -ne "\e[0;92mOnline\x1B[0m"
|
||||
line=$((line+1))
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
|
||||
@ -1,32 +1,82 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) Authors: https://www.armbian.com/authors
|
||||
# Armbian MOTD: Tips (compact + robust)
|
||||
#
|
||||
# This file is licensed under the terms of the GNU General Public
|
||||
# License version 2. This program is licensed "as is" without any
|
||||
# warranty of any kind, whether express or implied.
|
||||
|
||||
# DO NOT EDIT THIS FILE but add config options to /etc/default/armbian-motd
|
||||
# any changes will be lost on board support package update
|
||||
|
||||
THIS_SCRIPT="tips"
|
||||
MOTD_DISABLE=""
|
||||
|
||||
[[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
for f in $MOTD_DISABLE; do
|
||||
[[ $f == $THIS_SCRIPT ]] && exit 0
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
# --- helpers ---
|
||||
_has_prev_output() {
|
||||
[[ -e /run/armbian-motd/.after_header.printed ]] \
|
||||
|| [[ -e "${XDG_RUNTIME_DIR:-/tmp}/armbian-motd/.after_header.printed" ]]
|
||||
}
|
||||
|
||||
# extract field from a tiny JSON object; prefer jq, fallback to sed
|
||||
_json_get() {
|
||||
local key="$1" json="$2"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
jq -r --arg k "$key" '.[$k] // empty' <<<"$json" 2>/dev/null
|
||||
else
|
||||
printf '%s\n' "$json" | sed -n -E "s/.*\"$key\":\"([^\"]*)\".*/\1/p"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- collect content ---
|
||||
|
||||
lines=()
|
||||
|
||||
# 1) Optional quote: /etc/update-motd.d/quotes.txt lines like: YYYY-MM-DD|Some text...
|
||||
quotes="/etc/update-motd.d/quotes.txt"
|
||||
if [[ -f $quotes && $(( $RANDOM % 1 )) == 0 ]]; then
|
||||
random_line=$(shuf -i 1-$(wc -l < $quotes) -n 1)
|
||||
quote=$(sed -n -e "$random_line"p $quotes)
|
||||
DUE_DATE=$(echo $quote | cut -d"|" -f1)
|
||||
SELECTED_QUOTE=$(echo $quote | cut -d"|" -f2)
|
||||
if [[ -n $SELECTED_QUOTE && $(date +'%Y-%m-%d') < $(date -d $DUE_DATE +"%Y-%m-%d") ]]; then
|
||||
printf "\e[0;90m Tips:\x1B[0m\n " #; printf '%.s─' $(seq 1 5); echo -e " \x1B[0m"
|
||||
echo -e "\n$SELECTED_QUOTE" | fold -w 79 -s | sed 's/^/ /'
|
||||
echo ""
|
||||
fi
|
||||
if [[ -f "$quotes" ]]; then
|
||||
total=$(wc -l < "$quotes")
|
||||
if [[ "$total" -gt 0 ]]; then
|
||||
rline=$(( RANDOM % total + 1 ))
|
||||
raw=$(sed -n "${rline}p" "$quotes")
|
||||
due="${raw%%|*}"
|
||||
qtext="${raw#*|}"
|
||||
# show only if not expired or due is empty
|
||||
today=$(date +%Y-%m-%d)
|
||||
if [[ -z "$due" || "$today" < "$(date -d "$due" +%Y-%m-%d 2>/dev/null || echo 9999-12-31)" ]]; then
|
||||
[[ -n "$qtext" ]] && lines+=("$qtext")
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 2) One non-expired recommend_message (if defined)
|
||||
if [[ "$(declare -p recommend_messages 2>/dev/null)" == "declare -a"* ]]; then
|
||||
today=$(date +%Y-%m-%d)
|
||||
valid=()
|
||||
for item in "${recommend_messages[@]}"; do
|
||||
exp=$(_json_get expiration "$item")
|
||||
[[ -z "$exp" || "$today" < "$exp" ]] && valid+=("$item")
|
||||
done
|
||||
if ((${#valid[@]})); then
|
||||
pick=${valid[$RANDOM % ${#valid[@]}]}
|
||||
msg=$(_json_get message "$pick")
|
||||
url=$(_json_get url "$pick")
|
||||
[[ -n "$msg" ]] && lines+=("$msg")
|
||||
[[ -n "$url" ]] && lines+=("$url")
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- print (only if we actually have something to say) ---
|
||||
|
||||
if ((${#lines[@]})); then
|
||||
# One blank line ONLY if something printed earlier
|
||||
_has_prev_output && echo ""
|
||||
|
||||
printf "\e[0;90m Tips:\x1B[0m\n\n"
|
||||
for l in "${lines[@]}"; do
|
||||
printf "%s\n" " $l"
|
||||
done
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@ -12,15 +12,24 @@
|
||||
THIS_SCRIPT="commands"
|
||||
MOTD_DISABLE=""
|
||||
|
||||
[[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd
|
||||
safe_source() { [[ -f "$1" ]] && . "$1"; }
|
||||
|
||||
# Optional overrides
|
||||
safe_source /etc/default/armbian-motd
|
||||
for f in ${MOTD_DISABLE}; do
|
||||
[[ "${f}" == "${THIS_SCRIPT}" ]] && exit 0
|
||||
done
|
||||
|
||||
_motd_spacer() {
|
||||
local tag="${1:-after-header}"
|
||||
local dir="/run/armbian-motd"; local stamp="$dir/.${tag}.printed"
|
||||
mkdir -p "$dir" 2>/dev/null
|
||||
[[ -e "$stamp" ]] || { echo ""; : > "$stamp"; }
|
||||
}
|
||||
|
||||
# read upgrade count to show upgrade command
|
||||
[[ -f /var/cache/apt/archives/updates.number ]] && . /var/cache/apt/archives/updates.number
|
||||
|
||||
for f in $MOTD_DISABLE; do
|
||||
[[ $f == $THIS_SCRIPT ]] && exit 0
|
||||
done
|
||||
|
||||
# text, sudo / without, command, condition
|
||||
# condition can be fairly complex
|
||||
list=(
|
||||
@ -52,6 +61,7 @@ done
|
||||
|
||||
# show list for existing command only
|
||||
if [[ "${cmd_count}" -gt 0 ]]; then
|
||||
echo "" # ensure we start on a fresh line
|
||||
printf "\e[0;90m Commands: \x1B[0m\n" #; printf '%.s─' $(seq 1 39); echo -e "\x1B[0m"
|
||||
echo ""
|
||||
for l in "${output[@]}"
|
||||
@ -61,7 +71,7 @@ if [[ "${cmd_count}" -gt 0 ]]; then
|
||||
command=$(echo $l | cut -d"," -f3)
|
||||
printf " \e[1;33m%-${name_len}s\e[0m %-0s: $sudo$command\n" "$name"
|
||||
done
|
||||
echo -e "\033[0m"
|
||||
echo -en "\033[0m"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
3
packages/bsp/common/etc/update-motd.d/99-blank
Executable file
3
packages/bsp/common/etc/update-motd.d/99-blank
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
echo ""
|
||||
exit 0
|
||||
@ -992,6 +992,7 @@ if [[ -f /root/.not_logged_in_yet && -n $(tty) ]]; then
|
||||
else
|
||||
# no display manager detected -> clear screen and show motd
|
||||
clear
|
||||
run-parts --lsbsysinit /etc/cron.daily
|
||||
run-parts --lsbsysinit /etc/update-motd.d
|
||||
|
||||
# Display reboot recommendation if necessary
|
||||
|
||||
Loading…
Reference in New Issue
Block a user