chiark / gitweb /
55cdd7f457ba3f35ce74f196340f0798efced718
[chiark-utils.git] / scripts / expire-iso8601
1 #!/bin/bash
2 # usage:
3 #   expire-iso8601 <unit-in-seconds> <slop-in-seconds>
4 #      <min-interval-in-units> <extent-in-min-intervals>
5 #     [<min-interval-in-units> <extent-in-min-intervals> ...]
6 #
7 # eg
8 #   /home/ian/junk/expire-iso8601 86400 10000  1 14  7 4
9 # uses units of 86400s (1 day) with a slop of 10ks;
10 # it keeps daily copies (that is, dated no more than 86400+10000s apart)
11 #  for at least 1*14 days, ie the oldest will be at least 86400s*1*14-10000s
12 #  older than the very newest
13 # and weekly copies (that is, dated no more than 7*86400+10000s apart)
14 #  for at least 7*4 days, ie the oldest will be at least 86400s*7*4-10000s
15 #  older than the very newest
16
17 set -e
18
19 fail () { echo >&2 "$*"; exit 2; }
20 badusage () { fail "bad usage: $*"; }
21
22 #-------------------- argument parsing --------------------
23
24 [ $# -ge 4 ] || badusage 'too few arguments'
25
26 unit=$1
27 slop=$2
28 shift;shift
29
30 [ $(($# % 2)) = 0 ] || badusage 'odd keep arguments (need min/extent pairs)'
31 argl="$*"
32
33 alldigits () {
34         [ "x${1##*[^0-9]}" = "x$1" ] || badusage "$2 must be all digits"
35         [ x$1 ] || badusage "$2 must be nonempty"
36 }
37
38 while [ $# -gt 0 ]; do
39         min=$1; shift; extent=$1; shift
40         alldigits $min min
41         alldigits $extent extent
42 done
43
44 #-------------------- scanning the directory ----------
45
46 # We build in $l a list of the relevant filenames and the time_t's
47 # they represent.  And, while we're at it, we find the most recent
48 # such time_t ($ls) and its name ($ln).
49 #
50 # Each entry in $l is $time_t/$filename, and the list is
51 # newline-separated for the benefit of sort(1).
52
53 ls=0
54 for cn in [0-9]*; do
55         echo $cn
56         case "$cn" in
57         ????-??-??)
58                 conv="$cn";;
59         ????-??-??T[0-2][0-9]+[0-9][0-9][0-9][0-9]|\
60         ????-??-??T[0-2][0-9]:[0-6][0-9]+[0-9][0-9][0-9][0-9]|\
61         ????-??-??T[0-2][0-9]:[0-6][0-9]:[0-6][0-9]+[0-9][0-9][0-9][0-9])
62                 conv="${cn%T*} ${cn#*T}";;
63         *)
64                 echo >&2 "ignoring $cn"
65                 continue;;
66         esac
67         cs=$(date -d "$conv" +%s)
68         if [ $cs -gt $ls ]; then
69                 ls=$cs; ln=$cn
70         fi
71         l="$cs/$cn
72 $l"
73 done
74
75 echo "newest $ln"
76
77 #-------------------- main computation --------------------
78
79 # We go through the items from most to least recent
80 #   ie in order of increasing age
81 #   ie in order of decreasing time_t
82 # We constantly maintain records of this item (c) and the last two
83 # (b and a).
84 #
85 # We then check to see if any of the specified minimum/extent pairs
86 # mean we should keep c and b.
87 #
88 # We can delete c if b is older than every specified extent.  b will
89 # then be the latest version we keep and is old enough.  (Note that if
90 # the density isn't satisfied, the expected number of old items may
91 # not be satisfied either; in the worst case, if b is very old, we
92 # might end up with just two items left.)
93 #
94 # If we delete c then we just go on to the next c, which will
95 # definitely be older, so will be deleted too (because b remains
96 # unchanged): ie we then delete all the rest.
97 #
98 # If we don't delete c, we look at the gap between a and c.  If this
99 # gap is not too long (according to any of the minimum/extent pairs)
100 # then it is OK to delete b.  (A gap is too long if it's longer than a
101 # relevant pair's minimum, but a pair isn't relevant if c is older
102 # than the extent.)  If we delete b then current c becomes the new b.
103 #
104 # If we don't delete either then b and c become the new a and b.
105
106 - because b is clearly sufficient to
107 # satisfy the 
108 # if we delete 
109
110 # {l,a,b,c}{s,n,a} = seconds, name of a,b,c where
111 #       c is one we're looking at now and
112 #       b is previous one
113 #       a is one before that
114 #       l is last (most recent)
115 # where a, b, c have not been removed
116
117 as=''
118 an=''
119 bs=''
120 bn=''
121
122 remove () {
123         echo "expire $1 (have $2)"
124 }
125
126 l=$(sort -nr <<END
127 $l
128 END
129 )
130
131 for ce in $l; do
132         cs=${ce%%/*}; cn=${ce#*/}
133         set $argl
134         c_mightneed=false
135         b_needfordensity=false
136         if test "$as"; then
137                 ac_interval=$(( $as - $cs ))
138         fi
139         while [ $# != 0 ]; do
140                 min=$(( $1 * $unit + $slop ))
141                 extent=$(( $1 * $2 * $unit - $slop ))
142                 # if b is as old as required by anything
143                 #  then c is definitely surplus
144                 if ! [ "$bs" ] || \
145                    [ $(($ls - $bs)) -le $extent ]; then
146                         c_mightneed=true
147                 fi
148                 if ! test "$as" || \
149                    [ $(($ls - $as)) -le $extent -a \
150                      $ac_interval -gt $min ]; then
151                         b_needfordensity=true
152                 fi
153                 shift;shift
154         done
155         if ! $c_mightneed; then
156                 remove $cn "$bn"
157                 continue
158         fi
159         if ! $b_needfordensity; then
160                 remove $bn "$an $cn"
161                 bn=$cn; bs=$cs
162                 continue
163         fi
164         an=$bn; as=$bs
165         bn=$cn; bs=$cs
166 done
167
168 exit 0