blob: 1c3e8cad31ccb07b5388e5d71e2664fe94fce5cb (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
|
#!/usr/bin/env zsh
self_name=$0
default_cfg='/etc/backup.zsh.cfg'
default_postfix=$(date +%F-%H%M)
default_ftp_port='21'
default_ssh_port='22'
function err
{
[[ -n $1 ]] && echo $1 >&2
}
function cfg_err
{
[[ -n $1 ]] && echo "$1 is not set in configuration, but is required by $self_name to work." >&2
}
function usage
{
echo "usage: $self_name [--help|--conf /path/to/config]
--help -h show this message
--conf -c use config from the specified path
Default config path $default_cfg will be used if invoked without options"
}
# function to read the configuration file and spit out some exceptions if stuff is missing
function apply_config
{
function test_remote_settings
{
if [[ -z $remote_host ]]; then
cfg_err 'remote_host'
return 5
fi
if [[ -z $remote_user ]]; then
cfg_err 'remote_user'
return 5
fi
if [[ -z $remote_pass ]]; then
cfg_err 'remote_pass'
return 5
fi
if [[ -n $port && ! $port =~ ^[0-9]+$ ]]; then
err 'Remote port is not a numeric value.'
return 5
fi
}
source $cfg || { err "Config file $cfg is unreadable or does not exist"; return 15 }
if [[ -z $source_dirs ]]; then
cfg_err 'source_dirs'
return 5
fi
if [[ -z $backup_dir && $protocol != 'ssh' ]]; then
cfg_err 'backup_dir'
return 5
fi
if [[ -z $local_host ]]; then
err 'local_host is not set, using hostname.'
local_host=$HOST
fi
# date postfix
if [[ -z $outfile_postfix ]]; then
postfix=$default_postfix
else
postfix=$outfile_postfix
fi
case $protocol in
('ftp'|'ftps') port=${remote_port:-$default_ftp_port}; test_remote_settings; return $?;;
('sftp'|'ssh') port=${remote_port:-$default_ssh_port}; test_remote_settings; return $?;;
('local') unset remote_port;;
(*) cfg_err 'protocol'; return 5;;
esac
case $compress_format in
('xz') compress_flag='J' ;;
('bz2') compress_flag='j' ;;
('gz') compress_flag='z' ;;
('') unset compress_flag; unset compress_format ;;
(*) err "$compress_format is not a valid value for the compression format option."; return 5;;
esac
if [[ -n $exclude_list ]]; then
if [[ -r $exclude_list ]]; then
exclude_option='-X'
else
err "Exclusion list $exclude_list is either unreadable or does not exist. Proceeding without it."
fi
fi
}
# generate the full backup path
function generate_fullpath
{
local backup_type
# increment or full backup
if [[ -s $snapshot_file ]]; then
backup_type='incr'
else
backup_type='full'
fi
if [[ -z $backup_filename ]]; then
outfile="$backup_dir/${local_host}-${src_basename}_${postfix}_${backup_type}.t${compress_format:-'ar'}"
else
outfile=$backup_filename
fi
}
function compress # compress to stdout
{
if [[ -n $snapshot_file ]]; then
if printf '' >> $snapshot_file; then
snapshot_option='-g'
else
err "Snapshot file $snapshot_file cannot be written. Proceeding with full backup."
fi
fi
tar -c$compress_flag $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read -C $src_basedir $src_basename
}
function store # store to local or remote
{
case $protocol in
('local') dd of=$outfile ;;
('ssh') ssh -p$port $remote_user@$remote_host "dd of=$outfile" ;;
('sftp'|'ftp'|'ftps') curl -ksS -T - $protocol://$remote_host:$port/$outfile -u $remote_user:$remote_pass ;;
esac
}
function parse_opts
{
while [[ -n $1 ]]; do
case $1 in
('--help'|'-h') usage; exit 0;;
('--conf'|'-c') shift; opt_cfg=$1; shift;;
('') opt_cfg=$default_cfg;;
(*) err "unknown parameter $1"; exit 127;;
esac
done
}
function main
{
parse_opts $@
cfg=${opt_cfg:-$default_cfg}
apply_config
local apply_config_returns=$?
[[ $apply_config_returns -ne 0 ]] && return $apply_config_returns
for i in $source_dirs; do
unset src_basename src_basedir outfile
IFS=':' read source_dir snapshot_file <<< $i
src_basename=${source_dir:t}
src_basedir=${source_dir:h}
generate_fullpath
echo "Creating a backup of $source_dir. Using protocol $protocol to store it in $outfile."
compress | store
done
return 0
}
main $@
|