taperole 1.5.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.hound.yml +3 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +9 -0
- data/README.md +22 -5
- data/bin/tape +6 -0
- data/lib/tape/ansible_runner.rb +1 -0
- data/lib/tape/overwriter.rb +14 -0
- data/requirements.yml +1 -2
- data/roles/backend_install_essentials/meta/main.yml +1 -1
- data/roles/backend_install_essentials/tasks/main.yml +5 -0
- data/roles/backend_install_essentials/templates/gemrc.j2 +1 -0
- data/roles/postgres/meta/main.yml +2 -0
- data/roles/ruby/defaults/main.yml +11 -0
- data/roles/ruby/files/gemrc +5 -0
- data/{vendor/zzet.rbenv → roles/ruby}/tasks/apt_build_depends.yml +2 -3
- data/roles/ruby/tasks/main.yml +120 -0
- data/taperole.gemspec +1 -1
- data/templates/base/tape_vars.example.yml +17 -0
- data/test/base_docker_box/Dockerfile +38 -0
- data/test/rails/Dockerfile +34 -0
- data/test/rails/start_rails.sh +6 -0
- data/test/rails/tape_vars.yml +12 -0
- data/vars/defaults.yml +26 -0
- data/vendor/Stouts.backup/.bumpversion.cfg +6 -0
- data/vendor/Stouts.backup/.gitignore +1 -0
- data/vendor/Stouts.backup/.travis.yml +34 -0
- data/vendor/Stouts.backup/CONTRIBUTORS +7 -0
- data/vendor/Stouts.backup/LICENSE +21 -0
- data/vendor/Stouts.backup/Makefile +20 -0
- data/vendor/Stouts.backup/README.md +169 -0
- data/vendor/Stouts.backup/defaults/main.yml +93 -0
- data/vendor/Stouts.backup/meta/.galaxy_install_info +1 -0
- data/vendor/Stouts.backup/meta/main.yml +16 -0
- data/vendor/Stouts.backup/tasks/backup.yml +10 -0
- data/vendor/Stouts.backup/tasks/configure.yml +53 -0
- data/vendor/Stouts.backup/tasks/install.deb.yml +19 -0
- data/vendor/Stouts.backup/tasks/main.yml +5 -0
- data/vendor/Stouts.backup/tasks/remove.yml +12 -0
- data/vendor/Stouts.backup/templates/conf.j2 +78 -0
- data/vendor/Stouts.backup/templates/cron.j2 +10 -0
- data/vendor/Stouts.backup/templates/duply.sh.j2 +2240 -0
- data/vendor/Stouts.backup/templates/exclude.j2 +3 -0
- data/vendor/Stouts.backup/templates/logrotate.j2 +14 -0
- data/vendor/Stouts.backup/templates/post.j2 +14 -0
- data/vendor/Stouts.backup/templates/pre.j2 +26 -0
- data/vendor/Stouts.backup/templates/restore.j2 +29 -0
- data/vendor/Stouts.backup/test.yml +6 -0
- metadata +42 -26
- data/id_rsa_sb_basebox +0 -27
- data/vendor/zzet.rbenv/.kitchen.yml +0 -40
- data/vendor/zzet.rbenv/.travis.yml +0 -15
- data/vendor/zzet.rbenv/README.md +0 -100
- data/vendor/zzet.rbenv/defaults/main.yml +0 -20
- data/vendor/zzet.rbenv/files/default-gems +0 -1
- data/vendor/zzet.rbenv/files/gemrc +0 -4
- data/vendor/zzet.rbenv/meta/.galaxy_install_info +0 -1
- data/vendor/zzet.rbenv/meta/main.yml +0 -27
- data/vendor/zzet.rbenv/role.yml +0 -10
- data/vendor/zzet.rbenv/tasks/homebrew_build_depends.yml +0 -12
- data/vendor/zzet.rbenv/tasks/main.yml +0 -226
- data/vendor/zzet.rbenv/tasks/pacman_build_depends.yml +0 -2
- data/vendor/zzet.rbenv/tasks/yum_build_depends.yml +0 -15
- data/vendor/zzet.rbenv/test/integration/default/serverspec/rbenv_spec.rb +0 -26
- data/vendor/zzet.rbenv/test/integration/helpers/serverspec/spec_helper.rb +0 -10
- data/vendor/zzet.rbenv/test/integration/site.yml +0 -8
- data/vendor/zzet.rbenv/vars/user.yml +0 -2
- /data/{vendor/zzet.rbenv → roles/ruby}/files/vars +0 -0
- /data/{vendor/zzet.rbenv → roles/ruby}/handlers/main.yml +0 -0
- /data/{vendor/zzet.rbenv → roles/ruby}/templates/rbenv.sh.j2 +0 -0
- /data/{vendor/zzet.rbenv → roles/ruby}/vars/main.yml +0 -0
- /data/{vendor/zzet.rbenv → roles/ruby}/vars/system.yml +0 -0
@@ -0,0 +1,2240 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
#
|
3
|
+
###############################################################################
|
4
|
+
# duply (grown out of ftplicity), is a shell front end to duplicity that #
|
5
|
+
# simplifies the usage by managing settings for backup jobs in profiles. #
|
6
|
+
# It supports executing multiple commands in a batch mode to enable single #
|
7
|
+
# line cron entries and executes pre/post backup scripts. #
|
8
|
+
# Since version 1.5.0 all duplicity backends are supported. Hence the name #
|
9
|
+
# changed from ftplicity to duply. #
|
10
|
+
# See http://duply.net or http://ftplicity.sourceforge.net/ for more info. #
|
11
|
+
# (c) 2006 Christiane Ruetten, Heise Zeitschriften Verlag, Germany #
|
12
|
+
# (c) 2008-2014 Edgar Soldin (changes since version 1.3) #
|
13
|
+
###############################################################################
|
14
|
+
# LICENSE: #
|
15
|
+
# This program is licensed under GPLv2. #
|
16
|
+
# Please read the accompanying license information in gpl.txt. #
|
17
|
+
###############################################################################
|
18
|
+
# TODO/IDEAS/KNOWN PROBLEMS:
|
19
|
+
# - possibility to restore time frames (incl. deleted files)
|
20
|
+
# realizable by listing each backup and restore from
|
21
|
+
# oldest to the newest, problem: not performant
|
22
|
+
# - search file in all backups function and show available
|
23
|
+
# versions with backups date (list old avail since 0.6.06)
|
24
|
+
# - edit profile opens conf file in vi
|
25
|
+
# - implement log-fd interpretation
|
26
|
+
# - add a duplicity option check against the options pending
|
27
|
+
# deprecation since 0.5.10 namely --time-separator
|
28
|
+
# --short-filenames
|
29
|
+
# --old-filenames
|
30
|
+
# - add 'exclude_<command>' list usage eg. exclude_verify
|
31
|
+
# - featreq 25: a download/install duplicity option
|
32
|
+
# - hint on install software if a piece is missing
|
33
|
+
# - import/export profile from/to .tgz function !!!
|
34
|
+
#
|
35
|
+
#
|
36
|
+
# CHANGELOG:
|
37
|
+
# 1.9.1 (13.10.2014)
|
38
|
+
# - export CMD_ERR now for scripts to detect if CMD_PREV failed/succeeded
|
39
|
+
# - bugfix: CMD_PREV contained command even if it was skipped
|
40
|
+
#
|
41
|
+
# 1.9.0 (24.8.2014)
|
42
|
+
# - bugfix: env vars were not exported when external script was executable
|
43
|
+
# - rework GPG_KEY handling, allow virtually anything now (uid, keyid etc.)
|
44
|
+
# see gpg manpage, section "How to specify a user ID"
|
45
|
+
# let gpg complain when the delivered values are invalid for whatever reason
|
46
|
+
# - started to rework tmp space checking, exposed folder & writable check
|
47
|
+
# TODO: reimplement enough file space available checking
|
48
|
+
#
|
49
|
+
# 1.8.0 (13.7.2014)
|
50
|
+
# - add command verifyPath to expose 'verify --file-to-restore' action
|
51
|
+
# - add time parameter support to verify command
|
52
|
+
# - add section time formats to usage output
|
53
|
+
#
|
54
|
+
# 1.7.4 (24.6.2014)
|
55
|
+
# - remove ubuntu one support, service is discontinued
|
56
|
+
# - featreq 31: add authenticated swift (contributed by Justus Seifert)
|
57
|
+
#
|
58
|
+
# 1.7.3 (3.4.2014)
|
59
|
+
# - bugfix: test routines, gpg2 asked for passphrase although GPG_PW was set
|
60
|
+
#
|
61
|
+
# 1.7.2 (1.4.2014 "April,April")
|
62
|
+
# - bugfix: debian Bug#743190 "duply no longer allows restoration without
|
63
|
+
# gpg passphrase in conf file"
|
64
|
+
# GPG_AGENT_INFO env var is now needed to trigger --use-agent
|
65
|
+
# - bugfix: gpg keyenc test routines didn't work if GPG_PW was not set
|
66
|
+
#
|
67
|
+
# 1.7.1 (30.3.2014)
|
68
|
+
# - bugfix: purge-* commands renamed to purgeFull, purgeIncr due to
|
69
|
+
# incompatibility with new minus batch separator
|
70
|
+
#
|
71
|
+
# 1.7.0 (20.3.2014)
|
72
|
+
# - disabled gpg key id plausibility check, too many valid possibilities
|
73
|
+
# - featreq 7 "Halt if precondition fails":
|
74
|
+
# added and(+), or(-) batch command(separator) support
|
75
|
+
# - featreq 26 "pre/post script with shebang line":
|
76
|
+
# if a script is flagged executable it's executed in a subshell
|
77
|
+
# now as opposed to sourced to bash, which is the default
|
78
|
+
# - bugfix: do not check if dpbx, swift credentials are set anymore
|
79
|
+
# - bugfix: properly escape profile name, archdir if used as arguments
|
80
|
+
# - add DUPL_PRECMD conf setting for use with e.g. trickle
|
81
|
+
#
|
82
|
+
# 1.6.0 (1.1.2014)
|
83
|
+
# - support gs backend
|
84
|
+
# - support dropbox backend
|
85
|
+
# - add gpg-agent support to gpg test routines
|
86
|
+
# - autoenable --use-agent if passwords were not defined in config
|
87
|
+
# - GPG_OPTS are now honored everywhere, keyrings or complete gpg
|
88
|
+
# homedir can thus be configured to be located anywhere
|
89
|
+
# - always import both secret and public key if avail from config profile
|
90
|
+
# - new explanatory comments in initial exclude file
|
91
|
+
# - bugfix 7: Duply only imports one key at a time
|
92
|
+
#
|
93
|
+
# 1.5.11 (19.07.2013)
|
94
|
+
# - purge-incr command for remove-all-inc-of-but-n-full feature added
|
95
|
+
# patch provided by Moritz Augsburger, thanks!
|
96
|
+
# - documented version command in man page
|
97
|
+
#
|
98
|
+
# 1.5.10 (26.03.2013)
|
99
|
+
# - minor indent and documentation fixes
|
100
|
+
# - bugfix: exclude filter failed on ubuntu, mawk w/o posix char class support
|
101
|
+
# - bugfix: fix url_decoding generally and for python3
|
102
|
+
# - bugfix 3609075: wrong script results in status line (thx David Epping)
|
103
|
+
#
|
104
|
+
# 1.5.9 (22.11.2012)
|
105
|
+
# - bugfix 3588926: filter --exclude* params for restore/fetch ate too much
|
106
|
+
# - restore/fetch now also ignores --include* or --exclude='foobar'
|
107
|
+
#
|
108
|
+
# 1.5.8 (26.10.2012)
|
109
|
+
# - bugfix 3575487: implement proper cloud files support
|
110
|
+
#
|
111
|
+
# 1.5.7 (10.06.2012)
|
112
|
+
# - bugfix 3531450: Cannot use space in target URL (file:///) anymore
|
113
|
+
#
|
114
|
+
# 1.5.6 (24.5.2012)
|
115
|
+
# - commands purge, purge-full have no default value anymore for security
|
116
|
+
# reasons; instead max value can be given via cmd line or must be set
|
117
|
+
# in profile; else an error is shown.
|
118
|
+
# - minor man page modifications
|
119
|
+
#
|
120
|
+
# versioning scheme will be simplified to [major].[minor].[patch] version
|
121
|
+
# with the next version raise
|
122
|
+
#
|
123
|
+
# 1.5.5.5 (4.2.2012)
|
124
|
+
# - bugfix 3479605: SEL context confused profile folder's permission check
|
125
|
+
# - colon ':' in url passphrase got ignored, added python driven url_decoding
|
126
|
+
# for user & pass to better process special chars
|
127
|
+
#
|
128
|
+
# 1.5.5.4 (16.10.2011)
|
129
|
+
# - bugfix 3421268: SFTP passwords from conf ignored and always prompted for
|
130
|
+
# - add support for separate sign passphrase (needs duplicity 0.6.14+)
|
131
|
+
#
|
132
|
+
# 1.5.5.3 (1.10.2011)
|
133
|
+
# - bugfix 3416690: preview threw echo1 error
|
134
|
+
# - fix unknown cmds error usage & friends if more than 2 params were given
|
135
|
+
#
|
136
|
+
# 1.5.5.2 (23.9.2011)
|
137
|
+
# - bugfix 3409643: ssh key auth did ask for passphrase (--ssh-askpass ?)
|
138
|
+
# - bugfix: mawk does not support \W and did not split multikey definitions
|
139
|
+
# - all parameters should survive single (') and double (") quotes now
|
140
|
+
#
|
141
|
+
# 1.5.5.1 (7.6.2011)
|
142
|
+
# - featreq 3311881: add ftps as supported by duplicity 0.6.13 (thx mape2k)
|
143
|
+
# - bugfix 3312208: signing detection broke symmetric gpg test routine
|
144
|
+
#
|
145
|
+
# 1.5.5 (2.5.2011)
|
146
|
+
# - bugfix: fetch problem with space char in path, escape all params
|
147
|
+
# containing non word chars
|
148
|
+
# - list available profiles, if given profile cannot be found
|
149
|
+
# - added --use-agent configuration hint
|
150
|
+
# - bugfix 3174133: --exclude* params in conf DUPL_PARAMS broke
|
151
|
+
# fetch/restore
|
152
|
+
# - version command now prints out 'using installed' info
|
153
|
+
# - featreq 3166169: autotrust imported keys, based on code submitted by
|
154
|
+
# Martin Ellis - imported keys are now automagically trusted ultimately
|
155
|
+
# - new txt2man feature to create manpages for package maintainers
|
156
|
+
#
|
157
|
+
# 1.5.4.2 (6.1.2011)
|
158
|
+
# - new command changelog
|
159
|
+
# - bugfix 3109884: freebsd awk segfaulted on printf '%*', use print again
|
160
|
+
# - bugfix: freebsd awk hangs on 'awk -W version'
|
161
|
+
# - bugfix 3150244: mawk does not know '--version'
|
162
|
+
# - minor help text improvements
|
163
|
+
# - new env vars CMD_PREV,CMD_NEXT replacing CMD env var for scripts
|
164
|
+
#
|
165
|
+
# 1.5.4.1 (4.12.2010)
|
166
|
+
# - output awk, python, bash version now in prolog
|
167
|
+
# - shebang uses /usr/bin/env now for freebsd compatibility,
|
168
|
+
# bash not in /bin/bash
|
169
|
+
# - new --disable-encryption parameter,
|
170
|
+
# to override profile encr settings for one run
|
171
|
+
# - added exclude-if-present setting to conf template
|
172
|
+
# - bug 3126972: GPG_PW only needed for signing/symmetric encryption
|
173
|
+
# (even though duplicity still needs it)
|
174
|
+
#
|
175
|
+
# 1.5.4 (15.11.2010)
|
176
|
+
# - as of 1.5.3 already, new ARCH_DIR config option
|
177
|
+
# - multiple key support
|
178
|
+
# - ftplicity-Feature Requests-2994929: separate encryption and signing key
|
179
|
+
# - key signing of symmetric encryption possible (duplicity patch committed)
|
180
|
+
# - gpg tests disable switch
|
181
|
+
# - gpg tests now previewable and more intelligent
|
182
|
+
#
|
183
|
+
# 1.5.3 (1.11.2010)
|
184
|
+
# - bugfix 3056628: improve busybox compatibility, grep did not have -m param
|
185
|
+
# - bugfix 2995408: allow empty password for PGP key
|
186
|
+
# - bugfix 2996459: Duply erroneously escapes '-' symbol in username
|
187
|
+
# - url_encode function is now pythonized
|
188
|
+
# - rsync uses FTP_PASSWORD now if duplicity 0.6.10+ , else issue warning
|
189
|
+
# - feature 3059262: Make pre and post aware of parameters,
|
190
|
+
# internal parameters + CMD of pre or post
|
191
|
+
#
|
192
|
+
# 1.5.2.3 (16.4.2010)
|
193
|
+
# - bugfix: date again, should now work virtually anywhere
|
194
|
+
#
|
195
|
+
# 1.5.2.2 (3.4.2010)
|
196
|
+
# - minor bugfix: duplicity 0.6.8b version string now parsable
|
197
|
+
# - added INSTALL.txt
|
198
|
+
#
|
199
|
+
# 1.5.2.1 (23.3.2010)
|
200
|
+
# - bugfix: date formatting is awked now and should work on all platforms
|
201
|
+
#
|
202
|
+
# 1.5.2 (2.3.2010)
|
203
|
+
# - bugfix: errors print to STD_ERR now, failed tasks print an error message
|
204
|
+
# - added --name=duply_<profile> for duplicity 0.6.01+ to name cache folder
|
205
|
+
# - simplified & cleaned profileless commands, removed second instance
|
206
|
+
# - generalized separator time routines
|
207
|
+
# - added support for --no-encryption (GPG_KEY='disabled'), see conf examples
|
208
|
+
# - minor fixes
|
209
|
+
#
|
210
|
+
# 1.5.1.5 (5.2.2010)
|
211
|
+
# - bugfix: added special handling of credentials for rsync, imap(s)
|
212
|
+
#
|
213
|
+
# 1.5.1.4 (7.1.2010)
|
214
|
+
# - bugfix: nsecs defaults now to zeroes if date does not deliver [0-9]{9}
|
215
|
+
# - check if ncftp binary is available if url protocol is ftp
|
216
|
+
# - bugfix: duplicity output is now printed to screen directly to resolve
|
217
|
+
# 'mem alloc problem' bug report
|
218
|
+
# - bugfix: passwords will not be in the url anymore to solve the 'duply shows
|
219
|
+
# sensitive data in process listing' bug report
|
220
|
+
#
|
221
|
+
# 1.5.1.3 (24.12.2009) 'merry xmas'
|
222
|
+
# - bugfix: gpg pass now apostrophed to allow space and friends
|
223
|
+
# - bugfix: credentials are now url encoded to allow special chars in them
|
224
|
+
# a note about url encoding has been added to the conf template
|
225
|
+
#
|
226
|
+
# 1.5.1.2 (1.11.2009)
|
227
|
+
# - bugfix: open parenthesis in password broke duplicity execution
|
228
|
+
# - bugfix: ssh/scp backend does not always need credentials e.g. key auth
|
229
|
+
#
|
230
|
+
# 1.5.1.1 (21.09.2009)
|
231
|
+
# - bugfix: fixed s3[+http] TARGET_PASS not needed routine
|
232
|
+
# - bugfix: TYPO in duply 1.5.1 prohibited the use of /etc/duply
|
233
|
+
# see https://sourceforge.net/tracker/index.php?func=detail&
|
234
|
+
# aid=2864410&group_id=217745&atid=1041147
|
235
|
+
#
|
236
|
+
# 1.5.1 (21.09.2009) - duply (fka. ftplicity)
|
237
|
+
# - first things first: ftplicity (being able to support all backends since
|
238
|
+
# some time) will be called duply (fka. ftplicity) from now on. The addendum
|
239
|
+
# is for the time being to circumvent confusion.
|
240
|
+
# - bugfix: exit code is 1 (error) not 0 (success), if at least on duplicity
|
241
|
+
# command failed
|
242
|
+
# - s3[+http] now supported natively by translating user/pass to access_key/
|
243
|
+
# secret_key environment variables needed by duplicity s3 boto backend
|
244
|
+
# - bugfix: additional output lines do not confuse version check anymore
|
245
|
+
# - list command supports now age parameter (patch by stefan on feature
|
246
|
+
# request tracker)
|
247
|
+
# - bugfix: option/param pairs are now correctly passed on to duplicity
|
248
|
+
# - bugfix: s3[+http] needs no TARGET_PASS if command is read only
|
249
|
+
#
|
250
|
+
# 1.5.0.2 (31.07.1009)
|
251
|
+
# - bugfix: insert password in target url didn't work with debian mawk
|
252
|
+
# related to previous bug report
|
253
|
+
#
|
254
|
+
# 1.5.0.1 (23.07.2009)
|
255
|
+
# - bugfix: gawk gensub dependency raised an error on debian's default mawk
|
256
|
+
# replaced with match/substr command combination (bug report)
|
257
|
+
# https://sf.net/tracker/?func=detail&atid=1041147&aid=2825388&
|
258
|
+
# group_id=217745
|
259
|
+
#
|
260
|
+
# 1.5.0 (01.07.2009)
|
261
|
+
# - removed ftp limitation, all duplicity backends should work now
|
262
|
+
# - bugfix: date for separator failed on openwrt busybox date, added a
|
263
|
+
# detecting workaround, milliseconds are not available w/ busybox date
|
264
|
+
#
|
265
|
+
# 1.4.2.1 (14.05.2009)
|
266
|
+
# - bugfix: free temp space detection failed with lvm, fixed awk parse routine
|
267
|
+
#
|
268
|
+
# 1.4.2 (22.04.2009)
|
269
|
+
# - gpg keys are now exported as gpgkey.[id].asc , the suffix reflects the
|
270
|
+
# armored ascii nature, the id helps if the key is switched for some reason
|
271
|
+
# im/export routines are updated accordingly (import is backward compatible
|
272
|
+
# to the old profile/gpgkey files)
|
273
|
+
# - profile argument is treated as path if it contains slashes
|
274
|
+
# (for details see usage)
|
275
|
+
# - non-ftplicity options (all but --preview currently) are now passed
|
276
|
+
# on to duplicity
|
277
|
+
# - removed need for stat in secure_conf, it is ls based now
|
278
|
+
# - added profile folder readable check
|
279
|
+
# - added gpg version & home info output
|
280
|
+
# - awk utility availability is now checked, because it was mandatory already
|
281
|
+
# - tmp space is now checked on writability and space requirement
|
282
|
+
# test fails on less than 25MB or configured $VOLSIZE,
|
283
|
+
# test warns if there is less than two times $VOLSIZE because
|
284
|
+
# that's required for --asynchronous-upload option
|
285
|
+
# - gpg functionality is tested now before executing duplicity
|
286
|
+
# test drive contains encryption, decryption, comparison, cleanup
|
287
|
+
# this is meant to detect non trusted or other gpg errors early
|
288
|
+
# - added possibility of doing symmetric encryption with duplicity
|
289
|
+
# set GPG_KEY="" or simply comment it out
|
290
|
+
# - added hints in config template on the depreciation of
|
291
|
+
# --short-filenames, --time-separator duplicity options
|
292
|
+
#
|
293
|
+
# new versioning scheme 1.4.2b => 1.4.2,
|
294
|
+
# beta b's are replaced by a patch count number e.g. 1.4.2.1 will be assigned
|
295
|
+
# to the first bug fixing version and 1.4.2.2 to the second and so on
|
296
|
+
# also the releases will now have a release date formatted (Day.Month.Year)
|
297
|
+
#
|
298
|
+
# 1.4.1b1 - bugfix: ftplicity changed filesystem permission of a folder
|
299
|
+
# named exactly as the profile if existing in executing dir
|
300
|
+
# - improved plausibility checking of config and profile folder
|
301
|
+
# - secure_conf only acts if needed and prints a warning now
|
302
|
+
#
|
303
|
+
# 1.4.1b - introduce status (duplicity collection-status) command
|
304
|
+
# - pre/post script output printed always now, not only on errors
|
305
|
+
# - new config parameter GPG_OPTS to pass gpg options
|
306
|
+
# added examples & comments to profile template conf
|
307
|
+
# - reworked separator times, added duration display
|
308
|
+
# - added --preview switch, to preview generated command lines
|
309
|
+
# - disabled MAX_AGE, MAX_FULL_BACKUPS, VERBOSITY in generated
|
310
|
+
# profiles because they have reasonable defaults now if not set
|
311
|
+
#
|
312
|
+
# 1.4.0b1 - bugfix: incr forces incremental backups on duplicity,
|
313
|
+
# therefore backup translates to pre_bkp_post now
|
314
|
+
# - bugfix: new command bkp, which represents duplicity's
|
315
|
+
# default action (incr or full if full_if_older matches
|
316
|
+
# or no earlier backup chain is found)
|
317
|
+
#
|
318
|
+
# new versioning scheme 1.4 => 1.4.0, added new minor revision number
|
319
|
+
# this is meant to slow down the rapid version growing but still keep
|
320
|
+
# versions cleanly separated.
|
321
|
+
# only additional features will raise the new minor revision number.
|
322
|
+
# all releases start as beta, each bugfix release will raise the beta
|
323
|
+
# count, usually new features arrive before a version 'ripes' to stable
|
324
|
+
#
|
325
|
+
# 1.4.0b
|
326
|
+
# 1.4b - added startup info on version, time, selected profile
|
327
|
+
# - added time output to separation lines
|
328
|
+
# - introduced: command purge-full implements duplicity's
|
329
|
+
# remove-all-but-n-full functionality (patch by unknown),
|
330
|
+
# uses config variable $MAX_FULL_BACKUPS (default = 1)
|
331
|
+
# - purge config var $MAX_AGE defaults to 1M (month) now
|
332
|
+
# - command full does not execute pre/post anymore
|
333
|
+
# use batch command pre_full_post if needed
|
334
|
+
# - introduced batch mode cmd1_cmd2_etc
|
335
|
+
# (in turn removed the bvp command)
|
336
|
+
# - unknown/undefined command issues a warning/error now
|
337
|
+
# - bugfix: version check works with 0.4.2 and older now
|
338
|
+
# 1.3b3 - introduced pre/post commands to execute/debug scripts
|
339
|
+
# - introduced bvp (backup, verify, purge)
|
340
|
+
# - bugfix: removed need for awk gensub, now mawk compatible
|
341
|
+
# 1.3b2 - removed pre/post need executable bit set
|
342
|
+
# - profiles now under ~/.ftplicity as folders
|
343
|
+
# - root can keep profiles in /etc/ftplicity, folder must be
|
344
|
+
# created by hand, existing profiles must be moved there
|
345
|
+
# - removed ftplicity in path requirement
|
346
|
+
# - bugfix: bash < v.3 did not know '=~'
|
347
|
+
# - bugfix: purge works again
|
348
|
+
# 1.3 - introduces multiple profiles support
|
349
|
+
# - modified some script errors/docs
|
350
|
+
# - reordered gpg key check import routine
|
351
|
+
# - added 'gpg key id not set' check
|
352
|
+
# - added error_gpg (adds how to setup gpg key howto)
|
353
|
+
# - bugfix: duplicity 0.4.4RC4+ parameter syntax changed
|
354
|
+
# - duplicity_version_check routine introduced
|
355
|
+
# - added time separator, shortnames, volsize, full_if_older
|
356
|
+
# duplicity options to config file (inspired by stevie
|
357
|
+
# from http://weareroot.de)
|
358
|
+
# 1.1.1 - bugfix: encryption reactivated
|
359
|
+
# 1.1 - introduced config directory
|
360
|
+
# 1.0 - first release
|
361
|
+
###############################################################################
|
362
|
+
|
363
|
+
|
364
|
+
# important definitions #######################################################
|
365
|
+
|
366
|
+
ME_LONG="$0"
|
367
|
+
ME="$(basename $0)"
|
368
|
+
ME_NAME="${ME%%.*}"
|
369
|
+
ME_VERSION="1.9.1"
|
370
|
+
ME_WEBSITE="http://duply.net"
|
371
|
+
|
372
|
+
# default config values
|
373
|
+
DEFAULT_SOURCE='/path/of/source'
|
374
|
+
DEFAULT_TARGET='scheme://user[:password]@host[:port]/[/]path'
|
375
|
+
DEFAULT_TARGET_USER='_backend_username_'
|
376
|
+
DEFAULT_TARGET_PASS='_backend_password_'
|
377
|
+
DEFAULT_GPG_KEY='_KEY_ID_'
|
378
|
+
DEFAULT_GPG_PW='_GPG_PASSWORD_'
|
379
|
+
|
380
|
+
# function definitions ##########################
|
381
|
+
function set_config { # sets config vars
|
382
|
+
local CONFHOME_COMPAT="$HOME/.ftplicity"
|
383
|
+
local CONFHOME_ETC_COMPAT="/etc/ftplicity"
|
384
|
+
local CONFHOME_ETC="{{backup_home}}"
|
385
|
+
local CONFHOME="{{backup_home}}"
|
386
|
+
|
387
|
+
# confdir can be delivered as path (must contain /)
|
388
|
+
if [ `echo $FTPLCFG | grep /` ] ; then
|
389
|
+
CONFDIR=$(readlink -f $FTPLCFG 2>/dev/null || \
|
390
|
+
( echo $FTPLCFG|grep -v '^/' 1>/dev/null 2>&1 \
|
391
|
+
&& echo $(pwd)/${FTPLCFG} ) || \
|
392
|
+
echo ${FTPLCFG})
|
393
|
+
# or DEFAULT in home/.duply folder (NEW)
|
394
|
+
elif [ -d "${CONFHOME}" ]; then
|
395
|
+
CONFDIR="${CONFHOME}/${FTPLCFG}"
|
396
|
+
# or in home/.ftplicity folder (OLD)
|
397
|
+
elif [ -d "${CONFHOME_COMPAT}" ]; then
|
398
|
+
CONFDIR="${CONFHOME_COMPAT}/${FTPLCFG}"
|
399
|
+
warning_oldhome "${CONFHOME_COMPAT}" "${CONFHOME}"
|
400
|
+
# root can put profiles under /etc/duply (NEW) if path exists
|
401
|
+
elif [ -d "${CONFHOME_ETC}" ] && [ "$EUID" -eq 0 ]; then
|
402
|
+
CONFDIR="${CONFHOME_ETC}/${FTPLCFG}"
|
403
|
+
# root can keep profiles under /etc/ftplicity (OLD) if path exists
|
404
|
+
elif [ -d "${CONFHOME_ETC_COMPAT}" ] && [ "$EUID" -eq 0 ]; then
|
405
|
+
CONFDIR="${CONFHOME_ETC_COMPAT}/${FTPLCFG}"
|
406
|
+
warning_oldhome "${CONFHOME_ETC_COMPAT}" "${CONFHOME_ETC}"
|
407
|
+
# hmm no profile folder there, then use default for error later
|
408
|
+
else
|
409
|
+
CONFDIR="${CONFHOME}/${FTPLCFG}" # continue, will fail later in main
|
410
|
+
fi
|
411
|
+
|
412
|
+
# remove trailing slash, get profile name etc.
|
413
|
+
CONFDIR="${CONFDIR%/}"
|
414
|
+
NAME="${CONFDIR##*/}"
|
415
|
+
CONF="$CONFDIR/conf"
|
416
|
+
PRE="$CONFDIR/pre"
|
417
|
+
POST="$CONFDIR/post"
|
418
|
+
EXCLUDE="$CONFDIR/exclude"
|
419
|
+
KEYFILE="$CONFDIR/gpgkey.asc"
|
420
|
+
|
421
|
+
}
|
422
|
+
|
423
|
+
{% raw %}
|
424
|
+
|
425
|
+
function version_info { # print version information
|
426
|
+
cat <<END
|
427
|
+
$ME version $ME_VERSION
|
428
|
+
($ME_WEBSITE)
|
429
|
+
END
|
430
|
+
}
|
431
|
+
|
432
|
+
function version_info_using {
|
433
|
+
cat <<END
|
434
|
+
$(version_info)
|
435
|
+
|
436
|
+
$(using_info)
|
437
|
+
END
|
438
|
+
}
|
439
|
+
|
440
|
+
function using_info {
|
441
|
+
duplicity_version_get
|
442
|
+
# freebsd awk (--version only), debian mawk (-W version only), deliver '' so awk does not wait for input
|
443
|
+
AWK_VERSION=$((awk --version '' 2>/dev/null || awk -W version '' 2>/dev/null) | awk '/.+/{sub(/^[Aa][Ww][Kk][ \t]*/,"",$0);print $0;exit}')
|
444
|
+
PYTHON_VERSION=$(python -V 2>&1| awk '{print tolower($0);exit}')
|
445
|
+
GPG_INFO=`gpg --version 2>/dev/null| awk '/^gpg/{v=$1" "$3};/^Home/{print v" ("$0")"}'`
|
446
|
+
BASH_VERSION=$(bash --version | awk '/^GNU bash, version/{sub(/GNU bash, version[ ]+/,"",$0);print $0}')
|
447
|
+
echo -e "Using installed duplicity version ${DUPL_VERSION:-(not found)}${PYTHON_VERSION+, $PYTHON_VERSION}\
|
448
|
+
${GPG_INFO:+, $GPG_INFO}${AWK_VERSION:+, awk '${AWK_VERSION}'}${BASH_VERSION:+, bash '${BASH_VERSION}'}."
|
449
|
+
}
|
450
|
+
|
451
|
+
function usage_info { # print usage information
|
452
|
+
|
453
|
+
cat <<USAGE_EOF
|
454
|
+
VERSION:
|
455
|
+
$(version_info)
|
456
|
+
|
457
|
+
DESCRIPTION:
|
458
|
+
Duply deals as a wrapper for the mighty duplicity magic.
|
459
|
+
It simplifies running duplicity with cron or on command line by:
|
460
|
+
|
461
|
+
- keeping recurring settings in profiles per backup job
|
462
|
+
- enabling batch operations eg. backup_verify_purge
|
463
|
+
- executing pre/post scripts for every command
|
464
|
+
- precondition checking for flawless duplicity operation
|
465
|
+
|
466
|
+
For each backup job one configuration profile must be created.
|
467
|
+
The profile folder will be stored under '~/.${ME_NAME}/<profile>'
|
468
|
+
(where ~ is the current users home directory).
|
469
|
+
Hint:
|
470
|
+
If the folder '/etc/${ME_NAME}' exists, the profiles for the super
|
471
|
+
user root will be searched & created there.
|
472
|
+
|
473
|
+
USAGE:
|
474
|
+
first time usage (profile creation):
|
475
|
+
$ME <profile> create
|
476
|
+
|
477
|
+
general usage in single or batch mode (see EXAMPLES):
|
478
|
+
$ME <profile> <command>[[_|+|-]<command>[_|+|-]...] [<options> ...]
|
479
|
+
|
480
|
+
For batches the conditional separators can also be written as pseudo commands
|
481
|
+
and(+), or(-). See SEPARATORS for details.
|
482
|
+
|
483
|
+
Non $ME options are passed on to duplicity (see OPTIONS).
|
484
|
+
All conf parameters can also be defined in the environment instead.
|
485
|
+
|
486
|
+
PROFILE:
|
487
|
+
Indicated by a path or a profile name (<profile>), which is resolved
|
488
|
+
to '~/.${ME_NAME}/<profile>' (~ expands to environment variable \$HOME).
|
489
|
+
|
490
|
+
Superuser root can place profiles under '/etc/${ME_NAME}'. Simply create
|
491
|
+
the folder manually before running $ME as superuser.
|
492
|
+
Note:
|
493
|
+
Already existing profiles in root's profile folder will cease to work
|
494
|
+
unless there are moved to the new location manually.
|
495
|
+
|
496
|
+
example 1: $ME humbug backup
|
497
|
+
|
498
|
+
Alternatively a _path_ might be used e.g. useful for quick testing,
|
499
|
+
restoring or exotic locations. Shell expansion should work as usual.
|
500
|
+
Hint:
|
501
|
+
The path must contain at least one path separator '/',
|
502
|
+
e.g. './test' instead of only 'test'.
|
503
|
+
|
504
|
+
example 2: $ME ~/.${ME_NAME}/humbug backup
|
505
|
+
|
506
|
+
SEPARATORS:
|
507
|
+
_ (underscore)
|
508
|
+
neutral separator
|
509
|
+
+ (plus sign), _and_
|
510
|
+
conditional AND
|
511
|
+
the next command will only be executed if the previous succeeded
|
512
|
+
- (minus sign), _or_
|
513
|
+
conditional OR
|
514
|
+
the next command will only be executed if the previous failed
|
515
|
+
|
516
|
+
example:
|
517
|
+
'pre+bkp-verify_post' translates to 'pre_and_bkp_or_verify_post'
|
518
|
+
|
519
|
+
COMMANDS:
|
520
|
+
usage get usage help text
|
521
|
+
|
522
|
+
and/or pseudo commands for better batch cmd readability (see SEPARATORS)
|
523
|
+
create creates a configuration profile
|
524
|
+
backup backup with pre/post script execution (batch: pre_bkp_post),
|
525
|
+
full (if full_if_older matches or no earlier backup is found)
|
526
|
+
incremental (in all other cases)
|
527
|
+
pre/post execute '<profile>/$(basename "$PRE")', '<profile>/$(basename "$POST")' scripts
|
528
|
+
bkp as above but without executing pre/post scripts
|
529
|
+
full force full backup
|
530
|
+
incr force incremental backup
|
531
|
+
list [<age>]
|
532
|
+
list all files in backup (as it was at <age>, default: now)
|
533
|
+
status prints backup sets and chains currently in repository
|
534
|
+
verify [<age>] [--compare-data]
|
535
|
+
list files changed, since age if given
|
536
|
+
verifyPath <rel_path_in_bkp> <local_path> [<age>] [--compare-data]
|
537
|
+
list changes of a file or folder path in backup compared to a
|
538
|
+
local path, since age if given
|
539
|
+
restore <target_path> [<age>]
|
540
|
+
restore the complete backup to <target_path> [as it was at <age>]
|
541
|
+
fetch <src_path> <target_path> [<age>]
|
542
|
+
fetch single file/folder from backup [as it was at <age>]
|
543
|
+
purge [<max_age>] [--force]
|
544
|
+
list outdated backup files (older than \$MAX_AGE)
|
545
|
+
[use --force to actually delete these files]
|
546
|
+
purgeFull [<max_full_backups>] [--force]
|
547
|
+
list outdated backup files (\$MAX_FULL_BACKUPS being the number of
|
548
|
+
full backups and associated incrementals to keep, counting in
|
549
|
+
reverse chronological order)
|
550
|
+
[use --force to actually delete these files]
|
551
|
+
purgeIncr [<max_fulls_with_incrs>] [--force]
|
552
|
+
list outdated incremental backups (\$MAX_FULLS_WITH_INCRS being
|
553
|
+
the number of full backups which associated incrementals will be
|
554
|
+
kept, counting in reverse chronological order)
|
555
|
+
[use --force to actually delete these files]
|
556
|
+
cleanup [--force]
|
557
|
+
list broken backup chain files archives (e.g. after unfinished run)
|
558
|
+
[use --force to actually delete these files]
|
559
|
+
|
560
|
+
changelog print changelog / todo list
|
561
|
+
txt2man feature for package maintainers - create a manpage based on the
|
562
|
+
usage output. download txt2man from http://mvertes.free.fr/, put
|
563
|
+
it in the PATH and run '$ME txt2man' to create a man page.
|
564
|
+
version show version information of $ME and needed programs
|
565
|
+
|
566
|
+
OPTIONS:
|
567
|
+
--force passed to duplicity (see commands: purge, purge-full, cleanup)
|
568
|
+
--preview do nothing but print out generated duplicity command lines
|
569
|
+
--disable-encryption
|
570
|
+
disable encryption, overrides profile settings
|
571
|
+
|
572
|
+
TIME FORMATS:
|
573
|
+
For all time related parameters like age, max_age etc.
|
574
|
+
Refer to the duplicity manpage for all available formats. Here some examples:
|
575
|
+
2002-01-25T07:00:00+02:00 (full date time format string)
|
576
|
+
2002/3/5 (date string YYYY/MM/DD)
|
577
|
+
12D (interval, 12 days ago)
|
578
|
+
1h78m (interval, 1 hour 78 minutes ago)
|
579
|
+
|
580
|
+
PRE/POST SCRIPTS:
|
581
|
+
Useful internal duply variables will be readable in the scripts.
|
582
|
+
Some of interest may be
|
583
|
+
|
584
|
+
CONFDIR, SOURCE, TARGET_URL_<PROT|HOSTPATH|USER|PASS>,
|
585
|
+
GPG_<KEYS_ENC|KEY_SIGN|PW>, CMD_<PREV|NEXT>, CMD_ERR
|
586
|
+
|
587
|
+
The CMD_* variables were introduced to allow different actions according to
|
588
|
+
the command the scripts were attached to e.g. 'pre_bkp_post_pre_verify_post'
|
589
|
+
will call the pre script two times, with CMD_NEXT variable set to 'bkp'
|
590
|
+
on the first and to 'verify' on the second run.
|
591
|
+
CMD_ERR holds the exit code of the CMD_PREV .
|
592
|
+
|
593
|
+
EXAMPLES:
|
594
|
+
create profile 'humbug':
|
595
|
+
$ME humbug create (now edit the resulting conf file)
|
596
|
+
backup 'humbug' now:
|
597
|
+
$ME humbug backup
|
598
|
+
list available backup sets of profile 'humbug':
|
599
|
+
$ME humbug status
|
600
|
+
list and delete obsolete backup archives of 'humbug':
|
601
|
+
$ME humbug purge --force
|
602
|
+
restore latest backup of 'humbug' to /mnt/restore:
|
603
|
+
$ME humbug restore /mnt/restore
|
604
|
+
restore /etc/passwd of 'humbug' from 4 days ago to /root/pw:
|
605
|
+
$ME humbug fetch etc/passwd /root/pw 4D
|
606
|
+
(see "duplicity manpage", section TIME FORMATS)
|
607
|
+
a one line batch job on 'humbug' for cron execution:
|
608
|
+
$ME humbug backup_verify_purge --force
|
609
|
+
|
610
|
+
FILES:
|
611
|
+
in profile folder '~/.${ME_NAME}/<profile>' or '/etc/${ME_NAME}'
|
612
|
+
conf profile configuration file
|
613
|
+
pre,post pre/post scripts (see above for details)
|
614
|
+
gpgkey.*.asc exported GPG key files
|
615
|
+
exclude a globbing list of included or excluded files/folders
|
616
|
+
(see "duplicity manpage", section FILE SELECTION)
|
617
|
+
|
618
|
+
$(hint_profile)
|
619
|
+
|
620
|
+
SEE ALSO:
|
621
|
+
duplicity man page:
|
622
|
+
duplicity(1) or http://duplicity.nongnu.org/duplicity.1.html
|
623
|
+
USAGE_EOF
|
624
|
+
}
|
625
|
+
|
626
|
+
# to check call 'duply txt2man | man -l -'
|
627
|
+
function usage_txt2man {
|
628
|
+
usage_info | \
|
629
|
+
awk '/^^[^[:lower:][:space:]][^[:lower:]]+$/{gsub(/[^[:upper:]]/," ",$0)}{print}' |\
|
630
|
+
txt2man -t"$(toupper "${ME_NAME}")" -s1 -r"${ME_NAME}-${ME_VERSION}" -v'User Manuals'
|
631
|
+
}
|
632
|
+
|
633
|
+
function changelog {
|
634
|
+
cat $ME_LONG | awk '/^#####/{on=on+1}(on==3){sub(/^#( )?/,"",$0);print}'
|
635
|
+
}
|
636
|
+
|
637
|
+
function create_config {
|
638
|
+
if [ ! -d "$CONFDIR" ] ; then
|
639
|
+
mkdir -p "$CONFDIR" || error "Couldn't create config '$CONFDIR'."
|
640
|
+
# create initial config file
|
641
|
+
cat <<EOF >"$CONF"
|
642
|
+
# gpg encryption settings, simple settings:
|
643
|
+
# GPG_KEY='disabled' - disables encryption alltogether
|
644
|
+
# GPG_KEY='<key1>[,<key2>]'; GPG_PW='pass' - encrypt with keys,
|
645
|
+
# sign if secret key of key1 is available use GPG_PW for sign & decrypt
|
646
|
+
# Note: you can specify keys via all methods described in gpg manpage,
|
647
|
+
# section "How to specify a user ID", escape commas (,) via backslash (\)
|
648
|
+
# e.g. 'Mueller, Horst', 'Bernd' -> 'Mueller\, Horst, Bernd'
|
649
|
+
# as they are used to separate the entries
|
650
|
+
# GPG_PW='passphrase' - symmetric encryption using passphrase only
|
651
|
+
GPG_KEY='${DEFAULT_GPG_KEY}'
|
652
|
+
GPG_PW='${DEFAULT_GPG_PW}'
|
653
|
+
# gpg encryption settings in detail (extended settings)
|
654
|
+
# the above settings translate to the following more specific settings
|
655
|
+
# GPG_KEYS_ENC='<keyid1>[,<keyid2>,...]' - list of pubkeys to encrypt to
|
656
|
+
# GPG_KEY_SIGN='<keyid1>|disabled' - a secret key for signing
|
657
|
+
# GPG_PW='<passphrase>' - needed for signing, decryption and symmetric
|
658
|
+
# encryption. If you want to deliver different passphrases for e.g.
|
659
|
+
# several keys or symmetric encryption plus key signing you can use
|
660
|
+
# gpg-agent. Simply make sure that GPG_AGENT_INFO is set in environment.
|
661
|
+
# also see "A NOTE ON SYMMETRIC ENCRYPTION AND SIGNING" in duplicity manpage
|
662
|
+
# notes on en/decryption
|
663
|
+
# private key and passphrase will only be needed for decryption or signing.
|
664
|
+
# decryption happens on restore and incrementals (compare archdir contents).
|
665
|
+
# for security reasons it makes sense to separate the signing key from the
|
666
|
+
# encryption keys. https://answers.launchpad.net/duplicity/+question/107216
|
667
|
+
#GPG_KEYS_ENC='<pubkey1>,<pubkey2>,...'
|
668
|
+
#GPG_KEY_SIGN='<prvkey>'
|
669
|
+
# set if signing key passphrase differs from encryption (key) passphrase
|
670
|
+
# NOTE: available since duplicity 0.6.14, translates to SIGN_PASSPHRASE
|
671
|
+
#GPG_PW_SIGN='<signpass>'
|
672
|
+
|
673
|
+
# gpg options passed from duplicity to gpg process (default='')
|
674
|
+
# e.g. "--trust-model pgp|classic|direct|always"
|
675
|
+
# or "--compress-algo=bzip2 --bzip2-compress-level=9"
|
676
|
+
# or "--personal-cipher-preferences AES256,AES192,AES..."
|
677
|
+
# or "--homedir ~/.duply" - keep keyring and gpg settings duply specific
|
678
|
+
#GPG_OPTS=''
|
679
|
+
|
680
|
+
# disable preliminary tests with the following setting
|
681
|
+
#GPG_TEST='disabled'
|
682
|
+
|
683
|
+
# credentials & server address of the backup target (URL-Format)
|
684
|
+
# syntax is
|
685
|
+
# scheme://[user:password@]host[:port]/[/]path
|
686
|
+
# for details see duplicity manpage, section URL Format
|
687
|
+
# http://duplicity.nongnu.org/duplicity.1.html#sect8
|
688
|
+
# probably one out of
|
689
|
+
# # for cloudfiles backend user id is CLOUDFILES_USERNAME, password is
|
690
|
+
# # CLOUDFILES_APIKEY, you might need to set CLOUDFILES_AUTHURL manually
|
691
|
+
# cf+http://[user:password@]container_name
|
692
|
+
# dpbx:///some_dir
|
693
|
+
# file://[relative|/absolute]/local/path
|
694
|
+
# ftp[s]://user[:password]@other.host[:port]/some_dir
|
695
|
+
# gdocs://user[:password]@other.host/some_dir
|
696
|
+
# # for the google cloud storage (since duplicity 0.6.22)
|
697
|
+
# # user/password are GS_ACCESS_KEY_ID/GS_SECRET_ACCESS_KEY
|
698
|
+
# gs://bucket[/prefix]
|
699
|
+
# hsi://user[:password]@other.host/some_dir
|
700
|
+
# imap[s]://user[:password]@host.com[/from_address_prefix]
|
701
|
+
# mega://user[:password]@mega.co.nz/some_dir
|
702
|
+
# rsync://user[:password]@host.com[:port]::[/]module/some_dir
|
703
|
+
# # rsync over ssh (only keyauth)
|
704
|
+
# rsync://user@host.com[:port]/[relative|/absolute]_path
|
705
|
+
# # for the s3 user/password are AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY
|
706
|
+
# s3://[user:password@]host/bucket_name[/prefix]
|
707
|
+
# s3+http://[user:password@]bucket_name[/prefix]
|
708
|
+
# # scp and sftp are aliases for the ssh backend
|
709
|
+
# ssh://user[:password]@other.host[:port]/[/]some_dir
|
710
|
+
# # for authenticated swift define TARGET_USER or SWIFT_USERNAME,
|
711
|
+
# # TARGET_PASS or SWIFT_PASSWORD, SWIFT_AUTHURL (mandatory, the path to
|
712
|
+
# # your identity service, omitting leads to an error with swift),
|
713
|
+
# # optionally SWIFT_AUTHVERSION (which defaults to "1")
|
714
|
+
# swift://container_name
|
715
|
+
# tahoe://alias/directory
|
716
|
+
# webdav[s]://user[:password]@other.host/some_dir
|
717
|
+
# ATTENTION: characters other than A-Za-z0-9.-_.~ in the URL have
|
718
|
+
# to be replaced by their url encoded pendants, see
|
719
|
+
# http://en.wikipedia.org/wiki/Url_encoding
|
720
|
+
# if you define the credentials as TARGET_USER, TARGET_PASS below
|
721
|
+
# duply will try to url_encode them for you if the need arises
|
722
|
+
TARGET='${DEFAULT_TARGET}'
|
723
|
+
# optionally the username/password can be defined as extra variables
|
724
|
+
# setting them here _and_ in TARGET results in an error
|
725
|
+
#TARGET_USER='${DEFAULT_TARGET_USER}'
|
726
|
+
#TARGET_PASS='${DEFAULT_TARGET_PASS}'
|
727
|
+
|
728
|
+
# base directory to backup
|
729
|
+
SOURCE='${DEFAULT_SOURCE}'
|
730
|
+
|
731
|
+
# a command that runs duplicity e.g.
|
732
|
+
# shape bandwidth use via trickle
|
733
|
+
# "trickle -s -u 640 -d 5120" # 5Mb up, 40Mb down"
|
734
|
+
#DUPL_PRECMD=""
|
735
|
+
|
736
|
+
# exclude folders containing exclusion file (since duplicity 0.5.14)
|
737
|
+
# Uncomment the following two lines to enable this setting.
|
738
|
+
#FILENAME='.duplicity-ignore'
|
739
|
+
#DUPL_PARAMS="\$DUPL_PARAMS --exclude-if-present '\$FILENAME'"
|
740
|
+
|
741
|
+
# Time frame for old backups to keep, Used for the "purge" command.
|
742
|
+
# see duplicity man page, chapter TIME_FORMATS)
|
743
|
+
#MAX_AGE=1M
|
744
|
+
|
745
|
+
# Number of full backups to keep. Used for the "purge-full" command.
|
746
|
+
# See duplicity man page, action "remove-all-but-n-full".
|
747
|
+
#MAX_FULL_BACKUPS=1
|
748
|
+
|
749
|
+
# Number of full backups for which incrementals will be kept for.
|
750
|
+
# Used for the "purge-incr" command.
|
751
|
+
# See duplicity man page, action "remove-all-inc-of-but-n-full".
|
752
|
+
#MAX_FULLS_WITH_INCRS=1
|
753
|
+
|
754
|
+
# activates duplicity --full-if-older-than option (since duplicity v0.4.4.RC3)
|
755
|
+
# forces a full backup if last full backup reaches a specified age, for the
|
756
|
+
# format of MAX_FULLBKP_AGE see duplicity man page, chapter TIME_FORMATS
|
757
|
+
# Uncomment the following two lines to enable this setting.
|
758
|
+
#MAX_FULLBKP_AGE=1M
|
759
|
+
#DUPL_PARAMS="\$DUPL_PARAMS --full-if-older-than \$MAX_FULLBKP_AGE "
|
760
|
+
|
761
|
+
# sets duplicity --volsize option (available since v0.4.3.RC7)
|
762
|
+
# set the size of backup chunks to VOLSIZE MB instead of the default 25MB.
|
763
|
+
# VOLSIZE must be number of MB's to set the volume size to.
|
764
|
+
# Uncomment the following two lines to enable this setting.
|
765
|
+
#VOLSIZE=50
|
766
|
+
#DUPL_PARAMS="\$DUPL_PARAMS --volsize \$VOLSIZE "
|
767
|
+
|
768
|
+
# verbosity of output (error 0, warning 1-2, notice 3-4, info 5-8, debug 9)
|
769
|
+
# default is 4, if not set
|
770
|
+
#VERBOSITY=5
|
771
|
+
|
772
|
+
# temporary file space. at least the size of the biggest file in backup
|
773
|
+
# for a successful restoration process. (default is '/tmp', if not set)
|
774
|
+
#TEMP_DIR=/tmp
|
775
|
+
|
776
|
+
# Modifies archive-dir option (since 0.6.0) Defines a folder that holds
|
777
|
+
# unencrypted meta data of the backup, enabling new incrementals without the
|
778
|
+
# need to decrypt backend metadata first. If empty or deleted somehow, the
|
779
|
+
# private key and it's password are needed.
|
780
|
+
# NOTE: This is confidential data. Put it somewhere safe. It can grow quite
|
781
|
+
# big over time so you might want to put it not in the home dir.
|
782
|
+
# default '~/.cache/duplicity/duply_<profile>/'
|
783
|
+
# if set '\${ARCH_DIR}/<profile>'
|
784
|
+
#ARCH_DIR=/some/space/safe/.duply-cache
|
785
|
+
|
786
|
+
# DEPRECATED setting
|
787
|
+
# sets duplicity --time-separator option (since v0.4.4.RC2) to allow users
|
788
|
+
# to change the time separator from ':' to another character that will work
|
789
|
+
# on their system. HINT: For Windows SMB shares, use --time-separator='_'.
|
790
|
+
# NOTE: '-' is not valid as it conflicts with date separator.
|
791
|
+
# ATTENTION: only use this with duplicity < 0.5.10, since then default file
|
792
|
+
# naming is compatible and this option is pending depreciation
|
793
|
+
#DUPL_PARAMS="\$DUPL_PARAMS --time-separator _ "
|
794
|
+
|
795
|
+
# DEPRECATED setting
|
796
|
+
# activates duplicity --short-filenames option, when uploading to a file
|
797
|
+
# system that can't have filenames longer than 30 characters (e.g. Mac OS 8)
|
798
|
+
# or have problems with ':' as part of the filename (e.g. Microsoft Windows)
|
799
|
+
# ATTENTION: only use this with duplicity < 0.5.10, later versions default file
|
800
|
+
# naming is compatible and this option is pending depreciation
|
801
|
+
#DUPL_PARAMS="\$DUPL_PARAMS --short-filenames "
|
802
|
+
|
803
|
+
# more duplicity command line options can be added in the following way
|
804
|
+
# don't forget to leave a separating space char at the end
|
805
|
+
#DUPL_PARAMS="\$DUPL_PARAMS --put_your_options_here "
|
806
|
+
|
807
|
+
EOF
|
808
|
+
|
809
|
+
# create initial exclude file
|
810
|
+
cat <<EOF >"$EXCLUDE"
|
811
|
+
# although called exclude, this file is actually a globbing file list
|
812
|
+
# duplicity accepts some globbing patterns, even including ones here
|
813
|
+
# here is an example, this incl. only 'dir/bar' except it's subfolder 'foo'
|
814
|
+
# - dir/bar/foo
|
815
|
+
# + dir/bar
|
816
|
+
# - **
|
817
|
+
# for more details see duplicity manpage, section File Selection
|
818
|
+
# http://duplicity.nongnu.org/duplicity.1.html#sect9
|
819
|
+
|
820
|
+
EOF
|
821
|
+
|
822
|
+
# Hints on first usage
|
823
|
+
cat <<EOF
|
824
|
+
|
825
|
+
Congratulations. You just created the profile '$FTPLCFG'.
|
826
|
+
The initial config file has been created as
|
827
|
+
'$CONF'.
|
828
|
+
You should now adjust this config file to your needs.
|
829
|
+
|
830
|
+
$(hint_profile)
|
831
|
+
|
832
|
+
EOF
|
833
|
+
fi
|
834
|
+
|
835
|
+
}
|
836
|
+
|
837
|
+
# used in usage AND create_config
|
838
|
+
function hint_profile {
|
839
|
+
cat <<EOF
|
840
|
+
IMPORTANT:
|
841
|
+
Copy the _whole_ profile folder after the first backup to a safe place.
|
842
|
+
It contains everything needed to restore your backups. You will need
|
843
|
+
it if you have to restore the backup from another system (e.g. after a
|
844
|
+
system crash). Keep access to these files restricted as they contain
|
845
|
+
_all_ informations (gpg data, ftp data) to access and modify your backups.
|
846
|
+
|
847
|
+
Repeat this step after _all_ configuration changes. Some configuration
|
848
|
+
options are crucial for restoration.
|
849
|
+
|
850
|
+
EOF
|
851
|
+
}
|
852
|
+
|
853
|
+
function separator {
|
854
|
+
echo "--- $@ ---"
|
855
|
+
}
|
856
|
+
|
857
|
+
function inform {
|
858
|
+
echo -e "\nINFO:\n\n$@\n"
|
859
|
+
}
|
860
|
+
|
861
|
+
function warning {
|
862
|
+
echo -e "\nWARNING:\n\n$@\n"
|
863
|
+
}
|
864
|
+
|
865
|
+
function warning_oldhome {
|
866
|
+
local old=$1 new=$2
|
867
|
+
warning " ftplicity changed name to duply since you created your profiles.
|
868
|
+
Please rename the old folder
|
869
|
+
'$old'
|
870
|
+
to
|
871
|
+
'$new'
|
872
|
+
and this warning will disappear.
|
873
|
+
If you decide not to do so profiles will _only_ work from the old location."
|
874
|
+
}
|
875
|
+
|
876
|
+
function error_print {
|
877
|
+
echo -e "$@" >&2
|
878
|
+
}
|
879
|
+
|
880
|
+
function error {
|
881
|
+
error_print "\nSorry. A fatal ERROR occured:\n\n$@\n"
|
882
|
+
exit -1
|
883
|
+
}
|
884
|
+
|
885
|
+
function error_gpg {
|
886
|
+
[ -n "$2" ] && local hint="\n $2\n\n "
|
887
|
+
|
888
|
+
error "$1
|
889
|
+
|
890
|
+
Hint${hint:+s}:
|
891
|
+
${hint}Maybe you have not created a gpg key yet (e.g. gpg --gen-key)?
|
892
|
+
Don't forget the used _password_ as you will need it.
|
893
|
+
When done enter the 8 digit id & the password in the profile conf file.
|
894
|
+
|
895
|
+
The key id can be found doing a 'gpg --list-keys'. In the example output
|
896
|
+
below the key id would be FFFFFFFF for the public key.
|
897
|
+
|
898
|
+
pub 1024D/FFFFFFFF 2007-12-17
|
899
|
+
uid duplicity
|
900
|
+
sub 2048g/899FE27F 2007-12-17
|
901
|
+
"
|
902
|
+
}
|
903
|
+
|
904
|
+
function error_gpg_key {
|
905
|
+
local KEY_ID="$1"
|
906
|
+
local KIND="$2"
|
907
|
+
error_gpg "${KIND} gpg key '${KEY_ID}' cannot be found." \
|
908
|
+
"Doublecheck if the above key is listed by 'gpg --list-keys' or available
|
909
|
+
as gpg key file '$(basename "$(gpg_keyfile "${KEY_ID}")")' in the profile folder.
|
910
|
+
If not you can put it there and $ME will autoimport it on the next run.
|
911
|
+
Alternatively import it manually as the user you plan to run $ME with."
|
912
|
+
}
|
913
|
+
|
914
|
+
function error_gpg_test {
|
915
|
+
[ -n "$2" ] && local hint="\n $2\n\n "
|
916
|
+
|
917
|
+
error "$1
|
918
|
+
|
919
|
+
Hint${hint:+s}:
|
920
|
+
${hint}This error means that gpg is probably misconfigured or not working
|
921
|
+
correctly. The error message above should help to solve the problem.
|
922
|
+
However, if for some reason $ME should misinterpret the situation you
|
923
|
+
can define GPG_TEST='disabled' in the conf file to bypass the test.
|
924
|
+
Please do not forget to report the bug in order to resolve the problem
|
925
|
+
in future versions of $ME.
|
926
|
+
"
|
927
|
+
}
|
928
|
+
|
929
|
+
function error_path {
|
930
|
+
error "$@
|
931
|
+
PATH='$PATH'
|
932
|
+
"
|
933
|
+
}
|
934
|
+
|
935
|
+
function error_to_string {
|
936
|
+
[ -n "$1" ] && [ "$1" -eq 0 ] && echo "OK" || echo "FAILED 'code $1'"
|
937
|
+
}
|
938
|
+
|
939
|
+
function duplicity_version_get {
|
940
|
+
var_isset DUPL_VERSION && return
|
941
|
+
DUPL_VERSION=`duplicity --version 2>&1 | awk '/^duplicity /{print $2; exit;}'`
|
942
|
+
#DUPL_VERSION='0.6.08b' #,0.4.4.RC4,0.6.08b
|
943
|
+
DUPL_VERSION_VALUE=0
|
944
|
+
DUPL_VERSION_AWK=$(awk -v v="$DUPL_VERSION" 'BEGIN{
|
945
|
+
if (match(v,/[^\.0-9]+[0-9]*$/)){
|
946
|
+
rest=substr(v,RSTART,RLENGTH);v=substr(v,0,RSTART-1);}
|
947
|
+
if (pos=match(rest,/RC([0-9]+)$/)) rc=substr(rest,pos+2)
|
948
|
+
split(v,f,"[. ]"); if(f[1]f[2]f[3]~/^[0-9]+$/) vvalue=f[1]*10000+f[2]*100+f[3]; else vvalue=0
|
949
|
+
print "#"v"_"rest"("rc"):"f[1]"-"f[2]"-"f[3]
|
950
|
+
print "DUPL_VERSION_VALUE=\047"vvalue"\047"
|
951
|
+
print "DUPL_VERSION_RC=\047"rc"\047"
|
952
|
+
print "DUPL_VERSION_SUFFIX=\047"rest"\047"
|
953
|
+
}')
|
954
|
+
eval "$DUPL_VERSION_AWK"
|
955
|
+
#echo -e ",$DUPL_VERSION,$DUPL_VERSION_VALUE,$DUPL_VERSION_RC,$DUPL_VERSION_SUFFIX,"
|
956
|
+
}
|
957
|
+
|
958
|
+
function duplicity_version_check {
|
959
|
+
if [ $DUPL_VERSION_VALUE -eq 0 ]; then
|
960
|
+
inform "duplicity version check failed (please report, this is a bug)"
|
961
|
+
elif [ $DUPL_VERSION_VALUE -le 404 ] && [ ${DUPL_VERSION_RC:-4} -lt 4 ]; then
|
962
|
+
error "The installed version $DUPL_VERSION is incompatible with $ME v$ME_VERSION.
|
963
|
+
You should upgrade your version of duplicity to at least v0.4.4RC4 or
|
964
|
+
use the older ftplicity version 1.1.1 from $ME_WEBSITE."
|
965
|
+
fi
|
966
|
+
}
|
967
|
+
|
968
|
+
function duplicity_version_ge {
|
969
|
+
[ "$DUPL_VERSION_VALUE" -ge "$1" ]
|
970
|
+
}
|
971
|
+
|
972
|
+
function duplicity_version_lt {
|
973
|
+
! duplicity_version_ge "$1"
|
974
|
+
}
|
975
|
+
|
976
|
+
function run_script { # run pre/post scripts
|
977
|
+
local ERR=0
|
978
|
+
local SCRIPT="$1"
|
979
|
+
if [ ! -z "$PREVIEW" ] ; then
|
980
|
+
echo "$([ ! -x "$SCRIPT" ] && echo ". ")$SCRIPT"
|
981
|
+
elif [ -r "$SCRIPT" ] ; then
|
982
|
+
echo -n "Running '$SCRIPT' "
|
983
|
+
if [ -x "$SCRIPT" ]; then
|
984
|
+
OUT=$("$SCRIPT" 2>&1)
|
985
|
+
ERR=$?
|
986
|
+
else
|
987
|
+
OUT=$(. "$SCRIPT" 2>&1)
|
988
|
+
ERR=$?
|
989
|
+
fi
|
990
|
+
[ $ERR -eq "0" ] && echo "- OK" || echo "- FAILED (code $ERR)"
|
991
|
+
echo -en ${OUT:+"Output: $OUT\n"} ;
|
992
|
+
else
|
993
|
+
echo "Skipping n/a script '$SCRIPT'."
|
994
|
+
fi
|
995
|
+
return $ERR
|
996
|
+
}
|
997
|
+
|
998
|
+
function run_cmd {
|
999
|
+
# run or print escaped cmd string
|
1000
|
+
local CMD_ERR=0
|
1001
|
+
if [ -n "$PREVIEW" ]; then
|
1002
|
+
CMD_OUT=$( echo "$@ 2>&1" )
|
1003
|
+
CMD_MSG="-- Run cmd -- $CMD_MSG --\n$CMD_OUT"
|
1004
|
+
elif [ -n "$CMD_DISABLED" ]; then
|
1005
|
+
CMD_MSG="$CMD_MSG (DISABLED) - $CMD_DISABLED"
|
1006
|
+
else
|
1007
|
+
CMD_OUT=` eval "$@" 2>&1 `
|
1008
|
+
CMD_ERR=$?
|
1009
|
+
if [ "$CMD_ERR" = "0" ]; then
|
1010
|
+
CMD_MSG="$CMD_MSG (OK)"
|
1011
|
+
else
|
1012
|
+
CMD_MSG="$CMD_MSG (FAILED)"
|
1013
|
+
fi
|
1014
|
+
fi
|
1015
|
+
echo -e "$CMD_MSG"
|
1016
|
+
# reset
|
1017
|
+
unset CMD_DISABLED CMD_MSG
|
1018
|
+
return $CMD_ERR
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
function qw { quotewrap "$@"; }
|
1022
|
+
|
1023
|
+
function quotewrap {
|
1024
|
+
local param="$@"
|
1025
|
+
# quote strings having non word chars (e.g. spaces)
|
1026
|
+
if echo "$param" | awk '/[^A-Za-z0-9_\.\-]/{exit 0}{exit 1}'; then
|
1027
|
+
echo "$param" | awk '{\
|
1028
|
+
gsub(/[\047]/,"\047\\\047\047",$0);\
|
1029
|
+
gsub(/[\042]/,"\047\\\042\047",$0);\
|
1030
|
+
print "\047"$0"\047"}'
|
1031
|
+
return
|
1032
|
+
fi
|
1033
|
+
echo $param
|
1034
|
+
}
|
1035
|
+
|
1036
|
+
function duplicity_params_global {
|
1037
|
+
# already done? return
|
1038
|
+
var_isset 'DUPL_PARAMS_GLOBAL' && return
|
1039
|
+
local DUPL_ARG_ENC
|
1040
|
+
|
1041
|
+
# use key only if set in config, else leave it to symmetric encryption
|
1042
|
+
if gpg_disabled; then
|
1043
|
+
local DUPL_PARAM_ENC='--no-encryption'
|
1044
|
+
else
|
1045
|
+
local DUPL_PARAM_ENC=$(gpg_prefix_keyset '--encrypt-key' "${GPG_KEYS_ENC_ARRAY[@]}")
|
1046
|
+
gpg_signing && local DUPL_PARAM_SIGN=$(gpg_prefix_keyset '--sign-key' "$GPG_KEY_SIGN")
|
1047
|
+
# interpret password settings
|
1048
|
+
var_isset 'GPG_PW' && DUPL_ARG_ENC="PASSPHRASE=$(qw "${GPG_PW}")"
|
1049
|
+
var_isset 'GPG_PW_SIGN' && DUPL_ARG_ENC="${DUPL_ARG_ENC} SIGN_PASSPHRASE=$(qw "${GPG_PW_SIGN}")"
|
1050
|
+
fi
|
1051
|
+
|
1052
|
+
local GPG_OPTS=${GPG_OPTS:+"--gpg-options $(qw "${GPG_OPTS}")"}
|
1053
|
+
|
1054
|
+
# set name for dupl archive folder, since 0.6.0
|
1055
|
+
if duplicity_version_ge 601; then
|
1056
|
+
local DUPL_ARCHDIR=''
|
1057
|
+
if var_isset 'ARCH_DIR'; then
|
1058
|
+
DUPL_ARCHDIR="--archive-dir $(qw "${ARCH_DIR}")"
|
1059
|
+
fi
|
1060
|
+
DUPL_ARCHDIR="${DUPL_ARCHDIR} --name $(qw "duply_${NAME}")"
|
1061
|
+
fi
|
1062
|
+
|
1063
|
+
DUPL_PARAMS_GLOBAL="${DUPL_ARCHDIR} ${DUPL_PARAM_ENC} \
|
1064
|
+
${DUPL_PARAM_SIGN} --verbosity '${VERBOSITY:-4}' \
|
1065
|
+
${GPG_OPTS}"
|
1066
|
+
|
1067
|
+
DUPL_VARS_GLOBAL="TMPDIR='$TEMP_DIR' \
|
1068
|
+
${DUPL_ARG_ENC}"
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
# filter the DUPL_PARAMS var from conf
|
1072
|
+
function duplicity_params_conf {
|
1073
|
+
# reuse cmd var from main loop
|
1074
|
+
## in/exclude parameters are currently not supported on restores
|
1075
|
+
if [ "$cmd" = "fetch" ] || [ "$cmd" = "restore" ]; then
|
1076
|
+
# filter exclude params from fetch/restore
|
1077
|
+
echo "$DUPL_PARAMS" | awk '{gsub(/--(ex|in)clude[a-z-]*(([ \t]+|=)[^-][^ \t]+)?/,"");print}'
|
1078
|
+
return
|
1079
|
+
fi
|
1080
|
+
|
1081
|
+
echo "$DUPL_PARAMS"
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
function duplify { # the actual wrapper function
|
1085
|
+
local PARAMSNOW DUPL_CMD DUPL_CMD_PARAMS
|
1086
|
+
|
1087
|
+
# put command (with params) first in duplicity parameters
|
1088
|
+
for param in "$@" ; do
|
1089
|
+
# split cmd from params (everything before splitchar --)
|
1090
|
+
if [ "$param" == "--" ] ; then
|
1091
|
+
PARAMSNOW=1
|
1092
|
+
else
|
1093
|
+
# wrap in quotes to protect from spaces
|
1094
|
+
[ ! $PARAMSNOW ] && \
|
1095
|
+
DUPL_CMD="$DUPL_CMD $(qw $param)" \
|
1096
|
+
|| \
|
1097
|
+
DUPL_CMD_PARAMS="$DUPL_CMD_PARAMS $(qw $param)"
|
1098
|
+
fi
|
1099
|
+
done
|
1100
|
+
|
1101
|
+
# init global duplicity parameters same for all tasks
|
1102
|
+
duplicity_params_global
|
1103
|
+
|
1104
|
+
var_isset 'PREVIEW' && local RUN=echo || local RUN=eval
|
1105
|
+
$RUN ${DUPL_VARS_GLOBAL} ${BACKEND_PARAMS} \
|
1106
|
+
${DUPL_PRECMD} duplicity $DUPL_CMD $DUPL_PARAMS_GLOBAL $(duplicity_params_conf)\
|
1107
|
+
$GPG_USEAGENT $DUPL_CMD_PARAMS ${PREVIEW:+}
|
1108
|
+
|
1109
|
+
local ERR=$?
|
1110
|
+
return $ERR
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
function secureconf { # secure the configuration dir
|
1114
|
+
#PERMS=$(ls -la $(dirname $CONFDIR) | grep -e " $(basename $CONFDIR)\$" | awk '{print $1}')
|
1115
|
+
local PERMS="$(ls -la "$CONFDIR/." | awk 'NR==2{print $1}')"
|
1116
|
+
if [ "${PERMS/#drwx------*/OK}" != 'OK' ] ; then
|
1117
|
+
chmod u+rwX,go= "$CONFDIR"; local ERR=$?
|
1118
|
+
warning "The profile's folder
|
1119
|
+
'$CONFDIR'
|
1120
|
+
permissions are not safe ($PERMS). Secure them now. - ($(error_to_string $ERR))"
|
1121
|
+
fi
|
1122
|
+
}
|
1123
|
+
|
1124
|
+
# params are $1=timeformatstring (default like date output), $2=epoch seconds since 1.1.1970 (default now)
|
1125
|
+
function date_fix {
|
1126
|
+
local DEFAULTFORMAT='%a %b %d %H:%M:%S %Z %Y'
|
1127
|
+
# gnu date with -d @epoch
|
1128
|
+
date=$(date ${2:+-d @$2} ${1:++"$1"} 2> /dev/null) && \
|
1129
|
+
echo $date && return
|
1130
|
+
# date bsd,osx with -r epoch
|
1131
|
+
date=$(date ${2:+-r $2} ${1:++"$1"} 2> /dev/null) && \
|
1132
|
+
echo $date && return
|
1133
|
+
# date busybox with -d epoch -D %s
|
1134
|
+
date=$(date ${2:+-d $2 -D %s} ${1:++"$1"} 2> /dev/null) && \
|
1135
|
+
echo $date && return
|
1136
|
+
## some date commands do not support giving a time w/o setting it systemwide (irix,solaris,others?)
|
1137
|
+
# python fallback
|
1138
|
+
date=$(python -c "import time;print time.strftime('${1:-$DEFAULTFORMAT}',time.localtime(${2}))" 2> /dev/null) && \
|
1139
|
+
echo $date && return
|
1140
|
+
# awk fallback
|
1141
|
+
date=$(awk "BEGIN{print strftime(\"${1:-$DEFAULTFORMAT}\"${2:+,$2})}" 2> /dev/null) && \
|
1142
|
+
echo $date && return
|
1143
|
+
# perl fallback
|
1144
|
+
date=$(perl -e "use POSIX qw(strftime);\$date = strftime(\"${1:-$DEFAULTFORMAT}\",localtime(${2}));print \"\$date\n\";" 2> /dev/null) && \
|
1145
|
+
echo $date && return
|
1146
|
+
# error
|
1147
|
+
echo "ERROR"
|
1148
|
+
return 1
|
1149
|
+
}
|
1150
|
+
|
1151
|
+
function nsecs {
|
1152
|
+
# only 9 digit returns, e.g. not all date(s) deliver nsecs
|
1153
|
+
local NSECS=$(date +%N 2> /dev/null | head -1 |grep -e "^[[:digit:]]\{9\}$")
|
1154
|
+
echo ${NSECS:-000000000}
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
function nsecs_to_sec {
|
1158
|
+
echo $(($1/1000000000)).$(printf "%03d" $(($1/1000000%1000)) )
|
1159
|
+
}
|
1160
|
+
|
1161
|
+
function datefull_from_nsecs {
|
1162
|
+
date_from_nsecs $1 '%F %T'
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
function date_from_nsecs {
|
1166
|
+
local FORMAT=${2:-%T}
|
1167
|
+
local TIME=$(nsecs_to_sec $1)
|
1168
|
+
local SECS=${TIME%.*}
|
1169
|
+
local DATE=$(date_fix "%T" ${SECS:-0})
|
1170
|
+
echo $DATE.${TIME#*.}
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
function var_isset {
|
1174
|
+
if [ -z "$1" ]; then
|
1175
|
+
echo "ERROR: function var_isset needs a string as parameter"
|
1176
|
+
elif eval "[ \"\${$1}\" == 'not_set' ]" || eval "[ \"\${$1-not_set}\" != 'not_set' ]"; then
|
1177
|
+
return 0
|
1178
|
+
fi
|
1179
|
+
return 1
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
function url_encode {
|
1183
|
+
# utilize python, silently do nothing on error - because no python no duplicity
|
1184
|
+
OUT=$(python -c "
|
1185
|
+
try: import urllib.request as urllib
|
1186
|
+
except ImportError: import urllib
|
1187
|
+
print(urllib.${2}quote('$1'));
|
1188
|
+
" 2>/dev/null ); ERR=$?
|
1189
|
+
[ "$ERR" -eq 0 ] && echo $OUT || echo $1
|
1190
|
+
}
|
1191
|
+
|
1192
|
+
function url_decode {
|
1193
|
+
# reuse function above with a simple string param hack
|
1194
|
+
url_encode "$1" "un"
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
function toupper {
|
1198
|
+
echo "$@"|awk '$0=toupper($0)'
|
1199
|
+
}
|
1200
|
+
|
1201
|
+
function tolower {
|
1202
|
+
echo "$@"|awk '$0=tolower($0)'
|
1203
|
+
}
|
1204
|
+
|
1205
|
+
function isnumber {
|
1206
|
+
case "$*" in
|
1207
|
+
''|*[!0-9]*) return 1;;
|
1208
|
+
*) return 0;;
|
1209
|
+
esac
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
#function tmp_space {
|
1213
|
+
#
|
1214
|
+
# if ! isnumber $VOLSIZE; then
|
1215
|
+
# inform "failed to determine free space (please report, this is a bug)"
|
1216
|
+
# return
|
1217
|
+
# fi
|
1218
|
+
#
|
1219
|
+
# get free temp space
|
1220
|
+
# TEMP_FREE="$(df -P -k "$TEMP_DIR" 2>/dev/null | awk 'END{pos=(NF-2);if(pos>0) print $pos;}')"
|
1221
|
+
# # check for free space or FAIL
|
1222
|
+
# if [ $((${TEMP_FREE:-0}-${VOLSIZE:-0}*1024)) -lt 0-lt 0 ]; then
|
1223
|
+
# error "Temporary file space '$TEMP_DIR' free space is smaller ($((TEMP_FREE/1024))MB)
|
1224
|
+
#than one duplicity volume (${VOLSIZE}MB).
|
1225
|
+
#
|
1226
|
+
# Hint: Free space or change TEMP_DIR setting."
|
1227
|
+
#fi
|
1228
|
+
#
|
1229
|
+
#}
|
1230
|
+
|
1231
|
+
function gpg_disabled {
|
1232
|
+
echo "${GPG_KEY}" | grep -iq -e '^disabled$'
|
1233
|
+
}
|
1234
|
+
|
1235
|
+
# usage: join SEPARATOR "entry1" "entry2"
|
1236
|
+
function join {
|
1237
|
+
local SEP="$1" ENTRY OUT; shift;
|
1238
|
+
for ENTRY in "$@"; do
|
1239
|
+
ENTRY=${ENTRY//$SEP/\\$SEP}
|
1240
|
+
[ -z "$OUT" ] && OUT=$ENTRY || OUT="$OUT$SEP$ENTRY"
|
1241
|
+
done
|
1242
|
+
echo $OUT
|
1243
|
+
}
|
1244
|
+
|
1245
|
+
function gpg_signing {
|
1246
|
+
echo ${GPG_KEY_SIGN} | grep -v -q -e '^disabled$'
|
1247
|
+
}
|
1248
|
+
|
1249
|
+
# parameter key id, key_type
|
1250
|
+
function gpg_keyfile {
|
1251
|
+
local GPG_KEY=$(gpg_key_legalize $1) TYPE="$2"
|
1252
|
+
local KEYFILE="${KEYFILE//.asc/${GPG_KEY:+.$GPG_KEY}.asc}"
|
1253
|
+
echo "${KEYFILE//.asc/${TYPE:+.$(tolower $TYPE)}.asc}"
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
# parameter key id
|
1257
|
+
function gpg_import {
|
1258
|
+
local i FILE FOUND=0 KEY_ID="$1" KEY_TYPE="$2" KEY_FP="" ERR=0
|
1259
|
+
# create a list of legacy key file names and current naming scheme
|
1260
|
+
# we always import pub and sec if they are avail in conf folder
|
1261
|
+
local KEYFILES=( "$CONFDIR/gpgkey" $(gpg_keyfile "$KEY_ID") \
|
1262
|
+
$(gpg_keyfile "$KEY_ID" PUB) $(gpg_keyfile "$KEY_ID" SEC))
|
1263
|
+
|
1264
|
+
# Try autoimport from existing old gpgkey files
|
1265
|
+
# and new gpgkey.XXX.asc files (since v1.4.2)
|
1266
|
+
# and even newer gpgkey.XXX.[pub|sec].asc
|
1267
|
+
for (( i = 0 ; i < ${#KEYFILES[@]} ; i++ )); do
|
1268
|
+
FILE=${KEYFILES[$i]}
|
1269
|
+
if [ -f "$FILE" ]; then
|
1270
|
+
FOUND=1
|
1271
|
+
|
1272
|
+
CMD_MSG="Import keyfile '$FILE' to keyring"
|
1273
|
+
run_cmd "$GPG" $GPG_OPTS --batch --import "$FILE"
|
1274
|
+
if [ "$?" != "0" ]; then
|
1275
|
+
warning "Import failed.${CMD_OUT:+\n$CMD_OUT}"
|
1276
|
+
ERR=1
|
1277
|
+
# continue with next
|
1278
|
+
continue
|
1279
|
+
fi
|
1280
|
+
fi
|
1281
|
+
done
|
1282
|
+
|
1283
|
+
if [ "$FOUND" -eq 0 ]; then
|
1284
|
+
warning "No keyfile for '$KEY_ID' found in profile\n'$CONFDIR'."
|
1285
|
+
fi
|
1286
|
+
|
1287
|
+
# try to set trust automagically
|
1288
|
+
CMD_MSG="Autoset trust of key '$KEY_ID' to ultimate"
|
1289
|
+
run_cmd echo $(gpg_fingerprint "$KEY_ID"):6: \| "$GPG" $GPG_OPTS --import-ownertrust --batch --logger-fd 1
|
1290
|
+
if [ "$?" = "0" ] && [ -z "$PREVIEW" ]; then
|
1291
|
+
# success on all levels, we're done
|
1292
|
+
return $ERR
|
1293
|
+
fi
|
1294
|
+
|
1295
|
+
# failover: user has to set trust manually
|
1296
|
+
echo -e "For $ME to work you have to set the trust level
|
1297
|
+
with the command \"trust\" to \"ultimate\" (5) now.
|
1298
|
+
Exit the edit mode of gpg with \"quit\"."
|
1299
|
+
CMD_MSG="Running gpg to manually edit key '$KEY_ID'"
|
1300
|
+
run_cmd sleep 5\; "$GPG" $GPG_OPTS --edit-key "$KEY_ID"
|
1301
|
+
|
1302
|
+
return $ERR
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
# see 'How to specify a user ID' on gpg manpage
|
1306
|
+
function gpg_fingerprint {
|
1307
|
+
local PRINT=$("$GPG" $GPG_OPTS --fingerprint "$1" 2>&1|awk -F= 'NR==2{gsub(/ /,"",$2);$2=toupper($2); if ( $2 ~ /^[A-F0-9]+$/ && length($2) == 40 ) print $2; else exit 1}') \
|
1308
|
+
&& [ -n "$PRINT" ] && echo $PRINT && return 0
|
1309
|
+
return 1
|
1310
|
+
}
|
1311
|
+
|
1312
|
+
function gpg_export_if_needed {
|
1313
|
+
local SUCCESS FILE KEY_TYPE
|
1314
|
+
local TMPFILE="$TEMP_DIR/${ME_NAME}.$$.$(date_fix %s).gpgexp"
|
1315
|
+
for KEY_ID in "$@"; do
|
1316
|
+
# check if already exported, do it if not
|
1317
|
+
for KEY_TYPE in PUB SEC; do
|
1318
|
+
FILE="$(gpg_keyfile "$KEY_ID" $KEY_TYPE)"
|
1319
|
+
if [ ! -f "$FILE" ] && eval gpg_$(tolower $KEY_TYPE)_avail \"$KEY_ID\"; then
|
1320
|
+
# exporting
|
1321
|
+
CMD_MSG="Export $KEY_TYPE key '$KEY_ID'"
|
1322
|
+
run_cmd $GPG $GPG_OPTS --armor --export"$(test "SEC" = "$KEY_TYPE" && echo -secret-keys)"" $(qw $KEY_ID) >> \"$TMPFILE\""
|
1323
|
+
CMD_ERR=$?
|
1324
|
+
|
1325
|
+
if [ "$CMD_ERR" = "0" ]; then
|
1326
|
+
CMD_MSG="Write file '"$(basename "$FILE")"'"
|
1327
|
+
run_cmd " mv \"$TMPFILE\" \"$FILE\""
|
1328
|
+
fi
|
1329
|
+
|
1330
|
+
if [ "$CMD_ERR" != "0" ]; then
|
1331
|
+
warning "Backup failed.${CMD_OUT:+\n$CMD_OUT}"
|
1332
|
+
else
|
1333
|
+
SUCCESS=1
|
1334
|
+
fi
|
1335
|
+
|
1336
|
+
# cleanup
|
1337
|
+
rm "$TMPFILE" 1>/dev/null 2>&1
|
1338
|
+
fi
|
1339
|
+
done
|
1340
|
+
done
|
1341
|
+
|
1342
|
+
[ -n "$SUCCESS" ] && inform "$ME exported new keys to your profile.
|
1343
|
+
You should backup your changed profile folder now and store it in a safe place."
|
1344
|
+
}
|
1345
|
+
|
1346
|
+
# replace all non-alnum chars with underscore (for file operations)
|
1347
|
+
function gpg_key_legalize {
|
1348
|
+
echo $* | awk '{gsub(/[^a-zA-Z0-9]/,"_",$0); print}'
|
1349
|
+
}
|
1350
|
+
|
1351
|
+
function gpg_key_cache {
|
1352
|
+
local RES
|
1353
|
+
local MODE=$1
|
1354
|
+
shift
|
1355
|
+
local PREFIX="GPG_KEY"
|
1356
|
+
local SUFFIX=$(gpg_key_legalize "$@")
|
1357
|
+
local KEYID="$*"
|
1358
|
+
local CACHE="${PREFIX}_${MODE}_${SUFFIX}"
|
1359
|
+
if [ "$MODE" = "RESET" ]; then
|
1360
|
+
eval unset ${PREFIX}_PUB_$SUFFIX ${PREFIX}_SEC_$SUFFIX
|
1361
|
+
return 255
|
1362
|
+
elif ! var_isset "$CACHE"; then
|
1363
|
+
if [ "$MODE" = "PUB" ]; then
|
1364
|
+
RES=$("$GPG" $GPG_OPTS --list-key "$KEYID" > /dev/null 2>&1; echo -n $?)
|
1365
|
+
elif [ "$MODE" = "SEC" ]; then
|
1366
|
+
RES=$("$GPG" $GPG_OPTS --list-secret-key "$KEYID" > /dev/null 2>&1; echo -n $?)
|
1367
|
+
else
|
1368
|
+
return 255
|
1369
|
+
fi
|
1370
|
+
eval $CACHE=$RES
|
1371
|
+
fi
|
1372
|
+
eval return \$$CACHE
|
1373
|
+
}
|
1374
|
+
|
1375
|
+
function gpg_pub_avail {
|
1376
|
+
gpg_key_cache PUB "$@"
|
1377
|
+
}
|
1378
|
+
|
1379
|
+
function gpg_sec_avail {
|
1380
|
+
gpg_key_cache SEC "$@"
|
1381
|
+
}
|
1382
|
+
|
1383
|
+
function gpg_key_format {
|
1384
|
+
echo $1 | grep -q '^[0-9a-fA-F]\{8\}$'
|
1385
|
+
}
|
1386
|
+
|
1387
|
+
#function gpg_split_keyset {
|
1388
|
+
# return
|
1389
|
+
# awk "BEGIN{ keys=toupper(\"$@\"); gsub(/[^A-Z0-9]/,\" \",keys); print keys }"
|
1390
|
+
#}
|
1391
|
+
|
1392
|
+
# splits a comma separated line into lines, respects escaped commas
|
1393
|
+
function gpg_split_keyset2 {
|
1394
|
+
local LIST
|
1395
|
+
LIST=$(echo "$@" | awk '{ gsub(/,/,"\n",$0); gsub(/\\\n/,",",$0); print $0 }')
|
1396
|
+
echo -e "$LIST"
|
1397
|
+
}
|
1398
|
+
|
1399
|
+
function gpg_prefix_keyset {
|
1400
|
+
local PREFIX="$1" OUT=""
|
1401
|
+
shift
|
1402
|
+
for KEY_ID in "$@"; do
|
1403
|
+
OUT="${OUT} $PREFIX $(qw ${KEY_ID})"
|
1404
|
+
done
|
1405
|
+
echo $OUT
|
1406
|
+
}
|
1407
|
+
|
1408
|
+
# grep a variable from conf text file (currently not used)
|
1409
|
+
function gpg_passwd {
|
1410
|
+
[ -r "$CONF" ] && \
|
1411
|
+
awk '/^[ \t]*GPG_PW[ \t=]/{\
|
1412
|
+
sub(/^[ \t]*GPG_PW[ \t]*=*/,"",$0);\
|
1413
|
+
gsub(/^[ \t]*[\047"]|[\047"][ \t]*$/,"",$0);\
|
1414
|
+
print $0; exit}' "$CONF"
|
1415
|
+
}
|
1416
|
+
|
1417
|
+
# return success if at least one secret key is available
|
1418
|
+
function gpg_key_decryptable {
|
1419
|
+
local KEY_ID
|
1420
|
+
for KEY_ID in "${GPG_KEYS_ENC_ARRAY[@]}"; do
|
1421
|
+
gpg_sec_avail "$KEY_ID" && return 0
|
1422
|
+
done
|
1423
|
+
return 1
|
1424
|
+
}
|
1425
|
+
|
1426
|
+
function gpg_symmetric {
|
1427
|
+
[ -z "${GPG_KEY}${GPG_KEYS_ENC_ARRAY}" ]
|
1428
|
+
}
|
1429
|
+
|
1430
|
+
# checks for max two params if they are set, typically GPG_PW & GPG_PW_SIGN
|
1431
|
+
function gpg_param_passwd {
|
1432
|
+
var_isset GPG_USEAGENT && exit 1
|
1433
|
+
|
1434
|
+
if ( [ -n "$1" ] && var_isset "$1" ) || ( [ -n "$2" ] && var_isset "$2" ); then
|
1435
|
+
echo "--passphrase-fd 0 --batch"
|
1436
|
+
fi
|
1437
|
+
}
|
1438
|
+
|
1439
|
+
# select the earlist defined and create an "echo <value> |" string
|
1440
|
+
function gpg_pass_pipein {
|
1441
|
+
var_isset GPG_USEAGENT && exit 1
|
1442
|
+
|
1443
|
+
for var in "$@"
|
1444
|
+
do
|
1445
|
+
if var_isset "$var"; then
|
1446
|
+
echo "echo $(qw $(eval echo \$$var)) |"
|
1447
|
+
return 0
|
1448
|
+
fi
|
1449
|
+
done
|
1450
|
+
|
1451
|
+
return 1
|
1452
|
+
}
|
1453
|
+
|
1454
|
+
# checks if gpg-agent is available, returns error code
|
1455
|
+
# 0 on success
|
1456
|
+
# 1 if GPG_AGENT_INFO is not set
|
1457
|
+
# 2 if GPG_AGENT_INFO is stale
|
1458
|
+
function gpg_agent_avail {
|
1459
|
+
local ERR=1
|
1460
|
+
if var_isset GPG_AGENT_INFO; then
|
1461
|
+
ps -p $(echo $GPG_AGENT_INFO|awk -F: '{print $2}') > /dev/null 2>&1 &&\
|
1462
|
+
ERR=0 || ERR=2
|
1463
|
+
fi
|
1464
|
+
|
1465
|
+
return $ERR
|
1466
|
+
}
|
1467
|
+
|
1468
|
+
# start of script #######################################################################
|
1469
|
+
|
1470
|
+
# confidentiality first, all we create is only readable by us
|
1471
|
+
umask 077
|
1472
|
+
|
1473
|
+
# check if ftplicity is there & executable
|
1474
|
+
[ -n "$ME_LONG" ] && [ -x "$ME_LONG" ] || error "$ME missing. Executable & available in path? ($ME_LONG)"
|
1475
|
+
|
1476
|
+
if [ ${#@} -eq 1 ]; then
|
1477
|
+
cmd="${1}"
|
1478
|
+
else
|
1479
|
+
FTPLCFG="${1}" ; cmd="${2}"
|
1480
|
+
fi
|
1481
|
+
|
1482
|
+
# deal with command before profile validation calls
|
1483
|
+
# show requested version
|
1484
|
+
# OR requested usage info
|
1485
|
+
# OR create a profile
|
1486
|
+
# OR fall through
|
1487
|
+
##if [ ${#@} -le 2 ]; then
|
1488
|
+
case "$cmd" in
|
1489
|
+
changelog)
|
1490
|
+
changelog
|
1491
|
+
exit 0
|
1492
|
+
;;
|
1493
|
+
create)
|
1494
|
+
set_config
|
1495
|
+
if [ -d "$CONFDIR" ]; then
|
1496
|
+
error "The profile '$FTPLCFG' already exists in
|
1497
|
+
'$CONFDIR'.
|
1498
|
+
|
1499
|
+
Hint:
|
1500
|
+
If you _really_ want to create a new profile by this name you will
|
1501
|
+
have to manually delete the existing profile folder first."
|
1502
|
+
exit 1
|
1503
|
+
else
|
1504
|
+
create_config
|
1505
|
+
exit 0
|
1506
|
+
fi
|
1507
|
+
;;
|
1508
|
+
txt2man)
|
1509
|
+
set_config
|
1510
|
+
usage_txt2man
|
1511
|
+
exit 0
|
1512
|
+
;;
|
1513
|
+
usage|-help|--help|-h|-H)
|
1514
|
+
set_config
|
1515
|
+
usage_info
|
1516
|
+
exit 0
|
1517
|
+
;;
|
1518
|
+
version|-version|--version|-v|-V)
|
1519
|
+
version_info_using
|
1520
|
+
exit 0
|
1521
|
+
;;
|
1522
|
+
# fallthrough.. we got a command that needs an existing profile
|
1523
|
+
*)
|
1524
|
+
# if we reach here, user either forgot profile or chose wrong profileless command
|
1525
|
+
if [ ${#@} -le 1 ]; then
|
1526
|
+
error "\
|
1527
|
+
Missing or wrong parameters.
|
1528
|
+
Only the commands
|
1529
|
+
changelog, create, usage, txt2man, version
|
1530
|
+
can be called without selecting an existing profile first.
|
1531
|
+
Your command was '$cmd'.
|
1532
|
+
|
1533
|
+
Hint: Run '$ME usage' to get help."
|
1534
|
+
fi
|
1535
|
+
esac
|
1536
|
+
|
1537
|
+
|
1538
|
+
# Hello world
|
1539
|
+
echo "Start $ME v$ME_VERSION, time is $(date_fix '%F %T')."
|
1540
|
+
|
1541
|
+
# check system environment
|
1542
|
+
DUPLICITY="$(which duplicity 2>/dev/null)"
|
1543
|
+
[ -z "$DUPLICITY" ] && error_path "duplicity missing. installed und available in path?"
|
1544
|
+
# init, exec duplicity version check info
|
1545
|
+
duplicity_version_get
|
1546
|
+
duplicity_version_check
|
1547
|
+
|
1548
|
+
[ -z "$(which awk 2>/dev/null)" ] && error_path "awk missing. installed und available in path?"
|
1549
|
+
|
1550
|
+
### read configuration
|
1551
|
+
set_config
|
1552
|
+
# check validity
|
1553
|
+
if [ ! -d "$CONFDIR" ]; then
|
1554
|
+
error "Selected profile '$FTPLCFG' does not resolve to a profile folder in
|
1555
|
+
'$CONFDIR'.
|
1556
|
+
|
1557
|
+
Hints:
|
1558
|
+
Select one of the available profiles: $(ls -1p $(dirname "$CONFDIR")| awk 'BEGIN{ORS="";OFS=""}/\/$/&&!/^\.+\/$/{print sep"\047"substr($0,0,length($0)-1)"\047";sep=","}').
|
1559
|
+
Use '$ME <name> create' to create a new profile.
|
1560
|
+
Use '$ME usage' to get usage help."
|
1561
|
+
elif [ ! -x "$CONFDIR" ]; then
|
1562
|
+
error "\
|
1563
|
+
Profile folder in '$CONFDIR' cannot be accessed.
|
1564
|
+
|
1565
|
+
Hint:
|
1566
|
+
Check the filesystem permissions and set directory accessible e.g. 'chmod 700'."
|
1567
|
+
elif [ ! -f "$CONF" ] ; then
|
1568
|
+
error "'$CONF' not found."
|
1569
|
+
elif [ ! -r "$CONF" ] ; then
|
1570
|
+
error "'$CONF' not readable."
|
1571
|
+
else
|
1572
|
+
. "$CONF"
|
1573
|
+
#KEYFILE="${KEYFILE//.asc/${GPG_KEY:+.$GPG_KEY}.asc}"
|
1574
|
+
TEMP_DIR=${TEMP_DIR:-'/tmp'}
|
1575
|
+
# backward compatibility: old TARGET_PW overrides silently new TARGET_PASS if set
|
1576
|
+
if var_isset 'TARGET_PW'; then
|
1577
|
+
TARGET_PASS="${TARGET_PW}"
|
1578
|
+
fi
|
1579
|
+
fi
|
1580
|
+
echo "Using profile '$CONFDIR'."
|
1581
|
+
|
1582
|
+
# secure config dir, if needed w/ warning
|
1583
|
+
secureconf
|
1584
|
+
|
1585
|
+
# split TARGET in handy variables
|
1586
|
+
TARGET_SPLIT_URL=$(echo $TARGET | awk '{ \
|
1587
|
+
target=$0; match(target,/^([^\/:]+):\/\//); \
|
1588
|
+
prot=substr(target,RSTART,RLENGTH);\
|
1589
|
+
rest=substr(target,RSTART+RLENGTH); \
|
1590
|
+
if (credsavail=match(rest,/^[^@]*@/)){\
|
1591
|
+
creds=substr(rest,RSTART,RLENGTH-1);\
|
1592
|
+
credcount=split(creds,cred,":");\
|
1593
|
+
rest=substr(rest,RLENGTH+1);\
|
1594
|
+
# split creds with regexp\
|
1595
|
+
match(creds,/^([^:]+)/);\
|
1596
|
+
user=substr(creds,RSTART,RLENGTH);\
|
1597
|
+
pass=substr(creds,RSTART+1+RLENGTH);\
|
1598
|
+
};\
|
1599
|
+
# filter quotes or escape them\
|
1600
|
+
gsub(/[\047\042]/,"",prot);\
|
1601
|
+
gsub(/[\047\042]/,"",rest);\
|
1602
|
+
gsub(/[\047]/,"\047\\\047\047",creds);\
|
1603
|
+
print "TARGET_URL_PROT=\047"prot"\047\n"\
|
1604
|
+
"TARGET_URL_HOSTPATH=\047"rest"\047\n"\
|
1605
|
+
"TARGET_URL_CREDS=\047"creds"\047\n";\
|
1606
|
+
if(user){\
|
1607
|
+
gsub(/[\047]/,"\047\\\047\047",user);\
|
1608
|
+
print "TARGET_URL_USER=\047"user"\047\n"}\
|
1609
|
+
if(pass){\
|
1610
|
+
gsub(/[\047]/,"\047\\\047\047",pass);\
|
1611
|
+
print "TARGET_URL_PASS=$(url_decode \047"pass"\047)\n"}\
|
1612
|
+
}')
|
1613
|
+
eval ${TARGET_SPLIT_URL}
|
1614
|
+
|
1615
|
+
# check if backend specific software is in path
|
1616
|
+
[ -n "$(echo ${TARGET_URL_PROT} | grep -i -e '^ftp://$')" ] && \
|
1617
|
+
[ -z "$(which ncftp 2>/dev/null)" ] && error_path "Protocol 'ftp' needs ncftp. Installed und available in path?"
|
1618
|
+
[ -n "$(echo ${TARGET_URL_PROT} | grep -i -e '^ftps://$')" ] && \
|
1619
|
+
[ -z "$(which lftp 2>/dev/null)" ] && error_path "Protocol 'ftps' needs lftp. Installed und available in path?"
|
1620
|
+
|
1621
|
+
# fetch commmand from parameters ########################################################
|
1622
|
+
# Hint: cmds is also used to check if authentification info sufficient in the next step
|
1623
|
+
cmds="$2"; shift 2
|
1624
|
+
|
1625
|
+
# translate backup to batch command
|
1626
|
+
cmds=${cmds//backup/pre_bkp_post}
|
1627
|
+
|
1628
|
+
# complain if command(s) missing
|
1629
|
+
[ -z $cmds ] && error " No command given.
|
1630
|
+
|
1631
|
+
Hint:
|
1632
|
+
Use '$ME usage' to get usage help."
|
1633
|
+
|
1634
|
+
# process params
|
1635
|
+
for param in "$@"; do
|
1636
|
+
#echo !$param!
|
1637
|
+
case "$param" in
|
1638
|
+
# enable ftplicity preview mode
|
1639
|
+
'--preview')
|
1640
|
+
PREVIEW=1
|
1641
|
+
;;
|
1642
|
+
# interpret duplicity disable encr switch
|
1643
|
+
'--disable-encryption')
|
1644
|
+
GPG_KEY='disabled'
|
1645
|
+
;;
|
1646
|
+
*)
|
1647
|
+
if [ `echo "$param" | grep -e "^-"` ] || \
|
1648
|
+
[ `echo "$last_param" | grep -e "^-"` ] ; then
|
1649
|
+
# forward parameter[/option pairs] to duplicity
|
1650
|
+
dupl_opts["${#dupl_opts[@]}"]=${param}
|
1651
|
+
else
|
1652
|
+
# anything else must be a parameter (eg. for fetch, ...)
|
1653
|
+
ftpl_pars["${#ftpl_pars[@]}"]=${param}
|
1654
|
+
fi
|
1655
|
+
last_param=${param}
|
1656
|
+
;;
|
1657
|
+
esac
|
1658
|
+
done
|
1659
|
+
|
1660
|
+
# plausibility check config - VARS & KEY ################################################
|
1661
|
+
# check if src, trg, trg pw
|
1662
|
+
# auth info sufficient
|
1663
|
+
# gpg key, gpg pwd (might be empty) set in config
|
1664
|
+
# OR key in local gpg db
|
1665
|
+
# OR key can be imported from keyfile
|
1666
|
+
# OR fail
|
1667
|
+
if [ -z "$SOURCE" ] || [ "$SOURCE" == "${DEFAULT_SOURCE}" ]; then
|
1668
|
+
error " Source Path (setting SOURCE) not set or still default value in conf file
|
1669
|
+
'$CONF'."
|
1670
|
+
|
1671
|
+
elif [ -z "$TARGET" ] || [ "$TARGET" == "${DEFAULT_TARGET}" ]; then
|
1672
|
+
error " Backup Target (setting TARGET) not set or still default value in conf file
|
1673
|
+
'$CONF'."
|
1674
|
+
|
1675
|
+
elif var_isset 'TARGET_USER' && var_isset 'TARGET_URL_USER' && \
|
1676
|
+
[ "${TARGET_USER}" != "${TARGET_URL_USER}" ]; then
|
1677
|
+
error " TARGET_USER ('${TARGET_USER}') _and_ user in TARGET url ('${TARGET_URL_USER}')
|
1678
|
+
are configured with different values. There can be only one.
|
1679
|
+
|
1680
|
+
Hint: Remove conflicting setting."
|
1681
|
+
|
1682
|
+
elif var_isset 'TARGET_PASS' && var_isset 'TARGET_URL_PASS' && \
|
1683
|
+
[ "${TARGET_PASS}" != "${TARGET_URL_PASS}" ]; then
|
1684
|
+
error " TARGET_PASS ('${TARGET_PASS}') _and_ password in TARGET url ('${TARGET_URL_PASS}')
|
1685
|
+
are configured with different values. There can be only one.
|
1686
|
+
|
1687
|
+
Hint: Remove conflicting setting."
|
1688
|
+
fi
|
1689
|
+
|
1690
|
+
# check if authentication information sufficient
|
1691
|
+
if ( ( ! var_isset 'TARGET_USER' && ! var_isset 'TARGET_URL_USER' ) && \
|
1692
|
+
( ! var_isset 'TARGET_PASS' && ! var_isset 'TARGET_URL_PASS' ) ); then
|
1693
|
+
# ok here some exceptions:
|
1694
|
+
# protocols that do not need passwords
|
1695
|
+
# s3[+http] only needs password for write operations
|
1696
|
+
if [ -n "$( tolower "${TARGET_URL_PROT}" | grep -e '^\(dpbx\|file\|tahoe\|ssh\|scp\|sftp\|swift\)://$' )" ]; then
|
1697
|
+
: # all is well file/tahoe do not need passwords, ssh might use key auth
|
1698
|
+
elif [ -n "$(tolower "${TARGET_URL_PROT}" | grep -e '^s3\(\+http\)\?://$')" ] && \
|
1699
|
+
[ -z "$(echo ${cmds} | grep -e '\(bkp\|incr\|full\|purge\|cleanup\)')" ]; then
|
1700
|
+
: # still fine, it's possible to read only access configured buckets anonymously
|
1701
|
+
else
|
1702
|
+
error " Backup target credentials needed but not set in conf file
|
1703
|
+
'$CONF'.
|
1704
|
+
Setting TARGET_USER or TARGET_PASS or the corresponding values in TARGET url
|
1705
|
+
are missing. Some protocols only might need it for write access to the backup
|
1706
|
+
repository (commands: bkp,backup,full,incr,purge) but not for read only access
|
1707
|
+
(e.g. verify,list,restore,fetch).
|
1708
|
+
|
1709
|
+
Hints:
|
1710
|
+
Add the credentials (user,password) to the conf file.
|
1711
|
+
To force an empty password set TARGET_PASS='' or TARGET='prot://user:@host..'.
|
1712
|
+
"
|
1713
|
+
fi
|
1714
|
+
fi
|
1715
|
+
|
1716
|
+
# GPG config plausibility check1 (disabled check) #############################
|
1717
|
+
if gpg_disabled; then
|
1718
|
+
: # encryption disabled, all is well
|
1719
|
+
elif [ -z "${GPG_KEY}${GPG_KEYS_ENC}${GPG_KEY_SIGN}" ] && ! var_isset 'GPG_PW'; then
|
1720
|
+
warning "GPG_KEY, GPG_KEYS_ENC, GPG_KEY_SIGN and GPG_PW are empty/not set in conf file
|
1721
|
+
'$CONF'.
|
1722
|
+
Will disable encryption for duplicity now.
|
1723
|
+
|
1724
|
+
Hint:
|
1725
|
+
If you really want to use _no_ encryption you can disable this warning by
|
1726
|
+
setting GPG_KEY='disabled' in conf file."
|
1727
|
+
GPG_KEY='disabled'
|
1728
|
+
fi
|
1729
|
+
|
1730
|
+
# GPG availability check (now we know if gpg is really needed)#################
|
1731
|
+
if ! gpg_disabled; then
|
1732
|
+
GPG="$(which gpg 2>/dev/null)"
|
1733
|
+
[ -z "$GPG" ] && error_path "gpg missing. installed und available in path?"
|
1734
|
+
fi
|
1735
|
+
|
1736
|
+
|
1737
|
+
# Output versions info ########################################################
|
1738
|
+
using_info
|
1739
|
+
|
1740
|
+
# GPG create key settings, config check2 (needs gpg) ##########################
|
1741
|
+
if gpg_disabled; then
|
1742
|
+
: # the following tests are not necessary
|
1743
|
+
else
|
1744
|
+
|
1745
|
+
# key set?
|
1746
|
+
if [ "$GPG_KEY" == "${DEFAULT_GPG_KEY}" ]; then
|
1747
|
+
error_gpg "Encryption Key GPG_KEY still default in conf file
|
1748
|
+
'$CONF'."
|
1749
|
+
fi
|
1750
|
+
|
1751
|
+
# create array of gpg encr keys, for further processing
|
1752
|
+
OIFS="$IFS" IFS=$'\n'
|
1753
|
+
GPG_KEYS_ENC_ARRAY=( $( gpg_split_keyset2 ${GPG_KEY},${GPG_KEYS_ENC} ) )
|
1754
|
+
IFS="$OIFS"
|
1755
|
+
|
1756
|
+
# check gpg encr public keys availability
|
1757
|
+
for (( i = 0 ; i < ${#GPG_KEYS_ENC_ARRAY[@]} ; i++ )); do
|
1758
|
+
KEY_ID="${GPG_KEYS_ENC_ARRAY[$i]}"
|
1759
|
+
# test availability, try to import, retest
|
1760
|
+
if ! gpg_pub_avail "${KEY_ID}"; then
|
1761
|
+
echo "Encryption public key '${KEY_ID}' not found."
|
1762
|
+
gpg_import "${KEY_ID}" PUB
|
1763
|
+
gpg_key_cache RESET "${KEY_ID}"
|
1764
|
+
gpg_pub_avail "${KEY_ID}" || error_gpg_key "${KEY_ID}" "Public"
|
1765
|
+
fi
|
1766
|
+
done
|
1767
|
+
|
1768
|
+
# gpg secret sign key availability
|
1769
|
+
# if none set, autoset first encryption key as sign key
|
1770
|
+
if ! gpg_signing; then
|
1771
|
+
echo "Signing disabled per configuration."
|
1772
|
+
# try first key, if one set
|
1773
|
+
elif ! var_isset 'GPG_KEY_SIGN'; then
|
1774
|
+
KEY_ID="${GPG_KEYS_ENC_ARRAY[0]}"
|
1775
|
+
if [ -z "${KEY_ID}" ]; then
|
1776
|
+
echo "Signing disabled. Not GPG_KEY entries in config."
|
1777
|
+
GPG_KEY_SIGN='disabled'
|
1778
|
+
else
|
1779
|
+
# use avail OR try import OR fail
|
1780
|
+
if gpg_sec_avail "${KEY_ID}"; then
|
1781
|
+
GPG_KEY_SIGN="${KEY_ID}"
|
1782
|
+
else
|
1783
|
+
gpg_import "${KEY_ID}" SEC
|
1784
|
+
gpg_key_cache RESET "${KEY_ID}"
|
1785
|
+
if gpg_sec_avail "${KEY_ID}"; then
|
1786
|
+
GPG_KEY_SIGN="${KEY_ID}"
|
1787
|
+
fi
|
1788
|
+
fi
|
1789
|
+
|
1790
|
+
# interpret sign key setting
|
1791
|
+
if var_isset 'GPG_KEY_SIGN'; then
|
1792
|
+
echo "Autoset found secret key of first GPG_KEY entry '${KEY_ID}' for signing."
|
1793
|
+
else
|
1794
|
+
echo "Signing disabled. First GPG_KEY entry's '${KEY_ID}' private key is missing."
|
1795
|
+
GPG_KEY_SIGN='disabled'
|
1796
|
+
fi
|
1797
|
+
fi
|
1798
|
+
else
|
1799
|
+
KEY_ID="${GPG_KEY_SIGN}"
|
1800
|
+
if ! gpg_sec_avail "${KEY_ID}"; then
|
1801
|
+
inform "Secret signing key defined in setting GPG_KEY_SIGN='${KEY_ID}' not found.\nTry to import."
|
1802
|
+
gpg_import "${KEY_ID}" SEC
|
1803
|
+
gpg_key_cache RESET "${KEY_ID}"
|
1804
|
+
gpg_sec_avail "${KEY_ID}" || error_gpg_key "${KEY_ID}" "Private"
|
1805
|
+
fi
|
1806
|
+
fi
|
1807
|
+
|
1808
|
+
# pw set?
|
1809
|
+
# symmetric needs one, always
|
1810
|
+
if gpg_symmetric && ( [ -z "$GPG_PW" ] || [ "$GPG_PW" == "${DEFAULT_GPG_PW}" ] ) \
|
1811
|
+
; then
|
1812
|
+
error_gpg "Encryption passphrase GPG_PW (needed for symmetric encryption)
|
1813
|
+
is empty/not set or still default value in conf file
|
1814
|
+
'$CONF'."
|
1815
|
+
fi
|
1816
|
+
# this is a technicality, we can only pump one pass via pipe into gpg
|
1817
|
+
# but symmetric already always needs one for encryption
|
1818
|
+
if gpg_symmetric && var_isset GPG_PW && var_isset GPG_PW_SIGN &&\
|
1819
|
+
[ -n "$GPG_PW_SIGN" ] && [ "$GPG_PW" != "$GPG_PW_SIGN" ]; then
|
1820
|
+
error_gpg "GPG_PW _and_ GPG_PW_SIGN are defined but not identical in config
|
1821
|
+
'$CONF'.
|
1822
|
+
This is unfortunately impossible. For details see duplicity manpage,
|
1823
|
+
section 'A Note On Symmetric Encryption And Signing'.
|
1824
|
+
|
1825
|
+
Tip: Separate signing keys may have empty passwords e.g. GPG_PW_SIGN=''.
|
1826
|
+
Tip2: Use gpg-agent."
|
1827
|
+
fi
|
1828
|
+
# key enc can deal without, but might profit from gpg-agent
|
1829
|
+
# if GPG_PW is not set alltogether
|
1830
|
+
# if signing key is different from first (main) enc key (we can only pipe one pass into gpg)
|
1831
|
+
if ! gpg_symmetric && \
|
1832
|
+
( ! var_isset GPG_PW || \
|
1833
|
+
( gpg_signing && ! var_isset GPG_PW_SIGN && [ "$GPG_KEY_SIGN" != "${GPG_KEYS_ENC_ARRAY[0]}" ] ) ); then
|
1834
|
+
|
1835
|
+
GPG_AGENT_ERR=$(gpg_agent_avail ; echo $?)
|
1836
|
+
if [ "$GPG_AGENT_ERR" -eq 1 ]; then
|
1837
|
+
echo "Cannot use gpg-agent. GPG_AGENT_INFO not set."
|
1838
|
+
elif [ "$GPG_AGENT_ERR" -eq 2 ]; then
|
1839
|
+
echo "Cannot use gpg-agent! GPG_AGENT_INFO contains stale pid."
|
1840
|
+
else
|
1841
|
+
echo "Autoenable use of gpg-agent. GPG_PW or GPG_PW_SIGN (enc != sign key) not set."
|
1842
|
+
GPG_USEAGENT="--use-agent"
|
1843
|
+
fi
|
1844
|
+
fi
|
1845
|
+
|
1846
|
+
# end GPG config plausibility check2
|
1847
|
+
fi
|
1848
|
+
|
1849
|
+
# config plausibility check - SPACE ###########################################
|
1850
|
+
|
1851
|
+
# is tmp is a folder
|
1852
|
+
CMD_MSG="Checking TEMP_DIR '${TEMP_DIR}' is a folder"
|
1853
|
+
run_cmd test -d "$TEMP_DIR"
|
1854
|
+
if [ "$?" != "0" ]; then
|
1855
|
+
error "Temporary file space '$TEMP_DIR' is not a directory."
|
1856
|
+
fi
|
1857
|
+
# is tmp writeable
|
1858
|
+
CMD_MSG="Checking TEMP_DIR '${TEMP_DIR}' is writable"
|
1859
|
+
run_cmd test -w "$TEMP_DIR"
|
1860
|
+
if [ "$?" != "0" ]; then
|
1861
|
+
error "Temporary file space '$TEMP_DIR' not writable."
|
1862
|
+
fi
|
1863
|
+
|
1864
|
+
|
1865
|
+
# get volsize, default duplicity volume size is 25MB since v0.5.07
|
1866
|
+
VOLSIZE=${VOLSIZE:-25}
|
1867
|
+
# double if asynch is on
|
1868
|
+
echo $@ $DUPL_PARAMS | grep -q -e '--asynchronous-upload' && FACTOR=2 || FACTOR=1
|
1869
|
+
|
1870
|
+
# TODO: check for enough (async= upload space and WARN only
|
1871
|
+
# use function tmp_space
|
1872
|
+
echo TODO: reimplent tmp space check
|
1873
|
+
|
1874
|
+
|
1875
|
+
# test - GPG SANITY #####################################################################
|
1876
|
+
# if encryption is disabled, skip this whole section
|
1877
|
+
if gpg_disabled; then
|
1878
|
+
echo -e "Test - En/Decryption skipped. (GPG disabled)"
|
1879
|
+
elif [ "$GPG_TEST" = "disabled" ]; then
|
1880
|
+
echo -e "Test - En/Decryption skipped. (Testing disabled)"
|
1881
|
+
else
|
1882
|
+
|
1883
|
+
GPG_TEST="$TEMP_DIR/${ME_NAME}.$$.$(date_fix %s)"
|
1884
|
+
function cleanup_gpgtest {
|
1885
|
+
echo -en "Cleanup - Delete '${GPG_TEST}_*'"
|
1886
|
+
rm ${GPG_TEST}_* 2>/dev/null && echo "(OK)" || echo "(FAILED)"
|
1887
|
+
}
|
1888
|
+
|
1889
|
+
# signing enabled?
|
1890
|
+
if gpg_signing; then
|
1891
|
+
CMD_PARAM_SIGN="--sign --default-key $(qw ${GPG_KEY_SIGN})"
|
1892
|
+
CMD_MSG_SIGN="Sign with '${GPG_KEY_SIGN}'"
|
1893
|
+
fi
|
1894
|
+
|
1895
|
+
# using keys
|
1896
|
+
if [ ${#GPG_KEYS_ENC_ARRAY[@]} -gt 0 ]; then
|
1897
|
+
|
1898
|
+
for KEY_ID in "${GPG_KEYS_ENC_ARRAY[@]}"; do
|
1899
|
+
CMD_PARAMS="$CMD_PARAMS -r $(qw ${KEY_ID})"
|
1900
|
+
done
|
1901
|
+
# check encrypting
|
1902
|
+
CMD_MSG="Test - Encrypt to '$(join "','" "${GPG_KEYS_ENC_ARRAY[@]}")'${CMD_MSG_SIGN:+ & $CMD_MSG_SIGN}"
|
1903
|
+
run_cmd $(gpg_pass_pipein GPG_PW_SIGN GPG_PW) $GPG $CMD_PARAM_SIGN $(gpg_param_passwd GPG_PW_SIGN GPG_PW) $CMD_PARAMS $GPG_USEAGENT --status-fd 1 $GPG_OPTS -o "${GPG_TEST}_ENC" -e "$ME_LONG"
|
1904
|
+
CMD_ERR=$?
|
1905
|
+
|
1906
|
+
if [ "$CMD_ERR" != "0" ]; then
|
1907
|
+
KEY_NOTRUST=$(echo "$CMD_OUT"|awk '/^\[GNUPG:\] INV_RECP 10/ { print $4 }')
|
1908
|
+
[ -n "$KEY_NOTRUST" ] && HINT="Key '${KEY_NOTRUST}' seems to be untrusted. If you really trust this key try to
|
1909
|
+
'gpg --edit-key "$KEY_NOTRUST"' and raise the trust level to ultimate. If you
|
1910
|
+
can trust all of your keys set GPG_OPTS='--trust-model always' in conf file."
|
1911
|
+
error_gpg_test "Encryption failed (Code $CMD_ERR).${CMD_OUT:+\n$CMD_OUT}" "$HINT"
|
1912
|
+
fi
|
1913
|
+
|
1914
|
+
# check decrypting
|
1915
|
+
CMD_MSG="Test - Decrypt"
|
1916
|
+
gpg_key_decryptable || CMD_DISABLED="No matching secret key available."
|
1917
|
+
run_cmd $(gpg_pass_pipein GPG_PW) "$GPG" $(gpg_param_passwd GPG_PW) $GPG_OPTS -o "${GPG_TEST}_DEC" $GPG_USEAGENT -d "${GPG_TEST}_ENC"
|
1918
|
+
CMD_ERR=$?
|
1919
|
+
|
1920
|
+
if [ "$CMD_ERR" != "0" ]; then
|
1921
|
+
error_gpg_test "Decryption failed.${CMD_OUT:+\n$CMD_OUT}"
|
1922
|
+
fi
|
1923
|
+
|
1924
|
+
# symmetric only
|
1925
|
+
else
|
1926
|
+
# check encrypting
|
1927
|
+
CMD_MSG="Test - Encryption with passphrase${CMD_MSG_SIGN:+ & $CMD_MSG_SIGN}"
|
1928
|
+
run_cmd $(gpg_pass_pipein GPG_PW) "$GPG" $GPG_OPTS $CMD_PARAM_SIGN --passphrase-fd 0 -o "${GPG_TEST}_ENC" --batch -c "$ME_LONG"
|
1929
|
+
CMD_ERR=$?
|
1930
|
+
if [ "$CMD_ERR" != "0" ]; then
|
1931
|
+
error_gpg_test "Encryption failed.${CMD_OUT:+\n$CMD_OUT}"
|
1932
|
+
fi
|
1933
|
+
|
1934
|
+
# check decrypting
|
1935
|
+
CMD_MSG="Test - Decryption with passphrase"
|
1936
|
+
run_cmd $(gpg_pass_pipein GPG_PW) "$GPG" $GPG_OPTS --passphrase-fd 0 -o "${GPG_TEST}_DEC" --batch -d "${GPG_TEST}_ENC"
|
1937
|
+
CMD_ERR=$?
|
1938
|
+
if [ "$CMD_ERR" != "0" ]; then
|
1939
|
+
error_gpg_test "Decryption failed.${CMD_OUT:+\n$CMD_OUT}"
|
1940
|
+
fi
|
1941
|
+
fi
|
1942
|
+
|
1943
|
+
# compare original w/ decryptginal
|
1944
|
+
CMD_MSG="Test - Compare"
|
1945
|
+
[ -r "${GPG_TEST}_DEC" ] || CMD_DISABLED="File not found. Nothing to compare."
|
1946
|
+
run_cmd "test \"\$(cat '$ME_LONG')\" = \"\$(cat '${GPG_TEST}_DEC')\""
|
1947
|
+
CMD_ERR=$?
|
1948
|
+
if [ "$CMD_ERR" = "0" ]; then
|
1949
|
+
cleanup_gpgtest
|
1950
|
+
else
|
1951
|
+
error_gpg_test "Comparision failed.${CMD_OUT:+\n$CMD_OUT}"
|
1952
|
+
fi
|
1953
|
+
|
1954
|
+
fi # end disabled
|
1955
|
+
|
1956
|
+
## an empty line
|
1957
|
+
#echo
|
1958
|
+
|
1959
|
+
# Exclude file is needed, create it if necessary
|
1960
|
+
[ -f "$EXCLUDE" ] || touch "$EXCLUDE"
|
1961
|
+
|
1962
|
+
# export only used keys, if bkp not already exists ######################################
|
1963
|
+
gpg_export_if_needed "${GPG_KEYS_ENC_ARRAY[@]}" "$(gpg_signing && echo $GPG_KEY_SIGN)"
|
1964
|
+
|
1965
|
+
|
1966
|
+
# command execution #####################################################################
|
1967
|
+
|
1968
|
+
# urldecode url vars into plain text
|
1969
|
+
var_isset 'TARGET_URL_USER' && TARGET_URL_USER="$(url_decode "$TARGET_URL_USER")"
|
1970
|
+
var_isset 'TARGET_URL_PASS' && TARGET_URL_PASS="$(url_decode "$TARGET_URL_PASS")"
|
1971
|
+
|
1972
|
+
# defined TARGET_USER&PASS vars replace their URL pendants
|
1973
|
+
# (double defs already dealt with)
|
1974
|
+
var_isset 'TARGET_USER' && TARGET_URL_USER="$TARGET_USER"
|
1975
|
+
var_isset 'TARGET_PASS' && TARGET_URL_PASS="$TARGET_PASS"
|
1976
|
+
|
1977
|
+
# build target backend data depending on protocol
|
1978
|
+
case "$(tolower "${TARGET_URL_PROT%%:*}")" in
|
1979
|
+
's3'|'s3+http')
|
1980
|
+
BACKEND_PARAMS="AWS_ACCESS_KEY_ID='${TARGET_URL_USER}' AWS_SECRET_ACCESS_KEY='${TARGET_URL_PASS}'"
|
1981
|
+
BACKEND_URL="${TARGET_URL_PROT}${TARGET_URL_HOSTPATH}"
|
1982
|
+
;;
|
1983
|
+
'gs')
|
1984
|
+
BACKEND_PARAMS="GS_ACCESS_KEY_ID='${TARGET_URL_USER}' GS_SECRET_ACCESS_KEY='${TARGET_URL_PASS}'"
|
1985
|
+
BACKEND_URL="${TARGET_URL_PROT}${TARGET_URL_HOSTPATH}"
|
1986
|
+
;;
|
1987
|
+
'cf+http')
|
1988
|
+
# respect potentially set cloudfile env vars
|
1989
|
+
var_isset 'CLOUDFILES_USERNAME' && TARGET_URL_USER="$CLOUDFILES_USERNAME"
|
1990
|
+
var_isset 'CLOUDFILES_APIKEY' && TARGET_URL_PASS="$CLOUDFILES_APIKEY"
|
1991
|
+
# add them to duplicity params
|
1992
|
+
var_isset 'TARGET_URL_USER' && \
|
1993
|
+
BACKEND_PARAMS="CLOUDFILES_USERNAME=$(qw "${TARGET_URL_USER}")"
|
1994
|
+
var_isset 'TARGET_URL_PASS' && \
|
1995
|
+
BACKEND_PARAMS="$BACKEND_PARAMS CLOUDFILES_APIKEY=$(qw "${TARGET_URL_PASS}")"
|
1996
|
+
BACKEND_URL="${TARGET_URL_PROT}${TARGET_URL_HOSTPATH}"
|
1997
|
+
# info on missing AUTH_URL
|
1998
|
+
if ! var_isset 'CLOUDFILES_AUTHURL'; then
|
1999
|
+
echo -e "INFO: No CLOUDFILES_AUTHURL defined (in conf).\n Will use default from python-cloudfiles (probably rackspace)."
|
2000
|
+
else
|
2001
|
+
BACKEND_PARAMS="$BACKEND_PARAMS CLOUDFILES_AUTHURL=$(qw "${CLOUDFILES_AUTHURL}")"
|
2002
|
+
fi
|
2003
|
+
;;
|
2004
|
+
'file'|'tahoe'|'dpbx')
|
2005
|
+
BACKEND_URL="${TARGET_URL_PROT}${TARGET_URL_HOSTPATH}"
|
2006
|
+
;;
|
2007
|
+
'swift')
|
2008
|
+
BACKEND_URL="${TARGET_URL_PROT}${TARGET_URL_HOSTPATH}"
|
2009
|
+
# respect possibly set swift env vars
|
2010
|
+
var_isset 'SWIFT_USERNAME' && TARGET_URL_USER="$SWIFT_USERNAME"
|
2011
|
+
var_isset 'SWIFT_PASSWORD' && TARGET_URL_PASS="$SWIFT_PASSWORD"
|
2012
|
+
# add them to duplicity params like with cloudfile to make it look standardized
|
2013
|
+
var_isset 'TARGET_URL_USER' && \
|
2014
|
+
BACKEND_PARAMS="$BACKEND_PARAMS SWIFT_USERNAME=$(qw "${TARGET_URL_USER}")"
|
2015
|
+
var_isset 'SWIFT_AUTHURL' && \
|
2016
|
+
BACKEND_PARAMS="$BACKEND_PARAMS SWIFT_AUTHURL=$(qw "${SWIFT_AUTHURL}")"
|
2017
|
+
( var_isset 'TARGET_URL_USER' && ! var_isset 'SWIFT_AUTHURL' ) &&\
|
2018
|
+
warning "\
|
2019
|
+
Swift will probably fail because the conf var SWIFT_AUTHURL was not defined!"
|
2020
|
+
var_isset 'SWIFT_AUTHVERSION' && \
|
2021
|
+
BACKEND_PARAMS="$BACKEND_PARAMS SWIFT_AUTHVERSION=$(qw "${SWIFT_AUTHVERSION}")"
|
2022
|
+
var_isset 'TARGET_URL_PASS' && \
|
2023
|
+
BACKEND_PARAMS="$BACKEND_PARAMS SWIFT_PASSWORD=$(qw "${TARGET_URL_PASS}")"
|
2024
|
+
;;
|
2025
|
+
'rsync')
|
2026
|
+
# everything in url (this backend does not support pass in env var)
|
2027
|
+
# this is obsolete from version 0.6.10 (buggy), hopefully fixed in 0.6.11
|
2028
|
+
# print warning older version is detected
|
2029
|
+
var_isset 'TARGET_URL_USER' && BACKEND_CREDS="$(url_encode "${TARGET_URL_USER}")"
|
2030
|
+
if duplicity_version_lt 610; then
|
2031
|
+
warning "\
|
2032
|
+
Duplicity version '$DUPL_VERSION' does not support providing the password as
|
2033
|
+
env var for rsync backend. For security reasons you should consider to
|
2034
|
+
update to a version greater than '0.6.10' of duplicity."
|
2035
|
+
var_isset 'TARGET_URL_PASS' && BACKEND_CREDS="${BACKEND_CREDS}:$(url_encode "${TARGET_URL_PASS}")"
|
2036
|
+
else
|
2037
|
+
var_isset 'TARGET_URL_PASS' && BACKEND_PARAMS="FTP_PASSWORD=$(qw "${TARGET_URL_PASS}")"
|
2038
|
+
fi
|
2039
|
+
var_isset 'BACKEND_CREDS' && BACKEND_CREDS="${BACKEND_CREDS}@"
|
2040
|
+
BACKEND_URL="${TARGET_URL_PROT}${BACKEND_CREDS}${TARGET_URL_HOSTPATH}"
|
2041
|
+
;;
|
2042
|
+
*)
|
2043
|
+
# for all other protocols we put username in url and pass into env var
|
2044
|
+
# for sec˙rity reasons, we url_encode username to protect special chars
|
2045
|
+
var_isset 'TARGET_URL_USER' &&
|
2046
|
+
BACKEND_CREDS="$(url_encode "${TARGET_URL_USER}")@"
|
2047
|
+
# sortout backends with special ways to handle password
|
2048
|
+
case "$(tolower "${TARGET_URL_PROT%%:*}")" in
|
2049
|
+
'imap'|'imaps')
|
2050
|
+
var_isset 'TARGET_URL_PASS' && BACKEND_PARAMS="IMAP_PASSWORD=$(qw "${TARGET_URL_PASS}")"
|
2051
|
+
;;
|
2052
|
+
'ssh'|'sftp'|'scp')
|
2053
|
+
# ssh backend wants to be told that theres a pass to use
|
2054
|
+
var_isset 'TARGET_URL_PASS' && \
|
2055
|
+
DUPL_PARAMS="$DUPL_PARAMS --ssh-askpass" && \
|
2056
|
+
BACKEND_PARAMS="FTP_PASSWORD=$(qw "${TARGET_URL_PASS}")"
|
2057
|
+
;;
|
2058
|
+
*)
|
2059
|
+
# rest uses FTP_PASS var
|
2060
|
+
var_isset 'TARGET_URL_PASS' && \
|
2061
|
+
BACKEND_PARAMS="FTP_PASSWORD=$(qw "${TARGET_URL_PASS}")"
|
2062
|
+
;;
|
2063
|
+
esac
|
2064
|
+
BACKEND_URL="${TARGET_URL_PROT}${BACKEND_CREDS}${TARGET_URL_HOSTPATH}"
|
2065
|
+
;;
|
2066
|
+
esac
|
2067
|
+
|
2068
|
+
# protect eval from special chars in url (e.g. open ')' in password,
|
2069
|
+
# spaces in path, quotes) happens above in duplify() via quotewrap()
|
2070
|
+
SOURCE="$SOURCE"
|
2071
|
+
BACKEND_URL="$BACKEND_URL"
|
2072
|
+
EXCLUDE="$EXCLUDE"
|
2073
|
+
|
2074
|
+
# replace magic separators to condition command equivalents (+=and,-=or)
|
2075
|
+
cmds=$(awk -v cmds="$cmds" "BEGIN{ gsub(/\+/,\"_and_\",cmds); gsub(/\-/,\"_or_\",cmds); print cmds}")
|
2076
|
+
# convert cmds to array, lowercase for safety
|
2077
|
+
CMDS=( $(awk "BEGIN{ cmds=tolower(\"$cmds\"); gsub(/_/,\" \",cmds); print cmds }") )
|
2078
|
+
|
2079
|
+
# run cmds
|
2080
|
+
for cmd in ${CMDS[*]};
|
2081
|
+
do
|
2082
|
+
|
2083
|
+
## init
|
2084
|
+
# raise index in cmd array for pre/post param
|
2085
|
+
var_isset 'CMD_NO' && CMD_NO=$((++CMD_NO)) || CMD_NO=0
|
2086
|
+
|
2087
|
+
# deal with condition "commands"
|
2088
|
+
unset SKIP_NOW
|
2089
|
+
if var_isset 'CMD_SKIP' && [ $CMD_SKIP -gt 0 ]; then
|
2090
|
+
echo -e "\n--- Skipping command $(toupper $cmd) ! ---"
|
2091
|
+
CMD_SKIP=$(($CMD_SKIP - 1))
|
2092
|
+
SKIP_NOW="yes"
|
2093
|
+
elif [ "$cmd" == 'and' ] && [ "$CMD_ERR" -ne "0" ]; then
|
2094
|
+
CMD_SKIP=1
|
2095
|
+
SKIP_NOW="yes"
|
2096
|
+
elif [ "$cmd" == 'or' ] && [ "$CMD_ERR" -eq "0" ]; then
|
2097
|
+
CMD_SKIP=1
|
2098
|
+
SKIP_NOW="yes"
|
2099
|
+
elif [ "$cmd" == 'and' ] || [ "$cmd" == 'or' ]; then
|
2100
|
+
unset 'CMD_SKIP';
|
2101
|
+
SKIP_NOW="yes"
|
2102
|
+
fi
|
2103
|
+
|
2104
|
+
# sum up how many commands we skip and actually skip
|
2105
|
+
if [ -n "$SKIP_NOW" ]; then
|
2106
|
+
CMD_SKIPPED=$((${CMD_SKIPPED-0} + 1))
|
2107
|
+
continue
|
2108
|
+
fi
|
2109
|
+
|
2110
|
+
# get prev/nextcmd vars
|
2111
|
+
nextno=$(($CMD_NO+1))
|
2112
|
+
[ "$nextno" -lt "${#CMDS[@]}" ] && CMD_NEXT=${CMDS[$nextno]} || CMD_NEXT='END'
|
2113
|
+
# get previous command minus skipped commands
|
2114
|
+
prevno=$(( $CMD_NO - ${CMD_SKIPPED-0} - 1 )); unset CMD_SKIPPED
|
2115
|
+
[ "$prevno" -ge 0 ] && CMD_PREV=${CMDS[$prevno]} || CMD_PREV='START'
|
2116
|
+
|
2117
|
+
# export some useful env vars for external scripts/programs to use
|
2118
|
+
export CONFDIR SOURCE TARGET_URL_PROT TARGET_URL_HOSTPATH \
|
2119
|
+
TARGET_URL_USER TARGET_URL_PASS \
|
2120
|
+
GPG_KEYS_ENC=$(join "\n" "${GPG_KEYS_ENC_ARRAY[@]}") GPG_KEY_SIGN \
|
2121
|
+
GPG_PW CMD_PREV CMD_NEXT CMD_ERR
|
2122
|
+
|
2123
|
+
# save start time
|
2124
|
+
RUN_START=$(date_fix %s)$(nsecs)
|
2125
|
+
# user info
|
2126
|
+
echo; separator "Start running command $(toupper $cmd) at $(date_from_nsecs $RUN_START)"
|
2127
|
+
|
2128
|
+
case "$(tolower $cmd)" in
|
2129
|
+
'pre'|'post')
|
2130
|
+
if [ "$cmd" == 'pre' ]; then
|
2131
|
+
script=$PRE
|
2132
|
+
else
|
2133
|
+
script=$POST
|
2134
|
+
fi
|
2135
|
+
# script execution in a subshell, protect us from failures/var overwrites
|
2136
|
+
( run_script "$script" )
|
2137
|
+
;;
|
2138
|
+
'bkp')
|
2139
|
+
duplify -- "${dupl_opts[@]}" --exclude-globbing-filelist "$EXCLUDE" \
|
2140
|
+
"$SOURCE" "$BACKEND_URL"
|
2141
|
+
;;
|
2142
|
+
'incr')
|
2143
|
+
duplify incr -- "${dupl_opts[@]}" --exclude-globbing-filelist "$EXCLUDE" \
|
2144
|
+
"$SOURCE" "$BACKEND_URL"
|
2145
|
+
;;
|
2146
|
+
'full')
|
2147
|
+
duplify full -- "${dupl_opts[@]}" --exclude-globbing-filelist "$EXCLUDE" \
|
2148
|
+
"$SOURCE" "$BACKEND_URL"
|
2149
|
+
;;
|
2150
|
+
'verify')
|
2151
|
+
TIME="${ftpl_pars[0]:+"-t ${ftpl_pars[0]}"}"
|
2152
|
+
duplify verify -- $TIME "${dupl_opts[@]}" --exclude-globbing-filelist "$EXCLUDE" \
|
2153
|
+
"$BACKEND_URL" "$SOURCE"
|
2154
|
+
;;
|
2155
|
+
'verifypath')
|
2156
|
+
TIME="${ftpl_pars[2]:+"-t ${ftpl_pars[2]}"}"
|
2157
|
+
IN_PATH="${ftpl_pars[0]}"; OUT_PATH="${ftpl_pars[1]}";
|
2158
|
+
( [ -z "$IN_PATH" ] || [ -z "$OUT_PATH" ] ) && error " Missing parameter <rel_bkp_path> or <local_path> for verifyPath.
|
2159
|
+
|
2160
|
+
Hint:
|
2161
|
+
Syntax is -> $ME <profile> verifyPath <rel_bkp_path> <local_path> [<age>]"
|
2162
|
+
|
2163
|
+
duplify verify -- $TIME "${dupl_opts[@]}" --exclude-globbing-filelist "$EXCLUDE" \
|
2164
|
+
--file-to-restore "$IN_PATH" "$BACKEND_URL" "$OUT_PATH"
|
2165
|
+
;;
|
2166
|
+
'list')
|
2167
|
+
# time param exists since 0.5.10+
|
2168
|
+
TIME="${ftpl_pars[0]:+"-t ${ftpl_pars[0]}"}"
|
2169
|
+
duplify list-current-files -- $TIME "${dupl_opts[@]}" "$BACKEND_URL"
|
2170
|
+
;;
|
2171
|
+
'cleanup')
|
2172
|
+
duplify cleanup -- "${dupl_opts[@]}" "$BACKEND_URL"
|
2173
|
+
;;
|
2174
|
+
'purge')
|
2175
|
+
MAX_AGE=${ftpl_pars[0]:-$MAX_AGE}
|
2176
|
+
[ -z "$MAX_AGE" ] && error " Missing parameter <max_age>. Can be set in profile or as command line parameter."
|
2177
|
+
|
2178
|
+
duplify remove-older-than "${MAX_AGE}" \
|
2179
|
+
-- "${dupl_opts[@]}" "$BACKEND_URL"
|
2180
|
+
;;
|
2181
|
+
'purgefull')
|
2182
|
+
MAX_FULL_BACKUPS=${ftpl_pars[0]:-$MAX_FULL_BACKUPS}
|
2183
|
+
[ -z "$MAX_FULL_BACKUPS" ] && error " Missing parameter <max_full_backups>. Can be set in profile or as command line parameter."
|
2184
|
+
|
2185
|
+
duplify remove-all-but-n-full "${MAX_FULL_BACKUPS}" \
|
2186
|
+
-- "${dupl_opts[@]}" "$BACKEND_URL"
|
2187
|
+
;;
|
2188
|
+
'purgeincr')
|
2189
|
+
MAX_FULLS_WITH_INCRS=${ftpl_pars[0]:-$MAX_FULLS_WITH_INCRS}
|
2190
|
+
[ -z "$MAX_FULLS_WITH_INCRS" ] && error " Missing parameter <max_fulls_with_incrs>. Can be set in profile or as command line parameter."
|
2191
|
+
|
2192
|
+
duplify remove-all-inc-of-but-n-full "${MAX_FULLS_WITH_INCRS}" \
|
2193
|
+
-- "${dupl_opts[@]}" "$BACKEND_URL"
|
2194
|
+
;;
|
2195
|
+
'restore')
|
2196
|
+
OUT_PATH="${ftpl_pars[0]:-$SOURCE}"; TIME="${ftpl_pars[1]:-now}";
|
2197
|
+
[ -z "$OUT_PATH" ] && error " Missing parameter target_path for restore.
|
2198
|
+
|
2199
|
+
Hint:
|
2200
|
+
Syntax is -> $ME <profile> restore <target_path> [<age>]"
|
2201
|
+
|
2202
|
+
duplify -- -t "$TIME" "${dupl_opts[@]}" "$BACKEND_URL" "$OUT_PATH"
|
2203
|
+
run_script $CONFDIR/restore;
|
2204
|
+
;;
|
2205
|
+
'fetch')
|
2206
|
+
IN_PATH="${ftpl_pars[0]}"; OUT_PATH="${ftpl_pars[1]}";
|
2207
|
+
TIME="${ftpl_pars[2]:-now}";
|
2208
|
+
( [ -z "$IN_PATH" ] || [ -z "$OUT_PATH" ] ) && error " Missing parameter <src_path> or <target_path> for fetch.
|
2209
|
+
|
2210
|
+
Hint:
|
2211
|
+
Syntax is -> $ME <profile> fetch <src_path> <target_path> [<age>]"
|
2212
|
+
|
2213
|
+
# duplicity 0.4.7 doesnt like cmd restore in combination with --file-to-restore
|
2214
|
+
duplify -- --restore-time "$TIME" "${dupl_opts[@]}" \
|
2215
|
+
--file-to-restore "$IN_PATH" "$BACKEND_URL" "$OUT_PATH"
|
2216
|
+
;;
|
2217
|
+
'status')
|
2218
|
+
duplify collection-status -- "${dupl_opts[@]}" "$BACKEND_URL"
|
2219
|
+
;;
|
2220
|
+
*)
|
2221
|
+
warning "Unknown command '$cmd'."
|
2222
|
+
;;
|
2223
|
+
esac
|
2224
|
+
|
2225
|
+
CMD_ERR=$?
|
2226
|
+
RUN_END=$(date_fix %s)$(nsecs) ; RUNTIME=$(( $RUN_END - $RUN_START ))
|
2227
|
+
|
2228
|
+
# print message on error; set error code
|
2229
|
+
if [ "$CMD_ERR" -ne 0 ]; then
|
2230
|
+
error_print "$(datefull_from_nsecs $RUN_END) Task '$(echo $cmd|awk '$0=toupper($0)')' failed with exit code '$CMD_ERR'."
|
2231
|
+
FTPL_ERR=1
|
2232
|
+
fi
|
2233
|
+
|
2234
|
+
separator "Finished state $(error_to_string $CMD_ERR) at $(date_from_nsecs $RUN_END) - \
|
2235
|
+
Runtime $(printf "%02d:%02d:%02d.%03d" $((RUNTIME/1000000000/60/60)) $((RUNTIME/1000000000/60%60)) $((RUNTIME/1000000000%60)) $((RUNTIME/1000000%1000)) )"
|
2236
|
+
|
2237
|
+
done
|
2238
|
+
|
2239
|
+
exit ${FTPL_ERR}
|
2240
|
+
{% endraw %}
|