#!/bin/bash # blow up on non-zero exit code set -e # these are replaced by build.sh PACKAGE_NAME=RoonServer ARCH=x64 PACKAGE_URL=https://download.roonlabs.net/builds/RoonServer_linuxx64.tar.bz2 PACKAGE_FILE=${PACKAGE_NAME}_linux${ARCH}.tar.bz2 PACKAGE_NAME_LOWER=`echo "$PACKAGE_NAME" | tr "[A-Z]" "[a-z]"` TMPDIR=`mktemp -d` MACHINE_ARCH=`uname -m` OK=0 CLEAN_EXIT=0 # Auto-confirm mode (for curl | bash usage) AUTO_CONFIRM=0 # for colorization ESC_SEQ="\033[" COL_RESET=$ESC_SEQ"39;49;00m" COL_RED=$ESC_SEQ"31;01m" COL_GREEN=$ESC_SEQ"32;01m" COL_YELLOW=$ESC_SEQ"33;01m" COL_BLUE=$ESC_SEQ"34;01m" COL_MAGENTA=$ESC_SEQ"35;01m" COL_CYAN=$ESC_SEQ"36;01m" COL_BOLD=$ESC_SEQ"1m" function hr { echo -e "${COL_BOLD}--------------------------------------------------------------------------------------${COL_RESET}" } function clean_up { rm -Rf $TMPDIR if [ x$CLEAN_EXIT != x1 ]; then echo "" hr echo "" echo -e "${COL_BOLD}${COL_RED}The $PACKAGE_NAME installer did not complete successfully.${COL_RESET}" echo "" echo "If you are not sure how to proceed, please check out:" echo "" echo " - Roon Labs Community https://community.roonlabs.com/c/support" echo " - Roon Labs Knowledge Base https://kb.roonlabs.com/LinuxInstall" echo "" hr echo "" fi } trap clean_up EXIT function install { # # Print banner/message # echo "" hr echo "" echo -e "${COL_BOLD}Welcome to the $PACKAGE_NAME installer${COL_RESET}" echo "" echo "This installer sets up $PACKAGE_NAME to run on linux with the following settings:" echo "" echo " - $PACKAGE_NAME will be installed in /opt/$PACKAGE_NAME" echo " - $PACKAGE_NAME's data will be stored in /DATA/AppData/roon/$PACKAGE_NAME" echo " - $PACKAGE_NAME will be configured to run as a system service" echo " - $PACKAGE_NAME will run as root" echo "" echo "These settings are suitable for turning a dedicated or semi-dedicated device" echo "into an appliance that runs $PACKAGE_NAME" echo "" echo "If you want customize how $PACKAGE_NAME is installed, see:" echo "" echo " http://kb.roonlabs.com/LinuxInstall" echo "" hr echo "" # # Check for linux (in case someone runs on OS X, Cygwin, BSD, etc) # case `uname -s` in Linux) ;; *) echo -e "${COL_RED}${COL_BLOLD}Error:${COL_RESET} This package is intended for Linux platforms. It is not compatible with your machine. Exiting." ;; esac # # Check for proper architecture # case "$MACHINE_ARCH" in armv7*) if [ x$ARCH = xarmv7hf ]; then OK=1; fi ;; aarch64*) if [ x$ARCH = xarmv8 ]; then OK=1; fi if [ x$ARCH = xarmv7hf ]; then OK=1; fi ;; x86_64*) if [ x$ARCH = xx64 ]; then OK=1; fi ;; i686*) if [ x$ARCH = xx86 ]; then OK=1; fi ;; esac # # Check for root privileges # if [ x$UID != x0 ]; then echo "" echo -e "${COL_RED}${COL_BLOLD}Error:${COL_RESET} This installer must be run with root privileges. Exiting." echo "" exit 2 fi # # Check for ffmpeg/avconv # if [ x$OK != x1 ]; then echo "" echo -e "${COL_RED}${COL_BLOLD}Error:${COL_RESET} This package is intended for $ARCH platforms. It is not compatible with your machine. Exiting." echo "" exit 3 fi function confirm_n { # Auto-confirm mode: default to No (exit) if [ $AUTO_CONFIRM = 1 ]; then CLEAN_EXIT=1 echo "" echo "Auto mode: defaulting to No. Exiting." echo "" exit 4 fi while true; do read -p "$1 [y/N] " yn /dev/null; WHICH_WGET=$? set -e if [ $WHICH_WGET = 0 ]; then wget --show-progress -O "$TMPDIR/$PACKAGE_FILE" "$PACKAGE_URL" else curl -L -# -o "$TMPDIR/$PACKAGE_FILE" "$PACKAGE_URL" fi echo "" echo -n "Unpacking ${PACKAGE_FILE}..." cd $TMPDIR tar xf "$PACKAGE_FILE" echo "Done" if [ ! -d "$TMPDIR/$PACKAGE_NAME" ]; then echo "Missing directory: $TMPDIR/$PACKAGE_NAME. This indicates a broken package." exit 5 fi if [ ! -f "$TMPDIR/$PACKAGE_NAME/check.sh" ]; then echo "Missing $TMPDIR/$PACKAGE_NAME/check.sh. This indicates a broken package." exit 5 fi $TMPDIR/$PACKAGE_NAME/check.sh if [ -e /opt/$PACKAGE_NAME ]; then hr echo "" echo -e "${COL_RED}${COL_BOLD}Warning:${COL_RESET} The /opt/$PACKAGE_NAME directory already exists." echo "" echo "This usually indicates that $PACKAGE_NAME was installed previously on this machine. The previous" echo "installation must be deleted before the installation can proceed." echo "" echo "Under normal circumstances, this directory does not contain any user data, so it should be safe to delete it." echo "" hr echo "" confirm "Delete /opt/$PACKAGE_NAME and re-install?" rm -Rf /opt/$PACKAGE_NAME fi echo "" echo -n "Copying Files..." mv "$TMPDIR/$PACKAGE_NAME" /opt echo "Done" # set up systemd HAS_SYSTEMCTL=1; which systemctl >/dev/null || HAS_SYSTEMCTL=0 if [ $HAS_SYSTEMCTL = 1 -a -d /etc/systemd/system ]; then SERVICE_FILE=/etc/systemd/system/${PACKAGE_NAME_LOWER}.service # stop in case it's running from an old install systemctl stop $PACKAGE_NAME_LOWER || true echo "" echo "Installing $SERVICE_FILE" cat > $SERVICE_FILE << END_SYSTEMD [Unit] Description=$PACKAGE_NAME After=network-online.target [Service] Type=simple User=root Environment=ROON_DATAROOT=/DATA/AppData/roon Environment=ROON_ID_DIR=/DATA/AppData/roon ExecStart=/opt/$PACKAGE_NAME/start.sh Restart=on-abort [Install] WantedBy=multi-user.target END_SYSTEMD echo "" echo "Enabling service ${PACKAGE_NAME_LOWER}..." systemctl enable ${PACKAGE_NAME_LOWER}.service echo "Service Enabled" echo "" echo "Starting service ${PACKAGE_NAME_LOWER}..." systemctl start ${PACKAGE_NAME_LOWER}.service echo "Service Started" else echo "" SERVICE_FILE=/etc/init.d/${PACKAGE_NAME_LOWER} /etc/init.d/$PACKAGE_NAME_LOWER stop || true cat > $SERVICE_FILE << END_LSB_INIT #!/bin/sh ### BEGIN INIT INFO # Provides: ${PACKAGE_NAME_LOWER} # Required-Start: \$network # Required-Stop: \$network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Runs ${PACKAGE_NAME} ### END INIT INFO # Defaults DAEMON_NAME="$PACKAGE_NAME" DAEMON_EXECUTABLE="/opt/$PACKAGE_NAME/start.sh" DAEMON_OPTIONS="" DAEMON_HOMEDIR="/opt/$PACKAGE_NAME" DAEMON_PIDFILE="/var/run/${PACKAGE_NAME_LOWER}.pid" DAEMON_LOGFILE="/var/log/${PACKAGE_NAME_LOWER}.log" INIT_SLEEPTIME="2" export ROON_DATAROOT=/DATA/AppData/roon export ROON_ID_DIR=/DATA/AppData/roon PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin if test -f /lib/lsb/init-functions; then . /lib/lsb/init-functions fi if test -f /etc/init.d/functions; then . /etc/init.d/functions fi ### DO NOT EDIT BELOW THIS POINT ### is_running () { # Test whether pid file exists or not test -f \$DAEMON_PIDFILE || return 1 # Test whether process is running or not read PID < "\$DAEMON_PIDFILE" ps -p \$PID >/dev/null 2>&1 || return 1 # Is running return 0 } root_only () { if [ "\$(id -u)" != "0" ]; then echo "Only root should run this operation" exit 1 fi } run () { if is_running; then PID="\$(cat \$DAEMON_PIDFILE)" echo "Daemon is already running as PID \$PID" return 1 fi cd \$DAEMON_HOMEDIR nohup \$DAEMON_EXECUTABLE \$DAEMON_OPTIONS >>\$DAEMON_LOGFILE 2>&1 & echo \$! > \$DAEMON_PIDFILE read PID < "\$DAEMON_PIDFILE" sleep \$INIT_SLEEPTIME if ! is_running; then echo "Daemon died immediately after starting. Please check your logs and configurations." return 1 fi echo "Daemon is running as PID \$PID" return 0 } stop () { if is_running; then read PID < "\$DAEMON_PIDFILE" kill \$PID fi sleep \$INIT_SLEEPTIME if is_running; then while is_running; do echo "waiting for daemon to die (PID \$PID)" sleep \$INIT_SLEEPTIME done fi rm -f "\$DAEMON_PIDFILE" return 0 } case "\$1" in start) root_only log_daemon_msg "Starting \$DAEMON_NAME" run log_end_msg \$? ;; stop) root_only log_daemon_msg "Stopping \$DAEMON_NAME" stop log_end_msg \$? ;; restart) root_only \$0 stop && \$0 start ;; status) status_of_proc \ -p "\$DAEMON_PIDFILE" \ "\$DAEMON_EXECUTABLE" \ "\$DAEMON_NAME" \ && exit 0 \ || exit \$? ;; *) echo "Usage: \$0 {start|stop|restart|status}" exit 1 ;; esac END_LSB_INIT echo "wrote out file" chmod +x ${SERVICE_FILE} HAS_UPDATE_RC_D=1; which update-rc.d >/dev/null || HAS_UPDATE_RC_D=0 HAS_CHKCONFIG=1; which chkconfig >/dev/null || HAS_CHKCONFIG=0 if [ $HAS_UPDATE_RC_D = 1 ]; then echo "" echo "Enabling service ${PACKAGE_NAME_LOWER} using update-rc.d..." update-rc.d ${PACKAGE_NAME_LOWER} defaults echo "Service Enabled" elif [ $HAS_CHKCONFIG = 1 ]; then echo "" echo "Enabling service ${PACKAGE_NAME_LOWER} using chkconfig..." chkconfig --add ${PACKAGE_NAME_LOWER} echo "Service Enabled" else echo "Couldn't find a way to enable the init script" exit 1 fi echo "" echo "Starting service ${PACKAGE_NAME_LOWER}..." $SERVICE_FILE stop >/dev/null 2>&1 || true $SERVICE_FILE start echo "Service Started" echo "Setting up $PACKAGE_NAME to run at boot using LSB scripts" fi CLEAN_EXIT=1 echo "" hr echo "" echo "All Done! $PACKAGE_NAME should be running on your machine now". echo "" hr echo "" } function uninstall { # # Print banner/message # echo "" hr echo "" echo -e "${COL_BOLD}Welcome to the $PACKAGE_NAME uninstaller${COL_RESET}" echo "" echo "This removes $PACKAGE_NAME from your machine by doing the following:" echo "" echo " - deleting all files in /opt/$PACKAGE_NAME" echo " - removing $PACKAGE_NAME as a system service" echo "" echo "This uninstaller is only for systems that were installed using this installer script." echo "If you performed a custom install by hand, this is not for you." echo "" echo " http://kb.roonlabs.com/LinuxInstall" echo "" hr echo "" # # Check for linux (in case someone runs on OS X, Cygwin, BSD, etc) # case `uname -s` in Linux) ;; *) echo -e "${COL_RED}${COL_BLOLD}Error:${COL_RESET} This package is intended for Linux platforms. It is not compatible with your machine. Exiting." ;; esac # # Check for proper architecture # case "$MACHINE_ARCH" in armv7*) if [ x$ARCH = xarmv7hf ]; then OK=1; fi ;; aarch64*) if [ x$ARCH = xarmv8 ]; then OK=1; fi if [ x$ARCH = xarmv7hf ]; then OK=1; fi ;; x86_64*) if [ x$ARCH = xx64 ]; then OK=1; fi ;; i686*) if [ x$ARCH = xx86 ]; then OK=1; fi ;; esac # # Check for root privileges # if [ x$UID != x0 ]; then echo "" echo -e "${COL_RED}${COL_BLOLD}Error:${COL_RESET} This installer must be run with root privileges. Exiting." echo "" exit 2 fi if [ x$OK != x1 ]; then echo "" echo -e "${COL_RED}${COL_BLOLD}Error:${COL_RESET} This package is intended for $ARCH platforms. It is not compatible with your machine. Exiting." echo "" exit 3 fi function confirm_n { # Auto-confirm mode: default to No (exit) if [ $AUTO_CONFIRM = 1 ]; then CLEAN_EXIT=1 echo "" echo "Auto mode: defaulting to No. Exiting." echo "" exit 4 fi while true; do read -p "$1 [y/N] " yn /dev/null || HAS_SYSTEMCTL=0 if [ $HAS_SYSTEMCTL = 1 -a -d /etc/systemd/system ]; then SERVICE_FILE=/etc/systemd/system/${PACKAGE_NAME_LOWER}.service echo "" echo "Stopping service $PACKAGE_NAME_LOWER" systemctl stop $PACKAGE_NAME_LOWER || true echo "Service Stopped" echo "" echo "Disabling service ${PACKAGE_NAME_LOWER}..." systemctl disable ${PACKAGE_NAME_LOWER}.service || true echo "Service Disabled" echo "" echo "Removing service file $SERVICE_FILE" rm -f $SERVICE_FILE else SERVICE_FILE=/etc/init.d/${PACKAGE_NAME_LOWER} echo "" echo "Stopping service ${PACKAGE_NAME_LOWER}..." $SERVICE_FILE stop >/dev/null 2>&1 || true echo "Service Stopped" echo "" echo "Removing service ${PACKAGE_NAME_LOWER}..." if [ $HAS_UPDATE_RC_D = 1 ]; then echo "" echo "Disabling service ${PACKAGE_NAME_LOWER} using update-rc.d..." update-rc.d ${PACKAGE_NAME_LOWER} remove echo "Service Disabled" elif [ $HAS_CHKCONFIG = 1 ]; then echo "" echo "Disabling service ${PACKAGE_NAME_LOWER} using chkconfig..." chkconfig --del ${PACKAGE_NAME_LOWER} echo "Service Disabled" else echo "Couldn't find a way to disable the init script" exit 0 fi echo "Service Removed" echo "" echo "Removing service file $SERVICE_FILE" rm -f $SERVICE_FILE fi echo "" echo -n "Deleting all files in /opt/$PACKAGE_NAME" rm -Rf /opt/$PACKAGE_NAME CLEAN_EXIT=1 echo "" hr echo "" echo "All Done! $PACKAGE_NAME should be uninstalled." echo "" hr echo "" } # Parse command line arguments ACTION="install" for arg in "$@"; do case $arg in -y|--yes) AUTO_CONFIRM=1 ;; uninstall) ACTION="uninstall" ;; install) ACTION="install" ;; -h|--help) echo "Usage: $0 [install|uninstall] [-y|--yes]" echo "" echo "Options:" echo " install Install $PACKAGE_NAME (default)" echo " uninstall Uninstall $PACKAGE_NAME" echo " -y, --yes Auto-confirm all prompts (for non-interactive install)" echo "" echo "Examples:" echo " # Interactive install" echo " sudo bash $0" echo "" echo " # Non-interactive install via curl" echo " curl -fsSL | sudo bash -s -- -y" echo "" exit 0 ;; esac done if [ "$ACTION" == "uninstall" ]; then uninstall else install fi