#!/usr/bin/bash # # NextZenOS Installer Script v1.0 # Requires: bash, mv, rm, tr, grep, sed, curl/wget, tar, smartmontools, parted, ntfs-3g, net-tools # # This script installs NextZenOS to your system. # Usage: # # $ wget -qO- https://dl.nextzenos.com/| bash # or # $ curl -fsSL https://dl.nextzenos.com/ | bash # # In automated environments, you may want to run as root. # If using curl, we recommend using the -fsSL flags. # # This only work on Linux systems. Please # open an issue if you notice any bugs. # echo -e "\e[0m\c" # shellcheck disable=SC2016 echo ' _ _ _______ _______ __________ _ _ | \ | | ____\ \/ /_ _|__ / ____| \ | | | \| | _| \ / | | / /| _| | \| | | |\ | |___ / \ | | / /_| |___| |\ | |_| \_|_____/_/\_\ |_| /____|_____|_| \_| --- Power by NextZEN --- ' export PATH=/usr/sbin:$PATH export DEBIAN_FRONTEND=noninteractive # set -x set -e ############################################################################### # GOLBALS # ############################################################################### ((EUID)) && sudo_cmd="sudo" # shellcheck source=/dev/null source /etc/os-release # Version readonly Version="version-1.4" readonly Branch="version-1.x" # SYSTEM REQUIREMENTS readonly MINIMUM_DISK_SIZE_GB="5" readonly MINIMUM_MEMORY="400" readonly MINIMUM_DOCKER_VERSION="20" readonly NEXTZEN_DEPENDS_PACKAGE=('wget' 'curl' 'smartmontools' 'parted' 'ntfs-3g' 'net-tools' 'udevil' 'samba' 'cifs-utils' 'mergerfs' 'unzip') readonly NEXTZEN_DEPENDS_COMMAND=('wget' 'curl' 'smartctl' 'parted' 'ntfs-3g' 'netstat' 'udevil' 'smbd' 'mount.cifs' 'mount.mergerfs' 'unzip') # SYSTEM INFO PHYSICAL_MEMORY=$(LC_ALL=C free -m | awk '/Mem:/ { print $2 }') readonly PHYSICAL_MEMORY FREE_DISK_BYTES=$(LC_ALL=C df -P / | tail -n 1 | awk '{print $4}') readonly FREE_DISK_BYTES readonly FREE_DISK_GB=$((FREE_DISK_BYTES / 1024 / 1024)) LSB_DIST=$( ([ -n "${ID_LIKE}" ] && echo "${ID_LIKE}") || ([ -n "${ID}" ] && echo "${ID}")) readonly LSB_DIST DIST=$(echo "${ID}") readonly DIST UNAME_M="$(uname -m)" readonly UNAME_M UNAME_U="$(uname -s)" readonly UNAME_U readonly NEXTZEN_CONF_PATH=/etc/casaos/gateway.ini readonly NEXTZEN_UNINSTALL_URL="https://git.nextzenos.com/CDN/NextZenOS/raw/branch/$Branch/$Version/uninstall.sh" readonly BACKUP_UNINSTALL_URL="https://raw.githubusercontent.com/KaySar12/NextZen-Script/refs/heads/$Version/uninstall.sh" readonly NEXTZEN_UNINSTALL_PATH=/usr/bin/nextzenos-uninstall # REQUIREMENTS CONF PATH # Udevil readonly UDEVIL_CONF_PATH=/etc/udevil/udevil.conf readonly DEVMON_CONF_PATH=/etc/conf.d/devmon # COLORS readonly COLOUR_RESET='\e[0m' readonly aCOLOUR=( '\e[38;5;154m' # green | Lines, bullets and separators '\e[1m' # Bold white | Main descriptions '\e[90m' # Grey | Credits '\e[91m' # Red | Update notifications Alert '\e[33m' # Yellow | Emphasis ) readonly GREEN_LINE=" ${aCOLOUR[0]}─────────────────────────────────────────────────────$COLOUR_RESET" readonly GREEN_BULLET=" ${aCOLOUR[0]}-$COLOUR_RESET" readonly GREEN_SEPARATOR="${aCOLOUR[0]}:$COLOUR_RESET" # CASAOS VARIABLES TARGET_ARCH="" TMP_ROOT=/tmp/casaos-installer REGION="UNKNOWN" DOWNLOAD_DOMAIN="https://cdn.nextzenos.com" trap 'onCtrlC' INT onCtrlC() { echo -e "${COLOUR_RESET}" exit 1 } ############################################################################### # Helpers # ############################################################################### ####################################### # Custom printing function # Globals: # None # Arguments: # $1 0:OK 1:FAILED 2:INFO 3:NOTICE # message # Returns: # None ####################################### Show() { # OK if (($1 == 0)); then echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" # FAILED elif (($1 == 1)); then echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" exit 1 # INFO elif (($1 == 2)); then echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" # NOTICE elif (($1 == 3)); then echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" fi } Warn() { echo -e "${aCOLOUR[3]}$1$COLOUR_RESET" } GreyStart() { echo -e "${aCOLOUR[2]}\c" } ColorReset() { echo -e "$COLOUR_RESET\c" } # Clear Terminal Clear_Term() { # Without an input terminal, there is no point in doing this. [[ -t 0 ]] || return # Printing terminal height - 1 newlines seems to be the fastest method that is compatible with all terminal types. lines=$(tput lines) i newlines local lines for ((i = 1; i < ${lines% *}; i++)); do newlines+='\n'; done echo -ne "\e[0m$newlines\e[H" } # Check file exists exist_file() { if [ -e "$1" ]; then return 1 else return 2 fi } ############################################################################### # FUNCTIONS # ############################################################################### # 0 Get download url domain # To solve the problem that Chinese users cannot access github. # Get_Download_Url_Domain() { # # Use ipconfig.io/country and https://ifconfig.io/country_code to get the country code # REGION=$(${sudo_cmd} curl --connect-timeout 2 -s ipconfig.io/country || echo "") # if [ "${REGION}" = "" ]; then # REGION=$(${sudo_cmd} curl --connect-timeout 2 -s https://ifconfig.io/country_code || echo "") # fi # if [[ "${REGION}" = "China" ]] || [[ "${REGION}" = "CN" ]]; then # GITEA_DOWNLOAD_DOMAIN="https://casaos.oss-cn-shanghai.aliyuncs.com/" # fi # } # 1 Check Archclient_iduip Check_Arch() { case $UNAME_M in *aarch64*) TARGET_ARCH="arm64" ;; *64*) TARGET_ARCH="amd64" ;; *armv7*) TARGET_ARCH="arm-7" ;; *) Show 1 "Aborted, unsupported or unknown architecture: $UNAME_M" exit 1 ;; esac Show 0 "Your hardware architecture is : $UNAME_M" NEXTZEN_PACKAGES=( "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-gateway.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-message-bus.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-user-service.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-local-storage.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-app-management.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-cli.tar.gz" "${DOWNLOAD_DOMAIN}/CDN/NextZenOS/releases/download/$Version/linux-${TARGET_ARCH}-nextzenos-ui.tar.gz" ) } # PACKAGE LIST OF CASAOS (make sure the services are in the right order) NEXTZEN_SERVICES=( "casaos-gateway.service" "casaos-message-bus.service" "casaos-user-service.service" "casaos-local-storage.service" "casaos-app-management.service" "rclone.service" "casaos.service" # must be the last one so update from UI can work ) # 2 Check Distribution Check_Distribution() { sType=0 notice="" case $LSB_DIST in *debian*) ;; *ubuntu*) ;; *raspbian*) ;; *openwrt*) Show 1 "Aborted, OpenWrt cannot be installed using this script." exit 1 ;; *alpine*) Show 1 "Aborted, Alpine installation is not yet supported." exit 1 ;; *trisquel*) ;; *) sType=3 notice="We have not tested it on this system and it may fail to install." ;; esac Show ${sType} "Your Linux Distribution is : ${DIST} ${notice}" if [[ ${sType} == 1 ]]; then select yn in "Yes" "No"; do case $yn in [yY][eE][sS] | [yY]) Show 0 "Distribution check has been ignored." break ;; [nN][oO] | [nN]) Show 1 "Already exited the installation." exit 1 ;; esac done "${PREFIX}/tmp/nextzenos-uninstall" else echo "Backup URL is also not working, cannot uninstall Nextzen." return 1 fi else curl -fsSLk "$NEXTZEN_UNINSTALL_URL" >"${PREFIX}/tmp/nextzenos-uninstall" fi } Check_Dependency_Installation() { for ((i = 0; i < ${#NEXTZEN_DEPENDS_COMMAND[@]}; i++)); do cmd=${NEXTZEN_DEPENDS_COMMAND[i]} if [[ ! -x $(${sudo_cmd} which "$cmd") ]]; then packagesNeeded=${NEXTZEN_DEPENDS_PACKAGE[i]} Show 1 "Dependency \e[33m$packagesNeeded \e[0m installation failed, please try again manually!" exit 1 fi done } # Check Docker running Check_Docker_Running() { for ((i = 1; i <= 3; i++)); do sleep 3 if [[ ! $(${sudo_cmd} systemctl is-active docker) == "active" ]]; then Show 1 "Docker is not running, try to start" ${sudo_cmd} systemctl start docker else break fi done } #Check Docker Installed and version Check_Docker_Install() { if [[ -x "$(command -v docker)" ]]; then Docker_Version=$(${sudo_cmd} docker version --format '{{.Server.Version}}') if [[ $? -ne 0 ]]; then Install_Docker elif [[ ${Docker_Version:0:2} -lt "${MINIMUM_DOCKER_VERSION}" ]]; then Show 1 "Recommended minimum Docker version is \e[33m${MINIMUM_DOCKER_VERSION}.xx.xx\e[0m,\Current Docker version is \e[33m${Docker_Version}\e[0m,\nPlease uninstall current Docker and rerun the NextZenOS installation script." exit 1 else Show 0 "Current Docker version is ${Docker_Version}." fi else Install_Docker fi } # Check Docker installed Check_Docker_Install_Final() { if [[ -x "$(command -v docker)" ]]; then Docker_Version=$(${sudo_cmd} docker version --format '{{.Server.Version}}') if [[ $? -ne 0 ]]; then Install_Docker elif [[ ${Docker_Version:0:2} -lt "${MINIMUM_DOCKER_VERSION}" ]]; then Show 1 "Recommended minimum Docker version is \e[33m${MINIMUM_DOCKER_VERSION}.xx.xx\e[0m,\Current Docker version is \e[33m${Docker_Version}\e[0m,\nPlease uninstall current Docker and rerun the NextZenOS installation script." exit 1 else Show 0 "Current Docker version is ${Docker_Version}." Check_Docker_Running fi else Show 1 "Installation failed, please run 'curl -fsSL https://get.docker.com | bash' and rerun the NextZenOS installation script." exit 1 fi } #Install Docker Install_Docker() { Show 2 "Install the necessary dependencies: \e[33mDocker \e[0m" if [[ ! -d "${PREFIX}/etc/apt/sources.list.d" ]]; then ${sudo_cmd} mkdir -p "${PREFIX}/etc/apt/sources.list.d" fi GreyStart if [[ "${REGION}" = "CN" ]]; then ${sudo_cmd} curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun else ${sudo_cmd} curl -fsSL https://get.docker.com | bash fi ColorReset if [[ $? -ne 0 ]]; then Show 1 "Installation failed, please try again." exit 1 else Check_Docker_Install_Final fi } #Install Rclone Install_rclone_from_source() { ${sudo_cmd} wget -qO ./install.sh https://rclone.org/install.sh if [[ "${REGION}" = "China" ]] || [[ "${REGION}" = "CN" ]]; then sed -i 's/downloads.rclone.org/casaos.oss-cn-shanghai.aliyuncs.com/g' ./install.sh else sed -i 's/downloads.rclone.org/get.casaos.io/g' ./install.sh fi ${sudo_cmd} chmod +x ./install.sh ${sudo_cmd} ./install.sh || { Show 1 "Installation failed, please try again." ${sudo_cmd} rm -rf install.sh exit 1 } ${sudo_cmd} rm -rf install.sh Show 0 "Rclone v1.61.1 installed successfully." } Install_Rclone() { Show 2 "Install the necessary dependencies: Rclone" if [[ -x "$(command -v rclone)" ]]; then version=$(rclone --version 2>>errors | head -n 1) target_version="rclone v1.61.1" rclone1="${PREFIX}/usr/share/man/man1/rclone.1.gz" if [ "$version" != "$target_version" ]; then Show 3 "Will change rclone from $version to $target_version." rclone_path=$(command -v rclone) ${sudo_cmd} rm -rf "${rclone_path}" if [[ -f "$rclone1" ]]; then ${sudo_cmd} rm -rf "$rclone1" fi Install_rclone_from_source else Show 2 "Target version already installed." fi else Install_rclone_from_source fi ${sudo_cmd} systemctl enable rclone || Show 3 "Service rclone does not exist." } #Configuration Addons Configuration_Addons() { Show 2 "Configuration Addons" #Remove old udev rules if [[ -f "${PREFIX}/etc/udev/rules.d/11-usb-mount.rules" ]]; then ${sudo_cmd} rm -rf "${PREFIX}/etc/udev/rules.d/11-usb-mount.rules" fi if [[ -f "${PREFIX}/etc/systemd/system/usb-mount@.service" ]]; then ${sudo_cmd} rm -rf "${PREFIX}/etc/systemd/system/usb-mount@.service" fi #Udevil if [[ -f $PREFIX${UDEVIL_CONF_PATH} ]]; then # GreyStart # Add a devmon user USERNAME=devmon id ${USERNAME} &>/dev/null || { ${sudo_cmd} useradd -M -u 300 ${USERNAME} ${sudo_cmd} usermod -L ${USERNAME} } ${sudo_cmd} sed -i '/exfat/s/, nonempty//g' "$PREFIX"${UDEVIL_CONF_PATH} ${sudo_cmd} sed -i '/default_options/s/, noexec//g' "$PREFIX"${UDEVIL_CONF_PATH} ${sudo_cmd} sed -i '/^ARGS/cARGS="--mount-options nosuid,nodev,noatime --ignore-label EFI"' "$PREFIX"${DEVMON_CONF_PATH} # Add and start Devmon service GreyStart ${sudo_cmd} systemctl enable devmon@devmon ${sudo_cmd} systemctl start devmon@devmon ColorReset # ColorReset fi } # Download And Install NextZenOS DownloadAndInstallNextzenOS() { if [ -z "${BUILD_DIR}" ]; then ${sudo_cmd} rm -rf ${TMP_ROOT} mkdir -p ${TMP_ROOT} || Show 1 "Failed to create temporary directory" TMP_DIR=$(${sudo_cmd} mktemp -d -p ${TMP_ROOT} || Show 1 "Failed to create temporary directory") ${sudo_cmd} chmod 755 "${TMP_DIR}" ${sudo_cmd} chown "$USER" "${TMP_DIR}" pushd "${TMP_DIR}" for PACKAGE in "${NEXTZEN_PACKAGES[@]}"; do Show 2 "Downloading ${PACKAGE}..." GreyStart ${sudo_cmd} wget -t 3 -q --show-progress -c "${PACKAGE}" || Show 1 "Failed to download package" ColorReset done for PACKAGE_FILE in linux-*.tar.gz; do Show 2 "Extracting ${PACKAGE_FILE}..." GreyStart ${sudo_cmd} tar zxf "${PACKAGE_FILE}" || Show 1 "Failed to extract package" ColorReset done BUILD_DIR=$(${sudo_cmd} realpath -e "${TMP_DIR}"/build || Show 1 "Failed to find build directory") echo "${BUILD_DIR}" popd fi for SERVICE in "${NEXTZEN_SERVICES[@]}"; do if ${sudo_cmd} systemctl --quiet is-active "${SERVICE}"; then Show 2 "Stopping ${SERVICE}..." GreyStart ${sudo_cmd} systemctl stop "${SERVICE}" || Show 3 "Service ${SERVICE} does not exist." ColorReset fi done Show 2 "Installing NextZenOS..." SYSROOT_DIR=$(realpath -e "${BUILD_DIR}"/sysroot || Show 1 "Failed to find sysroot directory") # Generate manifest for uninstallation MANIFEST_FILE=${BUILD_DIR}/sysroot/var/lib/casaos/manifest ${sudo_cmd} touch "${MANIFEST_FILE}" || Show 1 "Failed to create manifest file" GreyStart find "${SYSROOT_DIR}" -type f | ${sudo_cmd} cut -c ${#SYSROOT_DIR}- | ${sudo_cmd} cut -c 2- | ${sudo_cmd} tee "${MANIFEST_FILE}" >/dev/null || Show 1 "Failed to create manifest file" ${sudo_cmd} cp -rf "${SYSROOT_DIR}"/* / || Show 1 "Failed to install NextZenOS" ColorReset SETUP_SCRIPT_DIR=$(realpath -e "${BUILD_DIR}"/scripts/setup/script.d || Show 1 "Failed to find setup script directory") for SETUP_SCRIPT in "${SETUP_SCRIPT_DIR}"/*.sh; do Show 2 "Running ${SETUP_SCRIPT}..." GreyStart ${sudo_cmd} bash "${SETUP_SCRIPT}" || Show 1 "Failed to run setup script" ColorReset done UI_EVENTS_REG_SCRIPT=/etc/casaos/start.d/register-ui-events.sh if [[ -f ${UI_EVENTS_REG_SCRIPT} ]]; then ${sudo_cmd} chmod +x $UI_EVENTS_REG_SCRIPT fi # Modify app store configuration #Download Uninstall Script if [[ -f $PREFIX/tmp/nextzenos-uninstall ]]; then ${sudo_cmd} rm -rf "$PREFIX/tmp/nextzenos-uninstall" fi # ${sudo_cmd} curl -fsSLk "$NEXTZEN_UNINSTALL_URL" >"$PREFIX/tmp/nextzenos-uninstall" setup_uninstall_nextzen ${sudo_cmd} cp -rf "$PREFIX/tmp/nextzenos-uninstall" $NEXTZEN_UNINSTALL_PATH || { Show 1 "Download uninstall script failed, Please check if your internet connection is working and retry." exit 1 } ${sudo_cmd} chmod +x $NEXTZEN_UNINSTALL_PATH Install_Rclone for SERVICE in "${NEXTZEN_SERVICES[@]}"; do Show 2 "Starting ${SERVICE}..." GreyStart ${sudo_cmd} systemctl start "${SERVICE}" || Show 3 "Service ${SERVICE} does not exist." ColorReset done } Clean_Temp_Files() { Show 2 "Clean temporary files..." ${sudo_cmd} rm -rf "${TMP_DIR}" || Show 1 "Failed to clean temporary files" } Check_Service_status() { for SERVICE in "${NEXTZEN_SERVICES[@]}"; do Show 2 "Checking ${SERVICE}..." if [[ $(${sudo_cmd} systemctl is-active "${SERVICE}") == "active" ]]; then Show 0 "${SERVICE} is running." else Show 1 "${SERVICE} is not running, Please reinstall." exit 1 fi done } # Get the physical NIC IP Get_IPs() { PORT=$(${sudo_cmd} cat ${NEXTZEN_CONF_PATH} | grep port | sed 's/port=//') ALL_NIC=$($sudo_cmd ls /sys/class/net/ | grep -v "$(ls /sys/devices/virtual/net/)") for NIC in ${ALL_NIC}; do IP=$($sudo_cmd ifconfig "${NIC}" | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | sed -e 's/addr://g') if [[ -n $IP ]]; then if [[ "$PORT" -eq "80" ]]; then echo -e "${GREEN_BULLET} http://$IP (${NIC})" else echo -e "${GREEN_BULLET} http://$IP:$PORT (${NIC})" fi fi done } # Show Welcome Banner Welcome_Banner() { NEXTZEN_TAG=$(casaos -v) echo -e "${GREEN_LINE}${aCOLOUR[1]}" echo -e " NextZenOS ${NEXTZEN_TAG}${COLOUR_RESET} is running at${COLOUR_RESET}${GREEN_SEPARATOR}" echo -e "${GREEN_LINE}" Get_IPs echo -e " Open your browser and visit the above address." echo -e "${GREEN_LINE}" echo -e " ${COLOUR_RESET}${aCOLOUR[1]}Uninstall ${COLOUR_RESET}: nextzenos-uninstall" echo -e "${COLOUR_RESET}" } ############################################################################### # Main # ############################################################################### #Usage usage() { cat <<-EOF Usage: install.sh [options] Valid options are: -p Specify build directory (Local install) -h Show this help message and exit EOF exit "$1" } while getopts ":p:h" arg; do case "$arg" in p) BUILD_DIR=$OPTARG ;; h) usage 0 ;; *) usage 1 ;; esac done # Step 0 : Get Download Url Domain # Get_Download_Url_Domain # Step 1: Check ARCH Check_Arch # Step 2: Check OS Check_OS # Step 3: Check Distribution Check_Distribution # Step 4: Check System Required Check_Memory Check_Disk # Step 5: Install Depends Update_Package_Resource Install_Depends Check_Dependency_Installation # Step 6: Check And Install Docker Check_Docker_Install # Step 7: Configuration Addon Configuration_Addons # Step 8: Download And Install NextZenOS DownloadAndInstallNextzenOS # Step 9: Check Service Status Check_Service_status # Step 10: Clear Term and Show Welcome Banner Welcome_Banner # set +x