This repository has been archived by the owner on Apr 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrestic.sh
executable file
·164 lines (144 loc) · 5.32 KB
/
restic.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env bash
# restic example
# In your run-script just run "$DATA_DIR/rte/restic.sh"
#
# provide these files:
# - $EXEC_DIR/RESTIC_PASSWORD
# - $EXEC_DIR/RESTIC_TARGETS
# - maybe some ssh-config
#
# RESTIC_PASSWORD just includes a single line with the password to all repos
#
# RESTIC_TARGETS has the following format:
# repo,exclude-file,path1,path2,path3,…,pathn
# examples:
# # backup of home dir without any exclusions
# rclone:host:backup/user-home,,/home/user
# # backup with exclusions
# rclone:host:backup/user-config,exclude-user-config,/home/user/.config
# # backup as root (add sudo: before repo name)
# sudo:rclone:host:etc,,/etc
# # (sudo must be possible without password)
# # multi-path, local repo
# /backup/home,exclude-home,/home/user,/opt,/mnt
#
# There can be these config options (anywhere in the file):
# # RESTIC: forget=--keep-hourly 8 --keep-daily 30 --keep-monthly 24 --keep-yearly 5 --group-by host
# → automatically forget and prune snapshots according to the policy
# (see https://restic.readthedocs.io/en/latest/060_forget.html)
#
# Lines starting with # are ignored.
# Don't include any special chars.
# Variables get expanded via eval, so be careful.
#
# exit code is 0 on success, 2 if target/password not found, 1 on any other (restic) error
# if $1 exist it gets filled with a line containing path→repo pairs with errors, seperated by |
result="$1"
# try updating restic, don't do anything if it's not working
restic self-update >/dev/null 2>/dev/null
# if there is a rclone.conf in $EXEC_DIR and RCLONE_CONFIG is not set, just use it
if [ -f "$EXEC_DIR/rclone.conf" ] && [ -z "$RCLONE_CONFIG" ]; then
export RCLONE_CONFIG="$EXEC_DIR/rclone.conf"
fi
errlog=""
exit_code=0
# targets and password file need to exist
if [ ! -f "$EXEC_DIR/RESTIC_TARGETS" ] || [ ! -f "$EXEC_DIR/RESTIC_PASSWORD" ]; then
echo "restic: coulnd't find targets or password file"
if [ -f "$1" ]; then
echo "restic: coulnd't find targets or password file" >> "$1"
fi
exit 2
fi
# auto-forget?
IFS=" " read -r -a forget <<< "$(awk '/RESTIC:[[:space:]]*forget=/{sub(/^.*RESTIC:[[:space:]]*forget=/,""); print}' "$EXEC_DIR/RESTIC_TARGETS")"
# files exist, error log is handled, let's start.
while IFS="," read -r repo exclude_file path_all; do
if [ -f "$EXEC_DIR/$exclude_file" ]; then
exclude_file="$EXEC_DIR/$exclude_file"
else
exclude_file="/dev/null"
fi
paths=()
while read -r path_element; do
paths+=("$path_element")
done < <(tr "," "\n" <<< "$path_all")
path_echo="$(printf ", %s" "${paths[@]}")"
path_echo="${path_echo:2}"
echo "backing up $path_echo → $repo"
# run as root?
if [ "${repo:0:5}" = "sudo:" ]; then
repo="${repo:5}"
if command -v setcap >/dev/null 2>/dev/null; then
sudo setcap "CAP_DAC_READ_SEARCH+ep" "$(command -v restic)"
unset sudo
else
sudo=("sudo" "-E")
fi
else
unset sudo
fi
# set arguments for restic
args=("-r" "$repo")
args+=("--password-file" "$EXEC_DIR/RESTIC_PASSWORD")
# verbose?
verbose="${verbose:-2}"
# check if repo exists
if ! "${sudo[@]}" restic cat config "${args[@]}" >/dev/null 2>/dev/null; then
echo "$repo has some error, try unlocking"
"${sudo[@]}" restic unlock "${args[@]}"
# check again
if ! "${sudo[@]}" restic cat config "${args[@]}" >/dev/null 2>/dev/null; then
echo "$repo doesn't exist yet, try to initialize it"
# try init
if ! "${sudo[@]}" restic init "${args[@]}"; then
echo "$repo couldn't be initialized, giving up"
errlog="$errlog|noinit $repo"
exit_code=1
continue
fi
fi
fi
# ok, looks like a backup is due
"${sudo[@]}" restic backup "${paths[@]}" "${args[@]}" \
"--verbose=$verbose" \
"--exclude-file=$exclude_file" | \
grep --line-buffered -v "^unchanged" | sed -u "s|^|$repo |"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
# and something went wrong
# first try rebuilding the index, maybe that helps
echo "some error occured during backup to $repo, trying to rebuild the index. no backup this time."
# since ver 0.16 use repair index
if restic version | tr -d . | awk '{exit (160>$2)}'; then
"${sudo[@]}" restic repair index "${args[@]}" | sed -u "s|^|$repo |"
else
"${sudo[@]}" restic rebuild-index "${args[@]}" | sed -u "s|^|$repo |"
fi
if [ ${PIPESTATUS[0]} -ne 0 ]; then
errlog="$errlog|err $path_echo → $repo (rebuilt index failed)"
else
errlog="$errlog|err $path_echo → $repo (rebuilt index succeded)"
fi
exit_code=1
else
# everything ok, just unlock everything again to remove stale locks
"${sudo[@]}" restic unlock "${args[@]}"
fi
# forget?
if [ "${#forget[@]}" -ne 0 ]; then
"${sudo[@]}" restic forget "${args[@]}" "${forget[@]}" --prune | sed -u "s|^|$repo |"
if [ ${PIPESTATUS[0]} -ne 0 ]; then
errlog="$errlog|err forget: $repo"
exit_code=1
fi
fi
# remove capability
if command -v setcap >/dev/null 2>/dev/null; then
sudo setcap "CAP_DAC_READ_SEARCH-ep" "$(command -v restic)"
fi
done < <(grep "^[^#]" "$EXEC_DIR/RESTIC_TARGETS" | sed 's/^ *//;s/ *,/,/g;s/, */,/g;s/ *$//')
if [ -f "$result" ] && [ $exit_code -ne 0 ]; then
echo "restic: ${errlog:1}" >> "$result"
fi
# all done, exit with $exit_code (1 on error)
exit $exit_code