blob: 77a89803be2e774a317a6521d490786a2d74453b (
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
|
#!/usr/bin/env zsh
self_name=$0
default_cfg='/usr/local/etc/backup.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 $0 to work." >&2
}
function usage
{
echo "usage: $self_name [--help|--config]
--help -h - show this message
--config -c - use config from the specified path
Default config path /usr/local/etc/backup.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
{
source $cfg || err 15 'Config file does not exist'
if [[ -z $source_dirs ]]; then
cfg_err 'Backup source'
exit 5
fi
if [[ -z $remote_host ]]; then
cfg_err 'Remote host'
exit 5
fi
if [[ -z $backup_dir && $protocol != 'ssh' ]]; then
cfg_err 'Target directory'
exit 5
fi
if [[ -z $local_host ]]; then
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};;
('sftp'|'ssh') port=${remote_port:-$default_ssh_port};;
('local') unset remote_port;;
(*) cfg_err 'Backup protocol'; exit 5;;
esac
if [[ ! $remote_port =~ ^[0-9]+$ ]]; then
err 'Remote port is not a numeric value.'
fi
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."; exit 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."
fi
fi
if [[ -n $snap_file ]]; then
if printf '' >> $snap_file; then
snapshot_option='-g'
else
err "Snapshot file $snap_file cannot be written."
fi
fi
}
# generate the full backup path
function generate_fullpath
{
local backup_type
# increment or full backup
if [[ -s $snap_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
{
tar cf$compress_flag - -C $src_basedir $src_basename $snapshot_option $snapshot_file $exclude_option $exclude_list --ignore-failed-read
}
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; return 0;;
('--config'|'-c') shift; opt_cfg=$1; shift;;
esac
done
}
function main
{
parse_opts
if [[ -z $opt_cfg ]]; then
cfg=$default_cfg
else
cfg=$opt_cfg
fi
apply_config
for i in $source_dirs; do
unset src_basename src_basedir outfile
IFS=':' read source_dir snap_file <<< $i
src_basename=${source_dir:t}
src_basedir=${source_dir:h}
generate_fullpath
compress | store
done
return 0
}
main $@
|