189 Commits

Author SHA1 Message Date
Laurent Cozic 9eba8e81c4 Merge pull request #114 from neddy70/no-auto-expire
Add option to disable automatic purging of backups
2018-03-18 11:55:50 +00:00
Neddy70 9f9a4945e2 Merge branch 'expiration-strategy' into no-auto-expire
Merge upstream fixes
2018-03-18 21:54:36 +11:00
Laurent Cozic 1a731243f7 Merge branch 'expiration-strategy' of github.com:laurent22/rsync-time-backup into expiration-strategy 2018-03-18 10:07:31 +00:00
Neddy70 b0654c96e0 Added option to disable automatically purging old backups when out of space. 2018-03-17 11:41:14 +11:00
Laurent Cozic 7f1431c13a Fixed date parsing issue 2018-03-05 19:29:14 +00:00
Laurent Cozic 4af7610dd1 Update README.md 2018-02-26 10:57:58 +00:00
Laurent Cozic c5af8528bb Merge branch 'master' into expiration-strategy 2018-02-24 12:26:46 +00:00
Laurent Cozic 051733df1e Fixed variable names 2018-02-23 18:19:09 +00:00
Laurent Cozic 7383c50134 Documented backup expiration strategy and added test file 2018-02-23 18:16:37 +00:00
Laurent Cozic a77169818e Changed order of expiration strategy items 2018-02-23 17:55:12 +00:00
Laurent Cozic c211541848 Merge pull request #110 from Loki3000/master
Fix work with symlink as a destination
2018-02-23 09:53:45 +00:00
Loki3000 80c4d26573 Fix work with symlink as a destination
https://github.com/laurent22/rsync-time-backup/issues/92
2018-02-23 12:25:36 +03:00
Laurent Cozic 5aac62cfc0 Merge pull request #109 from NathanFrench/master
Fix to assure only one instance is running.
2018-02-12 12:38:49 +01:00
Nathan French bfa1fd092e Fix to assure only one instance is running.
Since this script runs under /usr/bin/env, running 'pgrep $0' will fail
due to it being a child of 'bash'. In order to fix this, pgrep must use
the `-o` flag (oldest pid, or in this case, the leader), and `-f` which
searches the full process name.
2018-01-19 13:42:54 -05:00
Laurent Cozic c313e0a322 Merge pull request #102 from mmolinac/tigercompat
Replaced operator '=~' with 'grep -Eq' and using a perl one-liner to …
2017-12-09 11:07:56 +00:00
Manuel Molina Cuberos 19ecacad25 Newer versions of bash reports OSTYPE differently. This copes with the case. 2017-12-09 11:28:22 +01:00
Manuel Molina Cuberos 31a7bac4b4 Replaced operator '=~' with 'grep -Eq' and using a perl one-liner to avoid 'date -j'. Both are not compatible with Mac OS X Tiger. 2017-12-08 22:20:53 +01:00
Laurent Cozic b57cf4b732 Removed accidentally committed test files 2017-11-26 17:00:02 +00:00
Laurent Cozic 2d0cb29bcb Allow specifying the expiration strategy tokens in any order (less error prone) 2017-11-26 16:59:29 +00:00
Laurent Cozic c608793b60 Moved expiration logic to own function and implemented handling of strategy tokens 2017-11-26 16:48:03 +00:00
Matthias Kern e988703100 Added --strategy option description 2017-11-24 20:20:58 +01:00
Matthias Kern c5c1307062 Introduces function that parses the --strategy option into a two dimensional array 2017-11-24 20:16:22 +01:00
Laurent Cozic d13af58252 Merge pull request #96 from thomas-mc-work/patch-1
Also detect single dashes as error
2017-11-17 12:17:22 +01:00
Thomas McWork 260715a9b9 Also detect single dashes as error
This also recognizes single dashes as invalid input:

    rsync_tmbackup.sh - 1 2 3

Expected output: an error because it's unknown what the single `-` could mean

Actual output:

    rsync_tmbackup: Safety check failed - the destination does not appear to be a backup folder or drive (marker file not found).
    rsync_tmbackup: If it is indeed a backup folder, you may add the marker file by running the following command:
    rsync_tmbackup: 
    rsync_tmbackup: mkdir -p -- "1" ; touch "1/backup.marker"
    rsync_tmbackup:
2017-11-17 10:20:50 +01:00
Laurent Cozic fd4a29e848 Update README.md
It would make sense to have the list of features first
2017-10-05 21:18:14 +01:00
Laurent Cozic bb909c332b Update README.md
Added "Extensions" section
2017-10-05 21:13:17 +01:00
Laurent Cozic 74f418d561 Update README.md
Minor tweaks
2017-09-26 15:09:55 +01:00
Laurent Cozic 4393a86bd0 Merge pull request #90 from markalston/master
Added ability to pull files from remote ssh server
2017-09-26 15:08:36 +01:00
markalston 732ad83ed8 Update Readme.md to reflect remote pull method 2017-09-26 08:03:48 -06:00
Laurent Cozic d5b26f76e1 Merge pull request #91 from wietsefranssen/fix/zsh
fixed check for previous backups in zsh
2017-09-23 19:07:46 +01:00
Wietse Franssen dfeafb3d9f fixed check for previous backups in zsh 2017-09-22 15:23:46 +02:00
Laurent Cozic 1ed730e614 Update README.md
Added "How to restore" section
2017-09-21 11:55:08 +01:00
markalston 5a8ed7b008 Update rsync_tmbackup.sh 2017-09-20 10:54:52 -06:00
markalston 13357c1cc9 fixes for ssh source folder / 2017-09-20 10:40:34 -06:00
markalston 6b05dca48a fixed missing SSH_SRC_FOLDER_PREFIXs 2017-09-20 09:36:36 -06:00
markalston 88dce0e3f9 Updated rsync_tmbackup.sh to allow remote source
Changes to allow rsync_tmbackup.sh to pull files from remote server.
2017-09-20 09:22:18 -06:00
Laurent Cozic 9a5afa66f4 Merge pull request #76 from SimonHeimberg/patch-1
do not search for directories recurisve
2017-06-08 14:02:44 +01:00
Simon Heimberg db316ed18e correct argument for maxdept parameter 2017-06-08 14:51:59 +02:00
Laurent Cozic 8ef83da5d9 Reverted to use rm for directory deletion 2017-06-04 11:25:52 +01:00
Laurent Cozic b85bf9a6e5 Updated README and mentioned built-in lock 2017-06-03 15:39:19 +01:00
Laurent Cozic b41589ec77 Only use optimised deletion method for large directories 2017-06-03 15:31:48 +01:00
Laurent Cozic 8f8fbeb130 Merge pull request #80 from dovecode/cygwin-conflict-support
Add cygwin support for checking if the previous backup is still running
2017-06-03 14:54:10 +01:00
root dbc2f536fa Add support for checking if the previous backup is still running under cygwin 2017-05-18 20:04:31 -04:00
Laurent Cozic d152279d30 Still delete inprogress file in case of error or warning 2017-05-08 20:14:15 +00:00
SimonHeimberg 6364fe1ca0 correct position for maxdept parameter
as suggested by the output of find
2017-05-04 14:55:32 +02:00
SimonHeimberg 41f9bd1c8c do not search for directories recurisve
This gives a big speed improvement when the dest dir contains other directories containting many subdirectories.
2017-05-04 14:49:30 +02:00
Laurent Cozic 15329de603 Changed order of error/warning handling to avoid redundant error messages.
Displayed grep command that can be copied and pasted to investigate backup issues.
2017-05-02 21:14:22 +00:00
Laurent Cozic 77d8c56000 Added --stats --human-readable to default rsync flags, and done minor clean up and refactoring 2017-05-02 21:58:01 +00:00
Laurent Cozic 1ffb922e33 Update README.md
Fixed headers
2017-05-02 12:20:50 +01:00
Laurent Cozic d65cb504cc Documented expiration logic in README 2017-05-02 20:36:10 +00:00
Laurent Cozic f9205f6e20 Allow setting log file directory 2017-05-02 20:14:36 +00:00
Laurent Cozic 041ffb459b Merge pull request #74 from robocad/patch-1
Update README.md
2017-04-27 23:21:45 +01:00
robocad d612e9980e Update README.md
typo
2017-04-27 22:07:37 +02:00
Laurent Cozic b2e5582ade Update README.md
Fixed tabs
2017-04-25 16:10:48 +01:00
Laurent Cozic 1d8592c39a Update README.md
Fixed readme file
2017-04-25 16:09:30 +01:00
Laurent Cozic cffc330ff5 Update README.md
Moved `flock` information under Example and shortened it
2017-04-25 16:07:47 +01:00
Laurent Cozic 4b15ea8f75 Merge pull request #72 from jpartain89/master
Changed `flock` line
2017-04-25 16:05:01 +01:00
jpartain89 9367b9b80b Changed flock line
Changed `sh` to `bash` when calling `rsync_tmbackup.sh`. A bunch of errors pop up when using `sh`. 

