1#!/bin/sh 2# test for basic tee functionality. 3 4# Copyright (C) 2005-2023 Free Software Foundation, Inc. 5 6# This program is free software: you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation, either version 3 of the License, or 9# (at your option) any later version. 10 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <https://www.gnu.org/licenses/>. 18 19. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src 20print_ver_ tee 21 22echo line >sample || framework_failure_ 23 24# POSIX says: "Processing of at least 13 file operands shall be supported." 25for n in 0 1 2 12 13; do 26 files=$(seq $n) 27 rm -f $files 28 tee $files <sample >out || fail=1 29 for f in out $files; do 30 compare sample $f || fail=1 31 done 32done 33 34# Ensure tee treats '-' as the name of a file, as mandated by POSIX. 35# Between v5.3.0 and v8.23, a '-' argument caused tee to send another 36# copy of input to standard output. 37tee - <sample >out 2>err || fail=1 38compare sample ./- || fail=1 39compare sample out || fail=1 40compare /dev/null err || fail=1 41 42# Ensure tee exits early if no more writable outputs 43if test -w /dev/full && test -c /dev/full; then 44 yes | returns_ 1 timeout 10 tee /dev/full 2>err >/dev/full || fail=1 45 # Ensure an error for each of the 2 outputs 46 # (and no redundant errors for stdout). 47 test $(wc -l < err) = 2 || { cat err; fail=1; } 48 49 50 # Ensure we continue with outputs that are OK 51 seq 10000 > multi_read || framework_failure_ 52 53 returns_ 1 tee /dev/full out2 2>err >out1 <multi_read || fail=1 54 cmp multi_read out1 || fail=1 55 cmp multi_read out2 || fail=1 56 # Ensure an error for failing output 57 test $(wc -l < err) = 1 || { cat err; fail=1; } 58 59 returns_ 1 tee out1 out2 2>err >/dev/full <multi_read || fail=1 60 cmp multi_read out1 || fail=1 61 cmp multi_read out2 || fail=1 62 # Ensure an error for failing output 63 test $(wc -l < err) = 1 || { cat err; fail=1; } 64fi 65 66case $host_triplet in 67 *aix*) echo 'avoiding due to no way to detect closed outputs on AIX' ;; 68 *) 69# Test iopoll-powered early exit for closed pipes 70tee_exited() { sleep $1; test -f tee.exited; } 71# Currently this functionality is most useful with 72# intermittent input from a terminal, but here we 73# use an input pipe that doesn't write anything 74# but will exit as soon as tee does, or it times out 75retry_delay_ tee_exited .1 7 | # 12.7s (Must be > following timeout) 76{ timeout 10 tee -p 2>err && touch tee.exited; } | : 77test $(wc -l < err) = 0 || { cat err; fail=1; } 78test -f tee.exited || fail=1 ;; 79esac 80 81# Test with unwritable files 82if ! uid_is_privileged_; then # root does not get EPERM. 83 touch file.ro || framework_failure_ 84 chmod a-w file.ro || framework_failure_ 85 returns_ 1 tee -p </dev/null file.ro || fail=1 86fi 87 88mkfifo_or_skip_ fifo 89 90# Ensure tee handles nonblocking output correctly 91# Terminate any background processes 92cleanup_() { kill $pid 2>/dev/null && wait $pid; } 93read_fifo_delayed() { 94 { sleep .1; timeout 10 dd of=/dev/null status=none; } <fifo 95} 96read_fifo_delayed & pid=$! 97dd count=20 bs=100K if=/dev/zero status=none | 98{ 99 dd count=0 oflag=nonblock status=none 100 tee || { cleanup_; touch tee.fail; } 101} >fifo 102test -f tee.fail && fail=1 || cleanup_ 103 104# Ensure tee honors --output-error modes 105read_fifo() { timeout 10 dd count=1 if=fifo of=/dev/null status=none & } 106 107# Determine platform sigpipe exit status 108read_fifo 109yes >fifo 110pipe_status=$? 111 112# Default operation is to continue on output errors but exit silently on SIGPIPE 113read_fifo 114yes | returns_ $pipe_status timeout 10 tee ./e/noent 2>err >fifo || fail=1 115test $(wc -l < err) = 1 || { cat err; fail=1; } 116 117# With -p, SIGPIPE is suppressed, exit 0 for EPIPE when all outputs finished 118read_fifo 119yes | timeout 10 tee -p 2>err >fifo || fail=1 120test $(wc -l < err) = 0 || { cat err; fail=1; } 121 122# With --output-error=warn, exit 1 for EPIPE when all outputs finished 123read_fifo 124yes | returns_ 1 timeout 10 tee --output-error=warn 2>err >fifo || fail=1 125test $(wc -l < err) = 1 || { cat err; fail=1; } 126 127# With --output-error=exit, exit 1 immediately for EPIPE 128read_fifo 129yes | returns_ 1 timeout 10 tee --output-error=exit /dev/null 2>err >fifo \ 130 || fail=1 131test $(wc -l < err) = 1 || { cat err; fail=1; } 132 133# With --output-error=exit, exit 1 immediately on output error 134read_fifo 135yes | returns_ 1 timeout 10 tee --output-error=exit ./e/noent 2>err >fifo \ 136 || fail=1 137test $(wc -l < err) = 1 || { cat err; fail=1; } 138 139# With --output-error=exit-nopipe, exit 0 for EPIPE 140read_fifo 141yes | timeout 10 tee --output-error=exit-nopipe 2>err >fifo || fail=1 142test $(wc -l < err) = 0 || { cat err; fail=1; } 143 144wait 145Exit $fail 146