Files
armbian-build/packages/bsp/common/usr/lib/armbian/armbian-firstlogin
T
Igor Pecovnik ddb935698c firstlogin: prefer the GNOME Flashback xmonad session when it can start
The xmonad package ships two sessions: xmonad.desktop runs the window
manager bare, and gnome-flashback-xmonad.desktop runs it inside a GNOME
Flashback session, which is the one that gets a session dbus, a polkit
agent and session management. Autologin hardcoded the bare one, so the
flashback session was never selected even where it was usable.

Pick it when it can actually start, and keep the old behaviour otherwise.
Its TryExec is gnome-flashback, from gnome-session-flashback, which is
not installed on every image, so both the .desktop and the binary are
checked. Images without it are unaffected.

Signed-off-by: Igor Pecovnik <igor@armbian.com>
2026-09-08 15:48:22 +02:00

1214 lines
42 KiB
Bash
Executable File

#!/bin/bash
#
# 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.
# read distribution status
# shellcheck source=/dev/null
[[ -f /etc/lsb-release ]] && . /etc/lsb-release
[[ -f /etc/os-release ]] && . /etc/os-release
[[ -z "$DISTRIB_CODENAME" ]] && DISTRIB_CODENAME="${VERSION_CODENAME}"
[[ -n "$DISTRIB_CODENAME" && -f /etc/armbian-distribution-status ]] && DISTRIBUTION_STATUS=$(grep "^$DISTRIB_CODENAME" /etc/armbian-distribution-status | cut -d"=" -f2 | cut -d";" -f1)
. /etc/armbian-release
. /usr/lib/armbian/armbian-common
# Cleanup gate: EXIT trap removes the marker only after FIRSTLOGIN_SUCCESS=1,
# so power-loss or mid-run failure reruns firstlogin on next boot.
FIRSTLOGIN_SUCCESS=0
_firstlogin_cleanup() {
if [ "$FIRSTLOGIN_SUCCESS" = "1" ]; then
rm -f /root/.not_logged_in_yet
sync -f /root 2>/dev/null || sync
fi
}
trap _firstlogin_cleanup EXIT
# DM takeover (gdm/lightdm) sends SIGHUP to the console's foreground process
# while bash is mid-command. An untrapped SIGHUP kills bash mid-flight and its
# EXIT trap gets cut short: builtins still run, but external commands (rm) do
# not survive the dying shell. Handle the signal in normal execution context
# instead — cleanup runs fully, then exit.
trap '_firstlogin_cleanup; exit' HUP INT TERM
check_abort() {
echo -e "\nDisabling user account creation procedure\n"
FIRSTLOGIN_SUCCESS=1
if [[ ${USER_SHELL} == zsh ]]; then
printf "\nYou selected \e[0;91mZSH\x1B[0m as your default shell. If you want to use it right away, please logout and login! \n\n"
fi
trap - INT
exit 0
}
mask2cidr() {
nbits=0
IFS=.
for dec in $1 ; do
case $dec in
255) let nbits+=8;;
254) let nbits+=7;;
252) let nbits+=6;;
248) let nbits+=5;;
240) let nbits+=4;;
224) let nbits+=3;;
192) let nbits+=2;;
128) let nbits+=1;;
0);;
*) echo "Error: $dec is not recognised"; exit 1
esac
done
echo "$nbits"
}
createYAML() {
local YAML="network:\n"
YAML+=" $DEVTYPE:\n"
YAML+=" $DEVICE_NAME:\n"
if [[ "${PRESET_NET_USE_STATIC}" == 1 ]]; then
YAML+=" addresses:\n"
YAML+=" - $PRESET_NET_STATIC_IP/$PRESET_NET_STATIC_MASK\n"
if [[ -n "${PRESET_NET_STATIC_GATEWAY}" ]]; then YAML+=" routes:\n";fi
if [[ -n "${PRESET_NET_STATIC_GATEWAY}" ]]; then YAML+=" - to: default\n";fi
if [[ -n "${PRESET_NET_STATIC_GATEWAY}" ]]; then YAML+=" via: ${PRESET_NET_STATIC_GATEWAY}\n";fi
if [[ -n "${PRESET_NET_STATIC_DNS}" ]]; then YAML+=" nameservers:\n"; fi
if [[ -n "${PRESET_NET_STATIC_DNS}" ]]; then YAML+=" addresses: [$PRESET_NET_STATIC_DNS]\n"; fi
else
YAML+=" dhcp4: yes\n"
YAML+=" dhcp6: yes\n"
fi
if [[ "${PRESET_NET_WIFI_ENABLED}" == 1 ]]; then
if [[ -n "${PRESET_NET_WIFI_COUNTRYCODE}" ]]; then YAML+=" regulatory-domain: $PRESET_NET_WIFI_COUNTRYCODE\n"; fi
if [[ -n "${PRESET_NET_WIFI_SSID}" ]]; then YAML+=" access-points:\n"; fi
if [[ -n "${PRESET_NET_WIFI_SSID}" ]]; then YAML+=" \"$PRESET_NET_WIFI_SSID\":\n"; fi
if [[ -n "${PRESET_NET_WIFI_KEY}" ]]; then YAML+=" password: \"$PRESET_NET_WIFI_KEY\"\n" ; fi
fi
printf "%s" "$YAML"
}
do_firstrun_automated_network_configuration()
{
#-----------------------------------------------------------------------------
#Config FP
local fp_config='/root/.not_logged_in_yet'
#-----------------------------------------------------------------------------
#Grab user requested settings
if [[ -f $fp_config ]]; then
# Convert line endings to Unix from Dos
sed -i $'s/\r$//' "$fp_config"
# check syntax
bash -n "$fp_config" || return
# Load vars directly from file
source "$fp_config"
# Obtain backward configuration compatibility
PRESET_NET_STATIC_DNS=${PRESET_NET_STATIC_DNS// /,}
PRESET_NET_STATIC_MASK=$(mask2cidr $PRESET_NET_STATIC_MASK)
#-----------------------------------------------------------------------------
# Set Network
if [[ $PRESET_NET_CHANGE_DEFAULTS == 1 ]]; then
# - Get name of 1st available ethernet and wifi adapter
eth_index="$(ip link | awk -F: '$0 !~ "lo|vir|wl|^[^0-9]"{print $2;getline}' | sed 's/^[ \t]*//' | grep "^e" | head -1)"
wlan_index="$(iw dev | awk '$1=="Interface"{print $2}' | head -n 1)"
local CONFIG_NAME="-dhcp"
# for static IP we only append settings
if [[ $PRESET_NET_USE_STATIC == 1 ]]; then
local CONFIG_NAME="-static"
fi
# at least one device has to exits
if [[ -n $eth_index || -n $wlan_index ]]; then
# - Wifi enable
if [[ $PRESET_NET_WIFI_ENABLED == 1 ]]; then
DEVICE_NAME=${wlan_index}
DEVTYPE=wifis
local _f="/etc/netplan/30-${DEVTYPE}${CONFIG_NAME}.yaml"
if echo -e "$(createYAML)" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" 600 || return 1
else
rm -f "${_f}.tmp"
return 1
fi
PRESET_NET_ETHERNET_ENABLED=0
netplan apply > /dev/null 2>&1
# - Ethernet enable
elif [[ $PRESET_NET_ETHERNET_ENABLED == 1 ]]; then
# remove dhcp config
rm -f /etc/netplan/10-dhcp-all-interfaces.yaml
DEVICE_NAME=${eth_index}
DEVTYPE=ethernets
local _f="/etc/netplan/30-${DEVTYPE}${CONFIG_NAME}.yaml"
if echo -e "$(createYAML)" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" 600 || return 1
else
rm -f "${_f}.tmp"
return 1
fi
PRESET_NET_WIFI_ENABLED=0
netplan apply > /dev/null 2>&1
fi
fi
fi
fi
} #do_firstrun_automated_network_configuration
# Try to retrieve the local IP address to display
get_local_ip_addr() {
# How many seconds to wait at maximum to find out the local IP address
local ip_wait_seconds_counter=6
local local_device_ip=""
local retry_message=""
while [[ -z "$local_device_ip" ]] && [ ${ip_wait_seconds_counter} -ge 0 ]; do
local_device_ip=$(ip -4 addr show scope global | grep inet | awk '{print $2}' | cut -d/ -f1 | awk '{$1=$1}1' FS='\n' OFS=',' RS=)
# Set retry message if some retries are left, but no IP address has been found yet
if [[ -z "$local_device_ip" ]] && [[ $ip_wait_seconds_counter -gt 0 ]]; then
retry_message="\e[1m\e[97mWaiting for local connection!\x1B[0m Retrying... (${ip_wait_seconds_counter})"
# Empty retry message if IP address has been found
elif [[ -n "$local_device_ip" ]]; then
retry_message=""
# Set timeout message if no retries are left and no IP address has been found
else
retry_message="\e[1m\e[31mNetwork connection timeout!\x1B[0m"
fi
# Display the message
echo -e "\e[1A\e[KIP address: \x1B[92m${local_device_ip}\x1B[0m ${retry_message}"
# Wait for 1 second if the IP has not yet been found
if [[ -z "$local_device_ip" ]]; then
sleep 1
fi
ip_wait_seconds_counter=$((ip_wait_seconds_counter - 1))
done
}
read_password() {
unset password
unset charcount
prompt="$1 password: "
stty -echo
charcount=0
while IFS= read -p "$prompt" -r -s -n 1 char; do
if [[ $char == $'\0' ]]; then
break
fi
# Handle backspace
if [[ $char == $'\177' ]]; then
if [ $charcount -gt 0 ]; then
charcount=$((charcount - 1))
prompt=$'\b \b'
password="${password%?}"
else
prompt=''
fi
else
charcount=$((charcount + 1))
prompt='*'
password+="$char"
fi
done
stty echo
}
set_shell() {
readarray -t optionsAudits <<< "$(grep "zsh\|/bash" /etc/shells | sed 's/\/bin\///g' | sed 's/\/usr//g' | uniq)"
USER_SHELL="bash"
if [[ "${#optionsAudits[@]}" -gt 1 ]]; then
while :; do
while [[ ! "${reply}" =~ ^(1|2)$ ]]; do
i=1
echo -e "\nChoose default system command shell:\n"
for o in "${optionsAudits[@]}"; do
echo "$i) $o"
((i++)) || true
done
if [ -z $PRESET_USER_SHELL ];then
read -r reply
else
reply=1
for index in "${!optionsAudits[@]}"; do
if [[ "${optionsAudits[$index]}" == "$PRESET_USER_SHELL" ]]; then
reply=$(($index + 1))
break
fi
done
fi
done
case $reply in
"1" | "${optionsAudits[0]}")
USER_SHELL="${optionsAudits[0]}"
break
;;
"2" | "${optionsAudits[1]}")
USER_SHELL="${optionsAudits[1]}"
break
;;
*)
USER_SHELL="${optionsAudits[0]}"
break
;;
esac
done
# Display shell selection only if needs to be selected
echo -e "\nShell: \x1B[92m${USER_SHELL^^}\x1B[0m"
fi
SHELL_PATH=$(grep "/$USER_SHELL$" /etc/shells | tail -1)
chsh -s "$(grep -iF "/$USER_SHELL" /etc/shells | tail -1)"
# Change shell for future users. useradd reads /etc/default/useradd;
# /etc/adduser.conf is optional, so guard that edit.
local _f="/etc/default/useradd"
if sed "s|^SHELL=.*|SHELL=${SHELL_PATH}|" "${_f}" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" || return 1
else
rm -f "${_f}.tmp"
return 1
fi
if [[ -f /etc/adduser.conf ]]; then
_f="/etc/adduser.conf"
if sed "s|^DSHELL=.*|DSHELL=${SHELL_PATH}|" "${_f}" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" || return 1
else
rm -f "${_f}.tmp"
return 1
fi
fi
}
set_timezone_and_locales() {
# Grab this machine's public IP address
PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip)
# Check if we have wireless adaptor
WIFI_DEVICE=$(LC_ALL=C iw dev | awk '$1=="Interface"{print $2}' 2> /dev/null)
if [ -z "$PUBLIC_IP" ]; then
# ask for connecting to wireless if wifi device is found
if [[ -n "$WIFI_DEVICE" ]]; then
echo -e "Internet connection was \x1B[91mnot detected\x1B[0m."
echo ""
unset response
while [[ ! "${response}" =~ ^(Y|y|N|n)$ ]]; do
if [ -z $PRESET_CONNECT_WIRELESS ];then
read -r -p "Connect via wireless? [Y/n] " response
response=${response:-Y}
else
response=n
fi
done
if [[ "${response}" =~ ^(Y|y)$ ]]; then
# We could have multiple devices
if (( $(grep -c . <<<"$WIFI_DEVICE") > 1 )); then
scanning=0
while [[ ${scanning} -lt 3 ]]; do
scanning=$(( scanning + 1 ))
echo -e "\nMultiple wireless adaptors detected. Choose primary:\n"
WIFI_DEVICES=($(printf '%s\n' "${WIFI_DEVICE[@]}" | sed 's/^[ \t]*//' | sed 's/"//g' | sed 's/ESSID://' | awk 'BEGIN{FS=OFS=","} {$NF=++count OFS $NF} 1'))
for str in ${WIFI_DEVICES[@]}; do echo $str | sed "s/,/ \t /g"; done
echo ""
read -r -p "Enter a number of wireles adaptor: " input
if [[ "$input" =~ ^[0-9]{,2}$ && -n "$input" ]] ; then break; fi
done
[[ -z $input ]] && input=1
WIFI_DEVICE=$(echo ${WIFI_DEVICES[$input-1]} | cut -d"," -f2)
fi
# bring up wifi device (not done by networkd, only by NetworkManager)
ip link set ${WIFI_DEVICE} up
# get list of wireless networks
scanning=0
broken=1
while [[ ${scanning} -lt 3 ]]; do
sleep 0.5
scanning=$(( scanning + 1 ))
readarray -t ARRAY < <(iw dev ${WIFI_DEVICE} scan 2> /dev/null | egrep 'SSID' | sed 's/^[ \t]*//' | sed 's/"//g' | sed 's/SSID: //' | sed '/^$/d' | sort | uniq | awk 'BEGIN{FS=OFS=","} {$NF=++count OFS $NF} 1')
if [[ ${#ARRAY[@]} -gt 0 ]]; then broken=0; break; fi
done
# wifi can also fail
if [[ ${broken} == 1 ]]; then
echo -e "\nWireless connection was \x1B[91mnot detected\x1B[0m.\n"
else
echo -e "\nDetected wireless networks:\n"
scanning=0
broken=1
while [[ ${scanning} -lt 3 ]]; do
scanning=$(( scanning + 1 ))
while [[ 1 ]] ; do
for str in "${ARRAY[@]}"; do echo $str | sed "s/,/ \t /"; done
echo ""
read -r -p "Enter a number of SSID: " input
if [[ "$input" =~ ^[0-9]{,2}$ ]] ; then break; fi
done
# get password
while [[ -n "${input}" ]] ; do
SSID=$(echo ${ARRAY[$input-1]} | cut -d"," -f2-)
echo ""
read -r -p "Enter a password for ${SSID}: " password
break
done
# generate config
local _f="/etc/netplan/30-wifis-dhcp.yaml"
if cat <<- EOF > "${_f}.tmp"
# Created by Armbian firstlogin script
network:
wifis:
${WIFI_DEVICE}:
dhcp4: yes
dhcp6: yes
access-points:
"$SSID":
password: "${password}"
EOF
then
atomic_write "${_f}.tmp" "${_f}" 600 || return 1
else
rm -f "${_f}.tmp"
return 1
fi
# apply to netplan
systemctl daemon-reload
netplan apply --timeout 0 2>/dev/null
# wireless link probing
pinging=10
broken=1
WIRELESSLINK=""
echo ""
while [[ ${pinging} -gt 1 && -n "${input}" && -n "${password}" ]]; do
pinging=$(( pinging - 1 ))
printf "\rProbing wireless link ($pinging)"
WIRELESSLINK=$(iw "${WIFI_DEVICE}" link 2> /dev/null | grep "$SSID")
sleep 2
# exit if connection is suffesful
if [[ "${WIRELESSLINK}" == *$SSID* ]]; then
broken=0
break
fi
done
if [[ ${broken} == 1 ]]; then
echo -e "\n\nWireless link was \x1B[91mnot detected\x1B[0m. Wrong password or weak signal."
fi
# get public IP probing
broken=1
pinging=10
while [[ ${pinging} -gt 1 && -n "${input}" && -n "${password}" && "${WIRELESSLINK}" == *$SSID* ]]; do
pinging=$(( pinging - 1 ))
printf "\rProbing internet connection ($pinging)"
PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip)
if [[ -n "$PUBLIC_IP" ]]; then
broken=0
break
else
sleep 5
fi
done
echo ""
if [[ ${broken} == 0 ]]; then
break
fi
done
if [[ ${broken} == 1 ]]; then
echo -e "\n\x1B[91mUnable to connect to Access Point\x1B[0m.\n"
rm -f /etc/netplan/30-wifis-dhcp.yaml
netplan apply --timeout 0 2>/dev/null
systemctl daemon-reload
fi
fi # detected or not detected wireless network
fi
echo ""
fi
fi
# Grab IP once again if not found
sleep 3
[[ -z "$PUBLIC_IP" && -n "$WIFI_DEVICE" ]] && PUBLIC_IP=$(curl --max-time 5 -s https://ipinfo.io/ip)
# Call the geolocation API and capture the output
RES=$(
curl --max-time 5 -s "http://ipwhois.app/json/${PUBLIC_IP}" |
jq '.timezone, .country, .country_code' |
while read -r TIMEZONE; do
read -r COUNTRY
read -r COUNTRYCODE
echo "${TIMEZONE},${COUNTRY},${COUNTRYCODE}" | tr --delete '"\n'
done
)
TZDATA=$(echo "${RES}" | cut -d"," -f1)
CCODE=$(echo "${RES}" | cut -d"," -f3 | xargs)
unset response
while [[ ! "${response}" =~ ^(Y|y|N|n)$ ]]; do
if [ -z "${SET_LANG_BASED_ON_LOCATION}" ] && [ -n "${TZDATA}" ];then
echo -e "Detected timezone: \x1B[92m$TZDATA\x1B[0m"
echo ""
read -r -p "Set user language based on your location? [Y/n] " response
response=${response:-Y}
else
response=$SET_LANG_BASED_ON_LOCATION
break
fi
done
# change it only if we have a match and if we agree
if [[ "${response}" =~ ^(N|n)$ ]]; then
unset CCODE TZDATA
fi
LOCALES=$(grep territory /usr/share/i18n/locales/* | grep _"$CCODE" | cut -d ":" -f 1 | cut -d "/" -f 6 |
xargs -I{} grep {} /usr/share/i18n/SUPPORTED | grep "UTF-8$" | cut -d " " -f 1)
# UTF8 is not present everywhere so check again in case it returns empty value
[[ -z "$LOCALES" ]] && LOCALES=$(grep territory /usr/share/i18n/locales/* | grep _"$CCODE" | cut -d ":" -f 1 | cut -d "/" -f 6 |
xargs -I{} grep {} /usr/share/i18n/SUPPORTED | cut -d " " -f 1)
readarray -t options <<< "${LOCALES}"
if [ -z $PRESET_LOCALE ];then
# when having more locales, prompt for choosing one
if [[ "${#options[@]}" -gt 1 ]]; then
options+=("Skip generating locales")
echo -e "\nAt your location, more locales are possible:\n"
PS3='Please enter your choice:'
select opt in "${options[@]}"; do
if [[ " ${options[*]} " == *" ${opt} "* ]]; then
LOCALES=${opt}
break
fi
done
fi
else
LOCALES=$PRESET_LOCALE
fi
if [[ "${LOCALES}" != *Skip* ]]; then
if [ -z $PRESET_TIMEZONE ];then
# if TZDATA was not detected, we need to select one
if [[ -z ${TZDATA} ]]; then
TZDATA=$(tzselect | tail -1)
fi
echo ""
else
TZDATA=$PRESET_TIMEZONE
fi
timedatectl set-timezone "${TZDATA}"
dpkg-reconfigure --frontend=noninteractive tzdata > /dev/null 2>&1
# change default locales (atomic write)
local _f="/etc/default/locale"
if sed "s/=.*/=$(echo ${LOCALES} | cut -d" " -f1)/g" "${_f}" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" || return 1
else
rm -f "${_f}.tmp"
return 1
fi
# generate locales (atomic write for locale.gen)
local _f="/etc/locale.gen"
if sed 's/# '"${LOCALES}"'/'"${LOCALES}"'/' "${_f}" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" || return 1
else
rm -f "${_f}.tmp"
return 1
fi
echo -e "Generating locales: \x1B[92m${LOCALES}\x1B[0m"
locale-gen "${LOCALES}" > /dev/null 2>&1
# setting detected locales only for user (idempotent)
for rcfile in /home/"$RealUserName"/.bashrc /home/"$RealUserName"/.xsessionrc; do
if [[ ! -e "$rcfile" ]]; then
# Create empty file with proper ownership before appending; leave
# ownership alone if the file already exists (user may have customized).
touch -- "$rcfile"
chown -- "$RealUserName":"$RealUserName" "$rcfile"
fi
if ! grep -q "^export LC_ALL=$LOCALES" "$rcfile" 2>/dev/null; then
{
cat -- "$rcfile"
echo "export LC_ALL=$LOCALES"
echo "export LANG=$LOCALES"
echo "export LANGUAGE=$LOCALES"
} > "${rcfile}.tmp" && atomic_write "${rcfile}.tmp" "$rcfile" "" "$RealUserName:$RealUserName" || return 1
fi
done
fi
}
add_profile_sync_settings() {
if [[ ! -f /usr/bin/psd ]]; then
return 0
fi
/usr/bin/psd > /dev/null 2>&1
config_file="${HOME}/.config/psd/psd.conf"
if [ -f "${config_file}" ]; then
# test for overlayfs
sed -i 's/#USE_OVERLAYFS=.*/USE_OVERLAYFS="yes"/' "${config_file}"
case $(/usr/bin/psd p 2> /dev/null | grep Overlayfs) in
*active*)
echo -e "\nConfigured profile sync daemon with overlayfs."
;;
*)
echo -e "\nConfigured profile sync daemon."
sed -i 's/USE_OVERLAYFS="yes"/#USE_OVERLAYFS="no"/' "${config_file}"
;;
esac
fi
systemctl --user enable psd.service > /dev/null 2>&1
systemctl --user start psd.service > /dev/null 2>&1
}
add_user() {
read -r -t 0 _
REPEATS=3
# Power-loss re-run: detect existing regular user, skip username prompt
local existing_user
existing_user=$(awk -F: '$3 >= 1000 && $1 != "nobody" {print $1; exit}' /etc/passwd)
if [ -n "${existing_user}" ]; then
RealUserName="${existing_user}"
username="${existing_user}"
echo -e "Detected existing user \e[0;92m$RealUserName\x1B[0m from previous setup, reusing."
else
while [ -f "/root/.not_logged_in_yet" ]; do
[[ -z "${PRESET_USER_NAME}" ]] && echo -e "\nPlease provide a username (eg. your first name): \c"
if [ -z "$PRESET_USER_NAME" ];then
read -r -e username
else
username="$PRESET_USER_NAME"
fi
if ! grep '^[a-zA-Z][a-zA-Z0-9]*$' <<< "$username" > /dev/null; then
echo -e "\n\x1B[91mError\x1B[0m: illegal characters in username"
return
fi
RealUserName="$(echo "$username" | tr '[:upper:]' '[:lower:]' | tr -d -c '[:alnum:]')"
[ -z "$RealUserName" ] && return
if ! id "$RealUserName" > /dev/null 2>&1; then
break
elif [ ! -d "/home/$RealUserName" ]; then
# Partially created user from a prior interrupted run: break and let
# the home-repair branch below recover it. Without this, PRESET_USER_NAME
# would loop forever on "already exists".
break
else
echo -e "Username \e[0;31m$RealUserName\x1B[0m already exists on the system."
fi
done
fi
# Set default user login icon
set_user_icon "$RealUserName"
while [ -f "/root/.not_logged_in_yet" ]; do
if [ -z "$PRESET_USER_PASSWORD" ];then
read_password "Create user ($username)"
echo ""
else
password="$PRESET_USER_PASSWORD"
fi
first_input="$password"
if [ -z "$PRESET_USER_PASSWORD" ];then
read_password "Repeat user ($username)"
echo ""
else
password="$PRESET_USER_PASSWORD"
fi
second_input="$password"
if [[ "$first_input" == "$second_input" ]]; then
# minimal images might not have this
if command -v cracklib-check > /dev/null 2>&1; then
result="$(cracklib-check <<< "$password")"
okay="$(awk -F': ' '{ print $2}' <<< "$result")"
if [[ "$okay" != "OK" ]]; then
echo -e "\n\e[0;31mWarning:\x1B[0m Weak password, $okay \b!"
fi
fi
if [ -z "$PRESET_DEFAULT_REALNAME" ];then
echo -e ""
read -r -e -p "Please provide your real name: " -i "${RealUserName^}" RealName
else
RealName="$PRESET_DEFAULT_REALNAME"
fi
# Skip user creation if already exists (power-loss re-run). If the user
# exists but home is missing (passwd written, home creation interrupted),
# rebuild it from /etc/skel with correct ownership so the user can log in.
if ! id "$RealUserName" > /dev/null 2>&1; then
# useradd (Essential, from passwd) instead of adduser (optional on
# Debian forky+, absent from minimal images). --user-group mirrors
# adduser's per-user group; --create-home copies /etc/skel; login shell
# comes from SHELL_PATH (set_shell ran first). Password is disabled here
# and set right below, same as adduser --disabled-password.
useradd --create-home --home-dir /home/"$RealUserName" --shell "$SHELL_PATH" \
--comment "$RealName" --user-group "$RealUserName" || return 1
elif [ ! -d "/home/$RealUserName" ]; then
mkdir -p /home/"$RealUserName" || return 1
cp -r /etc/skel/. /home/"$RealUserName"/ 2>/dev/null || return 1
chown -R "$RealUserName:$RealUserName" /home/"$RealUserName" || return 1
echo -e "Repaired missing home directory for \e[0;92m$RealUserName\x1B[0m."
fi
# download and add SSH key if defined
if [[ -n "${PRESET_USER_KEY}" ]]; then
local _f="/home/$RealUserName/.ssh/authorized_keys"
mkdir -p /home/"$RealUserName"/.ssh/ || return 1
chown "$RealUserName:$RealUserName" /home/"$RealUserName"/.ssh/ || return 1
chmod 700 /home/"$RealUserName"/.ssh/ || return 1
if curl --retry 5 --connect-timeout 3 "${PRESET_USER_KEY}" > "${_f}.tmp" 2> /dev/null; then
atomic_write "${_f}.tmp" "${_f}" 600 "$RealUserName:$RealUserName" || return 1
else
rm -f "${_f}.tmp"
return 1
fi
fi
if [[ -n "$first_input" ]]; then
(
echo "$first_input"
echo "$second_input"
) | passwd "$RealUserName" > /dev/null 2>&1
else
passwd -d "$RealUserName" > /dev/null 2>&1
fi
# Pre-create docker group to ensure user membership is set up before Docker installation.
# (docker-ce package creates this group automatically during postinst, but we create it early
# to guarantee group membership is ready immediately after user creation.)
if ! getent group docker >/dev/null; then
if ! groupadd --system docker 2>/dev/null; then
echo "Warning: Failed to create docker group" >&2
fi
fi
for additionalgroup in sudo netdev audio video disk tty users games dialout plugdev input bluetooth systemd-journal ssh render docker; do
usermod -aG "${additionalgroup}" "${RealUserName}" 2> /dev/null
done
# fix for gksu in Xenial
touch /home/"$RealUserName"/.Xauthority
chown "$RealUserName":"$RealUserName" /home/"$RealUserName"/.Xauthority
RealName="$(awk -F":" "/^${RealUserName}:/ {print \$5}" < /etc/passwd | cut -d',' -f1)"
[ -z "$RealName" ] && RealName="$RealUserName"
echo -e "\nDear \e[0;92m${RealName}\x1B[0m, your account \e[0;92m${RealUserName}\x1B[0m has been created and is sudo enabled."
echo -e "Please use this account for your daily work from now on.\n"
# .not_logged_in_yet is NOT deleted here - the EXIT trap removes
# it once FIRSTLOGIN_SUCCESS=1 is set after all config completes.
# set up profile sync daemon on desktop systems
if command -v psd > /dev/null 2>&1; then
# Grant psd-overlay-helper NOPASSWD via a sudoers drop-in (atomic write).
# Using /etc/sudoers.d/ avoids touching /etc/sudoers directly: power-loss
# recovery is simply "no drop-in" → re-run recreates it, and there is no
# need to restore or validate any dist version of /etc/sudoers.
local _f="/etc/sudoers.d/armbian-firstlogin-psd"
if echo "${RealUserName} ALL=(ALL) NOPASSWD: /usr/bin/psd-overlay-helper" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" 440 || return 1
else
rm -f "${_f}.tmp"
return 1
fi
touch /home/"${RealUserName}"/.activate_psd
chown "$RealUserName":"$RealUserName" /home/"${RealUserName}"/.activate_psd
fi
break
elif [[ -n $password ]]; then
echo -e "Rejected - \e[0;31mpasswords do not match.\x1B[0m Try again [${REPEATS}]."
REPEATS=$((REPEATS - 1))
fi
[[ "$REPEATS" -eq 0 ]] && exit 0
done
}
set_user_icon() {
local U="$1"
local ICON_SRC="/usr/share/armbian/armbian-user-icon.png"
local ICON_DST="/var/lib/AccountsService/icons/$U"
local META="/var/lib/AccountsService/users/$U"
# If the icon doesn't exist, do nothing.
[ -f "$ICON_SRC" ] || return 0
mkdir -p /var/lib/AccountsService/icons /var/lib/AccountsService/users
# Copy icon
install -m 0644 "$ICON_SRC" "$ICON_DST"
# Write AccountsService metadata
cat <<- EOF > "$META"
[User]
Icon=$ICON_DST
SystemAccount=false
EOF
chown root:root "$ICON_DST" "$META"
chmod 0644 "$ICON_DST" "$META"
}
if [[ -f /root/.not_logged_in_yet ]] && tty -s; then
# All autologin consoles start the setup at once and the one that wins is
# often a console nobody is at, so let the newest session take it over.
# SIGTERM is the holder's clean-exit path: the marker survives a power
# loss the same way, and the setup simply runs again here.
exec 9<> /run/armbian-firstlogin.lock || exit 1
until flock -n 9; do
kill -TERM "$(< /run/armbian-firstlogin.lock)" 2> /dev/null
sleep 1
done
echo $$ > /run/armbian-firstlogin.lock
# it may have been finished by the session we just displaced
[[ -f /root/.not_logged_in_yet ]] || exit 0
. /root/.not_logged_in_yet
# override configuration from URL
if [[ -n "${PRESET_CONFIGURATION}" ]]; then
_f="/root/.not_logged_in_yet"
if curl --retry 5 --connect-timeout 3 "${PRESET_CONFIGURATION}" > "${_f}.tmp" 2> /dev/null && bash -n "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" 600 || exit 1
. "${_f}"
else
rm -f "${_f}.tmp"
exit 1
fi
fi
do_firstrun_automated_network_configuration || exit 1
# disable autologin (remove both generic and tty-specific paths)
rm -f /etc/systemd/system/getty@.service.d/override.conf
rm -f /etc/systemd/system/getty@tty1.service.d/override.conf
rm -f /etc/systemd/system/serial-getty@.service.d/override.conf
rm -f /etc/systemd/system/serial-getty@ttyGS0.service.d/override.conf
systemctl daemon-reload
declare desktop_dm="none"
declare -i desktop_is_sddm=0 desktop_is_lightdm=0 desktop_is_gdm3=0
if [[ -f /usr/bin/sddm ]]; then
desktop_dm="sddm"
desktop_is_sddm=1
fi
if [[ -f /usr/sbin/lightdm ]]; then
desktop_dm="lightdm"
desktop_is_lightdm=1
fi
if [[ -f /usr/sbin/gdm3 ]]; then
desktop_dm="gdm3"
desktop_is_gdm3=1
fi
echo -e "\nWaiting for system to finish booting ..."
systemctl is-system-running --wait > /dev/null
# enable networkManager-wait-online.service
# When NM is used the NetworkManager-wait-online.service must be enabled so that network-online.target is not reached until
# NetworkManager has brought up all the interfaces. Service units that require the network to be up before starting rely
# on network-online.target working correctly otherwise they will likely fail on boot
# Same goes for systemd-networkd stack
# https://github.com/armbian/build/issues/7896
if
systemctl is-enabled --quiet NetworkManager &&
! systemctl is-enabled --quiet NetworkManager-wait-online
then
systemctl enable NetworkManager-wait-online
# @TODO: determine if there is any value in starting it now
echo "Waiting for network startup to complete..."
systemctl start NetworkManager-wait-online
fi
if
systemctl is-enabled --quiet systemd-networkd &&
! systemctl is-enabled --quiet systemd-networkd-wait-online
then
systemctl enable systemd-networkd-wait-online
# @TODO: determine if there is any value in starting it now
echo "Waiting for network startup to complete..."
systemctl start systemd-networkd-wait-online
fi
# Enable HiDPI support only if the framebuffer size is detectable
FB_VIRTUAL_SIZE="/sys/class/graphics/fb0/virtual_size"
HIDPI_THRESHOLD="1920"
if [[ -r "$FB_VIRTUAL_SIZE" ]]; then
fb_virtual_width=$(cut -d, -f1 < "$FB_VIRTUAL_SIZE" 2>/dev/null)
if [[ "$fb_virtual_width" =~ ^[0-9]+$ && "$fb_virtual_width" -gt "${HIDPI_THRESHOLD}" ]]; then
# Enable HiDPI in LightDM slick-greeter
if [[ -f /etc/lightdm/slick-greeter.conf ]]; then
if ! grep -q "^enable-hidpi *= *on" /etc/lightdm/slick-greeter.conf; then
echo "enable-hidpi = on" >> /etc/lightdm/slick-greeter.conf
fi
fi
# Set XFCE scaling factor in skeleton config
XFCE_XSETTINGS="/etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml"
if [[ -f "$XFCE_XSETTINGS" ]]; then
sed -i 's|\(<property name="WindowScalingFactor" type="int" value="\)[^"]*\(">\)|\12\2|' "$XFCE_XSETTINGS"
fi
# Set a larger console font for framebuffer
if [[ -f /usr/share/consolefonts/Uni3-TerminusBold32x16.psf.gz ]]; then
setfont /usr/share/consolefonts/Uni3-TerminusBold32x16.psf.gz
fi
fi
fi
clear
# In case VENDORPRETTYNAME is defined, display that
[[ -n "${VENDORPRETTYNAME}" ]] && VENDOR="$VENDORPRETTYNAME"
echo -e "Welcome to \e[1m\e[97m${VENDOR}\x1B[0m! \n"
echo -e "Documentation: \e[1m\e[92m${VENDORDOCS}\x1B[0m | Community support: \e[1m\e[92m${VENDORSUPPORT}\x1B[0m\n"
echo "" # empty line
# Try to get the local IP address (script halts until IP was found or x retries were done)
get_local_ip_addr
if systemctl is-active --quiet armbian-web-config.service; then
WEB_SSID="$(cat /etc/hostname)-armbiansetup"
echo -e "\n\e[1m\e[96mWeb Configuration Available!\x1B[0m"
echo -e "Connect to Wi-Fi SSID: \e[1m\e[92m${WEB_SSID}\x1B[0m"
echo -e "Then open \e[1mhttp://10.42.0.1\x1B[0m in your browser"
echo -e "Or continue with CLI setup below...\n"
fi
[[ -z "$PRESET_ROOT_PASSWORD" ]] && echo "" # empty line
trap '' 2
REPEATS=3
while [ -f "/root/.not_logged_in_yet" ]; do
. /root/.not_logged_in_yet
if [ -z "$PRESET_ROOT_PASSWORD" ];then
read_password "Create root"
else
# allow automated install also in interactive session
#if [ "$(who am i | awk '{print $2}')" != "tty1" ];then
# exit
#fi
password="$PRESET_ROOT_PASSWORD"
# download SSH key if defined
if [[ -n "${PRESET_ROOT_KEY}" ]]; then
mkdir -p /root/.ssh/
_f="/root/.ssh/authorized_keys"
if curl --retry 5 --connect-timeout 3 "${PRESET_ROOT_KEY}" > "${_f}.tmp" 2> /dev/null; then
atomic_write "${_f}.tmp" "${_f}" 600 || exit 1
else
rm -f "${_f}.tmp"
exit 1
fi
fi
fi
# enable motd
chmod +x /etc/update-motd.d/*
first_input="$password"
if [ -z "$PRESET_ROOT_PASSWORD" ];then
echo ""
read_password "Repeat root"
echo ""
else
password="$PRESET_ROOT_PASSWORD"
fi
second_input="$password"
if [[ "$first_input" == "$second_input" ]]; then
# minimal might not have this
if command -v cracklib-check > /dev/null 2>&1; then
result="$(cracklib-check <<< "$password")"
okay="$(awk -F': ' '{ print $2}' <<< "$result")"
if [[ "$okay" != "OK" ]]; then
echo -e "\n\e[0;31mWarning:\x1B[0m Weak password, $okay \b!"
fi
fi
(
echo "$first_input"
echo "$second_input"
) | passwd root > /dev/null 2>&1
break
elif [[ -n $password ]]; then
echo -e "Rejected - \e[0;31mpasswords do not match.\x1B[0m Try again [${REPEATS}]."
REPEATS=$((REPEATS - 1))
fi
[[ "$REPEATS" -eq 0 ]] && exit
done
trap - INT
# display support status
if [ "$IMAGE_TYPE" != "nightly" ]; then
if [[ "$BRANCH" == "edge" ]]; then
echo -e "\nSupport status: \e[0;31mcommunity support\x1B[0m (edge kernel branch)"
elif [[ "$DISTRIBUTION_STATUS" != "supported" ]]; then
echo -e "\nSupport status: \e[0;31mcommunity support\x1B[0m (unsupported userspace)"
elif [[ "$BOARD_TYPE" != "conf" ]]; then
echo -e "\nSupport status: \e[0;31mcommunity support\x1B[0m (looking for a dedicated maintainer)"
fi
else
echo -e "\e[0;31m\nDeveloper Preview Build\x1B[0m\n"
echo -e "This Armbian image was generated automatically for development and testing purpose."
echo -e "It may include unfinished features or unstable components.\n"
echo -e "If you are not here to report issues or just test it, \e[0;31mplease do not use this image in production.\x1B[0m"
echo -e "Expect things to change — or even break — as improvements are made."
fi
# ask user to select shell
trap '' 2
set_shell || exit 1
trap - INT
trap check_abort INT
if [ -f "/root/.not_logged_in_yet" ]; then
[[ -z "${PRESET_USER_NAME}" ]] && echo -e "\nCreating a new user account. Press <Ctrl-C> to abort"
[[ "${desktop_dm}" != "none" ]] && echo -e "\n\e[0;31mDesktop environment will not be enabled if you abort the new user creation\x1B[0m"
add_user
if [[ $? -ne 0 || -z "${RealUserName:-}" ]]; then
echo -e "\nUser creation failed. Retrying on next login."
exit 1
fi
fi
trap - INT
# ask user to select automated locales or not
trap '' 2
set_timezone_and_locales || exit 1
trap - INT
if [[ ${USER_SHELL} == zsh ]]; then
printf "\nYou selected \e[0;91mZSH\x1B[0m as your default shell. If you want to use it right away, please logout and login! \n\n"
fi
# re-enable passing locale environment via ssh (atomic write)
_f="/etc/ssh/sshd_config"
if sed -E '/^[[:blank:]]*#[[:blank:]]*AcceptEnv[[:blank:]]+LANG/ s/^[[:blank:]]*#[[:blank:]]*//' "${_f}" > "${_f}.tmp"; then
atomic_write "${_f}.tmp" "${_f}" || exit 1
else
rm -f "${_f}.tmp"
exit 1
fi
# Restart ssh.service; on failure preserve /root/.not_logged_in_yet so
# firstlogin reruns for recovery. Unlike reload, restart succeeds on an
# inactive unit, so this does not loop on a fresh image.
systemctl restart ssh.service || exit 1
# All critical configuration is complete. The EXIT trap may now safely
# remove the marker; subsequent failures (DM startup, etc.) should not
# force a full firstlogin rerun.
FIRSTLOGIN_SUCCESS=1
# rpardini: hacks per-dm, very much legacy stuff that works by a miracle
if [[ "${desktop_dm}" == "lightdm" ]] && [ -n "$RealName" ]; then
mkdir -p /etc/lightdm/lightdm.conf.d
cat <<- EOF > /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
[Seat:*]
autologin-user=$RealUserName
autologin-user-timeout=0
user-session=xfce
EOF
# select gnome session (has to be first or it breaks budgie/cinnamon desktop autologin and user-session)
# @TODO: remove this, gnome should use gdm3, not lightdm
[[ -x $(command -v gnome-session) ]] && sed -i "s/user-session.*/user-session=ubuntu/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v gnome-session) ]] && sed -i "s/user-session.*/user-session=ubuntu/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select awesome session
[[ -x $(command -v awesome) ]] && sed -i "s/user-session.*/user-session=awesome/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v awesome) ]] && sed -i "s/user-session.*/user-session=awesome/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select budgie session
[[ -x $(command -v budgie-desktop) ]] && sed -i "s/user-session.*/user-session=budgie-desktop/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v budgie-desktop) ]] && sed -i "s/user-session.*/user-session=budgie-desktop/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select cinnamon session
[[ -x $(command -v cinnamon) ]] && sed -i "s/user-session.*/user-session=cinnamon/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v cinnamon) ]] && sed -i "s/user-session.*/user-session=cinnamon/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select deepin session
[[ -x $(command -v deepin-wm) ]] && sed -i "s/user-session.*/user-session=deepin/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v deepin-wm) ]] && sed -i "s/user-session.*/user-session=deepin/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select ice-wm session
[[ -x $(command -v icewm-session) ]] && sed -i "s/user-session.*/user-session=icewm-session/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v icewm-session) ]] && sed -i "s/user-session.*/user-session=icewm-session/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select i3 session
[[ -x $(command -v i3) ]] && sed -i "s/user-session.*/user-session=i3/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v i3) ]] && sed -i "s/user-session.*/user-session=i3/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select lxde session
[[ -x $(command -v startlxde) ]] && sed -i "s/user-session.*/user-session=LXDE/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v startlxde) ]] && sed -i "s/user-session.*/user-session=LXDE/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select lxqt session
[[ -x $(command -v startlxqt) ]] && sed -i "s/user-session.*/user-session=lxqt/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v startlxqt) ]] && sed -i "s/user-session.*/user-session=lxqt/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select mate session
[[ -x $(command -v mate-wm) ]] && sed -i "s/user-session.*/user-session=mate/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v mate-wm) ]] && sed -i "s/user-session.*/user-session=mate/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select sway wayland session
[[ -x $(command -v sway) ]] && sed -i "s/user-session.*/user-session=sway/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v sway) ]] && sed -i "s/user-session.*/user-session=sway/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
# select xmonad session. The xmonad package ships two of them:
# xmonad.desktop runs the window manager bare, while
# gnome-flashback-xmonad.desktop runs it inside a GNOME Flashback
# session and so gets a session dbus, a polkit agent and session
# management. Prefer the latter when it can actually start -- its
# TryExec is gnome-flashback, which comes from gnome-session-flashback
# and is not always installed -- and fall back to the bare session.
if [[ -x $(command -v xmonad) ]]; then
xmonad_session="xmonad"
if [[ -f /usr/share/xsessions/gnome-flashback-xmonad.desktop && -x $(command -v gnome-flashback) ]]; then
xmonad_session="gnome-flashback-xmonad"
fi
sed -i "s/user-session.*/user-session=${xmonad_session}/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
sed -i "s/user-session.*/user-session=${xmonad_session}/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
fi
ln -sf /lib/systemd/system/lightdm.service /etc/systemd/system/display-manager.service
if [[ -f /var/run/resize2fs-reboot ]]; then
# Let the user reboot now otherwise start desktop environment
printf "\n\n\e[0;91mWarning: a reboot is needed to finish resizing the filesystem \x1B[0m \n"
printf "\e[0;91mPlease reboot the system now \x1B[0m \n\n"
else
echo -e "\n\e[1m\e[39mNow starting desktop environment...\x1B[0m\n"
sleep 1
systemctl --no-block start lightdm 2>/dev/null
if [ -f /root/.desktop_autologin ]; then
rm /root/.desktop_autologin
else
systemctl -q enable armbian-disable-autologin.timer
systemctl start armbian-disable-autologin.timer
fi
# logout if logged at console
who -la | grep root | grep -q tty1 && exit 1
fi
elif [[ "${desktop_dm}" == "gdm3" ]] && [ -n "$RealName" ]; then
# 1st run goes without login
mkdir -p /etc/gdm3
cat <<- EOF > /etc/gdm3/custom.conf
[daemon]
AutomaticLoginEnable = true
AutomaticLogin = $RealUserName
EOF
ln -sf /lib/systemd/system/gdm3.service /etc/systemd/system/display-manager.service
if [[ -f /var/run/resize2fs-reboot ]]; then
# Let the user reboot now otherwise start desktop environment
printf "\n\n\e[0;91mWarning: a reboot is needed to finish resizing the filesystem \x1B[0m \n"
printf "\e[0;91mPlease reboot the system now \x1B[0m \n\n"
else
echo -e "\n\e[1m\e[39mNow starting desktop environment...\x1B[0m\n"
sleep 1
systemctl --no-block start gdm3 2>/dev/null
if [ -f /root/.desktop_autologin ]; then
rm /root/.desktop_autologin
else
(
sleep 20
sed -i "s/AutomaticLoginEnable.*/AutomaticLoginEnable = false/" /etc/gdm3/custom.conf
) &
fi
# logout if logged at console
who -la | grep root | grep -q tty1 && exit 1
fi
elif [[ "${desktop_dm}" == "sddm" ]] && [ -n "$RealName" ]; then
# create default sddm config
mkdir -p /etc/sddm.conf.d
if [ ! -e "/etc/sddm.conf.d/armbian.conf" ]; then
cat <<- EOF > /etc/sddm.conf.d/armbian.conf
[Theme]
Current=breeze
[General]
InputMethod=none
EOF
fi
# 1st run goes without login
cat <<- EOF > /etc/sddm.conf.d/autologin.conf
[Autologin]
User=$RealUserName
EOF
echo -e "\n\e[1m\e[39mNow starting desktop environment via ${desktop_dm}...\x1B[0m\n"
systemctl enable sddm 2>/dev/null
systemctl --no-block start sddm 2>/dev/null
if [ -f /root/.desktop_autologin ]; then
rm /root/.desktop_autologin
else
systemctl -q enable armbian-disable-autologin.timer
fi
# logout if logged at console
who -la | grep root | grep -q tty1 && exit 1
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
if [[ -f /var/run/resize2fs-reboot ]]; then
printf "\n\n\e[0;91mWarning: a reboot is needed to finish resizing the filesystem \x1B[0m \n"
printf "\e[0;91mPlease reboot the system now \x1B[0m \n\n"
fi
fi
fi
# Run provisioning script if exists
if [[ -f /root/provisioning.sh ]]; then
. /root/provisioning.sh
fi
# Marker cleanup is handled by _firstlogin_cleanup EXIT trap.