1#!/bin/sh 2# ensure that tail -F doesn't leak inotify resources 3 4# Copyright (C) 2015-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_ tail 21 22# Inotify not used on remote file systems 23require_local_dir_ 24 25grep '^#define HAVE_INOTIFY 1' "$CONFIG_HEADER" >/dev/null \ 26 || skip_ 'inotify required' 27 28require_strace_ 'inotify_add_watch,inotify_rm_watch' 29 30check_tail_output() 31{ 32 local delay="$1" 33 grep "$tail_re" out > /dev/null || 34 { sleep $delay; return 1; } 35} 36 37# Wait up to 25.5 seconds for grep REGEXP 'out' to succeed. 38grep_timeout() { tail_re="$1" retry_delay_ check_tail_output .1 8; } 39 40check_strace() 41{ 42 local delay="$1" 43 grep "$strace_re" strace.out > /dev/null || 44 { sleep $delay; return 1; } 45} 46 47cleanup_fail() 48{ 49 cat out 50 warn_ $1 51 fail=1 52} 53 54# Terminate any background tail process 55cleanup_() { kill $pid 2>/dev/null && wait $pid; } 56 57fastpoll='-s.1 --max-unchanged-stats=1' 58 59touch k || framework_failure_ 60 61# Note the timeout guard isn't strictly necessary here, 62# however without it strace will ignore SIGTERM. 63# strace does always honor SIGTERM with the -I2 option, 64# though that's not available on RHEL6 for example. 65timeout 180 strace -e inotify_add_watch,inotify_rm_watch -o strace.out \ 66 tail -F $fastpoll k >> out 2>&1 & pid=$! 67 68reverted_to_polling_=0 69for i in $(seq 2); do 70 echo $i 71 72 echo 'tailed' > k; 73 74 # Wait for watch on (new) file 75 strace_re='inotify_add_watch.*MODIFY' retry_delay_ check_strace .1 8 || 76 no_watch_=1 77 78 # Assume this is not because we're leaking 79 # (resources may already be depleted) 80 # The explicit check for inotify_rm_watch should confirm that. 81 grep -F 'reverting to polling' out >/dev/null && skip_ 'inotify unused' 82 83 # Otherwise failure is unknown 84 test "$no_watch_" && { cat out; framework_failure_ 'no inotify_add_watch'; } 85 86 mv k k.tmp 87 # wait for tail to detect the rename 88 grep_timeout 'inaccessible' || 89 { cleanup_fail 'failed to detect rename'; break; } 90 91 # Note we strace here rather than consuming all available watches 92 # to be more efficient, but more importantly avoid depleting resources. 93 # Note also available resources can currently be tuned with: 94 # sudo sysctl -w fs.inotify.max_user_watches=$smallish_number 95 # However that impacts all processes for the current user, and also 96 # may not be supported in future, instead being auto scaled to RAM 97 # like the Linux epoll resources were. 98 if test "$i" -gt 1; then 99 strace_re='inotify_rm_watch' retry_delay_ check_strace .1 8 || 100 { cleanup_fail 'failed to find inotify_rm_watch syscall'; break; } 101 fi 102 103 >out && >strace.out || framework_failure_ 'failed to reset output files' 104done 105 106cleanup_ 107 108Exit $fail 109