1 1.1 christos #!/bin/sh 2 1.1 christos # 3 1.1 christos # Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4 1.1 christos # 5 1.1 christos # SPDX-License-Identifier: MPL-2.0 6 1.1 christos # 7 1.1 christos # This Source Code Form is subject to the terms of the Mozilla Public 8 1.1 christos # License, v. 2.0. If a copy of the MPL was not distributed with this 9 1.1 christos # file, you can obtain one at https://mozilla.org/MPL/2.0/. 10 1.1 christos # 11 1.1 christos # See the COPYRIGHT file distributed with this work for additional 12 1.1 christos # information regarding copyright ownership. 13 1.1 christos 14 1.1 christos # When sourcing the script outside the pytest environment (e.g. during helper 15 1.1 christos # script development), the env variables have to be loaded. 16 1.1 christos if [ -z "$TOP_SRCDIR" ]; then 17 1.1 christos SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd | sed -E 's|(.*bin/tests/system).*|\1|') 18 1.1 christos eval "$(PYTHONPATH="$SCRIPT_DIR:$PYTHONPATH" /usr/bin/env python3 -m isctest)" 19 1.1 christos fi 20 1.1 christos 21 1.1 christos testsock6() { 22 1.1 christos if test -n "$PERL" && $PERL -e "use IO::Socket::IP;" 2>/dev/null; then 23 1.1 christos $PERL "$TOP_SRCDIR/bin/tests/system/testsock6.pl" "$@" 24 1.1 christos else 25 1.1 christos false 26 1.1 christos fi 27 1.1 christos } 28 1.1 christos 29 1.1 christos echofail() { 30 1.1 christos echo "$*" 31 1.1 christos } 32 1.1 christos echowarn() { 33 1.1 christos echo "$*" 34 1.1 christos } 35 1.1 christos echopass() { 36 1.1 christos echo "$*" 37 1.1 christos } 38 1.1 christos echoinfo() { 39 1.1 christos echo "$*" 40 1.1 christos } 41 1.1 christos echostart() { 42 1.1 christos echo "$*" 43 1.1 christos } 44 1.1 christos echoend() { 45 1.1 christos echo "$*" 46 1.1 christos } 47 1.1 christos 48 1.1 christos echo_i() { 49 1.1 christos echo "$@" | while IFS= read -r __LINE; do 50 1.1 christos echoinfo "I:$__LINE" 51 1.1 christos done 52 1.1 christos } 53 1.1 christos 54 1.1 christos echo_ic() { 55 1.1 christos echo "$@" | while IFS= read -r __LINE; do 56 1.1 christos echoinfo "I: $__LINE" 57 1.1 christos done 58 1.1 christos } 59 1.1 christos 60 1.1 christos echo_d() { 61 1.1 christos echo "$@" | while IFS= read -r __LINE; do 62 1.1 christos echoinfo "D:$__LINE" 63 1.1 christos done 64 1.1 christos } 65 1.1 christos 66 1.1 christos cat_i() { 67 1.1 christos while IFS= read -r __LINE; do 68 1.1 christos echoinfo "I:$__LINE" 69 1.1 christos done 70 1.1 christos } 71 1.1 christos 72 1.1 christos cat_d() { 73 1.1 christos while IFS= read -r __LINE; do 74 1.1 christos echoinfo "D:$__LINE" 75 1.1 christos done 76 1.1 christos } 77 1.1 christos 78 1.1 christos digcomp() { 79 1.1 christos { 80 1.1 christos output=$($PERL $TOP_SRCDIR/bin/tests/system/digcomp.pl "$@") 81 1.1 christos result=$? 82 1.1 christos } || true 83 1.1 christos [ -n "$output" ] && { 84 1.1 christos echo "digcomp failed:" 85 1.1 christos echo "$output" 86 1.1 christos } | cat_i 87 1.1 christos return $result 88 1.1 christos } 89 1.1 christos 90 1.1 christos start_server() { 91 1.1 christos $PERL "$TOP_SRCDIR/bin/tests/system/start.pl" "$SYSTESTDIR" "$@" 92 1.1 christos } 93 1.1 christos 94 1.1 christos stop_server() { 95 1.1 christos $PERL "$TOP_SRCDIR/bin/tests/system/stop.pl" "$SYSTESTDIR" "$@" 96 1.1 christos } 97 1.1 christos 98 1.1 christos send() { 99 1.1 christos $PERL "$TOP_SRCDIR/bin/tests/system/send.pl" "$@" 100 1.1 christos } 101 1.1 christos 102 1.1 christos # 103 1.1 christos # Useful functions in test scripts 104 1.1 christos # 105 1.1 christos 106 1.1 christos # assert_int_equal: compare two integer variables, $1 and $2 107 1.1 christos # 108 1.1 christos # If $1 and $2 are equal, return 0; if $1 and $2 are not equal, report 109 1.1 christos # the error using the description of the tested variable provided in $3 110 1.1 christos # and return 1. 111 1.1 christos assert_int_equal() { 112 1.1 christos found="$1" 113 1.1 christos expected="$2" 114 1.1 christos description="$3" 115 1.1 christos 116 1.1 christos if [ "${expected}" -ne "${found}" ]; then 117 1.1 christos echo_i "incorrect ${description}: got ${found}, expected ${expected}" 118 1.1 christos return 1 119 1.1 christos fi 120 1.1 christos 121 1.1 christos return 0 122 1.1 christos } 123 1.1 christos 124 1.1 christos # keyfile_to_keys_section: helper function for keyfile_to_*_keys() which 125 1.1 christos # converts keyfile data into a key-style trust anchor configuration 126 1.1 christos # section using the supplied parameters 127 1.1 christos keyfile_to_keys() { 128 1.1 christos section_name=$1 129 1.1 christos key_prefix=$2 130 1.1 christos shift 131 1.1 christos shift 132 1.1 christos echo "$section_name {" 133 1.1 christos for keyname in $*; do 134 1.1 christos awk '!/^; /{ 135 1.1 christos printf "\t\""$1"\" " 136 1.1 christos printf "'"$key_prefix "'" 137 1.1 christos printf $4 " " $5 " " $6 " \"" 138 1.1 christos for (i=7; i<=NF; i++) printf $i 139 1.1 christos printf "\";\n" 140 1.1 christos }' $keyname.key 141 1.1 christos done 142 1.1 christos echo "};" 143 1.1 christos } 144 1.1 christos 145 1.1 christos # keyfile_to_dskeys_section: helper function for keyfile_to_*_dskeys() 146 1.1 christos # converts keyfile data into a DS-style trust anchor configuration 147 1.1 christos # section using the supplied parameters 148 1.1 christos keyfile_to_dskeys() { 149 1.1 christos section_name=$1 150 1.1 christos key_prefix=$2 151 1.1 christos shift 152 1.1 christos shift 153 1.1 christos echo "$section_name {" 154 1.1 christos for keyname in $*; do 155 1.1 christos $DSFROMKEY $keyname.key \ 156 1.1 christos | awk '!/^; /{ 157 1.1 christos printf "\t\""$1"\" " 158 1.1 christos printf "'"$key_prefix "'" 159 1.1 christos printf $4 " " $5 " " $6 " \"" 160 1.1 christos for (i=7; i<=NF; i++) printf $i 161 1.1 christos printf "\";\n" 162 1.1 christos }' 163 1.1 christos done 164 1.1 christos echo "};" 165 1.1 christos } 166 1.1 christos 167 1.1 christos # keyfile_to_trusted_keys: convert key data contained in the keyfile(s) 168 1.1 christos # provided to a "trust-keys" section suitable for including in a 169 1.1 christos # resolver's configuration file 170 1.1 christos keyfile_to_trusted_keys() { 171 1.1 christos keyfile_to_keys "trusted-keys" "" $* 172 1.1 christos } 173 1.1 christos 174 1.1 christos # keyfile_to_static_keys: convert key data contained in the keyfile(s) 175 1.1 christos # provided to a *static-key* "trust-anchors" section suitable for including in 176 1.1 christos # a resolver's configuration file 177 1.1 christos keyfile_to_static_keys() { 178 1.1 christos keyfile_to_keys "trust-anchors" "static-key" $* 179 1.1 christos } 180 1.1 christos 181 1.1 christos # keyfile_to_initial_keys: convert key data contained in the keyfile(s) 182 1.1 christos # provided to an *initial-key* "trust-anchors" section suitable for including 183 1.1 christos # in a resolver's configuration file 184 1.1 christos keyfile_to_initial_keys() { 185 1.1 christos keyfile_to_keys "trust-anchors" "initial-key" $* 186 1.1 christos } 187 1.1 christos 188 1.1 christos # keyfile_to_static_ds_keys: convert key data contained in the keyfile(s) 189 1.1 christos # provided to a *static-ds* "trust-anchors" section suitable for including in a 190 1.1 christos # resolver's configuration file 191 1.1 christos keyfile_to_static_ds() { 192 1.1 christos keyfile_to_dskeys "trust-anchors" "static-ds" $* 193 1.1 christos } 194 1.1 christos 195 1.1 christos # keyfile_to_initial_ds_keys: convert key data contained in the keyfile(s) 196 1.1 christos # provided to an *initial-ds* "trust-anchors" section suitable for including 197 1.1 christos # in a resolver's configuration file 198 1.1 christos keyfile_to_initial_ds() { 199 1.1 christos keyfile_to_dskeys "trust-anchors" "initial-ds" $* 200 1.1 christos } 201 1.1 christos 202 1.1 christos # keyfile_to_key_id: convert a key file name to a key ID 203 1.1 christos # 204 1.1 christos # For a given key file name (e.g. "Kexample.+013+06160") provided as $1, 205 1.1 christos # print the key ID with leading zeros stripped ("6160" for the 206 1.1 christos # aforementioned example). 207 1.1 christos keyfile_to_key_id() { 208 1.1 christos echo "$1" | sed "s/.*+0\{0,4\}//" 209 1.1 christos } 210 1.1 christos 211 1.1 christos # private_type_record: write a private type record recording the state of the 212 1.1 christos # signing process 213 1.1 christos # 214 1.1 christos # For a given zone ($1), algorithm number ($2) and key file ($3), print the 215 1.1 christos # private type record with default type value of 65534, indicating that the 216 1.1 christos # signing process for this key is completed. 217 1.1 christos private_type_record() { 218 1.1 christos _zone=$1 219 1.1 christos _algorithm=$2 220 1.1 christos _keyfile=$3 221 1.1 christos 222 1.1 christos _id=$(keyfile_to_key_id "$_keyfile") 223 1.1 christos 224 1.1 christos printf "%s. 0 IN TYPE65534 %s 5 %02x%04x0000\n" "$_zone" "\\#" "$_algorithm" "$_id" 225 1.1 christos } 226 1.1 christos 227 1.1 christos # nextpart*() - functions for reading files incrementally 228 1.1 christos # 229 1.1 christos # These functions aim to facilitate looking for (or waiting for) 230 1.1 christos # messages which may be logged more than once throughout the lifetime of 231 1.1 christos # a given named instance by outputting just the part of the file which 232 1.1 christos # has been appended since the last time we read it. 233 1.1 christos # 234 1.1 christos # Calling some of these functions causes temporary *.prev files to be 235 1.1 christos # created. 236 1.1 christos # 237 1.1 christos # Note that unlike other nextpart*() functions, nextpartread() is not 238 1.1 christos # meant to be directly used in system tests; its sole purpose is to 239 1.1 christos # reduce code duplication below. 240 1.1 christos # 241 1.1 christos # A quick usage example: 242 1.1 christos # 243 1.1 christos # $ echo line1 > named.log 244 1.1 christos # $ echo line2 >> named.log 245 1.1 christos # $ nextpart named.log 246 1.1 christos # line1 247 1.1 christos # line2 248 1.1 christos # $ echo line3 >> named.log 249 1.1 christos # $ nextpart named.log 250 1.1 christos # line3 251 1.1 christos # $ nextpart named.log 252 1.1 christos # $ echo line4 >> named.log 253 1.1 christos # $ nextpartpeek named.log 254 1.1 christos # line4 255 1.1 christos # $ nextpartpeek named.log 256 1.1 christos # line4 257 1.1 christos # $ nextpartreset named.log 258 1.1 christos # $ nextpartpeek named.log 259 1.1 christos # line1 260 1.1 christos # line2 261 1.1 christos # line3 262 1.1 christos # line4 263 1.1 christos # $ nextpart named.log 264 1.1 christos # line1 265 1.1 christos # line2 266 1.1 christos # line3 267 1.1 christos # line4 268 1.1 christos # $ nextpart named.log 269 1.1 christos # $ 270 1.1 christos 271 1.1 christos # nextpartreset: reset the marker used by nextpart() and nextpartpeek() 272 1.1 christos # so that it points to the start of the given file 273 1.1 christos nextpartreset() { 274 1.1 christos echo "0" >$1.prev 275 1.1 christos } 276 1.1 christos 277 1.1 christos # nextpartread: read everything that's been appended to a file since the 278 1.1 christos # last time nextpart() was called and print it to stdout, print the 279 1.1 christos # total number of lines read from that file so far to file descriptor 3 280 1.1 christos nextpartread() { 281 1.1 christos [ -f $1.prev ] || nextpartreset $1 282 1.1 christos prev=$(cat $1.prev) 283 1.1 christos awk "NR > $prev "'{ print } 284 1.1 christos END { print NR > "/dev/stderr" }' $1 2>&3 285 1.1 christos } 286 1.1 christos 287 1.1 christos # nextpart: read everything that's been appended to a file since the 288 1.1 christos # last time nextpart() was called 289 1.1 christos nextpart() { 290 1.1 christos nextpartread $1 3>$1.prev.tmp 291 1.1 christos mv $1.prev.tmp $1.prev 292 1.1 christos } 293 1.1 christos 294 1.1 christos # nextpartpeek: read everything that's been appended to a file since the 295 1.1 christos # last time nextpart() was called 296 1.1 christos nextpartpeek() { 297 1.1 christos nextpartread $1 3>/dev/null 298 1.1 christos } 299 1.1 christos 300 1.1 christos # _search_log: look for message $1 in file $2 with nextpart(). 301 1.1 christos _search_log() ( 302 1.1 christos msg="$1" 303 1.1 christos file="$2" 304 1.1 christos nextpart "$file" | grep -F -e "$msg" >/dev/null 305 1.1 christos ) 306 1.1 christos 307 1.1 christos # _search_log_re: same as _search_log but the message is an grep -E regex 308 1.1 christos _search_log_re() ( 309 1.1 christos msg="$1" 310 1.1 christos file="$2" 311 1.1 christos nextpart "$file" | grep -E -e "$msg" >/dev/null 312 1.1 christos ) 313 1.1 christos 314 1.1 christos # _search_log_peek: look for message $1 in file $2 with nextpartpeek(). 315 1.1 christos _search_log_peek() ( 316 1.1 christos msg="$1" 317 1.1 christos file="$2" 318 1.1 christos nextpartpeek "$file" | grep -F -e "$msg" >/dev/null 319 1.1 christos ) 320 1.1 christos 321 1.1 christos # wait_for_log: wait until message $2 in file $3 appears. Bail out after 322 1.1 christos # $1 seconds. This needs to be used in conjunction with a prior call to 323 1.1 christos # nextpart() or nextpartreset() on the same file to guarantee the offset is 324 1.1 christos # set correctly. Tests using wait_for_log() are responsible for cleaning up 325 1.1 christos # the created <file>.prev files. 326 1.1 christos wait_for_log() ( 327 1.1 christos timeout="$1" 328 1.1 christos msg="$2" 329 1.1 christos file="$3" 330 1.1 christos retry_quiet "$timeout" _search_log "$msg" "$file" && return 0 331 1.1 christos echo_i "exceeded time limit waiting for literal '$msg' in $file" 332 1.1 christos return 1 333 1.1 christos ) 334 1.1 christos 335 1.1 christos # wait_for_log_re: same as wait_for_log, but the message is an grep -E regex 336 1.1 christos wait_for_log_re() ( 337 1.1 christos timeout="$1" 338 1.1 christos msg="$2" 339 1.1 christos file="$3" 340 1.1 christos retry_quiet "$timeout" _search_log_re "$msg" "$file" && return 0 341 1.1 christos echo_i "exceeded time limit waiting for regex '$msg' in $file" 342 1.1 christos return 1 343 1.1 christos ) 344 1.1 christos 345 1.1 christos # wait_for_log_peek: similar to wait_for_log() but peeking, so the file offset 346 1.1 christos # does not change. 347 1.1 christos wait_for_log_peek() ( 348 1.1 christos timeout="$1" 349 1.1 christos msg="$2" 350 1.1 christos file="$3" 351 1.1 christos retry_quiet "$timeout" _search_log_peek "$msg" "$file" && return 0 352 1.1 christos echo_i "exceeded time limit waiting for literal '$msg' in $file" 353 1.1 christos return 1 354 1.1 christos ) 355 1.1 christos 356 1.1 christos # _retry: keep running a command until it succeeds, up to $1 times, with 357 1.1 christos # one-second intervals, optionally printing a message upon every attempt 358 1.1 christos _retry() { 359 1.1 christos __retries="${1}" 360 1.1 christos shift 361 1.1 christos 362 1.1 christos while :; do 363 1.1 christos if "$@"; then 364 1.1 christos return 0 365 1.1 christos fi 366 1.1 christos __retries=$((__retries - 1)) 367 1.1 christos if [ "${__retries}" -gt 0 ]; then 368 1.1 christos if [ "${__retry_quiet}" -ne 1 ]; then 369 1.1 christos echo_i "retrying" 370 1.1 christos fi 371 1.1 christos sleep 1 372 1.1 christos else 373 1.1 christos return 1 374 1.1 christos fi 375 1.1 christos done 376 1.1 christos } 377 1.1 christos 378 1.1 christos # retry: call _retry() in verbose mode 379 1.1 christos retry() { 380 1.1 christos __retry_quiet=0 381 1.1 christos _retry "$@" 382 1.1 christos } 383 1.1 christos 384 1.1 christos # retry_quiet: call _retry() in silent mode 385 1.1 christos retry_quiet() { 386 1.1 christos __retry_quiet=1 387 1.1 christos _retry "$@" 388 1.1 christos } 389 1.1 christos 390 1.1 christos # _repeat: keep running command up to $1 times, unless it fails 391 1.1 christos _repeat() ( 392 1.1 christos __retries="${1}" 393 1.1 christos shift 394 1.1 christos while :; do 395 1.1 christos if ! "$@"; then 396 1.1 christos return 1 397 1.1 christos fi 398 1.1 christos __retries=$((__retries - 1)) 399 1.1 christos if [ "${__retries}" -le 0 ]; then 400 1.1 christos break 401 1.1 christos fi 402 1.1 christos done 403 1.1 christos return 0 404 1.1 christos ) 405 1.1 christos 406 1.1 christos _times() { 407 1.1 christos awk "BEGIN{ for(i = 1; i <= $1; i++) print i}" 408 1.1 christos } 409 1.1 christos 410 1.1 christos rndc_reload() { 411 1.1 christos $RNDC -c ../_common/rndc.conf -s $2 -p ${CONTROLPORT} reload $3 2>&1 | sed 's/^/'"I:$1"' /' 412 1.1 christos # reloading single zone is synchronous, if we're reloading whole server 413 1.1 christos # we need to wait for reload to finish 414 1.1 christos if [ -z "$3" ]; then 415 1.1 christos for _ in $(_times 10); do 416 1.1 christos $RNDC -c ../_common/rndc.conf -s $2 -p ${CONTROLPORT} status | grep "reload/reconfig in progress" >/dev/null || break 417 1.1 christos sleep 1 418 1.1 christos done 419 1.1 christos fi 420 1.1 christos } 421 1.1 christos 422 1.1 christos rndc_reconfig() { 423 1.1 christos seconds=${3:-10} 424 1.1 christos $RNDC -c ../_common/rndc.conf -s "$2" -p "${CONTROLPORT}" reconfig 2>&1 | sed 's/^/'"I:$1"' /' 425 1.1 christos for _ in $(_times "$seconds"); do 426 1.1 christos "$RNDC" -c ../_common/rndc.conf -s "$2" -p "${CONTROLPORT}" status | grep "reload/reconfig in progress" >/dev/null || break 427 1.1 christos sleep 1 428 1.1 christos done 429 1.1 christos } 430 1.1 christos 431 1.1 christos # rndc_dumpdb: call "rndc dumpdb [...]" and wait until it completes 432 1.1 christos # 433 1.1 christos # The first argument is the name server instance to send the command to, in the 434 1.1 christos # form of "nsX" (where "X" is the instance number), e.g. "ns5". The remaining 435 1.1 christos # arguments, if any, are appended to the rndc command line after "dumpdb". 436 1.1 christos # 437 1.1 christos # Control channel configuration for the name server instance to send the 438 1.1 christos # command to must match the contents of bin/tests/system/_common/rndc.conf. 439 1.1 christos # 440 1.1 christos # rndc output is stored in a file called rndc.out.test${n}; the "n" variable is 441 1.1 christos # required to be set by the calling tests.sh script. 442 1.1 christos # 443 1.1 christos # Return 0 if the dump completes successfully; return 1 if rndc returns an exit 444 1.1 christos # code other than 0 or if the "; Dump complete" string does not appear in the 445 1.1 christos # dump within 10 seconds. 446 1.1 christos rndc_dumpdb() { 447 1.1 christos __ret=0 448 1.1 christos __dump_complete=0 449 1.1 christos __server="${1}" 450 1.1.1.2 christos __ip="10.53.0.$(echo "${__server}" | tr -c -d '[:digit:]')" 451 1.1 christos 452 1.1 christos shift 453 1.1 christos ${RNDC} -c ../_common/rndc.conf -p "${CONTROLPORT}" -s "${__ip}" dumpdb "$@" >"rndc.out.test${n}" 2>&1 || __ret=1 454 1.1 christos 455 1.1 christos for _ in 0 1 2 3 4 5 6 7 8 9; do 456 1.1 christos if grep '^; Dump complete$' "${__server}/named_dump.db" >/dev/null; then 457 1.1 christos mv "${__server}/named_dump.db" "${__server}/named_dump.db.test${n}" 458 1.1 christos __dump_complete=1 459 1.1 christos break 460 1.1 christos fi 461 1.1 christos sleep 1 462 1.1 christos done 463 1.1 christos 464 1.1 christos if [ ${__dump_complete} -eq 0 ]; then 465 1.1 christos echo_i "timed out waiting for 'rndc dumpdb' to finish" 466 1.1 christos __ret=1 467 1.1 christos fi 468 1.1 christos 469 1.1 christos return ${__ret} 470 1.1 christos } 471 1.1 christos 472 1.1 christos # get_dig_xfer_stats: extract transfer statistics from dig output stored 473 1.1 christos # in $1, converting them to a format used by some system tests. 474 1.1 christos get_dig_xfer_stats() { 475 1.1 christos LOGFILE="$1" 476 1.1 christos sed -n "s/^;; XFR size: .*messages \([0-9][0-9]*\).*/messages=\1/p" "${LOGFILE}" 477 1.1 christos sed -n "s/^;; XFR size: \([0-9][0-9]*\) records.*/records=\1/p" "${LOGFILE}" 478 1.1 christos sed -n "s/^;; XFR size: .*bytes \([0-9][0-9]*\).*/bytes=\1/p" "${LOGFILE}" 479 1.1 christos } 480 1.1 christos 481 1.1 christos # get_named_xfer_stats: from named log file $1, extract transfer 482 1.1 christos # statistics for the last transfer for peer $2 and zone $3 (from a log 483 1.1 christos # message which has to contain the string provided in $4), converting 484 1.1 christos # them to a format used by some system tests. 485 1.1 christos get_named_xfer_stats() { 486 1.1 christos LOGFILE="$1" 487 1.1 christos PEER="$(echo $2 | sed 's/\./\\./g')" 488 1.1 christos ZONE="$(echo $3 | sed 's/\./\\./g')" 489 1.1 christos MESSAGE="$4" 490 1.1 christos grep " ${PEER}#.*${MESSAGE}:" "${LOGFILE}" \ 491 1.1 christos | sed -n "s/.* '${ZONE}\/.* \([0-9][0-9]*\) messages.*/messages=\1/p" | tail -1 492 1.1 christos grep " ${PEER}#.*${MESSAGE}:" "${LOGFILE}" \ 493 1.1 christos | sed -n "s/.* '${ZONE}\/.* \([0-9][0-9]*\) records.*/records=\1/p" | tail -1 494 1.1 christos grep " ${PEER}#.*${MESSAGE}:" "${LOGFILE}" \ 495 1.1 christos | sed -n "s/.* '${ZONE}\/.* \([0-9][0-9]*\) bytes.*/bytes=\1/p" | tail -1 496 1.1 christos } 497 1.1 christos 498 1.1 christos grep_v() { grep -v "$@" || test $? = 1; } 499