-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathgeoip-shell-cronsetup.sh
326 lines (259 loc) · 7.68 KB
/
geoip-shell-cronsetup.sh
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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#!/bin/sh
# shellcheck disable=SC2015,SC2317,SC2154,SC2086,SC1090,SC2034
# geoip-shell-cronsetup.sh
# Copyright: antonk ([email protected])
# github.com/friendly-bits
#### Initial setup
p_name="geoip-shell"
script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)
geoinit="${p_name}-geoinit.sh"
for geoinit_path in "$script_dir/$geoinit" "/usr/bin/$geoinit"; do
[ -f "$geoinit_path" ] && break
done
. "$geoinit_path" || exit 1
nolog=1
#### USAGE
usage() {
cat <<EOF
Usage: $me [-x <"expression">] [-d] [-V] [-h]
Validates a cron expression, or loads cron-related config from the config file and sets up cron jobs for geoip blocking accordingly.
Options:
-x <"expr"> : validate cron expression
-d : Debug
-V : Version
-h : This help
EOF
}
val_cron_exp() {
tolower sourceline "$1"
# Functions
reg_err() {
err=1
errstr="$errstr$1$_nl"
}
print_tip() {
printf '%s\n%s\n%s\n' "Crontab expression format: 'minute hour day-of-month month day-of-week'." \
"Valid example: '15 4 * * 6'." \
"Use double quotes around your cron schedule expression." >&2
}
validateNum() {
num="$1"; min="$2"; max="$3"
case "$num" in
'*' ) return 0 ;;
''|*[!0-9]* ) return 1
esac
[ "$num" -le "$prevnum" ] && return 1
prevnum="$num"
return $(( num<min || num>max))
}
validateDay() {
eval "case \"$1\" in
$dow_values) abbr=1; return 0
esac"
return 1
}
validateMon() {
eval "case \"$1\" in
$mon_values) abbr=1; return 0
esac"
return 1
}
validateName() {
case "$1" in
"mon") validateMon "$2" ;;
"dow") validateDay "$2" ;;
*) return 1
esac
}
# validates a field of the cron schedule (month, day of month, day of week, hour or minute)
# 1 - field name
# 2 - string
# 3 - min val
# 4 - max val
validateField() {
invalid_char() { reg_err "Invalid value '$1' in field '$fieldName': it $2 with '$3'."; }
check_edge_chars() {
case "${1%"${1#?}"}" in "$2") invalid_char "$1" "starts" "$2"; esac
case "${1#"${1%?}"}" in "$2") invalid_char "$1" "ends" "$2"; esac
}
field_id="$1"
eval "fieldName=\"\$$1\""
fieldStr="$2"
minval="$3"
maxval="$4"
segnum_field=0
astnum_field=0
check_edge_chars "$fieldStr" ","
newifs ","
for slice in $fieldStr; do
check_edge_chars "$slice" "-"
segnum=0 prevnum=$((minval-1)) abbr=
IFS='-'
for segment in $slice; do
oldifs
# try validating the segment as a number or an asterisk
if ! validateNum "$segment" "$minval" "$maxval" ; then
# if that fails, try validating the segment as a name or an asterisk
if ! validateName "$field_id" "$segment"; then
# if that fails, the segment is invalid
eval "val_seg=\"\$${field_id}_values\""
[ "$val_seg" ] && val_seg=", $val_seg"
reg_err "Invalid segment '$segment' in field: $fieldName. Valid values: $minval-$maxval$val_seg."
fi
fi
# count dash-separated segments in a slice
segnum=$((segnum+1))
# count all segments in a field
segnum_field=$((segnum_field+1))
# count asterisks in a field
[ "$segment" = "*" ] && astnum_field=$((astnum_field+1))
done
[ "$segnum" -gt 2 ] || { [ "$segnum" -gt 1 ] && [ "$abbr" ]; } && reg_err "Invalid value '$slice' in $fieldName '$fieldStr'."
done
oldifs
# if a field contains an asterisk then there should be only one segment
case $(( astnum_field > 0 && segnum_field > 1 )) in 1)
reg_err "Invalid $fieldName '$fieldStr'."
esac
}
err=0
errstr=
mn=minute
hr=hour
dom="day of month"
mon=month
dow="day of week"
dow_values="sun|mon|tue|wed|thu|fri|sat|'*'"
mon_values="jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|'*'"
#### Basic sanity check for input args
# separate the input by spaces and store results in variables
set -- $sourceline
for field in mn_val hr_val dom_val mon_val dow_val; do
case "$1" in
'') echo; echolog -err "Not enough fields in schedule expression."
print_tip; die ;;
*) eval "$field"='$1'; shift
esac
done
# check for extra args
[ -n "$*" ] && {
echo; echolog -err "Too many fields in schedule expression."
print_tip
die
}
for field in "mn $mn_val 0 59" "hr $hr_val 0 23" "dom $dom_val 1 31" "mon $mon_val 1 12" "dow $dow_val 0 6"; do
set -- $field
validateField "$1" "$2" "$3" "$4"
done
[ $err != 0 ] && {
printf '\n\n%s\n%s\n\n' "$me: errors in cron expression:" "${errstr%"$_nl"}" >&2
print_tip
}
return $err
}
#### PARSE ARGUMENTS
while getopts ":x:dVh" opt; do
case $opt in
x) val_cron_exp "$OPTARG"; exit $? ;;
d) debugmode_arg=1 ;;
V) echo "$curr_ver"; exit 0 ;;
h) usage; exit 0 ;;
*) unknownopt
esac
done
shift $((OPTIND-1))
extra_args "$@"
is_root_ok
setdebug
debugentermsg
#### Functions
# 1 - job type (update|reboot)
# 2 - <[schedule]|@reboot>
check_cron_job() {
get_matching_line "$curr_cron" "*" "${p_name}-$1" "" curr_job
case "$curr_job" in "$2 $run_cmd"*) return 0; esac
return 1
}
get_curr_cron() {
crontab -u root -l 2>/dev/null
}
create_cron_job() {
job_type="$1" w_sch=
[ -z "$inbound_iplists$outbound_iplists" ] && {
rm_cron_job update
rm_cron_job persistence
echo
die 0 "Configured countries list is empty. Not creating cron jobs."
}
curr_cron="$(get_curr_cron)" || die "$FAIL read crontab."
case "$job_type" in
update)
[ -z "$schedule" ] && die "Cron schedule in the config file is empty!"
check_cron_job update "$schedule" && return 0
# Validate cron schedule
debugprint "\nValidating cron schedule: '$schedule'."
val_cron_exp "$schedule"; rv=$?
case "$rv" in
0)
debugprint "Successfully validated cron schedule: '$schedule'."
;;
*) die "$FAIL validate cron schedule '$schedule'."
esac
# Remove existing update cron job before creating new one
rm_cron_job update
curr_cron="$(get_curr_cron)" || die "$FAIL read crontab."
cron_cmd="$schedule $run_cmd update -a 1>/dev/null 2>/dev/null # ${p_name}-update"
w_sch=" with schedule '$schedule'" ;;
persistence)
check_cron_job persistence "@reboot" && return 0
# using the restore action for the *run script
cron_cmd="@reboot $run_cmd restore -a 1>/dev/null 2>/dev/null # ${p_name}-persistence" ;;
*) die "Unrecognized type of cron job: '$job_type'."
esac
#### Create new cron job
debugprint "Creating $job_type cron job$w_sch... "
printf '%s\n' "${curr_cron#"$_nl"}$_nl$cron_cmd" | crontab -u root - 1>/dev/null 2>/dev/null ||
die "$FAIL create $job_type cron job."
}
# remove existing cron job
# cron jobs are identified by the comment at the end of each job in crontab
rm_cron_job() {
job_type="$1"
case "$job_type" in
update|persistence) ;;
*) die "rm_cron_job: unknown cron job type '$job_type'."
esac
debugprint "Removing $job_type cron job for $p_name... "
curr_cron="$(get_curr_cron)" || die "$FAIL read crontab."
printf '%s\n' "$curr_cron" | grep -v "${p_name}-${job_type}" | crontab -u root - 1>/dev/null 2>/dev/null ||
die "$FAIL remove $job_type cron job."
}
#### Variables
for entry in schedule no_persist inbound_iplists outbound_iplists; do
getconfig "$entry"
done
run_cmd="$i_script-run.sh"
schedule="${schedule:-$default_schedule}"
printf_s "Processing cron jobs... "
#### Checks
persist_support=1
check_cron_compat || persist_support=
checkvars no_persist
#### Main
# update job
case "$schedule" in
disable) rm_cron_job update ;;
*) create_cron_job update
esac
# persistence job
[ ! "$_OWRTFW" ] && {
[ ! "$persist_support" ] && { rm_cron_job persistence; exit 0; }
case "$no_persist" in
false) create_cron_job persistence ;;
true) rm_cron_job persistence
echolog "${_nl}Note: no-persistence option was specified during installation. Geoip blocking will likely be deactivated upon reboot." \
"To enable persistence, install $p_name again without the '-n' option."
esac
}
OK
: