#!/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}" == 0 ]]; then YAML+=" dhcp4: yes\n";fi if [[ "${PRESET_NET_USE_STATIC}" == 0 ]]; then YAML+=" dhcp6: yes\n";fi 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 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 break 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 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_ROOT_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 for additionalgroup in sudo netdev audio video disk tty users games dialout plugdev input bluetooth systemd-journal ssh render; 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 } 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 systemd-networkd then echo "Both NetworkManager and systemd-networkd services are enabled." echo "This is known to cause problems with network startup." # If no display manager was found, assume this is a server # with network managed by systemd-networkd. Otherwise, assume # it is a workstation with network managed by NetworkManager. if [[ "${desktop_dm}" == "none" ]]; then echo "No Display Manager detected: NetworkManager will be disabled" systemctl stop NetworkManager systemctl disable NetworkManager else echo "${desktop_dm} Display Manager detected: systemd-networkd will be disabled" systemctl stop systemd-networkd systemctl disable systemd-networkd fi fi 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 if [[ "$(cut -d, -f1 < /sys/class/graphics/fb0/virtual_size 2> /dev/null)" -gt 1920 ]]; then # lightdm [[ -f /etc/lightdm/slick-greeter.conf ]] && echo "enable-hidpi = on" >> /etc/lightdm/slick-greeter.conf # xfce [[ -f /etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml ]] && sed -i 's||g' /etc/skel/.config/xfce4/xfconf/xfce-perchannel-xml/xsettings.xml # framebuffer console larger font setfont /usr/share/consolefonts/Uni3-TerminusBold32x16.psf.gz 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 # only allow one login. Once you enter root password, kill others. loginfrom=$(who am i | awk '{print $2}') who -la | grep root | grep -v "$loginfrom" | awk '{print $7}' | 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\nWARNING!\x1B[0m\n\nYou are using an \e[0;31mautomated build\x1B[0m meant only for developers to provide" echo -e "constructive feedback to improve build system, OS settings or UX.\n" echo -e "If this does not apply to you, \e[0;31mSTOP NOW!\x1B[0m Especially don't use this " echo -e "image for production since things might not work as expected or at " echo -e "all. They may break anytime with next update." 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 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 cat <<- EOF > /etc/sddm.conf.d/armbian.conf [Theme] Current=breeze [General] InputMethod=none EOF # 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/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