summaryrefslogtreecommitdiff
path: root/debian/apt.cron.daily
blob: acecd29ac4d3a39612a786b57664f32034e57fbe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
#!/bin/sh
#set -e
#
# This file understands the following apt configuration variables:
# Values here are the default.
# Create /etc/apt/apt.conf.d/02periodic file to set your preference.
#
#  Dir "/";
#  - RootDir for all configuration files
#
#  Dir::Cache "var/apt/cache/";
#  - Set apt package cache directory
#
#  Dir::Cache::Archive "archives/";
#  - Set package archive directory
#
#  APT::Periodic::BackupArchiveInterval "0";
#  - Backup after n-days if archive contents changed.(0=disable)
#
#  APT::Periodic::BackupLevel "3";
#  - Backup level.(0=disable), 1 is invalid.
#
#  Dir::Cache::Backup "backup/";
#  - Set periodic package backup directory
#
#  APT::Archives::MaxAge "0"; (old, deprecated)
#  APT::Periodic::MaxAge "0"; (new)
#  - Set maximum allowed age of a cache package file. If a cache 
#    package file is older it is deleted (0=disable)
#
#  APT::Archives::MinAge "2"; (old, deprecated)
#  APT::Periodic::MinAge "2"; (new)
#  - Set minimum age of a package file. If a file is younger it
#    will not be deleted (0=disable). Usefull to prevent races 
#    and to keep backups of the packages for emergency.
#
#  APT::Archives::MaxSize "0"; (old, deprecated)
#  APT::Periodic::MaxSize "0"; (new)
#  - Set maximum size of the cache in MB (0=disable). If the cache
#    is bigger, cached package files are deleted until the size
#    requirement is met (the biggest packages will be deleted 
#    first).
#
#  APT::Periodic::Update-Package-Lists "0";
#  - Do "apt-get update" automatically every n-days (0=disable)
#    
#  APT::Periodic::Download-Upgradeable-Packages "0";
#  - Do "apt-get upgrade --download-only" every n-days (0=disable)
# 
#  APT::Periodic::Unattended-Upgrade "0";
#  - Run the "unattended-upgrade" security upgrade script 
#    every n-days (0=disabled)
#    Requires the package "unattended-upgrades" and will write
#    a log in /var/log/unattended-upgrades
# 
#  APT::Periodic::AutocleanInterval "0";
#  - Do "apt-get autoclean" every n-days (0=disable)
#
#  APT::Periodic::Verbose "0";
#  - Send report mail to root
#      0:  no report             (or null string)
#      1:  progress report       (actually any string)
#      2:  + command outputs     (remove -qq, remove 2>/dev/null, add -d)
#      3:  + trace on            

check_stamp()
{
    stamp="$1"
    interval="$2"

    if [ $interval -eq 0 ]; then
	debug_echo "check_stamp: interval=0."
	# treat as no time has passed
        return 1
    fi

    if [ ! -f $stamp ]; then
    	update_stamp $stamp
	debug_echo "check_stamp: missing time stamp file: $stamp."
	# treat as enough time has passed
        return 0
    fi

    # compare midnight today to midnight the day the stamp was updated
    stamp=$(date -r $stamp '+%s')
    delta=$(($now-$stamp))

    # intervall is in days, convert to sec.
    interval=$(($interval*60*60*24))
    debug_echo "check_stamp: interval=$interval, now=$now, stamp=$stamp, delta=$delta (sec)"

    if [ $delta -ge $interval ]; then
        return 0
    fi

    return 1
}

update_stamp()
{
    stamp="$1"
    touch $stamp
}

debug_echo()
{
    # Display message if $VERBOSE >= 1
    if [ "$VERBOSE" -ge 1 ]; then
	echo $1 1>&2
    fi
}

# check apt-config exstance
if ! which apt-config >/dev/null ; then
	exit 0
fi

# Set VERBOSE mode from  apt-config (or inherit from environment)
eval $(apt-config shell VERBOSE APT::Periodic::Verbose)
if [ -z "$VERBOSE" ]; then
    VERBOSE="0"
fi
if [ "$VERBOSE" -le 2 ]; then
    # quiet for 0,1,2
    XSTDOUT=">/dev/null"
    XSTDERR="2>/dev/null"
    XAPTOPT="-qq"
    XUUPOPT=""
else
    XSTDOUT=""
    XSTDERR=""
    XAPTOPT=""
    XUUPOPT="-d"
fi
if [ "$VERBOSE" -ge 3 ]; then
    # trace output
    set -x
