161 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
David Arthur 0c679903af Fixed
Brain fart
2013-11-13 15:52:24 -05:00
David Arthur bda24be003 Add a "latest" symlink when backup is successful 2013-11-13 13:29:08 -05:00
4 changed files with 508 additions and 154 deletions
+5
View File
@@ -0,0 +1,5 @@
.idea
test.sh
*~
tests/TestDest/
tests/TestSource/
+111 -22
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
@@ -19,31 +54,85 @@ Time Machine style backup with rsync. Should work on Linux, Mac OS X and Windows
* Backup with exclusion list:
rsync_tmbackup.sh /home /mnt/backup_drive excluded_patterns.txt
* Backup to remote drive over SSH, on port 2222:
rsync_tmbackup.sh -p 2222 /home user@example.com:/mnt/backup_drive
* 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.
## Exclude file
## How to restore
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.
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).
# Features
## Extensions
* Each backup is on its own folder named after the current timestamp. Files can be copied and restored directly, without any intermediate tool.
* [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
* 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
## 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
## LICENSE
[MIT](http://opensource.org/licenses/MIT)
The MIT License (MIT)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/laurent22/rsync-time-backup/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
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
+362 -132
View File
@@ -1,19 +1,20 @@
#!/usr/bin/env bash
APPNAME=$(basename $0 | sed "s/\.sh$//")
# -----------------------------------------------------------------------------
# Log functions
# -----------------------------------------------------------------------------
fn_log_info() {
echo "rsync_tmbackup: $1"
}
fn_log_warn() {
echo "rsync_tmbackup: [WARNING] $1"
}
fn_log_error() {
echo "rsync_tmbackup: [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,7 +22,7 @@ fn_log_error() {
# -----------------------------------------------------------------------------
fn_terminate_script() {
echo "rsync_tmbackup: SIGINT caught."
fn_log_info "SIGINT caught."
exit 1
}
@@ -30,39 +31,277 @@ 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.
date -d "${1:0:10} ${1:11:2}:${1:13:2}:${1:15:2}" +%s
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() {
find "$DEST_FOLDER" -type d -name "????-??-??-??????" -prune
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_is_backup_destination "$(dirname -- "$1")")" ]; then
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"
rm -rf -- "$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"
for arg in "$SRC_FOLDER" "$DEST_FOLDER" "$EXCLUSION_FILE"; do
if [[ "$arg" == *"'"* ]]; then
fn_log_error 'Arguments may not have any single quote characters.'
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
@@ -73,19 +312,14 @@ done
# 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() {
find "$(fn_backup_marker_path "$1")" 2>/dev/null
}
if [ -z "$(fn_is_backup_destination $DEST_FOLDER)" ]; 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
@@ -94,187 +328,183 @@ fi
# Setup additional variables
# -----------------------------------------------------------------------------
export IFS=$'\n' # Better for handling spaces in filenames.
# 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
PREVIOUS_DEST=$(fn_find_backups | sort | tail -n 1)
INPROGRESS_FILE=$DEST_FOLDER/backup.inprogress
KEEP_ALL_DATE=$(date -d '-1 day' +%s)
KEEP_DAILIES_DATE=$(date -d '-1 month' +%s)
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 [ "$PREVIOUS_DEST" != "" ]; 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=$(fn_find_backups | sort | tail -n 2 | wc -l)
mv -- "$PREVIOUS_DEST" "$DEST"
if [ "$LINE_COUNT" -gt 1 ]; then
PREVIOUS_PREVIOUS_DEST=$(fn_find_backups | sort | tail -n 2 | head -n 1)
PREVIOUS_DEST=$PREVIOUS_PREVIOUS_DEST
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
PREVIOUS_DEST=""
fi
# update PID to current process to avoid multiple concurrent resumes
fn_run_cmd "echo $MYPID > $INPROGRESS_FILE"
fi
fi
# Run in a loop to handle the "No space left on device" logic.
while [ "1" ]; do
while : ; do
# -----------------------------------------------------------------------------
# Check if we are doing an incremental backup (if previous backup exists) or not
# Check if we are doing an incremental backup (if previous backup exists).
# -----------------------------------------------------------------------------
LINK_DEST_OPTION=""
if [ "$PREVIOUS_DEST" == "" ]; then
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=`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"
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
# -----------------------------------------------------------------------------
# Create destination folder if it doesn't already exists
# -----------------------------------------------------------------------------
if [ ! -d "$DEST" ]; then
fn_log_info "Creating destination $DEST"
mkdir -p -- "$DEST"
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
# -----------------------------------------------------------------------------
# Purge certain old backups before beginning new backup.
# -----------------------------------------------------------------------------
# Default value for $prev ensures that the most recent backup is never deleted.
prev="0000-00-00-000000"
for fname in $(fn_find_backups | sort -r); do
date=$(basename "$fname")
stamp=$(fn_parse_date $date)
# Skip if failed to parse date...
[ -n "$stamp" ] || continue
if [ $stamp -ge $KEEP_ALL_DATE ]; then
true
elif [ $stamp -ge $KEEP_DAILIES_DATE ]; then
# Delete all but the most recent of each day.
[ ${date:8:2} -eq ${prev:8:2} ] && fn_expire_backup "$fname"
else
# Delete all but the most recent of each month.
[ ${date:5:2} -eq ${prev:5:2} ] && fn_expire_backup "$fname"
fi
prev=$date
done
fn_expire_backups
# -----------------------------------------------------------------------------
# Start backup
# -----------------------------------------------------------------------------
LOG_FILE="$PROFILE_FOLDER/$(date +"%Y-%m-%d-%H%M%S").log"
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 --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
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
# TODO: -y flag
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=$(fn_find_backups | 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
OLD_BACKUP_PATH=$(fn_find_backups | head -n 1)
if [ "$OLD_BACKUP_PATH" == "" ]; then
fn_log_error "No space left on device, and cannot get path to oldest backup to delete."
exit 1
fi
fn_expire_backup "$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"
# TODO: grep for "^rsync error:.*$" in log
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);
}