1#!/bin/bash 2# 3# 4# This file and its contents are supplied under the terms of the 5# Common Development and Distribution License ("CDDL"), version 1.0. 6# You may only use this file in accordance with the terms of version 7# 1.0 of the CDDL. 8# 9# A full copy of the text of the CDDL should have accompanied this 10# source. A copy is of the CDDL is also available via the Internet 11# at http://www.illumos.org/license/CDDL. 12# 13 14# 15# Copyright 2010 Chris Love. All rights reserved. 16# Copyright (c) 2013, Joyent, Inc. All rights reserved. 17# 18 19checktest() 20{ 21 local actual=$1 22 local output=$2 23 local test=$3 24 25 if [[ "$actual" != "$output" ]]; then 26 echo "$CMD: test $test: FAIL" 27 echo -e "$CMD: test $test: expected output:\n$output" 28 echo -e "$CMD: test $test: actual output:\n$actual" 29 else 30 echo "$CMD: test $test: pass" 31 fi 32} 33 34# 35# Test cases for 'tail', some based on CoreUtils test cases (validated 36# with legacy Solaris 'tail' and/or xpg4 'tail'). Note that this is designed 37# to be able to run on BSD systems as well to check our behavior against 38# theirs (some behavior that is known to be idiosyncratic to illumos is 39# skipped on non-illumos systems). 40# 41PROG=/usr/bin/tail 42CMD=`basename $0` 43DIR="" 44 45while [[ $# -gt 0 ]]; do 46 case $1 in 47 -x) 48 PROG=/usr/xpg4/bin/tail 49 shift 50 ;; 51 -o) 52 PROG=$2 53 shift 2 54 ;; 55 -d) 56 DIR=$2 57 shift 2 58 ;; 59 *) 60 echo "Usage: tailtests.sh" \ 61 "[-x][-o <override tail executable>]" \ 62 "[-d <override output directory>]" 63 exit 1 64 ;; 65 esac 66done 67 68# 69# Shut bash up upon receiving a term so we can drop it on our children 70# without disrupting the output. 71# 72trap "exit 0" TERM 73 74echo "$CMD: program is $PROG" 75 76if [[ $DIR != "" ]]; then 77 echo "$CMD: directory is $DIR" 78fi 79 80o=`echo -e "bcd"` 81a=`echo -e "abcd" | $PROG +2c` 82checktest "$a" "$o" 1 83 84o=`echo -e ""` 85a=`echo "abcd" | $PROG +8c` 86checktest "$a" "$o" 2 87 88o=`echo -e "abcd"` 89a=`echo "abcd" | $PROG -9c` 90checktest "$a" "$o" 3 91 92o=`echo -e "x"` 93a=`echo -e "x" | $PROG -1l` 94checktest "$a" "$o" 4 95 96o=`echo -e "\n"` 97a=`echo -e "x\ny\n" | $PROG -1l` 98checktest "$a" "$o" 5 99 100o=`echo -e "y\n"` 101a=`echo -e "x\ny\n" | $PROG -2l` 102checktest "$a" "$o" 6 103 104o=`echo -e "y"` 105a=`echo -e "x\ny" | $PROG -1l` 106checktest "$a" "$o" 7 107 108o=`echo -e "x\ny\n"` 109a=`echo -e "x\ny\n" | $PROG +1l` 110checktest "$a" "$o" 8 111 112o=`echo -e "y\n"` 113a=`echo -e "x\ny\n" | $PROG +2l` 114checktest "$a" "$o" 9 115 116o=`echo -e "x"` 117a=`echo -e "x" | $PROG -1` 118checktest "$a" "$o" 10 119 120o=`echo -e "\n"` 121a=`echo -e "x\ny\n" | $PROG -1` 122checktest "$a" "$o" 11 123 124o=`echo -e "y\n"` 125a=`echo -e "x\ny\n" | $PROG -2` 126checktest "$a" "$o" 12 127 128o=`echo -e "y"` 129a=`echo -e "x\ny" | $PROG -1` 130checktest "$a" "$o" 13 131 132o=`echo -e "x\ny\n"` 133a=`echo -e "x\ny\n" | $PROG +1` 134checktest "$a" "$o" 14 135 136o=`echo -e "y\n"` 137a=`echo -e "x\ny\n" | $PROG +2` 138checktest "$a" "$o" 15 139 140o=`echo -e "yyz"` 141a=`echo -e "xyyyyyyyyyyz" | $PROG +10c` 142checktest "$a" "$o" 16 143 144o=`echo -e "y\ny\nz"` 145a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +10l` 146checktest "$a" "$o" 17 147 148o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"` 149a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -10l` 150checktest "$a" "$o" 18 151 152# 153# For reasons that are presumably as accidental as they are ancient, legacy 154# (and closed) Solaris tail(1) allows +c, +l and -l to be aliases for +10c, 155# +10l and -10l, respectively. If we are on SunOS, verify that this silly 156# behavior is functional. 157# 158if [[ `uname -s` == "SunOS" ]]; then 159 o=`echo -e "yyz"` 160 a=`echo -e "xyyyyyyyyyyz" | $PROG +c` 161 checktest "$a" "$o" 16a 162 163 o=`echo -e "y\ny\nz"` 164 a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG +l` 165 checktest "$a" "$o" 17a 166 167 o=`echo -e "y\ny\ny\ny\ny\ny\ny\ny\ny\nz"` 168 a=`echo -e "x\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\nz" | $PROG -l` 169 checktest "$a" "$o" 18a 170fi 171 172o=`echo -e "c\nb\na"` 173a=`echo -e "a\nb\nc" | $PROG -r` 174checktest "$a" "$o" 19 175 176# 177# Now we want to do a series of follow tests. 178# 179if [[ $DIR == "" ]]; then 180 export TMPDIR=/var/tmp 181 tdir=$(mktemp -d -t tailtest.XXXXXXXX || exit 1) 182else 183 tdir=$(mktemp -d $DIR/tailtest.XXXXXXXX || exit 1) 184fi 185 186follow=$tdir/follow 187moved=$tdir/follow.moved 188out=$tdir/out 189 190# 191# First, verify that following works in its most basic sense. 192# 193echo -e "a\nb\nc" > $follow 194$PROG -f $follow > $out 2> /dev/null & 195child=$! 196sleep 2 197echo -e "d\ne\nf" >> $follow 198sleep 1 199kill $child 200sleep 1 201 202o=`echo -e "a\nb\nc\nd\ne\nf\n"` 203a=`cat $out` 204checktest "$a" "$o" 20 205rm $follow 206 207# 208# Now verify that following correctly follows the file being moved. 209# 210echo -e "a\nb\nc" > $follow 211$PROG -f $follow > $out 2> /dev/null & 212child=$! 213sleep 2 214mv $follow $moved 215 216echo -e "d\ne\nf" >> $moved 217sleep 1 218kill $child 219sleep 1 220 221o=`echo -e "a\nb\nc\nd\ne\nf\n"` 222a=`cat $out` 223checktest "$a" "$o" 21 224rm $moved 225 226# 227# And now truncation with the new offset being less than the old offset. 228# 229echo -e "a\nb\nc" > $follow 230$PROG -f $follow > $out 2> /dev/null & 231child=$! 232sleep 2 233echo -e "d\ne\nf" >> $follow 234sleep 1 235echo -e "g\nh\ni" > $follow 236sleep 1 237kill $child 238sleep 1 239 240o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` 241a=`cat $out` 242checktest "$a" "$o" 22 243rm $follow 244 245# 246# And truncation with the new offset being greater than the old offset. 247# 248echo -e "a\nb\nc" > $follow 249sleep 1 250$PROG -f $follow > $out 2> /dev/null & 251child=$! 252sleep 2 253echo -e "d\ne\nf" >> $follow 254sleep 1 255echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $follow 256sleep 1 257kill $child 258sleep 1 259 260o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"` 261a=`cat $out` 262checktest "$a" "$o" 23 263rm $follow 264 265# 266# Verify that we can follow the moved file and correctly see a truncation. 267# 268echo -e "a\nb\nc" > $follow 269$PROG -f $follow > $out 2> /dev/null & 270child=$! 271sleep 2 272mv $follow $moved 273 274echo -e "d\ne\nf" >> $moved 275sleep 1 276echo -e "g\nh\ni\nj\nk\nl\nm\no\np\nq" > $moved 277sleep 1 278kill $child 279sleep 1 280 281o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\no\np\nq"` 282a=`cat $out` 283checktest "$a" "$o" 24 284rm $moved 285 286# 287# Verify that capital-F follow properly deals with truncation 288# 289echo -e "a\nb\nc" > $follow 290$PROG -F $follow > $out 2> /dev/null & 291child=$! 292sleep 2 293echo -e "d\ne\nf" >> $follow 294sleep 1 295echo -e "g\nh\ni" > $follow 296sleep 1 297kill $child 298sleep 1 299 300o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` 301a=`cat $out` 302checktest "$a" "$o" 25 303rm $follow 304 305# 306# Verify that capital-F follow _won't_ follow the moved file and will 307# correctly deal with recreation of the original file. 308# 309echo -e "a\nb\nc" > $follow 310$PROG -F $follow > $out 2> /dev/null & 311child=$! 312sleep 2 313mv $follow $moved 314 315echo -e "x\ny\nz" >> $moved 316 317# 318# At this point, tail is polling on stat'ing the missing file; we need to 319# be sure to sleep long enough after recreating it to know that it will pick 320# it up. 321# 322echo -e "d\ne\nf" > $follow 323sleep 5 324kill $child 325sleep 1 326 327o=`echo -e "a\nb\nc\nd\ne\nf\n"` 328a=`cat $out` 329checktest "$a" "$o" 26 330rm $moved 331 332# 333# Verify that following two files works. 334# 335echo -e "one" > $follow 336echo -e "two" > $moved 337$PROG -f $follow $moved > $out 2> /dev/null & 338child=$! 339sleep 2 340echo -e "three" >> $follow 341sleep 1 342echo -e "four" >> $moved 343sleep 1 344echo -e "five" >> $follow 345sleep 1 346kill $child 347sleep 1 348 349# There is a bug where the content comes before the header lines, 350# where rlines/mapprint happens before the header. A pain to fix. 351# In this test, just make sure we see both files change. 352o="one 353 354==> $follow <== 355two 356 357==> $moved <== 358 359==> $follow <== 360three 361 362==> $moved <== 363four 364 365==> $follow <== 366five" 367a=`cat $out` 368checktest "$a" "$o" 27 369rm $follow $moved 370 371if [[ `uname -s` == "SunOS" ]]; then 372 # 373 # Use DTrace to truncate the file between the return from port_get() 374 # and the reassociation of the file object with the port, exposing 375 # any race conditions whereby FILE_TRUNC events are lost. 376 # 377 cat /dev/null > $follow 378 dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF 379 #pragma D option destructive 380 #pragma D option quiet 381 382 pid\$target::port_get:return 383 /++i == 5/ 384 { 385 stop(); 386 system("cat /dev/null > $follow"); 387 system("prun %d", pid); 388 } 389 390 tick-1sec 391 { 392 system("echo %d >> $follow", j++); 393 } 394 395 tick-1sec 396 /j == 10/ 397 { 398 exit(0); 399 } 400EOF 401 402 o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"` 403 a=`cat $out` 404 checktest "$a" "$o" 27a 405 rm $follow 406 407 cat /dev/null > $follow 408 dtrace -c "$PROG -f $follow" -s /dev/stdin > $out <<EOF 409 #pragma D option destructive 410 #pragma D option quiet 411 412 pid\$target::port_get:return 413 /++i == 5/ 414 { 415 stop(); 416 system("mv $follow $moved"); 417 system("cat /dev/null > $moved"); 418 system("prun %d", pid); 419 } 420 421 tick-1sec 422 { 423 system("echo %d >> %s", j++, 424 i < 5 ? "$follow" : "$moved"); 425 } 426 427 tick-1sec 428 /j == 10/ 429 { 430 exit(0); 431 } 432EOF 433 434 o=`echo -e "0\n1\n2\n3\n5\n6\n7\n8\n9\n"` 435 a=`cat $out` 436 checktest "$a" "$o" 27b 437 rm $moved 438 439 # 440 # Verify that -F will deal properly with the file being truncated 441 # not by truncation, but rather via an ftruncate() from logadm. 442 # 443 cat /dev/null > $follow 444 ( $PROG -F $follow > $out ) & 445 child=$! 446 echo -e "a\nb\nc\nd\ne\nf" >> $follow 447 logadm -c $follow 448 sleep 2 449 echo -e "g\nh\ni" >> $follow 450 sleep 2 451 kill $child 452 sleep 1 453 454 o=`echo -e "a\nb\nc\nd\ne\nf\ng\nh\ni\n"` 455 a=`cat $out` 456 checktest "$a" "$o" 27c 457fi 458 459# 460# We're now going to test that while we may miss output due to truncations 461# occurring faster than tail can read, we don't ever repeat output. 462# 463cat /dev/null > $follow 464( $PROG -f $follow > $out ) & 465tchild=$! 466( let i=0 ; while true; do echo $i > $follow ; sleep 0.1; let i=i+1 ; done ) & 467child=$! 468sleep 10 469kill $tchild 470kill $child 471 472a=`sort $out | uniq -c | sort -n | tail -1 | awk '{ print $1 }'` 473o=1 474 475checktest "$a" "$o" 28 476 477echo "$CMD: completed" 478 479exit $errs 480 481