#!/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) . /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 } 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 i=1 echo -e "\nChoose default system command shell:\n" for o in "${optionsAudits[@]}"; do echo "$i) $o" (( i++ )) || true done read -r -n1 -s reply 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 fi SHELL_PATH=$(grep "/$USER_SHELL$" /etc/shells | tail -1) chsh -s "$(grep -iF "/$USER_SHELL" /etc/shells | tail -1)" echo -e "\nShell: \x1B[92m${USER_SHELL^^}\x1B[0m" # 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 nmcli dev status | grep " wifi " 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 "" read -n1 -s -r -p "Connect via wireless? [Y/n] " response echo "" if [[ "${response}" =~ ^(Y|y|"")$ ]]; then nmtui-connect fi echo "" fi fi # Grab IP once again if not found [[ -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) echo -e "Detected timezone: \x1B[92m$TZDATA\x1B[0m" echo "" unset response while [[ ! "${response}" =~ ^(Y|y|N|n)$ ]]; do read -n1 -s -r -p "Set user language based on your location? [Y/n] " response echo "" 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}" # 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 if [[ "${LOCALES}" != *Skip* ]]; then # if TZDATA was not detected, we need to select one if [[ -z ${TZDATA} ]]; then TZDATA=$(tzselect | tail -1) fi timedatectl set-timezone "${TZDATA}" dpkg-reconfigure --frontend=noninteractive tzdata > /dev/null 2>&1 # generate locales echo "" 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() { /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 echo -e "\nPlease provide a username (eg. your first name): \c" read -r -e username if ! grep '^[a-zA-Z]*$' <<< "$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 read_password "Create user ($username)" first_input="$password" echo "" read_password "Repeat user ($username)" second_input="$password" echo "" if [[ "$first_input" == "$second_input" ]]; 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 echo -e "" read -r -e -p "Please provide your real name: " -i "${RealUserName^}" RealName adduser --quiet --disabled-password --home /home/"$RealUserName" --gecos "$RealName" "$RealUserName" (echo "$first_input";echo "$second_input";) | passwd "$RealUserName" >/dev/null 2>&1 for additionalgroup in sudo netdev audio video disk tty users games dialout plugdev input bluetooth systemd-journal ssh; 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}" /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 # 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 # detect display manager desktop_lightdm=$(dpkg-query -W -f='${db:Status-Abbrev}\n' lightdm 2>/dev/null) desktop_gdm3=$(dpkg-query -W -f='${db:Status-Abbrev}\n' gdm3 2>/dev/null) echo -e "\nWaiting for system to finish booting ..." systemctl is-system-running --wait >/dev/null # 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 setfont /usr/share/consolefonts/Uni3-TerminusBold32x16.psf.gz fi clear echo -e "Welcome to \e[1m\e[97mARMBIAN\x1B[0m! \n" echo -e "Documentation: \e[1m\e[92mhttps://docs.armbian.com\x1B[0m | Community: \e[1m\e[92mhttps://forum.armbian.com\x1B[0m\n" GET_IP=$(bash /etc/update-motd.d/30-armbian-sysinfo | grep IP | sed "s/.*IP://" | sed 's/^[ \t]*//') [[ -n "$GET_IP" ]] && echo -e "IP address: $GET_IP\n" trap '' 2 REPEATS=3 while [ -f "/root/.not_logged_in_yet" ]; do read_password "Create root" # 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 first_input="$password" echo "" read_password "Repeat root" second_input="$password" echo "" if [[ "$first_input" == "$second_input" ]]; 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!" (echo "$first_input";echo "$second_input";) | passwd root >/dev/null 2>&1 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 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 echo -e "\nCreating a new user account. Press to abort" [ -n "$desktop_lightdm" ] && 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 declare ConfigureDisplay # check whether desktop environment has to be considered if [ -n "$desktop_lightdm" ] && [ -n "$RealName" ] ; then # 1st run goes without login 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) [[ -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 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 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 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 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 plasma wayland session [[ -x $(command -v plasmashell) ]] && sed -i "s/user-session.*/user-session=plasmawayland/" /etc/lightdm/lightdm.conf.d/11-armbian.conf [[ -x $(command -v plasmashell) ]] && sed -i "s/user-session.*/user-session=plasmawayland/" /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" elif [ -z "$ConfigureDisplay" ] || [ "$ConfigureDisplay" = "n" ] || [ "$ConfigureDisplay" = "N" ]; then 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 [ -n "$desktop_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" elif [ -z "$ConfigureDisplay" ] || [ "$ConfigureDisplay" = "n" ] || [ "$ConfigureDisplay" = "N" ]; then 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 else # 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