1
|
#!/bin/bash
|
2
|
|
3
|
# Report SATA backplane usage
|
4
|
# * Report is created in /var/backup/sata_backplane_usage if different from
|
5
|
# the previous report
|
6
|
|
7
|
# Programmers' notes: function call tree
|
8
|
# +
|
9
|
# |
|
10
|
# +-- initialise
|
11
|
# |
|
12
|
# +-- generate_report
|
13
|
# |
|
14
|
# +-- finalise
|
15
|
#
|
16
|
# Utility functions called from various places:
|
17
|
# ck_file ck_uint fct msg
|
18
|
|
19
|
# Function definitions in alphabetical order. Execution begins after the last function definition.
|
20
|
|
21
|
#--------------------------
|
22
|
# Name: ck_file
|
23
|
# Purpose: for each file listed in the argument list: checks that it is
|
24
|
# * reachable and exists
|
25
|
# * is of the type specified (block special, ordinary file or directory)
|
26
|
# * has the requested permission(s) for the user
|
27
|
# * optionally, is absolute (begins with /)
|
28
|
# Usage: ck_file [ path <file_type>:<permissions>[:[a]] ] ...
|
29
|
# where
|
30
|
# file is a file name (path)
|
31
|
# file_type is b (block special file), f (file) or d (directory)
|
32
|
# permissions is none or more of r, w and x
|
33
|
# a requests an absoluteness test (that the path begins with /)
|
34
|
# Example: ck_file foo d:rwx:
|
35
|
# Outputs:
|
36
|
# * For the first requested property each file does not have, a message to
|
37
|
# stderr
|
38
|
# * For the first detected programminng error, a message to
|
39
|
# stderr
|
40
|
# Returns:
|
41
|
# 0 when all files have the requested properties
|
42
|
# 1 when at least one of the files have the requested properties
|
43
|
# 2 when a programming error is detected
|
44
|
#--------------------------
|
45
|
function ck_file {
|
46
|
|
47
|
local absolute_flag buf file_name file_type perm perms retval
|
48
|
|
49
|
# For each file ...
|
50
|
# ~~~~~~~~~~~~~~~~~
|
51
|
retval=0
|
52
|
while [[ $# -gt 0 ]]
|
53
|
do
|
54
|
file_name=$1
|
55
|
file_type=${2%%:*}
|
56
|
buf=${2#$file_type:}
|
57
|
perms=${buf%%:*}
|
58
|
absolute=${buf#$perms:}
|
59
|
[[ $absolute = $buf ]] && absolute=
|
60
|
case $absolute in
|
61
|
'' | a )
|
62
|
;;
|
63
|
* )
|
64
|
echo "ck_file: invalid absoluteness flag in '$2' specified for file '$file_name'" >&2
|
65
|
return 2
|
66
|
esac
|
67
|
shift 2
|
68
|
|
69
|
# Is the file reachable and does it exist?
|
70
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
71
|
case $file_type in
|
72
|
b )
|
73
|
if [[ ! -b $file_name ]]; then
|
74
|
echo "file '$file_name' is unreachable, does not exist or is not a block special file" >&2
|
75
|
retval=1
|
76
|
continue
|
77
|
fi
|
78
|
;;
|
79
|
f )
|
80
|
if [[ ! -f $file_name ]]; then
|
81
|
echo "file '$file_name' is unreachable, does not exist or is not an ordinary file" >&2
|
82
|
retval=1
|
83
|
continue
|
84
|
fi
|
85
|
;;
|
86
|
d )
|
87
|
if [[ ! -d $file_name ]]; then
|
88
|
echo "directory '$file_name' is unreachable, does not exist or is not a directory" >&2
|
89
|
retval=1
|
90
|
continue
|
91
|
fi
|
92
|
;;
|
93
|
* )
|
94
|
echo "Programming error: ck_file: invalid file type '$file_type' specified for file '$file_name'" >&2
|
95
|
return 2
|
96
|
esac
|
97
|
|
98
|
# Does the file have the requested permissions?
|
99
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
100
|
buf="$perms"
|
101
|
while [[ $buf ]]
|
102
|
do
|
103
|
perm="${buf:0:1}"
|
104
|
buf="${buf:1}"
|
105
|
case $perm in
|
106
|
r )
|
107
|
if [[ ! -r $file_name ]]; then
|
108
|
echo "$file_name: no read permission" >&2
|
109
|
retval=1
|
110
|
continue
|
111
|
fi
|
112
|
;;
|
113
|
w )
|
114
|
if [[ ! -w $file_name ]]; then
|
115
|
echo "$file_name: no write permission" >&2
|
116
|
retval=1
|
117
|
continue
|
118
|
fi
|
119
|
;;
|
120
|
x )
|
121
|
if [[ ! -x $file_name ]]; then
|
122
|
echo "$file_name: no execute permission" >&2
|
123
|
retval=1
|
124
|
continue
|
125
|
fi
|
126
|
;;
|
127
|
* )
|
128
|
echo "Programming error: ck_file: invalid permisssion '$perm' requested for file '$file_name'" >&2
|
129
|
return 2
|
130
|
esac
|
131
|
done
|
132
|
|
133
|
# Does the file have the requested absoluteness?
|
134
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
135
|
if [[ $absolute = a && ${file_name:0:1} != / ]]; then
|
136
|
echo "$file_name: does not begin with /" >&2
|
137
|
retval=1
|
138
|
fi
|
139
|
|
140
|
done
|
141
|
|
142
|
return $retval
|
143
|
|
144
|
} # end of function ck_file
|
145
|
|
146
|
#--------------------------
|
147
|
# Name: ck_uint
|
148
|
# Purpose: checks for a valid unsigned integer
|
149
|
# Usage: ck_uint <putative uint>
|
150
|
# Outputs: none
|
151
|
# Returns:
|
152
|
# 0 when $1 is a valid unsigned integer
|
153
|
# 1 otherwise
|
154
|
#--------------------------
|
155
|
function ck_uint {
|
156
|
local regex='^[[:digit:]]+$'
|
157
|
[[ $1 =~ $regex ]] && return 0 || return 1
|
158
|
} # end of function ck_uint
|
159
|
|
160
|
#--------------------------
|
161
|
# Name: fct
|
162
|
# Purpose: function call trace (for debugging)
|
163
|
# $1 - name of calling function
|
164
|
# $2 - message. If it starts with "started" or "returning" then the output is prettily indented
|
165
|
#--------------------------
|
166
|
function fct {
|
167
|
|
168
|
if [[ ! $debugging_flag ]]; then
|
169
|
return 0
|
170
|
fi
|
171
|
|
172
|
fct_indent="${fct_indent:=}"
|
173
|
|
174
|
case $2 in
|
175
|
'started'* )
|
176
|
fct_indent="$fct_indent "
|
177
|
msg D "$fct_indent$1: $2"
|
178
|
;;
|
179
|
'returning'* )
|
180
|
msg D "$fct_indent$1: $2"
|
181
|
fct_indent="${fct_indent# }"
|
182
|
;;
|
183
|
* )
|
184
|
msg D "$fct_indent$1: $2"
|
185
|
esac
|
186
|
|
187
|
} # end of function fct
|
188
|
|
189
|
#--------------------------
|
190
|
# Name: finalise
|
191
|
# Purpose: cleans up and exits
|
192
|
# Arguments:
|
193
|
# $1 return value
|
194
|
# Return code (on exit):
|
195
|
# The sum of zero plus
|
196
|
# 1 if any warnings
|
197
|
# 2 if any errors
|
198
|
# 4,8,16 unused
|
199
|
# 32 if terminated by a signal
|
200
|
#--------------------------
|
201
|
function finalise {
|
202
|
fct "${FUNCNAME[0]}" 'started'
|
203
|
finalising_flag=$true
|
204
|
|
205
|
# Temporary directory removal
|
206
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
207
|
tmp_dir_regex="^/tmp/$my_name\..{6}$"
|
208
|
[[ $tmp_dir_created_flag \
|
209
|
&& ${tmp_dir:-} =~ $tmp_dir_regex \
|
210
|
]] && rm -fr "$tmp_dir"
|
211
|
|
212
|
# Interrupted? Message and exit return value
|
213
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
214
|
my_retval=0
|
215
|
if ck_uint "${1:-}" && (($1>128)); then
|
216
|
((my_retval+=32))
|
217
|
case $1 in
|
218
|
129 )
|
219
|
buf=SIGHUP
|
220
|
;;
|
221
|
130 )
|
222
|
buf=SIGINT
|
223
|
;;
|
224
|
131 )
|
225
|
buf=SIGQUIT
|
226
|
;;
|
227
|
132 )
|
228
|
buf=SIGILL
|
229
|
;;
|
230
|
134 )
|
231
|
buf=SIGABRT
|
232
|
;;
|
233
|
135 )
|
234
|
buf=SIGBUS
|
235
|
;;
|
236
|
136 )
|
237
|
buf=SIGFPE
|
238
|
;;
|
239
|
138 )
|
240
|
buf=SIGUSR1
|
241
|
;;
|
242
|
139 )
|
243
|
buf=SIGSEGV
|
244
|
;;
|
245
|
140 )
|
246
|
buf=SIGUSR2
|
247
|
;;
|
248
|
141 )
|
249
|
buf=SIGPIPE
|
250
|
;;
|
251
|
142 )
|
252
|
buf=SIGALRM
|
253
|
;;
|
254
|
143 )
|
255
|
buf=SIGTERM
|
256
|
;;
|
257
|
146 )
|
258
|
buf=SIGCONT
|
259
|
;;
|
260
|
147 )
|
261
|
buf=SIGSTOP
|
262
|
;;
|
263
|
148 )
|
264
|
buf=SIGTSTP
|
265
|
;;
|
266
|
149 )
|
267
|
buf=SIGTTIN
|
268
|
;;
|
269
|
150 )
|
270
|
buf=SIGTTOU
|
271
|
;;
|
272
|
151 )
|
273
|
buf=SIGURG
|
274
|
;;
|
275
|
152 )
|
276
|
buf=SIGCPU
|
277
|
;;
|
278
|
153 )
|
279
|
buf=SIGXFSZ
|
280
|
;;
|
281
|
154 )
|
282
|
buf=SIGVTALRM
|
283
|
;;
|
284
|
155 )
|
285
|
buf=SIGPROF
|
286
|
;;
|
287
|
* )
|
288
|
msg E "${FUNCNAME[0]}: programming error: \$1 ($1) not serviced"
|
289
|
;;
|
290
|
esac
|
291
|
interrupt_flag=$true
|
292
|
msg="Finalising on $buf"
|
293
|
msg E "$msg" # Returns because finalising_flag is set
|
294
|
fi
|
295
|
|
296
|
# Old log removal
|
297
|
# ~~~~~~~~~~~~~~~
|
298
|
buf=$(find "$log_dir" -name '*.log' -mtime +$log_retention -execdir rm {} + 2>&1)
|
299
|
[[ $buf != '' ]] && msg W "Problem removing old logs: $buf"
|
300
|
|
301
|
# Exit return value adjustment
|
302
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
303
|
if [[ $warning_flag ]]; then
|
304
|
((my_retval+=1))
|
305
|
fi
|
306
|
if [[ $error_flag ]]; then
|
307
|
((my_retval+=2))
|
308
|
fi
|
309
|
|
310
|
# Exit
|
311
|
# ~~~~
|
312
|
exit $my_retval
|
313
|
} # end of function finalise
|
314
|
|
315
|
#--------------------------
|
316
|
# Name: generate_report
|
317
|
# Purpose:
|
318
|
# * Generates SATA backplane usage report
|
319
|
# Usage: generate_report
|
320
|
# Global variable set: none
|
321
|
# Outputs: writes dated report file; removes if same as last one
|
322
|
# Returns:
|
323
|
# 0 on success and warning. Does not return on error
|
324
|
#--------------------------
|
325
|
function generate_report {
|
326
|
fct "${FUNCNAME[0]}" 'started'
|
327
|
local ata buf cmd last_out_fn oIFS out out_dir out_fn
|
328
|
declare -A ata
|
329
|
|
330
|
# Get the ATA number for each sdX
|
331
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
332
|
# Based on syntaxerror's script in
|
333
|
# https://serverfault.com/questions/244944/linux-ata-errors-translating-to-a-device-name
|
334
|
oIFS=$IFS
|
335
|
while IFS=' ' read Path HostFull sdx
|
336
|
do
|
337
|
IFS=: h=($HostFull)
|
338
|
HostMain=${h[0]}; HostMid=${h[1]}; HostSub=${h[2]}
|
339
|
if echo $Path | grep -q '/usb[0-9]*/'; then
|
340
|
msg I "Device $sdx is not an ATA device, it is a USB device"
|
341
|
else
|
342
|
ata["$sdx"]=ata$(< "$Path/host$HostMain/scsi_host/host$HostMain/unique_id").$HostMid$HostSub
|
343
|
fi
|
344
|
done < <(
|
345
|
for i in /sys/block/sd*
|
346
|
do
|
347
|
readlink $i \
|
348
|
| sed \
|
349
|
-e 's|\.\./devices|/sys/devices|' \
|
350
|
-e 's|/host[0-9]\{1,2\}/target| |' \
|
351
|
-e 's|/[0-9]\{1,2\}\(:[0-9]\)\{3\}/block/| |'
|
352
|
done
|
353
|
)
|
354
|
IFS=$oIFS
|
355
|
|
356
|
# Generate report data
|
357
|
# ~~~~~~~~~~~~~~~~~~~~
|
358
|
out=$'Backplane Socket sdx ata Serial\n'
|
359
|
out+=$(
|
360
|
while read backplane socket sdx serno
|
361
|
do
|
362
|
msg D "backplane: $backplane, socket: $socket, sdx: $sdx, serno: $serno"
|
363
|
((backplane=backplane-6+1))
|
364
|
((socket++))
|
365
|
printf '%9s %6s %4s %8s %s\n' $backplane $socket $sdx ${ata[$sdx]} $serno
|
366
|
done < <(
|
367
|
lshw -class disk 2>&1 \
|
368
|
| grep -E '^ (bus info|logical name|serial)' \
|
369
|
| sed -e 's/^[[:space:]]*//' \
|
370
|
| xargs -L 3 \
|
371
|
| grep -Ev 'scsi@(0|1):0.0.0' \
|
372
|
| sed -e 's/bus info: scsi@//' -e 's|logical name: /dev/||' \
|
373
|
| sed -e 's/serial: //' -e 's/:/ /' -e 's/\.0\.0//' \
|
374
|
| sort -n
|
375
|
)
|
376
|
)
|
377
|
|
378
|
# Check reports directory
|
379
|
# ~~~~~~~~~~~~~~~~~~~~~~~
|
380
|
out_dir=/var/backup/sata_backplane_usage
|
381
|
buf=$(ck_file "$out_dir" d:rwx 2>&1)
|
382
|
[[ $buf != '' ]] && msg E "$buf"
|
383
|
|
384
|
# Get name of most recent report
|
385
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
386
|
last_out_fn=$(ls -1rt "$out_dir" | tail -1)
|
387
|
[[ $last_out_fn != '' ]] && last_out_fn=$out_dir/$last_out_fn
|
388
|
|
389
|
# Write report to file
|
390
|
# ~~~~~~~~~~~~~~~~~~~~
|
391
|
out_fn=$out_dir/$(date +%Y-%m-%d@%H:%M:%S).report
|
392
|
msg I "Writing report $out_fn"
|
393
|
buf=$(echo "$out" > "$out_fn" 2>&1)
|
394
|
[[ $buf != '' ]] && msg E "Writing report: $buf"
|
395
|
|
396
|
# Remove report file if same as previous one
|
397
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
398
|
if [[ $last_out_fn != '' ]]; then
|
399
|
cmd=(diff --brief "$last_out_fn" "$out_fn")
|
400
|
buf=$("${cmd[@]}" 2>&1)
|
401
|
if (($?==0)); then
|
402
|
msg I "Removing report $out_fn because identical to the last report"
|
403
|
buf=$(rm "$out_fn" 2>&1)
|
404
|
[[ $buf != '' ]] && msg E "Removing $out_fn: $buf"
|
405
|
elif (($?==1)); then
|
406
|
msg W "SATA backplane usage changed. Reports in $out_dir"
|
407
|
elif (($?==2)); then
|
408
|
msg E "${cmd[*]}: $buf"
|
409
|
fi
|
410
|
fi
|
411
|
|
412
|
fct "${FUNCNAME[0]}" 'returning'
|
413
|
return 0
|
414
|
} # end of function generate_report
|
415
|
|
416
|
#--------------------------
|
417
|
# Name: initialise
|
418
|
# Purpose: sets up environment, parses command line, reads config file
|
419
|
#--------------------------
|
420
|
function initialise {
|
421
|
local args buf emsg msg_part opt_c_flag opt_l_flag
|
422
|
local -r fn_date_format='%Y-%m-%d@%H:%M:%S'
|
423
|
|
424
|
# Configure shell environment
|
425
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
426
|
export LANG=en_GB.UTF-8
|
427
|
export LANGUAGE=en_GB.UTF-8
|
428
|
for var_name in LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
|
429
|
LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
|
430
|
LC_TELEPHONE LC_TIME
|
431
|
do
|
432
|
unset $var_name
|
433
|
done
|
434
|
|
435
|
export PATH=/usr/sbin:/sbin:/usr/bin:/bin
|
436
|
IFS=$' \n\t'
|
437
|
set -o nounset
|
438
|
shopt -s extglob # Enable extended pattern matching operators
|
439
|
unset CDPATH # Ensure cd behaves as expected
|
440
|
umask 022
|
441
|
|
442
|
# Initialise some global logic variables
|
443
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
444
|
readonly false=
|
445
|
readonly true=true
|
446
|
|
447
|
debugging_flag=$false
|
448
|
error_flag=$false
|
449
|
finalising_flag=$false
|
450
|
interrupt_flag=$false
|
451
|
logging_flag=$false
|
452
|
tmp_dir_created_flag=$false
|
453
|
warning_flag=$false
|
454
|
|
455
|
# Initialise some global string variables
|
456
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
457
|
readonly log_date_format='+%H:%M:%S'
|
458
|
readonly log_retention=28
|
459
|
readonly msg_lf=$'\n '
|
460
|
readonly my_name=${0##*/}
|
461
|
readonly script_ver=0.1
|
462
|
|
463
|
# Set traps
|
464
|
# ~~~~~~~~~
|
465
|
trap 'finalise 129' 'HUP'
|
466
|
trap 'finalise 130' 'INT'
|
467
|
trap 'finalise 131' 'QUIT'
|
468
|
trap 'finalise 132' 'ILL'
|
469
|
trap 'finalise 134' 'ABRT'
|
470
|
trap 'finalise 135' 'BUS'
|
471
|
trap 'finalise 136' 'FPE'
|
472
|
trap 'finalise 138' 'USR1'
|
473
|
trap 'finalise 139' 'SEGV'
|
474
|
trap 'finalise 140' 'USR2'
|
475
|
trap 'finalise 141' 'PIPE'
|
476
|
trap 'finalise 142' 'ALRM'
|
477
|
trap 'finalise 143' 'TERM'
|
478
|
trap 'finalise 146' 'CONT'
|
479
|
trap 'finalise 147' 'STOP'
|
480
|
trap 'finalise 148' 'TSTP'
|
481
|
trap 'finalise 149' 'TTIN'
|
482
|
trap 'finalise 150' 'TTOU'
|
483
|
trap 'finalise 151' 'URG'
|
484
|
trap 'finalise 152' 'XCPU'
|
485
|
trap 'finalise 153' 'XFSZ'
|
486
|
trap 'finalise 154' 'VTALRM'
|
487
|
trap 'finalise 155' 'PROF'
|
488
|
|
489
|
# Parse command line
|
490
|
# ~~~~~~~~~~~~~~~~~~
|
491
|
args=("$@")
|
492
|
conf_fn=/usr/local/etc/report_sata_backplane_usage.conf
|
493
|
emsg=
|
494
|
log_dir=/var/log/${my_name%.sh}
|
495
|
log_fn=$log_dir/$(date +$fn_date_format)
|
496
|
opt_c_flag=$false
|
497
|
opt_l_flag=$false
|
498
|
while getopts :c:dhl:v opt "$@"
|
499
|
do
|
500
|
case $opt in
|
501
|
c )
|
502
|
conf_fn=$OPTARG
|
503
|
opt_c_flag=$true
|
504
|
;;
|
505
|
d )
|
506
|
debugging_flag=$true
|
507
|
;;
|
508
|
h )
|
509
|
debugging_flag=$false
|
510
|
usage verbose
|
511
|
exit 0
|
512
|
;;
|
513
|
l )
|
514
|
log_fn=$OPTARG
|
515
|
opt_l_flag=$true
|
516
|
;;
|
517
|
v )
|
518
|
echo "$script_name version $script_ver" >&2
|
519
|
exit 0
|
520
|
;;
|
521
|
: )
|
522
|
emsg+=$msg_lf"Option $OPTARG must have an argument"
|
523
|
[[ $OPTARG = c ]] && { opt_c_flag=$true; conf_fn=/bin/bash; }
|
524
|
;;
|
525
|
* )
|
526
|
emsg+=$msg_lf"Invalid option '-$OPTARG'"
|
527
|
esac
|
528
|
done
|
529
|
|
530
|
# Check option arguments
|
531
|
# ~~~~~~~~~~~~~~~~~~~~~~
|
532
|
if [[ $opt_l_flag && $log_fn != /dev/tty ]]; then
|
533
|
log_dir=${log_fn%/*}
|
534
|
log_dir_realpath=$(readlink --canonicalize-missing -- "$log_dir" 2>&1)
|
535
|
if (($?==0)); then
|
536
|
buf=$(ck_file "$log_dir_realpath" d:rwx: 2>&1)
|
537
|
[[ $buf != '' ]] && emsg+=$msg_lf"Invalid -l option value '$log_fn': $buf"
|
538
|
else
|
539
|
emsg+=$msg_lf"Invalid -l option value '$log_fn' ($log_dir_realpath)"
|
540
|
fi
|
541
|
fi
|
542
|
|
543
|
# Test for mutually exclusive options
|
544
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
545
|
# There are no mutually exclusive options
|
546
|
|
547
|
# Test for mandatory options not set
|
548
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
549
|
# There are no mandatory options
|
550
|
|
551
|
# Test for extra arguments
|
552
|
# ~~~~~~~~~~~~~~~~~~~~~~~~
|
553
|
shift $(($OPTIND-1))
|
554
|
if [[ $* != '' ]]; then
|
555
|
emsg+=$msg_lf"Invalid extra argument(s) '$*'"
|
556
|
fi
|
557
|
|
558
|
# Report any command line errors
|
559
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
560
|
if [[ $emsg != '' ]]; then
|
561
|
emsg+=$msg_lf'(use -h option for help)'
|
562
|
msg E "$emsg"
|
563
|
fi
|
564
|
|
565
|
# Check the config file
|
566
|
# ~~~~~~~~~~~~~~~~~~~~~
|
567
|
buf=$(ck_file "$conf_fn" f:r: 2>&1)
|
568
|
[[ $buf != '' ]] && msg E "$buf"
|
569
|
|
570
|
# Set up logging
|
571
|
# ~~~~~~~~~~~~~~
|
572
|
if [[ ! $opt_l_flag ]]; then # No -l option; directory not yet error trapped
|
573
|
buf=$(ck_file "$log_dir" d:rwx: 2>&1)
|
574
|
if [[ $buf = '' ]]; then
|
575
|
log_fn=$log_dir/$(date +$fn_date_format).log
|
576
|
buf=$(touch "$log_fn" 2>&1)
|
577
|
if (($?>0)); then
|
578
|
msg E "cannot create $log_fn: $buf"
|
579
|
fi
|
580
|
chmod 600 "$log_fn"
|
581
|
else
|
582
|
msg E "cannot create log: $buf"
|
583
|
fi
|
584
|
fi
|
585
|
exec >>"$log_fn"
|
586
|
exec 2>>"$log_fn"
|
587
|
[[ $log_fn != /dev/tty ]] && logging_flag=$true
|
588
|
|
589
|
fct 'initialise' 'started (this message delayed until logging initialised)'
|
590
|
msg I "$(date '+%A %-d %B %Y %Z')"
|
591
|
msg I "$my_name version $script_ver started (PID: $$, PPID: $PPID)"
|
592
|
msg I "Command line:$msg_lf$0 $(printf '%q ' "${args[@]}" | sed "s/''//")"
|
593
|
|
594
|
# Read config file
|
595
|
# ~~~~~~~~~~~~~~~~
|
596
|
buf=$(ck_file "$conf_fn" f:r 2>&1)
|
597
|
[[ $buf != '' ]] && msg E "$buf"
|
598
|
source "$conf_fn"
|
599
|
emsg=
|
600
|
|
601
|
# Report any errors
|
602
|
# ~~~~~~~~~~~~~~~~~
|
603
|
if [[ $emsg != '' ]]; then
|
604
|
msg E "$conf_fn: ${emsg#, }"
|
605
|
fi
|
606
|
|
607
|
# Create temporary directory
|
608
|
# ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
609
|
# TODO: remove this code if tmp_dir not used
|
610
|
# If the mktemplate is changed, tmp_dir_regex in the finalise function
|
611
|
# may also need to be changed.
|
612
|
buf=$(mktemp -d "/tmp/$my_name.XXXXXX" 2>&1)
|
613
|
if (($?==0)); then
|
614
|
tmp_dir=$buf
|
615
|
tmp_dir_created_flag=$true
|
616
|
chmod 700 "$tmp_dir"
|
617
|
else
|
618
|
msg E "Unable to create temporary directory:$buf"
|
619
|
fi
|
620
|
|
621
|
fct "${FUNCNAME[0]}" 'returning'
|
622
|
} # end of function initialise
|
623
|
|
624
|
#--------------------------
|
625
|
# Name: msg
|
626
|
# Purpose: generalised messaging interface
|
627
|
# Arguments:
|
628
|
# $1 class: E, I or W indicating Error, Information or Warning
|
629
|
# $2 message text
|
630
|
# Global variables read:
|
631
|
# my_name
|
632
|
# Global variables written:
|
633
|
# error_flag
|
634
|
# warning_flag
|
635
|
# Output: information messages to stdout; the rest to stderr
|
636
|
# Returns:
|
637
|
# Does not return (calls finalise) when class is E for error
|
638
|
# Otherwise returns 0
|
639
|
#--------------------------
|
640
|
function msg {
|
641
|
local class level message_text prefix
|
642
|
|
643
|
# Process arguments
|
644
|
# ~~~~~~~~~~~~~~~~~
|
645
|
class="${1:-}"
|
646
|
message_text="${2:-}"
|
647
|
|
648
|
# Class-dependent set-up
|
649
|
# ~~~~~~~~~~~~~~~~~~~~~~
|
650
|
case "$class" in
|
651
|
D )
|
652
|
[[ ! $debugging_flag ]] && return
|
653
|
prefix='DEBUG: '
|
654
|
;;
|
655
|
E )
|
656
|
error_flag=$true
|
657
|
level=err
|
658
|
prefix='ERROR: '
|
659
|
;;
|
660
|
I )
|
661
|
prefix=
|
662
|
level=info
|
663
|
;;
|
664
|
W )
|
665
|
warning_flag=$true
|
666
|
level=warning
|
667
|
prefix='WARN: '
|
668
|
;;
|
669
|
* )
|
670
|
msg E "msg: invalid class '$class': '$*'"
|
671
|
esac
|
672
|
|
673
|
# Output
|
674
|
# ~~~~~~
|
675
|
message_text="$prefix$message_text"
|
676
|
[[ $class = E ]] && \
|
677
|
logger --tag $my_name[$$] --priority syslog.$level -- "$message_text"
|
678
|
[[ $logging_flag ]] && \
|
679
|
message_text="$(date "$log_date_format") $message_text"
|
680
|
if [[ $class = I ]]; then
|
681
|
printf '%s\n' "$message_text"
|
682
|
else
|
683
|
printf '%s\n' " $message_text" >&2
|
684
|
fi
|
685
|
|
686
|
# Return or not
|
687
|
# ~~~~~~~~~~~~~
|
688
|
if [[ $class = E ]]; then
|
689
|
[[ ! $finalising_flag ]] && finalise 1
|
690
|
fi
|
691
|
|
692
|
return 0
|
693
|
} # end of function msg
|
694
|
|
695
|
#--------------------------
|
696
|
# Name: usage
|
697
|
# Purpose: prints usage message
|
698
|
#--------------------------
|
699
|
function usage {
|
700
|
fct "${FUNCNAME[0]}" 'started'
|
701
|
local msg usage
|
702
|
|
703
|
# Build the messages
|
704
|
# ~~~~~~~~~~~~~~~~~~
|
705
|
usage="usage: $my_name -c conf [-d] [-h] [-l log] [-v]"
|
706
|
msg=' where:'
|
707
|
msg+=$'\n -c configuration file name'
|
708
|
msg+=$'\n -d debugging on'
|
709
|
msg+=$'\n -h prints this help and exits'
|
710
|
msg+=$'\n -l log file'
|
711
|
msg+=$'\n Use /dev/tty to log to screen'
|
712
|
msg+=$'\n '
|
713
|
msg+="Default: log to $log_fn"
|
714
|
msg+=$'\n '"-v prints the script's version and exits"
|
715
|
|
716
|
# Display the message(s)
|
717
|
# ~~~~~~~~~~~~~~~~~~~~~~
|
718
|
echo "$usage" >&2
|
719
|
if [[ ${1:-} != 'verbose' ]]; then
|
720
|
echo "(use -h for help)" >&2
|
721
|
else
|
722
|
echo "$msg" >&2
|
723
|
fi
|
724
|
|
725
|
fct "${FUNCNAME[0]}" 'returning'
|
726
|
} # end of function usage
|
727
|
|
728
|
#--------------------------
|
729
|
# Name: main
|
730
|
# Purpose: where it all happens
|
731
|
#--------------------------
|
732
|
initialise "${@:-}"
|
733
|
generate_report
|
734
|
finalise 0
|