123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- #!/bin/bash
-
- shellfu import jat
- shellfu import pretty
-
- #
- # Helpers for dumping files to log
- #
- # This module provides several functions to enable showing
- # contents of (supposedly small) files or command outputs directly
- # inside JAT log.
- #
- # This can be very helpful, especially if you know you are dealing
- # with relatively small files or to provide generic diagnostic
- # data.
- #
- # For example:
- #
- # some_command >out 2>err
- # jat__eval "grep foo out"
- # jat__eval "grep bar out" 1
- # jat__eval "test -s err" 1
- # jat_dump__file out err
- #
- # Here, if your asserts fail, it will be very useful to see the content
- # right in the log.
- #
- # Another example (from real life):
- #
- # rlPhaseStartTest infodump
- # ps -C cimserver -o euser,ruser,suser,fuser,f,comm,label \
- # | jat_dump__pipe PS_SECINFO
- # ps -C cimserver -f \
- # | jat_dump__pipe PS_F
- # ps -C cimserver -Lf \
- # | jat_dump__pipe PS_THREADS
- # ps -ef \
- # | jat_dump__pipe PS_EF
- # jat_dump__file /etc/passwd /etc/group
- # rlPhaseEnd
- #
- # Here, in one phase -- without getting in the way of other code
- # we can get reasonable profile of the cimserver process and
- # basic system setup.
- #
-
- #
- # Pipe via hexdump before printing?
- #
- # `always` to enforce all files through hexdump, `never` to disable
- # hexdump, `auto` to have the filetype detected.
- #
- JAT_DUMP__HEXMODE=${JAT_DUMP__HEXMODE:-auto}
-
-
- #
- # Command to use for hex dumping
- #
- JAT_DUMP__HEXCMD=${JAT_DUMP__HEXCMD:-hexdump -C}
-
-
- #
- # Limit in lines before jat__submit() is used instead of printing
- #
- JAT_DUMP__LIMIT_L=${JAT_DUMP__LIMIT_L:-100}
-
-
- #
- # Limit in bytes before jat__submit() is used instead of printing
- #
- JAT_DUMP__LIMIT_B=${JAT_DUMP__LIMIT_B:-10000}
-
-
- #
- # Function to use for logging
- #
- JAT_DUMP__DMPFUN=${JAT_DUMP__DMPFUN:-jat__log_info}
-
-
- jat_dump__file() {
- #
- # Dump contents of given file(s) using jat__log_info
- #
- # Usage:
- #
- # jat_dump__file [options] FILE...
- #
- # Description:
- #
- # Print contents of given file(s) to the main TESTOUT.log
- # using jat_log* functions and adding BEGIN/END delimiters.
- # If a file is too big, use jat__submit() instead. If a file
- # is binary, pipe it through `hexdump -C`.
- #
- # Options:
- #
- # * -h, --hex
- # * -H, --no-hex
- # enforce or ban hexdump for all files
- # * -b BYTES, --max-bytes BYTES
- # * -l LINES, --max-lines LINES
- # set limit to lines or bytes; if file is bigger,
- # use jat__submit() instead
- # * -I, --info
- # * -E, --error
- # * -D, --debug
- # use different level of jat_log* (Info by default)
- # * -s, --skip-empty
- # do not do anything if file is empty
- # * -S, --skip-missing
- # do not do anything if file is missing
- #
- local hexmode=$JAT_DUMP__HEXMODE
- local limit_b=$JAT_DUMP__LIMIT_B
- local limit_l=$JAT_DUMP__LIMIT_L
- local dmpfun="$JAT_DUMP__DMPFUN"
- local skip_empty=false
- local skip_missing=false
- while true; do case "$1" in
- -h|--hex) hexmode=always; shift 1 ;;
- -H|--no-hex) hexmode=never; shift 1 ;;
- -b|--max-bytes) limit_b=$2; shift 2 ;;
- -l|--max-lines) limit_l=$2; shift 2 ;;
- -I|--info) dmpfun=jat__log_info; shift 1 ;;
- -E|--error) dmpfun=jat__log_error; shift 1 ;;
- -D|--debug) dmpfun=debug; shift 1 ;;
- -s|--skip-empty) skip_empty=true; shift 1 ;;
- -S|--skip-missing) skip_missing=true; shift 1 ;;
- *) break ;;
- esac done
-
- local fpath # path to original file
- local hexdump # path to hexdumped file, if needed
-
- for fpath in "$@";
- do
-
- # skipping logic
- ! test -e "$fpath" && $skip_missing && continue
- test -e "$fpath" || { jat__log_error "no such file: $fpath"; continue; }
- test -f "$fpath" || { jat__log_error "not a file: $fpath"; continue; }
- ! test -s "$fpath" && $skip_empty && continue
-
- if __jat_dump__use_hex "$hexmode" "$fpath";
- then
- hexdump=$(__jat_dump__mkhexdump < "$fpath")
- __jat_dump__logobj HEXDUMP "$hexdump" "$fpath" "$fpath"
- rm "$hexdump"
- else
- __jat_dump__logobj FILE "$fpath" "$fpath"
- fi
-
- done
- }
-
- jat_dump__mkphase() {
- #
- # Make a dump phase incl. JAT formalities
- #
- # Usage:
- # jat_dump__mkphase [-n NAME] FILE...
- #
- # Create a separate diag phase called NAME ('dump' by
- # default) and dump all listed files there.
- #
- local name="dump" # phase name, should you not like dump
- while true; do case "$1" in
- -n|--name) name=$2; shift 2 ;;
- *) break ;;
- esac done
- jat__pstartd "$name"
- jat_dump__file "$@"
- jat__pend
- }
-
- jat_dump__pipe() {
- #
- # Dump contents passed to stdin using jat__log_info
- #
- # Usage:
- #
- # some_command | jat_dump__pipe [options] [NAME]
- #
- # Description:
- #
- # Cache contents of stream given on STDIN and print them to
- # the main TESTOUT.log using jat__log* functions and adding
- # BEGIN/END delimiters. If the content is too big, use
- # jat__submit() instead. If the content is binary, pipe it
- # through `hexdump -C`.
- #
- # NAME will appear along with pipe content delimiters, and
- # if a limit is reached, will be used as name for file to
- # store using jat__submit(). NAME will be generated if
- # omitted.
- #
- # Options:
- #
- # * -h, --hex
- # * -H, --no-hex
- # enforce or ban hexdump for all files
- # * -b BYTES, --max-bytes BYTES
- # * -l LINES, --max-lines LINES
- # set limit to lines or bytes; if file is bigger,
- # use jat__submit() instead
- # * -I, --info
- # * -E, --error
- # * -D, --debug
- # use different level of jat__log* (Info by default)
- #
- local hexmode=$JAT_DUMP__HEXMODE
- local limit_b=$JAT_DUMP__LIMIT_B
- local limit_l=$JAT_DUMP__LIMIT_L
- local dmpfun="$JAT_DUMP__DMPFUN"
- local cache
- while true; do case "$1" in
- -h|--hex) hexmode=always; shift 1 ;;
- -H|--no-hex) hexmode=never; shift 1 ;;
- -b|--max-bytes) limit_b=$2; shift 2 ;;
- -l|--max-lines) limit_l=$2; shift 2 ;;
- -I|--info) dmpfun=jat__log_info; shift 1 ;;
- -E|--error) dmpfun=jat__log_error; shift 1 ;;
- -D|--debug) dmpfun=debug; shift 1 ;;
- *) break ;;
- esac done
- local name="$1"
-
- # cache the stream
- cache=$(mktemp -t jat_dump-cache.XXXXXXXXXX)
- cat > "$cache"
-
- # make up name if not given (re-use XXXXXXXXXX from $cache)
- test -n "$name" || name="ANONYMOUS.$$.${cache##*.}"
-
- if __jat_dump__use_hex "$hexmode" "$cache";
- then
- hexdump=$(__jat_dump__mkhexdump < "$cache")
- __jat_dump__logobj HEXDUMP "$hexdump" "$name" "$cache"
- rm "$hexdump"
- else
- __jat_dump__logobj PIPE "$cache" "$name"
- fi
-
- rm "$cache"
- }
-
-
- ## ## starts behind this strip ##
- ## museum of INTERNAL ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
- ## '''''''' ## no admission; you can steal but not touch anything ##
-
- __jat_dump__count() {
- #
- # Count sizes, compare to limits and print problem report if any
- #
- #inherits: limit_l limit_b
- local file="$1"
- local sizes size_b size_l
- local over_b over_l many
- sizes="$(wc -lc <"$file")" # [newline] [bytes]
- sizes="$(eval "echo $sizes")" # squash + trim whites
- size_b="${sizes##* }"
- size_l="${sizes%% *}"
- test "$size_b" -gt "$limit_b"; over_b=$?
- test "$size_l" -gt "$limit_l"; over_l=$?
- case "$over_b:$over_l" in
- 1:1) many="" ;;
- 1:0) many="lines ($size_l)" ;;
- 0:1) many="bytes ($size_b)" ;;
- 0:0) many="lines ($size_l) and bytes ($size_b)" ;;
- *) jat__log_error "panic: ${FUNCNAME[1]}" ;;
- esac
- echo "$many"
- test -z "$many"
- }
-
- __jat_dump__is_binary() {
- #
- # Check if file is binary
- #
- local path="$1"
- test -s "$path" || return 1
- grep -qI . "$path" && return 1
- return 0
- }
-
- __jat_dump__logobj() {
- #
- # Dump the object type $1 from $2, or save it under name $3
- #
- # Type can be FILE, HEXDUMP or PIPE. If object is too big to dump
- # to log, it will be submitted using jat__submit(). in that case
- # $3 may be specified as alternative name.
- #
- # In case of HEXDUMP, path to original file must be specified
- # as $4, since in case file is too big, we want to submitt *both*
- # the original and the hexdump (suffixed with .hex).
- #
- # FIXME: no way of dumping HEXDUMP without supplying alternate name
- #
- # In case of PIPE that needs to be submitted, suffiix ".pipe" is added
- # to the filename.
- #
- # FIXME: Account for PIPE that gets HEXDUMPed (now be treated as file)
- #
- local otype=$1 # PIPE|HEXDUMP|FILE
- local opath=$2 # printable data
- local oname=${3:-$opath} # chosen name
- local oorig=$4 # original file in case of HEXDUMP
- local bloat="" # human description of limit excess; empty
- # .. means OK to dump to log; else use jat__submit()
- local llines=() # logged lines
-
- oname="$(tr / - <<<"$oname")"
- bloat="$(__jat_dump__count "$opath")"
-
- debug "${FUNCNAME[1]}:otype='$otype'"
- debug "${FUNCNAME[1]}:opath='$opath'"
- debug "${FUNCNAME[1]}:oname='$oname'"
- debug "${FUNCNAME[1]}:oorig='$oorig'"
- debug "${FUNCNAME[1]}:bloat='$bloat'"
-
- # start outputting
- if test -z "$bloat";
- then
- test -s "$opath" || {
- case $dmpfun in
- debug) debug "=====EMPTY $otype $oname=====" ;;
- *) jat__log_info "=====EMPTY $otype $oname=====" ;;
- esac
- return
- }
- {
- echo "=====BEGIN $otype $oname====="
- cat "$opath"
- test -n "$(tail -c 1 "$opath")" \
- && echo "=====NO_NEWLINE_AT_EOF====="
- echo "=====END $otype $oname====="
- } \
- | {
- readarray -t llines
- $dmpfun "${llines[@]}"
- }
- else
- case $otype in
- FILE)
- jat__log_info "not dumping, file has too many $bloat: $opath"
- jat__submit "$opath" "$oname"
- ;;
- PIPE)
- jat__log_info "not dumping, pipe has too many $bloat: $oname"
- jat__submit "$opath" "$oname.pipe"
- ;;
- HEXDUMP)
- jat__log_info "not dumping, hexdump is too long for: $oorig"
- jat__submit "$opath" "$oname.hex"
- jat__submit "$oorig" "$oname"
- ;;
- esac
- fi
-
- }
-
- __jat_dump__mkhexdump() {
- #
- # Create hexdump (as tempfile) from stdin and echo path to it
- #
- local hdtmp
- hdtmp=$(mktemp -t jat_dump-hex.XXXXXXXXXX)
- $JAT_DUMP__HEXCMD > "$hdtmp"
- echo "$hdtmp"
- }
-
- __jat_dump__use_hex() {
- #
- # True if we need to use hexdump
- #
- local hexmode="$1"
- local fpath="$2"
- case "$hexmode" in
- always) return 0 ;;
- never) return 1 ;;
- auto) __jat_dump__is_binary "$fpath"; return $? ;;
- *) jat__log_error "bad value of JAT_DUMP__HEXMODE: $hexmode" ;;
- esac
- }
-
- #shellfu module-version=__MKIT_PROJ_VERSION__
|