#!/bin/bash # Copyright (C) 2009 Przemyslaw Pawelczyk # (C) 2017 Jan Schulz ## ## This script is licensed under the terms of the MIT license. ## https://opensource.org/licenses/MIT # Fail on all errors set -o pipefail # We always start in the current users home directory so that names always start there cd ~ ### ARGUMENT PARSING ### SCRIPT="${0}" DIRECTORY=$(systemd-escape --unescape -- "$2") if [[ -z "$DIRECTORY" ]] || [[ ! -d "$DIRECTORY" ]] ; then echo "Need a directory name in the current users home directory as second argument. Aborting." exit 1 fi if [[ -z "${1}" ]] ; then echo "Need a command as first argument. Aborting." exit 1 else if [[ "sync" == "${1}" ]] ; then COMMAND=sync elif [[ "listen" == "${1}" ]] ; then COMMAND=listen else echo "Unknown command. Aborting." exit 1 fi fi ### LOCKFILE BOILERPLATE ### LOCKFILE="/run/user/"$(id -u)"/"$(basename "$0")"_"${DIRECTORY//\//_}"" LOCKFD=99 # PRIVATE _lock() { flock -"$1" "$LOCKFD"; } _no_more_locking() { _lock u; _lock xn && rm -f "$LOCKFILE"; } _prepare_locking() { eval "exec "$LOCKFD">\""$LOCKFILE"\""; trap _no_more_locking EXIT; } # ON START _prepare_locking # PUBLIC exlock_now() { _lock xn; } # obtain an exclusive lock immediately or fail exlock() { _lock x; } # obtain an exclusive lock shlock() { _lock s; } # obtain a shared lock unlock() { _lock u; } # drop a lock ### SYNC SCRIPT ### # Idea: only let one script run, but if the sync script is called a second time # make sure we sync a second time, too sync_directory() { _directory="${1}" reset_timer_and_exit() { echo "Retriggered google drive sync ('${_directory}')" && touch -m $LOCKFILE && exit; } exlock_now || reset_timer_and_exit if ping -c1 -W1 -q accounts.google.com >/dev/null 2>&1; then true # pass else echo "Google drive server not reachable, NOT syncing..." unlock exit 0 fi TIME_AT_START=0 TIME_AT_END=1 while [[ "${TIME_AT_START}" -lt "${TIME_AT_END}" ]]; do echo "Syncing '${_directory}'..." TIME_AT_START="$(stat -c %Y "$LOCKFILE")" grive -p "${_directory}" 2>&1 | grep -v -E "^Reading local directories$|^Reading remote server file list$|^Synchronizing files$|^Finished!$" TIME_AT_END="$(stat -c %Y "$LOCKFILE")" echo "Sync of '${_directory}' done." done # always exit ok, so that we never go into a wrong systemd state unlock exit 0 } ### LISTEN TO CHANGES IN DIRECTORY ### listen_directory() { _directory="${1}" type inotifywait >/dev/null 2>&1 || { echo >&2 "I require inotifywait but it's not installed. Aborting."; exit 1; } echo "Listening for changes in '${_directory}'" while true #run indefinitely do # Use a different call to not need to change exit into return inotifywait -q -r -e modify,attrib,close_write,move,create,delete --exclude ".grive_state|.grive" "${_directory}" > /dev/null 2>&1 && ${SCRIPT} sync $(systemd-escape "${_directory}") #echo ${SCRIPT} "${_directory}" done # always exit ok, so that we never go into a wrong systemd state exit 0 } if [[ "${COMMAND}" == listen ]] ; then listen_directory "${DIRECTORY}" else sync_directory "${DIRECTORY}" fi # always exit ok, so that we never go into a wrong systemd state exit 0