Commit | Line | Data |
---|---|---|
a4fcba60 SBS |
1 | #!/usr/bin/env bash |
2 | # Desc: Create and sign a checksum | |
2d28b4d6 | 3 | # Usage: bksum file.txt |
a4fcba60 SBS |
4 | # Input: stdin: file list (newline delimited) |
5 | # arg(s): file paths (IFS delimited) | |
6 | # Output: file containing sha256 hashes and file paths | |
7 | # Depends: bash v5.1.16, date (GNU Coreutils 8.32), gpg v2.2.27, ots v0.7.0 | |
e9bbd5a6 | 8 | # Version: 0.1.1 |
a4fcba60 SBS |
9 | |
10 | declare -Ag appRollCall # Associative array for storing app status | |
11 | declare -Ag fileRollCall # Associative array for storing file status | |
12 | declare -Ag dirRollCall # Associative array for storing dir status | |
13 | declare -ag arrPosArgs; # positional arguments | |
14 | declare -ag arrStdin; # standard input lines | |
15 | declare -ag arrInFiles; # input files | |
16 | ||
17 | yell() { echo "$0: $*" >&2; } # print script path and all args to stderr | |
18 | die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status | |
19 | try() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails | |
20 | vbm() { | |
21 | # Description: Prints verbose message ("vbm") to stderr if opVerbose is set to "true". | |
22 | # Usage: vbm "DEBUG :verbose message here" | |
23 | # Version 0.2.0 | |
24 | # Input: arg1: string | |
25 | # vars: opVerbose | |
26 | # Output: stderr | |
27 | # Depends: bash 5.1.16, GNU-coreutils 8.30 (echo, date) | |
28 | ||
29 | if [ "$opVerbose" = "true" ]; then | |
30 | functionTime="$(date --iso-8601=ns)"; # Save current time in nano seconds. | |
31 | echo "[$functionTime]:$0:""$*" 1>&2; # Display argument text. | |
32 | fi | |
33 | ||
34 | # End function | |
35 | return 0; # Function finished. | |
36 | } # Displays message if opVerbose true | |
37 | showUsage() { | |
38 | # Desc: Display script usage information | |
39 | # Usage: showUsage | |
40 | # Version 0.0.2 | |
41 | # Input: none | |
42 | # Output: stdout | |
43 | # Depends: GNU-coreutils 8.30 (cat) | |
44 | cat <<'EOF' | |
45 | USAGE: | |
2d28b4d6 | 46 | bksum [ options ] [FILE...] |
a4fcba60 SBS |
47 | |
48 | DESCRIPTION: | |
49 | Creates sha256 checksum of files. | |
50 | ||
51 | OPTIONS: | |
52 | -h, --help | |
53 | Display help information. | |
54 | --version | |
55 | Display script version. | |
56 | -v, --verbose | |
57 | Display debugging info. | |
58 | -o, --output-file | |
59 | Define output file path. By default, the file | |
60 | name includes the full ISO-8601 timestamp | |
61 | without separators, e.g.: | |
62 | 20220920T2117+0000..SHA256SUM | |
63 | -s, --sign | |
64 | Sign with GnuPG the checksum file. | |
65 | -t, --timestamp | |
66 | Timestamp with OpenTimestamps the checksum file | |
67 | (and GnuPG signature if -s/--sign specified). | |
07b20692 SBS |
68 | -T, --timestamp-wait |
69 | Same as '-t/--timestamp' except that | |
70 | OpenTimestamps will run as a background process | |
71 | until a calendar server returns a completed | |
72 | timestamp. | |
a4fcba60 SBS |
73 | -- |
74 | Indicate end of options. | |
75 | ||
76 | EXAMPLE: | |
2d28b4d6 SBS |
77 | bksum file.txt |
78 | bksum file1.txt file2.txt | |
79 | bksum -v -- file1.txt file2.txt | |
80 | bksum -v -t -- file1.txt file2.txt | |
81 | find . -type f | bksum | |
82 | find . -type f | bksum -v -s -t -- file.txt | |
a4fcba60 SBS |
83 | |
84 | NOTE: | |
85 | If GNU Coreutils 8.32 `sha256sum` used, checksum file | |
86 | can be verified using: | |
87 | sha256sum --check 20220920T2117+0000..SHA256SUM | |
88 | EOF | |
89 | } # Display information on how to use this script. | |
90 | showVersion() { | |
91 | # Desc: Displays script version and license information. | |
92 | # Usage: showVersion | |
93 | # Version: 0.0.2 | |
94 | # Input: scriptVersion var containing version string | |
95 | # Output: stdout | |
96 | # Depends: vbm(), yell, GNU-coreutils 8.30 | |
97 | ||
98 | # Initialize function | |
99 | vbm "DEBUG:showVersion function called." | |
100 | ||
101 | cat <<'EOF' | |
2d28b4d6 | 102 | bksum 0.0.1 |
a4fcba60 SBS |
103 | Copyright (C) 2022 Steven Baltakatei Sandoval |
104 | License GPLv3: GNU GPL version 3 | |
105 | This is free software; you are free to change and redistribute it. | |
106 | There is NO WARRANTY, to the extent permitted by law. | |
107 | ||
108 | EOF | |
109 | ||
110 | # End function | |
111 | vbm "DEBUG:showVersion function ended." | |
112 | return 0; # Function finished. | |
113 | } # Display script version. | |
114 | checkapp() { | |
115 | # Desc: If arg is a command, save result in assoc array 'appRollCall' | |
116 | # Usage: checkapp arg1 arg2 arg3 ... | |
117 | # Version: 0.1.1 | |
118 | # Input: global assoc. array 'appRollCall' | |
119 | # Output: adds/updates key(value) to global assoc array 'appRollCall' | |
120 | # Depends: bash 5.0.3 | |
121 | local returnState | |
122 | ||
123 | #===Process Args=== | |
124 | for arg in "$@"; do | |
125 | if command -v "$arg" 1>/dev/null 2>&1; then # Check if arg is a valid command | |
126 | appRollCall[$arg]="true"; | |
127 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi; | |
128 | else | |
129 | appRollCall[$arg]="false"; returnState="false"; | |
130 | fi; | |
131 | done; | |
132 | ||
133 | #===Determine function return code=== | |
134 | if [ "$returnState" = "true" ]; then | |
135 | return 0; | |
136 | else | |
137 | return 1; | |
138 | fi; | |
139 | } # Check that app exists | |
140 | checkfile() { | |
141 | # Desc: If arg is a file path, save result in assoc array 'fileRollCall' | |
142 | # Usage: checkfile arg1 arg2 arg3 ... | |
143 | # Version: 0.1.2 | |
144 | # Input: global assoc. array 'fileRollCall' | |
145 | # Output: adds/updates key(value) to global assoc array 'fileRollCall'; | |
146 | # Output: returns 0 if app found, 1 otherwise | |
147 | # Depends: bash 5.0.3 | |
148 | local returnState | |
149 | ||
150 | #===Process Args=== | |
151 | for arg in "$@"; do | |
152 | if [ -f "$arg" ]; then | |
153 | fileRollCall["$arg"]="true"; | |
154 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi; | |
155 | elif [ -z "$arg" ]; then | |
156 | fileRollCall["(no name)"]="false"; returnState="false"; | |
157 | else | |
158 | fileRollCall["$arg"]="false"; returnState="false"; | |
159 | fi; | |
160 | done; | |
161 | ||
162 | #===Determine function return code=== | |
163 | if [ "$returnState" = "true" ]; then | |
164 | return 0; | |
165 | else | |
166 | return 1; | |
167 | fi; | |
168 | } # Check that file exists | |
169 | checkdir() { | |
170 | # Desc: If arg is a dir path, save result in assoc array 'dirRollCall' | |
171 | # Usage: checkdir arg1 arg2 arg3 ... | |
172 | # Version 0.1.2 | |
173 | # Input: global assoc. array 'dirRollCall' | |
174 | # Output: adds/updates key(value) to global assoc array 'dirRollCall'; | |
175 | # Output: returns 0 if all args are dirs; 1 otherwise | |
176 | # Depends: Bash 5.0.3 | |
177 | local returnState | |
178 | ||
179 | #===Process Args=== | |
180 | for arg in "$@"; do | |
181 | if [ -z "$arg" ]; then | |
182 | dirRollCall["(Unspecified Dirname(s))"]="false"; returnState="false"; | |
183 | elif [ -d "$arg" ]; then | |
184 | dirRollCall["$arg"]="true"; | |
185 | if ! [ "$returnState" = "false" ]; then returnState="true"; fi | |
186 | else | |
187 | dirRollCall["$arg"]="false"; returnState="false"; | |
188 | fi | |
189 | done | |
190 | ||
191 | #===Determine function return code=== | |
192 | if [ "$returnState" = "true" ]; then | |
193 | return 0; | |
194 | else | |
195 | return 1; | |
196 | fi | |
197 | } # Check that dir exists | |
198 | displayMissing() { | |
199 | # Desc: Displays missing apps, files, and dirs | |
200 | # Usage: displayMissing | |
201 | # Version 1.0.0 | |
202 | # Input: associative arrays: appRollCall, fileRollCall, dirRollCall | |
203 | # Output: stderr: messages indicating missing apps, file, or dirs | |
204 | # Output: returns exit code 0 if nothing missing; 1 otherwise | |
205 | # Depends: bash 5, checkAppFileDir() | |
206 | local missingApps value appMissing missingFiles fileMissing | |
207 | local missingDirs dirMissing | |
208 | ||
209 | #==BEGIN Display errors== | |
210 | #===BEGIN Display Missing Apps=== | |
211 | missingApps="Missing apps :"; | |
212 | #for key in "${!appRollCall[@]}"; do echo "DEBUG:$key => ${appRollCall[$key]}"; done | |
213 | for key in "${!appRollCall[@]}"; do | |
214 | value="${appRollCall[$key]}"; | |
215 | if [ "$value" = "false" ]; then | |
216 | #echo "DEBUG:Missing apps: $key => $value"; | |
217 | missingApps="$missingApps""$key "; | |
218 | appMissing="true"; | |
219 | fi; | |
220 | done; | |
221 | if [ "$appMissing" = "true" ]; then # Only indicate if an app is missing. | |
222 | echo "$missingApps" 1>&2; | |
223 | fi; | |
224 | unset value; | |
225 | #===END Display Missing Apps=== | |
226 | ||
227 | #===BEGIN Display Missing Files=== | |
228 | missingFiles="Missing files:"; | |
229 | #for key in "${!fileRollCall[@]}"; do echo "DEBUG:$key => ${fileRollCall[$key]}"; done | |
230 | for key in "${!fileRollCall[@]}"; do | |
231 | value="${fileRollCall[$key]}"; | |
232 | if [ "$value" = "false" ]; then | |
233 | #echo "DEBUG:Missing files: $key => $value"; | |
234 | missingFiles="$missingFiles""$key "; | |
235 | fileMissing="true"; | |
236 | fi; | |
237 | done; | |
238 | if [ "$fileMissing" = "true" ]; then # Only indicate if an app is missing. | |
239 | echo "$missingFiles" 1>&2; | |
240 | fi; | |
241 | unset value; | |
242 | #===END Display Missing Files=== | |
243 | ||
244 | #===BEGIN Display Missing Directories=== | |
245 | missingDirs="Missing dirs:"; | |
246 | #for key in "${!dirRollCall[@]}"; do echo "DEBUG:$key => ${dirRollCall[$key]}"; done | |
247 | for key in "${!dirRollCall[@]}"; do | |
248 | value="${dirRollCall[$key]}"; | |
249 | if [ "$value" = "false" ]; then | |
250 | #echo "DEBUG:Missing dirs: $key => $value"; | |
251 | missingDirs="$missingDirs""$key "; | |
252 | dirMissing="true"; | |
253 | fi; | |
254 | done; | |
255 | if [ "$dirMissing" = "true" ]; then # Only indicate if an dir is missing. | |
256 | echo "$missingDirs" 1>&2; | |
257 | fi; | |
258 | unset value; | |
259 | #===END Display Missing Directories=== | |
260 | ||
261 | #==END Display errors== | |
262 | #==BEGIN Determine function return code=== | |
263 | if [ "$appMissing" == "true" ] || [ "$fileMissing" == "true" ] || [ "$dirMissing" == "true" ]; then | |
264 | return 1; | |
265 | else | |
266 | return 0; | |
267 | fi | |
268 | #==END Determine function return code=== | |
269 | } # Display missing apps, files, dirs | |
270 | processArgs() { | |
271 | # Desc: Processes arguments provided to script | |
272 | # Usage: processArgs "$@" | |
273 | # Version: 1.0.0 (modified) | |
274 | # Input: "$@" (list of arguments provided to the function) | |
275 | # Output: Sets following variables used by other functions: | |
276 | # opVerbose Indicates verbose mode enable status. (ex: "true", "false") | |
277 | # pathDirOut1 Path to output directory. | |
278 | # pathFileOut1 Path to output file. | |
279 | # opFileOut1_overwrite Indicates whether file pathFileOut1 should be overwritten (ex: "true", "false"). | |
07b20692 SBS |
280 | # opTs Indicates timestamp mode |
281 | # opTsWait Indicates timestamp mode with wait option | |
a4fcba60 SBS |
282 | # arrPosArgs Array of remaining positional argments |
283 | # Depends: | |
284 | # yell() Displays messages to stderr. | |
285 | # vbm() Displays messsages to stderr if opVerbose set to "true". | |
286 | # showUsage() Displays usage information about parent script. | |
287 | # showVersion() Displays version about parent script. | |
288 | # arrPosArgs Global array for storing non-option positional arguments (i.e. arguments following the `--` option). | |
289 | # External dependencies: bash (5.1.16), echo | |
290 | # Ref./Attrib.: | |
291 | # [1]: Marco Aurelio (2014-05-08). "echo that outputs to stderr". https://stackoverflow.com/a/23550347 | |
292 | # [2]: "Handling positional parameters" (2018-05-12). https://wiki.bash-hackers.org/scripting/posparams | |
293 | ||
294 | # Initialize function | |
295 | vbm "DEBUG:processArgs function called." | |
296 | ||
297 | # Perform work | |
298 | while [ ! $# -eq 0 ]; do # While number of arguments ($#) is not (!) equal to (-eq) zero (0). | |
299 | #yell "DEBUG:Starting processArgs while loop." # Debug stderr message. See [1]. | |
300 | #yell "DEBUG:Provided arguments are:""$*" # Debug stderr message. See [1]. | |
301 | case "$1" in | |
302 | -h | --help) showUsage; exit 1;; # Display usage. | |
303 | --version) showVersion; exit 1;; # Show version | |
304 | -v | --verbose) opVerbose="true"; vbm "DEBUG:Verbose mode enabled.";; # Enable verbose mode. See [1]. | |
305 | -o | --output-file) # Define output file path | |
306 | if [ -f "$2" ]; then # If $2 is file that exists, prompt user to continue to overwrite, set pathFileOut1 to $2, pop $2. | |
307 | yell "Specified output file $2 already exists. Overwrite? (y/n):" | |
308 | read -r m; case $m in | |
309 | y | Y | yes) opFileOut1_overwrite="true";; | |
310 | n | N | no) opFileOut1_overwrite="false";; | |
311 | *) yell "Invalid selection. Exiting."; exit 1;; | |
312 | esac | |
313 | if [ "$opFileOut1_overwrite" == "true" ]; then | |
314 | pathFileOut1="$2"; | |
315 | shift; | |
316 | vbm "DEBUG:Output file pathFileOut1 set to:""$2"; | |
317 | else | |
318 | yell "ERORR:Exiting in order to not overwrite output file:""$pathFileOut1"; | |
319 | exit 1; | |
320 | fi | |
321 | else | |
322 | pathFileOut1="$2"; | |
323 | shift; | |
324 | vbm "DEBUG:Output file pathFileOut1 set to:""$2"; | |
325 | fi ;; | |
326 | -s | --sign) # sign with gpg | |
327 | opSign="true"; vbm "DEBUG:Signing mode enabled.";; | |
328 | -t | --timestamp) # timestamp with ots | |
07b20692 SBS |
329 | opTs="true"; vbm "DEBUG:Timestamp mode enabled.";; |
330 | -T | --timestamp-wait) # timestamp with ots with wait option | |
331 | opTs="true"; vbm "DEBUG:Timestamp mode enabled."; | |
332 | opTsWait="true"; vbm "DEBUG:Timestamp wait mode enabled.";; | |
a4fcba60 SBS |
333 | --) # End of all options. See [2]. |
334 | shift; | |
335 | for arg in "$@"; do | |
336 | vbm "DEBUG:adding to arrPosArgs:$arg"; | |
337 | arrPosArgs+=("$arg"); | |
338 | done; | |
339 | break;; | |
340 | -*) showUsage; yell "ERROR: Unrecognized option."; exit 1;; # Display usage | |
341 | *) | |
342 | for arg in "$@"; do | |
343 | vbm "DEBUG:adding to arrPosArgs:$arg"; | |
344 | arrPosArgs+=("$arg"); | |
345 | done; | |
346 | break;; | |
347 | esac; | |
348 | shift; | |
349 | done; | |
350 | ||
351 | # End function | |
352 | vbm "DEBUG:processArgs function ended." | |
353 | return 0; # Function finished. | |
354 | } # Evaluate script options from positional arguments (ex: $1, $2, $3, etc.). | |
355 | processStdin() { | |
356 | # Desc: Save stdin lines to array | |
357 | # Input: stdin standard input | |
358 | # arrStdin array for storing stdin lines | |
359 | # Output: arrStdin array for storing stdin lines | |
360 | # Ref/Attrib: [1] https://unix.stackexchange.com/a/484643 Check if no command line arguments and STDIN is empty | |
361 | ||
362 | if [[ -t 0 ]]; then | |
363 | return 1; # error if file descriptor 0 (stdin) open | |
364 | else | |
365 | while read -r line; do | |
366 | arrStdin+=("$line"); done; | |
367 | return 0; | |
368 | fi; | |
369 | }; # Save stdin to array | |
370 | checkInput() { | |
371 | # Desc: Check that files (specified by positional arguments and | |
372 | # standard input lines) exist and are in the same directory. | |
373 | # Input: arrPosArgs[@] positional arguments | |
374 | # arrStdin[@] standard input lines | |
375 | # Output: return code 0 success | |
376 | # return code 1 failure | |
377 | # arrInFiles list of verified files | |
378 | # Depends: displayMissing(), checkfile(); | |
379 | local flagMissing flagDirErr workDir; | |
380 | ||
381 | # Check that positional arguments are files | |
382 | for elem in "${arrPosArgs[@]}"; do | |
383 | if checkfile "$elem"; then | |
384 | arrInFiles+=("$elem"); | |
385 | else | |
386 | flagMissing="true"; | |
387 | fi; | |
388 | done; | |
389 | ||
390 | # Check that stdin lines are files | |
391 | for elem in "${arrStdin[@]}"; do | |
392 | if checkfile "$elem"; then | |
393 | arrInFiles+=("$elem"); | |
394 | else | |
395 | flagMissing="true"; | |
396 | fi; | |
397 | done; | |
398 | ||
399 | # Check that all files are in the same directory | |
400 | if [[ "$flagMissing" != "true" ]]; then | |
401 | workDir="$( dirname "$( readlink -f "${arrInFiles[0]}" )" )"; | |
402 | for elem in "${arrInFiles[@]}"; do | |
403 | elem="$(readlink -f "$elem")"; # full path | |
404 | if [[ "$(dirname "$elem")" != "$workDir" ]]; then | |
405 | flagDirErr="true"; | |
406 | fi; | |
407 | done; | |
408 | fi; | |
409 | ||
410 | # Return non-zero if displayMissing() reports files missing. | |
411 | if [[ "$flagMissing" == "true" ]]; then | |
412 | displayMissing; return 1; fi; | |
413 | if [[ "$flagDirErr" == "true" ]]; then | |
414 | yell "ERROR:All files not in same directory."; | |
415 | return 1; fi; | |
416 | return 0; | |
417 | }; # Check positional arguments | |
418 | checkDepends() { | |
419 | # Desc: Check if expected commands available | |
420 | ||
421 | checkapp date sha256sum; | |
422 | if [[ $opSign == "true" ]]; then checkapp gpg; fi; | |
07b20692 | 423 | if [[ $opTs == "true" ]]; then checkapp ots; fi; |
a4fcba60 SBS |
424 | |
425 | # Return failure is displayMissing() reports apps missing. | |
426 | if ! displayMissing; then return 1; else return 0; fi; | |
427 | }; # Check dependencies | |
07b20692 SBS |
428 | count_jobs() { |
429 | # Desc: Count and return total number of jobs | |
430 | # Usage: count_jobs | |
431 | # Input: None. | |
432 | # Output: stdout integer number of jobs | |
433 | # Depends: Bash 5.1.16 | |
434 | # Example: while [[$(count_jobs) -gt 0]]; do echo "Working..."; sleep 1; done; | |
435 | # Version: 0.0.1 | |
436 | ||
437 | local job_count; | |
438 | job_count="$(jobs -r | wc -l | tr -d ' ' )"; | |
439 | #yell "DEBUG:job_count:$job_count"; | |
440 | if [[ -z $job_count ]]; then job_count="0"; fi; | |
441 | echo "$job_count"; | |
442 | }; # Return number of background jobs | |
a4fcba60 SBS |
443 | main() { |
444 | # Input: pathFileOut1 option-specified output file path | |
445 | # appRollCall assoc-array for checkapp(), displayMissing() | |
446 | # fileRollCall assoc-array for checkfile(), displayMissing() | |
447 | # dirRollCall assoc-array for checkdir(), displayMissing() | |
448 | # arrPosArgs array for processArgs() | |
449 | # arrStdin array for processStdin() | |
450 | # arrInFiles array for checkInput() | |
451 | # (args) for processArgs() | |
452 | # (stdin) for processStdin() | |
453 | # Output: file written to $pathSumOut | |
454 | # file written to $pathSigOut | |
455 | # | |
456 | local fileSumOut dirOut pathSumOut pathSigOut; | |
457 | ||
458 | # Check dependencies and input | |
459 | if ! checkDepends; then die "FATAL:Missing apps."; fi; | |
460 | processArgs "$@"; | |
461 | processStdin; | |
462 | vbm "DEBUG:$(declare -p arrPosArgs)"; | |
463 | vbm "DEBUG:$(declare -p arrStdin)"; | |
464 | if ! [[ "${#arrPosArgs[@]}" -ge 1 || "${#arrStdin[@]}" -ge 1 ]]; then | |
465 | yell "ERROR:No positional arguments or stdin lines."; | |
466 | showUsage; exit 1; fi; | |
467 | if ! checkInput; then die "FATAL:Invalid file list."; fi; | |
468 | vbm "DEBUG:$(declare -p arrInFiles)"; | |
469 | ||
470 | # Do work | |
471 | if [[ -n "$pathFileOut1" ]]; then | |
472 | pathSumOut="$pathFileOut1"; | |
473 | else | |
474 | dirOut="$( dirname "${arrInFiles[0]}" )"; | |
475 | fileSumOut="$(date +%Y%m%dT%H%M%S%z)..SHA256SUMS"; | |
476 | pathSumOut="$dirOut"/"$fileSumOut"; | |
477 | fi; | |
478 | pathSigOut="$pathSumOut".asc; | |
479 | for file in "${arrInFiles[@]}"; do | |
480 | sha256sum "$file" >> "$pathSumOut"; | |
481 | done; | |
482 | ||
483 | # Do optional work | |
484 | ## Sign checksum file. | |
485 | if [[ $opSign == "true" ]]; then | |
07b20692 SBS |
486 | yell "STATUS:Signing checksum file..."; |
487 | try gpg --detach-sign --armor --output "$pathSigOut" "$pathSumOut" && yell "STATUS:Checksum created."; | |
a4fcba60 SBS |
488 | fi; |
489 | ## Timestamp checksum file. | |
07b20692 SBS |
490 | if [[ $opTs == "true" ]]; then |
491 | yell "STATUS:Timestamping checksum file..."; | |
492 | if [[ $opTsWait != "true" ]]; then | |
493 | try ots s "$pathSumOut" & | |
494 | elif [[ $opTsWait == "true" ]]; then | |
495 | yell "NOTICE:Waiting for calendar server response in background. (This may take 8 to 24 hours)..."; | |
496 | yell "ADVICE:Do not close or suspend this terminal."; | |
e9bbd5a6 | 497 | try ots --wait s "$pathSumOut" 1>/dev/random 2>&1 & |
07b20692 SBS |
498 | fi; |
499 | fi; | |
a4fcba60 | 500 | ## Timestamp checksum signature file. |
07b20692 SBS |
501 | if [[ $opTs == "true" && $opSign == "true" ]]; then |
502 | yell "STATUS:Timestamping signature file..."; | |
503 | if [[ $opTsWait != "true" ]]; then | |
504 | try ots s "$pathSigOut" && yell "STATUS:Timestamp of checksum signature file created."; | |
505 | elif [[ $opTsWait == "true" ]]; then | |
506 | yell "NOTICE:Waiting for calendar server response in background. (This may take 8 to 24 hours)..."; | |
507 | yell "ADVICE:Do not close or suspend this terminal."; | |
e9bbd5a6 | 508 | try ots --wait s "$pathSigOut" 1>/dev/random 2>&1 & |
07b20692 | 509 | fi; |
a4fcba60 SBS |
510 | fi; |
511 | ||
07b20692 SBS |
512 | ## Wait until background jobs (if any) completed |
513 | for (( n = 1; "$(count_jobs)" > 0; n++ )); do | |
514 | if ! [[ $((n % 60)) -eq 0 ]]; then | |
515 | printf "."; | |
516 | else | |
517 | printf ".%05ds passed.\n" "$SECONDS"; | |
518 | fi; | |
519 | sleep 60; | |
520 | done; | |
521 | ||
522 | yell "Done."; | |
a4fcba60 SBS |
523 | return 0; |
524 | }; # main program | |
525 | ||
526 | main "$@"; | |
527 | ||
528 | # Author: Steven Baltakatei Sandoval | |
529 | # License: GPLv3+ |