1#!/usr/bin/env bash
2# SPDX-License-Identifier: LGPL-2.1-or-later
3#
4# Test the "Age" parameter (with age-by) for systemd-tmpfiles.
5set -e
6set -x
7
8# Test directory structure looks like this:
9#   /tmp/ageby/
10#   ├── d1
11#   │   ├── f1
12#   │   ├── f2
13#   │   ├── f3
14#   │   └── f4
15#   ├── d2
16#   │   ├── f1
17#   │   ├── f2
18#   ...
19
20export SYSTEMD_LOG_LEVEL="debug"
21
22rm -rf /tmp/ageby
23mkdir -p /tmp/ageby/d{1..4}
24
25# TODO: There is probably a better way to figure this out.
26# Test for [bB] age-by arguments only on filesystems that expose
27# the creation time. Note that this is _not_ an accurate way to
28# check if the filesystem or kernel version don't provide the
29# timestamp. But, if the timestamp is visible in "stat" it is a
30# good indicator that the test can be run.
31TEST_TMPFILES_AGEBY_BTIME=${TEST_TMPFILES_AGEBY_BTIME:-0}
32if stat --format "%w" /tmp/ageby 2>/dev/null | grep -qv '^[\?\-]$'; then
33    TEST_TMPFILES_AGEBY_BTIME=1
34fi
35
36touch -a --date "2 minutes ago" /tmp/ageby/d1/f1
37touch -m --date "4 minutes ago" /tmp/ageby/d2/f1
38
39# Create a bunch of other files.
40touch /tmp/ageby/d{1,2}/f{2..4}
41
42# For "ctime".
43touch /tmp/ageby/d3/f1
44chmod +x /tmp/ageby/d3/f1
45sleep 1
46
47# For "btime".
48touch /tmp/ageby/d4/f1
49sleep 1
50
51# More files with recent "{a,b}time" values.
52touch /tmp/ageby/d{3,4}/f{2..4}
53
54# Check for cleanup of "f1" in each of "/tmp/d{1..4}".
55systemd-tmpfiles --clean - <<-EOF
56d /tmp/ageby/d1 - - - a:1m -
57e /tmp/ageby/d2 - - - m:3m -
58D /tmp/ageby/d3 - - - c:2s -
59EOF
60
61for d in d{1..3}; do
62    test ! -f "/tmp/ageby/${d}/f1"
63done
64
65if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
66    systemd-tmpfiles --clean - <<-EOF
67d /tmp/ageby/d4 - - - b:1s -
68EOF
69
70    test ! -f "/tmp/ageby/d4/f1"
71else
72    # Remove the file manually.
73    rm "/tmp/ageby/d4/f1"
74fi
75
76# Check for an invalid "age" and "age-by" arguments.
77for a in ':' ':1s' '2:1h' 'nope:42h' '"  :7m"' 'm:' '::' '"+r^w-x:2/h"' 'b ar::64'; do
78    systemd-tmpfiles --clean - <<EOF 2>&1 | grep -q -F 'Invalid age'
79d /tmp/ageby - - - ${a} -
80EOF
81done
82
83for d in d{1..4}; do
84    for f in f{2..4}; do
85        test -f "/tmp/ageby/${d}/${f}"
86    done
87done
88
89# Check for parsing with whitespace, repeated values
90# for "age-by" (valid arguments).
91for a in '"  a:24h"' 'cccaab:2h' '" aa : 4h"' '" a A B C c:1h"'; do
92    systemd-tmpfiles --clean - <<EOF
93d /tmp/ageby - - - ${a} -
94EOF
95done
96
97for d in d{1..4}; do
98    for f in f{2..4}; do
99        test -f "/tmp/ageby/${d}/${f}"
100    done
101done
102
103# Check that all files are removed if the "Age" is
104# set to "0" (regardless of "age-by" argument).
105systemd-tmpfiles --clean - <<-EOF
106d /tmp/ageby/d1 - - - abc:0 -
107e /tmp/ageby/d2 - - - cmb:0 -
108EOF
109
110for d in d{1,2}; do
111    for f in f{2..4}; do
112        test ! -f "/tmp/ageby/${d}/${f}"
113    done
114done
115
116# Check for combinations:
117#   - "/tmp/ageby/d3/f2" has file timestamps that
118#     are older than the specified age, it will be
119#     removed
120#   - "/tmp/ageby/d4/f2", has not aged for the given
121#     timestamp combination, it will not be removed
122touch -a -m --date "4 minutes ago" /tmp/ageby/d3/f2
123touch -a -m --date "8 minutes ago" /tmp/ageby/d4/f2
124systemd-tmpfiles --clean - <<-EOF
125e /tmp/ageby/d3 - - - am:3m -
126D /tmp/ageby/d4 - - - mc:7m -
127EOF
128
129test ! -f "/tmp/ageby/d3/f2"
130test -f "/tmp/ageby/d4/f2"
131
132# Check that all files are removed if only "Age" is set to 0.
133systemd-tmpfiles --clean - <<-EOF
134e /tmp/ageby/d3 - - - 0s
135d /tmp/ageby/d4 - - - 0s
136EOF
137
138for d in d{3,4}; do
139    for f in f{2..4}; do
140        test ! -f "/tmp/ageby/$d/${f}"
141    done
142done
143
144# Check "age-by" argument for sub-directories in "/tmp/ageby".
145systemd-tmpfiles --clean - <<-EOF
146d /tmp/ageby/ - - - A:1m -
147EOF
148
149for d in d{1..4}; do
150    test -d "/tmp/ageby/${d}"
151done
152
153# Check for combinations.
154touch -a -m --date "5 seconds ago" /tmp/ageby/d{1,2}
155systemd-tmpfiles --clean - <<-EOF
156e /tmp/ageby/ - - - AM:4s -
157EOF
158
159for d in d{1,2}; do
160    test ! -d "/tmp/ageby/${d}"
161done
162
163for d in d{3,4}; do
164    test -d "/tmp/ageby/${d}"
165done
166
167# Check "btime" for directories.
168if [[ $TEST_TMPFILES_AGEBY_BTIME -gt 0 ]]; then
169    systemd-tmpfiles --clean - <<-EOF
170d /tmp/ageby/ - - - B:8s -
171EOF
172
173    for d in d{3,4}; do
174        test -d "/tmp/ageby/${d}"
175    done
176fi
177
178# To bump "atime".
179touch -a --date "1 second ago" /tmp/ageby/d3
180systemd-tmpfiles --clean - <<-EOF
181d /tmp/ageby/ - - - A:2s -
182EOF
183
184test -d /tmp/ageby/d3
185test ! -d /tmp/ageby/d4
186
187# Check if sub-directories are removed regardless
188# of "age-by", when "Age" is set to "0".
189systemd-tmpfiles --clean - <<-EOF
190D /tmp/ageby/ - - - AM:0 -
191EOF
192
193test ! -d /tmp/ageby/d3
194
195# Cleanup the test directory (fail if not empty).
196rmdir /tmp/ageby
197