1# 2# CDDL HEADER START 3# 4# The contents of this file are subject to the terms of the 5# Common Development and Distribution License (the "License"). 6# You may not use this file except in compliance with the License. 7# 8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9# or http://www.opensolaris.org/os/licensing. 10# See the License for the specific language governing permissions 11# and limitations under the License. 12# 13# When distributing Covered Code, include this CDDL HEADER in each 14# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15# If applicable, add the following below this CDDL HEADER, with the 16# fields enclosed by brackets "[]" replaced with your own identifying 17# information: Portions Copyright [yyyy] [name of copyright owner] 18# 19# CDDL HEADER END 20# 21 22# 23# Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24# Use is subject to license terms. 25# 26# Copyright (c) 2012, 2020 by Delphix. All rights reserved. 27# 28 29. ${STF_TOOLS}/include/stf.shlib 30 31# Output an assertion 32# 33# $@ - assertion text 34 35function log_assert 36{ 37 _printline ASSERTION: "$@" 38} 39 40# Output a comment 41# 42# $@ - comment text 43 44function log_note 45{ 46 _printline NOTE: "$@" 47} 48 49# Execute and print command with status where success equals non-zero result 50# 51# $@ - command to execute 52# 53# return 0 if command fails, otherwise return 1 54 55function log_neg 56{ 57 log_neg_expect "" "$@" 58 return $? 59} 60 61# Execute a positive test and exit $STF_FAIL is test fails 62# 63# $@ - command to execute 64 65function log_must 66{ 67 log_pos "$@" 68 (( $? != 0 )) && log_fail 69} 70 71# Execute a positive test but retry the command on failure if the output 72# matches an expected pattern. Otherwise behave like log_must and exit 73# $STF_FAIL is test fails. 74# 75# $1 - retry keyword 76# $2 - retry attempts 77# $3-$@ - command to execute 78# 79function log_must_retry 80{ 81 typeset out="" 82 typeset logfile="/tmp/log.$$" 83 typeset status=1 84 typeset expect=$1 85 typeset retry=$2 86 typeset delay=1 87 shift 2 88 89 while [[ -e $logfile ]]; do 90 logfile="$logfile.$$" 91 done 92 93 while (( $retry > 0 )); do 94 "$@" 2>$logfile 95 status=$? 96 out="cat $logfile" 97 98 if (( $status == 0 )); then 99 $out | egrep -i "internal error|assertion failed" \ 100 > /dev/null 2>&1 101 # internal error or assertion failed 102 if [[ $? -eq 0 ]]; then 103 print -u2 $($out) 104 _printerror "$@" "internal error or" \ 105 " assertion failure exited $status" 106 status=1 107 else 108 [[ -n $LOGAPI_DEBUG ]] && cat $logfile 109 _printsuccess "$@" 110 fi 111 break 112 else 113 $out | grep -i "$expect" > /dev/null 2>&1 114 if (( $? == 0 )); then 115 print -u2 $($out) 116 _printerror "$@" "Retry in $delay seconds" 117 sleep $delay 118 119 (( retry=retry - 1 )) 120 (( delay=delay * 2 )) 121 else 122 break; 123 fi 124 fi 125 done 126 127 if (( $status != 0 )) ; then 128 print -u2 $($out) 129 _printerror "$@" "exited $status" 130 fi 131 132 _recursive_output $logfile "false" 133 return $status 134} 135 136# Execute a positive test and exit $STF_FAIL is test fails after being 137# retried up to 5 times when the command returns the keyword "busy". 138# 139# $@ - command to execute 140function log_must_busy 141{ 142 log_must_retry "busy" 5 "$@" 143 (( $? != 0 )) && log_fail 144} 145 146# Execute a negative test and exit $STF_FAIL if test passes 147# 148# $@ - command to execute 149 150function log_mustnot 151{ 152 log_neg "$@" 153 (( $? != 0 )) && log_fail 154} 155 156# Execute a negative test with keyword expected, and exit 157# $STF_FAIL if test passes 158# 159# $1 - keyword expected 160# $2-$@ - command to execute 161 162function log_mustnot_expect 163{ 164 log_neg_expect "$@" 165 (( $? != 0 )) && log_fail 166} 167 168# Signal numbers are platform-dependent 169case $(uname) in 170Darwin|FreeBSD) 171 SIGBUS=10 172 SIGSEGV=11 173 ;; 174illumos|Linux|*) 175 SIGBUS=7 176 SIGSEGV=11 177 ;; 178esac 179EXIT_SUCCESS=0 180EXIT_NOTFOUND=127 181EXIT_SIGNAL=256 182EXIT_SIGBUS=$((EXIT_SIGNAL + SIGBUS)) 183EXIT_SIGSEGV=$((EXIT_SIGNAL + SIGSEGV)) 184 185# Execute and print command with status where success equals non-zero result 186# or output includes expected keyword 187# 188# $1 - keyword expected 189# $2-$@ - command to execute 190# 191# return 0 if command fails, or the output contains the keyword expected, 192# return 1 otherwise 193 194function log_neg_expect 195{ 196 typeset out="" 197 typeset logfile="/tmp/log.$$" 198 typeset ret=1 199 typeset expect=$1 200 shift 201 202 while [[ -e $logfile ]]; do 203 logfile="$logfile.$$" 204 done 205 206 "$@" 2>$logfile 207 typeset status=$? 208 out="cat $logfile" 209 210 # unexpected status 211 if (( $status == EXIT_SUCCESS )); then 212 print -u2 $($out) 213 _printerror "$@" "unexpectedly exited $status" 214 # missing binary 215 elif (( $status == EXIT_NOTFOUND )); then 216 print -u2 $($out) 217 _printerror "$@" "unexpectedly exited $status (File not found)" 218 # bus error - core dump 219 elif (( $status == EXIT_SIGBUS )); then 220 print -u2 $($out) 221 _printerror "$@" "unexpectedly exited $status (Bus Error)" 222 # segmentation violation - core dump 223 elif (( $status == EXIT_SIGSEGV )); then 224 print -u2 $($out) 225 _printerror "$@" "unexpectedly exited $status (SEGV)" 226 else 227 $out | egrep -i "internal error|assertion failed" \ 228 > /dev/null 2>&1 229 # internal error or assertion failed 230 if (( $? == 0 )); then 231 print -u2 $($out) 232 _printerror "$@" "internal error or assertion failure" \ 233 " exited $status" 234 elif [[ -n $expect ]] ; then 235 $out | grep -i "$expect" > /dev/null 2>&1 236 if (( $? == 0 )); then 237 ret=0 238 else 239 print -u2 $($out) 240 _printerror "$@" "unexpectedly exited $status" 241 fi 242 else 243 ret=0 244 fi 245 246 if (( $ret == 0 )); then 247 [[ -n $LOGAPI_DEBUG ]] && cat $logfile 248 _printsuccess "$@" "exited $status" 249 fi 250 fi 251 _recursive_output $logfile "false" 252 return $ret 253} 254 255# Execute and print command with status where success equals zero result 256# 257# $@ command to execute 258# 259# return command exit status 260 261function log_pos 262{ 263 typeset out="" 264 typeset logfile="/tmp/log.$$" 265 266 while [[ -e $logfile ]]; do 267 logfile="$logfile.$$" 268 done 269 270 "$@" 2>$logfile 271 typeset status=$? 272 out="cat $logfile" 273 274 if (( $status != 0 )) ; then 275 print -u2 $($out) 276 _printerror "$@" "exited $status" 277 else 278 $out | egrep -i "internal error|assertion failed" \ 279 > /dev/null 2>&1 280 # internal error or assertion failed 281 if [[ $? -eq 0 ]]; then 282 print -u2 $($out) 283 _printerror "$@" "internal error or assertion failure" \ 284 " exited $status" 285 status=1 286 else 287 [[ -n $LOGAPI_DEBUG ]] && cat $logfile 288 _printsuccess "$@" 289 fi 290 fi 291 _recursive_output $logfile "false" 292 return $status 293} 294 295# Set an exit handler 296# 297# $@ - function(s) to perform on exit 298 299function log_onexit 300{ 301 _CLEANUP=("$*") 302} 303 304# Push an exit handler on the cleanup stack 305# 306# $@ - function(s) to perform on exit 307 308function log_onexit_push 309{ 310 _CLEANUP+=("$*") 311} 312 313# Pop an exit handler off the cleanup stack 314 315function log_onexit_pop 316{ 317 _CLEANUP=("${_CLEANUP[@]:0:${#_CLEANUP[@]}-1}") 318} 319 320# 321# Exit functions 322# 323 324# Perform cleanup and exit $STF_PASS 325# 326# $@ - message text 327 328function log_pass 329{ 330 _endlog $STF_PASS "$@" 331} 332 333# Perform cleanup and exit $STF_FAIL 334# 335# $@ - message text 336 337function log_fail 338{ 339 _endlog $STF_FAIL "$@" 340} 341 342# Perform cleanup and exit $STF_UNRESOLVED 343# 344# $@ - message text 345 346function log_unresolved 347{ 348 _endlog $STF_UNRESOLVED "$@" 349} 350 351# Perform cleanup and exit $STF_NOTINUSE 352# 353# $@ - message text 354 355function log_notinuse 356{ 357 _endlog $STF_NOTINUSE "$@" 358} 359 360# Perform cleanup and exit $STF_UNSUPPORTED 361# 362# $@ - message text 363 364function log_unsupported 365{ 366 _endlog $STF_UNSUPPORTED "$@" 367} 368 369# Perform cleanup and exit $STF_UNTESTED 370# 371# $@ - message text 372 373function log_untested 374{ 375 _endlog $STF_UNTESTED "$@" 376} 377 378# Perform cleanup and exit $STF_UNINITIATED 379# 380# $@ - message text 381 382function log_uninitiated 383{ 384 _endlog $STF_UNINITIATED "$@" 385} 386 387# Perform cleanup and exit $STF_NORESULT 388# 389# $@ - message text 390 391function log_noresult 392{ 393 _endlog $STF_NORESULT "$@" 394} 395 396# Perform cleanup and exit $STF_WARNING 397# 398# $@ - message text 399 400function log_warning 401{ 402 _endlog $STF_WARNING "$@" 403} 404 405# Perform cleanup and exit $STF_TIMED_OUT 406# 407# $@ - message text 408 409function log_timed_out 410{ 411 _endlog $STF_TIMED_OUT "$@" 412} 413 414# Perform cleanup and exit $STF_OTHER 415# 416# $@ - message text 417 418function log_other 419{ 420 _endlog $STF_OTHER "$@" 421} 422 423function set_main_pid 424{ 425 _MAINPID=$1 426} 427 428# 429# Internal functions 430# 431 432# Execute custom callback scripts on test failure 433# 434# callback script paths are stored in TESTFAIL_CALLBACKS, delimited by ':'. 435 436function _execute_testfail_callbacks 437{ 438 typeset callback 439 440 print "$TESTFAIL_CALLBACKS:" | while read -d ":" callback; do 441 if [[ -n "$callback" ]] ; then 442 log_note "Performing test-fail callback ($callback)" 443 $callback 444 fi 445 done 446} 447 448# Perform cleanup and exit 449# 450# $1 - stf exit code 451# $2-$n - message text 452 453function _endlog 454{ 455 typeset logfile="/tmp/log.$$" 456 _recursive_output $logfile 457 458 typeset exitcode=$1 459 shift 460 (( ${#@} > 0 )) && _printline "$@" 461 462 # 463 # If we're running in a subshell then just exit and let 464 # the parent handle the failures 465 # 466 if [[ -n "$_MAINPID" && $$ != "$_MAINPID" ]]; then 467 log_note "subshell exited: "$_MAINPID 468 exit $exitcode 469 fi 470 471 if [[ $exitcode == $STF_FAIL ]] ; then 472 _execute_testfail_callbacks 473 fi 474 475 typeset stack=("${_CLEANUP[@]}") 476 log_onexit "" 477 typeset i=${#stack[@]} 478 while (( i-- )); do 479 typeset cleanup="${stack[i]}" 480 log_note "Performing local cleanup via log_onexit ($cleanup)" 481 $cleanup 482 done 483 484 exit $exitcode 485} 486 487# Output a formatted line 488# 489# $@ - message text 490 491function _printline 492{ 493 print "$@" 494} 495 496# Output an error message 497# 498# $@ - message text 499 500function _printerror 501{ 502 _printline ERROR: "$@" 503} 504 505# Output a success message 506# 507# $@ - message text 508 509function _printsuccess 510{ 511 _printline SUCCESS: "$@" 512} 513 514# Output logfiles recursively 515# 516# $1 - start file 517# $2 - indicate whether output the start file itself, default as yes. 518 519function _recursive_output #logfile 520{ 521 typeset logfile=$1 522 523 while [[ -e $logfile ]]; do 524 if [[ -z $2 || $logfile != $1 ]]; then 525 cat $logfile 526 fi 527 rm -f $logfile 528 logfile="$logfile.$$" 529 done 530} 531