taperole 1.5.5 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 %}
|