fi
# laptop check, on_ac_power returns:
#       0 (true)    System is on main power
#       1 (false)   System is not on main power
#       255 (false) Power status could not be determined
# Desktop systems always return 255 it seems
if which on_ac_power >/dev/null; then
    on_ac_power
    POWER=$?
    if [ $POWER -eq 1 ]; then
	debug_echo "exit: system on main power."
	exit 0
    elif [ $POWER -ne 0 ]; then
	debug_echo "exit: power status ($POWER) undetermined."
	exit 0
    fi
    debug_echo "system is on main power."
fi

# check if we can lock the cache and if the cache is clean
if which apt-get >/dev/null && ! eval apt-get check $XAPTOPT $XSTDERR ; then
    debug_echo "error encountered in cron job with \"apt-get check\"."
    exit 0
fi
# No need to check for apt-get below

# Global current time in seconds since 1970-01-01 00:00:00 UTC
now=$(date +%s)

# Set default values and normalize
Dir="/"
eval $(apt-config shell Dir Dir)
Dir=${Dir%/}

CacheDir="var/cache/apt/"
eval $(apt-config shell CacheDir Dir::Cache)
CacheDir=${CacheDir%/}
if [ -z "$CacheDir" ]; then
    debug_echo "practically empty Dir::Cache, exiting"
    exit 0
fi

CacheArchive="archives/"
eval $(apt-config shell CacheArchive Dir::Cache::Archives)
CacheArchive=${CacheArchive%/}
if [ -z "$CacheArchive" ]; then
    debug_echo "practically empty Dir::Cache::archives, exiting"
    exit 0
fi

BackupArchiveInterval=0
eval $(apt-config shell BackupArchiveInterval APT::Periodic::BackupArchiveInterval)

BackupLevel=3
eval $(apt-config shell BackupLevel APT::Periodic::BackupLevel)
if [ $BackupLevel -le 1 ]; then BackupLevel=2 ; fi

CacheBackup="backup/"
eval $(apt-config shell CacheBackup Dir::Cache::Backup)
CacheBackup=${CacheBackup%/}
if [ -z "$CacheBackup" ]; then
    echo "practically empty Dir::Cache::Backup, exiting" 1>&2
    exit 0
fi

# Support old Archive for compatibility.
# Document only Periodic for all controling parameters of this script.
MaxAge=0
eval $(apt-config shell MaxAge APT::Archives::MaxAge)
eval $(apt-config shell MaxAge APT::Periodic::MaxAge)

MinAge=2
eval $(apt-config shell MinAge APT::Archives::MinAge)
eval $(apt-config shell MinAge APT::Periodic::MinAge)

MaxSize=0
eval $(apt-config shell MaxSize APT::Archives::MaxSize)
eval $(apt-config shell MaxSize APT::Periodic::MaxSize)

UpdateInterval=0
eval $(apt-config shell UpdateInterval APT::Periodic::Update-Package-Lists)

DownloadUpgradeableInterval=0
eval $(apt-config shell DownloadUpgradeableInterval APT::Periodic::Download-Upgradeable-Packages)

UnattendedUpgradeInterval=0
eval $(apt-config shell UnattendedUpgradeInterval APT::Periodic::Unattended-Upgrade)

AutocleanInterval=0
eval $(apt-config shell AutocleanInterval APT::Periodic::AutocleanInterval)

Cache="${Dir}/${CacheDir}/${CacheArchive}/"
Back="${Dir}/${CacheDir}/${CacheBackup}/"
BackX="${Back}${CacheArchive}/"
for x in $(seq 0 1 $((${BackupLevel}-1))); do 
    eval "Back${x}=${Back}${x}/"
done

# backup after n-days if archive contents changed.
# (This uses hardlink to save disk space)
BACKUP_ARCHIVE_STAMP=/var/lib/apt/periodic/backup-archive-stamp
if check_stamp $BACKUP_ARCHIVE_STAMP $BackupArchiveInterval; then
    if [ $({(cd $Cache 2>/dev/null; find . -name "*.deb"); (cd $Back0 2>/dev/null;find . -name "*.deb") ;}| sort|uniq -u|wc -l) -ne 0 ]; then
	mkdir -p $Back
	rm -rf $Back$((${BackupLevel}-1))
	for y in $(seq $((${BackupLevel}-1)) -1 1); do 
	    eval BackY=${Back}$y
	    eval BackZ=${Back}$(($y-1))
	    if [ -e $BackZ ]; then mv -f $BackZ $BackY ; fi
	done
	cp -la $Cache $Back ; mv -f $BackX $Back0
	update_stamp $BACKUP_ARCHIVE_STAMP
	debug_echo "backup with hardlinks. (success)"
    else

	debug_echo "skip backup since same content."
    fi
