From 9dae851a9ca2460ab59e1695b86e56fa745a287b Mon Sep 17 00:00:00 2001 From: Michael Stanclift Date: Wed, 7 Oct 2020 17:45:39 -0500 Subject: [PATCH] 3.0.0 (#100) * first! * folders * break out colors * home * seperate update * breakout pull * move file * breakout push * breakout smart * restore breakout * include restore * breakout logs validate ssh * breakout config exit intent * source exit * breakout backup automation and purge * breakout root * file headers * create push pull restore smart functions * consolidate options * include root check * includes * includes 2 * automate * rearrange imports * header * ssh rsync * new install steps * ticks * yellow * host check * check for git * ticks * check for pihole * check sudo powers * create sudo file * write output * pihole directory * install sudo file * purge error message * comment cleanup * installer * header * instructions * l * read p * header * skip prompts * sudo no longer in config * compare exit code * exit with change * 3.0 Co-authored-by: Michael Stanclift Co-authored-by: Michael Stanclift --- README.md | 40 +- VERSION | 2 +- ADVANCED.md => docs/ADVANCED.md | 4 +- CHANGELOG.md => docs/CHANGELOG.md | 12 + gravity-sync.sh | 2226 +---------------- includes/gs-automate.sh | 114 + includes/gs-backup.sh | 75 + includes/gs-colors.sh | 54 + includes/gs-compare.sh | 15 + includes/gs-config.sh | 167 ++ includes/gs-exit.sh | 48 + includes/gs-hashing.sh | 148 ++ includes/gs-intent.sh | 41 + includes/gs-logging.sh | 94 + includes/gs-pull.sh | 178 ++ includes/gs-purge.sh | 78 + includes/gs-push.sh | 127 + includes/gs-restore.sh | 209 ++ includes/gs-root.sh | 40 + includes/gs-smart.sh | 153 ++ includes/gs-ssh.sh | 185 ++ includes/gs-update.sh | 143 ++ includes/gs-validate.sh | 148 ++ prep/gs-install.sh | 117 + .../gs-nopasswd.sudo | 0 25 files changed, 2226 insertions(+), 2192 deletions(-) rename ADVANCED.md => docs/ADVANCED.md (99%) rename CHANGELOG.md => docs/CHANGELOG.md (95%) create mode 100644 includes/gs-automate.sh create mode 100644 includes/gs-backup.sh create mode 100644 includes/gs-colors.sh create mode 100644 includes/gs-compare.sh create mode 100644 includes/gs-config.sh create mode 100644 includes/gs-exit.sh create mode 100644 includes/gs-hashing.sh create mode 100644 includes/gs-intent.sh create mode 100644 includes/gs-logging.sh create mode 100644 includes/gs-pull.sh create mode 100644 includes/gs-purge.sh create mode 100644 includes/gs-push.sh create mode 100644 includes/gs-restore.sh create mode 100644 includes/gs-root.sh create mode 100644 includes/gs-smart.sh create mode 100644 includes/gs-ssh.sh create mode 100644 includes/gs-update.sh create mode 100644 includes/gs-validate.sh create mode 100644 prep/gs-install.sh rename gs-nopasswd.sudo => templates/gs-nopasswd.sudo (100%) diff --git a/README.md b/README.md index 9b86272..84fe5cf 100644 --- a/README.md +++ b/README.md @@ -28,17 +28,19 @@ Gravity Sync is not developed by or affiliated with the Pi-hole project. This is ## Setup Steps 1. Read the installation Requirements! -2. Install Gravity Sync to your Pi-hole. -3. Configure your Gravity Sync installation. -4. Test your Gravity Sync install. -5. Automate future synchronizations. +2. Prepare your Pi-hole hosts. +3. Install Gravity Sync to your secondary Pi-hole. +4. Configure your Gravity Sync installation. +5. Test your Gravity Sync install. +6. Automate future synchronizations. +7. Profit. ## Requirements - Pi-hole 5.0 (or higher) must already be installed on at least two systems, using any of the Linux distribution that Pi-hole is [certified to run on](https://docs.pi-hole.net/main/prerequesites/#supported-operating-systems). - While it is possible to leverage container/Docker deployments of Pi-hole and Gravity Sync, this configuration is currently not officially supported. Instructions here assume a "native" installation of Pi-hole. -- You will need to make sure that you have `SUDO` enabled for the user accounts on both the primary and secondary Pi-hole. Most of the pre-built images available for the Raspberry Pi already have this configured. During configuration you will be prompted to enable this for your Gravity Sync user. -- Make sure `SSH` and `RSYNC` are installed on both the primary and secondary Pi-hole prior to installation. These two binaries are what does the heavy lifting between your Pi-hole nodes. In the past, Dropbear was supported but this has proven problematic. If you're using a ultra-lightweight Pi distribution (such as DietPi) that uses Dropbear by default, you will need to convert to OpenSSH as of Gravity Sync version 2.2. +- You will need to make sure that you have a Linux user account designated on both Pi-hole systems with `sudo` abilities on both the primary and secondary Pi-hole. Most of the pre-built images available for the Raspberry Pi already have this configured. During configuration you will be prompted to enable this for your Gravity Sync user. +- Make sure OpenSSH `ssh` and `rsync` commands are available on both the primary and secondary Pi-hole prior to installation. These two binaries are what do the heavy lifting between your Pi-hole nodes. If you're using a ultra-lightweight Pi distribution (such as DietPi) that uses Dropbear by default, you will need to convert to OpenSSH. - You will need to make sure that `SQLite3` is installed on both Pi-hole systems, in order for the backup and restore functions against the databases to completely successfully. This should be covered by the installation of Pi-hole or already installed on most Linux distros. ### Pi-hole Architecture @@ -58,19 +60,13 @@ Starting with version 2.0, Gravity Sync will attempt to sync the Adlist database Minimal preperation is required (as of version 2.2.3) on your primary Pi-hole. -Login to your *primary* Pi-hole, and install a copy of the software there: +Login to your *primary* Pi-hole, and run the following command: ```bash -git clone https://github.com/vmstan/gravity-sync.git $HOME/gravity-sync +export GS_INSTALL=primary && curl -sSL https://raw.githubusercontent.com/vmstan/gravity-sync/master/prep/gs-install.sh | bash ``` -From your home directory, you should run `./gravity-sync/gravity-sync.sh sudo` - -**Once this process has completed, you can remove the entire `gravity-sync` directory from the primary Pi-hole.** - -```bash -rm $HOME/gravity-sync -``` +This will verify you have everything necessary to use Gravity Sync. It will also add a passwordless sudo configuration file for the current user to run commands in `/etc/pihole` -- The installer will then exit, and direct you to proceed to the secondary Pi-hole. After you have completed this step, log out of the *primary* Pi-hole. @@ -78,13 +74,13 @@ After you have completed this step, log out of the *primary* Pi-hole. From this point forward, all operations will take place on your secondary Pi-hole. -Login to your *secondary* Pi-hole, and install a copy of the software there: +Login to your *secondary* Pi-hole, and run the following command: ```bash -git clone https://github.com/vmstan/gravity-sync.git $HOME/gravity-sync +export GS_INSTALL=secondary && curl -sSL https://raw.githubusercontent.com/vmstan/gravity-sync/master/prep/gs-install.sh | bash ``` -You will now have a folder called `gravity-sync` in your home directory. Everything Gravity Sync runs from there. +This will verify you have everything necessary to use Gravity Sync. The installer will then use Git to make a copy of the Gravity Sync executables in your home directory, and direct you to proceed to Configuration step below. Once this has completed, you will now have a folder called `gravity-sync` in your home directory. Everything Gravity Sync runs from there. Proceed to the Configuration section. @@ -94,12 +90,12 @@ After you install Gravity Sync to your *secondary Pi-hole* you will need to crea ```bash cd $HOME/gravity-sync -./gravity-sync.sh config +./gravity-sync.sh configure ``` This will guide you through the process of: -- Specifying the IP or xDNS name of your primary Pi-hole. +- Specifying the IP or DNS name of your primary Pi-hole. - Specifying the SSH username to connect to your primary Pi-hole. - Selecting the SSH authentication mechanism (key-pair or password.) - Configuring your key-pair and applying it to your primary Pi-hole. @@ -185,7 +181,7 @@ Your copy of the `gravity-sync.conf` file, logs and backups should not be be imp You can run a `./gravity-sync.sh config` at any time to generate a new configuration file if you're concerned that you're missing something. -- If the update script fails, make sure you did your original deployment via `git clone` and not a manual install. Refer to [ADVANCED.md](https://github.com/vmstan/gravity-sync/blob/master/ADVANCED.md) for more details. +- If the update script fails, make sure you did your original deployment via `git clone` and not a manual install. Refer to [ADVANCED.md](https://github.com/vmstan/gravity-sync/blob/master/docs/ADVANCED.md) for more details. ## Starting Over @@ -213,7 +209,7 @@ If you are completely uninstalling Gravity Sync, the last step would be to remov ## Advanced Installation -Please review the [Advanced Installation](https://github.com/vmstan/gravity-sync/blob/master/ADVANCED.md) guide for more assistance. +Please review the [Advanced Installation](https://github.com/vmstan/gravity-sync/blob/master/docs/ADVANCED.md) guide for more assistance. ## Troubleshooting diff --git a/VERSION b/VERSION index 6b4d157..56fea8a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.3 \ No newline at end of file +3.0.0 \ No newline at end of file diff --git a/ADVANCED.md b/docs/ADVANCED.md similarity index 99% rename from ADVANCED.md rename to docs/ADVANCED.md index 58dd660..9b70644 100644 --- a/ADVANCED.md +++ b/docs/ADVANCED.md @@ -18,8 +18,8 @@ Download the latest release from [GitHub](https://github.com/vmstan/gravity-sync ```bash cd ~ -wget https://github.com/vmstan/gravity-sync/archive/v2.2.3.zip -unzip v2.2.3.zip -d gravity-sync +wget https://github.com/vmstan/gravity-sync/archive/v3.0.0.zip +unzip v3.0.0.zip -d gravity-sync cd gravity-sync ``` diff --git a/CHANGELOG.md b/docs/CHANGELOG.md similarity index 95% rename from CHANGELOG.md rename to docs/CHANGELOG.md index f3750eb..e0de4ac 100644 --- a/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,17 @@ # The Changelog +## 3.0 + +### The Breakout Release + +This release focuses on breaking out elements of the script from the main file into a collection of a dozen or so files located under the `includes/gs-*.sh` hirearchy. Seperating out allows new contributors to work on different parts of the script individually, provides an oppertunity to clean up and reorganize parts of the code, and hopefully provides less risk of breaking the entire script. + +This release also features a brand new installation script, including a host check for both the primary and secondary Pi-hole system. This should reduce frustration of users who are missing parts of the system requirements. This will also place a file in the sudoers.d file on both systems to make sure passwordless sudo is configured as part of the installation. + +Lastly, we adopts Pi-hole style iconography such as `✓ ✗ e ! ?` instead of `[ GOOD ]` in command output. + +Enjoy! + ## 2.2 ### The Purged Release diff --git a/gravity-sync.sh b/gravity-sync.sh index 99762b4..96d88fd 100755 --- a/gravity-sync.sh +++ b/gravity-sync.sh @@ -3,10 +3,11 @@ SCRIPT_START=$SECONDS # GRAVITY SYNC BY VMSTAN ##################### PROGRAM='Gravity Sync' -VERSION='2.2.3' +VERSION='3.0.0' # Execute from the home folder of the user who owns it (ex: 'cd ~/gravity-sync') # For documentation or downloading 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 ########################## @@ -60,23 +61,8 @@ SSH_PKIF='.ssh/id_rsa' # default local SSH key ### DO NOT CHANGE ANYTHING BELOW THIS LINE ### ############################################## -# 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}FAIL${NC} ]" -WARN="[ ${PURPLE}WARN${NC} ]" -GOOD="[ ${GREEN} OK ${NC} ]" -STAT="[ ${CYAN}EXEC${NC} ]" -INFO="[ ${YELLOW}INFO${NC} ]" -NEED="[ ${BLUE}NEED${NC} ]" +# Import Color/Message Includes +source includes/gs-colors.sh # FUNCTION DEFINITIONS ####################### @@ -104,1080 +90,39 @@ function import_gs { fi } -function show_target { - MESSAGE="Targeting ${REMOTE_USER}@${REMOTE_HOST}" - echo_info - - detect_ssh -} - -# GS Update Functions -## Master Branch -function update_gs { - if [ -f "$HOME/${LOCAL_FOLDR}/dev" ] - then - source $HOME/${LOCAL_FOLDR}/dev - else - BRANCH='origin/master' - fi - - if [ "$BRANCH" != "origin/master" ] - then - MESSAGE="Pulling from ${BRANCH}" - echo_info - fi - - GIT_CHECK=$(git status | awk '{print $1}') - if [ "$GIT_CHECK" == "fatal:" ] - then - MESSAGE="Requires GitHub Installation" - echo_warn - exit_nochange - else - MESSAGE="Updating Cache" - echo_stat - git fetch --all >/dev/null 2>&1 - error_validate - MESSAGE="Applying Update" - echo_stat - git reset --hard ${BRANCH} >/dev/null 2>&1 - error_validate - fi -} - # Gravity Core Functions -## Pull Gravity -function pull_gs_grav { - - backup_local_gravity - backup_remote_gravity - - MESSAGE="Pulling ${GRAVITY_FI} from ${REMOTE_HOST}" - echo_stat - RSYNC_REPATH="rsync" - RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${GRAVITY_FI}.backup" - RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${GRAVITY_FI}.pull" - create_rsynccmd - - MESSAGE="Replacing ${GRAVITY_FI} on $HOSTNAME" - echo_stat - sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${GRAVITY_FI}.pull ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 - error_validate - - MESSAGE="Validating Settings of ${GRAVITY_FI}" - echo_stat - - GRAVDB_OWN=$(ls -ld ${PIHOLE_DIR}/${GRAVITY_FI} | awk 'OFS=":" {print $3,$4}') - if [ "$GRAVDB_OWN" != "$FILE_OWNER" ] - then - MESSAGE="Validating Ownership on ${GRAVITY_FI}" - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${GRAVITY_FI}" - echo_stat - sudo chown ${FILE_OWNER} ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 - error_validate - - MESSAGE="Continuing Validation of ${GRAVITY_FI}" - echo_stat - fi - - GRAVDB_RWE=$(namei -m ${PIHOLE_DIR}/${GRAVITY_FI} | grep -v f: | grep ${GRAVITY_FI} | awk '{print $1}') - if [ "$GRAVDB_RWE" != "-rw-rw-r--" ] - then - MESSAGE="Validating Permissions on ${GRAVITY_FI}" - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Permissions on ${GRAVITY_FI}" - echo_stat - sudo chmod 664 ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 - error_validate - - MESSAGE="Continuing Validation of ${GRAVITY_FI}" - echo_stat - fi - - echo_good -} - -## Pull Custom -function pull_gs_cust { - if [ "$SKIP_CUSTOM" != '1' ] - then - if [ "$REMOTE_CUSTOM_DNS" == "1" ] - then - backup_local_custom - backup_remote_custom - - MESSAGE="Pulling ${CUSTOM_DNS} from ${REMOTE_HOST}" - echo_stat - RSYNC_REPATH="rsync" - RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${CUSTOM_DNS}.backup" - RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${CUSTOM_DNS}.pull" - create_rsynccmd - - MESSAGE="Replacing ${CUSTOM_DNS} on $HOSTNAME" - echo_stat - sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${CUSTOM_DNS}.pull ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 - error_validate - - MESSAGE="Validating Settings on ${CUSTOM_DNS}" - echo_stat - - CUSTOMLS_OWN=$(ls -ld ${PIHOLE_DIR}/${CUSTOM_DNS} | awk '{print $3 $4}') - if [ "$CUSTOMLS_OWN" != "rootroot" ] - then - MESSAGE="Validating Ownership on ${CUSTOM_DNS}" - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${CUSTOM_DNS}" - echo_stat - sudo chown root:root ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 - error_validate - - MESSAGE="Continuing Validation of ${GRAVITY_FI}" - echo_stat - fi - - CUSTOMLS_RWE=$(namei -m ${PIHOLE_DIR}/${CUSTOM_DNS} | grep -v f: | grep ${CUSTOM_DNS} | awk '{print $1}') - if [ "$CUSTOMLS_RWE" != "-rw-r--r--" ] - then - MESSAGE="Validating Permissions on ${CUSTOM_DNS}" - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${CUSTOM_DNS}" - echo_stat - sudo chmod 644 ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 - error_validate - - MESSAGE="Continuing Validation of ${GRAVITY_FI}" - echo_stat - fi - - echo_good - fi - fi -} - -## Pull Reload -function pull_gs_reload { - MESSAGE="Isolating Regeneration Pathways" - echo_info - sleep 1 - - MESSAGE="Updating FTLDNS Configuration" - echo_stat - ${PIHOLE_BIN} restartdns reloadlists >/dev/null 2>&1 - error_validate - - MESSAGE="Reloading FTLDNS Services" - echo_stat - ${PIHOLE_BIN} restartdns >/dev/null 2>&1 - error_validate -} - -## Pull Function -function pull_gs { - previous_md5 - md5_compare - - backup_settime - pull_gs_grav - pull_gs_cust - pull_gs_reload - md5_recheck - - logs_export - exit_withchange -} - -## Push Gravity -function push_gs_grav { - backup_remote_gravity - backup_local_gravity - - MESSAGE="Copying ${GRAVITY_FI} from ${REMOTE_HOST}" - echo_stat - RSYNC_REPATH="rsync" - RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${GRAVITY_FI}.backup" - RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${GRAVITY_FI}.push" - create_rsynccmd - - MESSAGE="Pushing ${GRAVITY_FI} to ${REMOTE_HOST}" - echo_stat - RSYNC_REPATH="sudo rsync" - RSYNC_SOURCE="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${GRAVITY_FI}.backup" - RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${GRAVITY_FI}" - create_rsynccmd - - MESSAGE="Setting Permissions on ${GRAVITY_FI}" - echo_stat - CMD_TIMEOUT='15' - CMD_REQUESTED="sudo chmod 664 ${PIHOLE_DIR}/${GRAVITY_FI}" - create_sshcmd - - MESSAGE="Setting Ownership on ${GRAVITY_FI}" - echo_stat - CMD_TIMEOUT='15' - CMD_REQUESTED="sudo chown ${RFILE_OWNER} ${PIHOLE_DIR}/${GRAVITY_FI}" - create_sshcmd -} - -## Push Custom -function push_gs_cust { - if [ "$SKIP_CUSTOM" != '1' ] - then - if [ "$REMOTE_CUSTOM_DNS" == "1" ] - then - backup_remote_custom - backup_local_custom - - MESSAGE="Copying ${CUSTOM_DNS} from ${REMOTE_HOST}" - echo_stat - RSYNC_REPATH="rsync" - RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${CUSTOM_DNS}.backup" - RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${CUSTOM_DNS}.push" - create_rsynccmd - - MESSAGE="Pushing ${CUSTOM_DNS} to ${REMOTE_HOST}" - echo_stat - RSYNC_REPATH="sudo rsync" - RSYNC_SOURCE="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${CUSTOM_DNS}.backup" - RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${CUSTOM_DNS}" - create_rsynccmd - - MESSAGE="Setting Permissions on ${CUSTOM_DNS}" - echo_stat - CMD_TIMEOUT='15' - CMD_REQUESTED="sudo chmod 644 ${PIHOLE_DIR}/${CUSTOM_DNS}" - create_sshcmd - - MESSAGE="Setting Ownership on ${CUSTOM_DNS}" - echo_stat - CMD_TIMEOUT='15' - CMD_REQUESTED="sudo chown root:root ${PIHOLE_DIR}/${CUSTOM_DNS}" - create_sshcmd - fi - fi -} - -## Push Reload -function push_gs_reload { - MESSAGE="Inverting Tachyon Pulses" - echo_info - sleep 1 - - MESSAGE="Updating Remote FTLDNS Configuration" - echo_stat - CMD_TIMEOUT='15' - CMD_REQUESTED="${RIHOLE_BIN} restartdns reloadlists" - create_sshcmd - - MESSAGE="Reloading Remote FTLDNS Services" - echo_stat - CMD_TIMEOUT='15' - CMD_REQUESTED="${RIHOLE_BIN} restartdns" - create_sshcmd -} - -## Push Function -function push_gs { - previous_md5 - md5_compare - backup_settime - - intent_validate - - push_gs_grav - push_gs_cust - push_gs_reload - - md5_recheck - logs_export - exit_withchange -} - -function previous_md5 { - if [ -f "${LOG_PATH}/${HISTORY_MD5}" ] - then - last_primaryDBMD5=$(sed "1q;d" ${LOG_PATH}/${HISTORY_MD5}) - last_secondDBMD5=$(sed "2q;d" ${LOG_PATH}/${HISTORY_MD5}) - last_primaryCLMD5=$(sed "3q;d" ${LOG_PATH}/${HISTORY_MD5}) - last_secondCLMD5=$(sed "4q;d" ${LOG_PATH}/${HISTORY_MD5}) - else - last_primaryDBMD5="0" - last_secondDBMD5="0" - last_primaryCLMD5="0" - last_secondCLMD5="0" - fi -} - -## Smart Sync Function -function smart_gs { - previous_md5 - md5_compare - backup_settime - - PRIDBCHANGE="0" - SECDBCHANGE="0" - PRICLCHANGE="0" - SECCLCHANGE="0" - - if [ "${primaryDBMD5}" != "${last_primaryDBMD5}" ] - then - PRIDBCHANGE="1" - fi - - if [ "${secondDBMD5}" != "${last_secondDBMD5}" ] - then - SECDBCHANGE="1" - fi - - if [ "${PRIDBCHANGE}" == "${SECDBCHANGE}" ] - then - if [ "${PRIDBCHANGE}" != "0" ] - then - MESSAGE="Both ${GRAVITY_FI} Changed" - echo_warn - - PRIDBDATE=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${PIHOLE_DIR}/${GRAVITY_FI}") - SECDBDATE=$(stat -c %Y ${PIHOLE_DIR}/${GRAVITY_FI}) - - if [ "${PRIDBDATE}" -gt "$SECDBDATE" ] - then - MESSAGE="Primary ${GRAVITY_FI} Last Changed" - echo_info - - pull_gs_grav - PULLRESTART="1" - else - MESSAGE="Secondary ${GRAVITY_FI} Last Changed" - echo_info - - push_gs_grav - PUSHRESTART="1" - fi - fi - else - if [ "${PRIDBCHANGE}" != "0" ] - then - pull_gs_grav - PULLRESTART="1" - elif [ "${SECDBCHANGE}" != "0" ] - then - push_gs_grav - PUSHRESTART="1" - fi - fi - - if [ "${primaryCLMD5}" != "${last_primaryCLMD5}" ] - then - PRICLCHANGE="1" - fi - - if [ "${secondCLMD5}" != "${last_secondCLMD5}" ] - then - SECCLCHANGE="1" - fi - - if [ "$SKIP_CUSTOM" != '1' ] - then - - if [ -f "${PIHOLE_DIR}/${CUSTOM_DNS}" ] - then - - if [ "${PRICLCHANGE}" == "${SECCLCHANGE}" ] - then - if [ "${PRICLCHANGE}" != "0" ] - then - MESSAGE="Both ${CUSTOM_DNS} Changed" - echo_warn - - PRICLDATE=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${PIHOLE_DIR}/${CUSTOM_DNS}") - SECCLDATE=$(stat -c %Y ${PIHOLE_DIR}/${CUSTOM_DNS}) - - if [ "${PRICLDATE}" -gt "${SECCLDATE}" ] - then - MESSAGE="Primary ${CUSTOM_DNS} Last Changed" - echo_info - - pull_gs_cust - PULLRESTART="1" - else - MESSAGE="Secondary ${CUSTOM_DNS} Last Changed" - echo_info - - push_gs_cust - PUSHRESTART="1" - fi - fi - else - if [ "${PRICLCHANGE}" != "0" ] - then - pull_gs_cust - PULLRESTART="1" - elif [ "${SECCLCHANGE}" != "0" ] - then - push_gs_cust - PUSHRESTART="1" - fi - fi - else - pull_gs_cust - PULLRESTART="1" - fi - fi - - if [ "$PULLRESTART" == "1" ] - then - pull_gs_reload - fi - - if [ "$PUSHRESTART" == "1" ] - then - push_gs_reload - fi - - md5_recheck - - logs_export - exit_withchange -} - -function restore_gs { - MESSAGE="This will restore your settings on $HOSTNAME with a previous version!" - echo_warn - - MESSAGE="PREVIOUS BACKUPS AVAILABLE FOR RESTORATION" - echo_info - ls $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD} | grep $(date +%Y) | grep ${GRAVITY_FI} | colrm 18 - - MESSAGE="Select backup date to restore ${GRAVITY_FI} from" - echo_need - read INPUT_BACKUP_DATE - - if [ -f $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_BACKUP_DATE}-${GRAVITY_FI}.backup ] - then - MESSAGE="Backup File Selected" - else - MESSAGE="Invalid Request" - echo_info - - exit_nochange - fi - - if [ "$SKIP_CUSTOM" != '1' ] - then - - if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] - then - ls $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD} | grep $(date +%Y) | grep ${CUSTOM_DNS} | colrm 18 - - MESSAGE="Select backup date to restore ${CUSTOM_DNS} from" - echo_need - read INPUT_DNSBACKUP_DATE - - if [ -f $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_DNSBACKUP_DATE}-${CUSTOM_DNS}.backup ] - then - MESSAGE="Backup File Selected" - else - MESSAGE="Invalid Request" - echo_info - - exit_nochange - fi - fi - fi - - MESSAGE="${GRAVITY_FI} from ${INPUT_BACKUP_DATE} Selected" - echo_info - MESSAGE="${CUSTOM_DNS} from ${INPUT_DNSBACKUP_DATE} Selected" - echo_info - - intent_validate - - MESSAGE="Making Time Warp Calculations" - echo_info - - MESSAGE="Stopping Pi-hole Services" - echo_stat - - sudo service pihole-FTL stop >/dev/null 2>&1 - error_validate - - MESSAGE="Restoring ${GRAVITY_FI} on $HOSTNAME" - echo_stat - sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_BACKUP_DATE}-${GRAVITY_FI}.backup ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 - error_validate - - MESSAGE="Validating Ownership on ${GRAVITY_FI}" - echo_stat - - GRAVDB_OWN=$(ls -ld ${PIHOLE_DIR}/${GRAVITY_FI} | awk 'OFS=":" {print $3,$4}') - if [ "$GRAVDB_OWN" == "$FILE_OWNER" ] - then - echo_good - else - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${GRAVITY_FI}" - echo_stat - sudo chown ${FILE_OWNER} ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 - error_validate - fi - - MESSAGE="Validating Permissions on ${GRAVITY_FI}" - echo_stat - - GRAVDB_RWE=$(namei -m ${PIHOLE_DIR}/${GRAVITY_FI} | grep -v f: | grep ${GRAVITY_FI} | awk '{print $1}') - if [ "$GRAVDB_RWE" = "-rw-rw-r--" ] - then - echo_good - else - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${GRAVITY_FI}" - echo_stat - sudo chmod 664 ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 - error_validate - fi - - if [ "$SKIP_CUSTOM" != '1' ] - then - if [ -f $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_DNSBACKUP_DATE}-${CUSTOM_DNS}.backup ] - then - MESSAGE="Restoring ${CUSTOM_DNS} on $HOSTNAME" - echo_stat - sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_DNSBACKUP_DATE}-${CUSTOM_DNS}.backup ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 - error_validate - - MESSAGE="Validating Ownership on ${CUSTOM_DNS}" - echo_stat - - CUSTOMLS_OWN=$(ls -ld ${PIHOLE_DIR}/${CUSTOM_DNS} | awk '{print $3 $4}') - if [ "$CUSTOMLS_OWN" == "rootroot" ] - then - echo_good - else - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${CUSTOM_DNS}" - echo_stat - sudo chown root:root ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 - error_validate - fi - - MESSAGE="Validating Permissions on ${CUSTOM_DNS}" - echo_stat - - CUSTOMLS_RWE=$(namei -m ${PIHOLE_DIR}/${CUSTOM_DNS} | grep -v f: | grep ${CUSTOM_DNS} | awk '{print $1}') - if [ "$CUSTOMLS_RWE" == "-rw-r--r--" ] - then - echo_good - else - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Setting Ownership on ${CUSTOM_DNS}" - echo_stat - sudo chmod 644 ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 - error_validate - fi - fi - fi - - MESSAGE="Evacuating Saucer Section" - echo_info - sleep 1 - - MESSAGE="Restarting FTLDNS Services" - echo_stat - sudo service pihole-FTL start >/dev/null 2>&1 - error_validate - - MESSAGE="Updating FTLDNS Configuration" - echo_stat - ${PIHOLE_BIN} restartdns reloadlists >/dev/null 2>&1 - error_validate - - - MESSAGE="Do you want to push the restored configuration to the primary Pi-hole? (yes/no)" - echo_need - read PUSH_TO_PRIMARY - - if [ "${PUSH_TO_PRIMARY}" == "Yes" ] || [ "${PUSH_TO_PRIMARY}" == "yes" ] || [ "${PUSH_TO_PRIMARY}" == "Y" ] || [ "${PUSH_TO_PRIMARY}" == "y" ] - then - push_gs - elif [ "${PUSH_TO_PRIMARY}" == "No" ] || [ "${PUSH_TO_PRIMARY}" == "no" ] || [ "${PUSH_TO_PRIMARY}" == "N" ] || [ "${PUSH_TO_PRIMARY}" == "n" ] - then - logs_export - exit_withchange - else - MESSAGE="Invalid Selection - Defaulting No" - echo_warn - - logs_export - exit_withchange - fi -} +source includes/gs-compare.sh +source includes/gs-pull.sh +source includes/gs-push.sh +source includes/gs-smart.sh +source includes/gs-restore.sh +source includes/gs-backup.sh + +# Hashing & SSH Functions +source includes/gs-hashing.sh +source includes/gs-ssh.sh # Logging Functions -## Core Logging -### Write Logs Out -function logs_export { - - if [ "${TASKTYPE}" != "BACKUP" ] - then - MESSAGE="Saving File Hashes" - echo_stat - rm -f ${LOG_PATH}/${HISTORY_MD5} - echo -e ${primaryDBMD5} >> ${LOG_PATH}/${HISTORY_MD5} - echo -e ${secondDBMD5} >> ${LOG_PATH}/${HISTORY_MD5} - echo -e ${primaryCLMD5} >> ${LOG_PATH}/${HISTORY_MD5} - echo -e ${secondCLMD5} >> ${LOG_PATH}/${HISTORY_MD5} - error_validate - fi +source includes/gs-logging.sh - MESSAGE="Logging Successful ${TASKTYPE}" - echo_stat - echo -e $(date) "[${TASKTYPE}]" >> ${LOG_PATH}/${SYNCING_LOG} - error_validate -} +# Validation Functions +source includes/gs-validate.sh +source includes/gs-intent.sh +source includes/gs-root.sh -### Output Sync Logs -function logs_gs { - # import_gs +# Configuration Management +source includes/gs-config.sh +source includes/gs-update.sh +source includes/gs-automate.sh +source includes/gs-purge.sh - MESSAGE="Tailing ${LOG_PATH}/${SYNCING_LOG}" - echo_info +# Exit Codes +source includes/gs-exit.sh - echo -e "========================================================" - echo -e "Recent Complete ${YELLOW}SMART${NC} Executions" - tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep SMART - echo -e "Recent Complete ${YELLOW}PULL${NC} Executions" - tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep PULL - echo -e "Recent Complete ${YELLOW}PUSH${NC} Executions" - tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep PUSH - echo -e "Recent Complete ${YELLOW}BACKUP${NC} Executions" - tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep BACKUP - echo -e "Recent Complete ${YELLOW}RESTORE${NC} Executions" - tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep RESTORE - echo -e "========================================================" - - exit_nochange -} - -## Crontab Logs -### Core Crontab Logs -function show_crontab { - # import_gs - - MESSAGE="Replaying Last Cronjob" - echo_stat - - if [ -f ${LOG_PATH}/${CRONJOB_LOG} ] - then - if [ -s ${LOG_PATH}/${CRONJOB_LOG} ] - echo_good - - MESSAGE="Tailing ${LOG_PATH}/${CRONJOB_LOG}" - echo_info - - echo -e "========================================================" - date -r ${LOG_PATH}/${CRONJOB_LOG} - cat ${LOG_PATH}/${CRONJOB_LOG} - echo -e "========================================================" - - exit_nochange - then - echo_fail - - MESSAGE="${LOG_PATH}/${CRONJOB_LOG} is Empty" - echo_info - - exit_nochange - fi - else - echo_fail - - MESSAGE="${LOG_PATH}/${CRONJOB_LOG} is Missing" - echo_info - - exit_nochange - fi -} - -# Validate Functions -## Validate GS Folders -function validate_gs_folders { - MESSAGE="Validating ${PROGRAM} Folders on $HOSTNAME" - echo_stat - if [ ! -d $HOME/${LOCAL_FOLDR} ] - then - MESSAGE="Unable to Find $HOME/${LOCAL_FOLDR}" - echo_fail - exit_nochange - fi - - if [ ! -d $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD} ] - then - MESSAGE="Unable to Find $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}" - echo_fail - exit_nochange - fi - echo_good -} - -## Validate Pi-hole Folders -function validate_ph_folders { - MESSAGE="Validating Pi-hole Configuration on $HOSTNAME" - echo_stat - if [ ! -f ${PIHOLE_BIN} ] - then - MESSAGE="Unable to Validate Pi-Hole is Installed" - echo_fail - exit_nochange - fi - - if [ ! -d ${PIHOLE_DIR} ] - then - MESSAGE="Unable to Validate Pi-Hole Configuration Directory" - echo_fail - exit_nochange - fi - echo_good -} - -## Validate SQLite3 -function validate_sqlite3 { - MESSAGE="Validating SQLITE Installed on $HOSTNAME" - echo_stat - if hash sqlite3 2>/dev/null - then - MESSAGE="SQLITE3 Utility Detected" - echo_good - else - MESSAGE="SQLITE3 Utility Missing" - echo_warn - - MESSAGE="Installing SQLLITE3 with ${PKG_MANAGER}" - echo_stat - - ${PKG_INSTALL} sqllite3 >/dev/null 2>&1 - error_validate - fi -} - -## Validate SSHPASS -function validate_os_sshpass { - SSHPASSWORD='' - - if hash sshpass 2>/dev/null - then - MESSAGE="SSHPASS Utility Detected" - echo_warn - if [ -z "$REMOTE_PASS" ] - then - MESSAGE="Using SSH Key-Pair Authentication" - echo_info - else - MESSAGE="Testing Authentication Options" - echo_stat - - timeout 5 ssh -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} 'exit' >/dev/null 2>&1 - if [ "$?" != "0" ] - then - SSHPASSWORD="sshpass -p ${REMOTE_PASS}" - MESSAGE="Using SSH Password Authentication" - echo_warn - else - MESSAGE="Valid Key-Pair Detected ${NC}(${RED}Password Ignored${NC})" - echo_info - fi - fi - else - SSHPASSWORD='' - MESSAGE="Using SSH Key-Pair Authentication" - echo_info - fi - - MESSAGE="Validating Connection to ${REMOTE_HOST}" - echo_stat - - CMD_TIMEOUT='5' - CMD_REQUESTED="exit" - create_sshcmd - -} - -## Determine SSH Pathways -function create_sshcmd { - if hash ssh 2>/dev/null - then - if [ -z "$SSHPASSWORD" ] - then - timeout --preserve-status ${CMD_TIMEOUT} ${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF} -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} "${CMD_REQUESTED}" - error_validate - else - timeout --preserve-status ${CMD_TIMEOUT} ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} "${CMD_REQUESTED}" - error_validate - fi - # elif hash dbclient 2>/dev/null - # then - # timeout --preserve-status ${CMD_TIMEOUT} ${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF} ${REMOTE_USER}@${REMOTE_HOST} "${CMD_REQUESTED}" - # error_validate - fi -} - -## Determine SSH Pathways -function create_rsynccmd { - if hash ssh 2>/dev/null - then - if [ -z "$SSHPASSWORD" ] - then - rsync --rsync-path="${RSYNC_REPATH}" -e "${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF}" ${RSYNC_SOURCE} ${RSYNC_TARGET} >/dev/null 2>&1 - error_validate - else - rsync --rsync-path="${RSYNC_REPATH}" -e "${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF}" ${RSYNC_SOURCE} ${RSYNC_TARGET} >/dev/null 2>&1 - error_validate - fi - # elif hash dbclient 2>/dev/null - # then - # rsync --rsync-path="${RSYNC_REPATH}" -e "${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF}" ${RSYNC_SOURCE} ${RSYNC_TARGET} >/dev/null 2>&1 - # error_validate - fi -} - -## Detect SSH-KEYGEN -function detect_sshkeygen { - MESSAGE="Validating SSH-KEYGEN install on $HOSTNAME" - echo_stat - - if hash ssh-keygen >/dev/null 2>&1 - then - echo_good - else - echo_fail - MESSAGE="SSH-KEYGEN is Required" - echo_info - - exit_nochange - # MESSAGE="Attempting to Compensate" - # echo_warn - - # if hash dropbearkey >/dev/null 2>&1 - # then - # MESSAGE="Using DROPBEARKEY Instead" - # echo_info - # if [ ! -d $HOME/.ssh ] - # then - # mkdir $HOME/.ssh >/dev/null 2>&1 - # fi - # KEYGEN_COMMAND="dropbearkey -t rsa -f" - # else - # MESSAGE="No Alternatives Located" - # echo_info - # exit_nochange - # fi - fi -} - -function generate_sshkey { - if [ -z $INPUT_REMOTE_PASS ] - then - if [ -f $HOME/${SSH_PKIF} ] - then - MESSAGE="Using Existing ~/${SSH_PKIF}" - echo_info - else - if hash ssh-keygen >/dev/null 2>&1 - then - MESSAGE="Generating ~/${SSH_PKIF} (SSH-KEYGEN)" - echo_stat - - ssh-keygen -q -P "" -t rsa -f $HOME/${SSH_PKIF} >/dev/null 2>&1 - error_validate - - # elif hash dropbearkey >/dev/null 2>&1 - # then - # MESSAGE="Generating ~/${SSH_PKIF} (DROPBEARKEY)" - # echo_stat - # if [ ! -d $HOME/.ssh ] - # then - # mkdir $HOME/.ssh >/dev/null 2>&1 - # fi - # - # dropbearkey -t rsa -f $HOME/${SSH_PKIF} >/dev/null 2>&1 - # error_validate - else - MESSAGE="No SSH Key Generator Located" - echo_warn - exit_nochange - fi - fi - fi -} - -function export_sshkey { - if [ -z $REMOTE_PASS ] - then - if [ -f $HOME/${SSH_PKIF} ] - then - MESSAGE="Registering Key-Pair on ${REMOTE_HOST}" - echo_info - - #MESSAGE="Enter ${REMOTE_USER}@${REMOTE_HOST} Password Below" - #echo -e "${NEED} ${MESSAGE}" - - # if hash ssh-copy-id 2>/dev/null - # then - ssh-copy-id -f -p ${SSH_PORT} -i $HOME/${SSH_PKIF}.pub ${REMOTE_USER}@${REMOTE_HOST} - # elif hash dbclient 2>/dev/null - # then - # dropbearkey -y -f $HOME/${SSH_PKIF} | grep "^ssh-rsa " > $HOME/${SSH_PKIF}.pub - # cat $HOME/${SSH_PKIF}.pub | dbclient -p ${SSH_PORT} ${REMOTE_USER}@${REMOTE_HOST} 'cat - >> .ssh/authorized_keys' - # fi - else - MESSAGE="Error Registering Key-Pair" - echo_warn - fi - fi -} - -## Detect Package Manager -function distro_check { - if hash apt-get 2>/dev/null - then - PKG_MANAGER="apt-get" - PKG_INSTALL="sudo apt-get --yes --no-install-recommends --quiet install" - elif hash rpm 2>/dev/null - then - if hash dnf 2>/dev/null - then - PKG_MANAGER="dnf" - elif hash yum 2>/dev/null - then - PKG_MANAGER="yum" - else - MESSAGE="Unable to find OS Package Manager" - echo_info - exit_nochange - fi - PKG_INSTALL="sudo ${PKG_MANAGER} install -y" - else - MESSAGE="Unable to find OS Package Manager" - echo_info - exit_nochange - fi -} - -## Detect SSH & RSYNC -function detect_ssh { - MESSAGE="Validating SSH Client on $HOSTNAME" - echo_stat - - if hash ssh 2>/dev/null - then - MESSAGE="${MESSAGE} (OpenSSH)" - echo_good - SSH_CMD='ssh' - elif hash dbclient 2>/dev/null - then - MESSAGE="${MESSAGE} (Dropbear)" - echo_fail - - MESSAGE="Dropbear not supported in GS ${VERSION}" - echo_info - exit_nochange - else - echo_fail - - MESSAGE="Attempting to Compensate" - echo_warn - MESSAGE="Installing SSH Client with ${PKG_MANAGER}" - echo_stat - - ${PKG_INSTALL} ssh-client >/dev/null 2>&1 - error_validate - fi - - MESSAGE="Validating RSYNC Installed on $HOSTNAME" - echo_stat - - if hash rsync 2>/dev/null - then - echo_good - else - echo_fail - MESSAGE="RSYNC is Required" - echo_warn - - distro_check - - MESSAGE="Attempting to Compensate" - echo_warn - - MESSAGE="Installing RSYNC with ${PKG_MANAGER}" - echo_stat - ${PKG_INSTALL} rsync >/dev/null 2>&1 - error_validate - fi -} - -function detect_remotersync { - MESSAGE="Creating Test File on ${REMOTE_HOST}" - echo_stat - - CMD_TIMEOUT='15' - CMD_REQUESTED="touch ~/gs.test" - create_sshcmd - - MESSAGE="If pull test fails ensure RSYNC is installed on ${REMOTE_HOST}" - echo_warn - - MESSAGE="Pulling Test File to $HOSTNAME" - echo_stat - - RSYNC_REPATH="rsync" - RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:~/gs.test" - RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/gs.test" - create_rsynccmd - - MESSAGE="Cleaning Up Local Test File" - echo_stat - - rm $HOME/${LOCAL_FOLDR}/gs.test - error_validate - - MESSAGE="Cleaning Up Remote Test File" - echo_stat - - CMD_TIMEOUT='15' - CMD_REQUESTED="rm ~/gs.test" - create_sshcmd +# Invalid Tasks +function task_invalid { + echo_fail + list_gs_arguments } ## Error Validation @@ -1191,925 +136,6 @@ function error_validate { fi } -## Validate Sync Required -function md5_compare { - # last_primaryDBMD5="0" - # last_secondDBMD5="0" - # last_primaryCLMD5="0" - # last_secondCLMD5="0" - - HASHMARK='0' - - MESSAGE="Analyzing ${GRAVITY_FI} on ${REMOTE_HOST}" - echo_stat - primaryDBMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${GRAVITY_FI}" | sed 's/\s.*$//') - error_validate - - MESSAGE="Analyzing ${GRAVITY_FI} on $HOSTNAME" - echo_stat - secondDBMD5=$(md5sum ${PIHOLE_DIR}/${GRAVITY_FI} | sed 's/\s.*$//') - error_validate - - if [ "$primaryDBMD5" == "$last_primaryDBMD5" ] && [ "$secondDBMD5" == "$last_secondDBMD5" ] - then - HASHMARK=$((HASHMARK+0)) - else - MESSAGE="Differenced ${GRAVITY_FI} Detected" - echo_warn - HASHMARK=$((HASHMARK+1)) - fi - - if [ "$SKIP_CUSTOM" != '1' ] - then - if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] - then - if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} - then - REMOTE_CUSTOM_DNS="1" - MESSAGE="Analyzing ${CUSTOM_DNS} on ${REMOTE_HOST}" - echo_stat - - primaryCLMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//'") - error_validate - - MESSAGE="Analyzing ${CUSTOM_DNS} on $HOSTNAME" - echo_stat - secondCLMD5=$(md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//') - error_validate - - if [ "$primaryCLMD5" == "$last_primaryCLMD5" ] && [ "$secondCLMD5" == "$last_secondCLMD5" ] - then - # MESSAGE="${CUSTOM_DNS} Identical" - # echo_info - HASHMARK=$((HASHMARK+0)) - else - MESSAGE="Differenced ${CUSTOM_DNS} Detected" - echo_warn - HASHMARK=$((HASHMARK+1)) - fi - else - MESSAGE="No ${CUSTOM_DNS} Detected on ${REMOTE_HOST}" - echo_info - fi - else - if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} - then - REMOTE_CUSTOM_DNS="1" - MESSAGE="${REMOTE_HOST} has ${CUSTOM_DNS}" - HASHMARK=$((HASHMARK+1)) - echo_info - fi - MESSAGE="No ${CUSTOM_DNS} Detected on $HOSTNAME" - echo_info - fi - fi - - if [ "$HASHMARK" != "0" ] - then - MESSAGE="Replication Required" - echo_warn - HASHMARK=$((HASHMARK+0)) - else - MESSAGE="No Replication Required" - echo_info - exit_nochange - fi -} - -function md5_recheck { - MESSAGE="Performing Replicator Diagnostics" - echo_info - - HASHMARK='0' - - MESSAGE="Reanalyzing ${GRAVITY_FI} on ${REMOTE_HOST}" - echo_stat - primaryDBMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${GRAVITY_FI}" | sed 's/\s.*$//') - error_validate - - MESSAGE="Reanalyzing ${GRAVITY_FI} on $HOSTNAME" - echo_stat - secondDBMD5=$(md5sum ${PIHOLE_DIR}/${GRAVITY_FI} | sed 's/\s.*$//') - error_validate - - # if [ "$primaryDBMD5" == "$secondDBMD5" ] - # then - # HASHMARK=$((HASHMARK+0)) - # else - # MESSAGE="Differenced ${GRAVITY_FI} Detected" - # echo_warn - # HASHMARK=$((HASHMARK+1)) - # fi - - if [ "$SKIP_CUSTOM" != '1' ] - then - if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] - then - if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} - then - REMOTE_CUSTOM_DNS="1" - MESSAGE="Reanalyzing ${CUSTOM_DNS} on ${REMOTE_HOST}" - echo_stat - - primaryCLMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//'") - error_validate - - MESSAGE="Reanalyzing ${CUSTOM_DNS} on $HOSTNAME" - echo_stat - secondCLMD5=$(md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//') - error_validate - - # if [ "$primaryCLMD5" == "$secondCLMD5" ] - # then - # MESSAGE="${CUSTOM_DNS} Identical" - # echo_info - # HASHMARK=$((HASHMARK+0)) - # else - # MESSAGE="Differenced ${CUSTOM_DNS} Detected" - # echo_warn - # HASHMARK=$((HASHMARK+1)) - # fi - else - MESSAGE="No ${CUSTOM_DNS} Detected on ${REMOTE_HOST}" - echo_info - fi - else - if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} - then - REMOTE_CUSTOM_DNS="1" - MESSAGE="${REMOTE_HOST} has ${CUSTOM_DNS}" - # HASHMARK=$((HASHMARK+1)) - echo_info - fi - MESSAGE="No ${CUSTOM_DNS} Detected on $HOSTNAME" - echo_info - fi - fi - - # if [ "$HASHMARK" != "0" ] - # then - # MESSAGE="Replication Checks Failed" - # echo_warn - # else - # MESSAGE="Replication Was Successful" - # echo_info - # fi -} - -## Validate Intent -function intent_validate { - if [ "$VERIFY_PASS" == "0" ] - then - PHASER=$((( RANDOM % 4 ) + 1 )) - if [ "$PHASER" = "1" ] - then - INTENT="FIRE PHOTON TORPEDOS" - elif [ "$PHASER" = "2" ] - then - INTENT="FIRE ALL PHASERS" - elif [ "$PHASER" = "3" ] - then - INTENT="EJECT THE WARPCORE" - elif [ "$PHASER" = "4" ] - then - INTENT="ENGAGE TRACTOR BEAM" - fi - - MESSAGE="Type ${INTENT} to Confirm" - echo_need - - read INPUT_INTENT - - if [ "${INPUT_INTENT}" != "${INTENT}" ] - then - MESSAGE="${TASKTYPE} Aborted" - echo_info - exit_nochange - fi - else - MESSAGE="Verification Bypassed" - echo_warn - fi -} - -# Configuration Management -## Generate New Configuration -function config_generate { - task_sudo - - detect_ssh - - MESSAGE="Creating ${CONFIG_FILE} from Template" - echo_stat - cp $HOME/${LOCAL_FOLDR}/${CONFIG_FILE}.example $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - - MESSAGE="Environment Customization" - echo_info - - MESSAGE="Enter a custom SSH port if required (Leave blank for default '22')" - echo_need - read INPUT_SSH_PORT - INPUT_SSH_PORT="${INPUT_SSH_PORT:-22}" - - if [ "${INPUT_SSH_PORT}" != "22" ] - then - MESSAGE="Saving Custom SSH Port to ${CONFIG_FILE}" - echo_stat - sed -i "/# SSH_PORT=''/c\SSH_PORT='${INPUT_SSH_PORT}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - fi - - MESSAGE="Perform PING tests between Pi-holes? (Leave blank for default 'Yes')" - echo_need - read INPUT_PING_AVOID - INPUT_PING_AVOID="${INPUT_PING_AVOID:-Y}" - - if [ "${INPUT_PING_AVOID}" != "Y" ] - then - MESSAGE="Saving Ping Avoidance to ${CONFIG_FILE}" - echo_stat - sed -i "/# PING_AVOID=''/c\PING_AVOID='1'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - PING_AVOID=1 - fi - - MESSAGE="Standard Settings" - echo_info - - MESSAGE="IP or DNS of Primary Pi-hole" - echo_need - read INPUT_REMOTE_HOST - - if [ "${PING_AVOID}" != "1" ] - then - - - MESSAGE="Testing Network Connection (PING)" - echo_stat - ping -c 3 ${INPUT_REMOTE_HOST} >/dev/null 2>&1 - error_validate - else - MESSAGE="Bypassing Network Testing (PING)" - echo_warn - fi - - MESSAGE="SSH User with SUDO rights" - echo_need - read INPUT_REMOTE_USER - - MESSAGE="Saving Host to ${CONFIG_FILE}" - echo_stat - sed -i "/REMOTE_HOST='192.168.1.10'/c\REMOTE_HOST='${INPUT_REMOTE_HOST}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - - MESSAGE="Saving User to ${CONFIG_FILE}" - echo_stat - sed -i "/REMOTE_USER='pi'/c\REMOTE_USER='${INPUT_REMOTE_USER}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - - if hash sshpass 2>/dev/null - then - MESSAGE="SSHPASS Utility Detected" - echo_warn - if hash ssh 2>/dev/null - then - MESSAGE="Please Reference Documentation" - echo_info - - MESSAGE="${BLUE}https://github.com/vmstan/gravity-sync/blob/master/ADVANCED.md#ssh-configuration${NC}" - echo_info - - MESSAGE="Leave password blank to use key-pair! (reccomended)" - echo_warn - - MESSAGE="SSH User Password" - echo_need - read INPUT_REMOTE_PASS - - MESSAGE="Saving Password to ${CONFIG_FILE}" - echo_stat - sed -i "/REMOTE_PASS=''/c\REMOTE_PASS='${INPUT_REMOTE_PASS}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - elif hash dbclient 2>/dev/null - then - MESSAGE="Dropbear SSH Detected" - echo_warn - MESSAGE="Skipping Password Setup" - echo_info - fi - fi - - generate_sshkey - - MESSAGE="Importing New ${CONFIG_FILE}" - echo_stat - source $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - - export_sshkey - - MESSAGE="Testing Configuration" - echo_info - - validate_os_sshpass - validate_sqlite3 - - detect_remotersync -} - -## Delete Existing Configuration -function config_delete { - source $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - MESSAGE="Configuration File Exists" - echo_warn - - echo -e "========================================================" - cat $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - echo -e "========================================================" - - MESSAGE="Are you sure you want to erase this configuration?" - echo_warn - - intent_validate - - MESSAGE="Erasing Existing Configuration" - echo_stat - rm -f $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} - error_validate - - config_generate -} - -# Exit Codes -## No Changes Made -function exit_nochange { - SCRIPT_END=$SECONDS - MESSAGE="${PROGRAM} ${TASKTYPE} Aborting ($((SCRIPT_END-SCRIPT_START)) seconds)" - echo_info - exit 0 -} - -## Changes Made -function exit_withchange { - SCRIPT_END=$SECONDS - MESSAGE="${PROGRAM} ${TASKTYPE} Completed ($((SCRIPT_END-SCRIPT_START)) seconds)" - echo_info - exit 0 -} - -## List GS Arguments -function list_gs_arguments { - echo -e "Usage: $0 [options]" - echo -e "Example: '$0 pull'" - echo -e "" - echo -e "Setup Options:" - echo -e " ${YELLOW}config${NC} Create a new ${CONFIG_FILE} file" - echo -e " ${YELLOW}automate${NC} Add a scheduled pull task to crontab" - echo -e " ${YELLOW}update${NC} Update ${PROGRAM} to the latest version" - echo -e " ${YELLOW}version${NC} Display installed version of ${PROGRAM}" - echo -e "" - echo -e "Replication Options:" - echo -e " ${YELLOW}smart${NC} Detect changes on each side and bring them together" - echo -e " ${YELLOW}pull${NC} Force remote configuration changes to this server" - echo -e " ${YELLOW}push${NC} Force local configuration made on this server back" - echo -e " ${YELLOW}restore${NC} Restore the ${GRAVITY_FI} on this server" - echo -e " ${YELLOW}compare${NC} Just check for differences" - echo -e "" - echo -e "Debug Options:" - echo -e " ${YELLOW}logs${NC} Show recent successful replication jobs" - echo -e " ${YELLOW}cron${NC} Display output of last crontab execution" - echo -e "" - exit_nochange -} - -# Version Control -## Show Version -function show_version { - echo -e "========================================================" - MESSAGE="${BOLD}${PROGRAM}${NC} by ${CYAN}@vmstan${NC}" - echo_info - - MESSAGE="${BLUE}https://github.com/vmstan/gravity-sync${NC}" - echo_info - - if [ -f $HOME/${LOCAL_FOLDR}/dev ] - then - DEVVERSION="dev" - elif [ -f $HOME/${LOCAL_FOLDR}/beta ] - then - DEVVERSION="beta" - else - DEVVERSION="" - fi - - MESSAGE="Running Version: ${GREEN}${VERSION}${NC} ${DEVVERSION}" - echo_info - - GITVERSION=$(curl -sf https://raw.githubusercontent.com/vmstan/gravity-sync/master/VERSION) - if [ -z "$GITVERSION" ] - then - MESSAGE="Latest Version: ${RED}Unknown${NC}" - else - if [ "$GITVERSION" != "$VERSION" ] - then - MESSAGE="Update Available: ${PURPLE}${GITVERSION}${NC}" - else - MESSAGE="Latest Version: ${GREEN}${GITVERSION}${NC}" - fi - fi - echo_info - echo -e "========================================================" -} - -function dbclient_warning { - if hash dbclient 2>/dev/null - then - if hash ssh 2>/dev/null - then - NOEMPTYBASHIF="1" - else - MESSAGE="Dropbear support has been deprecated - please convert to OpenSSH" - echo_warn - fi - fi -} - -# Task Stack -## Automate Task -function task_automate { - TASKTYPE='AUTOMATE' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - - CRON_EXIST='0' - CRON_CHECK=$(crontab -l | grep -q "${GS_FILENAME}" && echo '1' || echo '0') - if [ ${CRON_CHECK} == 1 ] - then - MESSAGE="Automation Task Already Exists" - echo_info - CRON_EXIST='1' - fi - - MESSAGE="Configuring Hourly Smart Sync" - echo_info - - if [[ $1 =~ ^[0-9][0-9]?$ ]] - then - INPUT_AUTO_FREQ=$1 - else - MESSAGE="Sync Frequency in Minutes (1-30) or 0 to Disable" - echo_need - read INPUT_AUTO_FREQ - fi - - if [ $INPUT_AUTO_FREQ -gt 30 ] - then - MESSAGE="Invalid Frequency Range" - echo_fail - exit_nochange - elif [ $INPUT_AUTO_FREQ -lt 1 ] - then - if [ $CRON_EXIST == 1 ] - then - clear_cron - - MESSAGE="Sync Automation Disabled" - echo_warn - else - MESSAGE="No Sync Automation Scheduled" - echo_warn - fi - else - if [ $CRON_EXIST == 1 ] - then - clear_cron - fi - - MESSAGE="Saving New Sync Automation" - echo_stat - (crontab -l 2>/dev/null; echo "*/${INPUT_AUTO_FREQ} * * * * ${BASH_PATH} $HOME/${LOCAL_FOLDR}/${GS_FILENAME} smart > ${LOG_PATH}/${CRONJOB_LOG}") | crontab - - error_validate - fi - - MESSAGE="Configuring Daily Backup Frequency" - echo_info - - if [[ $2 =~ ^[0-9][0-9]?$ ]] - then - INPUT_AUTO_BACKUP=$2 - else - MESSAGE="Hour of Day to Backup (1-24) or 0 to Disable" - echo_need - read INPUT_AUTO_BACKUP - fi - - if [ $INPUT_AUTO_BACKUP -gt 24 ] - then - MESSAGE="Invalid Frequency Range" - echo_fail - exit_nochange - elif [ $INPUT_AUTO_BACKUP -lt 1 ] - then - MESSAGE="No Backup Automation Scheduled" - echo_warn - else - MESSAGE="Saving New Backup Automation" - echo_stat - (crontab -l 2>/dev/null; echo "0 ${INPUT_AUTO_BACKUP} * * * ${BASH_PATH} $HOME/${LOCAL_FOLDR}/${GS_FILENAME} backup >/dev/null 2>&1") | crontab - - error_validate - fi - - exit_withchange -} - -## Clear Existing Automation Settings -function clear_cron { - MESSAGE="Removing Existing Automation" - echo_stat - - crontab -l > cronjob-old.tmp - sed "/${GS_FILENAME}/d" cronjob-old.tmp > cronjob-new.tmp - crontab cronjob-new.tmp 2>/dev/null - error_validate - rm cronjob-old.tmp - rm cronjob-new.tmp -} - -## Configure Task -function task_configure { - TASKTYPE='CONFIGURE' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - if [ -f $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} ] - then - config_delete - else - MESSAGE="No Active ${CONFIG_FILE}" - echo_warn - - config_generate - fi - - backup_settime - backup_local_gravity - backup_local_custom - backup_cleanup - - exit_withchange -} - -## Devmode Task -function task_devmode { - TASKTYPE='DEV' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - if [ -f $HOME/${LOCAL_FOLDR}/dev ] - then - MESSAGE="Disabling ${TASKTYPE}" - echo_stat - rm -f $HOME/${LOCAL_FOLDR}/dev - error_validate - elif [ -f $HOME/${LOCAL_FOLDR}/beta ] - then - MESSAGE="Disabling BETA" - echo_stat - rm -f $HOME/${LOCAL_FOLDR}/beta - error_validate - - MESSAGE="Enabling ${TASKTYPE}" - echo_stat - touch $HOME/${LOCAL_FOLDR}/dev - error_validate - else - MESSAGE="Enabling ${TASKTYPE}" - echo_stat - touch $HOME/${LOCAL_FOLDR}/dev - error_validate - - git branch -r - - MESSAGE="Select Branch to Update Against" - echo_need - read INPUT_BRANCH - - echo -e "BRANCH='${INPUT_BRANCH}'" >> $HOME/${LOCAL_FOLDR}/dev - fi - - MESSAGE="Run UPDATE to apply changes" - echo_info - - exit_withchange -} - -## Devmode Task -function task_betamode { - TASKTYPE='BETA' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - if [ -f $HOME/${LOCAL_FOLDR}/beta ] - then - MESSAGE="Disabling ${TASKTYPE}" - echo_stat - rm -f $HOME/${LOCAL_FOLDR}/beta - error_validate - elif [ -f $HOME/${LOCAL_FOLDR}/dev ] - then - MESSAGE="Disabling DEV" - echo_stat - rm -f $HOME/${LOCAL_FOLDR}/dev - error_validate - - MESSAGE="Enabling ${TASKTYPE}" - echo_stat - touch $HOME/${LOCAL_FOLDR}/beta - error_validate - else - MESSAGE="Enabling ${TASKTYPE}" - echo_stat - touch $HOME/${LOCAL_FOLDR}/beta - error_validate - fi - - MESSAGE="Run UPDATE to apply changes" - echo_info - - exit_withchange -} - -## Update Task -function task_update { - TASKTYPE='UPDATE' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - dbclient_warning - - update_gs - - exit_withchange -} - -## Version Task -function task_version { - TASKTYPE='VERSION' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - show_version - exit_nochange -} - -## Logs Task -function task_logs { - TASKTYPE='LOGS' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - logs_gs -} - -## Compare Task -function task_compare { - TASKTYPE='COMPARE' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_os_sshpass - - previous_md5 - md5_compare -} - -## Cron Task -function task_cron { - TASKTYPE='CRON' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - show_crontab -} - -function task_invalid { - echo_fail - list_gs_arguments -} - -## Purge Task -function task_purge { - TASKTYPE="THE-PURGE" - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - MESSAGE="THIS WILL RESET YOUR ENTIRE GRAVITY SYNC INSTALLATION" - echo_warn - MESSAGE="This will remove:" - echo_warn - MESSAGE="- All backups files." - echo_warn - MESSAGE="- Your ${CONFIG_FILE} file." - echo_warn - - if [ -f "$HOME/${LOCAL_FOLDR}/dev" ] - then - MESSAGE="- Your development branch updater." - elif [ -f "$HOME/${LOCAL_FOLDR}/beta" ] - then - MESSAGE="- Your beta branch updater." - fi - echo_warn - - MESSAGE="- All cronjob/automation tasks." - echo_warn - MESSAGE="- All job history/logs." - echo_warn - MESSAGE="- Associated SSH id_rsa keys." - echo_warn - MESSAGE="This function cannot be undone!" - echo_warn - - MESSAGE="YOU WILL NEED TO REBUILD GRAVITY SYNC AFTER EXECUTION" - echo_warn - - MESSAGE="Pi-hole binaries, configuration and services ARE NOT impacted!" - echo_info - MESSAGE="Your device will continue to resolve and block DNS requests," - echo_info - MESSAGE="but your ${GRAVITY_FI} and ${CUSTOM_DNS} WILL NOT sync anymore," - echo_info - MESSAGE="until you reconfigure Gravity Sync on this device." - echo_info - - intent_validate - - MESSAGE="Cleaning Gravity Sync Directory" - echo_stat - - git clean -f -X -d >/dev/null 2>&1 - error_validate - - clear_cron - - MESSAGE="Deleting SSH Key-files" - echo_stat - - rm -f $HOME/${SSH_PKIF} >/dev/null 2>&1 - rm -f $HOME/${SSH_PKIF}.pub >/dev/null 2>&1 - error_validate - - MESSAGE="Realigning Dilithium Matrix" - echo_fail - - sleep 1 - - update_gs -} - -## Sudo Creation Task -function task_sudo { - TASKTYPE='SUDO' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - MESSAGE="Creating Sudoer.d Template" - echo_stat - - NEW_SUDO_USER=$(whoami) - echo -e "${NEW_SUDO_USER} ALL=(ALL) NOPASSWD: ${PIHOLE_DIR}" > gs-nopasswd.sudo - error_validate - - MESSAGE="Installing Sudoer.d File" - echo_stat - - sudo install -m 0440 gs-nopasswd.sudo /etc/sudoers.d/gs-nopasswd - error_validate -} - -## Backup Task -function task_backup { - TASKTYPE='BACKUP' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - backup_settime - backup_local_gravity - backup_local_custom - backup_cleanup - - logs_export - exit_withchange -} - -function backup_settime { - BACKUPTIMESTAMP=$(date +%F-%H%M%S) -} - -function backup_local_gravity { - MESSAGE="Performing Backup of Local ${GRAVITY_FI}" - echo_stat - - sqlite3 ${PIHOLE_DIR}/${GRAVITY_FI} ".backup '$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${GRAVITY_FI}.backup'" - error_validate -} - -function backup_local_custom { - if [ "$SKIP_CUSTOM" != '1' ] - then - if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] - then - MESSAGE="Performing Backup Up Local ${CUSTOM_DNS}" - echo_stat - - cp ${PIHOLE_DIR}/${CUSTOM_DNS} $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${CUSTOM_DNS}.backup - error_validate - fi - fi -} - -function backup_remote_gravity { - MESSAGE="Performing Backup of Remote ${GRAVITY_FI}" - echo_stat - - CMD_TIMEOUT='60' - CMD_REQUESTED="sudo sqlite3 ${PIHOLE_DIR}/${GRAVITY_FI} \".backup '${PIHOLE_DIR}/${GRAVITY_FI}.backup'\"" - create_sshcmd -} - -function backup_remote_custom { - if [ "$SKIP_CUSTOM" != '1' ] - then - MESSAGE="Performing Backup of Remote ${CUSTOM_DNS}" - echo_stat - - CMD_TIMEOUT='15' - CMD_REQUESTED="sudo cp ${PIHOLE_DIR}/${CUSTOM_DNS} ${PIHOLE_DIR}/${CUSTOM_DNS}.backup" - create_sshcmd - fi -} - -function backup_cleanup { - MESSAGE="Cleaning Up Old Backups" - echo_stat - - find $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/$(date +%Y)*.backup -mtime +${BACKUP_RETAIN} -type f -delete - error_validate -} - -# Echo Stack -## Informative -function echo_info { - echo -e "${INFO} ${YELLOW}${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}" -} - -## Failure -function echo_fail { - echo -e "\r${FAIL} ${MESSAGE}" -} - -## Request -function echo_need { - echo -en "${NEED} ${MESSAGE}: " -} - -# Root Check -function root_check { - if [ ! "$EUID" -ne 0 ] - then - TASKTYPE='ROOT' - MESSAGE="${MESSAGE} ${TASKTYPE}" - echo_fail - - MESSAGE="${PROGRAM} Must Not Run as Root" - echo_warn - - exit_nochange - fi -} - # SCRIPT EXECUTION ########################### MESSAGE="${PROGRAM} ${VERSION} Executing" @@ -2126,198 +152,64 @@ function root_check { fi case $# in - 0) - TASKTYPE='SMART' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_sqlite3 - validate_os_sshpass - - smart_gs - exit - ;; - + task_smart ;; 1) case $1 in - sync) - TASKTYPE='SMART' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_sqlite3 - validate_os_sshpass - - smart_gs - exit - ;; - - smart) - TASKTYPE='SMART' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_sqlite3 - validate_os_sshpass - - smart_gs - exit - ;; - + smart|sync) + task_smart ;; pull) - TASKTYPE='PULL' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_sqlite3 - validate_os_sshpass - - pull_gs - exit - ;; - + task_pull ;; push) - TASKTYPE='PUSH' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_sqlite3 - validate_os_sshpass - - push_gs - exit - ;; - + task_push ;; restore) - TASKTYPE='RESTORE' - MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" - echo_good - - # import_gs - show_target - validate_gs_folders - validate_ph_folders - validate_sqlite3 - - restore_gs - exit - ;; - + task_restore ;; version) - task_version - ;; - - update) - task_update - ;; - - upgrade) - task_update - ;; - - dev) - task_devmode - ;; - - # beta) - # task_betamode - # ;; - - devmode) - task_devmode - ;; - - development) - task_devmode - ;; - - logs) - task_logs - ;; - + task_version ;; + update|upgrade) + task_update ;; + dev|devmode|development|develop) + task_devmode ;; + logs|log) + task_logs ;; compare) - task_compare - ;; - + task_compare ;; cron) - task_cron - ;; - - config) - task_configure - ;; - - configure) - task_configure - ;; - - auto) - task_automate - ;; - - automate) - task_automate - ;; - + task_cron ;; + config|configure) + task_configure ;; + auto|automate) + task_automate ;; backup) - task_backup - ;; - + task_backup ;; purge) - task_purge - ;; - + task_purge ;; sudo) task_sudo exit_withchange ;; *) - task_invalid - ;; + task_invalid ;; esac ;; 2) case $1 in - automate) - task_automate - ;; - + auto|automate) + task_automate ;; esac ;; 3) case $1 in - automate) - task_automate $2 $3 - ;; - + auto|automate) + task_automate $2 $3 ;; esac ;; *) - task_invalid - ;; + task_invalid ;; esac + +# END OF SCRIPT ############################## \ No newline at end of file diff --git a/includes/gs-automate.sh b/includes/gs-automate.sh new file mode 100644 index 0000000..1bb57a3 --- /dev/null +++ b/includes/gs-automate.sh @@ -0,0 +1,114 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-automate.sh ############################# + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Automate Task +function task_automate { + TASKTYPE='AUTOMATE' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + CRON_EXIST='0' + CRON_CHECK=$(crontab -l | grep -q "${GS_FILENAME}" && echo '1' || echo '0') + if [ ${CRON_CHECK} == 1 ] + then + MESSAGE="Automation Task Already Exists" + echo_info + CRON_EXIST='1' + fi + + MESSAGE="Configuring Hourly Smart Sync" + echo_info + + if [[ $1 =~ ^[0-9][0-9]?$ ]] + then + INPUT_AUTO_FREQ=$1 + else + MESSAGE="Sync Frequency in Minutes (1-30) or 0 to Disable" + echo_need + read INPUT_AUTO_FREQ + fi + + if [ $INPUT_AUTO_FREQ -gt 30 ] + then + MESSAGE="Invalid Frequency Range" + echo_fail + exit_nochange + elif [ $INPUT_AUTO_FREQ -lt 1 ] + then + if [ $CRON_EXIST == 1 ] + then + clear_cron + + MESSAGE="Sync Automation Disabled" + echo_warn + else + MESSAGE="No Sync Automation Scheduled" + echo_warn + fi + else + if [ $CRON_EXIST == 1 ] + then + clear_cron + fi + + MESSAGE="Saving New Sync Automation" + echo_stat + (crontab -l 2>/dev/null; echo "*/${INPUT_AUTO_FREQ} * * * * ${BASH_PATH} $HOME/${LOCAL_FOLDR}/${GS_FILENAME} smart > ${LOG_PATH}/${CRONJOB_LOG}") | crontab - + error_validate + fi + + MESSAGE="Configuring Daily Backup Frequency" + echo_info + + if [[ $2 =~ ^[0-9][0-9]?$ ]] + then + INPUT_AUTO_BACKUP=$2 + else + MESSAGE="Hour of Day to Backup (1-24) or 0 to Disable" + echo_need + read INPUT_AUTO_BACKUP + fi + + if [ $INPUT_AUTO_BACKUP -gt 24 ] + then + MESSAGE="Invalid Frequency Range" + echo_fail + exit_nochange + elif [ $INPUT_AUTO_BACKUP -lt 1 ] + then + MESSAGE="No Backup Automation Scheduled" + echo_warn + else + MESSAGE="Saving New Backup Automation" + echo_stat + (crontab -l 2>/dev/null; echo "0 ${INPUT_AUTO_BACKUP} * * * ${BASH_PATH} $HOME/${LOCAL_FOLDR}/${GS_FILENAME} backup >/dev/null 2>&1") | crontab - + error_validate + fi + + exit_withchange +} + +## Clear Existing Automation Settings +function clear_cron { + MESSAGE="Removing Existing Automation" + echo_stat + + crontab -l > cronjob-old.tmp + sed "/${GS_FILENAME}/d" cronjob-old.tmp > cronjob-new.tmp + crontab cronjob-new.tmp 2>/dev/null + error_validate + rm cronjob-old.tmp + rm cronjob-new.tmp +} + +## Cron Task +function task_cron { + TASKTYPE='CRON' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_crontab +} \ No newline at end of file diff --git a/includes/gs-backup.sh b/includes/gs-backup.sh new file mode 100644 index 0000000..c49c416 --- /dev/null +++ b/includes/gs-backup.sh @@ -0,0 +1,75 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-backup.sh ############################### + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Backup Task +function task_backup { + TASKTYPE='BACKUP' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + backup_settime + backup_local_gravity + backup_local_custom + backup_cleanup + + logs_export + exit_withchange +} + +function backup_settime { + BACKUPTIMESTAMP=$(date +%F-%H%M%S) +} + +function backup_local_gravity { + MESSAGE="Performing Backup of Local ${GRAVITY_FI}" + echo_stat + + sqlite3 ${PIHOLE_DIR}/${GRAVITY_FI} ".backup '$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${GRAVITY_FI}.backup'" + error_validate +} + +function backup_local_custom { + if [ "$SKIP_CUSTOM" != '1' ] + then + if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] + then + MESSAGE="Performing Backup Up Local ${CUSTOM_DNS}" + echo_stat + + cp ${PIHOLE_DIR}/${CUSTOM_DNS} $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${CUSTOM_DNS}.backup + error_validate + fi + fi +} + +function backup_remote_gravity { + MESSAGE="Performing Backup of Remote ${GRAVITY_FI}" + echo_stat + + CMD_TIMEOUT='60' + CMD_REQUESTED="sudo sqlite3 ${PIHOLE_DIR}/${GRAVITY_FI} \".backup '${PIHOLE_DIR}/${GRAVITY_FI}.backup'\"" + create_sshcmd +} + +function backup_remote_custom { + if [ "$SKIP_CUSTOM" != '1' ] + then + MESSAGE="Performing Backup of Remote ${CUSTOM_DNS}" + echo_stat + + CMD_TIMEOUT='15' + CMD_REQUESTED="sudo cp ${PIHOLE_DIR}/${CUSTOM_DNS} ${PIHOLE_DIR}/${CUSTOM_DNS}.backup" + create_sshcmd + fi +} + +function backup_cleanup { + MESSAGE="Cleaning Up Old Backups" + echo_stat + + find $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/$(date +%Y)*.backup -mtime +${BACKUP_RETAIN} -type f -delete + error_validate +} \ No newline at end of file diff --git a/includes/gs-colors.sh b/includes/gs-colors.sh new file mode 100644 index 0000000..36e5a2f --- /dev/null +++ b/includes/gs-colors.sh @@ -0,0 +1,54 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-colors.sh ############################### + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## 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}e${NC}]" +INFO="[${YELLOW}i${NC}]" +NEED="[${BLUE}?${NC}]" + +## Echo Stack +### Informative +function echo_info { + echo -e "${INFO} ${YELLOW}${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}" +} + +### Failure +function echo_fail { + echo -e "\r${FAIL} ${MESSAGE}" +} + +### Request +function echo_need { + echo -en "${NEED} ${MESSAGE}: " +} \ No newline at end of file diff --git a/includes/gs-compare.sh b/includes/gs-compare.sh new file mode 100644 index 0000000..dba1f7d --- /dev/null +++ b/includes/gs-compare.sh @@ -0,0 +1,15 @@ +## Compare Task +function task_compare { + TASKTYPE='COMPARE' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_target + validate_gs_folders + validate_ph_folders + validate_os_sshpass + + previous_md5 + md5_compare + exit_withchange +} \ No newline at end of file diff --git a/includes/gs-config.sh b/includes/gs-config.sh new file mode 100644 index 0000000..435a8fc --- /dev/null +++ b/includes/gs-config.sh @@ -0,0 +1,167 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-config.sh ############################### + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Configure Task +function task_configure { + TASKTYPE='CONFIGURE' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + if [ -f $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} ] + then + config_delete + else + MESSAGE="No Active ${CONFIG_FILE}" + echo_warn + + config_generate + fi + + backup_settime + backup_local_gravity + backup_local_custom + backup_cleanup + + exit_withchange +} + +## Generate New Configuration +function config_generate { + detect_ssh + + MESSAGE="Creating ${CONFIG_FILE} from Template" + echo_stat + cp $HOME/${LOCAL_FOLDR}/${CONFIG_FILE}.example $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + + MESSAGE="Environment Customization" + echo_info + + MESSAGE="Enter a custom SSH port if required (Leave blank for default '22')" + echo_need + read INPUT_SSH_PORT + INPUT_SSH_PORT="${INPUT_SSH_PORT:-22}" + + if [ "${INPUT_SSH_PORT}" != "22" ] + then + MESSAGE="Saving Custom SSH Port to ${CONFIG_FILE}" + echo_stat + sed -i "/# SSH_PORT=''/c\SSH_PORT='${INPUT_SSH_PORT}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + fi + + MESSAGE="Perform PING tests between Pi-holes? (Leave blank for default 'Yes')" + echo_need + read INPUT_PING_AVOID + INPUT_PING_AVOID="${INPUT_PING_AVOID:-Y}" + + if [ "${INPUT_PING_AVOID}" != "Y" ] + then + MESSAGE="Saving Ping Avoidance to ${CONFIG_FILE}" + echo_stat + sed -i "/# PING_AVOID=''/c\PING_AVOID='1'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + PING_AVOID=1 + fi + + MESSAGE="Standard Settings" + echo_info + + MESSAGE="IP or DNS of Primary Pi-hole" + echo_need + read INPUT_REMOTE_HOST + + if [ "${PING_AVOID}" != "1" ] + then + MESSAGE="Testing Network Connection (PING)" + echo_stat + ping -c 3 ${INPUT_REMOTE_HOST} >/dev/null 2>&1 + error_validate + else + MESSAGE="Bypassing Network Testing (PING)" + echo_warn + fi + + MESSAGE="SSH User with SUDO rights" + echo_need + read INPUT_REMOTE_USER + + MESSAGE="Saving Host to ${CONFIG_FILE}" + echo_stat + sed -i "/REMOTE_HOST='192.168.1.10'/c\REMOTE_HOST='${INPUT_REMOTE_HOST}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + + MESSAGE="Saving User to ${CONFIG_FILE}" + echo_stat + sed -i "/REMOTE_USER='pi'/c\REMOTE_USER='${INPUT_REMOTE_USER}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + + if hash sshpass 2>/dev/null + then + MESSAGE="SSHPASS Utility Detected" + echo_warn + if hash ssh 2>/dev/null + then + MESSAGE="Please Reference Documentation" + echo_info + + MESSAGE="${BLUE}https://github.com/vmstan/gravity-sync/blob/master/ADVANCED.md#ssh-configuration${NC}" + echo_info + + MESSAGE="Leave password blank to use key-pair! (reccomended)" + echo_warn + + MESSAGE="SSH User Password" + echo_need + read INPUT_REMOTE_PASS + + MESSAGE="Saving Password to ${CONFIG_FILE}" + echo_stat + sed -i "/REMOTE_PASS=''/c\REMOTE_PASS='${INPUT_REMOTE_PASS}'" $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + fi + fi + + generate_sshkey + + MESSAGE="Importing New ${CONFIG_FILE}" + echo_stat + source $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + + export_sshkey + + MESSAGE="Testing Configuration" + echo_info + + validate_os_sshpass + validate_sqlite3 + + detect_remotersync +} + +## Delete Existing Configuration +function config_delete { + source $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + MESSAGE="Configuration File Exists" + echo_warn + + echo -e "========================================================" + cat $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + echo -e "========================================================" + + MESSAGE="Are you sure you want to erase this configuration?" + echo_warn + + intent_validate + + MESSAGE="Erasing Existing Configuration" + echo_stat + rm -f $HOME/${LOCAL_FOLDR}/${CONFIG_FILE} + error_validate + + config_generate +} \ No newline at end of file diff --git a/includes/gs-exit.sh b/includes/gs-exit.sh new file mode 100644 index 0000000..52198d5 --- /dev/null +++ b/includes/gs-exit.sh @@ -0,0 +1,48 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-exit.sh ################################# + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## No Changes Made +function exit_nochange { + SCRIPT_END=$SECONDS + MESSAGE="${PROGRAM} ${TASKTYPE} Aborting ($((SCRIPT_END-SCRIPT_START)) seconds)" + echo_info + exit 0 +} + +## Changes Made +function exit_withchange { + SCRIPT_END=$SECONDS + MESSAGE="${PROGRAM} ${TASKTYPE} Completed ($((SCRIPT_END-SCRIPT_START)) seconds)" + echo_info + exit 0 +} + +## List GS Arguments +function list_gs_arguments { + echo -e "Usage: $0 [options]" + echo -e "Example: '$0 pull'" + echo -e "" + echo -e "Setup Options:" + echo -e " ${YELLOW}config${NC} Create a new ${CONFIG_FILE} file" + echo -e " ${YELLOW}automate${NC} Add a scheduled pull task to crontab" + echo -e " ${YELLOW}update${NC} Update ${PROGRAM} to the latest version" + echo -e " ${YELLOW}version${NC} Display installed version of ${PROGRAM}" + echo -e " ${YELLOW}sudo${NC} Configure passwordless sudo for current user" + echo -e "" + echo -e "Replication Options:" + echo -e " ${YELLOW}smart${NC} Detect changes on each side and bring them together" + echo -e " ${YELLOW}pull${NC} Force remote configuration changes to this server" + echo -e " ${YELLOW}push${NC} Force local configuration made on this server back" + echo -e " ${YELLOW}restore${NC} Restore the ${GRAVITY_FI} on this server" + echo -e " ${YELLOW}backup${NC} Backup the ${GRAVITY_FI} on this server" + echo -e " ${YELLOW}compare${NC} Just check for differences" + echo -e "" + echo -e "Debug Options:" + echo -e " ${YELLOW}logs${NC} Show recent successful replication jobs" + echo -e " ${YELLOW}cron${NC} Display output of last crontab execution" + echo -e "" + exit_nochange +} \ No newline at end of file diff --git a/includes/gs-hashing.sh b/includes/gs-hashing.sh new file mode 100644 index 0000000..e6f2dc4 --- /dev/null +++ b/includes/gs-hashing.sh @@ -0,0 +1,148 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-hasing.sh ############################### + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Validate Sync Required +function md5_compare { + HASHMARK='0' + + MESSAGE="Analyzing ${GRAVITY_FI} on ${REMOTE_HOST}" + echo_stat + primaryDBMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${GRAVITY_FI}" | sed 's/\s.*$//') + error_validate + + MESSAGE="Analyzing ${GRAVITY_FI} on $HOSTNAME" + echo_stat + secondDBMD5=$(md5sum ${PIHOLE_DIR}/${GRAVITY_FI} | sed 's/\s.*$//') + error_validate + + if [ "$primaryDBMD5" == "$last_primaryDBMD5" ] && [ "$secondDBMD5" == "$last_secondDBMD5" ] + then + HASHMARK=$((HASHMARK+0)) + else + MESSAGE="Differenced ${GRAVITY_FI} Detected" + echo_warn + HASHMARK=$((HASHMARK+1)) + fi + + if [ "$SKIP_CUSTOM" != '1' ] + then + if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] + then + if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} + then + REMOTE_CUSTOM_DNS="1" + MESSAGE="Analyzing ${CUSTOM_DNS} on ${REMOTE_HOST}" + echo_stat + + primaryCLMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//'") + error_validate + + MESSAGE="Analyzing ${CUSTOM_DNS} on $HOSTNAME" + echo_stat + secondCLMD5=$(md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//') + error_validate + + if [ "$primaryCLMD5" == "$last_primaryCLMD5" ] && [ "$secondCLMD5" == "$last_secondCLMD5" ] + then + HASHMARK=$((HASHMARK+0)) + else + MESSAGE="Differenced ${CUSTOM_DNS} Detected" + echo_warn + HASHMARK=$((HASHMARK+1)) + fi + else + MESSAGE="No ${CUSTOM_DNS} Detected on ${REMOTE_HOST}" + echo_info + fi + else + if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} + then + REMOTE_CUSTOM_DNS="1" + MESSAGE="${REMOTE_HOST} has ${CUSTOM_DNS}" + HASHMARK=$((HASHMARK+1)) + echo_info + fi + MESSAGE="No ${CUSTOM_DNS} Detected on $HOSTNAME" + echo_info + fi + fi + + if [ "$HASHMARK" != "0" ] + then + MESSAGE="Replication Required" + echo_warn + HASHMARK=$((HASHMARK+0)) + else + MESSAGE="No Replication Required" + echo_info + exit_nochange + fi +} + +function previous_md5 { + if [ -f "${LOG_PATH}/${HISTORY_MD5}" ] + then + last_primaryDBMD5=$(sed "1q;d" ${LOG_PATH}/${HISTORY_MD5}) + last_secondDBMD5=$(sed "2q;d" ${LOG_PATH}/${HISTORY_MD5}) + last_primaryCLMD5=$(sed "3q;d" ${LOG_PATH}/${HISTORY_MD5}) + last_secondCLMD5=$(sed "4q;d" ${LOG_PATH}/${HISTORY_MD5}) + else + last_primaryDBMD5="0" + last_secondDBMD5="0" + last_primaryCLMD5="0" + last_secondCLMD5="0" + fi +} + +function md5_recheck { + MESSAGE="Performing Replicator Diagnostics" + echo_info + + HASHMARK='0' + + MESSAGE="Reanalyzing ${GRAVITY_FI} on ${REMOTE_HOST}" + echo_stat + primaryDBMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${GRAVITY_FI}" | sed 's/\s.*$//') + error_validate + + MESSAGE="Reanalyzing ${GRAVITY_FI} on $HOSTNAME" + echo_stat + secondDBMD5=$(md5sum ${PIHOLE_DIR}/${GRAVITY_FI} | sed 's/\s.*$//') + error_validate + + if [ "$SKIP_CUSTOM" != '1' ] + then + if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] + then + if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} + then + REMOTE_CUSTOM_DNS="1" + MESSAGE="Reanalyzing ${CUSTOM_DNS} on ${REMOTE_HOST}" + echo_stat + + primaryCLMD5=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//'") + error_validate + + MESSAGE="Reanalyzing ${CUSTOM_DNS} on $HOSTNAME" + echo_stat + secondCLMD5=$(md5sum ${PIHOLE_DIR}/${CUSTOM_DNS} | sed 's/\s.*$//') + error_validate + else + MESSAGE="No ${CUSTOM_DNS} Detected on ${REMOTE_HOST}" + echo_info + fi + else + if ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} test -e ${PIHOLE_DIR}/${CUSTOM_DNS} + then + REMOTE_CUSTOM_DNS="1" + MESSAGE="${REMOTE_HOST} has ${CUSTOM_DNS}" + echo_info + fi + MESSAGE="No ${CUSTOM_DNS} Detected on $HOSTNAME" + echo_info + fi + fi +} \ No newline at end of file diff --git a/includes/gs-intent.sh b/includes/gs-intent.sh new file mode 100644 index 0000000..399b2be --- /dev/null +++ b/includes/gs-intent.sh @@ -0,0 +1,41 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-intent.sh ############################### + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Validate Intent +function intent_validate { + if [ "$VERIFY_PASS" == "0" ] + then + PHASER=$((( RANDOM % 4 ) + 1 )) + if [ "$PHASER" = "1" ] + then + INTENT="FIRE PHOTON TORPEDOS" + elif [ "$PHASER" = "2" ] + then + INTENT="FIRE ALL PHASERS" + elif [ "$PHASER" = "3" ] + then + INTENT="EJECT THE WARPCORE" + elif [ "$PHASER" = "4" ] + then + INTENT="ENGAGE TRACTOR BEAM" + fi + + MESSAGE="Type ${INTENT} to Confirm" + echo_need + + read INPUT_INTENT + + if [ "${INPUT_INTENT}" != "${INTENT}" ] + then + MESSAGE="${TASKTYPE} Aborted" + echo_info + exit_nochange + fi + else + MESSAGE="Verification Bypassed" + echo_warn + fi +} \ No newline at end of file diff --git a/includes/gs-logging.sh b/includes/gs-logging.sh new file mode 100644 index 0000000..5d20b4d --- /dev/null +++ b/includes/gs-logging.sh @@ -0,0 +1,94 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-logging.sh ############################## + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Logs Task +function task_logs { + TASKTYPE='LOGS' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + logs_gs +} + +## Core Logging +### Write Logs Out +function logs_export { + if [ "${TASKTYPE}" != "BACKUP" ] + then + MESSAGE="Saving File Hashes" + echo_stat + rm -f ${LOG_PATH}/${HISTORY_MD5} + echo -e ${primaryDBMD5} >> ${LOG_PATH}/${HISTORY_MD5} + echo -e ${secondDBMD5} >> ${LOG_PATH}/${HISTORY_MD5} + echo -e ${primaryCLMD5} >> ${LOG_PATH}/${HISTORY_MD5} + echo -e ${secondCLMD5} >> ${LOG_PATH}/${HISTORY_MD5} + error_validate + fi + + MESSAGE="Logging Successful ${TASKTYPE}" + echo_stat + echo -e $(date) "[${TASKTYPE}]" >> ${LOG_PATH}/${SYNCING_LOG} + error_validate +} + +### Output Sync Logs +function logs_gs { + MESSAGE="Tailing ${LOG_PATH}/${SYNCING_LOG}" + echo_info + + echo -e "========================================================" + echo -e "Recent Complete ${YELLOW}SMART${NC} Executions" + tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep SMART + echo -e "Recent Complete ${YELLOW}PULL${NC} Executions" + tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep PULL + echo -e "Recent Complete ${YELLOW}PUSH${NC} Executions" + tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep PUSH + echo -e "Recent Complete ${YELLOW}BACKUP${NC} Executions" + tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep BACKUP + echo -e "Recent Complete ${YELLOW}RESTORE${NC} Executions" + tail -n 7 "${LOG_PATH}/${SYNCING_LOG}" | grep RESTORE + echo -e "========================================================" + + exit_nochange +} + +## Crontab Logs +### Core Crontab Logs +function show_crontab { + MESSAGE="Replaying Last Cronjob" + echo_stat + + if [ -f ${LOG_PATH}/${CRONJOB_LOG} ] + then + if [ -s ${LOG_PATH}/${CRONJOB_LOG} ] + echo_good + + MESSAGE="Tailing ${LOG_PATH}/${CRONJOB_LOG}" + echo_info + + echo -e "========================================================" + date -r ${LOG_PATH}/${CRONJOB_LOG} + cat ${LOG_PATH}/${CRONJOB_LOG} + echo -e "========================================================" + + exit_nochange + then + echo_fail + + MESSAGE="${LOG_PATH}/${CRONJOB_LOG} is Empty" + echo_info + + exit_nochange + fi + else + echo_fail + + MESSAGE="${LOG_PATH}/${CRONJOB_LOG} is Missing" + echo_info + + exit_nochange + fi +} diff --git a/includes/gs-pull.sh b/includes/gs-pull.sh new file mode 100644 index 0000000..951ac07 --- /dev/null +++ b/includes/gs-pull.sh @@ -0,0 +1,178 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-pull.sh ################################# + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Pull Task +function task_pull { + TASKTYPE='PULL' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_target + validate_gs_folders + validate_ph_folders + validate_sqlite3 + validate_os_sshpass + + pull_gs + exit +} + +## Pull Gravity +function pull_gs_grav { + + backup_local_gravity + backup_remote_gravity + + MESSAGE="Pulling ${GRAVITY_FI} from ${REMOTE_HOST}" + echo_stat + RSYNC_REPATH="rsync" + RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${GRAVITY_FI}.backup" + RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${GRAVITY_FI}.pull" + create_rsynccmd + + MESSAGE="Replacing ${GRAVITY_FI} on $HOSTNAME" + echo_stat + sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${GRAVITY_FI}.pull ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 + error_validate + + MESSAGE="Validating Settings of ${GRAVITY_FI}" + echo_stat + + GRAVDB_OWN=$(ls -ld ${PIHOLE_DIR}/${GRAVITY_FI} | awk 'OFS=":" {print $3,$4}') + if [ "$GRAVDB_OWN" != "$FILE_OWNER" ] + then + MESSAGE="Validating Ownership on ${GRAVITY_FI}" + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${GRAVITY_FI}" + echo_stat + sudo chown ${FILE_OWNER} ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 + error_validate + + MESSAGE="Continuing Validation of ${GRAVITY_FI}" + echo_stat + fi + + GRAVDB_RWE=$(namei -m ${PIHOLE_DIR}/${GRAVITY_FI} | grep -v f: | grep ${GRAVITY_FI} | awk '{print $1}') + if [ "$GRAVDB_RWE" != "-rw-rw-r--" ] + then + MESSAGE="Validating Permissions on ${GRAVITY_FI}" + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Permissions on ${GRAVITY_FI}" + echo_stat + sudo chmod 664 ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 + error_validate + + MESSAGE="Continuing Validation of ${GRAVITY_FI}" + echo_stat + fi + + echo_good +} + +## Pull Custom +function pull_gs_cust { + if [ "$SKIP_CUSTOM" != '1' ] + then + if [ "$REMOTE_CUSTOM_DNS" == "1" ] + then + backup_local_custom + backup_remote_custom + + MESSAGE="Pulling ${CUSTOM_DNS} from ${REMOTE_HOST}" + echo_stat + RSYNC_REPATH="rsync" + RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${CUSTOM_DNS}.backup" + RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${CUSTOM_DNS}.pull" + create_rsynccmd + + MESSAGE="Replacing ${CUSTOM_DNS} on $HOSTNAME" + echo_stat + sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${CUSTOM_DNS}.pull ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 + error_validate + + MESSAGE="Validating Settings on ${CUSTOM_DNS}" + echo_stat + + CUSTOMLS_OWN=$(ls -ld ${PIHOLE_DIR}/${CUSTOM_DNS} | awk '{print $3 $4}') + if [ "$CUSTOMLS_OWN" != "rootroot" ] + then + MESSAGE="Validating Ownership on ${CUSTOM_DNS}" + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${CUSTOM_DNS}" + echo_stat + sudo chown root:root ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 + error_validate + + MESSAGE="Continuing Validation of ${GRAVITY_FI}" + echo_stat + fi + + CUSTOMLS_RWE=$(namei -m ${PIHOLE_DIR}/${CUSTOM_DNS} | grep -v f: | grep ${CUSTOM_DNS} | awk '{print $1}') + if [ "$CUSTOMLS_RWE" != "-rw-r--r--" ] + then + MESSAGE="Validating Permissions on ${CUSTOM_DNS}" + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${CUSTOM_DNS}" + echo_stat + sudo chmod 644 ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 + error_validate + + MESSAGE="Continuing Validation of ${GRAVITY_FI}" + echo_stat + fi + + echo_good + fi + fi +} + +## Pull Reload +function pull_gs_reload { + MESSAGE="Isolating Regeneration Pathways" + echo_info + sleep 1 + + MESSAGE="Updating FTLDNS Configuration" + echo_stat + ${PIHOLE_BIN} restartdns reloadlists >/dev/null 2>&1 + error_validate + + MESSAGE="Reloading FTLDNS Services" + echo_stat + ${PIHOLE_BIN} restartdns >/dev/null 2>&1 + error_validate +} + +## Pull Function +function pull_gs { + previous_md5 + md5_compare + + backup_settime + pull_gs_grav + pull_gs_cust + pull_gs_reload + md5_recheck + + logs_export + exit_withchange +} \ No newline at end of file diff --git a/includes/gs-purge.sh b/includes/gs-purge.sh new file mode 100644 index 0000000..4b91eab --- /dev/null +++ b/includes/gs-purge.sh @@ -0,0 +1,78 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-purge.sh ################################ + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Purge Task +function task_purge { + TASKTYPE="THE-PURGE" + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + MESSAGE="THIS WILL RESET YOUR ENTIRE GRAVITY SYNC INSTALLATION" + echo_warn + MESSAGE="This will remove:" + echo_warn + MESSAGE="- All backups files." + echo_warn + MESSAGE="- Your ${CONFIG_FILE} file." + echo_warn + + if [ -f "$HOME/${LOCAL_FOLDR}/dev" ] + then + MESSAGE="- Your development branch updater." + elif [ -f "$HOME/${LOCAL_FOLDR}/beta" ] + then + MESSAGE="- Your beta branch updater." + fi + echo_warn + + MESSAGE="- All cronjob/automation tasks." + echo_warn + MESSAGE="- All job history/logs." + echo_warn + MESSAGE="- Associated SSH id_rsa keys." + echo_warn + MESSAGE="This function cannot be undone!" + echo_warn + + MESSAGE="YOU WILL NEED TO REBUILD GRAVITY SYNC AFTER EXECUTION" + echo_warn + + MESSAGE="Pi-hole binaries, configuration and services ARE NOT impacted!" + echo_info + MESSAGE="Your device will continue to resolve and block DNS requests," + echo_info + MESSAGE="but your ${GRAVITY_FI} and ${CUSTOM_DNS} WILL NOT sync anymore," + echo_info + MESSAGE="until you reconfigure Gravity Sync on this device." + echo_info + + intent_validate + + MESSAGE="Cleaning Gravity Sync Directory" + echo_stat + + git clean -f -X -d >/dev/null 2>&1 + error_validate + + clear_cron + + MESSAGE="Deleting SSH Key-files" + echo_stat + + rm -f $HOME/${SSH_PKIF} >/dev/null 2>&1 + rm -f $HOME/${SSH_PKIF}.pub >/dev/null 2>&1 + error_validate + + MESSAGE="Realigning Dilithium Matrix" + echo_stat + + sleep 1 + + MESSAGE="Realigning Dilithium Matrix" + echo_good + + update_gs +} \ No newline at end of file diff --git a/includes/gs-push.sh b/includes/gs-push.sh new file mode 100644 index 0000000..7210b09 --- /dev/null +++ b/includes/gs-push.sh @@ -0,0 +1,127 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-push.sh ################################# + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Push Task +function task_push { + TASKTYPE='PUSH' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_target + validate_gs_folders + validate_ph_folders + validate_sqlite3 + validate_os_sshpass + + push_gs + exit +} + +## Push Gravity +function push_gs_grav { + backup_remote_gravity + backup_local_gravity + + MESSAGE="Copying ${GRAVITY_FI} from ${REMOTE_HOST}" + echo_stat + RSYNC_REPATH="rsync" + RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${GRAVITY_FI}.backup" + RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${GRAVITY_FI}.push" + create_rsynccmd + + MESSAGE="Pushing ${GRAVITY_FI} to ${REMOTE_HOST}" + echo_stat + RSYNC_REPATH="sudo rsync" + RSYNC_SOURCE="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${GRAVITY_FI}.backup" + RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${GRAVITY_FI}" + create_rsynccmd + + MESSAGE="Setting Permissions on ${GRAVITY_FI}" + echo_stat + CMD_TIMEOUT='15' + CMD_REQUESTED="sudo chmod 664 ${PIHOLE_DIR}/${GRAVITY_FI}" + create_sshcmd + + MESSAGE="Setting Ownership on ${GRAVITY_FI}" + echo_stat + CMD_TIMEOUT='15' + CMD_REQUESTED="sudo chown ${RFILE_OWNER} ${PIHOLE_DIR}/${GRAVITY_FI}" + create_sshcmd +} + +## Push Custom +function push_gs_cust { + if [ "$SKIP_CUSTOM" != '1' ] + then + if [ "$REMOTE_CUSTOM_DNS" == "1" ] + then + backup_remote_custom + backup_local_custom + + MESSAGE="Copying ${CUSTOM_DNS} from ${REMOTE_HOST}" + echo_stat + RSYNC_REPATH="rsync" + RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${CUSTOM_DNS}.backup" + RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${CUSTOM_DNS}.push" + create_rsynccmd + + MESSAGE="Pushing ${CUSTOM_DNS} to ${REMOTE_HOST}" + echo_stat + RSYNC_REPATH="sudo rsync" + RSYNC_SOURCE="$HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${BACKUPTIMESTAMP}-${CUSTOM_DNS}.backup" + RSYNC_TARGET="${REMOTE_USER}@${REMOTE_HOST}:${PIHOLE_DIR}/${CUSTOM_DNS}" + create_rsynccmd + + MESSAGE="Setting Permissions on ${CUSTOM_DNS}" + echo_stat + CMD_TIMEOUT='15' + CMD_REQUESTED="sudo chmod 644 ${PIHOLE_DIR}/${CUSTOM_DNS}" + create_sshcmd + + MESSAGE="Setting Ownership on ${CUSTOM_DNS}" + echo_stat + CMD_TIMEOUT='15' + CMD_REQUESTED="sudo chown root:root ${PIHOLE_DIR}/${CUSTOM_DNS}" + create_sshcmd + fi + fi +} + +## Push Reload +function push_gs_reload { + MESSAGE="Inverting Tachyon Pulses" + echo_info + sleep 1 + + MESSAGE="Updating Remote FTLDNS Configuration" + echo_stat + CMD_TIMEOUT='15' + CMD_REQUESTED="${RIHOLE_BIN} restartdns reloadlists" + create_sshcmd + + MESSAGE="Reloading Remote FTLDNS Services" + echo_stat + CMD_TIMEOUT='15' + CMD_REQUESTED="${RIHOLE_BIN} restartdns" + create_sshcmd +} + +## Push Function +function push_gs { + previous_md5 + md5_compare + backup_settime + + intent_validate + + push_gs_grav + push_gs_cust + push_gs_reload + + md5_recheck + logs_export + exit_withchange +} \ No newline at end of file diff --git a/includes/gs-restore.sh b/includes/gs-restore.sh new file mode 100644 index 0000000..f1f1db0 --- /dev/null +++ b/includes/gs-restore.sh @@ -0,0 +1,209 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-restore.sh ############################## + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Restore Task +function task_restore { + TASKTYPE='RESTORE' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_target + validate_gs_folders + validate_ph_folders + validate_sqlite3 + + restore_gs + exit +} + +## Restore Gravity +function restore_gs { + MESSAGE="This will restore your settings on $HOSTNAME with a previous version!" + echo_warn + + MESSAGE="PREVIOUS BACKUPS AVAILABLE FOR RESTORATION" + echo_info + ls $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD} | grep $(date +%Y) | grep ${GRAVITY_FI} | colrm 18 + + MESSAGE="Select backup date to restore ${GRAVITY_FI} from" + echo_need + read INPUT_BACKUP_DATE + + if [ -f $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_BACKUP_DATE}-${GRAVITY_FI}.backup ] + then + MESSAGE="Backup File Selected" + else + MESSAGE="Invalid Request" + echo_info + + exit_nochange + fi + + if [ "$SKIP_CUSTOM" != '1' ] + then + + if [ -f ${PIHOLE_DIR}/${CUSTOM_DNS} ] + then + ls $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD} | grep $(date +%Y) | grep ${CUSTOM_DNS} | colrm 18 + + MESSAGE="Select backup date to restore ${CUSTOM_DNS} from" + echo_need + read INPUT_DNSBACKUP_DATE + + if [ -f $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_DNSBACKUP_DATE}-${CUSTOM_DNS}.backup ] + then + MESSAGE="Backup File Selected" + else + MESSAGE="Invalid Request" + echo_info + + exit_nochange + fi + fi + fi + + MESSAGE="${GRAVITY_FI} from ${INPUT_BACKUP_DATE} Selected" + echo_info + MESSAGE="${CUSTOM_DNS} from ${INPUT_DNSBACKUP_DATE} Selected" + echo_info + + intent_validate + + MESSAGE="Making Time Warp Calculations" + echo_info + + MESSAGE="Stopping Pi-hole Services" + echo_stat + + sudo service pihole-FTL stop >/dev/null 2>&1 + error_validate + + MESSAGE="Restoring ${GRAVITY_FI} on $HOSTNAME" + echo_stat + sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_BACKUP_DATE}-${GRAVITY_FI}.backup ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 + error_validate + + MESSAGE="Validating Ownership on ${GRAVITY_FI}" + echo_stat + + GRAVDB_OWN=$(ls -ld ${PIHOLE_DIR}/${GRAVITY_FI} | awk 'OFS=":" {print $3,$4}') + if [ "$GRAVDB_OWN" == "$FILE_OWNER" ] + then + echo_good + else + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${GRAVITY_FI}" + echo_stat + sudo chown ${FILE_OWNER} ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 + error_validate + fi + + MESSAGE="Validating Permissions on ${GRAVITY_FI}" + echo_stat + + GRAVDB_RWE=$(namei -m ${PIHOLE_DIR}/${GRAVITY_FI} | grep -v f: | grep ${GRAVITY_FI} | awk '{print $1}') + if [ "$GRAVDB_RWE" = "-rw-rw-r--" ] + then + echo_good + else + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${GRAVITY_FI}" + echo_stat + sudo chmod 664 ${PIHOLE_DIR}/${GRAVITY_FI} >/dev/null 2>&1 + error_validate + fi + + if [ "$SKIP_CUSTOM" != '1' ] + then + if [ -f $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_DNSBACKUP_DATE}-${CUSTOM_DNS}.backup ] + then + MESSAGE="Restoring ${CUSTOM_DNS} on $HOSTNAME" + echo_stat + sudo cp $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}/${INPUT_DNSBACKUP_DATE}-${CUSTOM_DNS}.backup ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 + error_validate + + MESSAGE="Validating Ownership on ${CUSTOM_DNS}" + echo_stat + + CUSTOMLS_OWN=$(ls -ld ${PIHOLE_DIR}/${CUSTOM_DNS} | awk '{print $3 $4}') + if [ "$CUSTOMLS_OWN" == "rootroot" ] + then + echo_good + else + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${CUSTOM_DNS}" + echo_stat + sudo chown root:root ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 + error_validate + fi + + MESSAGE="Validating Permissions on ${CUSTOM_DNS}" + echo_stat + + CUSTOMLS_RWE=$(namei -m ${PIHOLE_DIR}/${CUSTOM_DNS} | grep -v f: | grep ${CUSTOM_DNS} | awk '{print $1}') + if [ "$CUSTOMLS_RWE" == "-rw-r--r--" ] + then + echo_good + else + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Setting Ownership on ${CUSTOM_DNS}" + echo_stat + sudo chmod 644 ${PIHOLE_DIR}/${CUSTOM_DNS} >/dev/null 2>&1 + error_validate + fi + fi + fi + + MESSAGE="Evacuating Saucer Section" + echo_info + sleep 1 + + MESSAGE="Restarting FTLDNS Services" + echo_stat + sudo service pihole-FTL start >/dev/null 2>&1 + error_validate + + MESSAGE="Updating FTLDNS Configuration" + echo_stat + ${PIHOLE_BIN} restartdns reloadlists >/dev/null 2>&1 + error_validate + + + MESSAGE="Do you want to push the restored configuration to the primary Pi-hole? (yes/no)" + echo_need + read PUSH_TO_PRIMARY + + if [ "${PUSH_TO_PRIMARY}" == "Yes" ] || [ "${PUSH_TO_PRIMARY}" == "yes" ] || [ "${PUSH_TO_PRIMARY}" == "Y" ] || [ "${PUSH_TO_PRIMARY}" == "y" ] + then + push_gs + elif [ "${PUSH_TO_PRIMARY}" == "No" ] || [ "${PUSH_TO_PRIMARY}" == "no" ] || [ "${PUSH_TO_PRIMARY}" == "N" ] || [ "${PUSH_TO_PRIMARY}" == "n" ] + then + logs_export + exit_withchange + else + MESSAGE="Invalid Selection - Defaulting No" + echo_warn + + logs_export + exit_withchange + fi +} \ No newline at end of file diff --git a/includes/gs-root.sh b/includes/gs-root.sh new file mode 100644 index 0000000..7d17238 --- /dev/null +++ b/includes/gs-root.sh @@ -0,0 +1,40 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-root.sh ################################# + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Sudo Creation Task +function task_sudo { + TASKTYPE='SUDO' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + MESSAGE="Creating Sudoer.d Template" + echo_stat + + NEW_SUDO_USER=$(whoami) + echo -e "${NEW_SUDO_USER} ALL=(ALL) NOPASSWD: ${PIHOLE_DIR}" > $HOME/${LOCAL_FOLDR}/templates/gs-nopasswd.sudo + error_validate + + MESSAGE="Installing Sudoer.d File" + echo_stat + + sudo install -m 0440 $HOME/${LOCAL_FOLDR}/templates/gs-nopasswd.sudo /etc/sudoers.d/gs-nopasswd + error_validate +} + +## Root Check +function root_check { + if [ ! "$EUID" -ne 0 ] + then + TASKTYPE='ROOT' + MESSAGE="${MESSAGE} ${TASKTYPE}" + echo_fail + + MESSAGE="${PROGRAM} Must Not Run as Root" + echo_warn + + exit_nochange + fi +} \ No newline at end of file diff --git a/includes/gs-smart.sh b/includes/gs-smart.sh new file mode 100644 index 0000000..8754a2d --- /dev/null +++ b/includes/gs-smart.sh @@ -0,0 +1,153 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-smart.sh ################################ + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Smart Task +function task_smart { + TASKTYPE='SMART' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_target + validate_gs_folders + validate_ph_folders + validate_sqlite3 + validate_os_sshpass + + smart_gs + exit +} + +## Smart Sync Function +function smart_gs { + previous_md5 + md5_compare + backup_settime + + PRIDBCHANGE="0" + SECDBCHANGE="0" + PRICLCHANGE="0" + SECCLCHANGE="0" + + if [ "${primaryDBMD5}" != "${last_primaryDBMD5}" ] + then + PRIDBCHANGE="1" + fi + + if [ "${secondDBMD5}" != "${last_secondDBMD5}" ] + then + SECDBCHANGE="1" + fi + + if [ "${PRIDBCHANGE}" == "${SECDBCHANGE}" ] + then + if [ "${PRIDBCHANGE}" != "0" ] + then + MESSAGE="Both ${GRAVITY_FI} Changed" + echo_warn + + PRIDBDATE=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${PIHOLE_DIR}/${GRAVITY_FI}") + SECDBDATE=$(stat -c %Y ${PIHOLE_DIR}/${GRAVITY_FI}) + + if [ "${PRIDBDATE}" -gt "$SECDBDATE" ] + then + MESSAGE="Primary ${GRAVITY_FI} Last Changed" + echo_info + + pull_gs_grav + PULLRESTART="1" + else + MESSAGE="Secondary ${GRAVITY_FI} Last Changed" + echo_info + + push_gs_grav + PUSHRESTART="1" + fi + fi + else + if [ "${PRIDBCHANGE}" != "0" ] + then + pull_gs_grav + PULLRESTART="1" + elif [ "${SECDBCHANGE}" != "0" ] + then + push_gs_grav + PUSHRESTART="1" + fi + fi + + if [ "${primaryCLMD5}" != "${last_primaryCLMD5}" ] + then + PRICLCHANGE="1" + fi + + if [ "${secondCLMD5}" != "${last_secondCLMD5}" ] + then + SECCLCHANGE="1" + fi + + if [ "$SKIP_CUSTOM" != '1' ] + then + + if [ -f "${PIHOLE_DIR}/${CUSTOM_DNS}" ] + then + + if [ "${PRICLCHANGE}" == "${SECCLCHANGE}" ] + then + if [ "${PRICLCHANGE}" != "0" ] + then + MESSAGE="Both ${CUSTOM_DNS} Changed" + echo_warn + + PRICLDATE=$(${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" ${REMOTE_USER}@${REMOTE_HOST} "stat -c %Y ${PIHOLE_DIR}/${CUSTOM_DNS}") + SECCLDATE=$(stat -c %Y ${PIHOLE_DIR}/${CUSTOM_DNS}) + + if [ "${PRICLDATE}" -gt "${SECCLDATE}" ] + then + MESSAGE="Primary ${CUSTOM_DNS} Last Changed" + echo_info + + pull_gs_cust + PULLRESTART="1" + else + MESSAGE="Secondary ${CUSTOM_DNS} Last Changed" + echo_info + + push_gs_cust + PUSHRESTART="1" + fi + fi + else + if [ "${PRICLCHANGE}" != "0" ] + then + pull_gs_cust + PULLRESTART="1" + elif [ "${SECCLCHANGE}" != "0" ] + then + push_gs_cust + PUSHRESTART="1" + fi + fi + else + pull_gs_cust + PULLRESTART="1" + fi + fi + + if [ "$PULLRESTART" == "1" ] + then + pull_gs_reload + fi + + if [ "$PUSHRESTART" == "1" ] + then + push_gs_reload + fi + + md5_recheck + + logs_export + exit_withchange +} \ No newline at end of file diff --git a/includes/gs-ssh.sh b/includes/gs-ssh.sh new file mode 100644 index 0000000..e77828d --- /dev/null +++ b/includes/gs-ssh.sh @@ -0,0 +1,185 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-ssh.sh ################################## + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Determine SSH Pathways +function create_sshcmd { + if hash ssh 2>/dev/null + then + if [ -z "$SSHPASSWORD" ] + then + timeout --preserve-status ${CMD_TIMEOUT} ${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF} -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} "${CMD_REQUESTED}" + error_validate + else + timeout --preserve-status ${CMD_TIMEOUT} ${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} "${CMD_REQUESTED}" + error_validate + fi + fi +} + +## Determine SSH Pathways +function create_rsynccmd { + if hash ssh 2>/dev/null + then + if [ -z "$SSHPASSWORD" ] + then + rsync --rsync-path="${RSYNC_REPATH}" -e "${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF}" ${RSYNC_SOURCE} ${RSYNC_TARGET} >/dev/null 2>&1 + error_validate + else + rsync --rsync-path="${RSYNC_REPATH}" -e "${SSHPASSWORD} ${SSH_CMD} -p ${SSH_PORT} -i $HOME/${SSH_PKIF}" ${RSYNC_SOURCE} ${RSYNC_TARGET} >/dev/null 2>&1 + error_validate + fi + fi +} + +## Detect SSH-KEYGEN +function detect_sshkeygen { + MESSAGE="Validating SSH-KEYGEN install on $HOSTNAME" + echo_stat + + if hash ssh-keygen >/dev/null 2>&1 + then + echo_good + else + echo_fail + MESSAGE="SSH-KEYGEN is Required" + echo_info + + exit_nochange + fi +} + +function generate_sshkey { + if [ -z $INPUT_REMOTE_PASS ] + then + if [ -f $HOME/${SSH_PKIF} ] + then + MESSAGE="Using Existing ~/${SSH_PKIF}" + echo_info + else + if hash ssh-keygen >/dev/null 2>&1 + then + MESSAGE="Generating ~/${SSH_PKIF} (SSH-KEYGEN)" + echo_stat + + ssh-keygen -q -P "" -t rsa -f $HOME/${SSH_PKIF} >/dev/null 2>&1 + error_validate + else + MESSAGE="No SSH Key Generator Located" + echo_warn + exit_nochange + fi + fi + fi +} + +function export_sshkey { + if [ -z $REMOTE_PASS ] + then + if [ -f $HOME/${SSH_PKIF} ] + then + MESSAGE="Registering Key-Pair on ${REMOTE_HOST}" + echo_info + + ssh-copy-id -f -p ${SSH_PORT} -i $HOME/${SSH_PKIF}.pub ${REMOTE_USER}@${REMOTE_HOST} + else + MESSAGE="Error Registering Key-Pair" + echo_warn + fi + fi +} + +## Detect SSH & RSYNC +function detect_ssh { + MESSAGE="Validating SSH Client on $HOSTNAME" + echo_stat + + if hash ssh 2>/dev/null + then + MESSAGE="${MESSAGE} (OpenSSH)" + echo_good + SSH_CMD='ssh' + elif hash dbclient 2>/dev/null + then + MESSAGE="${MESSAGE} (Dropbear)" + echo_fail + + MESSAGE="Dropbear not supported in GS ${VERSION}" + echo_info + exit_nochange + else + echo_fail + + MESSAGE="Attempting to Compensate" + echo_warn + MESSAGE="Installing SSH Client with ${PKG_MANAGER}" + echo_stat + + ${PKG_INSTALL} ssh-client >/dev/null 2>&1 + error_validate + fi + + MESSAGE="Validating RSYNC Installed on $HOSTNAME" + echo_stat + + if hash rsync 2>/dev/null + then + echo_good + else + echo_fail + MESSAGE="RSYNC is Required" + echo_warn + + distro_check + + MESSAGE="Attempting to Compensate" + echo_warn + + MESSAGE="Installing RSYNC with ${PKG_MANAGER}" + echo_stat + ${PKG_INSTALL} rsync >/dev/null 2>&1 + error_validate + fi +} + +function detect_remotersync { + MESSAGE="Creating Test File on ${REMOTE_HOST}" + echo_stat + + CMD_TIMEOUT='15' + CMD_REQUESTED="touch ~/gs.test" + create_sshcmd + + MESSAGE="If pull test fails ensure RSYNC is installed on ${REMOTE_HOST}" + echo_warn + + MESSAGE="Pulling Test File to $HOSTNAME" + echo_stat + + RSYNC_REPATH="rsync" + RSYNC_SOURCE="${REMOTE_USER}@${REMOTE_HOST}:~/gs.test" + RSYNC_TARGET="$HOME/${LOCAL_FOLDR}/gs.test" + create_rsynccmd + + MESSAGE="Cleaning Up Local Test File" + echo_stat + + rm $HOME/${LOCAL_FOLDR}/gs.test + error_validate + + MESSAGE="Cleaning Up Remote Test File" + echo_stat + + CMD_TIMEOUT='15' + CMD_REQUESTED="rm ~/gs.test" + create_sshcmd +} + +function show_target { + MESSAGE="Targeting ${REMOTE_USER}@${REMOTE_HOST}" + echo_info + + detect_ssh +} \ No newline at end of file diff --git a/includes/gs-update.sh b/includes/gs-update.sh new file mode 100644 index 0000000..66875bf --- /dev/null +++ b/includes/gs-update.sh @@ -0,0 +1,143 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-update.sh ############################### + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Master Branch +function update_gs { + if [ -f "$HOME/${LOCAL_FOLDR}/dev" ] + then + source $HOME/${LOCAL_FOLDR}/dev + else + BRANCH='origin/master' + fi + + if [ "$BRANCH" != "origin/master" ] + then + MESSAGE="Pulling from ${BRANCH}" + echo_info + fi + + GIT_CHECK=$(git status | awk '{print $1}') + if [ "$GIT_CHECK" == "fatal:" ] + then + MESSAGE="Requires GitHub Installation" + echo_warn + exit_nochange + else + MESSAGE="Updating Cache" + echo_stat + git fetch --all >/dev/null 2>&1 + error_validate + MESSAGE="Applying Update" + echo_stat + git reset --hard ${BRANCH} >/dev/null 2>&1 + error_validate + fi +} + +## Show Version +function show_version { + echo -e "========================================================" + MESSAGE="${BOLD}${PROGRAM}${NC} by ${CYAN}@vmstan${NC}" + echo_info + + MESSAGE="${BLUE}https://github.com/vmstan/gravity-sync${NC}" + echo_info + + if [ -f $HOME/${LOCAL_FOLDR}/dev ] + then + DEVVERSION="dev" + elif [ -f $HOME/${LOCAL_FOLDR}/beta ] + then + DEVVERSION="beta" + else + DEVVERSION="" + fi + + MESSAGE="Running Version: ${GREEN}${VERSION}${NC} ${DEVVERSION}" + echo_info + + GITVERSION=$(curl -sf https://raw.githubusercontent.com/vmstan/gravity-sync/master/VERSION) + if [ -z "$GITVERSION" ] + then + MESSAGE="Latest Version: ${RED}Unknown${NC}" + else + if [ "$GITVERSION" != "$VERSION" ] + then + MESSAGE="Update Available: ${PURPLE}${GITVERSION}${NC}" + else + MESSAGE="Latest Version: ${GREEN}${GITVERSION}${NC}" + fi + fi + echo_info + echo -e "========================================================" +} + +## Devmode Task +function task_devmode { + TASKTYPE='DEV' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + if [ -f $HOME/${LOCAL_FOLDR}/dev ] + then + MESSAGE="Disabling ${TASKTYPE}" + echo_stat + rm -f $HOME/${LOCAL_FOLDR}/dev + error_validate + elif [ -f $HOME/${LOCAL_FOLDR}/beta ] + then + MESSAGE="Disabling BETA" + echo_stat + rm -f $HOME/${LOCAL_FOLDR}/beta + error_validate + + MESSAGE="Enabling ${TASKTYPE}" + echo_stat + touch $HOME/${LOCAL_FOLDR}/dev + error_validate + else + MESSAGE="Enabling ${TASKTYPE}" + echo_stat + touch $HOME/${LOCAL_FOLDR}/dev + error_validate + + git branch -r + + MESSAGE="Select Branch to Update Against" + echo_need + read INPUT_BRANCH + + echo -e "BRANCH='${INPUT_BRANCH}'" >> $HOME/${LOCAL_FOLDR}/dev + fi + + MESSAGE="Run UPDATE to apply changes" + echo_info + + exit_withchange +} + +## Update Task +function task_update { + TASKTYPE='UPDATE' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + dbclient_warning + + update_gs + + exit_withchange +} + +## Version Task +function task_version { + TASKTYPE='VERSION' + MESSAGE="${MESSAGE}: ${TASKTYPE} Requested" + echo_good + + show_version + exit_nochange +} \ No newline at end of file diff --git a/includes/gs-validate.sh b/includes/gs-validate.sh new file mode 100644 index 0000000..96f90a8 --- /dev/null +++ b/includes/gs-validate.sh @@ -0,0 +1,148 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-validate.sh ############################# + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code is called from the main gravity-sync.sh file and should not execute directly! + +## Validate GS Folders +function validate_gs_folders { + MESSAGE="Validating ${PROGRAM} Folders on $HOSTNAME" + echo_stat + if [ ! -d $HOME/${LOCAL_FOLDR} ] + then + MESSAGE="Unable to Find $HOME/${LOCAL_FOLDR}" + echo_fail + exit_nochange + fi + + if [ ! -d $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD} ] + then + MESSAGE="Unable to Find $HOME/${LOCAL_FOLDR}/${BACKUP_FOLD}" + echo_fail + exit_nochange + fi + echo_good +} + +## Validate Pi-hole Folders +function validate_ph_folders { + MESSAGE="Validating Pi-hole Configuration on $HOSTNAME" + echo_stat + if [ ! -f ${PIHOLE_BIN} ] + then + MESSAGE="Unable to Validate Pi-Hole is Installed" + echo_fail + exit_nochange + fi + + if [ ! -d ${PIHOLE_DIR} ] + then + MESSAGE="Unable to Validate Pi-Hole Configuration Directory" + echo_fail + exit_nochange + fi + echo_good +} + +## Validate SQLite3 +function validate_sqlite3 { + MESSAGE="Validating SQLITE Installed on $HOSTNAME" + echo_stat + if hash sqlite3 2>/dev/null + then + MESSAGE="SQLITE3 Utility Detected" + echo_good + else + MESSAGE="SQLITE3 Utility Missing" + echo_warn + + MESSAGE="Installing SQLLITE3 with ${PKG_MANAGER}" + echo_stat + + ${PKG_INSTALL} sqllite3 >/dev/null 2>&1 + error_validate + fi +} + +## Validate SSHPASS +function validate_os_sshpass { + SSHPASSWORD='' + + if hash sshpass 2>/dev/null + then + MESSAGE="SSHPASS Utility Detected" + echo_warn + if [ -z "$REMOTE_PASS" ] + then + MESSAGE="Using SSH Key-Pair Authentication" + echo_info + else + MESSAGE="Testing Authentication Options" + echo_stat + + timeout 5 ssh -p ${SSH_PORT} -i "$HOME/${SSH_PKIF}" -o StrictHostKeyChecking=no ${REMOTE_USER}@${REMOTE_HOST} 'exit' >/dev/null 2>&1 + if [ "$?" != "0" ] + then + SSHPASSWORD="sshpass -p ${REMOTE_PASS}" + MESSAGE="Using SSH Password Authentication" + echo_warn + else + MESSAGE="Valid Key-Pair Detected ${NC}(${RED}Password Ignored${NC})" + echo_info + fi + fi + else + SSHPASSWORD='' + MESSAGE="Using SSH Key-Pair Authentication" + echo_info + fi + + MESSAGE="Validating Connection to ${REMOTE_HOST}" + echo_stat + + CMD_TIMEOUT='5' + CMD_REQUESTED="exit" + create_sshcmd + +} + +## Detect Package Manager +function distro_check { + if hash apt-get 2>/dev/null + then + PKG_MANAGER="apt-get" + PKG_INSTALL="sudo apt-get --yes --no-install-recommends --quiet install" + elif hash rpm 2>/dev/null + then + if hash dnf 2>/dev/null + then + PKG_MANAGER="dnf" + elif hash yum 2>/dev/null + then + PKG_MANAGER="yum" + else + MESSAGE="Unable to find OS Package Manager" + echo_info + exit_nochange + fi + PKG_INSTALL="sudo ${PKG_MANAGER} install -y" + else + MESSAGE="Unable to find OS Package Manager" + echo_info + exit_nochange + fi +} + +## Dropbear Warning +function dbclient_warning { + if hash dbclient 2>/dev/null + then + if hash ssh 2>/dev/null + then + NOEMPTYBASHIF="1" + else + MESSAGE="Dropbear support has been deprecated - please convert to OpenSSH" + echo_warn + fi + fi +} \ No newline at end of file diff --git a/prep/gs-install.sh b/prep/gs-install.sh new file mode 100644 index 0000000..ce1549e --- /dev/null +++ b/prep/gs-install.sh @@ -0,0 +1,117 @@ +# GRAVITY SYNC BY VMSTAN ##################### +# gs-install.sh ############################## + +# For documentation or downloading updates visit https://github.com/vmstan/gravity-sync +# This code will be called from a curl call via installation instructions + +# Run this script on your primary Pi-hole to aid in preparing for Gravity Sync installation. + +set -e + +# 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' + +# Variables +CROSSCOUNT="0" +CURRENTUSER=$(whoami) + +# Header +echo -e "${YELLOW}Gravity Sync 3.0 - Installation Script${NC}" + +# Check Root +if [ ! "$EUID" -ne 0 ] +then + echo -e "[${RED}✗${NC}] Running as Root" + CROSSCOUNT=$((CROSSCOUNT+1)) +else + echo -e "[${GREEN}✓${NC}] Not Running as Root" +fi + +# Check Sudo +sudo --validate +if [ "$?" != "0" ] +then + echo -e "[${RED}✗${NC}] No Sudo Powers for ${CURRENTUSER}" + CROSSCOUNT=$((CROSSCOUNT+1)) +else + echo -e "[${GREEN}✓${NC}] Sudo Powers Valid" +fi + +# Check OpenSSH +if hash ssh +then + echo -e "[${GREEN}✓${NC}] OpenSSH Detected" +else + echo -e "[${RED}✗${NC}] OpenSSH Not Installed" + CROSSCOUNT=$((CROSSCOUNT+1)) +fi + +# Check Rsync +if hash rsync +then + echo -e "[${GREEN}✓${NC}] RSYNC Detected" +else + echo -e "[${RED}✗${NC}] RSYNC Not Installed" + CROSSCOUNT=$((CROSSCOUNT+1)) +fi + +# Check SQLITE3 +if hash sqlite3 +then + echo -e "[${GREEN}✓${NC}] SQLITE3 Detected" +else + echo -e "[${RED}✗${NC}] SQLITE3 Not Installed" + CROSSCOUNT=$((CROSSCOUNT+1)) +fi + +# Check GIT +if hash git +then + echo -e "[${GREEN}✓${NC}] GIT Detected" +else + echo -e "[${RED}✗${NC}] GIT Not Installed" + CROSSCOUNT=$((CROSSCOUNT+1)) +fi + +# Check Pihole +if hash pihole +then + echo -e "[${GREEN}✓${NC}] Pi-Hole Detected" +else + echo -e "[${RED}✗${NC}] Pi-hole Not Installed" + CROSSCOUNT=$((CROSSCOUNT+1)) +fi + +# Combine Outputs +if [ "$CROSSCOUNT" != "0" ] +then + echo -e "[${PURPLE}!${NC}] ${RED}${CROSSCOUNT}${NC} failures detected, correct these errors before deploying Gravity Sync!" +else + echo -e "[${CYAN}>${NC}] Creating Sudoers.d File" + touch /tmp/gs-nopasswd.sudo + echo -e "${CURRENTUSER} ALL=(ALL) NOPASSWD: /etc/pihole" > /tmp/gs-nopasswd.sudo + sudo install -m 0440 /tmp/gs-nopasswd.sudo /etc/sudoers.d/gs-nopasswd + + if [ "$GS_INSTALL" != "secondary" ] + then + echo -e "[${YELLOW}i${NC}] This host is prepared to use Gravity Sync, you may log off now!" + echo -e "[${YELLOW}i${NC}] Run this script again on your secondary Pi-hole host to proceed." + echo -e "[${YELLOW}i${NC}] Visit https://github.com/vmstan/gravity-sync for more instructions." + else + echo -e "[${CYAN}>${NC}] Creating Gravity Sync Directories" + git clone https://github.com/vmstan/gravity-sync.git $HOME/gravity-sync + echo -e "[${YELLOW}i${NC}] This host is now prepared to configure Gravity Sync!" + echo -e "[${YELLOW}i${NC}] Please run './gravity-sync configure' from $HOME/gravity-sync" + echo -e "[${YELLOW}i${NC}] Visit https://github.com/vmstan/gravity-sync for more instructions." + fi + +fi + +exit \ No newline at end of file diff --git a/gs-nopasswd.sudo b/templates/gs-nopasswd.sudo similarity index 100% rename from gs-nopasswd.sudo rename to templates/gs-nopasswd.sudo