diff --git a/CMakeLists.txt b/CMakeLists.txt index 95443aa..951d874 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,7 @@ cmake_minimum_required(VERSION 2.8) +include(GNUInstallDirs) + # Grive version. remember to update it for every new release! set( GRIVE_VERSION "0.5.1-dev" ) @@ -7,5 +9,7 @@ set( GRIVE_VERSION "0.5.1-dev" ) add_definitions( -DVERSION="${GRIVE_VERSION}" ) add_definitions( -D_FILE_OFFSET_BITS=64 -std=c++0x ) +add_subdirectory( systemd ) add_subdirectory( libgrive ) add_subdirectory( grive ) + \ No newline at end of file diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt new file mode 100644 index 0000000..e3e57ba --- /dev/null +++ b/systemd/CMakeLists.txt @@ -0,0 +1,26 @@ +SET(GRIVE_SYNC_SH_BINARY "${CMAKE_INSTALL_PREFIX}/lib/grive/grive-sync.sh") + +CONFIGURE_FILE(grive-changes@.service.in grive-changes@.service @ONLY) +CONFIGURE_FILE(grive-timer@.service.in grive-timer@.service @ONLY) + +install( + FILES + ${CMAKE_BINARY_DIR}/systemd/grive-changes@.service + ${CMAKE_BINARY_DIR}/systemd/grive-timer@.service + DESTINATION + lib/systemd/user +) + +install( + FILES + grive-timer@.timer + DESTINATION + lib/systemd/user +) + +install( + PROGRAMS + grive-sync.sh + DESTINATION + lib/grive +) \ No newline at end of file diff --git a/systemd/grive-changes@.service.in b/systemd/grive-changes@.service.in new file mode 100644 index 0000000..172ab19 --- /dev/null +++ b/systemd/grive-changes@.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=Google drive sync (changed files) + +[Service] +ExecStart=@GRIVE_SYNC_SH_BINARY@ listen "%i" +Type=simple +Restart=always +RestartSec=30 + +[Install] +WantedBy=default.target diff --git a/systemd/grive-sync.sh b/systemd/grive-sync.sh new file mode 100755 index 0000000..f070ac0 --- /dev/null +++ b/systemd/grive-sync.sh @@ -0,0 +1,118 @@ +#!/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} + +if [[ -z ${2} ]] || [[ ! -d ${2} ]] ; then + echo "Need a directory name in the current users home directory as second argument. Aborting." + exit 1 +fi + +DIRECTORY=$2 + +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} + if ping -c1 -W1 -q accounts.google.com >/dev/null 2>&1; then + true + # pass + else + echo "Google drive server not reachable..." + exit 0 + fi + + reset_timer_and_exit() { echo "Retriggered google drive sync" && touch -m $LOCKFILE && exit; } + + exlock_now || reset_timer_and_exit + + 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 + exit 0 +} + +### LISTEN TO DIRECTORY CHANGES ### + + +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 ${_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 \ No newline at end of file diff --git a/systemd/grive-timer@.service.in b/systemd/grive-timer@.service.in new file mode 100644 index 0000000..a6aae6a --- /dev/null +++ b/systemd/grive-timer@.service.in @@ -0,0 +1,6 @@ +[Unit] +Description=Google drive sync +After=network-online.target + +[Service] +ExecStart=@GRIVE_SYNC_SH_BINARY@ sync "%i" diff --git a/systemd/grive-timer@.timer b/systemd/grive-timer@.timer new file mode 100644 index 0000000..32f14b9 --- /dev/null +++ b/systemd/grive-timer@.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Google drive sync (fixed intervals) + +[Timer] +OnCalendar=*:0/5 +OnBootSec=3min +OnUnitActiveSec=5min +Unit=grive-timer@%i.service + +[Install] +WantedBy=timers.target