Let r and R lines participate in --clean when they specify an age. The
target itself is removed only after its selected file or directory
timestamps have aged enough; --remove remains unconditional.
Follow-up for: beca6b6e6b
386 lines
12 KiB
Bash
Executable File
386 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||
#
|
||
# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
|
||
set -e
|
||
set -x
|
||
|
||
# Test directory structure looks like this:
|
||
# /tmp/ageby/
|
||
# ├── d1
|
||
# │ ├── f1
|
||
# │ ├── f2
|
||
# │ ├── f3
|
||
# │ └── f4
|
||
# ├── d2
|
||
# │ ├── f1
|
||
# │ ├── f2
|
||
# ...
|
||
|
||
export SYSTEMD_LOG_LEVEL="debug"
|
||
|
||
rm -rf /tmp/ageby
|
||
mkdir -p /tmp/ageby/d{1..4}
|
||
|
||
# TODO: There is probably a better way to figure this out.
|
||
# Test for [bB] age-by arguments only on filesystems that expose
|
||
# the creation time. Note that this is _not_ an accurate way to
|
||
# check if the filesystem or kernel version don't provide the
|
||
# timestamp. But, if the timestamp is visible in "stat" it is a
|
||
# good indicator that the test can be run.
|
||
TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
|
||
if stat --format "%w" /tmp/ageby 2>/dev/null | grep -v '^[\?\-]$' >/dev/null; then
|
||
TEST_TMPFILES_AGEBY_BTIME=1
|
||
fi
|
||
|
||
touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
|
||
touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
|
||
|
||
# Create a bunch of other files.
|
||
touch /tmp/ageby/d{1,2}/f{2..4}
|
||
|
||
# For "ctime".
|
||
touch /tmp/ageby/d3/f1
|
||
chmod +x /tmp/ageby/d3/f1
|
||
sleep 1
|
||
|
||
# For "btime".
|
||
touch /tmp/ageby/d4/f1
|
||
sleep 1
|
||
|
||
# More files with recent "{a,b}time" values.
|
||
touch /tmp/ageby/d{3,4}/f{2..4}
|
||
|
||
# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
|
||
systemd-tmpfiles --dry-run --clean - <<-EOF
|
||
d /tmp/ageby/d1 - - - a:1m -
|
||
e /tmp/ageby/d2 - - - m:3m -
|
||
D /tmp/ageby/d3 - - - c:2s -
|
||
EOF
|
||
|
||
for d in d{1..3}; do
|
||
test -f "/tmp/ageby/${d}/f1"
|
||
done
|
||
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby/d1 - - - a:1m -
|
||
e /tmp/ageby/d2 - - - m:3m -
|
||
D /tmp/ageby/d3 - - - c:2s -
|
||
EOF
|
||
|
||
for d in d{1..3}; do
|
||
test ! -f "/tmp/ageby/${d}/f1"
|
||
done
|
||
|
||
if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby/d4 - - - b:1s -
|
||
EOF
|
||
|
||
test ! -f "/tmp/ageby/d4/f1"
|
||
else
|
||
# Remove the file manually.
|
||
rm "/tmp/ageby/d4/f1"
|
||
fi
|
||
|
||
# Check for an invalid "age" and "age-by" arguments.
|
||
for a in ':' ':1s' '2:1h' 'nope:42h' '" :7m"' 'm:' '::' '"+r^w-x:2/h"' '"b ar::64"'; do
|
||
systemd-tmpfiles --clean - <<EOF 2>&1 | grep -F 'Invalid age' >/dev/null
|
||
d /tmp/ageby - - - ${a} -
|
||
EOF
|
||
done
|
||
|
||
for d in d{1..4}; do
|
||
for f in f{2..4}; do
|
||
test -f "/tmp/ageby/${d}/${f}"
|
||
done
|
||
done
|
||
|
||
# Check for parsing with whitespace, repeated values
|
||
# for "age-by" (valid arguments).
|
||
for a in '" a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
|
||
systemd-tmpfiles --clean - <<EOF
|
||
d /tmp/ageby - - - ${a} -
|
||
EOF
|
||
done
|
||
|
||
for d in d{1..4}; do
|
||
for f in f{2..4}; do
|
||
test -f "/tmp/ageby/${d}/${f}"
|
||
done
|
||
done
|
||
|
||
# Check that all files are removed if the "Age" is
|
||
# set to "0" (regardless of "age-by" argument).
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby/d1 - - - abc:0 -
|
||
e /tmp/ageby/d2 - - - cmb:0 -
|
||
EOF
|
||
|
||
for d in d{1,2}; do
|
||
for f in f{2..4}; do
|
||
test ! -f "/tmp/ageby/${d}/${f}"
|
||
done
|
||
done
|
||
|
||
# Check for combinations:
|
||
# - "/tmp/ageby/d3/f2" has file timestamps that
|
||
# are older than the specified age, it will be
|
||
# removed
|
||
# - "/tmp/ageby/d4/f2", has not aged for the given
|
||
# timestamp combination, it will not be removed
|
||
touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
|
||
touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
e /tmp/ageby/d3 - - - am:3m -
|
||
D /tmp/ageby/d4 - - - mc:7m -
|
||
EOF
|
||
|
||
test ! -f "/tmp/ageby/d3/f2"
|
||
test -f "/tmp/ageby/d4/f2"
|
||
|
||
# Check that all files are removed if only "Age" is set to 0.
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
e /tmp/ageby/d3 - - - 0s
|
||
d /tmp/ageby/d4 - - - 0s
|
||
EOF
|
||
|
||
for d in d{3,4}; do
|
||
for f in f{2..4}; do
|
||
test ! -f "/tmp/ageby/$d/${f}"
|
||
done
|
||
done
|
||
|
||
# Check "age-by" argument for sub-directories in "/tmp/ageby".
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby/ - - - A:1m -
|
||
EOF
|
||
|
||
for d in d{1..4}; do
|
||
test -d "/tmp/ageby/${d}"
|
||
done
|
||
|
||
# Check for combinations.
|
||
touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
e /tmp/ageby/ - - - AM:4s -
|
||
EOF
|
||
|
||
for d in d{1,2}; do
|
||
test ! -d "/tmp/ageby/${d}"
|
||
done
|
||
|
||
for d in d{3,4}; do
|
||
test -d "/tmp/ageby/${d}"
|
||
done
|
||
|
||
# Check "btime" for directories.
|
||
if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby/ - - - B:8s -
|
||
EOF
|
||
|
||
for d in d{3,4}; do
|
||
test -d "/tmp/ageby/${d}"
|
||
done
|
||
fi
|
||
|
||
# To bump "atime".
|
||
touch -a --date "1 second ago" /tmp/ageby/d3
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby/ - - - A:2s -
|
||
EOF
|
||
|
||
test -d /tmp/ageby/d3
|
||
test ! -d /tmp/ageby/d4
|
||
|
||
# Check if sub-directories are removed regardless
|
||
# of "age-by", when "Age" is set to "0".
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
D /tmp/ageby/ - - - AM:0 -
|
||
EOF
|
||
|
||
test ! -d /tmp/ageby/d3
|
||
|
||
# Cleanup the test directory (fail if not empty).
|
||
rmdir /tmp/ageby
|
||
|
||
# Removing an old child directory should restore the parent directory mtime.
|
||
rm -rf /tmp/ageby-mtime
|
||
mkdir -p /tmp/ageby-mtime/old-child
|
||
touch --date "2020-01-01 00:00:00" /tmp/ageby-mtime/old-child /tmp/ageby-mtime
|
||
before="$(stat -c %Y /tmp/ageby-mtime)"
|
||
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby-mtime - - - M:1s
|
||
EOF
|
||
|
||
after="$(stat -c %Y /tmp/ageby-mtime)"
|
||
test "$before" = "$after"
|
||
test ! -e /tmp/ageby-mtime/old-child
|
||
rmdir /tmp/ageby-mtime
|
||
|
||
# r/R entries honor age when cleaning.
|
||
rm -rf /tmp/ageby-remove
|
||
mkdir -p /tmp/ageby-remove/old-dir/child /tmp/ageby-remove/new-dir/child \
|
||
/tmp/ageby-remove/old-empty-dir /tmp/ageby-remove/new-empty-dir
|
||
touch /tmp/ageby-remove/old-file /tmp/ageby-remove/new-file \
|
||
/tmp/ageby-remove/old-recursive-file /tmp/ageby-remove/new-recursive-file
|
||
touch --date "3 minutes ago" \
|
||
/tmp/ageby-remove/old-file \
|
||
/tmp/ageby-remove/old-recursive-file \
|
||
/tmp/ageby-remove/old-dir/child \
|
||
/tmp/ageby-remove/old-dir \
|
||
/tmp/ageby-remove/old-empty-dir
|
||
|
||
output="$(systemd-tmpfiles --dry-run --clean - 2>&1 <<-EOF
|
||
r /tmp/ageby-remove/old-file - - - m:1m -
|
||
r /tmp/ageby-remove/new-file - - - m:1m -
|
||
R /tmp/ageby-remove/old-recursive-file - - - m:1m -
|
||
R /tmp/ageby-remove/new-recursive-file - - - m:1m -
|
||
R /tmp/ageby-remove/old-dir - - - M:1m -
|
||
R /tmp/ageby-remove/new-dir - - - M:1m -
|
||
R /tmp/ageby-remove/old-empty-dir - - - M:1m -
|
||
R /tmp/ageby-remove/new-empty-dir - - - M:1m -
|
||
EOF
|
||
)"
|
||
[[ "$output" == *"Would remove directory \"/tmp/ageby-remove/old-dir\""* ]]
|
||
[[ "$output" != *"Would remove directory \"/tmp/ageby-remove/new-dir\""* ]]
|
||
[[ "$output" == *"Would remove directory \"/tmp/ageby-remove/old-empty-dir\""* ]]
|
||
[[ "$output" != *"Would remove directory \"/tmp/ageby-remove/new-empty-dir\""* ]]
|
||
|
||
test -e /tmp/ageby-remove/old-file
|
||
test -e /tmp/ageby-remove/new-file
|
||
test -e /tmp/ageby-remove/old-recursive-file
|
||
test -e /tmp/ageby-remove/new-recursive-file
|
||
test -d /tmp/ageby-remove/old-dir
|
||
test -d /tmp/ageby-remove/old-dir/child
|
||
test -d /tmp/ageby-remove/new-dir
|
||
test -d /tmp/ageby-remove/new-dir/child
|
||
test -d /tmp/ageby-remove/old-empty-dir
|
||
test -d /tmp/ageby-remove/new-empty-dir
|
||
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
r /tmp/ageby-remove/old-file - - - m:1m -
|
||
r /tmp/ageby-remove/new-file - - - m:1m -
|
||
R /tmp/ageby-remove/old-recursive-file - - - m:1m -
|
||
R /tmp/ageby-remove/new-recursive-file - - - m:1m -
|
||
R /tmp/ageby-remove/old-dir - - - M:1m -
|
||
R /tmp/ageby-remove/new-dir - - - M:1m -
|
||
R /tmp/ageby-remove/old-empty-dir - - - M:1m -
|
||
R /tmp/ageby-remove/new-empty-dir - - - M:1m -
|
||
EOF
|
||
|
||
test ! -e /tmp/ageby-remove/old-file
|
||
test -e /tmp/ageby-remove/new-file
|
||
test ! -e /tmp/ageby-remove/old-recursive-file
|
||
test -e /tmp/ageby-remove/new-recursive-file
|
||
test ! -e /tmp/ageby-remove/old-dir
|
||
test -d /tmp/ageby-remove/new-dir
|
||
test -d /tmp/ageby-remove/new-dir/child
|
||
test ! -e /tmp/ageby-remove/old-empty-dir
|
||
test -d /tmp/ageby-remove/new-empty-dir
|
||
|
||
# Aged r cleanup removes empty directories but not non-empty or too-new directories.
|
||
mkdir -p /tmp/ageby-remove/old-r-empty-dir \
|
||
/tmp/ageby-remove/new-r-empty-dir \
|
||
/tmp/ageby-remove/old-r-nonempty-dir/child
|
||
touch --date "3 minutes ago" \
|
||
/tmp/ageby-remove/old-r-empty-dir \
|
||
/tmp/ageby-remove/old-r-nonempty-dir
|
||
output="$(systemd-tmpfiles --dry-run --clean - 2>&1 <<-EOF
|
||
r /tmp/ageby-remove/old-r-empty-dir - - - M:1m -
|
||
r /tmp/ageby-remove/new-r-empty-dir - - - M:1m -
|
||
r /tmp/ageby-remove/old-r-nonempty-dir - - - M:1m -
|
||
EOF
|
||
)"
|
||
[[ "$output" == *"Would remove directory \"/tmp/ageby-remove/old-r-empty-dir\""* ]]
|
||
[[ "$output" != *"Would remove directory \"/tmp/ageby-remove/new-r-empty-dir\""* ]]
|
||
[[ "$output" != *"Would remove directory \"/tmp/ageby-remove/old-r-nonempty-dir\""* ]]
|
||
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
r /tmp/ageby-remove/old-r-empty-dir - - - M:1m -
|
||
r /tmp/ageby-remove/new-r-empty-dir - - - M:1m -
|
||
r /tmp/ageby-remove/old-r-nonempty-dir - - - M:1m -
|
||
EOF
|
||
test ! -e /tmp/ageby-remove/old-r-empty-dir
|
||
test -d /tmp/ageby-remove/new-r-empty-dir
|
||
test -d /tmp/ageby-remove/old-r-nonempty-dir/child
|
||
|
||
# Aged R cleanup keeps the top-level directory if a child is too new.
|
||
mkdir -p /tmp/ageby-remove/old-dir-new-child/new-child
|
||
touch --date "3 minutes ago" /tmp/ageby-remove/old-dir-new-child
|
||
systemd-tmpfiles --clean --inline "R /tmp/ageby-remove/old-dir-new-child - - - M:1m -"
|
||
test -d /tmp/ageby-remove/old-dir-new-child/new-child
|
||
|
||
(! systemd-tmpfiles --clean --inline "r /tmp/ageby-remove/new-file - - - ~1m -")
|
||
|
||
# The ~ age modifier on R keeps the target and its immediate children.
|
||
mkdir -p /tmp/ageby-remove/keep-root/old-child/grandchild
|
||
touch --date "3 minutes ago" \
|
||
/tmp/ageby-remove/keep-root \
|
||
/tmp/ageby-remove/keep-root/old-child \
|
||
/tmp/ageby-remove/keep-root/old-child/grandchild
|
||
systemd-tmpfiles --clean --inline "R /tmp/ageby-remove/keep-root - - - ~M:1m -"
|
||
test -d /tmp/ageby-remove/keep-root
|
||
test -d /tmp/ageby-remove/keep-root/old-child
|
||
test ! -e /tmp/ageby-remove/keep-root/old-child/grandchild
|
||
|
||
# Aged r/R cleanup skips locked files.
|
||
touch /tmp/ageby-remove/locked-file /tmp/ageby-remove/locked-recursive-file
|
||
touch --date "3 minutes ago" /tmp/ageby-remove/locked-file /tmp/ageby-remove/locked-recursive-file
|
||
exec {lock_fd}<>/tmp/ageby-remove/locked-file
|
||
exec {recursive_lock_fd}<>/tmp/ageby-remove/locked-recursive-file
|
||
flock --exclusive "$lock_fd"
|
||
flock --exclusive "$recursive_lock_fd"
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
r /tmp/ageby-remove/locked-file - - - m:1m -
|
||
R /tmp/ageby-remove/locked-recursive-file - - - m:1m -
|
||
EOF
|
||
test -e /tmp/ageby-remove/locked-file
|
||
test -e /tmp/ageby-remove/locked-recursive-file
|
||
exec {lock_fd}>&-
|
||
exec {recursive_lock_fd}>&-
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
r /tmp/ageby-remove/locked-file - - - m:1m -
|
||
R /tmp/ageby-remove/locked-recursive-file - - - m:1m -
|
||
EOF
|
||
test ! -e /tmp/ageby-remove/locked-file
|
||
test ! -e /tmp/ageby-remove/locked-recursive-file
|
||
|
||
# Aged r cleanup skips locked directories.
|
||
mkdir -p /tmp/ageby-remove/locked-r-dir
|
||
touch --date "3 minutes ago" /tmp/ageby-remove/locked-r-dir
|
||
flock --exclusive /tmp/ageby-remove/locked-r-dir systemd-tmpfiles --clean --inline \
|
||
"r /tmp/ageby-remove/locked-r-dir - - - M:1m -"
|
||
test -d /tmp/ageby-remove/locked-r-dir
|
||
systemd-tmpfiles --clean --inline "r /tmp/ageby-remove/locked-r-dir - - - M:1m -"
|
||
test ! -e /tmp/ageby-remove/locked-r-dir
|
||
|
||
# Aged R cleanup skips locked directories and everything below them.
|
||
mkdir -p /tmp/ageby-remove/locked-dir/child
|
||
touch --date "3 minutes ago" /tmp/ageby-remove/locked-dir /tmp/ageby-remove/locked-dir/child
|
||
flock --exclusive /tmp/ageby-remove/locked-dir systemd-tmpfiles --clean --inline \
|
||
"R /tmp/ageby-remove/locked-dir - - - M:1m -"
|
||
test -d /tmp/ageby-remove/locked-dir/child
|
||
systemd-tmpfiles --clean --inline "R /tmp/ageby-remove/locked-dir - - - M:1m -"
|
||
test ! -e /tmp/ageby-remove/locked-dir
|
||
|
||
rm -rf /tmp/ageby-remove
|
||
|
||
# X entries inherit the parent cleanup age and its age-by fields.
|
||
rm -rf /tmp/ageby-x-inherit
|
||
mkdir -p /tmp/ageby-x-inherit/parent/child /tmp/ageby-x-inherit/parent/other
|
||
printf old >/tmp/ageby-x-inherit/parent/child/file
|
||
printf old >/tmp/ageby-x-inherit/parent/other/file
|
||
touch --date "3 days ago" /tmp/ageby-x-inherit/parent/child/file /tmp/ageby-x-inherit/parent/other/file
|
||
|
||
systemd-tmpfiles --clean - <<-EOF
|
||
d /tmp/ageby-x-inherit/parent - - - m:1d
|
||
X /tmp/ageby-x-inherit/parent/child - - - -
|
||
EOF
|
||
|
||
test ! -e /tmp/ageby-x-inherit/parent/child/file
|
||
test ! -e /tmp/ageby-x-inherit/parent/other/file
|
||
rm -rf /tmp/ageby-x-inherit
|