22#
33# Copyright (c) Authors: https://www.armbian.com/authors
44#
5- # This file is licensed under the terms of the GNU General Public
6- # License version 2. This program is licensed "as is" without any
7- # warranty of any kind, whether express or implied.
5+ # Licensed under the GNU General Public License, version 2.
6+ # Distributed “as is” without any warranty of any kind.
87
9- # DO NOT EDIT THIS FILE but add config options to /etc/default/armbian-motd
10- # any changes will be lost on board support package update
8+ # DO NOT EDIT THIS FILE.
9+ # To customize behavior, override options in /etc/default/armbian-motd.
10+ # Any local edits here will be lost during board support package updates.
11+
12+ set -o pipefail
1113
1214THIS_SCRIPT=" header"
1315MOTD_DISABLE=" "
14- HIDE_IP_PATTERN=" ^dummy0|^lo|^docker|^hassio|^br-|^veth|^vnet|^virbr"
15- HIDE_LOCAL_IPV6=" true"
1616
17- # Read image configuration
18- [[ -f /etc/armbian-image-release ]] && . /etc/armbian-image-release
19- VENDORTEMP=" ${VENDOR} "
17+ _motd_mark_after_header () {
18+ local base=" /run/armbian-motd"
19+ mkdir -p " $base " 2> /dev/null || base=" ${XDG_RUNTIME_DIR:-/ tmp} /armbian-motd"
20+ mkdir -p " $base " 2> /dev/null || return 0
21+ local stamp=" ${base} /.after_header.printed"
22+ [[ -e " $stamp " ]] || { echo " " ; : > " $stamp " ; }
23+ }
2024
21- # Read update configuration
22- [[ -f /etc/armbian-release ]] && . /etc/armbian-release
25+ safe_source () { [[ -f " $1 " ]] && . " $1 " ; }
2326
24- # Keep the VENDOR from image if its defined there
25- [[ -n " ${VENDORTEMP} " && " ${VENDORTEMP} " != " ${VENDOR} " ]] && VENDOR=" ${VENDORTEMP} "
27+ # --- Load metadata from image and release files ---
28+ safe_source /etc/armbian-image-release
29+ VENDORTEMP=" ${VENDOR:- } "
30+ safe_source /etc/armbian-release
31+ [[ -n " ${VENDORTEMP} " && " ${VENDORTEMP} " != " ${VENDOR:- } " ]] && VENDOR=" ${VENDORTEMP} "
32+ [[ -n " ${VENDORPRETTYNAME:- } " ]] && VENDOR=" ${VENDORPRETTYNAME} "
2633
27- # If VENDORPRETTYNAME is defined, used that
28- [[ -n ${VENDORPRETTYNAME} ]] && VENDOR=" ${VENDORPRETTYNAME} "
34+ # --- Distribution and upgrade information ---
35+ DISTRIBUTION_CODENAME=" "
36+ DISTRIBUTION_ID=" "
37+ upgrade=" "
2938
3039if [[ -f /etc/armbian-distribution-status ]]; then
31- . /etc/armbian-distribution-status
32- # Find a way that works
33- [[ -f /etc/lsb-release ]] && DISTRIBUTION_CODENAME=$( grep CODENAME /etc/lsb-release | cut -d" =" -f2)
34- [[ -f /etc/lsb-release ]] && DISTRIBUTION_ID=$( grep DISTRIB_ID /etc/lsb-release | cut -d" =" -f2)
35- [[ -z " ${DISTRIBUTION_CODENAME} " && -f /etc/os-release ]] && DISTRIBUTION_CODENAME=$( grep VERSION_CODENAME /etc/os-release | cut -d" =" -f2)
36- [[ -z " ${DISTRIBUTION_ID} " && -f /etc/os-release ]] && DISTRIBUTION_ID=$( grep " ^ID" /etc/os-release | cut -d" =" -f2)
37- [[ -z " ${DISTRIBUTION_CODENAME} " && -x /usr/bin/lsb_release ]] && DISTRIBUTION_CODENAME=$( /usr/bin/lsb_release -c | cut -d" :" -f2 | tr -d " \t" )
38- [[ -z " ${DISTRIBUTION_ID} " && -x /usr/bin/lsb_release ]] && DISTRIBUTION_ID=$( /usr/bin/lsb_release -i | cut -d" :" -f2 | tr -d " \t" )
39- # Read Armbian distribution status
40- DISTRIBUTION_STATUS=$( grep " ^${DISTRIBUTION_CODENAME} " /etc/armbian-distribution-status | cut -d" =" -f2 | cut -d" ;" -f1)
41-
42- # Read upgrade possibilities on stable channel
43- filter=$( grep " supported" /etc/armbian-distribution-status | cut -d" =" -f1)
44- upgrade=$( for j in ${filter} ; do
45- for i in $( grep " ^${DISTRIBUTION_CODENAME} " /etc/armbian-distribution-status | cut -d" ;" -f2 | cut -d" =" -f2 | sed " s/,/ /g" ) ; do
46- if [[ " ${i} " == " ${j} " ]]; then
47- echo " ${i} "
48- fi
40+ # Prefer lsb-release (safe to source; no VERSION conflict)
41+ if [[ -f /etc/lsb-release ]]; then
42+ # shellcheck disable=SC1091
43+ . /etc/lsb-release
44+ DISTRIBUTION_CODENAME=" ${DISTRIB_CODENAME:- ${DISTRIBUTION_CODENAME} } "
45+ DISTRIBUTION_ID=" ${DISTRIB_ID:- ${DISTRIBUTION_ID} } "
46+ fi
47+
48+ # If still missing, read only the keys we need from os-release (do NOT source it)
49+ if [[ -z " ${DISTRIBUTION_CODENAME} " || -z " ${DISTRIBUTION_ID} " ]]; then
50+ if [[ -f /etc/os-release ]]; then
51+ DISTRIBUTION_CODENAME=" $( awk -F= ' /^VERSION_CODENAME=/{sub(/^"/,"",$2); sub(/"$/,"",$2); print $2}' /etc/os-release) "
52+ DISTRIBUTION_ID=" $( awk -F= ' /^ID=/{sub(/^"/,"",$2); sub(/"$/,"",$2); print $2}' /etc/os-release) "
53+ fi
54+ fi
55+
56+ # If still missing, fallback to lsb_release command
57+ if [[ -z " ${DISTRIBUTION_CODENAME} " || -z " ${DISTRIBUTION_ID} " ]]; then
58+ if command -v lsb_release > /dev/null 2>&1 ; then
59+ DISTRIBUTION_CODENAME=" $( lsb_release -c | awk -F: ' {gsub(/\t/,"",$2); print $2}' ) "
60+ DISTRIBUTION_ID=" $( lsb_release -i | awk -F: ' {gsub(/\t/,"",$2); print $2}' ) "
61+ fi
62+ fi
63+
64+ DISTRIBUTION_STATUS=" $( awk -F' [=;]' -v c=" ${DISTRIBUTION_CODENAME} " ' $1==c{print $2; exit}' /etc/armbian-distribution-status) "
65+ filter=$( awk -F= ' /supported/{print $1}' /etc/armbian-distribution-status)
66+ current_line=" $( awk -F' ;' -v c=" ${DISTRIBUTION_CODENAME} " ' $1==c{print $2; exit}' /etc/armbian-distribution-status) "
67+ candidates=" $( printf ' %s\n' " ${current_line#* =} " | tr ' ,' ' ' ) "
68+ for j in $filter ; do
69+ for i in $candidates ; do
70+ if [[ " $i " == " $j " ]]; then upgrade=" $i " ; fi
4971 done
50- done | tail -1 )
72+ done
5173fi
5274
53- [[ -f /etc/default/armbian-motd ]] && . /etc/default/armbian-motd
75+ safe_source /etc/default/armbian-motd
5476for f in ${MOTD_DISABLE} ; do
5577 [[ " ${f} " == " ${THIS_SCRIPT} " ]] && exit 0
5678done
5779
58- function get_wan_address() {
59- curl -4 --max-time 2 -s https://ipv4.whatismyip.akamai.com/
60- } # get wan ip address
61-
62- function get_wan6_address() {
63- curl -6 --max-time 2 -s https://ipv6.whatismyip.akamai.com/
64- } # get wan ip6 address
65-
66- function get_ip_addresses() {
67- local ipv4s=()
68- local ipv4
69- local address4
70- local ipv6s=()
71- local ipv6
72- local address6
73- local intf
74- local f
75-
76- for f in /sys/class/net/* ; do
77- intf=$( basename " ${f} " )
78- # match only interface names
79- if [[ ${intf} =~ ${HIDE_IP_PATTERN} ]]; then
80- continue
81- else
82- # List all IP addresses without reordering or duplicates
83- ipv4=$( ip -4 addr show dev " ${intf} " )
84- ipv4=$( echo " ${ipv4} " | grep -v " ${intf} :avahi" )
85- ipv4=$( echo " ${ipv4} " | awk ' /inet/ {print $2}' )
86- ipv4=$( echo " ${ipv4} " | cut -d' /' -f1)
87- ipv4=$( echo " ${ipv4} " | awk ' !x[$0]++' )
88- ipv6=$( ip -6 addr show dev " ${intf} " )
89- if [[ " ${HIDE_LOCAL_IPV6} " == true ]]; then
90- ipv6=$( ip -6 addr show dev " ${intf} " | grep -v fe80)
91- fi
92- ipv6=$( echo " ${ipv6} " | grep -v " ${intf} :avahi" )
93- ipv6=$( echo " ${ipv6} " | awk ' /inet6/ {print $2}' )
94- ipv6=$( echo " ${ipv6} " | cut -d' /' -f1)
95- ipv6=$( echo " ${ipv6} " | awk ' !x[$0]++' )
96-
97- for address4 in ${ipv4} ; do
98- ipv4s+=(" ${address4} " )
99- done
100- for address6 in ${ipv6} ; do
101- ipv6s+=(" ${address6} " )
102- done
103- fi
104- done
105-
106- echo " ${ipv4s[*]} |${ipv6s[*]} "
107- } # get_ip_addresses
80+ # --- Kernel and system info ---
81+ KERNELID=" $( uname -r) "
10882
109- # Read Armbian kernel version
110- KERNELID=$( uname -r)
111- # Get other variables
112- ip_address=$( get_ip_addresses & )
113- wan_ip_address=$( get_wan_address & )
114- wan_ip6_address=$( get_wan6_address & )
83+ _motd_mark_after_header
11584
116- # Get access point info
117- if systemctl is-active --quiet service hostapd && [[ -f /etc/hostapd/hostapd.conf ]]; then
118- . /etc/hostapd/hostapd.conf
85+ # --- Vendor banner ---
86+ if [[ -n " ${VENDORCOLOR:- } " ]]; then
87+ if command -v figlet > /dev/null 2>&1 ; then
88+ printf ' \e[38;2;%sm%s\e[0m\n' " ${VENDORCOLOR} " " $( figlet -f small " ${VENDOR} " ) "
89+ else
90+ printf ' \e[38;2;%sm### %s ###\e[0m\n' " ${VENDORCOLOR} " " ${VENDOR} "
91+ fi
92+ else
93+ if command -v figlet > /dev/null 2>&1 ; then
94+ printf ' \e[1;91m%s\e[0m\n' " $( figlet -f small " ${VENDOR} " ) "
95+ else
96+ printf ' \e[1;91m### %s ###\e[0m\n' " ${VENDOR} "
97+ fi
11998fi
12099
121- # Display software vendor logo
122- if [[ -n " ${VENDORCOLOR} " ]]; then
123- echo -e " \e[38;2;${VENDORCOLOR} m$( figlet -f small " ${VENDOR} " ) \e[0m"
124- else
125- echo -e " \e[1;91m$( figlet -f small " ${VENDOR} " ) \e[0m"
100+ # --- Board name and version line ---
101+ # Keep BOARD_NAME from Armbian files; only special-case RPi4 model tweak.
102+ if [[ " ${BOARD:- } " == " rpi4b" ]]; then
103+ BOARD_NAME=" $( tr ' \0' ' \n' < /proc/device-tree/model | sed -E ' s/ Rev [0-9.]+$//' ) "
126104fi
127105
128- # Read RPI model from cpuinfo
129- if [[ ${BOARD:- } == rpi4b ]]; then
130- BOARD_NAME=$( tr ' \0' ' \n' < /proc/device-tree/model | sed -E ' s/ Rev [0-9.]+$//' )
106+ # Preserve Armbian VERSION; if it ends with "trunk", show "rolling"
107+ if [[ " ${VERSION:- } " == * trunk* ]]; then
108+ VERSION=" $( printf ' %s' " ${VERSION} " | cut -d' .' -f1-2) "
109+ VERSION=" ${VERSION} rolling"
131110fi
132111
133- # Display version, board, and kernel version
134- [[ ${VERSION} == * trunk* ]] && VERSION=$( echo -e ${VERSION} | cut -d" ." -f1-2 | sed " s/\$ / rolling/" )
135- echo -e " \e[0;92mv${VERSION} \x1B[0m for ${BOARD_NAME} running Armbian Linux \e[0;92m${KERNELID^} \x1B[0m"
112+ printf ' \e[0;92mv%s\x1B[0m for %s running Armbian Linux \e[0;92m%s\x1B[0m\n' \
113+ " ${VERSION} " " ${BOARD_NAME} " " ${KERNELID} "
136114
137- # render image and board type
115+ # --- Hardware support status ---
116+ HARDWARE_STATUS=" "
138117if [[ " ${IMAGE_TYPE:- } " != " stable" ]]; then
139- [[ " ${IMAGE_TYPE} " == " user-built" ]] && HARDWARE_STATUS=" \e[0;91mDIY\x1B[0m (custom image)\x1B[0m"
140- [[ " ${IMAGE_TYPE} " == " nightly" ]] && HARDWARE_STATUS=" \e[0;91mfor advanced users\x1B[0m (rolling release)\x1B[0m"
141- else
142- [[ " ${BOARD_TYPE:- } " == " csc" || " ${BOARD_TYPE} " == " tvb" ]] && HARDWARE_STATUS=" \e[0;91mDIY (community maintained)\x1B[0m"
143- [[ " ${BOARD_TYPE} " == " wip" ]] && HARDWARE_STATUS=" \e[0;91mfor advanced users\x1B[0m (work in progress)\x1B[0m"
144- [[ " ${BOARD_TYPE} " == " eos" ]] && HARDWARE_STATUS=" \e[0;91mend of life\x1B[0m"
145- fi
146-
147- # render distribution status
148- if [[ ${DISTRIBUTION_STATUS} == supported ]]; then
149- DISTRO_STATUS=" \e[0;92mstable\e[0m (${DISTRIBUTION_CODENAME} )"
150- elif [[ ${DISTRIBUTION_STATUS} == eos ]]; then
151- DISTRO_STATUS=" \e[0;91mend of life\e[0m (${DISTRIBUTION_CODENAME} )"
118+ case " ${IMAGE_TYPE:- } " in
119+ " user-built" ) HARDWARE_STATUS=$' \e [0;91mDIY\x1B [0m (custom image)\x1B [0m' ;;
120+ " nightly" ) HARDWARE_STATUS=$' \e [0;91mfor advanced users\x1B [0m (rolling release)\x1B [0m' ;;
121+ esac
152122else
153- DISTRO_STATUS=" \e[0;93mrolling\e[0m (${DISTRIBUTION_CODENAME} )"
154- fi
155-
156- # read packages update status
157- [[ -f /var/cache/apt/archives/updates.number ]] && . /var/cache/apt/archives/updates.number
158- if [[ ${NUM_UPDATES:- } -gt 0 ]]; then
159- if apt-mark showhold | grep -q linux-image 2> /dev/null; then
123+ case " ${BOARD_TYPE:- } " in
124+ " csc" |" tvb" ) HARDWARE_STATUS=$' \e [0;91mDIY (community maintained)\x1B [0m' ;;
125+ " wip" ) HARDWARE_STATUS=$' \e [0;91mfor advanced users\x1B [0m (work in progress)\x1B [0m' ;;
126+ " eos" ) HARDWARE_STATUS=$' \e [0;91mend of life\x1B [0m' ;;
127+ esac
128+ fi
129+
130+ # --- Distribution status line ---
131+ case " ${DISTRIBUTION_STATUS:- } " in
132+ supported) DISTRO_STATUS=$' \e [0;92mstable\e [0m' ;;
133+ eos) DISTRO_STATUS=$' \e [0;91mend of life\e [0m' ;;
134+ * ) DISTRO_STATUS=$' \e [0;93mrolling\e [0m' ;;
135+ esac
136+ [[ -n " ${DISTRIBUTION_CODENAME} " ]] && DISTRO_STATUS+=" (${DISTRIBUTION_CODENAME} )"
137+
138+ # --- Updates status ---
139+ if [[ -f /var/cache/apt/archives/updates.number ]]; then
140+ # shellcheck disable=SC1091
141+ . /var/cache/apt/archives/updates.number
142+ fi
143+ UPDATE_STATUS=" "
144+ if [[ " ${NUM_UPDATES:- 0} " -gt 0 ]]; then
145+ if apt-mark showhold 2> /dev/null | grep -q ' ^linux-image' ; then
160146 UPDATE_STATUS=" Kernel upgrade \e[0;91mdisabled\e[0m"
161147 else
162148 UPDATE_STATUS=" Kernel upgrade \e[0;92menabled\e[0m"
163149 fi
164150 UPDATE_STATUS+=" and \e[1;92m${NUM_UPDATES} \e[0m package"
165- # Cosmetic is important
166- [[ ${NUM_UPDATES} -gt 1 ]] && UPDATE_STATUS+=" s"
151+ [[ " ${NUM_UPDATES} " -gt 1 ]] && UPDATE_STATUS+=" s"
167152 UPDATE_STATUS+=" available for upgrade\e[0m "
168153fi
169154
170- # read running Docker containers if any
171- if systemctl is-active docker > /dev/null; then
172- CONTAINERS_STATUS=$( docker ps --format " {{.Names}}" | tr ' \n' ' ,' | sed ' s/,/, /g' | sed ' s/, $//' )
173- fi
174-
175- echo " "
176-
177- # Display packages status
178- if [[ -n ${DISTRO_STATUS} ]]; then
179- if [[ -n " ${upgrade} " ]]; then
180- DISTRO_STATUS+=" , possible distro upgrade (${upgrade} )"
181- fi
182- echo -e " Packages: ${DISTRIBUTION_ID^} ${DISTRO_STATUS} "
183- fi
184-
185- # Display available updates
186- if [[ -n ${UPDATE_STATUS} ]]; then
187- echo -e " Updates: ${UPDATE_STATUS} "
188- fi
189-
190- # Display hardware support status
191- if [[ -n ${HARDWARE_STATUS} ]]; then
192- echo -e " Support: ${HARDWARE_STATUS} "
193- fi
194-
195- # Display IP addresses
196- IFS=' |' read -r ipv4s ipv6s <<< " ${ip_address}"
197- # remove WAN IPv4 from the list of all IPv4
198- if [[ " ${ipv4s} " == * ${wan_ip_address} * ]]; then
199- ipv4s=$( echo $ipv4s | sed " s/" ${wan_ip_address} " //g" | xargs )
200- fi
201- if [[ -n ${ipv4s} || -n ${wan_ip_address} ]]; then
202- all_ip_address=" IPv4: "
203- if [[ -n ${ipv4s} ]]; then
204- all_ip_address+=" \x1B[93m(LAN)\x1B[0m "
205- fi
206- all_ip_address+=" \x1B[92m${ipv4s// / , } \x1B[0m "
207- if [[ -n ${wan_ip_address} ]]; then
208- all_ip_address+=" \x1B[93m(WAN)\x1B[0m \x1B[95m${wan_ip_address} \x1B[0m "
209- fi
210- echo -e " ${all_ip_address} "
211- fi
212- # remove WAN IPv6 from the list of all IPv6
213- if [[ -n " ${wan_ip6_address} " && " ${ipv6s} " == * ${wan_ip6_address} * ]]; then
214- ipv6s=$( echo $ipv6s | sed " s/" ${wan_ip6_address} " //g" | xargs )
215- fi
155+ printf ' \n'
216156
217- if [[ -n ${ipv6s} || -n ${wan_ip6_address} ]]; then
218- all_ip6_address=" IPv6: "
219- if [[ -n ${ipv6s} ]]; then
220- all_ip6_address+=" \x1B[96m${ipv6s// / , } \x1B[0m "
221- fi
222- if [[ -n ${wan_ip6_address} ]]; then
223- all_ip6_address+=" \x1B[93m(WAN)\x1B[0m \x1B[95m${wan_ip6_address} \x1B[0m "
224- fi
225- echo -e " ${all_ip6_address} "
157+ # --- Display summary ---
158+ if [[ -n " ${DISTRO_STATUS:- } " ]]; then
159+ [[ -n " ${upgrade} " ]] && DISTRO_STATUS+=" , possible distro upgrade (${upgrade} )"
160+ printf ' Packages: %s %b\n' " ${DISTRIBUTION_ID^} " " ${DISTRO_STATUS} "
226161fi
227162
228- # Display running docker containers
229- if [[ -n " ${CONTAINERS_STATUS} " ]]; then
230- echo -e " Containers: \x1B[92m${CONTAINERS_STATUS} \x1B[0m"
231- fi
232-
233- # Display hostapd
234- if [[ -n ${ssid} ]]; then
235- echo -e " WiFi AP: SSID: (\x1B[91m${ssid} \x1B[0m), $( iw dev " ${interface:- } " info | grep channel | xargs) "
236- fi
163+ [[ -n " ${UPDATE_STATUS} " ]] && printf ' Updates: %b\n' " ${UPDATE_STATUS} "
164+ [[ -n " ${HARDWARE_STATUS} " ]] && printf ' Support: %b\n' " ${HARDWARE_STATUS} "
237165
238- echo " "
166+ # ensure zero exit when executed directly
167+ (return 0 2> /dev/null) || exit 0
0 commit comments