else
	debug_echo "skip backup since too new."
fi

# package archive contnts removal by package age
if [ $MaxAge -ne 0 ] && [ $MinAge -ne 0 ]; then
    find $Cache -name "*.deb"  \( -mtime +$MaxAge -and -ctime +$MaxAge \) -and -not \( -mtime -$MinAge -or -ctime -$MinAge \) -print0 | xargs -r -0 rm -f
    debug_echo "aged: ctime <$MaxAge and mtime <$MaxAge and ctime>$MinAge and mtime>$MinAge"
elif [ $MaxAge -ne 0 ]; then
    find $Cache -name "*.deb"  -ctime +$MaxAge -and -mtime +$MaxAge -print0 | xargs -r -0 rm -f
    debug_echo "aged: ctime <$MaxAge and mtime <$MaxAge only"
else
    debug_echo "skip aging since MaxAge is 0"
fi
    
# package archive contnts removal down to $MaxSize
if [ $MaxSize -ne 0 ]; then

    MinAgeSec=$(($MinAge*24*60*60))

    # reverse-sort by mtime
    for file in $(ls -rt $Cache/*.deb 2>/dev/null); do 
	du=$(du -m -s $Cache)
	size=${du%%/*}
	# check if the cache is small enough
	if [ $size -lt $MaxSize ]; then
	    debug_echo "end remove by archive size:  size=$size < $MaxSize"
	    break
	fi

	# check for MinAge in second of the file
	if [ $MinAgeSec -ne 0 ]; then 
	    # check both ctime and mtime 
	    mtime=$(stat -c %Y $file)
	    ctime=$(stat -c %Z $file)
	    if [ $mtime -gt $ctime ]; then
		delta=$(($now-$mtime))
	    else
		delta=$(($now-$ctime))
	    fi
	    if [ $delta -le $MinAgeSec ]; then
		debug_echo "skip remove by archive size:  $file, delta=$delta < $MinAgeSec"
	    else
		# delete oldest file
		debug_echo "remove by archive size: $file, delta=$delta >= $MinAgeSec (sec), size=$size >= $MaxSize"
		rm -f $file
	    fi
	fi

    done
fi

# update package lists
UPDATE_STAMP=/var/lib/apt/periodic/update-stamp
if check_stamp $UPDATE_STAMP $UpdateInterval; then
    if eval apt-get $XAPTOPT -y update $XSTDERR; then
	debug_echo "download updated metadata (success)."
	if which dbus-send >/dev/null; then
	    if dbus-send --system / app.apt.dbus.updated boolean:true ; then
		debug_echo "send dbus signal (success)"
	    else
		debug_echo "send dbus signal (error)"
	    fi
	else
	    debug_echo "dbus signal not send (command not available)"
	fi
	update_stamp $UPDATE_STAMP
	# download all upgradeable packages if it is requested
	DOWNLOAD_UPGRADEABLE_STAMP=/var/lib/apt/periodic/download-upgradeable-stamp
	if check_stamp $DOWNLOAD_UPGRADEABLE_STAMP $DownloadUpgradeableInterval; then
	    if eval apt-get $XAPTOPT -y -d dist-upgrade $XSTDERR; then
		update_stamp $DOWNLOAD_UPGRADEABLE_STAMP
		debug_echo "download upgradable (success)."
		# auto upgrade all upgradeable packages
		UPGRADE_STAMP=/var/lib/apt/periodic/upgrade-stamp
		if which unattended-upgrade >/dev/null && check_stamp $UPGRADE_STAMP $UnattendedUpgradeInterval; then
		    if unattended-upgrade $XUUPOPT; then
			update_stamp $UPGRADE_STAMP
			debug_echo "unattended-upgrade (success)."
		    else
			debug_echo "unattended-upgrade (error)."
		    fi
		else
		    debug_echo "unattended-upgrade (not run)."
		fi
	    else
		debug_echo "download upgradable (error)."
	    fi
	else
	    debug_echo "download upgradable (not run)."
	fi
    else
	debug_echo "download updated metadata (error)."
    fi
else
    debug_echo "download updated metadata (not run)."
fi

# autoclean package archive
AUTOCLEAN_STAMP=/var/lib/apt/periodic/autoclean-stamp
if check_stamp $AUTOCLEAN_STAMP $AutocleanInterval; then
    if apt-get $XAPTOPT -y autoclean $XSTDERR; then
	debug_echo "autoclean (success)."
	update_stamp $AUTOCLEAN_STAMP
    else
	debug_echo "autoclean (error)."
    fi
else
    debug_echo "autoclean (not run)."
fi

#
#     vim: set sts=4 ai :
#