#!/bin/bash # To install new device: # sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- -t TOKEN_HERE # To uninstall device: # sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- --uninstall clear cat <<"EOF" .&@&. %% .%@% #@@@% *&@@@&. %@@@# &@@&. .&@@% .%@@@@@%/. .%@@&. ,&@@% %@@&. /@@@# *&@@@@&&&@@@@&, %@@&* .&@@& /@@@* .@@@# &@@&( (@@@% #@@&, *@@@* %@@&. #@@&. (@@@, ,&@@( .@@@( &@@# %@@&. #@@&. /@@@, *&@@( .@@@( &@@# *@@@* .&@@# %@@@# %@@@# #@@&, /@@@, %@@&, *@@@% .&@@@@@@@@@@@%. .&@@&, ,&@@% %@@&* %@@# *#%%%#* #@@% *&@@% (@@@&. .&@@&/ #@# #@# ____ _ __ _ ____ / ___|(_)_ __/ _| __ _| |__ / ___|___ _ __ ___ \___ \| \ \/ / |_ / _` | '_ \ | | / _ \| '__/ _ \ ___) | |> <| _| (_| | |_) | | |__| (_) | | | __/ |____/|_/_/\_\_| \__,_|_.__/ \____\___/|_| \___| ===================================================== EOF AGENT_REPOSITORY="https://github.com/sixfab/core_agent.git" MANAGER_REPOSITORY="https://github.com/sixfab/core_manager.git" VERBOSE_SUFFIX="/dev/null" OS_DISTRO="Raspbian" print_help() { printf "[HELP] $1\n" } print_info() { YELLOW='\033[0;33m' NC='\033[0m' printf "${YELLOW}[INFO]${NC} $1\n" } print_error() { RED='\033[0;31m' NC='\033[0m' printf "${RED}[ERROR]${NC} $1\n" } print_done() { GREEN='\033[0;32m' NC='\033[0m' printf "${GREEN}[DONE]${NC} $1\n" } help() { print_help "To install new device:" print_help 'sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- -t TOKEN_HERE' print_help "To uninstall device:" print_help 'sudo bash -c "$(curl -sN https://install.connect.sixfab.com)" -- --uninstall' destroy_scroll_area } run_command() { for count in {1..3}; do COMMAND=$1 sudo su sixfab -c "eval $COMMAND" &> $VERBOSE_SUFFIX STATUS_CODE=$? if [ $STATUS_CODE -eq "0" ]; then return fi done print_error "Installer faced an error during the following command, please re-run installer" print_error "*****************************************************" printf "\033[0;31m[ERROR]\033[0m $COMMAND\n" print_error "*****************************************************" exit 1 } initialize_parameters() { TOKEN="" B_TOKEN="" REGION="global" BOARD="RaspberryPi4" IS_DEV=False VERBOSE=False deployment_type="device" while getopts "t:p:r:b:dv" arg; do case "$arg" in t) TOKEN="$OPTARG" ;; p) B_TOKEN="$OPTARG" ;; r) REGION="$OPTARG" ;; b) BOARD="$OPTARG" ;; v) VERBOSE=True ;; d) IS_DEV=True ;; -) break ;; \?) ;; esac done if [ -z "$TOKEN" ] && [ -z "$B_TOKEN" ]; then print_error "Device token is missing" help exit 1 fi if [ -z "$TOKEN" ]; then deployment_type="bulk" fi if [ "$VERBOSE" == True ]; then VERBOSE_SUFFIX="/dev/stdout" fi } check_is_root() { if [ $(id -u) != 0 ]; then print_error "This script must be run as root" exit 1 fi } if [ "$1" = "--uninstall" ]; then print_info "Uninstall Sixfab Core..." if [ -d "/opt/sixfab/core" ]; then print_info "Removing source..." sudo rm -r /opt/sixfab/core &> $VERBOSE_SUFFIX print_info "Source removed." fi if [ -d "/home/sixfab/.core" ]; then print_info "Removing local files..." sudo rm -r /home/sixfab/.core &> $VERBOSE_SUFFIX print_info "Local files removed." fi if [ -e "/opt/sixfab/.env.yaml" ]; then print_info "Removing environment file..." sudo rm -r /opt/sixfab/.env.yaml &> $VERBOSE_SUFFIX print_info "Environment file removed." fi print_info "Stopping & removing services..." sudo systemctl stop core_agent &> $VERBOSE_SUFFIX sudo systemctl stop core_manager &> $VERBOSE_SUFFIX sudo rm /etc/systemd/system/core_agent.service &> $VERBOSE_SUFFIX sudo rm /etc/systemd/system/core_manager.service &> $VERBOSE_SUFFIX sudo systemctl daemon-reload print_info "Removed services." print_done "Sixfab Core is uninstalled successfully" exit 0 fi check_distro() { OS_DETAILS=$(cat /etc/os-release) case "$OS_DETAILS" in *Raspbian*) OS_DISTRO="Raspbian" return ;; *Ubuntu*|*ubuntu*) OS_DISTRO="Ubuntu" return ;; esac read -p "[WARNING] The operating system is not one of the supported ones, Sixfab Core may not run properly. Do you want to continue? (y/N) " yn case "$yn" in *y*|*Y*) return ;; *) print_info "Installer is exiting..." exit 1 ;; esac } check_usb_is_connected() { GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m' NC='\033[0m' LSUSB_OUTPUT=$(lsusb) > /dev/null 2>&1 case "$LSUSB_OUTPUT" in *Telit*|*1b7c*|*Quectel*|*2c7c*) printf -- "-------\n" printf "${RED}Warning\n${NC}" printf -- "-------\n" printf "You are using the cellular modem. It may cause unintended data costs or failed installation!\n" printf -- "-> Unplug the USB cable from the Sixfab HAT before continuing! (${GREEN}Recommended${NC})\n" printf -- "-> Then press ${YELLOW}ENTER${NC} to continue to installation.\n" read -r -p "" foo return ;; *) return ;; esac } update_system() { print_info "Updating system package index..." run_command "sudo apt update -y" } check_user() { create_sixfab_user() { sudo adduser --disabled-password --gecos "" sixfab &> $VERBOSE_SUFFIX } add_usb_permissions_to_plugdev() { PERMISSIONS_TO_ADD="SUBSYSTEM==\"usb\", ENV{DEVTYPE}==\"usb_device\", MODE=\"0664\", GROUP=\"plugdev\"" PLUGDEV_RULES_PATH=/etc/udev/rules.d/plugdev_usb.rules if [ ! -f $PLUGDEV_RULES_PATH ]; then echo $PERMISSIONS_TO_ADD | sudo tee $PLUGDEV_RULES_PATH &> $VERBOSE_SUFFIX sudo udevadm control --reload sudo udevadm trigger fi } add_gpio_permissions_for_ubuntu() { GPIO_RULES_PATH=/etc/udev/rules.d/gpio.rules if [ ! -f $GPIO_RULES_PATH ]; then echo "\"SUBSYSTEM==\"gpio\", GROUP=\"gpio\", MODE=\"0660\" SUBSYSTEM==\"gpio*\", PROGRAM=\"/bin/sh -c '\ chown -R root:gpio /sys/class/gpio && chmod -R 770 /sys/class/gpio;\ chown -R root:gpio /sys/devices/virtual/gpio && chmod -R 770 /sys/devices/virtual/gpio;\ chown -R root:gpio /sys\$devpath && chmod -R 770 /sys\$devpath\ '\"" | sudo tee $GPIO_RULES_PATH &> $VERBOSE_SUFFIX sudo groupadd gpio sudo usermod -aG gpio sixfab sudo udevadm control --reload sudo udevadm trigger fi } check_sixfab_user_privilege() { add_usb_permissions_to_plugdev case "$OS_DISTRO" in *Raspbian*) sudo usermod -aG spi sixfab &> $VERBOSE_SUFFIX sudo usermod -aG i2c sixfab &> $VERBOSE_SUFFIX sudo usermod -aG gpio sixfab &> $VERBOSE_SUFFIX ;; *Ubuntu*|*ubuntu*) add_gpio_permissions_for_ubuntu ;; esac # common usermods sudo usermod -aG sudo sixfab &> $VERBOSE_SUFFIX sudo usermod -aG dialout sixfab &> $VERBOSE_SUFFIX sudo usermod -aG users sixfab &> $VERBOSE_SUFFIX sudo usermod -aG plugdev sixfab &> $VERBOSE_SUFFIX } if id -u "sixfab" &> $VERBOSE_SUFFIX; then print_info "Sixfab user already exists, updating..." check_sixfab_user_privilege else print_info "Creating sixfab user..." create_sixfab_user check_sixfab_user_privilege fi } initialize_sudoers() { print_info "Updating sudoers..." echo "sixfab ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/sixfab_core &> $VERBOSE_SUFFIX print_info "Sudoers updated" } check_system_dependencies() { git --version &> $VERBOSE_SUFFIX IS_GIT_INSTALLED=$? python3 --version &> $VERBOSE_SUFFIX IS_PYTHON_INSTALLED=$? pip3 --version &> $VERBOSE_SUFFIX IS_PIP_INSTALLED=$? if [ ! "$IS_GIT_INSTALLED" = "0" ] || [ ! "$IS_PYTHON_INSTALLED" = "0" ] || [ ! "$IS_PIP_INSTALLED" = "0" ]; then install_system_dependencies fi } install_system_dependencies() { print_info "Looking for dependencies..." # Check if git installed if ! [ -x "$(command -v git)" ]; then print_info 'Git is not installed, installing...' run_command "sudo apt install git -y" fi # Check if python3 installed if ! [ -x "$(command -v python3)" ]; then print_info 'Python3 is not installed, installing...' run_command "sudo apt install python3 -y" fi # Check python3 version, minimum python3.6 required version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)' | sed -e 's/\.//g') if [ "$version" -lt "360" ]; then print_error "Python 3.6 or later version is required to run Sixfab Core. Please upgrade Python and re-try. Using latest version of Raspberry Pi OS (Previously known as Raspbian OS) is recommended." exit fi # Check if pip3 installed if ! [ -x "$(command -v pip3)" ]; then print_info 'Pip for python3 is not installed, installing...' run_command "sudo apt install python3-pip -y" fi # Check if ifmetric installed if ! [ -x "$(command -v ifmetric)" ]; then print_info 'ifmetric is not installed, installing...' run_command "sudo apt install ifmetric -y" fi # Check if net-tools installed if ! [ -x "$(command -v route)" ]; then print_info 'net-tools is not installed, installing...' run_command "sudo apt install net-tools -y" fi # Check if lshw if ! [ -x "$(command -v lshw)" ]; then print_info 'lshw is not installed, installing...' run_command "sudo apt install lshw -y" fi check_system_dependencies } remove_conflicting_packages() { # Check if modemmanager installed if [ -x "$(command -v ModemManager)" ]; then print_info 'Removing ModemManager...' run_command "sudo apt purge modemmanager -y" fi } check_network_layer_dependencies() { # Configuration for compatibility of systemd-networkd service # on ubuntu-server with raspberry pi hardware configure_systemd_networkd_for_ubuntu(){ NETWORKD_CONF_PATH="/etc/systemd/network/10-allifs.network" echo "[Match] Name=* [Network] DHCP=true" | sudo tee $NETWORKD_CONF_PATH &> $VERBOSE_SUFFIX sudo systemctl restart systemd-networkd.service } case "$BOARD" in *Raspberry*) case "$OS_DISTRO" in *Raspbian*) print_info "Board: Raspberry Pi - OS: Raspbian" ;; *Ubuntu*) print_info "Board: Raspberry Pi - OS: Ubuntu" configure_systemd_networkd_for_ubuntu ;; esac ;; *Jetson*) print_info "Board: Nvidia Jetson Nano - OS: Ubuntu" ;; esac } install_atcom() { print_info "Installing Sixfab ATCom tool..." pip3 show atcom &> $VERBOSE_SUFFIX && pip3 uninstall atcom -y &> $VERBOSE_SUFFIX run_command "sudo pip3 install --no-cache-dir -U atcom" if atcom --help &> $VERBOSE_SUFFIX; then print_info "Sixfab ATCom installed" else print_error "Couldn't install Sixfab ATCom, please retry" exit 1 fi } install_agent() { if [ -d "/opt/sixfab/core/agent" ]; then print_info "Agent source already exists, updating..." git -C /opt/sixfab/core/agent reset --hard HEAD &> $VERBOSE_SUFFIX git -C /opt/sixfab/core/agent pull &> $VERBOSE_SUFFIX else print_info "Downloading agent source..." git clone $AGENT_REPOSITORY /opt/sixfab/core/agent &> $VERBOSE_SUFFIX fi print_info "Installing agent dependencies..." run_command "pip3 install -r /opt/sixfab/core/agent/requirements.txt --no-cache-dir" print_info "Installed agent dependencies." print_info "Initializing agent service..." echo "[Unit] Description=Sixfab Core - Agent [Service] User=sixfab Group=sudo Restart=always RestartSec=3 ExecStart=/usr/bin/python3 /opt/sixfab/core/agent/run.py [Install] WantedBy=multi-user.target" | sudo tee /etc/systemd/system/core_agent.service &> $VERBOSE_SUFFIX sudo chown sixfab /etc/systemd/system/core_agent.service &> $VERBOSE_SUFFIX sudo systemctl daemon-reload &> $VERBOSE_SUFFIX sudo systemctl enable core_agent &> $VERBOSE_SUFFIX restart_sixfab_services "agent" print_info "Agent service initialized successfully." } install_manager() { if [ -d "/opt/sixfab/core/manager" ]; then print_info "Manager source already exists, updating..." git -C /opt/sixfab/core/manager reset --hard HEAD &> $VERBOSE_SUFFIX git -C /opt/sixfab/core/manager pull &> $VERBOSE_SUFFIX else print_info "Downloading manager source..." git clone $MANAGER_REPOSITORY /opt/sixfab/core/manager &> $VERBOSE_SUFFIX fi print_info "Installing manager dependencies..." run_command "pip3 install -r /opt/sixfab/core/manager/requirements.txt --no-cache-dir" print_info "Installed manager dependencies." print_info "Initializing manager service..." echo "[Unit] Description=Sixfab Core Manager [Service] User=sixfab Restart=always Group=sudo RestartSec=3 WorkingDirectory=/opt/sixfab/core/manager/core_manager ExecStart=/usr/bin/python3 run.py [Install] WantedBy=multi-user.target" | sudo tee /etc/systemd/system/core_manager.service &> $VERBOSE_SUFFIX sudo chown sixfab /etc/systemd/system/core_manager.service &> $VERBOSE_SUFFIX sudo systemctl daemon-reload &> $VERBOSE_SUFFIX sudo systemctl enable core_manager &> $VERBOSE_SUFFIX restart_sixfab_services "manager" print_info "Manager service initialized successfully." } check_sixfab_folder() { if [ ! -d "/opt/sixfab" ]; then sudo mkdir /opt/sixfab fi if [ ! -d "/opt/sixfab/core" ]; then sudo mkdir /opt/sixfab/core fi sudo chown sixfab /opt/sixfab sudo chown sixfab /opt/sixfab/core } initialize_environment_file() { print_info "Initializing environment file..." pip3 install pyyaml --no-cache-dir&> $VERBOSE_SUFFIX python3 -c " import yaml region = '$REGION' board = '$BOARD' is_dev = $IS_DEV try: with open('/opt/sixfab/.env.yaml', 'r') as env_file: environments = yaml.safe_load(env_file) except: environments = {} if not 'core' in environments: environments['core'] = {} elif not is_dev and 'MQTT_HOST' in environments['core']: del environments['core']['MQTT_HOST'] if '$TOKEN': environments['core']['token'] = '$TOKEN' if '$B_TOKEN': environments['core']['b_token'] = '$B_TOKEN' if is_dev: environments['core']['MQTT_HOST'] = 'mqtt.dev.connect.sixfab.com' if region == 'global' and environments['core'].get('apn', False): environments['core'].pop('apn', False) elif region == 'emea': environments['core']['apn'] = 'de1.super' elif region == 'apac': environments['core']['apn'] = 'sg1.super' environments['core']['board'] = board with open('/opt/sixfab/.env.yaml', 'w') as env_file: yaml.dump(environments, env_file) " print_info "Initialized environment file" } apply_dev_mode_if_necessary() { if [ "$IS_DEV" == True ]; then # Managers cd /opt/sixfab/core/manager/ &> $VERBOSE_SUFFIX sudo git reset --hard HEAD &> $VERBOSE_SUFFIX sudo git checkout dev &> $VERBOSE_SUFFIX restart_sixfab_services "manager" # Agent cd /opt/sixfab/core/agent/ &> $VERBOSE_SUFFIX sudo git reset --hard HEAD &> $VERBOSE_SUFFIX sudo git checkout dev &> $VERBOSE_SUFFIX restart_sixfab_services "agent" fi } restart_sixfab_services(){ if [ $deployment_type == "bulk" ]; then if [ $1 == "agent" ]; then sudo systemctl stop core_agent &> $VERBOSE_SUFFIX elif [ $1 == "manager" ]; then sudo systemctl stop core_manager &> $VERBOSE_SUFFIX fi else if [ $1 == "agent" ]; then sudo systemctl restart core_agent &> $VERBOSE_SUFFIX elif [ $1 == "manager" ]; then sudo systemctl restart core_manager &> $VERBOSE_SUFFIX fi fi } reboot_system() { GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[0;33m' NC='\033[0m' remove_conflicting_packages if [ $deployment_type == "bulk" ]; then printf "\n" printf -- "-----------------------------------------------------------------------\n" printf "Bulk deployment image is ready. You can use this image to deploy multiple CORE devices\n" printf "Follow instructions at the link below to copy os image and create multiple SD cards\n" printf "${YELLOW}Link:${NC} https://docs.sixfab.com/page/bulk-deployments#cloning-the-sd-card" printf "\n" printf "Press ${YELLOW}ENTER${NC} to power off your system as the first step.\n" printf "Press ${YELLOW}Ctrl+C${NC} (^C) to finish installation without power off.\n" printf -- "-----------------------------------------------------------------------\n" printf "\n" read -r -p "" foo sudo poweroff else printf "\n" printf -- "-----------------------------------------------------------------------\n" printf "Press ${YELLOW}ENTER${NC} to reboot your system. (${GREEN}Recommended${NC})\n" printf "Press ${YELLOW}Ctrl+C${NC} (^C) to finish installation without reboot.\n" printf "\n" printf "${GREEN}Reminder${NC}: Plug the USB cable to Sixfab HAT!\n" printf "${RED}Warning${NC}: Network priority settings will be effective after reboot!\n" printf -- "-----------------------------------------------------------------------\n" printf "\n" read -r -p "" foo sudo reboot fi } ### Progress Bar ### # Revised by selengalp (yasinkaya.121@gmail.com) on 28/11/2021 # Main Source: https://github.com/pollev/bash_progress_bar # Constants CODE_SAVE_CURSOR="\033[s" CODE_RESTORE_CURSOR="\033[u" CODE_CURSOR_IN_SCROLL_AREA="\033[1A" COLOR_FG="\e[32m" COLOR_BG="\e[47m" COLOR_BG_BLOCKED="\e[43m" RESTORE_FG="\e[39m" RESTORE_BG="\e[49m" # Variables PROGRESS_BLOCKED="false" TRAPPING_ENABLED="false" TRAP_SET="false" CURRENT_NR_LINES=0 setup_scroll_area() { # If trapping is enabled, we will want to activate it whenever # we setup the scroll area and remove it when we break the scroll area if [ "$TRAPPING_ENABLED" = "true" ]; then trap_on_interrupt fi lines=$(tput lines) CURRENT_NR_LINES=$lines let lines=$lines-1 # Scroll down a bit to avoid visual glitch when the screen area shrinks by one row echo -en "\n" # Save cursor echo -en "$CODE_SAVE_CURSOR" # Set scroll region (this will place the cursor in the top left) echo -en "\033[0;${lines}r" # Restore cursor but ensure its inside the scrolling area echo -en "$CODE_RESTORE_CURSOR" echo -en "$CODE_CURSOR_IN_SCROLL_AREA" # Start empty progress bar draw_progress_bar 0 } destroy_scroll_area() { lines=$(tput lines) # Save cursor echo -en "$CODE_SAVE_CURSOR" # Set scroll region (this will place the cursor in the top left) echo -en "\033[0;${lines}r" # Restore cursor but ensure its inside the scrolling area echo -en "$CODE_RESTORE_CURSOR" echo -en "$CODE_CURSOR_IN_SCROLL_AREA" # We are done so clear the scroll bar clear_progress_bar # Scroll down a bit to avoid visual glitch when the screen area grows by one row echo -en "\n\n" # Once the scroll area is cleared, we want to remove any trap previously set. Otherwise, ctrl+c will exit our shell if [ "$TRAP_SET" = "true" ]; then trap - INT fi } draw_progress_bar() { percentage=$1 lines=$(tput lines) let lines=$lines # Check if the window has been resized. If so, reset the scroll area if [ "$lines" -ne "$CURRENT_NR_LINES" ]; then setup_scroll_area fi # Save cursor echo -en "$CODE_SAVE_CURSOR" # Move cursor position to last row echo -en "\033[${lines};0f" # Clear progress bar tput el # Draw progress bar PROGRESS_BLOCKED="false" print_bar_text $percentage # Restore cursor position echo -en "$CODE_RESTORE_CURSOR" } block_progress_bar() { percentage=$1 lines=$(tput lines) let lines=$lines # Save cursor echo -en "$CODE_SAVE_CURSOR" # Move cursor position to last row echo -en "\033[${lines};0f" # Clear progress bar tput el # Draw progress bar PROGRESS_BLOCKED="true" print_bar_text $percentage # Restore cursor position echo -en "$CODE_RESTORE_CURSOR" } clear_progress_bar() { lines=$(tput lines) let lines=$lines # Save cursor echo -en "$CODE_SAVE_CURSOR" # Move cursor position to last row echo -en "\033[${lines};0f" # clear progress bar tput el # Restore cursor position echo -en "$CODE_RESTORE_CURSOR" } print_bar_text() { local percentage=$1 local cols=$(tput cols) let bar_size=$cols-30 local color="${COLOR_FG}${COLOR_BG}" if [ "$PROGRESS_BLOCKED" = "true" ]; then color="${COLOR_FG}${COLOR_BG_BLOCKED}" fi # Prepare progress bar let complete_size=($bar_size*$percentage)/100 let remainder_size=$bar_size-$complete_size progress_bar=$(echo -ne ""; echo -en "${color}"; printf_new "█" $complete_size; echo -en "${RESTORE_FG}${RESTORE_BG}"; printf_new "" $remainder_size; echo -ne ""); # Print progress bar echo -ne "Total Progress-> ${progress_bar} ${percentage}%" } enable_trapping() { TRAPPING_ENABLED="true" } trap_on_interrupt() { # If this function is called, we setup an interrupt handler to cleanup the progress bar TRAP_SET="true" trap cleanup_on_interrupt INT } cleanup_on_interrupt() { destroy_scroll_area exit } printf_new() { str=$1 num=$2 v=$(printf "%-${num}s" "$str") echo -ne "${v// /$str}" } ### End of progress bar ### main() { enable_trapping setup_scroll_area initialize_parameters "$@" check_is_root check_distro check_usb_is_connected draw_progress_bar 5 check_user draw_progress_bar 10 initialize_sudoers draw_progress_bar 20 update_system draw_progress_bar 30 install_system_dependencies draw_progress_bar 40 check_network_layer_dependencies draw_progress_bar 50 check_system_dependencies draw_progress_bar 60 install_atcom draw_progress_bar 70 check_sixfab_folder initialize_environment_file draw_progress_bar 80 install_agent draw_progress_bar 90 install_manager draw_progress_bar 95 apply_dev_mode_if_necessary draw_progress_bar 100 print_done "Installation completed successfully." reboot_system destroy_scroll_area } main "$@"