This repository was archived by the owner on Jan 1, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbash_backup
More file actions
286 lines (247 loc) · 7.9 KB
/
bash_backup
File metadata and controls
286 lines (247 loc) · 7.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#!/usr/bin/env bash
################################################################################
#: Title : bash_backup
#: Date : 2017-Nov-30
#: Author : Anthony Tilelli
#: Version : 1
#: Description : Runs backups defined in configuration file
################################################################################
# strict mode (http://redsymbol.net/articles/unofficial-bash-strict-mode/)
set -euo pipefail
# CONSTANTS
readonly GL_BACKUP_CONFIG="example_config/backup.config"
readonly GL_PRMG_CONFIG="example_config/prgm.config"
# FUNCTIONS
function fail() {
# Outputs error-message and force-quits script
local -r ERRORCODE="${1:-1}"
local -r ERROR_MESSAGE="${2:-"fail called but \"ERROR_MESSAGE\" not set"}"
printf "ERROR %d: %s\\n" "$ERRORCODE" "$ERROR_MESSAGE" >&2
exit "$ERRORCODE"
}
function validate_config_file() {
# Evaluates config files:
# - Readable/exist
# - ASCII text
# - Owned by script user or root
# - Writable by owner only.
# Return:
# Returns 0 or exits with failure
local -r USERID="$1" #UID of the user calling script
local -r CONFIG="$2" #File to evaluate
local config_user="" #config user owner
#Validates config files is readable by script user
if [[ -r "$CONFIG" ]]; then
#Is file ASCII text?
if [[ "$(file "$CONFIG")" =~ "ASCII" ]] ; then
#If file owned by root or USERID
config_user=$(stat -c %u "$CONFIG")
if (( config_user == 0 )) || (( config_user == "$USERID" )) ; then
#writable by owner only??
if [[ "$(stat -c %A "$CONFIG")" != -r???--?-- ]]; then
fail 10 \
"Wrong permissions on file: $CONFIG.(Is file write/executable by group/other?)"
fi
else
fail 9 \
"\"$CONFIG\" is not owned by root or $USERID (File owner: $config_user)"
fi
else
fail 8 "File \"$CONFIG\" is not ASCII text type"
fi
else
fail 7 \
"Unable to use file: \"$CONFIG\". (Does files exist and is it readable?)"
fi
return 0
}
function find_config() {
#Finds and processes config from given backup config file
local -r BID="$1" #assumes there are no #
local -r ACTION="$2"
local -r BCKP_CONFIG="$3" #assumed validated
#Return Variables (expected set)
# - cfg_Program
# - cfg_Arguments
# - cfg_Source
# - cfg_Backup
# - cfg_cd
# - cfg_UMASK
#checking if variable exists (prevent non-local variable creation in function)
local assert
assert=${cfg_Program?"cfg_Program is not set"}
assert=${cfg_Arguments?"cfg_Arguments is not set"}
assert=${cfg_Source?"cfg_Source is not set"}
assert=${cfg_Backup?"cfg_Backup is not set"}
assert=${cfg_cd? "cfg_cd is not set"}
assert=${cfg_UMASK? "cfg_UMASK is not set"}
local -r CONFIG_LINE=$(grep "^$BID:.*$ACTION" "$BCKP_CONFIG" | sed 's/\s*#.*//g')
#sed - removes comments from end of lines
local IFS=:
set -- $CONFIG_LINE
if (( ${#} == 7 )) || (( ${#} == 8 )) ; then
cfg_Program=$3
cfg_Arguments=$4
cfg_Source=$5
cfg_Backup=$6
cfg_cd=$7
cfg_UMASK=${8-""} #$8 may be unset, defaults to empty
elif ((${#} == 0)) ; then
fail 3 "No entries found for \"${BID}:${ACTION}\""
else
fail 4 \
"Entry \"${CONFIG_LINE}\" is incorrect or more then one line was found"
fi
return 0
}
function find_prgm(){
#Finds and processes config from given prgm config file
local -r PROGRAM="$1"
local -ru ACTION="$2"
local -r ARGUMENTS="$3"
local -r SOURCE="$4"
local -r BACKUP="$5"
local -r PRMG_CONFIG_FILE="$6"
#Return Variables (expected set)
# - backup_command
#checking if variable exists (prevent non-local variable creation in function)
local assert=${backup_command?"backup_command is not set"}
local -r CONFIG_LINE=$(grep "^$PROGRAM:.*$ACTION" "$PRMG_CONFIG_FILE" | sed 's/\s*#.*//g')
#sed - removes comments from end of lines
local -r NUM_OF_ENTRIES=$(grep -c "^$PROGRAM:.*$ACTION" "$PRMG_CONFIG_FILE")
if (( NUM_OF_ENTRIES == 1 )); then
local IFS=:
# word splitting intended
set -- $CONFIG_LINE
shift 2 #removing program and Action
backup_command="${PROGRAM} "
while (( $# > 0 )) ; do
case "${1}" in
"arguments") backup_command+="${ARGUMENTS} " ;;
"source") backup_command+="${SOURCE} " ;;
"backup") backup_command+="${BACKUP} " ;;
*) fail 6 "${1} is unknown" ;;
esac
shift
done
#remove end space
backup_command=${backup_command% }
else
fail 5 \
"${NUM_OF_ENTRIES} entries found in ${PRMG_CONFIG_FILE} for \"${PROGRAM}:${ACTION}\""
fi
}
function run_backup () {
# Change directory and umask, if needed, and runs backup
local -r BACKUP_COMMAND="$1" #Command to run backup (required)
local -r WORKING_DIR="${2:-/}"
#Directory to change to before running backup (default /)
local -r SET_UMASK="${3:-SKIP}"
#umask to set before running backup (default SKIP)
cd "$WORKING_DIR" || fail 20 "Could not cd to directory ${WORKING_DIR}"
case "$SET_UMASK" in #ensuring is a valid umask
SKIP) ;; #DO-NOTHING
[0-7] | [0-7][0-7] | [0-7][0-7][0-7] | [0-7][0-7][0-7][0-7] )
umask "${SET_UMASK}"
;;
*) fail 11 "umask is invalid (${SET_UMASK})" ;;
esac
$BACKUP_COMMAND
#script will terminate on program failure
}
function usage_short() {
#outputs usage
printf "%s -[ioRs]-[B <BID>]\\n" "${0##*/}"
}
function usage() {
#OUTPUT
#help information
usage_short
printf "\\nAction Parameters (choose one)\\n"
printf " -i Run INITIAL Action\\n"
printf " -o Run ONGOING Action\\n"
printf " -R Run RESTORE Action\\n"
printf " -s Run SHOW Action\\n"
printf " -h Prints help message and exits\\n"
printf "Backup ID\\n"
printf " -B sets backup ID\\n"
}
function user_input() {
#Processes user command line input
local bid=""
local action=""
#Return Variables (expected set)
# - cfg_BID
# - cfg_action
#checking if variable exists (prevent nonlocal variable creation in function)
local assert
assert=${cfg_BID?"cfg_BID is not set"}
assert=${cfg_action?"cfg_action is not set"}
local -i action_count=0
while getopts ":ioRshB:" opt; do
case $opt in
"i") action="INITIAL" && action_count+=1 ;;
"o") action="ONGOING" && action_count+=1 ;;
"R") action="RESTORE" && action_count+=1 ;;
"s") action="SHOW" && action_count+=1 ;;
"h") usage ; exit 0 ;;
"B")
if [[ -z "$cfg_BID" ]]; then
bid="${OPTARG}"
else
fail 16 "-B already set as ${bid}"
fi
;;
\?) fail 13 "Invalid argument entered -${OPTARG}" ;;
":") fail 14 "-${OPTARG} requires an argument" ;;
esac
done
shift $((OPTIND-1)) #remove process commad line options
if (( ${#} != 0 )) ; then
action="$*"
fail 15 "Unknown remaining command line argument (args: ${action} )"
fi
if (( action_count == 1 )) ; then
if [[ -z $bid ]] ; then
fail 17 "BID was never set"
elif [[ $bid =~ ^# ]] ; then
fail 18 "BID has # in it (${bid})"
fi
else
fail 12 "More or less then one action set (count: ${action_count})"
fi
cfg_BID="$bid"
cfg_action="$action"
return 0
}
function main() {
# Reads Global variables
# - GL_BACKUP_CONFIG
# - GL_PRMG_CONFIG
local cfg_BID=""
local -u cfg_action=""
local cfg_Program=""
local cfg_Arguments=""
local cfg_Source=""
local cfg_Backup=""
local cfg_cd=""
local cfg_UMASK=""
local backup_command=""
if (( $# == 0 )) ; then
usage_short
fail 19 "Command Line argument Missing"
else
user_input "$@"
fi
find_config "$cfg_BID" "$cfg_action" "$GL_BACKUP_CONFIG"
find_prgm "$cfg_Program" "$cfg_action" "$cfg_Arguments" "$cfg_Source" "$cfg_Backup" "$GL_PRMG_CONFIG"
run_backup "$backup_command" "$cfg_cd" "$cfg_UMASK"
}
if ((BASH_VERSINFO[0] > 4))
then
fail 21 "Bash-4.0+ is required to run this script"
fi
validate_config_file "$UID" "$GL_BACKUP_CONFIG"
validate_config_file "$UID" "$GL_PRMG_CONFIG"
main "$@"
exit 0