trap: SIGINT: bad trap
rsync_tmbackup.sh: 173: rsync_tmbackup.sh: [[: not found
rsync_tmbackup.sh: 173: rsync_tmbackup.sh: -z: not found
rsync_tmbackup.sh: 71: rsync_tmbackup.sh: [[: not found
rsync_tmbackup.sh: 194: rsync_tmbackup.sh: [[: not found
rsync_tmbackup.sh: 194: rsync_tmbackup.sh: [[: not found
rsync_tmbackup.sh: 194: rsync_tmbackup.sh: [[: not found
2017-04-25 09:24:49 -05:00
Laurent Cozic 674d5a1878 Merge pull request #71 from joekerna/fn_rm
Improve speed of expiring backups
2017-04-25 12:42:13 +01:00
joekerna dff1ea00e1 rsync doesn't work with files
Use rsync only to delete directories and stick to rm for files.
2017-04-19 10:01:30 +02:00
joekerna 78d7121586 Improve backup removal (Issue #55)
Implemented quicker removal of old backups according to issue #55
2017-04-18 14:48:40 +02:00
Laurent Cozic 75ff2a6aca Merge pull request #69 from mackdata/master
Update README.md
2017-04-06 14:36:43 +01:00
mackdata 1964baa4a2 Update README.md 2017-04-06 15:28:45 +02:00
mackdata c44911155e Update README.md 2017-04-06 15:23:28 +02:00
mackdata 103f85cd65 Update README.md
Added section ## Lockfile
2017-04-06 13:33:34 +02:00
Laurent Cozic 756b03bc32 Fixes issue #59 - script didn't work when source set to '/' 2016-11-29 12:29:07 +00:00
Laurent Cozic 1880478bc5 Merge pull request #57 from dliessi/fix-date-FreeBSD
fix date parsing on FreeBSD
2016-10-08 11:28:02 +01:00
Davide Liessi 4cf2f2e081 fix date parsing on FreeBSD 2016-10-05 22:51:08 +02:00
Laurent Cozic 9bf68c1e1e Removed test dir 2016-10-03 21:04:20 +01:00
Laurent Cozic 41427964ac Removed --archive flag and replaced it with the actual options, to make it clear what's happening. Also removed --verbose 2016-10-01 13:48:30 +01:00
Laurent Cozic 56a3a89866 Fixed issue with paths that contain spaces 2016-10-01 13:44:56 +01:00
Laurent Cozic b9d359f797 Update rsync_tmbackup.sh
Allow getting and setting rsync options
2016-09-10 11:32:11 +01:00
Laurent Cozic d680039e5f Update README.md
Documented `--rsync-set-flags` option.
2016-09-10 11:30:17 +01:00
Laurent Cozic 5a0fd5f3b1 Added SSH and Time Machine style backup example 2016-08-16 09:36:57 +01:00
Laurent Cozic 6157826651 Added support for setting SSH port 2016-08-15 12:01:37 +01:00
Laurent Cozic 9c5046e518 Added support for command line flags 2016-08-15 11:29:15 +01:00
Laurent Cozic 4fcbb0eab5 Formatting - not sure when that was changed to spaces 2016-08-12 17:29:09 +01:00
Laurent Cozic b905b61a86 Not supported on certain versions of BusyBox - fixes issue #52 2016-05-05 14:10:06 +02:00
Laurent Cozic ded89f98d6 Merge pull request #47 from BrandonDusseau/master
Fixes issue with incorrectly detecting if backup is already running if interrupted
2016-03-22 15:44:49 +01:00
Brandon Dusseau e8e0806823 Fixes issue with incorrectly detecting if backup is already running if interrupted 2016-03-19 00:27:51 -04:00
Laurent Cozic 7585aae16c Merge pull request #32 from bhuvi8/master
display usage info if required args not passed
2016-02-03 17:22:48 +01:00
Bhuvanesh Kumar a8504cbf4c modified to not assume destination if not provided 2016-02-03 21:38:17 +05:30
Bhuvanesh Kumar 6e27f77316 Add backup files(*~) to .gitignore 2015-12-08 00:32:10 +05:30
Bhuvanesh Kumar 5e4250d5bc Merge remote-tracking branch 'upstream/master'
Conflicts:
	rsync_tmbackup.sh
2015-12-08 00:25:50 +05:30
Laurent Cozic 9c36702657 Update README.md
Mentioned support for SSH.
2015-11-08 12:15:21 +01:00
Laurent Cozic 2f2ac64aa3 'Fix' active backup task issue in Cygwin, added warning, and TODO solution 2015-11-08 12:09:28 +01:00
Laurent Cozic b7d2cae0b3 Merge pull request #29 from fustundag/master
SSH support added.
2015-11-08 12:00:37 +01:00
Laurent Cozic a58148736b Update README.md
Provided more information as to what the script actually does
2015-10-30 22:52:39 +01:00
Fatih Üstündağ e07f7e7123 Tested at Cygwin 2.2.1 at Windows Server 2012 R2 64bit 2015-09-26 01:15:35 +03:00
Fatih Üstündağ 5b22966589 Merge branch 'master' of /Users/fatihustundag/Projects/rsync-time-backup with conflicts. 2015-09-26 01:11:05 +03:00
Fatih Üstündağ 91fb6ffee7 Merge remote-tracking branch 'origin/master' 2015-09-26 01:06:53 +03:00
Fatih Üstündağ dc2d5c2209 Tested at Cygwin 2.2.1 at Windows Server 2012 R2 64bit 2015-09-26 01:06:18 +03:00
Laurent Cozic 73dce717df Removed broken Bitdeli badge and updated date 2015-09-25 00:05:03 +02:00
Laurent Cozic 69ef82f274 Merge pull request #37 from uglygus/master
Fixed PID re-use issue
2015-04-05 14:42:19 +02:00
uglygus 2f64db5973 PID reuse
Checks for PID and $APPNAME in the results from pgrep in order to avoid
the problem where the PID from a previous run has been recycled by
another process.  This caused us to exit even though we are not
currently running.
2015-04-04 07:50:11 -04:00
Fatih Üstündağ ae7191c108 Merge pull request #2 from StevenWolfe/master
Cleanup by homebysix
2015-03-02 11:45:56 +02:00
Steve Wolfe edcc51e709 Merge pull request #1 from homebysix/master
Code cleanup and cosmetic formatting changes.
2015-02-27 10:24:19 -05:00
Laurent Cozic a728820bfd Merge pull request #36 from eaut/avoid-concurrent
Prevent execution of concurrent backup tasks
2015-02-15 10:57:48 +01:00
eaut 5740b9a43e prevent execution of concurrent backup tasks 2015-02-13 22:51:59 +01:00
Elliot Jordan 742412fb3e Cleaned up whitespace (no changes to code). 2015-01-20 13:42:53 -08:00
Elliot Jordan 3202ca5da0 Consistent formatting for conditionals. 2015-01-20 13:41:52 -08:00
Elliot Jordan 6c4607d853 Removed unnecessary $ from arithmetics. 2015-01-20 13:36:48 -08:00
lau 21c24be547 Update README.md
Added complete license
2014-09-19 11:02:32 +02:00
Laurent Cozic b9cc101d77 Merge branch 'master' of https://github.com/laurent22/rsync-time-backup 2014-04-28 22:18:55 -03:00
Laurent Cozic 1a3dc0b144 More complete command 2014-04-28 22:18:32 -03:00
Laurent 1384fdfd1c Update README.md 2014-02-03 02:20:46 +08:00
Laurent 7265d26b0b Update README.md
Clarify advantages/disadvantages
2014-02-03 02:19:25 +08:00
Laurent 64f7005ab7 Update README.md
Update compatibility.
2014-02-03 02:05:10 +08:00
Bhuvanesh Kumar 9bc6be475a if destination is not specified assume current folder 2014-01-30 23:13:30 +05:30
Bhuvanesh Kumar 18258ad2a9 display usage info if required args not passed 2014-01-29 00:18:38 +05:30
Laurent 45a90310e3 Merge pull request #31 from musically-ut/patch-1
Fix minor typos in the README.md
2014-01-26 21:26:56 -08:00
Utkarsh Upadhyay 2c9805b450 Fix minor typos in the README.md 2014-01-26 21:20:49 +01:00
Laurent 60c04350fc Merge pull request #30 from Meldanya/master
Fix expiring old backups
2014-01-14 05:16:42 -08:00
Erik Jansson 12930a24eb Fix expiring old backups
This got (accidentally?) turned off in a previous commit.
2014-01-14 13:55:43 +01:00
Laurent Cozic 27a98fb54b Added TODO to README.md 2014-01-12 03:09:12 +08:00
Fatih Üstündağ f3a265b07e SSH support added.
Backups can be created at remote server via ssh. Destination folder
should be given like “root@192.168.0.1:/bakcups”

NOTE: Assumed public/private key config is ok.
2014-01-03 14:40:53 +02:00
Laurent a3bd5009b1 Update README.md
Testing status.
2013-12-27 06:06:39 +08:00
Laurent Cozic 52376db2b5 OS X compatibility and splitted rsync errors and warnings
- Fixed bug on OS X since "--" is not supported by sed.
- Changed sed to grep for consistency with rest of the code.
- Splitted handling of rsync errors/warnings - for errors we quit with
  an error code, for warnings we print a message but complete the backup
  process.
2013-12-21 18:33:01 +08:00
Laurent c105c0a9e2 Merge pull request #27 from bhuvi8/master
Added rsync error handling and modified WARN and ERROR functions to print to stderr
2013-12-21 02:26:32 -08:00
bhuvi8 c8ae234904 Deleting log file when no error is found and proper handling of rsync errors
through log file rather than through exit code
2013-12-18 00:01:56 +05:30
bhuvi8 25ad8f07a6 Added rsync error handling and modified WARN and ERROR functions to print to
stderr
2013-12-17 02:15:43 +05:30
Laurent 7b9de2b5f5 Merge pull request #26 from jakschu/patch-1
Fix typo
2013-12-16 02:01:58 -08:00
jakschu 29f9f85361 Fix typo 2013-12-16 10:41:00 +01:00
Laurent 9a87c1d4e4 Merge pull request #23 from robru/simplify-logfiles
Simplify logfile parsing and drop unreachable codepath.
2013-12-06 08:39:40 -08:00
Robert Bruce Park 808767dc66 Merge @laurent22/master. 2013-11-26 22:05:46 -08:00
Laurent Cozic c854cd17c6 Restoring $(basename -- "$DEST")
In case the destination is later on generated in a different way,
also makes the code and intention more explicit.
2013-11-27 12:36:54 +08:00
Robert Bruce Park bff263ab57 Simplify logfile parsing and drop unreachable codepath. 2013-11-26 13:46:26 -08:00
Robert Bruce Park d73b3ffe43 Yet more quotes! 2013-11-26 13:13:58 -08:00
Robert Bruce Park b16c3cf4ce Even more quotes. 2013-11-26 13:11:53 -08:00
Robert Bruce Park e7fc37076c Rebase on @laurent22/master. 2013-11-26 13:11:00 -08:00
Robert Bruce Park 4720f404af Extra quotes everywhere! 2013-11-26 13:08:52 -08:00
Robert Bruce Park 2f0092ac9f Use -n and -z for easier string emptiness checks. 2013-11-26 13:04:30 -08:00
Robert Bruce Park 40e5ce4721 Use bash : noop for efficiency. 2013-11-26 13:02:29 -08:00
Robert Bruce Park e5f939222e Determine $APPNAME programmatically. 2013-11-26 12:59:00 -08:00
Robert Bruce Park 0db8b3a913 Define trivial functions on one line only. 2013-11-26 12:55:17 -08:00
Robert Bruce Park c3f15c7834 Simplify symlink creation. 2013-11-26 12:48:49 -08:00
Robert Bruce Park 4da7172099 Redundant. 2013-11-26 12:46:15 -08:00
Robert Bruce Park 45875055a4 Simpler absolute paths. 2013-11-26 12:45:12 -08:00
Robert Bruce Park 16fd955e6c Drop some single-use variables. 2013-11-26 12:43:58 -08:00
Robert Bruce Park 08d090f19c Sort fn_find_backups newest first. 2013-11-26 12:41:42 -08:00
Laurent Cozic 2e413d82d7 Merge branch 'master' of https://github.com/laurent22/rsync-time-backup 2013-11-26 03:44:06 +08:00
Laurent cb252f7545 Update README.md
Mentioned level of testing.
2013-11-26 03:40:23 +08:00
Laurent 5d1027fd78 Merge pull request #21 from efournie/efournie-master
Force deleting INPROGRESS_FILE

Avoids waiting for user input if rm is aliased to rm -i
2013-11-25 10:17:53 -08:00
Eric Fournie 928b32f83e Force deleting INPROGRESS_FILE (avoids waiting for user input if rm is aliased to rm -i) 2013-11-25 14:47:18 +00:00
Laurent Cozic b8fb75bbc0 Removed dangerous and unneeded delete flags.
Since there's normally nothing in the destination directory, the
delete flags are not really needed.
2013-11-24 17:23:00 +08:00
Robert Bruce Park 9ece9fbec3 Fix spaces in filenames. 2013-11-23 01:18:08 -08:00
Robert Bruce Park 392b8089a5 Add a simple script for running rsync_tmbackup.sh from cron. 2013-11-23 01:07:31 -08:00
Laurent Cozic d31b689f6b Removed old backup deletion prompt, and formatting tweaks.
- Removed old backup deletion prompt as it's not really needed.
- Made all variables uppercase for consistency.
- Used double square brackets in `if` statements.
2013-11-16 22:50:10 +08:00
Laurent Cozic 81d6b4fb99 Merge branch 'master' of https://github.com/laurent22/rsync-time-backup 2013-11-16 22:20:07 +08:00
Laurent 12784d1d32 Merge pull request #14 from robru/master
Time Machine style backup pruning.
2013-11-16 06:17:54 -08:00
Robert Bruce Park f5a4275a04 Merge master. 2013-11-15 23:07:01 -08:00
Robert Bruce Park a474f139f5 Stop creating "latest" symlink (implemented in a different branch).
This reverts commit 28735b1815.
2013-11-15 23:02:24 -08:00
Robert Bruce Park 28735b1815 Create "latest" symlink pointing at the most recent completed backup. 2013-11-15 12:01:52 -08:00
Robert Bruce Park f011e1a7ab More elegant cross-platform date logic. 2013-11-15 11:08:28 -08:00
Robert Bruce Park 9b9a564447 More robust date handling.
Previously the date handling expiry logic had a bug where if you had
backups that were a year apart to the day, it wouldn't notice the
difference in year and only notice that the month was the same, and
expire the older one (eg, if you had a backup on 2012-04-01 and
another on 2013-04-01, it'd delete the one from 2012. This commit
makes it compare the full date string instead of just the month, so
that it more robustly keeps older backups.
2013-11-15 10:35:04 -08:00
Robert Bruce Park ae7998025a Add MacOSX compatibility. 2013-11-15 10:24:52 -08:00
Laurent Cozic e092d5c2fd Updated list of features in README 2013-11-15 18:06:49 +08:00
Laurent Cozic 2ade9de422 Fixed latest backup symlink issues
Code was in the wrong place (link being added when the backup failed only).
Also made the symlink relative to the current folder so that the drive
can be mounted with a different path.
2013-11-15 17:20:16 +08:00
Laurent 85fea581a6 Merge pull request #15 from mumrah/master
Add a "latest" symlink when backup is successful
2013-11-15 01:16:05 -08:00
Robert Bruce Park 324a3ffc9a Whitespace fix, oops. 2013-11-14 19:36:32 -08:00
Robert Bruce Park 12deac25d6 Correctly handle most recent backup if it's older than 24hs.
Code was assuming that you'd run backups hourly, eg, so you'd have
many backups within the last 24 hours. Code assumed that by the time
you found a backup more than 24h old, you'd already have a value for
$prev set. In the event that your most recent backup is more than 24
hours old, $prev would not be set and bash would get an error "unary
operator expected" when it tried to compare the day of the newest
backup to the empty $prev value.

I fix this by setting a default value for $prev, one that cannot be
mistaken for an existing backup because it does not represent a valid
date. This ensures that the most recent backup is correctly preserved
regardless of it's age.
2013-11-14 19:31:22 -08:00
Robert Bruce Park 855c77a83f Simplify fn_is_backup_destination implementation. 2013-11-14 19:03:26 -08:00
Robert Bruce Park 4250423d98 Make fn_expire_backup more robust. 2013-11-14 18:50:34 -08:00
David Arthur 0c679903af Fixed
Brain fart
2013-11-13 15:52:24 -05:00
Robert Bruce Park 584e3a9b4b Drop BACKUP_FOLDER_PATTERN since it's now only used in one place. 2013-11-13 10:53:13 -08:00
Robert Bruce Park 19446c24b9 Take the training wheels off ;-) 2013-11-13 10:34:47 -08:00
David Arthur bda24be003 Add a "latest" symlink when backup is successful 2013-11-13 13:29:08 -05:00
Robert Bruce Park 8bb5b3f265 Rearrange function definitions. 2013-11-13 10:21:47 -08:00
Robert Bruce Park a67efdd0f1 Implement fn_expire_backups to reduce code duplication. 2013-11-13 10:17:41 -08:00
Robert Bruce Park 32e71ce9aa Implement fn_find_backups to reduce code duplication. 2013-11-13 10:16:27 -08:00
Robert Bruce Park 72df5bf33e Rebase on master. 2013-11-13 09:16:31 -08:00
Robert Bruce Park fe0bfde041 More robust path handling. 2013-11-13 09:12:50 -08:00
Laurent 8a2e91813b Update README.md
Now done: "Check if there's enough space in the destination before doing the backup. Also automatically delete old backups."
2013-11-13 17:57:51 +08:00
Laurent Cozic c505ecbfb8 Merge branch 'master' of https://github.com/rastkov/rsync-time-backup into rastkov-master
Conflicts:
	rsync_tmbackup.sh
2013-11-13 17:51:15 +08:00
Laurent Cozic 159d8b6416 Merge branch 'master' of https://github.com/laurent22/rsync-time-backup
Conflicts:
	rsync_tmbackup.sh
2013-11-13 17:47:58 +08:00
Laurent Cozic 2fd6b109f0 Using find instead of ls to find the last and second last directory used for backup. 2013-11-13 17:46:20 +08:00
Robert Bruce Park d425a05528 Initial implementation of Time Machine style backup pruning.
Within 24 hours, all backups are kept. Within 1 month, only the most
recent backup for each day is kept. For all previous backups, only the
most recent of each month is kept.

This is not *quite* the same as Time Machine, but this implementation
is a lot easier to do since it is based on string comparisons of the
dates and doesn't require any "hard" date logic.

Also this commit just 'echo's what will be deleted, and does not
actually delete anything yet because I am still testing it.
2013-11-13 00:01:17 -08:00
Laurent Cozic c23f47164e Merge branch 'master' of https://github.com/tyriis/rsync-time-backup into tyriis-master
Changed regex for grep iirc POSIX mode

Conflicts:
	rsync_tmbackup.sh
2013-11-12 15:50:02 +08:00
Laurent Cozic 9128fa2a6f Merge branch 'master' of https://github.com/tyriis/rsync-time-backup into tyriis-master
Conflicts:
	rsync_tmbackup.sh
2013-11-12 15:40:00 +08:00
Laurent Cozic 1f5aabc31f Merge branch 'master' of https://github.com/laurent22/rsync-time-backup
Conflicts:
	rsync_tmbackup.sh
2013-11-12 15:31:20 +08:00
Laurent Cozic b481985f2a Move more setup code inside loop 2013-11-12 11:14:02 +08:00
Laurent 00ba40b617 Merge pull request #11 from Flimm/master
Deal with arguments with special characters better
2013-11-07 01:06:50 -08:00
David D Lowe 24540f33ee Handle source and dest arguments with spaces 2013-11-07 08:51:12 +00:00
rastkov 99258d80ab Update rsync_tmbackup.sh
Set LAST_TIME, LINE_CONUT and SECOND_LAST_TIME based on `uname -s` output for Mac OS compatibility.
2013-11-01 14:32:59 +01:00
rastkov 27a0a1881f Update rsync_tmbackup.sh
Using find with regex instead of glob
2013-11-01 08:48:21 +01:00
nils biesalski c046c4dd42 Changed regex for grep iirc POSIX mode
http://stackoverflow.com/questions/6901171/is-d-not-supported-by-greps-basic-expressions
2013-10-31 13:51:08 +01:00
David D Lowe 396b42d483 Deal with arguments with special characters better
Script now handles filenames with special characters (such as spaces,
hard tabs, newlines, double quotes and a dash prefix). The only
exceptions are filenames with single quotes. In this case, the script
will fail early with a clear error message.
2013-10-30 13:45:48 +00:00
rastkov 8318db90c5 Update rsync_tmbackup.sh 2013-10-29 12:15:38 +01:00
rastkov a40a733902 Update rsync_tmbackup.sh
Using find instead of ls to find the last and second last directory used for backup.
2013-10-29 12:07:30 +01:00
4 changed files with 562 additions and 165 deletions
+5
View File
@@ -0,0 +1,5 @@
.idea
test.sh
*~
tests/TestDest/
tests/TestSource/
+116 -31
View File
@@ -1,14 +1,49 @@
# Rsync time backup
Time Machine style backup with rsync. Should work on Linux, Mac OS X and Windows with Cygwin.
This script offers Time Machine-style backup using rsync. It creates incremental backups of files and directories to the destination of your choice. The backups are structured in a way that makes it easy to recover any file at any point in time.
# Installation
It works on Linux, macOS and Windows (via WSL or Cygwin). The main advantage over Time Machine is the flexibility as it can backup from/to any filesystem and works on any platform. You can also backup, for example, to a Truecrypt drive without any problem.
On macOS, it has a few disadvantages compared to Time Machine - in particular it does not auto-start when the backup drive is plugged (though it can be achieved using a launch agent), it requires some knowledge of the command line, and no specific GUI is provided to restore files. Instead files can be restored by using any file explorer, including Finder, or the command line.
## Installation
git clone https://github.com/laurent22/rsync-time-backup
# Usage
## Usage
rsync_tmbackup.sh <source> <destination> [excluded-pattern-path]
Usage: rsync_tmbackup.sh [OPTION]... <[USER@HOST:]SOURCE> <[USER@HOST:]DESTINATION> [exclude-pattern-file]
Options
-p, --port SSH port.
-h, --help Display this help message.
--rsync-get-flags Display the default rsync flags that are used for backup.
--rsync-set-flags Set the rsync flags that are going to be used for backup.
--log-dir Set the log file directory. If this flag is set, generated files will
not be managed by the script - in particular they will not be
automatically deleted.
--strategy Set the expiration strategy. Default: "1:1 30:7 365:30" means after one
day, keep one backup per day. After 30 days, keep one backup every 7 days.
After 365 days keep one backup every 30 days.
--no-auto-expire Set option to disable automatically purging old backups when out of space.
## Features
* Each backup is on its own folder named after the current timestamp. Files can be copied and restored directly, without any intermediate tool.
* Backup to/from remote destinations over SSH.
* Files that haven't changed from one backup to the next are hard-linked to the previous backup so take very little extra space.
* Safety check - the backup will only happen if the destination has explicitly been marked as a backup destination.
* Resume feature - if a backup has failed or was interrupted, the tool will resume from there on the next backup.
* Exclude file - support for pattern-based exclusion via the `--exclude-from` rsync parameter.
* Automatically purge old backups - within 24 hours, all backups are kept. Within one month, the most recent backup for each day is kept. For all previous backups, the most recent of each month is kept.
* "latest" symlink that points to the latest successful backup.
## Examples
@@ -20,34 +55,84 @@ Time Machine style backup with rsync. Should work on Linux, Mac OS X and Windows
rsync_tmbackup.sh /home /mnt/backup_drive excluded_patterns.txt
## Exclude file
* Backup to remote drive over SSH, on port 2222:
An optional exclude file can be provided as a third parameter. It should be compabible with the `--exclude-from` parameter of rsync. See [this tutorial] (https://sites.google.com/site/rsync2u/home/rsync-tutorial/the-exclude-from-option) for more information.
# Features
* Each backup is on its own folder named after the current timestamp. Files can be copied and restored directly, without any intermediate tool.
* Files that haven't changed from one backup to the next are hard-linked to the previous backup so take very little extra space.
* Safety check - the backup will only happen if the destination has explicitely been marked as a backup destination.
* Resume feature - if a backup has failed or was interrupted, the tool will resume from there on the next backup.
* Exclude file - support for pattern-based exclusion via the `--exclude-from` rsync parameter.
* The application is one bash script that can be easily edited.
# TODO
* Check if there's enough space in the destination before doing the backup. Also automatically delete old backups.
* Manage the backups in a way similar to Time Machine - hourly backups for the past 24 hours; daily backups for the past month; weekly backups for the previous months.
# LICENSE
[MIT](http://opensource.org/licenses/MIT)
rsync_tmbackup.sh -p 2222 /home user@example.com:/mnt/backup_drive
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/laurent22/rsync-time-backup/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
* Backup from remote drive over SSH:
rsync_tmbackup.sh user@example.com:/home /mnt/backup_drive
* To mimic Time Machine's behaviour, a cron script can be setup to backup at regular interval. For example, the following cron job checks if the drive "/mnt/backup" is currently connected and, if it is, starts the backup. It does this check every 1 hour.
0 */1 * * * if [[ -d /mnt/backup ]]; then rsync_tmbackup.sh /home /mnt/backup; fi
## Backup expiration logic
Backup sets are automatically deleted following a simple expiration strategy defined with the `--strategy` flag. This strategy is a series of time intervals with each item being defined as `x:y`, which means "after x days, keep one backup every y days". The default strategy is `1:1 30:7 365:30`, which means:
- After **1** day, keep one backup every **1** day (**1:1**).
- After **30** days, keep one backup every **7** days (**30:7**).
- After **365** days, keep one backup every **30** days (**365:30**).
Before the first interval (i.e. by default within the first 24h) it is implied that all backup sets are kept. Additionally, if the backup destination directory is full, the oldest backups are deleted until enough space is available.
## Exclusion file
An optional exclude file can be provided as a third parameter. It should be compatible with the `--exclude-from` parameter of rsync. See [this tutorial](https://sites.google.com/site/rsync2u/home/rsync-tutorial/the-exclude-from-option) for more information.
## Built-in lock
The script is designed so that only one backup operation can be active for a given directory. If a new backup operation is started while another is still active (i.e. it has not finished yet), the new one will be automaticalled interrupted. Thanks to this the use of `flock` to run the script is not necessary.
## Rsync options
To display the rsync options that are used for backup, run `./rsync_tmbackup.sh --rsync-get-flags`. It is also possible to add or remove options using the `--rsync-set-flags` option. For example, to exclude backing up permissions and groups:
rsync_tmbackup --rsync-set-flags "--numeric-ids --links --hard-links \
--one-file-system --archive --no-perms --no-groups --itemize-changes" /src /dest
## No automatic backup expiration
An option to diable the default behaviour to purge old backups when out of space. This option is set with the `--no-auto-expire` flag.
## How to restore
The script creates a backup in a regular directory so you can simply copy the files back to the original directory. You could do that with something like `rsync -aP /path/to/last/backup/ /path/to/restore/to/`. Consider using the `--dry-run` option to check what exactly is going to be copied. Use `--delete` if you also want to delete files that exist in the destination but not in the backup (obviously extra care must be taken when using this option).
## Extensions
* [rtb-wrapper](https://github.com/thomas-mc-work/rtb-wrapper): Allows creating backup profiles in config files. Handles both backup and restore operations.
* [time-travel](https://github.com/joekerna/time-travel): Smooth integration into OSX Notification Center
## TODO
* Check source and destination file-system (`df -T /dest`). If one of them is FAT, use the --modify-window rsync parameter (see `man rsync`) with a value of 1 or 2
* Add `--whole-file` arguments on Windows? See http://superuser.com/a/905415/73619
* Minor changes (see TODO comments in the source).
## LICENSE
The MIT License (MIT)
Copyright (c) 2013-2018 Laurent Cozic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Regular → Executable
+412 -135
View File
@@ -1,19 +1,20 @@
#!/usr/bin/env bash
APPNAME=$(basename $0 | sed "s/\.sh$//")
# -----------------------------------------------------------------------------
# Log functions
# -----------------------------------------------------------------------------
fn_log_info() {
echo "[RB INFO] $1"
}
fn_log_warn() {
echo "[RB WARN] $1"
}
fn_log_error() {
echo "[RB ERROR] $1"
fn_log_info() { echo "$APPNAME: $1"; }
fn_log_warn() { echo "$APPNAME: [WARNING] $1" 1>&2; }
fn_log_error() { echo "$APPNAME: [ERROR] $1" 1>&2; }
fn_log_info_cmd() {
if [ -n "$SSH_DEST_FOLDER_PREFIX" ]; then
echo "$APPNAME: $SSH_CMD '$1'";
else
echo "$APPNAME: $1";
fi
}
# -----------------------------------------------------------------------------
@@ -21,19 +22,289 @@ fn_log_error() {
# -----------------------------------------------------------------------------
fn_terminate_script() {
echo "SIGINT caught"
fn_log_info "SIGINT caught."
exit 1
}
trap 'fn_terminate_script' SIGINT
# -----------------------------------------------------------------------------
# Small utility functions for reducing code duplication
# -----------------------------------------------------------------------------
fn_display_usage() {
echo "Usage: $(basename $0) [OPTION]... <[USER@HOST:]SOURCE> <[USER@HOST:]DESTINATION> [exclude-pattern-file]"
echo ""
echo "Options"
echo " -p, --port SSH port."
echo " -h, --help Display this help message."
echo " --rsync-get-flags Display the default rsync flags that are used for backup."
echo " --rsync-set-flags Set the rsync flags that are going to be used for backup."
echo " --log-dir Set the log file directory. If this flag is set, generated files will"
echo " not be managed by the script - in particular they will not be"
echo " automatically deleted."
echo " Default: $LOG_DIR"
echo " --strategy Set the expiration strategy. Default: \"1:1 30:7 365:30\" means after one"
echo " day, keep one backup per day. After 30 days, keep one backup every 7 days."
echo " After 365 days keep one backup every 30 days."
echo " --no-auto-expire Disable automatically deleting backups when out of space. Instead an error"
echo " is logged, and the backup is aborted."
echo ""
echo "For more detailed help, please see the README file:"
echo ""
echo "https://github.com/laurent22/rsync-time-backup/blob/master/README.md"
}
fn_parse_date() {
# Converts YYYY-MM-DD-HHMMSS to YYYY-MM-DD HH:MM:SS and then to Unix Epoch.
case "$OSTYPE" in
linux*) date -d "${1:0:10} ${1:11:2}:${1:13:2}:${1:15:2}" +%s ;;
cygwin*) date -d "${1:0:10} ${1:11:2}:${1:13:2}:${1:15:2}" +%s ;;
darwin8*) yy=`expr ${1:0:4}`
mm=`expr ${1:5:2} - 1`
dd=`expr ${1:8:2}`
hh=`expr ${1:11:2}`
mi=`expr ${1:13:2}`
ss=`expr ${1:15:2}`
# Because under MacOS X Tiger 'date -j' doesn't work, we do this:
perl -e 'use Time::Local; print timelocal('$ss','$mi','$hh','$dd','$mm','$yy'),"\n";' ;;
darwin*) date -j -f "%Y-%m-%d-%H%M%S" "$1" "+%s" ;;
FreeBSD*) date -j -f "%Y-%m-%d-%H%M%S" "$1" "+%s" ;;
esac
}
fn_find_backups() {
fn_run_cmd "find "$DEST_FOLDER/" -maxdepth 1 -type d -name \"????-??-??-??????\" -prune | sort -r"
}
fn_expire_backup() {
# Double-check that we're on a backup destination to be completely
# sure we're deleting the right folder
if [ -z "$(fn_find_backup_marker "$(dirname -- "$1")")" ]; then
fn_log_error "$1 is not on a backup destination - aborting."
exit 1
fi
fn_log_info "Expiring $1"
fn_rm_dir "$1"
}
fn_expire_backups() {
local current_timestamp=$EPOCH
local last_kept_timestamp=9999999999
# Process each backup dir from most recent to oldest
for backup_dir in $(fn_find_backups | sort -r); do
local backup_date=$(basename "$backup_dir")
local backup_timestamp=$(fn_parse_date "$backup_date")
# Skip if failed to parse date...
if [ -z "$backup_timestamp" ]; then
fn_log_warn "Could not parse date: $backup_dir"
continue
fi
# Find which strategy token applies to this particular backup
for strategy_token in $(echo $EXPIRATION_STRATEGY | tr " " "\n" | sort -r -n); do
IFS=':' read -r -a t <<< "$strategy_token"
# After which date (relative to today) this token applies (X)
local cut_off_timestamp=$((current_timestamp - ${t[0]} * 86400))
# Every how many days should a backup be kept past the cut off date (Y)
local cut_off_interval=$((${t[1]} * 86400))
# If we've found the strategy token that applies to this backup
if [ "$backup_timestamp" -le "$cut_off_timestamp" ]; then
# Special case: if Y is "0" we delete every time
if [ $cut_off_interval -eq "0" ]; then
fn_expire_backup "$backup_dir"
break
fi
# Check if the current backup is in the interval between
# the last backup that was kept and Y
local interval_since_last_kept=$((last_kept_timestamp - backup_timestamp))
if [ "$interval_since_last_kept" -lt "$cut_off_interval" ]; then
# Yes: Delete that one
fn_expire_backup "$backup_dir"
else
# No: Keep it
last_kept_timestamp=$backup_timestamp
fi
break
fi
done
done
}
fn_parse_ssh() {
# To keep compatibility with bash version < 3, we use grep
if echo "$DEST_FOLDER"|grep -Eq '^[A-Za-z0-9\._%\+\-]+@[A-Za-z0-9.\-]+\:.+$'
then
SSH_USER=$(echo "$DEST_FOLDER" | sed -E 's/^([A-Za-z0-9\._%\+\-]+)@([A-Za-z0-9.\-]+)\:(.+)$/\1/')
SSH_HOST=$(echo "$DEST_FOLDER" | sed -E 's/^([A-Za-z0-9\._%\+\-]+)@([A-Za-z0-9.\-]+)\:(.+)$/\2/')
SSH_DEST_FOLDER=$(echo "$DEST_FOLDER" | sed -E 's/^([A-Za-z0-9\._%\+\-]+)@([A-Za-z0-9.\-]+)\:(.+)$/\3/')
SSH_CMD="ssh -p $SSH_PORT ${SSH_USER}@${SSH_HOST}"
SSH_DEST_FOLDER_PREFIX="${SSH_USER}@${SSH_HOST}:"
elif echo "$SRC_FOLDER"|grep -Eq '^[A-Za-z0-9\._%\+\-]+@[A-Za-z0-9.\-]+\:.+$'
then
SSH_USER=$(echo "$SRC_FOLDER" | sed -E 's/^([A-Za-z0-9\._%\+\-]+)@([A-Za-z0-9.\-]+)\:(.+)$/\1/')
SSH_HOST=$(echo "$SRC_FOLDER" | sed -E 's/^([A-Za-z0-9\._%\+\-]+)@([A-Za-z0-9.\-]+)\:(.+)$/\2/')
SSH_SRC_FOLDER=$(echo "$SRC_FOLDER" | sed -E 's/^([A-Za-z0-9\._%\+\-]+)@([A-Za-z0-9.\-]+)\:(.+)$/\3/')
SSH_CMD="ssh -p $SSH_PORT ${SSH_USER}@${SSH_HOST}"
SSH_SRC_FOLDER_PREFIX="${SSH_USER}@${SSH_HOST}:"
fi
}
fn_run_cmd() {
if [ -n "$SSH_DEST_FOLDER_PREFIX" ]
then
eval "$SSH_CMD '$1'"
else
eval $1
fi
}
fn_find() {
fn_run_cmd "find '$1'" 2>/dev/null
}
fn_get_absolute_path() {
fn_run_cmd "cd '$1';pwd"
}
fn_mkdir() {
fn_run_cmd "mkdir -p -- '$1'"
}
# Removes a file or symlink - not for directories
fn_rm_file() {
fn_run_cmd "rm -f -- '$1'"
}
fn_rm_dir() {
fn_run_cmd "rm -rf -- '$1'"
}
fn_touch() {
fn_run_cmd "touch -- '$1'"
}
fn_ln() {
fn_run_cmd "ln -s -- '$1' '$2'"
}
# -----------------------------------------------------------------------------
# Source and destination information
# -----------------------------------------------------------------------------
SSH_USER=""
SSH_HOST=""
SSH_DEST_FOLDER=""
SSH_SRC_FOLDER=""
SSH_CMD=""
SSH_DEST_FOLDER_PREFIX=""
SSH_SRC_FOLDER_PREFIX=""
SSH_PORT="22"
SRC_FOLDER=${1%/}
DEST_FOLDER=${2%/}
EXCLUSION_FILE=$3
SRC_FOLDER=""
DEST_FOLDER=""
EXCLUSION_FILE=""
LOG_DIR="$HOME/.$APPNAME"
AUTO_DELETE_LOG="1"
EXPIRATION_STRATEGY="1:1 30:7 365:30"
AUTO_EXPIRE="1"
RSYNC_FLAGS="-D --compress --numeric-ids --links --hard-links --one-file-system --itemize-changes --times --recursive --perms --owner --group --stats --human-readable"
while :; do
case $1 in
-h|-\?|--help)
fn_display_usage
exit
;;
-p|--port)
shift
SSH_PORT=$1
;;
--rsync-get-flags)
shift
echo $RSYNC_FLAGS
exit
;;
--rsync-set-flags)
shift
RSYNC_FLAGS="$1"
;;
--strategy)
shift
EXPIRATION_STRATEGY="$1"
;;
--log-dir)
shift
LOG_DIR="$1"
AUTO_DELETE_LOG="0"
;;
--no-auto-expire)
AUTO_EXPIRE="0"
;;
--)
shift
SRC_FOLDER="$1"
DEST_FOLDER="$2"
EXCLUSION_FILE="$3"
break
;;
-*)
fn_log_error "Unknown option: \"$1\""
fn_log_info ""
fn_display_usage
exit 1
;;
*)
SRC_FOLDER="$1"
DEST_FOLDER="$2"
EXCLUSION_FILE="$3"
break
esac
shift
done
# Display usage information if required arguments are not passed
if [[ -z "$SRC_FOLDER" || -z "$DEST_FOLDER" ]]; then
fn_display_usage
exit 1
fi
# Strips off last slash from dest. Note that it means the root folder "/"
# will be represented as an empty string "", which is fine
# with the current script (since a "/" is added when needed)
# but still something to keep in mind.
# However, due to this behavior we delay stripping the last slash for
# the source folder until after parsing for ssh usage.
DEST_FOLDER="${DEST_FOLDER%/}"
fn_parse_ssh
if [ -n "$SSH_DEST_FOLDER" ]; then
DEST_FOLDER="$SSH_DEST_FOLDER"
fi
if [ -n "$SSH_SRC_FOLDER" ]; then
SRC_FOLDER="$SSH_SRC_FOLDER"
fi
# Now strip off last slash from source folder.
SRC_FOLDER="${SRC_FOLDER%/}"
for ARG in "$SRC_FOLDER" "$DEST_FOLDER" "$EXCLUSION_FILE"; do
if [[ "$ARG" == *"'"* ]]; then
fn_log_error 'Source and destination directories may not contain single quote characters.'
exit 1
fi
done
# -----------------------------------------------------------------------------
# Check that the destination drive is a backup drive
@@ -41,24 +312,14 @@ EXCLUSION_FILE=$3
# TODO: check that the destination supports hard links
fn_backup_marker_path() {
echo "$1/backup.marker"
}
fn_backup_marker_path() { echo "$1/backup.marker"; }
fn_find_backup_marker() { fn_find "$(fn_backup_marker_path "$1")" 2>/dev/null; }
fn_is_backup_destination() {
DEST_MARKER_FILE="$(fn_backup_marker_path $1)"
if [ -f "$DEST_MARKER_FILE" ]; then
echo "1"
else
echo "0"
fi
}
if [ "$(fn_is_backup_destination $DEST_FOLDER)" != "1" ]; then
if [ -z "$(fn_find_backup_marker "$DEST_FOLDER")" ]; then
fn_log_info "Safety check failed - the destination does not appear to be a backup folder or drive (marker file not found)."
fn_log_info "If it is indeed a backup folder, you may add the marker file by running the following command:"
fn_log_info ""
fn_log_info "touch \"$(fn_backup_marker_path $DEST_FOLDER)\""
fn_log_info_cmd "mkdir -p -- \"$DEST_FOLDER\" ; touch \"$(fn_backup_marker_path "$DEST_FOLDER")\""
fn_log_info ""
exit 1
fi
@@ -67,167 +328,183 @@ fi
# Setup additional variables
# -----------------------------------------------------------------------------
BACKUP_FOLDER_PATTERN="\d\d\d\d-\d\d-\d\d-\d\d\d\d\d\d"
# Date logic
NOW=$(date +"%Y-%m-%d-%H%M%S")
PROFILE_FOLDER="$HOME/.rsync_tmbackup"
LOG_FILE="$PROFILE_FOLDER/$NOW.log"
DEST=$DEST_FOLDER/$NOW
LAST_TIME=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | tail -n 1)
PREVIOUS_DEST=$DEST_FOLDER/$LAST_TIME
INPROGRESS_FILE=$DEST_FOLDER/backup.inprogress
EPOCH=$(date "+%s")
KEEP_ALL_DATE=$((EPOCH - 86400)) # 1 day ago
KEEP_DAILIES_DATE=$((EPOCH - 2678400)) # 31 days ago
export IFS=$'\n' # Better for handling spaces in filenames.
DEST="$DEST_FOLDER/$NOW"
PREVIOUS_DEST="$(fn_find_backups | head -n 1)"
INPROGRESS_FILE="$DEST_FOLDER/backup.inprogress"
MYPID="$$"
# -----------------------------------------------------------------------------
# Create profile folder if it doesn't exist
# Create log folder if it doesn't exist
# -----------------------------------------------------------------------------
if [ ! -d "$PROFILE_FOLDER" ]; then
fn_log_info "Creating profile folder in '$PROFILE_FOLDER'..."
mkdir -- "$PROFILE_FOLDER"
if [ ! -d "$LOG_DIR" ]; then
fn_log_info "Creating log folder in '$LOG_DIR'..."
mkdir -- "$LOG_DIR"
fi
# -----------------------------------------------------------------------------
# Handle case where a previous backup failed or was interrupted.
# -----------------------------------------------------------------------------
if [ -f "$INPROGRESS_FILE" ]; then
if [ "$LAST_TIME" != "" ]; then
if [ -n "$(fn_find "$INPROGRESS_FILE")" ]; then
if [ "$OSTYPE" == "cygwin" ]; then
# 1. Grab the PID of previous run from the PID file
RUNNINGPID="$(fn_run_cmd "cat $INPROGRESS_FILE")"
# 2. Get the command for the process currently running under that PID and look for our script name
RUNNINGCMD="$(procps -wwfo cmd -p $RUNNINGPID --no-headers | grep "$APPNAME")"
# 3. Grab the exit code from grep (0=found, 1=not found)
GREPCODE=$?
# 4. if found, assume backup is still running
if [ "$GREPCODE" = 0 ]; then
fn_log_error "Previous backup task is still active - aborting (command: $RUNNINGCMD)."
exit 1
fi
else
RUNNINGPID="$(fn_run_cmd "cat $INPROGRESS_FILE")"
if [ "$RUNNINGPID" = "$(pgrep -o -f "$APPNAME")" ]; then
fn_log_error "Previous backup task is still active - aborting."
exit 1
fi
fi
if [ -n "$PREVIOUS_DEST" ]; then
# - Last backup is moved to current backup folder so that it can be resumed.
# - 2nd to last backup becomes last backup.
fn_log_info "$INPROGRESS_FILE already exists - the previous backup failed or was interrupted. Backup will resume from there."
LINE_COUNT=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | tail -n 2 | wc -l)
mv $PREVIOUS_DEST $DEST
if [ "$LINE_COUNT" -gt 1 ]; then
SECOND_LAST_TIME=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | tail -n 2 | head -n 1)
LAST_TIME=$SECOND_LAST_TIME
fn_log_info "$SSH_DEST_FOLDER_PREFIX$INPROGRESS_FILE already exists - the previous backup failed or was interrupted. Backup will resume from there."
fn_run_cmd "mv -- $PREVIOUS_DEST $DEST"
if [ "$(fn_find_backups | wc -l)" -gt 1 ]; then
PREVIOUS_DEST="$(fn_find_backups | sed -n '2p')"
else
LAST_TIME=""
PREVIOUS_DEST=""
fi
PREVIOUS_DEST=$DEST_FOLDER/$LAST_TIME
# update PID to current process to avoid multiple concurrent resumes
fn_run_cmd "echo $MYPID > $INPROGRESS_FILE"
fi
fi
# -----------------------------------------------------------------------------
# Check if we are doing an incremental backup (if previous backup exists) or not
# -----------------------------------------------------------------------------
# Run in a loop to handle the "No space left on device" logic.
while : ; do
LINK_DEST_OPTION=""
if [ "$LAST_TIME" == "" ]; then
fn_log_info "No previous backup - creating new one."
else
# If the path is relative, it needs to be relative to the destination. To keep
# it simple, just use an absolute path. See http://serverfault.com/a/210058/118679
PREVIOUS_DEST=`cd \`dirname "$PREVIOUS_DEST"\`; pwd`"/"`basename "$PREVIOUS_DEST"`
fn_log_info "Previous backup found - doing incremental backup from $PREVIOUS_DEST"
LINK_DEST_OPTION="--link-dest=$PREVIOUS_DEST"
fi
# -----------------------------------------------------------------------------
# Check if we are doing an incremental backup (if previous backup exists).
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Create destination folder if it doesn't already exists
# -----------------------------------------------------------------------------
LINK_DEST_OPTION=""
if [ -z "$PREVIOUS_DEST" ]; then
fn_log_info "No previous backup - creating new one."
else
# If the path is relative, it needs to be relative to the destination. To keep
# it simple, just use an absolute path. See http://serverfault.com/a/210058/118679
PREVIOUS_DEST="$(fn_get_absolute_path "$PREVIOUS_DEST")"
fn_log_info "Previous backup found - doing incremental backup from $SSH_DEST_FOLDER_PREFIX$PREVIOUS_DEST"
LINK_DEST_OPTION="--link-dest='$PREVIOUS_DEST'"
fi
if [ ! -d "$DEST" ]; then
fn_log_info "Creating destination $DEST"
mkdir -p $DEST
fi
# -----------------------------------------------------------------------------
# Create destination folder if it doesn't already exists
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Start backup
# -----------------------------------------------------------------------------
if [ -z "$(fn_find "$DEST -type d" 2>/dev/null)" ]; then
fn_log_info "Creating destination $SSH_DEST_FOLDER_PREFIX$DEST"
fn_mkdir "$DEST"
fi
# Run in a loop to handle the "No space left on device" logic
while [ "1" ]; do
LOG_FILE="$PROFILE_FOLDER/$(date +"%Y-%m-%d-%H%M%S").log"
# -----------------------------------------------------------------------------
# Purge certain old backups before beginning new backup.
# -----------------------------------------------------------------------------
fn_expire_backups
# -----------------------------------------------------------------------------
# Start backup
# -----------------------------------------------------------------------------
LOG_FILE="$LOG_DIR/$(date +"%Y-%m-%d-%H%M%S").log"
fn_log_info "Starting backup..."
fn_log_info "From: $SRC_FOLDER"
fn_log_info "To: $DEST"
fn_log_info "From: $SSH_SRC_FOLDER_PREFIX$SRC_FOLDER/"
fn_log_info "To: $SSH_DEST_FOLDER_PREFIX$DEST/"
CMD="rsync"
CMD="$CMD --compress"
CMD="$CMD --numeric-ids"
CMD="$CMD --links"
CMD="$CMD --hard-links"
CMD="$CMD --delete"
CMD="$CMD --delete-excluded"
CMD="$CMD --one-file-system"
CMD="$CMD --archive"
CMD="$CMD --progress"
CMD="$CMD --itemize-changes"
CMD="$CMD --verbose"
if [ -n "$SSH_CMD" ]; then
CMD="$CMD -e 'ssh -p $SSH_PORT -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'"
fi
CMD="$CMD $RSYNC_FLAGS"
CMD="$CMD --log-file '$LOG_FILE'"
if [ "$EXCLUSION_FILE" != "" ]; then
CMD="$CMD --exclude-from \"$EXCLUSION_FILE\""
if [ -n "$EXCLUSION_FILE" ]; then
# We've already checked that $EXCLUSION_FILE doesn't contain a single quote
CMD="$CMD --exclude-from '$EXCLUSION_FILE'"
fi
CMD="$CMD $LINK_DEST_OPTION"
CMD="$CMD $SRC_FOLDER/ $DEST/"
CMD="$CMD | grep -E '^deleting|[^/]$'"
CMD="$CMD -- '$SSH_SRC_FOLDER_PREFIX$SRC_FOLDER/' '$SSH_DEST_FOLDER_PREFIX$DEST/'"
fn_log_info "Running command:"
fn_log_info "$CMD"
touch $INPROGRESS_FILE
fn_run_cmd "echo $MYPID > $INPROGRESS_FILE"
eval $CMD
RSYNC_EXIT_CODE=$?
# -----------------------------------------------------------------------------
# Check if we ran out of space
# -----------------------------------------------------------------------------
# TODO: find better way to check for out of space condition without parsing log.
grep --quiet "No space left on device (28)" "$LOG_FILE"
NO_SPACE_LEFT="$?"
if [ "$NO_SPACE_LEFT" != "0" ]; then
# This error might also happen if there is no space left
grep --quiet "Result too large (34)" "$LOG_FILE"
NO_SPACE_LEFT="$?"
fi
NO_SPACE_LEFT="$(grep "No space left on device (28)\|Result too large (34)" "$LOG_FILE")"
rm -- "$LOG_FILE"
if [ -n "$NO_SPACE_LEFT" ]; then
if [ "$NO_SPACE_LEFT" == "0" ]; then
read -p "It looks like there is no space left on the destination. Delete old backup? (Y/n) " yn
case $yn in
[Nn]* ) exit 0;;
esac
if [[ $AUTO_EXPIRE == "0" ]]; then
fn_log_error "No space left on device, and automatic purging of old backups is disabled."
exit 1
fi
fn_log_warn "No space left on device - removing oldest backup and resuming."
BACKUP_FOLDER_COUNT=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | wc -l)
if [ "$BACKUP_FOLDER_COUNT" -lt "2" ]; then
if [[ "$(fn_find_backups | wc -l)" -lt "2" ]]; then
fn_log_error "No space left on device, and no old backup to delete."
exit 1
fi
# TODO: handle case where only two backup folders are left. In which case, need to remove --link-dest option
OLDEST_BACKUP=$(ls -1 $DEST_FOLDER | grep "$BACKUP_FOLDER_PATTERN" | head -n 1)
if [ "$OLDEST_BACKUP" == "" ]; then
fn_log_error "No space left on device, and cannot get path to oldest backup to delete."
exit 1
fi
OLD_BACKUP_PATH="$DEST_FOLDER/$OLDEST_BACKUP"
# Double-check that we're on a backup destination to be completely sure we're deleting the right folder
OLD_BACKUP_PARENT_PATH=$(dirname -- "$OLD_BACKUP_PATH")
if [ "$(fn_is_backup_destination $OLD_BACKUP_PARENT_PATH)" != "1" ]; then
fn_log_error "'$OLD_BACKUP_PATH' is not on a backup destination - aborting."
exit 1
fi
fn_log_info "Deleting '$OLD_BACKUP_PATH'..."
rm -rf -- "$OLD_BACKUP_PATH"
fn_expire_backup "$(fn_find_backups | tail -n 1)"
# Resume backup
continue
fi
if [ "$RSYNC_EXIT_CODE" != "0" ]; then
fn_log_error "Exited with error code $RSYNC_EXIT_CODE"
exit $RSYNC_EXIT_CODE
# -----------------------------------------------------------------------------
# Check whether rsync reported any errors
# -----------------------------------------------------------------------------
EXIT_CODE="1"
if [ -n "$(grep "rsync error:" "$LOG_FILE")" ]; then
fn_log_error "Rsync reported an error. Run this command for more details: grep -E 'rsync:|rsync error:' '$LOG_FILE'"
elif [ -n "$(grep "rsync:" "$LOG_FILE")" ]; then
fn_log_warn "Rsync reported a warning. Run this command for more details: grep -E 'rsync:|rsync error:' '$LOG_FILE'"
else
fn_log_info "Backup completed without errors."
if [[ $AUTO_DELETE_LOG == "1" ]]; then
rm -f -- "$LOG_FILE"
fi
EXIT_CODE="0"
fi
rm $INPROGRESS_FILE
fn_log_info "Backup completed without errors."
exit 0
# -----------------------------------------------------------------------------
# Add symlink to last backup
# -----------------------------------------------------------------------------
fn_rm_file "$DEST_FOLDER/latest"
fn_ln "$(basename -- "$DEST")" "$DEST_FOLDER/latest"
fn_rm_file "$INPROGRESS_FILE"
exit $EXIT_CODE
done
+30
View File
@@ -0,0 +1,30 @@
<?php
// This PHP script can be used to test the expiration strategy.
// It is going to populate a directory with fake backup sets (directories named Y-m-d-His) over several months.
// Then the backup script can be run on it to check what directories are going to be deleted.
// rm -rf ./tests/TestDest/201* && php ./tests/populate_dest.php && ./rsync_tmbackup.sh ./tests/TestSource/ ./tests/TestDest/
$baseDir = dirname(__FILE__);
$destDir = $baseDir . '/TestDest';
$backupsPerDay = 2;
$totalDays = 500;
$intervalBetweenBackups = null;
if ($backupsPerDay === 1) {
$intervalBetweenBackups = 'PT1D';
} else if ($backupsPerDay === 2) {
$intervalBetweenBackups = 'PT12H';
} else {
throw new Exception('Not implemented');
}
$d = new DateTime();
$d->sub(new DateInterval('P' . $totalDays . 'D'));
for ($i = 0; $i < $backupsPerDay * $totalDays; $i++) {
$d->add(new DateInterval($intervalBetweenBackups));
mkdir($destDir . '/' . $d->format('Y-m-d-His'), 0777, true);
}