armbian-build/packages/bsp/common/usr/lib/armbian/armbian-firstlogin

1039 lines
35 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
check_abort() {
echo -e "\nDisabling user account creation procedure\n"
rm -f /root/.not_logged_in_yet
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
echo -e "$(createYAML)" > /etc/netplan/30-${DEVTYPE}${CONFIG_NAME}.yaml
chmod 600 /etc/netplan/30-${DEVTYPE}${CONFIG_NAME}.yaml
#Enable Wlan, disable Eth
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
echo -e "$(createYAML)" > /etc/netplan/30-${DEVTYPE}${CONFIG_NAME}.yaml
chmod 600 /etc/netplan/30-${DEVTYPE}${CONFIG_NAME}.yaml
# Enable Eth, disable Wlan
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
sed -i "s|^SHELL=.*|SHELL=${SHELL_PATH}|" /etc/default/useradd
sed -i "s|^DSHELL=.*|DSHELL=${SHELL_PATH}|" /etc/adduser.conf
}
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
cat <<- EOF > /etc/netplan/30-wifis-dhcp.yaml
# Created by Armbian firstlogin script
network:
wifis:
${WIFI_DEVICE}:
dhcp4: yes
dhcp6: yes
access-points:
"$SSID":
password: "${password}"
EOF
chmod 600 /etc/netplan/30-wifis-dhcp.yaml
# 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
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
sed -i "s/=.*/=$(echo ${LOCALES} | cut -d" " -f1)/g" /etc/default/locale
# generate locales
sed -i 's/# '"${LOCALES}"'/'"${LOCALES}"'/' /etc/locale.gen
echo -e "Generating locales: \x1B[92m${LOCALES}\x1B[0m"
locale-gen "${LOCALES}" > /dev/null 2>&1
# setting detected locales only for user
{
echo "export LC_ALL=$LOCALES"
echo "export LANG=$LOCALES"
echo "export LANGUAGE=$LOCALES"
} >> /home/"$RealUserName"/.bashrc
{
echo "export LC_ALL=$LOCALES"
echo "export LANG=$LOCALES"
echo "export LANGUAGE=$LOCALES"
} >> /home/"$RealUserName"/.xsessionrc
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
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; else echo -e "Username \e[0;31m$RealUserName\x1B[0m already exists on the system."; fi
done
# 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
adduser --quiet --disabled-password --home /home/"$RealUserName" --gecos "$RealName" "$RealUserName"
# download and add SSH key if defined
if [[ -n "${PRESET_USER_KEY}" ]]; then
mkdir -p /home/"$RealUserName"/.ssh/
curl --retry 5 --connect-timeout 3 "${PRESET_USER_KEY}" > /home/"$RealUserName"/.ssh/authorized_keys 2> /dev/null
chown -R "$RealUserName":"$RealUserName" /home/"$RealUserName"/.ssh/
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 ! addgroup --system --quiet 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"
rm -f /root/.not_logged_in_yet
# set up profile sync daemon on desktop systems
if command -v psd > /dev/null 2>&1; then
echo -e "${RealUserName} ALL=(ALL) NOPASSWD: /usr/bin/psd-overlay-helper" >> /etc/sudoers
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 ]] && logout
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 && -n $(tty) ]]; then
. /root/.not_logged_in_yet
# override configuration from URL
if [[ -n "${PRESET_CONFIGURATION}" ]]; then
curl --retry 5 --connect-timeout 3 "${PRESET_CONFIGURATION}" > /root/.not_logged_in_yet 2> /dev/null
fi
do_firstrun_automated_network_configuration
# disable autologin
rm -f /etc/systemd/system/getty@.service.d/override.conf
rm -f /etc/systemd/system/serial-getty@.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
[[ -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/
curl --retry 5 --connect-timeout 3 "${PRESET_ROOT_KEY}" > /root/.ssh/authorized_keys 2> /dev/null
fi
fi
# Get current session identifiers
current_tty=$(tty | sed 's:/dev/::') # e.g., "tty1"
current_pid=$$
# Kill ONLY other active root SHELL sessions (not login processes)
ps -u root -o pid,tty,comm |
awk -v me="$current_tty" -v mypid="$current_pid" '
NR>1 && $2 != "?" && $2 != me && $1 != mypid && $3 ~ /sh$/ {
print $1
}' |
xargs --no-run-if-empty kill -9
# 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 TERM EXIT
# 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
trap - INT TERM EXIT
trap check_abort INT
while [ -f "/root/.not_logged_in_yet" ]; do
[[ -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
done
trap - INT TERM EXIT
# ask user to select automated locales or not
trap '' 2
set_timezone_and_locales
trap - INT TERM EXIT
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
sed -e '/^#AcceptEnv LANG/ s/^#//' -i /etc/ssh/sshd_config
# restart sshd daemon
systemctl restart ssh.service
# 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
[[ -x $(command -v xmonad) ]] && sed -i "s/user-session.*/user-session=xmonad/" /etc/lightdm/lightdm.conf.d/11-armbian.conf
[[ -x $(command -v xmonad) ]] && sed -i "s/user-session.*/user-session=xmonad/" /etc/lightdm/lightdm.conf.d/22-armbian-autologin.conf
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
service lightdm start 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
service gdm3 start 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 --now 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