diff --git a/msynk.sh b/msynk.sh index 0e204c0..b400041 100755 --- a/msynk.sh +++ b/msynk.sh @@ -1,15 +1,14 @@ #!/bin/bash # Made by Matthias Quintern -# 02/2022 +# 03/2022 # This software comes with no warranty. +# This software is licensed under the GPL3 # ABOUT # Synchronizes files from a sender to a receiver using rsync # The files can be encrypted before or decrypted after the transfer using the mkrypt script -# # SETTINGS -# # The directory that is searched for config files CONFIG_DIR=~/.config/msynk/ # When using encrypt/decrypt, the source is en/decrypted to TMP_DIR and then rsynced to receiver from there @@ -22,7 +21,7 @@ mkrypt=/usr/bin/mkrypt # Additional flags for mkrypt mkrypt_flags= -rsync_flags=(-ruvh) +rsync_flags=(-ruh) # rsync_flags+=(--rsh="ssh -p 69") # r - relative # v - verbose @@ -33,15 +32,15 @@ rsync_flags=(-ruvh) # see "man rsync" for more -# # UTILITY -# FMT_MESSAGE="\e[1;34m%s\e[0m\n" FMT_ERROR="\e[1;31mERROR: \e[0m%s\n" -FMT_SYNC="\e[1;32m:Syncing\e[0m %s\n" -# FMT_UPDATE="\e[1;33mUpdating:\e[0m %s\n" +FMT_SYNC="\e[1;32mSyncing: \e[0m%s\n" FMT_CONFIG="\e[34m%s\e:\t\e[1;33m%s\e[0m\n" +FMT_CMD="\e[1;33mRunning: \e[0m%s\n" +# FUNCTIONS +# silence commands quiet() { "$@" > /dev/null 2>&1; } # check if a passed parameter is contained in file @@ -59,9 +58,7 @@ check_path_in_args() } -# -# sync sender to receiver -# +# SYNC SENDER TO RECEIVER sync_sender_to_receiver() { # perform a dry run to see which files would be deleted @@ -97,7 +94,7 @@ sync_sender_to_receiver() dest=$receiver$(basename $path) elif [[ -f $path ]]; then dest=$receiver - elif [[ $sender == *@*:* ]]; then # if sender is remote, assume the path is a dir if it has a slash and a file otherwise + elif [[ $sender == *:* ]]; then # if sender is remote, assume the path is a dir if it has a slash and a file otherwise [[ $path == *"/" ]] && dest=$receiver$(basename $path) || dest=$receiver else printf "$FMT_ERROR" "Invalid path: $path"; exit 1 @@ -108,57 +105,67 @@ sync_sender_to_receiver() if [[ -n $encrypt ]]; then [[ -f $path ]] && tmp_source=$TMP_DIR$(basename $path).gpg [[ $v -ge 3 ]] && printf "$FMT_CMD" "bash $mkrypt --encrypt $path --output $TMP_DIR $mkrypt_flags" - bash $mkrypt --encrypt $path --output $TMP_DIR $mkrypt_flags || exit 1 - [[ $v -ge 3 ]] && printf "$FMT_CMD" "rsync ${rsync_flags[@]} $tmp_source $dest" - rsync "${rsync_flags[@]}" $tmp_source $dest + bash $mkrypt --encrypt $path --output $TMP_DIR $mkrypt_flags || exit 3 + [[ $v -ge 3 ]] && printf "$FMT_CMD" "rsync ${rsync_flags[*]} $tmp_source $dest" + rsync ${rsync_flags[*]} $tmp_source $dest || { printf "$FMT_ERROR" "rsync exited with exit code $?"; exit 2; } else [[ -f $path ]] && tmp_source=$TMP_DIR$(basename $path) - [[ $v -ge 3 ]] && printf "$FMT_CMD" "rsync ${rsync_flags[@]} $path $TMP_DIR" - rsync "${rsync_flags[@]}" $path $TMP_DIR + [[ $v -ge 3 ]] && printf "$FMT_CMD" "rsync ${rsync_flags[*]} $path $TMP_DIR" + rsync ${rsync_flags[*]} $path $TMP_DIR || { printf "$FMT_ERROR" "rsync exited with exit code $?"; exit 2; } [[ $v -ge 3 ]] && printf "$FMT_CMD" "bash $mkrypt --decrypt $tmp_source --output $(readlink -f $dest) $mkrypt_flags" - bash $mkrypt --decrypt $tmp_source --output $(readlink -f $dest) $mkrypt_flags || exit 1 + bash $mkrypt --decrypt $tmp_source --output $(readlink -f $dest) $mkrypt_flags || exit 3 fi else - printf "$FMT_CMD" "rsync ${rsync_flags[@]} $delete $path $dest" - rsync "${rsync_flags[@]}" $delete $path $dest + [[ $v -ge 3 ]] && printf "$FMT_CMD" "rsync ${rsync_flags[*]} $delete $path $dest" + rsync ${rsync_flags[*]} $delete $path $dest || { printf "$FMT_ERROR" "rsync exited with exit code $?"; exit 2; } fi [[ $TMP_DIR == *msynk* ]] && { rm -rf $TMP_DIR/* || { printf "$FMT_ERROR" "Can not delete \$TMP_DIR: $TMP_DIR"; exit 1; } } || { printf "$FMT_ERROR" "Will not delete \$TMP_DIR because it might be dangerous (path does not contain 'msynk'): $TMP_DIR"; exit 1; } done + # put todays date in the config + if [[ -f $CONFIG_DIR/$config ]]; then + if grep -xq "date=.*" $CONFIG_DIR$config; then + [[ $v -ge 2 ]] && printf "$FMT_MESSAGE" "Updating date in $CONFIG_DIR$config." + sed "s/date=.*/date=\"$(date)\"/" $CONFIG_DIR$config -i + else + [[ $v -ge 2 ]] && printf "$FMT_MESSAGE" "Writing current date to $CONFIG_DIR$config." + echo "date=\"$(date)\"" >> $CONFIG_DIR$config + fi + fi + [[ $v -ge 2 ]] && printf "$FMT_MESSAGE" "Running 'sync' to write cached writes to persistent storage" sync [[ $v -ge 1 ]] && printf "$FMT_MESSAGE" "Done!" } -# # HELP -# show_help() { printf "\e[33mArgument Short Action:\e[0m ---help -h show this ---settings -s show current settings +--help -h Show this. +--settings -s Show current settings. ---config [name] -c use sender, receiver, paths from config file with [name] +--config [name] -c Use sender, receiver, paths from config file with [name]. ---sender [path] -s sender directory, with trailing slash! Defaults to the current working directory ---receiver [path] -r receiver directory, with trailing slash! ---reverse swap receiver and sender +--sender [path] -s Sender directory, with trailing slash! Defaults to the current working directory. +--receiver [path] -r Receiver directory, with trailing slash!. +--reverse Swap receiver and sender. ---encrypt encrypt files before syncing ---decrypt decrypt files before syncing ---mkrypt-flags [flags] additional flags for mkrypt +--encrypt Encrypt files before syncing. +--decrypt Decrypt files before syncing. +--date Only process files that have been modified since the program was last run. Needs -c and (--encrypt/--decrypt). +--mkrypt-flags [flags] Additional flags for mkrypt. ---delete -d delete files that exist on receiver, but not on sender ---skip-dryrun delete without asking first ---rsync-flags [flags] additional flags for rsync +--delete -d Delete files that exist on receiver, but not on sender. Does not work with --encrypt/--decrypt. +--skip-dryrun Delete without asking first. +--rsync-flags [flags] Additional flags for rsync. ---verbose -v increase verbosity ---silent decrease verbosity ---debug maximum verbosity +--verbose -v Increase verbosity. +--silent Decrease verbosity. +--debug Maximum verbosity. Positional arguments are: - if using a config: a string that must be in the configs paths: eg. 'foo' will include ~/foo but nor ~/bar @@ -169,27 +176,26 @@ See the manpage for more information. } +# SETTINGS show_settings() { printf "\e[1mThe current settings are:\e[0m\n" + printf "$FMT_CONFIG" "\$TMP_DIR " "$TMP_DIR" printf "$FMT_CONFIG" "\$TMP_FILE " "$TMP_FILE" + printf "$FMT_CONFIG" "\$CONFIG_DIR " "$CONFIG_DIR" + [[ -f $mkrypt ]] || mkrypt_installed="(file not found)" + printf "$FMT_CONFIG" "\$mkrypt " "$mkrypt $mkrypt_installed" printf "$FMT_CONFIG" "\$rsync_flags " "${rsync_flags[*]}" } -# # PARSE ARGS -# if [ -z $1 ]; then show_help exit 0 fi - - -all=0 -BACKUP=1 # use if the script gets more functions later -# all command line args with no "-" are interpreted as part of filepaths. +all=1 paths_2=() while (( "$#" )); do case "$1" in @@ -219,9 +225,11 @@ while (( "$#" )); do shift ;; --encrypt) encrypt=1 + use_encryption=1 shift ;; --decrypt) decrypt=1 + use_encryption=1 shift;; -d|--delete) delete=--delete @@ -234,6 +242,9 @@ while (( "$#" )); do rsync_flags+=("${2}") shift 2 else printf "$FMT_ERROR" "Missing argument for $1"; exit 1; fi ;; + --date) + use_date=1 + shift ;; --msynk-flags) if [[ -n ${2} ]]; then # && "${2:0:1}" != "-" ]]; then # possibly problematic if $2 is an msynk flag mkrypt_flags+=("${2}") @@ -263,14 +274,23 @@ while (( "$#" )); do esac done +# PREPARATION & CHECKS # check if rsync is installed if ! command -v rsync &> /dev/null; then printf "$FMT_ERROR" "rsync is not installed." + exit 1 fi +# if using encryption, check if mkrypt is installed +[[ $use_encryption = 1 && ! -f $mkrypt ]] && printf "$FMT_ERROR" "Can not use encryption: mkrypt is not installed at given path: $mkrypt." + # if using a config if [[ -n $config ]]; then - bash $CONFIG_DIR/$config || { printf "$FMT_ERROR" "Error running the config file: $CONFIG_DIR/$config"; exit 1; } + source $CONFIG_DIR$config || { printf "$FMT_ERROR" "Error running the config file: $CONFIG_DIR/$config"; exit 1; } + if [[ $use_encryption = 1 ]]; then + [[ -z $reverse ]] && encrypt=1 || decrypt=1 + fi + [[ $v -ge 3 ]] && printf "$FMT_MESSAGE" "Loaded config: $CONFIG_DIR$config:: date:$date sender:$sender receiver:$receiver use_encryption:$use_encryption encrypt:$encrypt decrypt:$decrypt paths:${paths[*]}" fi # overwrite stuff from the config if anything else was given / set variables of no config was given @@ -288,23 +308,19 @@ if [[ -n $reverse ]]; then receiver=$tmp_sender fi -# make sure sender is absolute with slash -wd=$PWD -# quiet cd $sender && sender="$PWD/" || { printf "$FMT_ERROR" "Sender directory does not exist: $sender"; exit 1; } -# make sure reveiver is absolute -# cd $wd -# mkdir -p $receiver || { printf "$FMT_ERROR" "Can not create config dir: $receiver"; exit 1; } -# quiet cd $receiver && receiver="$PWD/" || { printf "$FMT_ERROR" "Receiver directory does not exist: $receiver"; exit 1; } # make sure receiver has trailing slash [[ $receiver != *"/" ]] && receiver=$receiver"/" +# create receiver dir if its local +[[ ! $receiver == *:* ]] && { mkdir -p $receiver || { printf "$FMT_ERROR" "Can not create receiver directory: $receiver"; exit 1; }} -echo "sender:$sender rec:$receiver paths=$paths paths2=$paths_2" +[[ $v -ge 3 ]] && printf "$FMT_MESSAGE" "sender:$sender receiver:$receiver paths:$paths paths_2:$paths_2" # filter paths: # - if using config: all paths from config that have a pos.arg in them # - else: pos.args, must be relative to specified --sender filtered_paths=() if [[ -n $config ]]; then + [[ $sender != *"/" ]] && sender=$sender"/" for path in ${paths[@]}; do if check_path_in_args; then filtered_paths+=($sender$path) @@ -312,20 +328,23 @@ if [[ -n $config ]]; then done else for path in ${paths_2[@]}; do # add slash if directory - # quiet cd $sender [[ -d $path ]] && [[ $path != *"/" ]] && path=$path"/" - # quiet cd $sender && quiet cd $path && filtered_paths+=($PWD/) || { - # quiet cd $sender && ls -d $path && filtered_paths+=($PWD/$path) - # } filtered_paths+=($sender$path) done fi -cd $wd -echo "FPaths: ${filtered_paths[@]}" +[[ $v -ge 3 ]] && printf "$FMT_MESSAGE" "Filtered paths: ${filtered_paths[@]}" # sanity checks [[ -z $filtered_paths ]] && { printf "$FMT_ERROR" "Missing valid paths."; exit 1; } [[ -z $receiver ]] && { printf "$FMT_ERROR" "Missing receiver. Specifiy with --receiver"; exit 1; } +# if using --date +if [[ -n $use_date && -z $date ]]; then # if --date but date is not set, use 0 (unix time) + [[ $v -ge 1 ]] && printf "$FMT_MESSAGE" "--date passed but date not set in config, using 0 (unix time)" + date=$(date -d @0) +fi +[[ -n $use_date ]] && mkrypt_flags+=("--date \"${date}\"") + +# RUN! sync_sender_to_receiver exit 0