gravity-sync/gravity-sync
Michael Stanclift 0fc086c93d
4.0.7
2024-01-15 09:20:15 -06:00

2502 lines
87 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# shellcheck disable=SC2086,SC1091
GS_RUN_START=$SECONDS
# GRAVITY SYNC BY VMSTAN #####################
PROGRAM='Gravity Sync'
GS_VERSION='4.0.7'
# For documentation or the changelog/updates visit https://github.com/vmstan/gravity-sync
# Requires Pi-Hole 5.x or higher already be installed, for help visit https://pi-hole.net
# REQUIRED SETTINGS ##########################
# Run 'gravity-sync config' to get started, it will customize the script for your environment
# You should NOT to change the values of any variables here, to customize your install
# Only add replacement variables to gravity-sync.conf, which will overwrite these defaults
# Gravity Sync 4.0 introduces a new configuration file format, there is no direct upgrade path
# CUSTOM VARIABLES ###########################
# Pi-hole Folder/File Customization - Only need to be customized when using containers
LOCAL_PIHOLE_DIRECTORY=${LOCAL_PIHOLE_DIRECTORY:-'/etc/pihole'} # replace in gravity-sync.conf to overwrite
REMOTE_PIHOLE_DIRECTORY=${REMOTE_PIHOLE_DIRECTORY:-'/etc/pihole'} # replace in gravity-sync.conf to overwrite
LOCAL_DNSMASQ_DIRECTORY=${LOCAL_DNSMASQ_DIRECTORY:-'/etc/dnsmasq.d'} # replace in gravity-sync.conf to overwrite
REMOTE_DNSMASQ_DIRECTORY=${REMOTE_DNSMASQ_DIRECTORY:-'/etc/dnsmasq.d'} # replace in gravity-sync.conf to overwrite
LOCAL_FILE_OWNER=${LOCAL_FILE_OWNER:-'pihole:pihole'} # replace in gravity-sync.conf to overwrite
REMOTE_FILE_OWNER=${REMOTE_FILE_OWNER:-'pihole:pihole'} # replace in gravity-sync.conf to overwrite
# Pi-hole Docker/Podman container name - Docker will pattern match anything set below
LOCAL_DOCKER_CONTAINER=${LOCAL_DOCKER_CONTAINER:-'pihole'} # replace in gravity-sync.conf to overwrite
REMOTE_DOCKER_CONTAINER=${REMOTE_DOCKER_CONTAINER:-'pihole'} # replace in gravity-sync.conf to overwrite
# STANDARD VARIABLES #########################
DEFAULT_PIHOLE_DIRECTORY=${DEFAULT_PIHOLE_DIRECTORY:-'/etc/pihole'} # Default Pi-hole data directory
LOCAL_PIHOLE_BINARY=${LOCAL_PIHOLE_BINARY:-'/usr/local/bin/pihole'} # Local Pi-hole binary directory (default)
REMOTE_PIHOLE_BINARY=${REMOTE_PIHOLE_BINARY:-'/usr/local/bin/pihole'} # Remote Pi-hole binary directory (default)
LOCAL_FTL_BINARY=${LOCAL_FTL_BINARY:-'/usr/bin/pihole-FTL'} # Local FTL binary directory (default)
REMOTE_FTL_BINARY=${REMOTE_FTL_BINARY:-'/usr/bin/pihole-FTL'} # Remote FTL binary directory (default)
LOCAL_DOCKER_BINARY=${LOCAL_DOCKER_BINARY:-'/usr/bin/docker'} # Local Docker binary directory (default)
REMOTE_DOCKER_BINARY=${REMOTE_DOCKER_BINARY:-'/usr/bin/docker'} # Remote Docker binary directory (default)
LOCAL_PODMAN_BINARY=${LOCAL_PODMAN_BINARY:-'/usr/bin/podman'} # Local Podman binary directory (default)
REMOTE_PODMAN_BINARY=${REMOTE_PODMAN_BINARY:-'/usr/bin/podman'} # Remote Podman binary directory (default)
PIHOLE_CONTAINER_IMAGE=${PIHOLE_CONTAINER_IMAGE:-'pihole/pihole'} # Official Pi-hole container image name
###############################################
####### THE NEEDS OF THE MANY, OUTWEIGH #######
############ THE NEEDS OF THE FEW #############
###############################################
PH_GRAVITY_FI=${PH_GRAVITY_FI:-'gravity.db'} # Pi-hole database file name
PH_CUSTOM_DNS=${PH_CUSTOM_DNS:-'custom.list'} # Pi-hole DNS lookup filename
PH_CNAME_CONF=${PH_CNAME_CONF:-'05-pihole-custom-cname.conf'} # DNSMASQ CNAME alias file
PH_SDHCP_CONF=${PH_SDHCP_CONF:-'04-pihole-static-dhcp.conf'} # DNSMASQ Static DHCP file
# Backup Customization
GS_BACKUP_TIMEOUT=${GS_BACKUP_TIMEOUT:-'240'} # replace in gravity-sync.conf to overwrite
GS_BACKUP_INTEGRITY_WAIT=${GS_BACKUP_INTEGRITY_WAIT:-'5'} # replace in gravity-sync.conf to overwrite
GS_BACKUP_EXT=${GS_BACKUP_EXT:-'gsb'} # replace in gravity-sync.conf to overwrite
# GS Folder/File Locations
GS_FILEPATH='/usr/local/bin/gravity-sync'
GS_ETC_PATH=${GS_ETC_PATH:-"/etc/gravity-sync"} # replace in gravity-sync.conf to overwrite
GS_CONFIG_FILE=${GS_CONFIG_FILE:-'gravity-sync.conf'} # replace in gravity-sync.conf to overwrite
GS_SYNCING_LOG=${GS_SYNCING_LOG:-'gs-sync.log'} # replace in gravity-sync.conf to overwrite
GS_GRAVITY_FI_MD5_LOG=${GS_GRAVITY_FI_MD5_LOG:-'gs-gravity.md5'} # replace in gravity-sync.conf to overwrite
GS_CUSTOM_DNS_MD5_LOG=${GS_CUSTOM_DNS_MD5_LOG:-'gs-clist.md5'} # replace in gravity-sync.conf to overwrite
GS_CNAME_CONF_MD5_LOG=${GS_CNAME_CONF_MD5_LOG:-'05-pihole-custom-cname.conf.md5'} # replace in gravity-sync.conf to overwrite
GS_SDHCP_CONF_MD5_LOG=${GS_SDHCP_CONF_MD5_LOG:-'04-pihole-static-dhcp.conf.md5'} # replace in gravity-sync.conf to overwrite
# SSH Customization
GS_SSH_PORT=${GS_SSH_PORT:-'22'} # replace in gravity-sync.conf to overwrite
GS_SSH_PKIF=${GS_SSH_PKIF:-"${GS_ETC_PATH}/gravity-sync.rsa"} # replace in gravity-sync.conf to overwrite
# Github Customization
GS_LOCAL_REPO=${GS_LOCAL_REPO:-"${GS_ETC_PATH}/.gs"} # replace in gravity-sync.conf to overwrite
# OS Settings
OS_DAEMON_PATH='/etc/systemd/system'
OS_TMP='/tmp'
OS_SSH_CMD='ssh'
# Interface Settings
UI_GRAVITY_NAME='Gravity Database'
UI_CUSTOM_NAME='DNS Records'
UI_CNAME_NAME='DNS CNAMEs'
UI_SDHCP_NAME='Static DHCP Addresses'
# Reused UI Text
UI_CORE_LOADING='Loading'
UI_CORE_EVALUATING='Evaluating arguments'
UI_CORE_INIT="Initializing ${PROGRAM} (${GS_VERSION})"
UI_CORE_APP='Pi-hole'
UI_CORE_APP_DNS='DNSMASQ'
UI_EXIT_CALC_END='after'
UI_EXIT_ABORT='exited'
UI_EXIT_COMPLETE='completed'
UI_EXIT_CALC_TIMER='seconds'
UI_HASHING_HASHING='Hashing the remote'
UI_HASHING_COMPARING='Comparing to the local'
UI_HASHING_DIFFERENCE='Differences detected in the'
UI_HASHING_DETECTED='has been detected on the'
UI_HASHING_NOT_DETECTED='not detected on the'
UI_HASHING_REMOTE="remote ${UI_CORE_APP}"
UI_HASHING_LOCAL="local ${UI_CORE_APP}"
UI_HASHING_REHASHING='Rehashing the remote'
UI_HASHING_RECOMPARING='Recomparing to local'
UI_VALIDATING='Validating pathways to'
UI_VALIDATING_FAIL_CONTAINER='Unable to validate running container instance of'
UI_VALIDATING_FAIL_FOLDER='Unable to validate configuration folder for'
UI_VALIDATING_FAIL_BINARY='Unable to validate the availability of'
UI_SET_LOCAL_FILE_OWNERSHIP='Setting file ownership on'
UI_SET_FILE_PERMISSION='Setting file permissions on'
UI_PULL_REMOTE='Pulling the remote'
UI_PUSH_LOCAL='Pushing the local'
UI_REPLACE_LOCAL='Replacing the local'
UI_FTLDNS_CONFIG_PULL_RELOAD='Reloading local FTLDNS services'
UI_FTLDNS_CONFIG_PUSH_RELOAD='Reloading remote FTLDNS services'
UI_LOGGING_RECENT_COMPLETE='Recent complete executions of'
UI_BACKUP_REMOTE='Performing backup of remote'
UI_BACKUP_LOCAL='Performing backup of local'
UI_BACKUP_INTEGRITY="Checking ${UI_GRAVITY_NAME} copy integrity"
UI_BACKUP_INTEGRITY_FAILED='Integrity check has failed for the remote'
UI_BACKUP_INTEGRITY_DELETE='Removing failed copies'
UI_CONFIG_ALREADY='already exists'
UI_CONFIG_CONFIRM='Proceeding will replace your existing configuration'
UI_CONFIG_ERASING='Erasing existing'
UI_CONFIG_LOCAL='local host'
UI_CONFIG_CONTAINER_NAME='container name'
UI_CONFIG_SAVING='Saving'
UI_CONFIG_ETC_VOLUME_PATH="'etc' volume path"
UI_CONFIG_VOLUME_OWNER='volume ownership'
## Script Colors
RED='\033[0;91m'
GREEN='\033[0;92m'
CYAN='\033[0;96m'
YELLOW='\033[0;93m'
PURPLE='\033[0;95m'
BLUE='\033[0;94m'
BOLD='\033[1m'
NC='\033[0m'
## Message Codes
FAIL="${RED}${NC}"
WARN="${PURPLE}!${NC}"
GOOD="${GREEN}${NC}"
STAT="${CYAN}${NC}"
INFO="${YELLOW}»${NC}"
INF1="${CYAN}${NC}"
NEED="${BLUE}?${NC}"
LOGO="${PURPLE}${NC}"
## Echo Stack
### Informative
function echo_info {
echo -e "${INFO} ${YELLOW}${MESSAGE}${NC}"
}
function echo_prompt {
echo -e "${INF1} ${CYAN}${MESSAGE}${NC}"
}
### Warning
function echo_warn {
echo -e "${WARN} ${PURPLE}${MESSAGE}${NC}"
}
### Executing
function echo_stat {
echo -en "${STAT} ${MESSAGE}"
}
### Success
function echo_good {
echo -e "\r${GOOD} ${MESSAGE}"
}
### Success
function echo_good_clean {
echo -e "\r${GOOD} ${MESSAGE}"
}
### Failure
function echo_fail {
echo -e "\r${FAIL} ${MESSAGE}"
}
### Request
function echo_need {
echo -en "${NEED} ${BOLD}${MESSAGE}:${NC} "
}
### Indent
function echo_over {
echo -e " ${MESSAGE}"
}
### Gravity Sync Logo
function echo_grav {
echo -e "${LOGO} ${BOLD}${MESSAGE}${NC}"
}
### Lines
function echo_blank {
echo -e ""
}
# Standard Output
function start_gs {
MESSAGE="${UI_CORE_INIT}"
echo_grav
import_gs_config
detect_local_pihole
detect_remote_pihole
detect_gs_peer
set_pihole_exec
MESSAGE="${UI_CORE_EVALUATING}"
echo_stat
validate_sudo_status
}
# Standard Output No Config
function start_gs_no_config {
MESSAGE="${UI_CORE_INIT}"
echo_grav
MESSAGE="${UI_CORE_EVALUATING}"
echo_stat
}
## Import Settings
function import_gs_config {
MESSAGE="${UI_CORE_LOADING} ${GS_CONFIG_FILE}"
echo -en "${STAT} $MESSAGE"
if [ -f ${GS_ETC_PATH}/${GS_CONFIG_FILE} ]; then
# shellcheck source=/etc/gravity-sync/gravity-sync.conf
source ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
else
echo_fail
MESSAGE="Missing ${GS_CONFIG_FILE}"
echo_warn
GS_TASK_TYPE='CONFIG'
config_generate
fi
}
## Invalid Tasks
function task_invalid {
start_gs_no_config
echo_fail
list_gs_arguments
}
## Error Validation
function error_validate {
if [ "$?" != "0" ]; then
echo_fail
exit 1
else
echo_good
fi
}
function set_pihole_exec {
if [ "$LOCAL_PIHOLE_TYPE" == "default" ]; then
PH_EXEC="${LOCAL_PIHOLE_BINARY}"
FTL_EXEC="${LOCAL_FTL_BINARY}"
elif [ "$LOCAL_PIHOLE_TYPE" == "docker" ]; then
PH_EXEC="sudo ${LOCAL_DOCKER_BINARY} exec $(sudo ${LOCAL_DOCKER_BINARY} ps -qf name=^${LOCAL_DOCKER_CONTAINER}$) pihole"
FTL_EXEC="sudo ${LOCAL_DOCKER_BINARY} exec $(sudo ${LOCAL_DOCKER_BINARY} ps -qf name=^${LOCAL_DOCKER_CONTAINER}$) pihole-FTL"
elif [ "$LOCAL_PIHOLE_TYPE" == "podman" ]; then
PH_EXEC="sudo ${LOCAL_PODMAN_BINARY} exec ${LOCAL_DOCKER_CONTAINER} pihole"
FTL_EXEC="sudo ${LOCAL_PODMAN_BINARY} exec ${LOCAL_DOCKER_CONTAINER} pihole-FTL"
fi
if [ "$REMOTE_PIHOLE_TYPE" == "default" ]; then
RH_EXEC="${REMOTE_PIHOLE_BINARY}"
RFTL_EXEC="${REMOTE_FTL_BINARY}"
elif [ "$REMOTE_PIHOLE_TYPE" == "docker" ]; then
RH_EXEC="sudo ${REMOTE_DOCKER_BINARY} exec \$(sudo ${REMOTE_DOCKER_BINARY} ps -qf name=^${REMOTE_DOCKER_CONTAINER}$) pihole"
RFTL_EXEC="sudo ${REMOTE_DOCKER_BINARY} exec \$(sudo ${REMOTE_DOCKER_BINARY} ps -qf name=^${REMOTE_DOCKER_CONTAINER}$) pihole-FTL"
elif [ "$REMOTE_PIHOLE_TYPE" == "podman" ]; then
RH_EXEC="sudo ${REMOTE_PODMAN_BINARY} exec ${REMOTE_DOCKER_CONTAINER} pihole"
RFTL_EXEC="sudo ${REMOTE_PODMAN_BINARY} exec ${REMOTE_DOCKER_CONTAINER} pihole-FTL"
fi
}
## Compare Task
function task_compare {
start_gs
GS_TASK_TYPE='COMPARE'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
show_target
validate_ph_folders
validate_dns_folders
previous_md5
md5_compare
exit_with_changes
}
## Pull Task
function task_pull {
start_gs
GS_TASK_TYPE='PULL'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
show_target
validate_ph_folders
validate_dns_folders
pull_gs
exit
}
## Pull Gravity
function pull_gs_grav {
backup_local_gravity
backup_remote_gravity
backup_remote_gravity_integrity
MESSAGE="${UI_PULL_REMOTE} ${UI_GRAVITY_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${OS_TMP}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}"
create_rsync_cmd
MESSAGE="${UI_REPLACE_LOCAL} ${UI_GRAVITY_NAME}"
echo_stat
sudo mv ${OS_TMP}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} >/dev/null 2>&1
error_validate
validate_gravity_permissions
}
## Pull Custom
function pull_gs_custom {
if [ "$REMOTE_PH_CUSTOM_DNS" == "1" ]; then
backup_local_custom
backup_remote_custom
MESSAGE="${UI_PULL_REMOTE} ${UI_CUSTOM_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${OS_TMP}/${PH_CUSTOM_DNS}.${GS_BACKUP_EXT}"
create_rsync_cmd
MESSAGE="${UI_REPLACE_LOCAL} ${UI_CUSTOM_NAME}"
echo_stat
sudo mv ${OS_TMP}/${PH_CUSTOM_DNS}.${GS_BACKUP_EXT} ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} >/dev/null 2>&1
error_validate
validate_custom_permissions
fi
}
## Pull CNAME
function pull_gs_cname {
if [ "$REMOTE_CNAME_DNS" == "1" ]; then
backup_local_cname
backup_remote_cname
MESSAGE="${UI_PULL_REMOTE} ${UI_CNAME_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PIHOLE_DIRECTORY}/${PH_CNAME_CONF}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${OS_TMP}/${PH_CNAME_CONF}.${GS_BACKUP_EXT}"
create_rsync_cmd
MESSAGE="${UI_REPLACE_LOCAL} ${UI_CNAME_NAME}"
echo_stat
sudo mv ${OS_TMP}/${PH_CNAME_CONF}.${GS_BACKUP_EXT} ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} >/dev/null 2>&1
error_validate
validate_cname_permissions
fi
}
## Pull DHCP
function pull_gs_sdhcp {
if [ "$REMOTE_SDHCP_DNS" == "1" ]; then
backup_local_sdhcp
backup_remote_sdhcp
MESSAGE="${UI_PULL_REMOTE} ${UI_SDHCP_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PIHOLE_DIRECTORY}/${PH_SDHCP_CONF}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${OS_TMP}/${PH_SDHCP_CONF}.${GS_BACKUP_EXT}"
create_rsync_cmd
MESSAGE="${UI_REPLACE_LOCAL} ${UI_SDHCP_NAME}"
echo_stat
sudo mv ${OS_TMP}/${PH_SDHCP_CONF}.${GS_BACKUP_EXT} ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} >/dev/null 2>&1
error_validate
validate_sdhcp_permissions
fi
}
## Pull Reload
function pull_gs_reload {
sleep 1
MESSAGE="Updating local FTLDNS configuration"
echo_stat
${PH_EXEC} restartdns reload-lists >/dev/null 2>&1
error_validate
if [ "${GS_TASK_TYPE}" == SMART ]; then
if [ "${REMOTE_DNS_CHANGE}" == "1" ] || [ "${LOCAL_DNS_CHANGE}" == "1" ] || [ "${REMOTE_CNAME_CHANGE}" == "1" ] || [ "${LOCAL_CNAME_CHANGE}" == "1" ] || [ "${REMOTE_SDHCP_CHANGE}" == "1" ] || [ "${LOCAL_SDHCP_CHANGE}" == "1" ]; then
MESSAGE="${UI_FTLDNS_CONFIG_PULL_RELOAD}"
echo_stat
${PH_EXEC} restartdns >/dev/null 2>&1
error_validate
fi
else
MESSAGE="${UI_FTLDNS_CONFIG_PULL_RELOAD}"
echo_stat
${PH_EXEC} restartdns >/dev/null 2>&1
error_validate
fi
}
## Pull Function
function pull_gs {
previous_md5
md5_compare
pull_gs_grav
pull_gs_custom
pull_gs_cname
pull_gs_sdhcp
pull_gs_reload
md5_recheck
logs_export
exit_with_changes
}
## Push Task
function task_push {
start_gs
GS_TASK_TYPE='PUSH'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
show_target
validate_ph_folders
validate_dns_folders
push_gs
exit
}
## Push Gravity
function push_gs_grav {
backup_remote_gravity
backup_local_gravity
backup_local_gravity_integrity
MESSAGE="${UI_PUSH_LOCAL} ${UI_GRAVITY_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}"
create_rsync_cmd
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_GRAVITY_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chown ${REMOTE_FILE_OWNER} ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}"
create_ssh_cmd
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_GRAVITY_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chmod 664 ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}"
create_ssh_cmd
}
## Push Custom
function push_gs_custom {
if [ "$REMOTE_PH_CUSTOM_DNS" == "1" ]; then
backup_remote_custom
backup_local_custom
MESSAGE="${UI_PUSH_LOCAL} ${UI_CUSTOM_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}"
create_rsync_cmd
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_CUSTOM_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chown ${REMOTE_FILE_OWNER} ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}"
create_ssh_cmd
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_CUSTOM_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chmod 644 ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}"
create_ssh_cmd
fi
}
## Push Custom
function push_gs_cname {
if [ "$REMOTE_CNAME_DNS" == "1" ]; then
backup_remote_cname
backup_local_cname
MESSAGE="${UI_PUSH_LOCAL} ${UI_CNAME_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${LOCAL_PIHOLE_DIRECTORY}/${PH_CNAME_CONF}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}"
create_rsync_cmd
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_CNAME_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chown ${REMOTE_FILE_OWNER} ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}"
create_ssh_cmd
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_CNAME_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chmod 644 ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}"
create_ssh_cmd
fi
}
## Push Custom
function push_gs_sdhcp {
if [ "$REMOTE_SDHCP_DNS" == "1" ]; then
backup_remote_sdhcp
backup_local_sdhcp
MESSAGE="${UI_PUSH_LOCAL} ${UI_SDHCP_NAME}"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${LOCAL_PIHOLE_DIRECTORY}/${PH_SDHCP_CONF}.${GS_BACKUP_EXT}"
RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}"
create_rsync_cmd
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_SDHCP_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chown ${REMOTE_FILE_OWNER} ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}"
create_ssh_cmd
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_SDHCP_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chmod 644 ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}"
create_ssh_cmd
fi
}
## Push Reload
function push_gs_reload {
sleep 1
MESSAGE="Updating remote FTLDNS configuration"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="${RH_EXEC} restartdns reload-lists"
create_ssh_cmd
if [ "${GS_TASK_TYPE}" == SMART ]; then
if [ "${REMOTE_DNS_CHANGE}" == "1" ] || [ "${LOCAL_DNS_CHANGE}" == "1" ] || [ "${REMOTE_CNAME_CHANGE}" == "1" ] || [ "${LOCAL_CNAME_CHANGE}" == "1" ] || [ "${REMOTE_SDHCP_CHANGE}" == "1" ] || [ "${LOCAL_SDHCP_CHANGE}" == "1" ]; then
MESSAGE="${UI_FTLDNS_CONFIG_PUSH_RELOAD}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="${RH_EXEC} restartdns"
create_ssh_cmd
fi
else
MESSAGE="${UI_FTLDNS_CONFIG_PUSH_RELOAD}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="${RH_EXEC} restartdns"
create_ssh_cmd
fi
}
## Push Function
function push_gs {
previous_md5
md5_compare
push_gs_grav
push_gs_custom
push_gs_cname
push_gs_sdhcp
push_gs_reload
md5_recheck
logs_export
exit_with_changes
}
## Smart Task
function task_smart {
start_gs
GS_TASK_TYPE='SMART'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
show_target
validate_ph_folders
validate_dns_folders
smart_gs
exit
}
## Smart Sync Function
function smart_gs {
MESSAGE="Starting ${GS_TASK_TYPE} Analysis"
echo_info
previous_md5
md5_compare
REMOTE_GRAVITY_CHANGE="0"
LOCAL_GRAVITY_CHANGE="0"
REMOTE_DNS_CHANGE="0"
LOCAL_DNS_CHANGE="0"
REMOTE_CNAME_CHANGE="0"
LOCAL_CNAME_CHANGE="0"
REMOTE_SDHCP_CHANGE="0"
LOCAL_SDHCP_CHANGE="0"
if [ "${REMOTE_DB_MD5}" != "${LAST_REMOTE_DB_MD5}" ]; then
REMOTE_GRAVITY_CHANGE="1"
fi
if [ "${LOCAL_DB_MD5}" != "${LAST_LOCAL_DB_MD5}" ]; then
LOCAL_GRAVITY_CHANGE="1"
fi
if [ "${REMOTE_GRAVITY_CHANGE}" == "${LOCAL_GRAVITY_CHANGE}" ]; then
if [ "${REMOTE_GRAVITY_CHANGE}" != "0" ]; then
MESSAGE="Both ${UI_GRAVITY_NAME} have changed"
echo_warn
REMOTE_GRAVITY_DATE=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}")
LOCAL_GRAVITY_DATE=$(stat -c %Y ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI})
if (( "$REMOTE_GRAVITY_DATE" >= "$LOCAL_GRAVITY_DATE" )); then
MESSAGE="Remote ${UI_GRAVITY_NAME} was last changed"
echo_warn
pull_gs_grav
GS_PULL_RESTART="1"
else
MESSAGE="Local ${UI_GRAVITY_NAME} was last changed"
echo_warn
push_gs_grav
GS_PUSH_RESTART="1"
fi
fi
else
if [ "${REMOTE_GRAVITY_CHANGE}" != "0" ]; then
pull_gs_grav
GS_PULL_RESTART="1"
elif [ "${LOCAL_GRAVITY_CHANGE}" != "0" ]; then
push_gs_grav
GS_PUSH_RESTART="1"
fi
fi
if [ "${REMOTE_CL_MD5}" != "${LAST_REMOTE_CL_MD5}" ]; then
REMOTE_DNS_CHANGE="1"
fi
if [ "${LOCAL_CL_MD5}" != "${LAST_LOCAL_CL_MD5}" ]; then
LOCAL_DNS_CHANGE="1"
fi
if [ -f "${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}" ]; then
if [ "${REMOTE_DNS_CHANGE}" == "${LOCAL_DNS_CHANGE}" ]; then
if [ "${REMOTE_DNS_CHANGE}" != "0" ]; then
MESSAGE="Both ${UI_CUSTOM_NAME} have changed"
echo_warn
REMOTE_DNS_DATE=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}")
LOCAL_DNS_DATE=$(stat -c %Y ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS})
if (( "$REMOTE_DNS_DATE" >= "$LOCAL_DNS_DATE" )); then
MESSAGE="Remote ${UI_CUSTOM_NAME} was last changed"
echo_warn
pull_gs_custom
GS_PULL_RESTART="1"
else
MESSAGE="Local ${UI_CUSTOM_NAME} was last changed"
echo_warn
push_gs_custom
GS_PUSH_RESTART="1"
fi
fi
else
if [ "${REMOTE_DNS_CHANGE}" != "0" ]; then
pull_gs_custom
GS_PULL_RESTART="1"
elif [ "${LOCAL_DNS_CHANGE}" != "0" ]; then
push_gs_custom
GS_PUSH_RESTART="1"
fi
fi
else
pull_gs_custom
GS_PULL_RESTART="1"
fi
if [ "${REMOTE_CN_MD5}" != "${LAST_REMOTE_CN_MD5}" ]; then
REMOTE_CNAME_CHANGE="1"
fi
if [ "${LOCAL_CN_MD5}" != "${LAST_LOCAL_CN_MD5}" ]; then
LOCAL_CNAME_CHANGE="1"
fi
if [ -f "${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}" ]; then
if [ "${REMOTE_CNAME_CHANGE}" == "${LOCAL_CNAME_CHANGE}" ]; then
if [ "${REMOTE_CNAME_CHANGE}" != "0" ]; then
MESSAGE="Both ${UI_CNAME_NAME} have Changed"
echo_warn
REMOTE_CNAME_DATE=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}")
LOCAL_CNAME_DATE=$(stat -c %Y ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF})
if (( "$REMOTE_CNAME_DATE" >= "$LOCAL_CNAME_DATE" )); then
MESSAGE="Remote ${UI_CNAME_NAME} was last changed"
echo_warn
pull_gs_cname
GS_PULL_RESTART="1"
else
MESSAGE="Local ${UI_CNAME_NAME} was last changed"
echo_warn
push_gs_cname
GS_PUSH_RESTART="1"
fi
fi
else
if [ "${REMOTE_CNAME_CHANGE}" != "0" ]; then
pull_gs_cname
GS_PULL_RESTART="1"
elif [ "${LOCAL_CNAME_CHANGE}" != "0" ]; then
push_gs_cname
GS_PUSH_RESTART="1"
fi
fi
else
pull_gs_cname
GS_PULL_RESTART="1"
fi
if [ "${REMOTE_SDHCP_MD5}" != "${LAST_REMOTE_SDHCP_MD5}" ]; then
REMOTE_SDHCP_CHANGE="1"
fi
if [ "${LOCAL_SDHCP_MD5}" != "${LAST_LOCAL_SDHCP_MD5}" ]; then
LOCAL_SDHCP_CHANGE="1"
fi
if [ -f "${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}" ]; then
if [ "${REMOTE_SDHCP_CHANGE}" == "${LOCAL_SDHCP_CHANGE}" ]; then
if [ "${REMOTE_SDHCP_CHANGE}" != "0" ]; then
MESSAGE="Both ${UI_SDHCP_NAME} have Changed"
echo_warn
REMOTE_SDHCP_DATE=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}")
LOCAL_SDHCP_DATE=$(stat -c %Y ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF})
if (( "$REMOTE_SDHCP_DATE" >= "$LOCAL_SDHCP_DATE" )); then
MESSAGE="Remote ${UI_SDHCP_NAME} was last changed"
echo_warn
pull_gs_sdhcp
GS_PULL_RESTART="1"
else
MESSAGE="Local ${UI_SDHCP_NAME} was last changed"
echo_warn
push_gs_sdhcp
GS_PUSH_RESTART="1"
fi
fi
else
if [ "${REMOTE_SDHCP_CHANGE}" != "0" ]; then
pull_gs_sdhcp
GS_PULL_RESTART="1"
elif [ "${LOCAL_SDHCP_CHANGE}" != "0" ]; then
push_gs_sdhcp
GS_PUSH_RESTART="1"
fi
fi
else
pull_gs_sdhcp
GS_PULL_RESTART="1"
fi
if [ "$GS_PULL_RESTART" == "1" ]; then
pull_gs_reload
fi
if [ "$GS_PUSH_RESTART" == "1" ]; then
push_gs_reload
fi
md5_recheck
logs_export
exit_with_changes
}
function backup_local_gravity {
MESSAGE="${UI_BACKUP_LOCAL} ${UI_GRAVITY_NAME}"
echo_stat
if [ "$LOCAL_PIHOLE_TYPE" == "default" ]; then
sudo ${FTL_EXEC} sql ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} ".backup '${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}'"
error_validate
elif [ "$LOCAL_PIHOLE_TYPE" == "docker" ]; then
sudo ${FTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} ".backup '${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}'"
error_validate
elif [ "$LOCAL_PIHOLE_TYPE" == "podman" ]; then
sudo ${FTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} ".backup '${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}'"
error_validate
fi
}
function backup_local_gravity_integrity {
MESSAGE="${UI_BACKUP_INTEGRITY}"
echo_stat
sleep $GS_BACKUP_INTEGRITY_WAIT
if [ "$LOCAL_PIHOLE_TYPE" == "default" ]; then
LOCAL_INTEGRITY_CHECK=$(${FTL_EXEC} sql ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} 'PRAGMA integrity_check;' | sed 's/\s.*$//')
error_validate
elif [ "$LOCAL_PIHOLE_TYPE" == "docker" ]; then
LOCAL_INTEGRITY_CHECK=$(${FTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} 'PRAGMA integrity_check;' | sed 's/\s.*$//')
error_validate
elif [ "$LOCAL_PIHOLE_TYPE" == "podman" ]; then
LOCAL_INTEGRITY_CHECK=$(${FTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} 'PRAGMA integrity_check;' | sed 's/\s.*$//')
error_validate
fi
if [ "$LOCAL_INTEGRITY_CHECK" != 'ok' ]; then
MESSAGE="${UI_BACKUP_INTEGRITY_FAILED} ${UI_GRAVITY_NAME}"
echo_fail
MESSAGE="${UI_BACKUP_INTEGRITY_DELETE} ${UI_GRAVITY_NAME}"
echo_stat
sudo rm ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}
error_validate
exit_no_change
fi
}
function backup_local_custom {
if [ -f ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} ]; then
MESSAGE="${UI_BACKUP_LOCAL} ${UI_CUSTOM_NAME}"
echo_stat
sudo cp ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}.${GS_BACKUP_EXT}
error_validate
else
MESSAGE="No local ${PH_CUSTOM_DNS} detected"
echo_warn
fi
}
function backup_local_cname {
if [ -f ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} ]; then
MESSAGE="${UI_BACKUP_LOCAL} ${UI_CNAME_NAME}"
echo_stat
sudo cp ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} ${LOCAL_PIHOLE_DIRECTORY}/${PH_CNAME_CONF}.${GS_BACKUP_EXT}
error_validate
else
MESSAGE="No local ${PH_CNAME_CONF} detected"
echo_warn
fi
}
function backup_local_sdhcp {
if [ -f ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} ]; then
MESSAGE="${UI_BACKUP_LOCAL} ${UI_SDHCP_NAME}"
echo_stat
sudo cp ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} ${LOCAL_PIHOLE_DIRECTORY}/${PH_SDHCP_CONF}.${GS_BACKUP_EXT}
error_validate
else
MESSAGE="No local ${PH_SDHCP_CONF} detected"
echo_warn
fi
}
function backup_remote_gravity {
MESSAGE="${UI_BACKUP_REMOTE} ${UI_GRAVITY_NAME}"
echo_stat
if [ "$REMOTE_PIHOLE_TYPE" == "default" ]; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo ${RFTL_EXEC} sql ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} \".backup '${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}'\""
create_ssh_cmd
elif [ "$REMOTE_PIHOLE_TYPE" == "docker" ]; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo ${RFTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} \".backup '${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}'\""
create_ssh_cmd
elif [ "$REMOTE_PIHOLE_TYPE" == "podman" ]; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo ${RFTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} \".backup '${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}'\""
create_ssh_cmd
fi
}
function backup_remote_gravity_integrity {
MESSAGE="${UI_BACKUP_INTEGRITY}"
echo_stat
sleep $GS_BACKUP_INTEGRITY_WAIT
if [ "$REMOTE_PIHOLE_TYPE" == "default" ]; then
REMOTE_INTEGRITY_CHECK=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "${RFTL_EXEC} sql ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} 'PRAGMA integrity_check;'" | sed 's/\s.*$//')
error_validate
elif [ "$REMOTE_PIHOLE_TYPE" == "docker" ]; then
REMOTE_INTEGRITY_CHECK=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "${RFTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} 'PRAGMA integrity_check;'" | sed 's/\s.*$//')
error_validate
elif [ "$REMOTE_PIHOLE_TYPE" == "podman" ]; then
REMOTE_INTEGRITY_CHECK=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "${RFTL_EXEC} sql ${DEFAULT_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT} 'PRAGMA integrity_check;'" | sed 's/\s.*$//')
error_validate
fi
if [ "$REMOTE_INTEGRITY_CHECK" != 'ok' ]; then
MESSAGE="${UI_BACKUP_INTEGRITY_FAILED} ${UI_GRAVITY_NAME}"
echo_fail
MESSAGE="${UI_BACKUP_INTEGRITY_DELETE} ${UI_GRAVITY_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo rm ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}.${GS_BACKUP_EXT}"
create_ssh_cmd
exit_no_change
fi
}
function backup_remote_custom {
MESSAGE="${UI_BACKUP_REMOTE} ${UI_CUSTOM_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo cp ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}.${GS_BACKUP_EXT}"
create_ssh_cmd
}
function backup_remote_cname {
MESSAGE="${UI_BACKUP_REMOTE} ${UI_CNAME_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo cp ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} ${REMOTE_PIHOLE_DIRECTORY}/${PH_CNAME_CONF}.${GS_BACKUP_EXT}"
create_ssh_cmd
}
function backup_remote_sdhcp {
MESSAGE="${UI_BACKUP_REMOTE} ${UI_SDHCP_NAME}"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo cp ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} ${REMOTE_PIHOLE_DIRECTORY}/${PH_SDHCP_CONF}.${GS_BACKUP_EXT}"
create_ssh_cmd
}
function backup_cleanup {
MESSAGE="Purging backups"
echo_stat
# git clean -fq
sudo rm -f ${LOCAL_PIHOLE_DIRECTORY}/*.${GS_BACKUP_EXT}
error_validate
# MESSAGE="${UI_BACKUP_PURGE} on remote"
# echo_stat
# CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
# CMD_REQUESTED="sudo rm -f ${REMOTE_PIHOLE_DIRECTORY}/*.${GS_BACKUP_EXT}"
# create_ssh_cmd
}
function md5_compare {
GS_HASH_MARK='0'
MESSAGE="${UI_HASHING_HASHING} ${UI_GRAVITY_NAME}"
echo_stat
REMOTE_DB_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}" | sed 's/\s.*$//')
error_validate
MESSAGE="${UI_HASHING_COMPARING} ${UI_GRAVITY_NAME}"
echo_stat
LOCAL_DB_MD5=$(md5sum ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} | sed 's/\s.*$//')
error_validate
if [ "$REMOTE_DB_MD5" == "$LAST_REMOTE_DB_MD5" ] && [ "$LOCAL_DB_MD5" == "$LAST_LOCAL_DB_MD5" ]; then
GS_HASH_MARK=$((GS_HASH_MARK+0))
else
MESSAGE="${UI_HASHING_DIFFERENCE} ${UI_GRAVITY_NAME}"
echo_warn
GS_HASH_MARK=$((GS_HASH_MARK+1))
fi
if [ -f ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} ]; then
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}; then
REMOTE_PH_CUSTOM_DNS="1"
MESSAGE="${UI_HASHING_HASHING} ${UI_CUSTOM_NAME}"
echo_stat
REMOTE_CL_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} | sed 's/\s.*$//'")
error_validate
MESSAGE="${UI_HASHING_COMPARING} ${UI_CUSTOM_NAME}"
echo_stat
LOCAL_CL_MD5=$(md5sum ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} | sed 's/\s.*$//')
error_validate
if [ "$REMOTE_CL_MD5" == "$LAST_REMOTE_CL_MD5" ] && [ "$LOCAL_CL_MD5" == "$LAST_LOCAL_CL_MD5" ]; then
GS_HASH_MARK=$((GS_HASH_MARK+0))
else
MESSAGE="${UI_HASHING_DIFFERENCE} ${UI_CUSTOM_NAME}"
echo_warn
GS_HASH_MARK=$((GS_HASH_MARK+1))
fi
else
MESSAGE="${UI_CUSTOM_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
else
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}; then
REMOTE_PH_CUSTOM_DNS="1"
MESSAGE="${UI_CUSTOM_NAME} ${UI_HASHING_DETECTED} ${UI_HASHING_REMOTE}"
GS_HASH_MARK=$((GS_HASH_MARK+1))
echo_warn
fi
MESSAGE="${UI_CUSTOM_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_LOCAL}"
echo_warn
fi
if [ -f ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} ]; then
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo touch ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}"
create_ssh_cmd
REMOTE_CNAME_DNS="1"
MESSAGE="${UI_HASHING_HASHING} ${UI_CNAME_NAME}"
echo_stat
REMOTE_CN_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} | sed 's/\s.*$//'")
error_validate
MESSAGE="${UI_HASHING_COMPARING} ${UI_CNAME_NAME}"
echo_stat
LOCAL_CN_MD5=$(md5sum ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} | sed 's/\s.*$//')
error_validate
if [ "$REMOTE_CN_MD5" == "$LAST_REMOTE_CN_MD5" ] && [ "$LOCAL_CN_MD5" == "$LAST_LOCAL_CN_MD5" ]; then
GS_HASH_MARK=$((GS_HASH_MARK+0))
else
MESSAGE="${UI_HASHING_DIFFERENCE} ${UI_CNAME_NAME}"
echo_warn
GS_HASH_MARK=$((GS_HASH_MARK+1))
fi
else
MESSAGE="${UI_CNAME_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
else
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}; then
REMOTE_CNAME_DNS="1"
MESSAGE="${UI_CNAME_NAME} ${UI_HASHING_DETECTED} ${UI_HASHING_REMOTE}"
GS_HASH_MARK=$((GS_HASH_MARK+1))
echo_warn
fi
MESSAGE="${UI_CNAME_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_LOCAL}"
echo_warn
fi
if [ -f ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} ]; then
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo touch ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}"
create_ssh_cmd
REMOTE_SDHCP_DNS="1"
MESSAGE="${UI_HASHING_HASHING} ${UI_SDHCP_NAME}"
echo_stat
REMOTE_SDHCP_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} | sed 's/\s.*$//'")
error_validate
MESSAGE="${UI_HASHING_COMPARING} ${UI_SDHCP_NAME}"
echo_stat
LOCAL_SDHCP_MD5=$(md5sum ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} | sed 's/\s.*$//')
error_validate
if [ "$REMOTE_SDHCP_MD5" == "$LAST_REMOTE_SDHCP_MD5" ] && [ "$LOCAL_SDHCP_MD5" == "$LAST_LOCAL_SDHCP_MD5" ]; then
GS_HASH_MARK=$((GS_HASH_MARK+0))
else
MESSAGE="${UI_HASHING_DIFFERENCE} ${UI_SDHCP_NAME}"
echo_warn
GS_HASH_MARK=$((GS_HASH_MARK+1))
fi
else
MESSAGE="${UI_SDHCP_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
else
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}; then
REMOTE_SDHCP_DNS="1"
MESSAGE="${UI_SDHCP_NAME} ${UI_HASHING_DETECTED} ${UI_HASHING_REMOTE}"
GS_HASH_MARK=$((GS_HASH_MARK+1))
echo_warn
fi
MESSAGE="${UI_SDHCP_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_LOCAL}"
echo_warn
fi
if [ "$GS_HASH_MARK" != "0" ]; then
MESSAGE="Replication of ${UI_CORE_APP} settings is required"
echo_warn
GS_HASH_MARK=$((GS_HASH_MARK+0))
else
MESSAGE="No replication is required at this time"
echo_warn
exit_no_change
fi
}
function previous_md5 {
if [ -f "${GS_ETC_PATH}/${GS_GRAVITY_FI_MD5_LOG}" ]; then
LAST_REMOTE_DB_MD5=$(sed "1q;d" ${GS_ETC_PATH}/${GS_GRAVITY_FI_MD5_LOG})
LAST_LOCAL_DB_MD5=$(sed "2q;d" ${GS_ETC_PATH}/${GS_GRAVITY_FI_MD5_LOG})
else
LAST_REMOTE_DB_MD5="0"
LAST_LOCAL_DB_MD5="0"
fi
if [ -f "${GS_ETC_PATH}/${GS_CUSTOM_DNS_MD5_LOG}" ]; then
LAST_REMOTE_CL_MD5=$(sed "1q;d" ${GS_ETC_PATH}/${GS_CUSTOM_DNS_MD5_LOG})
LAST_LOCAL_CL_MD5=$(sed "2q;d" ${GS_ETC_PATH}/${GS_CUSTOM_DNS_MD5_LOG})
else
LAST_REMOTE_CL_MD5="0"
LAST_LOCAL_CL_MD5="0"
fi
if [ -f "${GS_ETC_PATH}/${GS_CNAME_CONF_MD5_LOG}" ]; then
LAST_REMOTE_CN_MD5=$(sed "1q;d" ${GS_ETC_PATH}/${GS_CNAME_CONF_MD5_LOG})
LAST_LOCAL_CN_MD5=$(sed "2q;d" ${GS_ETC_PATH}/${GS_CNAME_CONF_MD5_LOG})
else
LAST_REMOTE_CN_MD5="0"
LAST_LOCAL_CN_MD5="0"
fi
if [ -f "${GS_ETC_PATH}/${GS_SDHCP_CONF_MD5_LOG}" ]; then
LAST_REMOTE_SDHCP_MD5=$(sed "1q;d" ${GS_ETC_PATH}/${GS_SDHCP_CONF_MD5_LOG})
LAST_LOCAL_SDHCP_MD5=$(sed "2q;d" ${GS_ETC_PATH}/${GS_SDHCP_CONF_MD5_LOG})
else
LAST_REMOTE_SDHCP_MD5="0"
LAST_LOCAL_SDHCP_MD5="0"
fi
}
function md5_recheck {
MESSAGE="Performing replicator diagnostics"
echo_prompt
GS_HASH_MARK='0'
MESSAGE="${UI_HASHING_REHASHING} ${UI_GRAVITY_NAME}"
echo_stat
REMOTE_DB_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI}" | sed 's/\s.*$//')
error_validate
MESSAGE="${UI_HASHING_RECOMPARING} ${UI_GRAVITY_NAME}"
echo_stat
LOCAL_DB_MD5=$(md5sum ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} | sed 's/\s.*$//')
error_validate
if [ -f ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} ]; then
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}; then
REMOTE_PH_CUSTOM_DNS="1"
MESSAGE="${UI_HASHING_REHASHING} ${UI_CUSTOM_NAME}"
echo_stat
REMOTE_CL_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} | sed 's/\s.*$//'")
error_validate
MESSAGE="${UI_HASHING_RECOMPARING} ${UI_CUSTOM_NAME}"
echo_stat
LOCAL_CL_MD5=$(md5sum ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} | sed 's/\s.*$//')
error_validate
else
MESSAGE="${UI_CUSTOM_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
else
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS}; then
REMOTE_PH_CUSTOM_DNS="1"
MESSAGE="${UI_CUSTOM_NAME} ${UI_HASHING_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
MESSAGE="${UI_CUSTOM_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_LOCAL}"
echo_warn
fi
if [ -f ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} ]; then
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo touch ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}"
create_ssh_cmd
REMOTE_CNAME_DNS="1"
MESSAGE="${UI_HASHING_REHASHING} ${UI_CNAME_NAME}"
echo_stat
REMOTE_CN_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} | sed 's/\s.*$//'")
error_validate
MESSAGE="${UI_HASHING_RECOMPARING} ${UI_CNAME_NAME}"
echo_stat
LOCAL_CN_MD5=$(md5sum ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} | sed 's/\s.*$//')
error_validate
else
MESSAGE="${UI_CNAME_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
else
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF}; then
REMOTE_CNAME_DNS="1"
MESSAGE="${UI_CNAME_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
MESSAGE="${UI_CNAME_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_LOCAL}"
echo_warn
fi
if [ -f ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} ]; then
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}; then
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo touch ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}"
create_ssh_cmd
REMOTE_SDHCP_DNS="1"
MESSAGE="${UI_HASHING_REHASHING} ${UI_SDHCP_NAME}"
echo_stat
REMOTE_SDHCP_MD5=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} | sed 's/\s.*$//'")
error_validate
MESSAGE="${UI_HASHING_RECOMPARING} ${UI_SDHCP_NAME}"
echo_stat
LOCAL_SDHCP_MD5=$(md5sum ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} | sed 's/\s.*$//')
error_validate
else
MESSAGE="${UI_SDHCP_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
else
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF}; then
REMOTE_SDHCP_DNS="1"
MESSAGE="${UI_SDHCP_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_REMOTE}"
echo_warn
fi
MESSAGE="${UI_SDHCP_NAME} ${UI_HASHING_NOT_DETECTED} ${UI_HASHING_LOCAL}"
echo_warn
fi
}
## Determine SSH Pathways
function create_ssh_cmd {
timeout --preserve-status ${CMD_TIMEOUT} ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i ${GS_SSH_PKIF} -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} "${CMD_REQUESTED}"
error_validate
}
## Determine SSH Pathways
function create_rsync_cmd {
rsync --rsync-path="${RSYNC_REPATH}" -e "${OS_SSH_CMD} -p ${GS_SSH_PORT} -i ${GS_SSH_PKIF}" ${RSYNC_SOURCE} ${RSYNC_TARGET} >/dev/null 2>&1
error_validate
}
function generate_ssh_key {
if [ -z $INPUT_REMOTE_PASS ]; then
if [ -f ${GS_SSH_PKIF} ]; then
MESSAGE="Using existing SSH key"
echo_good_clean
else
if hash ssh-keygen >/dev/null 2>&1; then
MESSAGE="Generating new SSH key"
echo_stat
ssh-keygen -q -P "" -t rsa -f ${OS_TMP}/gravity-sync.rsa >/dev/null 2>&1
error_validate
MESSAGE="Moving private key to ${GS_SSH_PKIF}"
sudo mv ${OS_TMP}/gravity-sync.rsa ${GS_SSH_PKIF}
error_validate
MESSAGE="Moving public key to ${GS_SSH_PKIF}.pub"
sudo mv ${OS_TMP}/gravity-sync.rsa.pub ${GS_SSH_PKIF}.pub
error_validate
else
MESSAGE="No SSH-KEYGEN available"
echo_warn
exit_no_change
fi
fi
fi
}
function export_ssh_key {
if [ -z $REMOTE_PASS ]; then
if [ -f ${GS_SSH_PKIF} ]; then
MESSAGE="Registering SSH key to ${REMOTE_HOST}"
echo_prompt
ssh-copy-id -f -p ${GS_SSH_PORT} -i ${GS_SSH_PKIF}.pub ${REMOTE_USER}@${REMOTE_HOST}
else
MESSAGE="Error registering SSH key to ${REMOTE_HOST}"
echo_warn
fi
fi
}
function show_target {
if [ "${GS_SSH_PORT}" != '22' ]; then
MESSAGE="Remote target ${REMOTE_USER}@${REMOTE_HOST}:${GS_SSH_PORT}"
else
MESSAGE="Remote target ${REMOTE_USER}@${REMOTE_HOST}"
fi
echo_info
}
## Logs Task
function task_logs {
start_gs_no_config
GS_TASK_TYPE='LOGS'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
logs_gs
}
## Core Logging
### Write Logs Out
function logs_export {
MESSAGE="Saving updated data hashes"
echo_stat
sudo rm -f ${GS_ETC_PATH}/*.md5
echo -e ${REMOTE_DB_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_GRAVITY_FI_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_DB_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_GRAVITY_FI_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_CL_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_CUSTOM_DNS_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_CL_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_CUSTOM_DNS_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_CN_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_CNAME_CONF_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_CN_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_CNAME_CONF_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_SDHCP_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_SDHCP_CONF_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_SDHCP_MD5} | sudo tee -a ${GS_ETC_PATH}/${GS_SDHCP_CONF_MD5_LOG} 1> /dev/null
if [ "${GS_PEERLESS_MODE}" != "1" ]; then
sudo rm -f ${OS_TMP}/*.md5
echo -e ${LOCAL_DB_MD5} | sudo tee -a ${OS_TMP}/${GS_GRAVITY_FI_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_DB_MD5} | sudo tee -a ${OS_TMP}/${GS_GRAVITY_FI_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_CL_MD5} | sudo tee -a ${OS_TMP}/${GS_CUSTOM_DNS_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_CL_MD5} | sudo tee -a ${OS_TMP}/${GS_CUSTOM_DNS_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_CN_MD5} | sudo tee -a ${OS_TMP}/${GS_CNAME_CONF_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_CN_MD5} | sudo tee -a ${OS_TMP}/${GS_CNAME_CONF_MD5_LOG} 1> /dev/null
echo -e ${LOCAL_SDHCP_MD5} | sudo tee -a ${OS_TMP}/${GS_SDHCP_CONF_MD5_LOG} 1> /dev/null
echo -e ${REMOTE_SDHCP_MD5} | sudo tee -a ${OS_TMP}/${GS_SDHCP_CONF_MD5_LOG} 1> /dev/null
error_validate
MESSAGE="Sending hashes to ${PROGRAM} peer"
echo_stat
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${OS_TMP}/*.md5"
RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${GS_ETC_PATH}/"
create_rsync_cmd
MESSAGE="Setting permissions on remote hashing files"
echo_stat
CMD_TIMEOUT=$GS_BACKUP_TIMEOUT
CMD_REQUESTED="sudo chmod 644 ${GS_ETC_PATH}/*.md5"
create_ssh_cmd
sudo rm -f ${OS_TMP}/*.md5
fi
MESSAGE="Logging successful ${GS_TASK_TYPE}"
echo_stat
echo -e "$(date) [${GS_TASK_TYPE}]" | sudo tee -a ${GS_ETC_PATH}/${GS_SYNCING_LOG} 1> /dev/null
error_validate
}
### Output Sync Logs
function logs_gs {
MESSAGE="Displaying output of previous jobs"
echo_info
echo -e "${UI_LOGGING_RECENT_COMPLETE} ${YELLOW}SMART${NC}"
tail -n 7 "${GS_ETC_PATH}/${GS_SYNCING_LOG}" | grep SMART
echo -e "${UI_LOGGING_RECENT_COMPLETE} ${YELLOW}PULL${NC}"
tail -n 7 "${GS_ETC_PATH}/${GS_SYNCING_LOG}" | grep PULL
echo -e "${UI_LOGGING_RECENT_COMPLETE} ${YELLOW}PUSH${NC}"
tail -n 7 "${GS_ETC_PATH}/${GS_SYNCING_LOG}" | grep PUSH
exit_no_change
}
## Validate Pi-hole Folders
function validate_ph_folders {
MESSAGE="${UI_VALIDATING} ${UI_CORE_APP}"
echo_stat
if [ "$LOCAL_PIHOLE_TYPE" == "default" ]; then
if [ ! -f ${LOCAL_PIHOLE_BINARY} ]; then
MESSAGE="${UI_VALIDATING_FAIL_BINARY} ${UI_CORE_APP}"
echo_fail
exit_no_change
fi
elif [ "$LOCAL_PIHOLE_TYPE" == "docker" ]; then
PH_FTL_CHECK=$(sudo docker container ls | grep "${PIHOLE_CONTAINER_IMAGE}")
if [ "$PH_FTL_CHECK" == "" ]; then
MESSAGE="${UI_VALIDATING_FAIL_CONTAINER} ${UI_CORE_APP}"
echo_fail
exit_no_change
fi
elif [ "$LOCAL_PIHOLE_TYPE" == "podman" ]; then
PH_FTL_CHECK=$(sudo podman container ls | grep "${PIHOLE_CONTAINER_IMAGE}")
if [ "$PH_FTL_CHECK" == "" ]; then
MESSAGE="${UI_VALIDATING_FAIL_CONTAINER} ${UI_CORE_APP}"
echo_fail
exit_no_change
fi
fi
if [ ! -d ${LOCAL_PIHOLE_DIRECTORY} ]; then
MESSAGE="${UI_VALIDATING_FAIL_FOLDER} ${UI_CORE_APP}"
echo_fail
exit_no_change
fi
echo_good
}
function detect_local_pihole {
MESSAGE="Detecting local ${UI_CORE_APP} installation"
echo_stat
if hash pihole 2>/dev/null; then
LOCAL_PIHOLE_TYPE="default"
echo_good
elif hash docker 2>/dev/null; then
PH_FTL_CHECK=$(sudo docker container ls | grep ${PIHOLE_CONTAINER_IMAGE})
if [ "$PH_FTL_CHECK" != "" ]; then
LOCAL_PIHOLE_TYPE="docker"
echo_good
else
LOCAL_PIHOLE_TYPE="none"
echo_fail
fi
elif hash podman 2>/dev/null; then
PH_FTL_CHECK=$(sudo podman container ls | grep ${PIHOLE_CONTAINER_IMAGE})
if [ "$PH_FTL_CHECK" != "" ]; then
LOCAL_PIHOLE_TYPE="podman"
echo_good
else
LOCAL_PIHOLE_TYPE="none"
echo_fail
fi
else
LOCAL_PIHOLE_TYPE="none"
echo_fail
fi
}
function detect_remote_pihole {
MESSAGE="Detecting remote ${UI_CORE_APP} installation"
echo_stat
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${REMOTE_PIHOLE_BINARY}; then
REMOTE_PIHOLE_TYPE="default"
echo_good
else
REMOTE_DETECT_DOCKER=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo ${REMOTE_DOCKER_BINARY} container ls | grep ${PIHOLE_CONTAINER_IMAGE}" 2>/dev/null)
REMOTE_DETECT_PODMAN=$(${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo ${REMOTE_PODMAN_BINARY} container ls | grep ${PIHOLE_CONTAINER_IMAGE}" 2>/dev/null)
if [ "${REMOTE_DETECT_DOCKER}" != "" ]; then
REMOTE_PIHOLE_TYPE="docker"
MESSAGE="${MESSAGE} - docker"
echo_good
elif [ "${REMOTE_DETECT_PODMAN}" != "" ]; then
REMOTE_PIHOLE_TYPE="podman"
MESSAGE="${MESSAGE} - podman"
echo_good
else
REMOTE_PIHOLE_TYPE="none"
echo_fail
fi
fi
}
function detect_gs_peer {
MESSAGE="Checking on peer"
echo_stat
if ${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${GS_ETC_PATH}/${GS_CONFIG_FILE}; then
MESSAGE="${PROGRAM} remote peer is configured"
echo_good
else
GS_PEERLESS_MODE="1"
MESSAGE="${PROGRAM} falling back to peerless mode"
echo_good
MESSAGE="Please configure ${PROGRAM} on remote host"
echo_warn
fi
}
## Validate DNSMASQ Folders
function validate_dns_folders {
MESSAGE="${UI_VALIDATING} ${UI_CORE_APP_DNS}"
echo_stat
if [ ! -d ${LOCAL_DNSMASQ_DIRECTORY} ]; then
MESSAGE="${UI_VALIDATING_FAIL_FOLDER} ${UI_CORE_APP_DNS}"
echo_fail
exit_no_change
fi
echo_good
}
## Validate Domain Database Permissions
function validate_gravity_permissions {
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_GRAVITY_NAME}"
echo_stat
sudo chown ${LOCAL_FILE_OWNER} ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} >/dev/null 2>&1
error_validate
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_GRAVITY_NAME}"
echo_stat
sudo chmod 664 ${LOCAL_PIHOLE_DIRECTORY}/${PH_GRAVITY_FI} >/dev/null 2>&1
error_validate
}
## Validate Local DNS Records Permissions
function validate_custom_permissions {
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_CUSTOM_NAME}"
echo_stat
sudo chown ${LOCAL_FILE_OWNER} ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} >/dev/null 2>&1
error_validate
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_CUSTOM_NAME}"
echo_stat
sudo chmod 644 ${LOCAL_PIHOLE_DIRECTORY}/${PH_CUSTOM_DNS} >/dev/null 2>&1
error_validate
}
## Validate Local DNS CNAME Permissions
function validate_cname_permissions {
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_CNAME_NAME}"
echo_stat
sudo chown ${LOCAL_FILE_OWNER} ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} >/dev/null 2>&1
error_validate
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_CNAME_NAME}"
echo_stat
sudo chmod 644 ${LOCAL_DNSMASQ_DIRECTORY}/${PH_CNAME_CONF} >/dev/null 2>&1
error_validate
}
function validate_sdhcp_permissions {
MESSAGE="${UI_SET_LOCAL_FILE_OWNERSHIP} ${UI_SDHCP_NAME}"
echo_stat
sudo chown ${LOCAL_FILE_OWNER} ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} >/dev/null 2>&1
error_validate
MESSAGE="${UI_SET_FILE_PERMISSION} ${UI_SDHCP_NAME}"
echo_stat
sudo chmod 644 ${LOCAL_DNSMASQ_DIRECTORY}/${PH_SDHCP_CONF} >/dev/null 2>&1
error_validate
}
## Validate Intent
function intent_validate {
PHASER=$((( RANDOM % 4 ) + 1 ))
if [ "$PHASER" = "1" ]; then
INTENT="FIRE PHOTON TORPEDOES"
elif [ "$PHASER" = "2" ]; then
INTENT="FIRE ALL PHASERS"
elif [ "$PHASER" = "3" ]; then
INTENT="EJECT THE WARP CORE"
elif [ "$PHASER" = "4" ]; then
INTENT="ENGAGE TRACTOR BEAM"
fi
MESSAGE="Type ${INTENT} to confirm"
echo_need
read -r INPUT_INTENT
if [ "${INPUT_INTENT}" != "${INTENT}" ]; then
MESSAGE="${GS_TASK_TYPE} excited"
echo_info
exit_no_change
fi
}
## Sudo Creation Task
function task_sudo {
start_gs_no_config
GS_TASK_TYPE='SUDO'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
MESSAGE="Creating sudoers.d template file"
echo_stat
NEW_SUDO_USER=$(whoami)
echo -e "${NEW_SUDO_USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee ${GS_LOCAL_REPO}/templates/gs-nopasswd.sudo 1> /dev/null
error_validate
MESSAGE="Installing sudoers.d file on $HOSTNAME"
echo_stat
sudo install -m 0440 ${GS_LOCAL_REPO}/templates/gs-nopasswd.sudo /etc/sudoers.d/gs-nopasswd
error_validate
exit_with_changes
}
function validate_sudo_status {
OS_CURRENT_USER=$(whoami)
if [ ! "$EUID" -ne 0 ]; then
OS_LOCAL_ADMIN=""
else
/usr/bin/sudo -u ${OS_CURRENT_USER} --validate
OS_SUDO_CHECK=$?
if [ $OS_SUDO_CHECK -ne 0 ]; then
OS_LOCAL_ADMIN="nosudo"
else
OS_LOCAL_ADMIN="sudo"
fi
fi
if [ "$OS_LOCAL_ADMIN" == "nosudo" ]; then
GS_TASK_TYPE='ROOT'
MESSAGE="${MESSAGE} ${GS_TASK_TYPE}"
echo_fail
MESSAGE="${OS_CURRENT_USER} has insufficient user rights for ${PROGRAM}"
echo_warn
exit_no_change
fi
}
## Configure Task
function task_configure {
start_gs_no_config
GS_TASK_TYPE='CONFIGURE'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
if [[ ${INPUT_SSH} =~ ^[0-9]+$ ]]; then
GS_SSH_PORT=${INPUT_SSH}
MESSAGE="TARGET HOST SSH PORT SET TO ${GS_SSH_PORT}"
echo_warn
fi
if [ -f ${GS_ETC_PATH}/${GS_CONFIG_FILE} ]; then
config_delete
else
config_generate
fi
exit_with_changes
}
## Generate New Configuration
function config_generate {
MESSAGE="Creating new ${GS_CONFIG_FILE}"
echo_stat
sudo cp ${GS_LOCAL_REPO}/templates/${GS_CONFIG_FILE}.example ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
echo_blank
echo -e " Welcome to the ${PURPLE}${PROGRAM}${NC} Configuration Wizard"
echo -e " Please read through ${BLUE}https://github.com/vmstan/gravity-sync/wiki${NC} before you continue"
echo -e " Make sure that ${UI_CORE_APP} is running on this system before your configure ${PROGRAM}"
echo_blank
MESSAGE="${PROGRAM} Remote Host Settings"
echo_info
MESSAGE="Remote ${UI_CORE_APP} host address"
echo_prompt
MESSAGE="IP"
echo_need
read -r INPUT_REMOTE_HOST
if [ "${INPUT_REMOTE_HOST}" == "" ]; then
MESSAGE="Remote host cannot be blank!"
echo_fail
exit_no_change
fi
MESSAGE="${UI_CONFIG_SAVING} ${INPUT_REMOTE_HOST} host to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/REMOTE_HOST=''/c\REMOTE_HOST='${INPUT_REMOTE_HOST}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="Remote ${UI_CORE_APP} host username"
echo_prompt
MESSAGE="User"
echo_need
read -r INPUT_REMOTE_USER
if [ "${INPUT_REMOTE_USER}" == "" ]; then
MESSAGE="User name cannot be blank!"
echo_fail
exit_no_change
fi
MESSAGE="${UI_CONFIG_SAVING} ${INPUT_REMOTE_USER}@${INPUT_REMOTE_HOST} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/REMOTE_USER=''/c\REMOTE_USER='${INPUT_REMOTE_USER}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="${PROGRAM} SSH Key Settings"
echo_info
generate_ssh_key
MESSAGE="${UI_CORE_LOADING} ${GS_CONFIG_FILE}"
echo_stat
if [ "${GS_SSH_PORT}" != '22' ]; then
MESSAGE="${UI_CONFIG_SAVING} custom SSH port to ${GS_CONFIG_FILE}"
echo_stat
echo -e "GS_SSH_PORT='${GS_SSH_PORT}'" | sudo tee -a ${GS_ETC_PATH}/${GS_CONFIG_FILE} 1> /dev/null
error_validate
fi
# shellcheck source=/etc/gravity-sync/gravity-sync.conf
source ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
export_ssh_key
MESSAGE="SSH key registered to ${INPUT_REMOTE_HOST}"
echo_good_clean
MESSAGE="${UI_CORE_APP} Installation Settings"
echo_info
detect_local_pihole
if [ "${LOCAL_PIHOLE_TYPE}" == "default" ]; then
MESSAGE="Default install of ${UI_CORE_APP} detected"
echo_good_clean
elif [ "${LOCAL_PIHOLE_TYPE}" == "docker" ]; then
MESSAGE="Docker container ${UI_CORE_APP} install detected"
echo_good_clean
elif [ "${LOCAL_PIHOLE_TYPE}" == "podman" ]; then
MESSAGE="Podman container ${UI_CORE_APP} install detected"
echo_good_clean
elif [ "${LOCAL_PIHOLE_TYPE}" == "none" ]; then
MESSAGE="No local ${UI_CORE_APP} installed detected"
echo_warn
end_config_no_pi
fi
detect_remote_pihole
if [ "${REMOTE_PIHOLE_TYPE}" == "default" ]; then
MESSAGE="Remote install of ${UI_CORE_APP} detected"
echo_good_clean
elif [ "${REMOTE_PIHOLE_TYPE}" == "docker" ]; then
MESSAGE="Remote Docker container of ${UI_CORE_APP} detected"
echo_good_clean
elif [ "${REMOTE_PIHOLE_TYPE}" == "podman" ]; then
MESSAGE="Remote Podman container of ${UI_CORE_APP} detected"
echo_good_clean
elif [ "${REMOTE_PIHOLE_TYPE}" == "none" ]; then
MESSAGE="No remote ${UI_CORE_APP} installed detected"
echo_warn
end_config_no_pi
fi
if [ "${LOCAL_PIHOLE_TYPE}" == "default" ] && [ "${REMOTE_PIHOLE_TYPE}" == "default" ]; then
end_config
else
advanced_config_generate
fi
}
function end_config {
echo_blank
echo -e " Configuration has been completed successfully, once ${PROGRAM} has been installed your other"
echo -e " node, your next step is to push all of the of data from the currently authoritative"
echo -e " ${UI_CORE_APP} instance to the other."
echo -e " ex: ${YELLOW}gravity-sync push${NC}"
echo_blank
echo -e " If that completes successfully you can automate future sync jobs to run at a regular interval on"
echo -e " both of your ${PROGRAM} peers."
echo -e " ex: ${YELLOW}gravity-sync auto${NC}"
echo_blank
}
function end_config_no_pi {
echo_blank
echo -e " Configuration could not be completed successfully, as no instances of ${UI_CORE_APP} could be detected"
echo -e " on one or more of your systems. Please make sure they are running on both peers and try again."
echo_blank
}
## Advanced Configuration Options
function advanced_config_generate {
if [ "${LOCAL_PIHOLE_TYPE}" == "docker" ] || [ "${LOCAL_PIHOLE_TYPE}" == "podman" ]; then
MESSAGE="Local Container Image Configuration"
echo_info
MESSAGE="Displaying running containers on $HOSTNAME"
echo_good_clean
if [ "${LOCAL_PIHOLE_TYPE}" == "docker" ]; then
sudo docker ps -a --format 'table {{.ID}}\t{{.Names}}'
elif [ "${LOCAL_PIHOLE_TYPE}" == "podman" ]; then
sudo podman container ls
fi
MESSAGE="Enter local ${UI_CORE_APP} container name"
echo_prompt
MESSAGE="ex, 'pihole'"
echo_need
read -r INPUT_LOCAL_DOCKER_CONTAINER
if [ "${INPUT_LOCAL_DOCKER_CONTAINER}" == "" ]; then
MESSAGE="Container name cannot be blank!"
echo_fail
exit_no_change
fi
MESSAGE="${UI_CONFIG_SAVING} ${UI_CONFIG_LOCAL} ${UI_CONFIG_CONTAINER_NAME} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# LOCAL_DOCKER_CONTAINER=''/c\LOCAL_DOCKER_CONTAINER='${INPUT_LOCAL_DOCKER_CONTAINER}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="Examining local container configuration"
echo_stat
if [ "${LOCAL_PIHOLE_TYPE}" == "docker" ]; then
sudo docker container inspect ${INPUT_LOCAL_DOCKER_CONTAINER} | grep -i -B 1 '"Destination": "/etc/pihole"' > ${OS_TMP}/local_container_pihole_etc.log; sed -i '$d' ${OS_TMP}/local_container_pihole_etc.log; sed -i 's/"Source": "//' ${OS_TMP}/local_container_pihole_etc.log; sed -i 's/",//' ${OS_TMP}/local_container_pihole_etc.log; sed -i 's/ //g' ${OS_TMP}/local_container_pihole_etc.log
sudo docker container inspect ${INPUT_LOCAL_DOCKER_CONTAINER} | grep -i -B 1 '"Destination": "/etc/dnsmasq.d"' > ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i '$d' ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i 's/"Source": "//' ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i 's/",//' ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i 's/ //g' ${OS_TMP}/local_container_dnsmasq_etc.log
elif [ "${LOCAL_PIHOLE_TYPE}" == "podman" ]; then
sudo podman container inspect ${INPUT_LOCAL_DOCKER_CONTAINER} | grep -i -B 1 '"Destination": "/etc/pihole"' > ${OS_TMP}/local_container_pihole_etc.log; sed -i '$d' ${OS_TMP}/local_container_pihole_etc.log; sed -i 's/"Source": "//' ${OS_TMP}/local_container_pihole_etc.log; sed -i 's/",//' ${OS_TMP}/local_container_pihole_etc.log; sed -i 's/ //g' ${OS_TMP}/local_container_pihole_etc.log
sudo podman container inspect ${INPUT_LOCAL_DOCKER_CONTAINER} | grep -i -B 1 '"Destination": "/etc/dnsmasq.d"' > ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i '$d' ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i 's/"Source": "//' ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i 's/",//' ${OS_TMP}/local_container_dnsmasq_etc.log; sed -i 's/ //g' ${OS_TMP}/local_container_dnsmasq_etc.log
fi
INPUT_LOCAL_PIHOLE_DIRECTORY=$(cat ${OS_TMP}/local_container_pihole_etc.log)
INPUT_LOCAL_DNSMASQ_DIRECTORY=$(cat ${OS_TMP}/local_container_dnsmasq_etc.log)
echo_good
MESSAGE="${UI_CONFIG_SAVING} ${UI_CONFIG_LOCAL} ${UI_CORE_APP} ${UI_CONFIG_ETC_VOLUME_PATH} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# LOCAL_PIHOLE_DIRECTORY=''/c\LOCAL_PIHOLE_DIRECTORY='${INPUT_LOCAL_PIHOLE_DIRECTORY}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="${UI_CONFIG_SAVING} ${UI_CONFIG_LOCAL} ${UI_CORE_APP_DNS} ${UI_CONFIG_ETC_VOLUME_PATH} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# LOCAL_DNSMASQ_DIRECTORY=''/c\LOCAL_DNSMASQ_DIRECTORY='${INPUT_LOCAL_DNSMASQ_DIRECTORY}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="${UI_CONFIG_SAVING} ${UI_CONFIG_LOCAL} ${UI_CONFIG_VOLUME_OWNER} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# LOCAL_FILE_OWNER=''/c\LOCAL_FILE_OWNER='999:999'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
fi
if [ "${REMOTE_PIHOLE_TYPE}" == "docker" ] || [ "${REMOTE_PIHOLE_TYPE}" == "podman" ]; then
MESSAGE="Remote Container Image Configuration"
echo_info
MESSAGE="Querying running containers on ${REMOTE_HOST}"
echo_stat
if [ "${REMOTE_PIHOLE_TYPE}" == "docker" ]; then
${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo docker ps -a --format 'table {{.Image}}\t{{.Names}}' > /tmp/gs_local_container.log"
error_validate
elif [ "${REMOTE_PIHOLE_TYPE}" == "podman" ]; then
${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo podman container ls > /tmp/gs_local_container.log"
error_validate
fi
MESSAGE="Retrieving container list from ${REMOTE_HOST}"
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${OS_TMP}/gs_local_container.log"
RSYNC_TARGET="${OS_TMP}/gs_remote_container.log"
create_rsync_cmd
MESSAGE="Displaying running containers on ${REMOTE_HOST}"
echo_good_clean
cat ${OS_TMP}/gs_remote_container.log
MESSAGE="Enter remote ${UI_CORE_APP} container name"
echo_prompt
MESSAGE="ex, 'pihole'"
echo_need
read -r INPUT_REMOTE_DOCKER_CONTAINER
if [ "${INPUT_REMOTE_DOCKER_CONTAINER}" == "" ]; then
MESSAGE="Container name cannot be blank!"
echo_fail
exit_no_change
fi
MESSAGE="${UI_CONFIG_SAVING} remote host ${UI_CONFIG_CONTAINER_NAME} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# REMOTE_DOCKER_CONTAINER=''/c\REMOTE_DOCKER_CONTAINER='${INPUT_REMOTE_DOCKER_CONTAINER}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="Examining remote container configuration"
echo_stat
if [ "${REMOTE_PIHOLE_TYPE}" == "docker" ]; then
${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo docker container inspect ${INPUT_REMOTE_DOCKER_CONTAINER} | grep -i -B 1 '\"Destination\": \"/etc/pihole\"' > ${OS_TMP}/local_container_pihole_etc.log"
${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo docker container inspect ${INPUT_REMOTE_DOCKER_CONTAINER} | grep -i -B 1 '\"Destination\": \"/etc/dnsmasq.d\"' > ${OS_TMP}/local_container_dnsmasq_etc.log"
error_validate
elif [ "${REMOTE_PIHOLE_TYPE}" == "podman" ]; then
${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo podman container inspect ${INPUT_REMOTE_DOCKER_CONTAINER} | grep -i -B 1 '\"Destination\": \"/etc/pihole\"' > ${OS_TMP}/local_container_pihole_etc.log"
${OS_SSH_CMD} -p ${GS_SSH_PORT} -i "${GS_SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "sudo podman container inspect ${INPUT_REMOTE_DOCKER_CONTAINER} | grep -i -B 1 '\"Destination\": \"/etc/dnsmasq.d\"' > ${OS_TMP}/local_container_dnsmasq_etc.log"
error_validate
fi
MESSAGE="Retrieving remote ${UI_CORE_APP} configuration settings"
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${OS_TMP}/local_container_pihole_etc.log"
RSYNC_TARGET="${OS_TMP}/remote_container_pihole_etc.log"
create_rsync_cmd
MESSAGE="Retrieving remote ${UI_CORE_APP_DNS} configuration settings"
RSYNC_REPATH="sudo rsync"
RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${OS_TMP}/local_container_dnsmasq_etc.log"
RSYNC_TARGET="${OS_TMP}/remote_container_dnsmasq_etc.log"
create_rsync_cmd
sed -i '$d' ${OS_TMP}/remote_container_pihole_etc.log; sed -i 's/"Source": "//' ${OS_TMP}/remote_container_pihole_etc.log; sed -i 's/",//' ${OS_TMP}/remote_container_pihole_etc.log; sed -i 's/ //g' ${OS_TMP}/remote_container_pihole_etc.log
sed -i '$d' ${OS_TMP}/remote_container_dnsmasq_etc.log; sed -i 's/"Source": "//' ${OS_TMP}/remote_container_dnsmasq_etc.log; sed -i 's/",//' ${OS_TMP}/remote_container_dnsmasq_etc.log; sed -i 's/ //g' ${OS_TMP}/remote_container_dnsmasq_etc.log
INPUT_REMOTE_PIHOLE_DIRECTORY=$(cat ${OS_TMP}/remote_container_pihole_etc.log)
INPUT_REMOTE_DNSMASQ_DIRECTORY=$(cat ${OS_TMP}/remote_container_dnsmasq_etc.log)
echo_good
MESSAGE="${UI_CONFIG_SAVING} remote host ${UI_CORE_APP} ${UI_CONFIG_ETC_VOLUME_PATH} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# REMOTE_PIHOLE_DIRECTORY=''/c\REMOTE_PIHOLE_DIRECTORY='${INPUT_REMOTE_PIHOLE_DIRECTORY}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="${UI_CONFIG_SAVING} remote host ${UI_CORE_APP_DNS} ${UI_CONFIG_ETC_VOLUME_PATH} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# REMOTE_DNSMASQ_DIRECTORY=''/c\REMOTE_DNSMASQ_DIRECTORY='${INPUT_REMOTE_DNSMASQ_DIRECTORY}'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
MESSAGE="${UI_CONFIG_SAVING} remote host ${UI_CONFIG_VOLUME_OWNER} to ${GS_CONFIG_FILE}"
echo_stat
sudo sed -i "/# REMOTE_FILE_OWNER=''/c\REMOTE_FILE_OWNER='999:999'" ${GS_ETC_PATH}/${GS_CONFIG_FILE}
error_validate
fi
end_config
}
## Delete Existing Configuration
function config_delete {
# shellcheck source=/etc/gravity-sync/gravity-sync.conf
if [ -n "${GS_SSH_PORT}" ]; then
_GS_SSH_PORT=${GS_SSH_PORT}
fi
source ${GS_ETC_PATH}/${GS_CONFIG_FILE}
if [ -n "${_GS_SSH_PORT}" ]; then
GS_SSH_PORT=${_GS_SSH_PORT}
fi
MESSAGE="${GS_CONFIG_FILE} ${UI_CONFIG_ALREADY}"
echo_warn
MESSAGE="${UI_CONFIG_CONFIRM}"
echo_prompt
intent_validate
MESSAGE="${UI_CONFIG_ERASING} ${GS_CONFIG_FILE}"
echo_stat
sudo mv ${GS_ETC_PATH}/${GS_CONFIG_FILE} ${GS_ETC_PATH}/${GS_CONFIG_FILE}.${GS_BACKUP_EXT}
error_validate
config_generate
}
## Master Branch
function update_gs {
bash ${GS_LOCAL_REPO}/update.sh
}
## Show Version
function show_version {
if [ -f ${GS_LOCAL_REPO}/dev ]; then
GS_DEV_VERSION="Beta"
else
GS_DEV_VERSION=""
fi
MESSAGE="Running version: ${GREEN}${GS_VERSION}${NC} ${GS_DEV_VERSION}"
echo_info
GS_GIT_VERSION=$(curl -sf https://raw.githubusercontent.com/vmstan/gravity-sync/master/VERSION)
if [ -z "$GS_GIT_VERSION" ]; then
MESSAGE="Latest version: ${RED}Unknown${NC}"
else
if [[ ! "${GS_GIT_VERSION}" > "${GS_VERSION}" ]]; then
MESSAGE="Latest version: ${GREEN}${GS_GIT_VERSION}${NC}"
else
MESSAGE="Update available: ${RED}${GS_GIT_VERSION}${NC}"
fi
fi
echo_info
}
function show_info {
echo -e "${YELLOW}Local Software Versions${NC}"
echo -e "${BLUE}${UI_CORE_APP}${NC}"
if [ "${LOCAL_PIHOLE_TYPE}" == "default" ]; then
pihole version
elif [ "${LOCAL_PIHOLE_TYPE}" == "docker" ]; then
sudo docker exec -it pihole pihole -v
elif [ "${LOCAL_PIHOLE_TYPE}" == "podman" ]; then
sudo podman exec -it pihole pihole -v
fi
if [ -f /etc/os-release ]; then
. /etc/os-release
OS_OS=$NAME
OS_VER=$VERSION_ID
echo -e "${BLUE}${OS_OS} ${OS_VER}${NC}"
fi
uname -srm
echo -e "bash $BASH_VERSION"
ssh -V
rsync --version | grep version
sudo --version | grep "Sudo version"
git --version
if hash docker 2>/dev/null; then
docker --version
fi
if hash podman 2>/dev/null; then
podman --version
fi
echo -e ""
echo -e "${YELLOW}Global Instance Settings${NC}"
if [ ${GS_SSH_PORT} == '22' ]; then
echo -e "SSH Port: 22 (default)"
else
echo -e "SSH Port: ${GS_SSH_PORT} (custom)"
fi
echo -e "SSH Key: ${GS_SSH_PKIF}"
if systemctl is-active --quiet gravity-sync.timer; then
echo -e "Automated Replication: Enabled"
else
echo -e "Automated Replication: Disabled"
fi
echo -e ""
echo -e "${YELLOW}Local Instance Settings${NC}"
echo -e "Local Hostname: $HOSTNAME"
echo -e "Local ${UI_CORE_APP} Type: ${LOCAL_PIHOLE_TYPE}"
echo -e "Local ${UI_CORE_APP} Config Directory: ${LOCAL_PIHOLE_DIRECTORY}"
echo -e "Local ${UI_CORE_APP_DNS} Config Directory: ${LOCAL_DNSMASQ_DIRECTORY}"
echo -e "Local ${PROGRAM} Binary: ${GS_FILEPATH}"
echo -e "Local ${PROGRAM} Config Directory: ${GS_ETC_PATH}"
if [ "${LOCAL_PIHOLE_TYPE}" == "default" ]; then
echo -e "Local ${UI_CORE_APP} Binary Directory: ${LOCAL_PIHOLE_BINARY}"
elif [ "${LOCAL_PIHOLE_TYPE}" == "docker" ]; then
echo -e "Local ${UI_CORE_APP} Container Name: ${LOCAL_DOCKER_CONTAINER}"
echo -e "Local Docker Binary Directory: ${LOCAL_DOCKER_BINARY}"
elif [ "${LOCAL_PIHOLE_TYPE}" == "podman" ]; then
echo -e "Local ${UI_CORE_APP} Container Name: ${LOCAL_DOCKER_CONTAINER}"
echo -e "Local Podman Binary Directory: ${LOCAL_PODMAN_BINARY}"
fi
echo -e "Local File Owner Settings: ${LOCAL_FILE_OWNER}"
echo -e ""
echo -e "${YELLOW}Remote Instance Settings${NC}"
echo -e "Remote Hostname/IP: ${REMOTE_HOST}"
echo -e "Remote Username: ${REMOTE_USER}"
echo -e "Remote ${UI_CORE_APP} Type: ${REMOTE_PIHOLE_TYPE}"
echo -e "Remote ${UI_CORE_APP} Config Directory: ${REMOTE_PIHOLE_DIRECTORY}"
echo -e "Remote ${UI_CORE_APP_DNS} Config Directory: ${REMOTE_DNSMASQ_DIRECTORY}"
if [ "${REMOTE_PIHOLE_TYPE}" == "default" ]; then
echo -e "Remote ${UI_CORE_APP} Binary Directory: ${REMOTE_PIHOLE_BINARY}"
elif [ "${REMOTE_PIHOLE_TYPE}" == "docker" ]; then
echo -e "Remote ${UI_CORE_APP} Container Name: ${REMOTE_DOCKER_CONTAINER}"
echo -e "Remote Docker Binary Directory: ${REMOTE_DOCKER_BINARY}"
elif [ "${REMOTE_PIHOLE_TYPE}" == "podman" ]; then
echo -e "Remote ${UI_CORE_APP} Container Name: ${REMOTE_DOCKER_CONTAINER}"
echo -e "Remote Podman Binary Directory: ${REMOTE_PODMAN_BINARY}"
fi
echo -e "Remote File Owner Settings: ${REMOTE_FILE_OWNER}"
}
## Dev Task
function task_dev {
start_gs_no_config
GS_TASK_TYPE='DEV'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
if [ -f ${GS_LOCAL_REPO}/dev ]; then
MESSAGE="Disabling ${GS_TASK_TYPE}"
echo_stat
sudo rm -f ${GS_LOCAL_REPO}/dev
error_validate
else
MESSAGE="Enabling ${GS_TASK_TYPE}"
echo_stat
sudo touch ${GS_LOCAL_REPO}/dev
error_validate
MESSAGE="Checking available branches"
echo_stat
(cd ${GS_LOCAL_REPO} || exit; sudo git fetch --all >/dev/null 2>&1)
error_validate
(cd ${GS_LOCAL_REPO} || exit; sudo git branch -r)
MESSAGE="Select GitHub branch to update against"
echo_need
read -r INPUT_BRANCH
echo -e "BRANCH='${INPUT_BRANCH}'" | sudo tee ${GS_LOCAL_REPO}/dev 1> /dev/null
fi
update_gs
exit_with_changes
}
## Update Task
function task_update {
start_gs_no_config
GS_TASK_TYPE='UPDATE'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
update_gs
exit_with_changes
}
## Version Task
function task_version {
start_gs_no_config
GS_TASK_TYPE='VERSION'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
show_version
exit_no_change
}
## Info Task
function task_info {
start_gs
GS_TASK_TYPE='INFO'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
show_info
exit_no_change
}
## Automate Task
function task_automate {
start_gs
GS_TASK_TYPE='AUTOMATE'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
sudo cp ${GS_LOCAL_REPO}/templates/gravity-sync.service ${OS_TMP}/gravity-sync.service
sudo cp ${GS_LOCAL_REPO}/templates/gravity-sync.timer ${OS_TMP}/gravity-sync.timer
MESSAGE="Customizing service file username"
echo_stat
OS_CURRENT_USER=$(whoami)
sudo sed -i "/User=unknown/c\User=${OS_CURRENT_USER}" ${OS_TMP}/gravity-sync.service
error_validate
MESSAGE="Customizing service file executable path"
echo_stat
sudo sed -i "/ExecStart=/c\ExecStart=${GS_FILEPATH}" ${OS_TMP}/gravity-sync.service
error_validate
if [ "${GS_SYSLOG_REDIRECT}" != '' ]; then
MESSAGE="Customizing standard output location"
echo_stat
sudo sed -i "/Type=simple/a\StandardOutput=file:${GS_SYSLOG_REDIRECT}" ${OS_TMP}/gravity-sync.service
sudo sed -i "/Type=simple/a\StandardError=file:${GS_SYSLOG_REDIRECT}" ${OS_TMP}/gravity-sync.service
error_validate
fi
if [ "${INPUT_SPEED}" == 'slow' ] || [ "${INPUT_SPEED}" == 'hour' ]; then
MESSAGE="Randomizing service timers (hour)"
echo_stat
ACTIVE_REP=$(shuf -i 50-55 -n1)
RANDOM_REP=$(shuf -i 7-10 -n1)
elif [ "${INPUT_SPEED}" == 'half' ]; then
MESSAGE="Randomizing service timers (half)"
echo_stat
ACTIVE_REP=$(shuf -i 25-30 -n1)
RANDOM_REP=$(shuf -i 5-7 -n1)
elif [ "${INPUT_SPEED}" == 'quad' ]; then
MESSAGE="Randomizing service timers (quad)"
echo_stat
ACTIVE_REP=$(shuf -i 12-15 -n1)
RANDOM_REP=$(shuf -i 3-5 -n1)
else
MESSAGE="Randomizing service timers"
echo_stat
ACTIVE_REP=$(shuf -i 3-10 -n1)
RANDOM_REP=$(shuf -i 2-5 -n1)
fi
sudo sed -i "/OnUnitInactiveSec=5m/c\OnUnitInactiveSec=${ACTIVE_REP}m" ${OS_TMP}/gravity-sync.timer
sudo sed -i "/RandomizedDelaySec=5m/c\RandomizedDelaySec=${RANDOM_REP}m" ${OS_TMP}/gravity-sync.timer
error_validate
if systemctl is-active --quiet gravity-sync.timer; then
MESSAGE="Stopping existing systemd service"
echo_stat
sudo systemctl stop gravity-sync
error_validate
fi
MESSAGE="Moving systemd timer into place"
echo_stat
sudo cp ${OS_TMP}/gravity-sync.timer ${OS_DAEMON_PATH}
error_validate
MESSAGE="Moving systemd service into place"
echo_stat
sudo cp ${OS_TMP}/gravity-sync.service ${OS_DAEMON_PATH}
error_validate
MESSAGE="Reloading systemd daemon"
echo_stat
sudo systemctl daemon-reload --quiet
error_validate
MESSAGE="Enabling ${PROGRAM} timer"
echo_stat
sudo systemctl enable gravity-sync.timer --quiet
error_validate
MESSAGE="Starting ${PROGRAM} service"
echo_stat
sudo systemctl start gravity-sync --quiet
error_validate
exit_with_changes
}
function task_disable_automate {
start_gs_no_config
GS_TASK_TYPE='DISABLE'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
kill_automation_service
exit_with_changes
}
function task_monitor {
start_gs_no_config
GS_TASK_TYPE='MONITOR'
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
MESSAGE="Press CTRL+Z to exit MONITOR mode"
echo_warn
sudo journalctl -fu gravity-sync
}
function kill_automation_service {
if systemctl is-active --quiet gravity-sync.timer; then
MESSAGE="Stopping ${PROGRAM} timer"
echo_stat
sudo systemctl stop gravity-sync
error_validate
MESSAGE="Disabling ${PROGRAM} automation service"
echo_stat
sudo systemctl disable gravity-sync --quiet
error_validate
MESSAGE="Removing systemd timer"
echo_stat
sudo rm -f ${OS_DAEMON_PATH}/gravity-sync.timer
error_validate
MESSAGE="Removing systemd service"
echo_stat
sudo rm -f ${OS_DAEMON_PATH}/gravity-sync.service
error_validate
MESSAGE="Reloading systemd daemon"
echo_stat
sudo systemctl daemon-reload --quiet
error_validate
fi
}
## Purge Task
function task_purge {
start_gs_no_config
GS_TASK_TYPE="PURGE"
MESSAGE="${MESSAGE}: ${GS_TASK_TYPE}"
echo_good
echo_blank
echo -e " THIS WILL REMOVE YOUR ENTIRE GRAVITY SYNC INSTALLATION"
echo -e " ${UI_CORE_APP} binaries, configuration and services ARE NOT impacted!"
echo -e " Your devices will continue to resolve and block DNS requests,"
echo -e " but your ${UI_GRAVITY_NAME} and ${UI_CUSTOM_NAME} WILL NOT sync anymore,"
echo -e " until you reconfigure ${PROGRAM} on this device."
echo_blank
echo -e " In order to fully remove ${PROGRAM} from your infrastructure, you will also"
echo -e " need to run this same command from the peer instance as well."
echo_blank
intent_validate
kill_automation_service
MESSAGE="Removing ${PROGRAM} backup files"
echo_stat
sudo rm -f ${OS_TMP}/*.${GS_BACKUP_EXT}
error_validate
MESSAGE="Removing ${PROGRAM} configuration and logs"
echo_stat
sudo rm -fr ${GS_ETC_PATH}
error_validate
MESSAGE="Removing ${PROGRAM} binary"
echo_stat
sudo rm ${GS_FILEPATH}
error_validate
exit_with_changes
}
## No Changes Made
function exit_no_change {
GS_RUN_END=$SECONDS
((GS_RUNTIME=GS_RUN_END-GS_RUN_START))
if [ "${GS_TASK_TYPE}" == "" ]; then
MESSAGE="${PROGRAM} ${UI_EXIT_ABORT} ${UI_EXIT_CALC_END} ${GS_RUNTIME} ${UI_EXIT_CALC_TIMER}"
else
MESSAGE="${PROGRAM} ${GS_TASK_TYPE} ${UI_EXIT_ABORT} ${UI_EXIT_CALC_END} ${GS_RUNTIME} ${UI_EXIT_CALC_TIMER}"
fi
echo_grav
exit 0
}
## Changes Made
function exit_with_changes {
GS_RUN_END=$SECONDS
((GS_RUNTIME=GS_RUN_END-GS_RUN_START))
if [ "${GS_TASK_TYPE}" == "" ]; then
MESSAGE="${PROGRAM} ${UI_EXIT_COMPLETE} ${UI_EXIT_CALC_END} ${GS_RUNTIME} ${UI_EXIT_CALC_TIMER}"
else
MESSAGE="${PROGRAM} ${GS_TASK_TYPE} ${UI_EXIT_COMPLETE} ${UI_EXIT_CALC_END} ${GS_RUNTIME} ${UI_EXIT_CALC_TIMER}"
fi
echo_grav
exit 0
}
## List GS Arguments
function list_gs_arguments {
echo -e "Usage: $0 [options]"
echo -e "Example: '$0 pull'"
echo_blank
echo -e "Setup Options:"
echo -e " ${YELLOW}config${NC} Creates a new ${PROGRAM} configuration file"
echo -e " ${YELLOW}version${NC} Lists the installed version of ${PROGRAM} and checks for updates"
echo -e " ${YELLOW}update${NC} Upgrades ${PROGRAM} to the latest available version on GitHub"
echo -e " ${YELLOW}dev${NC} Sets upgrade command to use a development version of ${PROGRAM} (toggle on/off)"
echo -e " ${YELLOW}sudo${NC} Enables password-less sudo for current user"
echo -e " ${YELLOW}purge${NC} Uninstalls ${PROGRAM} from this system"
echo_blank
echo -e "Replication Options:"
echo -e " ${YELLOW}smart${NC} Reviews all ${UI_CORE_APP} changes syncs them (default)"
echo -e " ${YELLOW}pull${NC} Syncs only the remote ${UI_CORE_APP} configuration to this server"
echo -e " ${YELLOW}push${NC} Syncs only the local ${UI_CORE_APP} configuration to the remote"
echo -e " ${YELLOW}compare${NC} Checks for ${UI_CORE_APP} differences without making changes"
echo_blank
echo -e "Automation Options:"
echo -e " ${YELLOW}auto${NC} Schedules ${PROGRAM} replication tasks using systemd timers"
echo -e " ${YELLOW}monitor${NC} Monitors the ${PROGRAM} replication job in real time"
echo -e " ${YELLOW}disable${NC} Disables the ${PROGRAM} automated replication task"
echo_blank
echo -e "Debug Options:"
echo -e " ${YELLOW}logs${NC} Shows the recent successful replication jobs/times"
echo -e " ${YELLOW}info${NC} Shows information about the current configuration"
echo_blank
exit_no_change
}
# SCRIPT EXECUTION ###########################
case "${1}" in
"" | "smart" | "sync" ) task_smart;;
"pull" ) task_pull;;
"push" ) task_push;;
"compare" ) task_compare;;
"config" | "conf" | "configure" ) INPUT_SSH=$2; task_configure;;
"auto" | "automate" ) INPUT_SPEED=$2 task_automate;;
"disable" | "stop" ) task_disable_automate;;
"monitor" | "follow" ) task_monitor;;
"purge" | "uninstall" | "remove" ) task_purge;;
"sudo" ) task_sudo;;
"version" | "ver" ) task_version;;
"info" ) task_info;;
"update" | "upgrade" | "up" ) task_update;;
"dev" | "beta" ) task_dev;;
"logs" | "log" ) task_logs;;
* ) task_invalid;;
esac
# END OF SCRIPT ##############################