Skip to content

Commit ed3adc0

Browse files
committed
Add support for multiple source directories
1 parent ed0cd9a commit ed3adc0

File tree

4 files changed

+108
-39
lines changed

4 files changed

+108
-39
lines changed

README.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,38 @@ An easy to use file backup script where each job is based around a simple config
99
- Can hard link identical files with a single backup
1010
- Very customizable
1111

12+
Quick Example
13+
------------------------
14+
Setup SSH PubKey authentication between backup machine and client.
1215

13-
Quick Links
16+
Create a config file on backup machine:
17+
```
18+
19+
SOURCE_DIR=server1.example.com:/home/ :/etc :/var/www
20+
TARGET_DIR=/backups/server1/
21+
BACKUP_TYPE=rotation
22+
MAX_ROTATIONS=30
23+
ROTATIONALS_HARD_LINK=1
24+
ALLOW_DELETIONS=1
25+
ALLOW_OVERWRITES=1
26+
LOG_DIR=/var/log/cfgbackup/
27+
COMPRESS_LOGS=1
28+
```
29+
30+
Start the backup:
31+
```
32+
cfgbackup my.conf run
33+
```
34+
35+
Check the status of current/last run:
36+
```
37+
cfgbackup my.conf status
38+
```
39+
40+
That's all there is to getting started! But there is a whole lot more functionality and customization available.
41+
42+
43+
Links
1444
------------------------
1545
* [Dependencies](#dependencies)
1646
* [Installation](#installation)
@@ -190,11 +220,18 @@ Config Options
190220
<a name="source-dir"></a>
191221
`SOURCE_DIR` [Required]
192222
The directory to create backups from. Can be local or remote
193-
via SSH.
223+
via SSH. Can specify multiple directories, whitespace delimited. If specifying multiple
224+
remote directories, all directories must be on same host (limitation of rsync).
225+
226+
Source directories WITH a trailing slash will sync the contents of the source directory
227+
into the target backup. Source directories WITHOUT a trailing slash will sync the directory
228+
itself (along with its contents) into the target backup.
194229
```
195230
SOURCE_DIR=/home/
196231
SOURCE_DIR=/var/data
197232
[email protected]:/path/to/files/
233+
SOURCE_DIR=/etc /var/www /home
234+
SOURCE_DIR=server.example.com:/etc :/var/www :/home
198235
```
199236

200237
<a name="target-dir"></a>

cfgbackup

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# SOFTWARE.
2222
################################################################################
2323

24-
VERSION=0.4.1
24+
VERSION=0.4.2
2525

2626
#######################################
2727
# MISC HELPER FUNCTIONS
@@ -102,6 +102,7 @@ substr_index() {
102102
local PREMATCH="${HAYSTACK%%$NEEDLE*}"
103103
if [[ $PREMATCH != $HAYSTACK ]]; then
104104
echo ${#PREMATCH}
105+
return
105106
fi
106107
echo -1
107108
}
@@ -423,6 +424,10 @@ parse_config() {
423424
if [[ ${CONFIG[BACKUP_TYPE]} == "mirror" && ${CONFIG[MIRROR_CONFLICT_ACTION]} != "update" && ${CONFIG[MIRROR_CONFLICT_ACTION]} != "delete" ]]; then
424425
PARSE_ERRORS+=("Value of MIRROR_CONFLICT_ACTION must be either update or delete.")
425426
fi
427+
read -r -a source_dirs <<< "${CONFIG[SOURCE_DIR]}"
428+
if [[ ${CONFIG[BACKUP_TYPE]} == "mirror" && ${#source_dirs[@]} -gt 1 ]]; then
429+
PARSE_ERRORS+=("Backup type of mirror can only have a single SOURCE_DIR directory.")
430+
fi
426431
# Ensure ROTATE_SUBDIR is valid
427432
if [[ ${CONFIG[BACKUP_TYPE]} == "rotation" ]]; then
428433
MATCH_NUM0=$( substr_index "${CONFIG[ROTATE_SUBDIR]}" "NUM0" )
@@ -874,7 +879,7 @@ dirpath_is_remote() {
874879
DIRPATH="$1"
875880
local COLON_IDX=$( substr_index "${DIRPATH}" ":" )
876881
local SLASH_IDX=$( substr_index "${DIRPATH}" "/" )
877-
if [[ $COLON_IDX -ge "0" && $COLON_IDX -lt $SLASH_IDX ]]; then
882+
if [[ $COLON_IDX -ge 0 && $COLON_IDX -lt $SLASH_IDX ]]; then
878883
return 0
879884
fi
880885
return 1
@@ -891,31 +896,39 @@ command_check() {
891896
fi
892897

893898
# Test source access
894-
dirpath_is_remote "${CONFIG[SOURCE_DIR]}"
895-
if [[ $? -eq 0 ]]; then
896-
local COLON_IDX=$( substr_index "${CONFIG[SOURCE_DIR]}" ":" )+1
897-
local SSH_CONNECT=${CONFIG[SOURCE_DIR]:0:$COLON_IDX}
898-
local SSH_SOURCE=${CONFIG[SOURCE_DIR]:1+$COLON_IDX}
899-
# Check SSH connection
900-
ssh -o BatchMode=yes $SSH_CONNECT exit 0
901-
if [[ $? != 0 ]]; then
902-
echo "ERROR: Could not connect via SSH to ${SSH_CONNECT}"
903-
exit 1
904-
fi
905-
906-
# Check remote directory
907-
ssh -o BatchMode=yes $SSH_CONNECT "[[ ! -d $SSH_SOURCE || ! -r $SSH_SOURCE ]]"
899+
read -r -a SOURCE_DIRS <<< "${CONFIG[SOURCE_DIR]}"
900+
LAST_SSH_CONNECT=
901+
for SRC_DIR in "${SOURCE_DIRS[@]}"; do
902+
dirpath_is_remote "${SRC_DIR}"
908903
if [[ $? -eq 0 ]]; then
909-
echo "ERROR: Cannot read from remote source directory of ${SSH_SOURCE}"
910-
exit 1
911-
fi
912-
else
913-
# Check local directory
914-
if [[ ! -d ${CONFIG[SOURCE_DIR]} || ! -r ${CONFIG[SOURCE_DIR]} ]]; then
915-
echo "ERROR: Cannot read from local source directory of ${CONFIG[SOURCE_DIR]}"
916-
exit 1
904+
local COLON_IDX=$( substr_index "${SRC_DIR}" ":" )
905+
local SSH_CONNECT=${SRC_DIR:0:$COLON_IDX}
906+
local SSH_SOURCE=${SRC_DIR:1+$COLON_IDX}
907+
if [[ -z $SSH_CONNECT ]]; then
908+
SSH_CONNECT=$LAST_SSH_CONNECT
909+
fi
910+
# Check SSH connection
911+
ssh -o BatchMode=yes $SSH_CONNECT exit 0
912+
if [[ $? != 0 ]]; then
913+
echo "ERROR: Could not connect via SSH to ${SSH_CONNECT}"
914+
exit 1
915+
fi
916+
LAST_SSH_CONNECT=$SSH_CONNECT
917+
918+
# Check remote directory
919+
ssh -o BatchMode=yes $LAST_SSH_CONNECT "[[ ! -d $SSH_SOURCE || ! -r $SSH_SOURCE ]]"
920+
if [[ $? -eq 0 ]]; then
921+
echo "ERROR: Cannot read from remote source directory of ${SSH_SOURCE}"
922+
exit 1
923+
fi
924+
else
925+
# Check local directory
926+
if [[ ! -d ${SRC_DIR} || ! -r ${SRC_DIR} ]]; then
927+
echo "ERROR: Cannot read from local source directory of ${SRC_DIR}"
928+
exit 1
929+
fi
917930
fi
918-
fi
931+
done
919932

920933
# Test target access
921934
if [[ ! -d ${CONFIG[TARGET_DIR]} || ! -w ${CONFIG[TARGET_DIR]} ]]; then
@@ -1133,14 +1146,23 @@ hardlink_identicals() {
11331146
## Escape the rsync source path, including the user and host for remote ssh sources
11341147
## Output the escaped path
11351148
escaped_rsync_source() {
1136-
ESCPATH=$( epath_join ${CONFIG[SOURCE_DIR]} )
1137-
dirpath_is_remote "${CONFIG[SOURCE_DIR]}"
1138-
if [[ $? == 0 ]]; then
1139-
local COLON_IDX=$( substr_index "${CONFIG[SOURCE_DIR]}" ":" )+1
1140-
local SSH_CONNECT=${CONFIG[SOURCE_DIR]:0:$COLON_IDX}
1141-
local SSH_SOURCE=${CONFIG[SOURCE_DIR]:1+$COLON_IDX}
1142-
ESCPATH="${SSH_CONNECT}:\"$( epath_join "$SSH_SOURCE" )\""
1143-
fi
1149+
read -r -a SOURCE_DIRS <<< "${CONFIG[SOURCE_DIR]}"
1150+
ESCPATH=
1151+
for SRC_DIR in "${SOURCE_DIRS[@]}"; do
1152+
TRAILSLASH=${SRC_DIR: -1}
1153+
if [[ $TRAILSLASH != "/" ]]; then
1154+
TRAILSLASH=
1155+
fi
1156+
ESCSRCPATH="$( epath_join ${SRC_DIR} )${TRAILSLASH}"
1157+
dirpath_is_remote "${SRC_DIR}"
1158+
if [[ $? == 0 ]]; then
1159+
local COLON_IDX=$( substr_index "${SRC_DIR}" ":" )
1160+
local SSH_CONNECT=${SRC_DIR:0:$COLON_IDX}
1161+
local SSH_SOURCE=${SRC_DIR:1+$COLON_IDX}
1162+
ESCSRCPATH="${SSH_CONNECT}:\"$( epath_join "$SSH_SOURCE" )${TRAILSLASH}\""
1163+
fi
1164+
ESCPATH="${ESCPATH} ${ESCSRCPATH}"
1165+
done
11441166
echo "$ESCPATH"
11451167
}
11461168

@@ -1161,7 +1183,7 @@ runjob_sync() {
11611183
# Exclude PID_FILE from being synced
11621184
RSYNC_FLAGS="${RSYNC_FLAGS} --exclude=/${PID_FILE}"
11631185

1164-
RSYNC_COMMAND="${CONFIG[RSYNC_PATH]} ${RSYNC_FLAGS} ${SYNC_FROM}/ ${RUN_DIR}/"
1186+
RSYNC_COMMAND="${CONFIG[RSYNC_PATH]} ${RSYNC_FLAGS} ${SYNC_FROM} ${RUN_DIR}/"
11651187
log_entry "| Running rsync: $RSYNC_COMMAND"
11661188
RSYNC_COMMAND="$RSYNC_COMMAND >> ${LOG_FILE} 2>&1"
11671189
eval $RSYNC_COMMAND
@@ -1233,7 +1255,7 @@ runjob_rotation() {
12331255
fi
12341256

12351257
SYNC_FROM=$( escaped_rsync_source )
1236-
RSYNC_COMMAND="${CONFIG[RSYNC_PATH]} ${RSYNC_FLAGS} ${SYNC_FROM}/ ${RUN_DIR}/"
1258+
RSYNC_COMMAND="${CONFIG[RSYNC_PATH]} ${RSYNC_FLAGS} ${SYNC_FROM} ${RUN_DIR}/"
12371259
log_entry "| Running rsync: $RSYNC_COMMAND"
12381260
RSYNC_COMMAND="$RSYNC_COMMAND >> ${LOG_FILE} 2>&1"
12391261
eval $RSYNC_COMMAND
@@ -1275,7 +1297,7 @@ runjob_skipped_files() {
12751297
RSYNC_FLAGS="${RSYNC_FLAGS} --exclude=/${PID_FILE}"
12761298
fi
12771299

1278-
RSYNC_COMMAND="${CONFIG[RSYNC_PATH]} ${RSYNC_FLAGS} ${SYNC_FROM}/ ${RUN_DIR}/"
1300+
RSYNC_COMMAND="${CONFIG[RSYNC_PATH]} ${RSYNC_FLAGS} ${SYNC_FROM} ${RUN_DIR}/"
12791301
log_entry "| Checking for skipped files..."
12801302
log_entry "| Running rsync: $RSYNC_COMMAND"
12811303
SKIP_RESULTS=$( $RSYNC_COMMAND | tee -a $LOG_FILE 2>&1 )

example.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,12 @@
1010
NOTIFY_EMAIL=
1111

1212
# Where to backup files from (or mirror); can be local or a SSH location
13+
# Multiple source locations may be specified, whitespace separated
14+
# Trailing slash will sync dir contents only; no trailing slash will sync dir name and its contents
1315
# SOURCE_DIR=/path/to/files/
1416
# [email protected]:/path/to/files/
17+
# SOURCE_DIR=/path/to/files /more/files/here /yet/more/files
18+
# SOURCE_DIR=remote:/path/to/files :/more/remote :/still/more
1519
SOURCE_DIR=
1620

1721
# Target where to backup files to (or mirror); must be a local directory

man/cfgbackup.1

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.TH "cfgbackup" "1" "26 Feb 2017" "" ""
1+
.TH "cfgbackup" "1" "03 Jul 2018" "" ""
22
.SH "NAME"
33
cfgbackup \- "cfgbackup's a fairly good backup"
44

@@ -45,6 +45,12 @@ Binary options allow values of 0 (disabled) or 1 (enabled).
4545
.TP
4646
.B SOURCE_DIR
4747
The directory to create backups from. Can be local or remote via SSH.
48+
Can specify multiple directories, whitespace delimited. If specifying multiple
49+
remote directories, all directories must be on same host (limitation of rsync).
50+
.IP
51+
Source directories WITH a trailing slash will sync the contents of the source directory
52+
into the target backup. Source directories WITHOUT a trailing slash will sync the directory
53+
itself (along with its contents) into the target backup.
4854

4955
.TP
5056
.B TARGET_DIR

0 commit comments

Comments
 (0)