Skip to content

Instantly share code, notes, and snippets.

@ThomasTJdev
Created April 19, 2019 14:29
Show Gist options
  • Save ThomasTJdev/898e9de422d5a1c4f33f9b546b46e6b0 to your computer and use it in GitHub Desktop.
Save ThomasTJdev/898e9de422d5a1c4f33f9b546b46e6b0 to your computer and use it in GitHub Desktop.
NimHA install script
#!/usr/bin/env bash
# INSPIRED BY PIHOLE. PLEASE SEE PIHOLE LICENSE FOR USE.
# Pi-hole: A black hole for Internet advertisements
# (c) 2017-2018 Pi-hole, LLC (https://pi-hole.net)
#
# PIHOLE FILE CAN BE FOUND AT https://install.pi-hole.net
#
# THIS FILE IS NOT PUBLIC DOMAIN AND MAY NOT BE USED.
# THIS IS A PRIVATE DEVELOPMENT FILE.
# NimHA automated installer
# -e option instructs bash to immediately exit if any command [1] has a non-zero exit status
# We do not want users to end up with a partially working install, so we exit the script
# instead of continuing the installation with something broken
set -e
# Location for final installation log storage
installLoc=/var/tmp/nimha
configFile=/etc/nimha/nimha.cfg
CFG_SYSTEM_USERNAME=nimha
CFG_SYSTEM_USERGROUP=nimha
# Git url
gitRepoUrl="https://github.com/ThomasTJdev/nim_homeassistant.git"
# Colors
COL_NC='\e[0m' # No Color
COL_LIGHT_GREEN='\e[1;32m'
COL_LIGHT_RED='\e[1;31m'
COL_LIGHT_YELLOW='\e[1;33m'
COL_LIGHT_BLUE='\e[1;34m'
TICK="[${COL_LIGHT_GREEN}✓${COL_NC}]"
CROSS="[${COL_LIGHT_RED}✗${COL_NC}]"
INFO="[i]"
# shellcheck disable=SC2034
DONE="${COL_LIGHT_GREEN} done!${COL_NC}"
OVER="\\r\\033[K"
# Params
CFG_SYSTEMCTL=false
CFG_SYSTEMCTLNAME="nimha"
CFG_SYMBOLIC=false
CFG_USERNAME="admin"
CFG_EMAIL="admin@admin.com"
CFG_PASS="nimhaadmin"
CFG_APPNAME="nimha_main"
# BinBash
# Find the rows and columns will default to 80x24 if it can not be detected
screen_size=$(stty size || printf '%d %d' 24 80)
# Set rows variable to contain first number
printf -v rows '%d' "${screen_size%% *}"
# Set columns variable to contain second number
printf -v columns '%d' "${screen_size##* }"
# Divide by two so the dialogs take up half of the screen, which looks nice.
r=$(( rows / 2 ))
c=$(( columns / 2 ))
# Unless the screen is tiny
r=$(( r < 20 ? 20 : r ))
c=$(( c < 70 ? 70 : c ))
#
# NIMHA
#
show_ascii_logo() {
echo -e "${COL_LIGHT_BLUE}
_ ___ __ _____
/ | / (_)___ ___ / / / / |
/ |/ / / __ '__ \/ /_/ / /| |
/ /| / / / / / / / __ / ___ |
/_/ |_/_/_/ /_/ /_/_/ /_/_/ |_|
${COL_NC}"
}
#
# USERS
#
setup_user() {
printf " %b %s\\n" "${INFO}" "Adding NimHA user"
# force: This option causes the command to exit with success status if the specified group already exists.
groupadd --system --force ${CFG_SYSTEM_USERGROUP} || return 1
useradd --system -M -N --gid ${CFG_SYSTEM_USERGROUP} --home /var/lib/${CFG_SYSTEM_USERNAME} ${CFG_SYSTEM_USERNAME} &> /dev/null || printf " %b %s\\n" "${TICK}" "User already exists"
printf " %b %s\\n" "${TICK}" "User created"
}
#
# FOLDERS
#
setup_folder() {
local directory="${1}"
if [[ -d "${directory}" ]]; then
printf " %b %s\\n" "${TICK}" "Directory exists: ${directory}"
else
printf " %b %s\\n" "${INFO}" "Directory does not exists. Creating ${directory}"
mkdir -p "${directory}" || return 1
printf " %b %s\\n" "${TICK}" "Directory created"
fi
}
setup_folder_permission() {
local directory="${1}"
local user="${2}"
local group="${3}"
printf " %b %s\\n" "${INFO}" "Setting folder permissions"
chown -R ${user}:${group} /var/lib/nimha || return 1
printf " %b %s\\n" "${TICK}" "Folder permission set to ${user}:${group}"
}
#
# CHECK REQUIRED
#
is_command() {
local check_command="$1"
command -v "${check_command}" >/dev/null 2>&1
}
check_req() {
if is_command ${1} ; then
local str="${1} is installed"
printf " %b %s\\n" "${TICK}" "${str}"
else
local str="${1} is requied"
printf " %b %s\\n" "${CROSS}" "${str}"
exit 1
fi
}
#
# GIT REPO
#
is_repo() {
local directory="${1}"
local curdir
local rc
curdir="${PWD}"
# If the first argument passed to this function is a directory,
if [[ -d "${directory}" ]]; then
# move into the directory
cd "${directory}"
# Use git to check if the directory is a repo
# git -C is not used here to support git versions older than 1.8.4
git status --short &> /dev/null || rc=$?
# If the command was not successful,
else
# Set a non-zero return code if directory does not exist
rc=1
fi
# Move back into the directory the user started in
cd "${curdir}"
# Return the code; if one is not set, return 0
return "${rc:-0}"
}
make_repo() {
# Set named variables for better readability
local directory="${1}"
local remoteRepo="${2}"
# The message to display when this function is running
str="Clone ${remoteRepo} into ${directory}"
# Display the message and use the color table to preface the message with an "info" indicator
printf " %b %s..." "${INFO}" "${str}"
# If the directory exists,
if [[ -d "${directory}" ]]; then
# delete everything in it so git can clone into it
rm -rf "${directory}"
fi
# Clone the repo and return the return code from this command
git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null || return $?
# Show a colored message showing it's status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Always return 0? Not sure this is correct
return 0
}
update_repo() {
local directory="${1}"
local curdir
local str="Update repo in ${1}"
curdir="${PWD}"
# Move into the directory that was passed as an argument
cd "${directory}" &> /dev/null || return 1
printf " %b %s..." "${INFO}" "${str}"
# Stash any local commits as they conflict with our working code
git stash --all --quiet &> /dev/null || true # Okay for stash failure
git clean --quiet --force -d || true # Okay for already clean directory
git pull --quiet &> /dev/null || return $?
# Show a completion message
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Move back into the original directory
cd "${curdir}" &> /dev/null || return 1
return 0
}
# A function that combines the functions previously made
getGitFiles() {
local directory="${1}"
local remoteRepo="${2}"
local str="Check for existing repository in ${1}"
printf " %b %s..." "${INFO}" "${str}"
# Check if the directory is a repository
if is_repo "${directory}"; then
# Show that we're checking it
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Update the repo, returning an error message on failure
update_repo "${directory}" || { printf "\\n %b: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
# If it's not a .git repo,
else
# Show an error
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
# Attempt to make the repository, showing an error on failure
make_repo "${directory}" "${remoteRepo}" || { printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
fi
echo ""
return 0
}
# Reset a repo to get rid of any local changed
resetRepo() {
# Use named variables for arguments
local directory="${1}"
# Move into the directory
cd "${directory}" &> /dev/null || return 1
# Store the message in a variable
str="Resetting repository within ${1}..."
# Show the message
printf " %b %s..." "${INFO}" "${str}"
# Use git to remove the local changes
git reset --hard &> /dev/null || return $?
# And show the status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Returning success anyway?
return 0
}
clone_or_update_repos() {
# We need the directory
local directory="${1}"
# as well as the repo URL
local remoteRepo="${2}"
# so get git files for Core
getGitFiles ${directory} ${remoteRepo} || return 1
}
welcomeDialogs() {
whiptail --msgbox --backtitle "Welcome" --title "Nim Website Creator" "\\n\\nThis installer will install and enable NimHA!" ${r} ${c}
}
setInstallFolder() {
local directory=${1}
local installDir=$(whiptail --inputbox "Full path to install" 8 78 "${directory}" --title "Install location" 3>&1 1>&2 2>&3)
installLoc=${installDir}
printf " %b %s\\n" "${INFO}" "Install directory: ${installLoc}"
}
setUserParams() {
CFG_USERNAME=$(whiptail --inputbox "Username (x > 3)" 8 78 "${CFG_USERNAME}" --title "Admin user" 3>&1 1>&2 2>&3)
CFG_EMAIL=$(whiptail --inputbox "Email (x > 5)" 8 78 "${CFG_EMAIL}" --title "Admin user" 3>&1 1>&2 2>&3)
CFG_PASS=$(whiptail --inputbox "Password (x > 9)" 8 78 "${CFG_PASS}" --title "Admin user" 3>&1 1>&2 2>&3)
printf " %b %s\\n" "${TICK}" "User data updated. You can always edit them within the browser."
}
setSystemctl() {
local ToggleCommand
local ChooseOptions
local Choices
ToggleCommand=(whiptail --separate-output --radiolist "Enable systemctl?" ${r} ${c} 6)
ChooseOptions=("On (Recommended)" "" on
Off "" off)
Choices=$("${ToggleCommand[@]}" "${ChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1)
case ${Choices} in
"On (Recommended)")
printf " %b Systemctl On\\n" "${INFO}"
#enable_service ${CFG_SYSTEMCTLNAME}
CFG_SYSTEMCTL=true
;;
Off)
printf " %b Systemctl Off\\n" "${INFO}"
#disable_service ${CFG_SYSTEMCTLNAME}
CFG_SYSTEMCTL=false
;;
esac
}
setSymbolicLink() {
local ToggleCommand
local ChooseOptions
local Choices
ToggleCommand=(whiptail --separate-output --radiolist "Make symbolic link to NimHA?" ${r} ${c} 6)
ChooseOptions=("On (Recommended)" "" on
Off "" off)
Choices=$("${ToggleCommand[@]}" "${ChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1)
case ${Choices} in
"On (Recommended)")
printf " %b Symbolic link On\\n" "${INFO}"
check_req firejail
# Set it to true
CFG_SYMBOLIC=true
;;
Off)
printf " %b Symbolic link Off\\n" "${INFO}"
# or false
CFG_SYMBOLIC=false
;;
esac
}
setCompileFlag() {
local ToggleCommand
local ChooseOptions
local Choices
ToggleCommand=(whiptail --separate-output --radiolist "Use firejail?" ${r} ${c} 6)
ChooseOptions=("On (Recommended)" "" on
Off "" off)
Choices=$("${ToggleCommand[@]}" "${ChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1)
case ${Choices} in
"On (Recommended)")
printf " %b Firejail On\\n" "${INFO}"
check_req firejail
# Set it to true
CFG_FIREJAIL=true
;;
Off)
printf " %b Firejail Off\\n" "${INFO}"
# or false
CFG_FIREJAIL=false
;;
esac
}
config_nimha() {
local directory=${1}
local strConfig=${2}
local strConfigDefault=${directory}"/config/config_default.cfg"
# Check if config.cfg exists or create it
if [ ! -f ${strConfig} ]; then
printf " %b %s\\n" "${INFO}" "Copy default config file to ${strConfig}"
cp -v ${strConfigDefault} ${strConfig} &> /dev/null || return 1
printf " %b %s\\n" "${TICK}" "Config file created"
else
printf " %b %s\\n" "${TICK}" "Config exists: ${strConfig}"
fi
}
compile_nimha() {
local directory=${1}
local nimUser=$(logname)
# Stop service if running
if check_service_active ${CFG_SYSTEMCTLNAME} ; then
stop_service ${CFG_SYSTEMCTLNAME} &> /dev/null
printf " %b %s\\n" "${TICK}" "Service stopped"
fi
# Cd to directory
cd "${directory}" &> /dev/null || return 1
# Delete sub runner
if [ -f ${CFG_APPNAME} ]; then
rm ${CFG_APPNAME} &> /dev/null || return 1
printf " %b %s\\n" "${TICK}" "Old compile file removed"
fi
# Compile
printf " %b %s\\n" "${INFO}" "Compiling"
local strCompile="nim c -d:release -d:ssl nimha"
eval $strCompile
# Admin user and standard data
printf " %b %s\\n" "${INFO}" "Adding admin user"
printf " %b %s\\n" "${INFO}" "Inserting standard data"
local strRunData="./nimha newuser -u:${CFG_USERNAME} -e:${CFG_EMAIL} -p:${CFG_PASS} ${CFG_STANDARDDATA}"
eval $strRunData
# Symbolic link
if [[ "${CFG_SYMBOLIC}" == true ]]; then
ln -s ${directory}/nimha /usr/bin/nimha || return 1
printf " %b %s\\n" "${TICK}" "Symbolic link created"
fi
# Systemctl
if [[ "${CFG_SYSTEMCTL}" == true ]]; then
enable_service ${CFG_SYSTEMCTLNAME}
printf " %b %s\\n" "${TICK}" "Systemctl enabled"
fi
}
stop_service() {
# Stop service passed in as argument.
# Can softfail, as process may not be installed when this is called
local str="Stopping ${1} service"
printf " %b %s..." "${INFO}" "${str}"
if is_command systemctl ; then
systemctl stop "${1}" &> /dev/null || true
else
service "${1}" stop &> /dev/null || true
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
}
# Start/Restart service passed in as argument
restart_service() {
# Local, named variables
local str="Restarting ${1} service"
printf " %b %s..." "${INFO}" "${str}"
# If systemctl exists,
if is_command systemctl ; then
# use that to restart the service
systemctl restart "${1}" &> /dev/null
# Otherwise,
else
# fall back to the service command
service "${1}" restart &> /dev/null
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
}
# Enable service so that it will start with next reboot
enable_service() {
# Local, named variables
local str="Enabling ${1} service to start on reboot"
printf " %b %s..." "${INFO}" "${str}"
# If systemctl exists,
if is_command systemctl ; then
# use that to enable the service
systemctl enable "${1}" &> /dev/null
# Otherwise,
else
# use update-rc.d to accomplish this
update-rc.d "${1}" defaults &> /dev/null
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
}
# Disable service so that it will not with next reboot
disable_service() {
# Local, named variables
local str="Disabling ${1} service"
printf " %b %s..." "${INFO}" "${str}"
# If systemctl exists,
if is_command systemctl ; then
# use that to disable the service
systemctl disable "${1}" &> /dev/null
# Otherwise,
else
# use update-rc.d to accomplish this
update-rc.d "${1}" disable &> /dev/null
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
}
check_service_active() {
# If systemctl exists,
if is_command systemctl ; then
# use that to check the status of the service
systemctl is-enabled "${1}" &> /dev/null
# Otherwise,
else
# fall back to service command
service "${1}" status &> /dev/null
fi
}
main() {
######## FIRST CHECK ########
# Must not be root to install!
printf "\\n\\n"
show_ascii_logo
printf " %b %s\\n" "${INFO}" "User priviligies"
if [[ "${EUID}" -eq 0 ]]; then
# Root
printf " %b %s\\n" "${INFO}" "You are running as root - take care"
printf " Make sure to download this script from a trusted source\\n\\n"
printf " %b %s\\n" "${TICK}" "NimHA will be enabled with systemctl"
# Otherwise,
else
# They do not have enough privileges, so let the user know
printf " %b %s\\n" "${CROSS}" "You are running as a non-root user"
printf " Please enable root and run again\\n\\n"
fi
# Check req
printf "\\n\\n"
printf " %b %s\\n" "${INFO}" "Checking required software"
check_req nim
# Display welcome dialogs
welcomeDialogs
# Install folder
setInstallFolder ${installLoc}
# Login details
setUserParams
# Flags
#setCompileFlag
# Symbolic link
setSymbolicLink
# Enable with systemctl
setSystemctl
# Create user
setup_user
# Create folder
setup_folder ${installLoc}
# Clone/Update the repos
clone_or_update_repos ${installLoc} ${gitRepoUrl}
# Config NimHA
config_nimha ${installLoc} ${configFile}
# Set folder permission
#setup_folder_permission ${installLoc} ${CFG_SYSTEM_USERNAME} ${CFG_SYSTEM_USERGROUP}
# Compile
# TODO: Use normal users Nim env path. If Nim is not installed or exported for root or nimha-user, this breaks
compile_nimha ${installLoc}
# Set folder permission
#setup_folder_permission ${installLoc} ${CFG_SYSTEM_USERNAME} ${CFG_SYSTEM_USERGROUP}
}
if [[ "${PH_TEST}" != true ]] ; then
main "$@"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment