apt cron script
attached is the apt script that gets placed into /etc/cron.daily, with
what i think are improvements.
i am not on the mailing list, so please cc me in any discussions.
thanks.
--
Hiren Patel
hiren@obsidian.co.za
If you pick up a starving dog and make him prosperous, he will not bite you.
This is the principal difference between a dog and a man.
--Mark Twain
--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
#!/bin/bash
#
#
# This file understands the following apt configuration variables:
#
# APT::Periodic::Update-Package-Lists "1";
# - Do "apt-get update" automatically every n-days (0=disable)
#
# APT::Periodic::Download-Upgradeable-Packages "1";
# - Do "apt-get upgrade --download-only" every n-days (0=disable)
#
# APT::Periodic::AutocleanInterval" "1";
# - Do "apt-get autoclean" every n-days (0=disable)
#
# APT::Archives::MaxAge",
# - Set maximum allowed age of a cache package file. If a cache
# package file is older it is deleted (0=disable)
#
# APT::Archives::MaxSize",
# - 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::Archives::MinAge"
# - 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.
#
check_stamp()
{
if [[ "$#" -ne 2 ]]; then
echo "check_stamp: arguments to function does not equal 2, returning 1." 1>&2
return 1
fi
local file="$1"
local file_mod_time=""
local now=""
declare -i now_seconds=""
declare -i stamp=0
declare -i interval="$2"
declare -i delta=0
declare -i stamp_error=0
if [ "$interval" -eq 0 ]; then
return 1
fi
if ! [ -f "$file" ]; then
return 0
fi
if file_mod_time=$(date -r $file --iso-8601 2>/dev/null); then
if [[ "$file_mod_time" =~ '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' ]]; then
if stamp=$(date --date=${file_mod_time} +%s 2>/dev/null); then
if [[ "$stamp" -gt 0 ]]; then
if now=$(date --iso-8601 2>/dev/null); then
if [[ "$now" =~ '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' ]]; then
if now_seconds=$(date --date=${now} +%s 2>/dev/null); then
if [[ "$now_seconds" -gt 0 ]]; then
delta=$((${now_seconds}-${stamp}))
if ! [[ "$delta" -ge 0 ]]; then
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
else
stamp_error=1
fi
if [[ "$stamp_error" -eq 1 ]]; then
echo "timestamp calculation failed somewhere, file is ${file}, returning 1." 1>&2
return 1
fi
# interval is in days,
interval=$((${interval}*60*60*24))
if ! [[ "$interval" -gt 0 ]]; then
echo "check_stamp: variable 'interval' is not > 0, returning 1" 1>&2
return 1
fi
if [ "$delta" -ge "$interval" ]; then
return 0
else
return 1
fi
}
update_stamp()
{
if [[ "$#" -ne 1 ]]; then
echo "update_stamp: number of arguments to functions is not exactly 1, returning 1." 1>&2
return 1
fi
local file="$1"
if ! [ -f "$file" ]; then
return 1
fi
if touch $file; then
return 0
else
return 1
fi
}
# we check here if autoclean was enough sizewise
check_size_constraints()
{
set --
# min-age in days
declare -i MaxAge=0
declare -i MinAge=2
declare -i MaxSize=0
local Dir="/"
local CacheDir="var/cache/apt"
local CacheArchive="archives/"
local to_run=""
local Cache=""
local tempfile=""
local tempdir=""
local filename=""
if ! to_run=$(apt-config shell MaxAge APT::Archives::MaxAge MinAge APT::Archives::MinAge MaxSize APT::Archives::MaxSize Dir Dir CacheDir Dir::Cache CacheArchive Dir::Cache::archives); then
echo "check_size_constraints: could not retrieve apt variables with apt-config, returning 1." 1>&2
return 1
fi
if ! eval "$to_run"; then
echo "check_size_constraints: could not initialize apt variables, returning 1." 1>&2
return 1
fi
# sanity check
if [ -z "$CacheDir" -o -z "$CacheArchive" -o -z "$Dir" ]; then
echo "check_size_constraints: empty Dir::Cache or Dir::Cache::archives or Dir in apt config, returning 1." 1>&2
return 1
fi
if ! [[ "$MaxAge" -ge 0 && "$MinAge" -ge 0 && "$MaxSize" -ge 0 ]]; then
echo "check_size_constraints: variable MaxAge or MinAge or MaxSize is not >= 0, returning 1." 1>&2
return 1
fi
if ! [[ "$CacheDir" =~ '^[a-zA-Z0-9/_-]+$' && "$Dir" =~ '^[a-zA-Z0-9/_-]+$' && "$CacheArchive" =~ '^[a-zA-Z0-9/_-]+$' ]]; then
echo "check_size_constraints: variables CacheDir or Dir or CacheArchive failed syntax check, returning 1." 1>&2
return 1
fi
Cache="${Dir%/}/${CacheDir%/}/${CacheArchive%/}"
if ! [[ -d "$Cache" && -r "$Cache" && -w "$Cache" ]]; then
echo "check_size_constraints: directory '$Cache' does not exist or is not readable or writable, returning 1." 1>&2
return 1
fi
# check age
if [[ "$MaxAge" -gt 0 ]]; then
TMPDIR=""
if [[ -d "/var/tmp" && -r "/var/tmp" && -w "/var/tmp" ]]; then
tmpdir="/var/tmp"
else
tmpdir="/tmp"
fi
trap "rm -f $tempfile; exit 1" 1 2 3 15
if tempfile=$(mktemp -p $tmpdir aptcronscript.XXXXXX); then
#the reason why MaxAge is reduced by 1:
#in find(1)
#When find figures out how many 24-hour periods ago the file was last accessed,
#any fractional part is ignored, so to match -atime +1, a file has to have been modified at least two days ago.
#same seems to apply for ctime and mtime
if ! [[ "$MaxAge" -eq 0 && "$MinAge" -eq 0 ]]; then
find $Cache -name "*.deb" \( -mtime +$(($MaxAge - 1)) -and -ctime +$(($MaxAge -1 )) \) -and -not \( -mtime -${MinAge} -or -ctime -${MinAge} \) > $tempfile
elif ! [ "$MaxAge" -eq 0 ]; then
find $Cache -name "*.deb" -ctime +$(($MaxAge - 1)) -and -mtime +$(($MaxAge - 1)) > $tempfile
fi
if [[ -s "$tempfile" ]]; then
{
while read filename; do
if ! rm -f $filename; then
echo "check_size_constraints: failed to remove file '$filename'" 1>&2
fi
done;
} < ${tempfile}
fi
rm -f $tempfile
trap - 1 2 3 15
else
trap - 1 2 3 15
echo "check_size_constraints: creating temp file returned non-zero status, returning 1." 1>&2
return 1
fi
fi
# check size
if ! [ "$MaxSize" -eq 0 ]; then
# MaxSize is in MB
MaxSize=$(($MaxSize*1024))
if ! [[ "$MaxSize" -gt 0 ]]; then
echo "check_size_constraints: varilable MaxSize not greater then zero after conversion to MB, returning 1." 1>&2
return 1
fi
local date=""
local now=""
#get current time
if date=$(date --iso-8601); then
if ! now=$(date --date=$(date --iso-8601) +%s); then
echo "check_size_constraints: failed to accuire current time, returning 1." 1>&2
return 1
fi
else
echo "check_size_constraints: failed to accuire current date, returning 1." 1>&2
return 1
fi
MinAge=$(($MinAge*24*60*60))
if ! [[ "$MinAge" -ge 0 ]]; then
echo "check_size_constraints: variable MinAge not greater or equal to zero after conversion to seconds, returning 1." 1>&2
return 1
fi
trap "rm -f $tempfile; exit 1" 1 2 3 15
if tempfile=$(mktemp -p $tmpdir aptcronscript.XXXXXX); then
if ! find $Cache -name '*.deb' > $tempfile; then
rm -f $tempfile
trap - 1 2 3 15
echo "check_size_constraints: check size: find for .deb files returned non-zero, returning 1." 1>&2
return 1
fi
if [[ -s "$tempfile" ]]; then
{
declare -i size=0
declare -i mtime=0
declare -i delta=0
while read filename; do
if ! size=$(stat -c '%s' $filename); then
echo "check_size_constraints: check size: could not stat file $filename" 1>&2
continue
fi
if ! mtime=$(stat -c '%Y' $filename); then
echo "check_size_constraints: check size: could not get mtime of $filename" 1>&2
continue
fi
if ! [[ "$zize" -gt 0 && "$now" -gt 0 && "$mtime" -gt 0 ]]; then
echo "check_size_constraints: check size: variable size or now or mtime not greater then zero." 1>&2
continue
fi
delta=$(($now - $mtime))
if ! [[ "$delta" -gt 0 ]]; then
echo "check_size_constraints: check size: time - mtime for $filename not greater then zero." 1>&2
continue
fi
if [[ "$delta" -lt "$MinAge" ]]; then
continue
fi
if [[ "$size" -lt "$MaxSize" ]]; then
continue
fi
if ! rm -f $filename; then
echo "check_size_constraints: check size: could not delete file $filename" 1>&2
continue
fi
done;
} < ${tempfile}
fi
rm -f $tempfile
trap - 1 2 3 15
else
trap - 1 2 3 15
echo "check_size_constraints: check size: creating temp file returned non-zero status, returning 1." 1>&2
return 1
fi
fi
}
declare -i UpdateInterval=0
declare -i DownloadUpgradeableInterval=0
declare -i AutocleanInterval=0
to_run=""
if ! to_run=$(apt-config shell UpdateInterval APT::Periodic::Update-Package-Lists DownloadUpgradeableInterval APT::Periodic::Download-Upgradeable-Packages AutocleanInterval APT::Periodic::AutocleanInterval 2>/dev/null); then
echo "could not retrieve apt variables with apt-config, exiting." 1>&2
exit 1
fi
if ! eval "$to_run"; then
echo "could not initialize apt variables, exiting." 1>&2
exit 1
fi
if ! [[ "$UpdateInterval" -ge 0 && "$DownloadUpgradeableInterval" -ge 0 && "$AutocleanInterval" -ge 0 ]]; then
echo "variable UpdateInterval or DownloadUpgradeableInterval is not >= 0, exiting." 1>&2
exit 1
fi
if [[ "$UpdateInterval" -eq 0 && "$DownloadUpgradeableInterval" -gt 0 ]]; then
echo "note : package index file syncronization has been disabled, yet upgradeable package downloads have been enabled." 1>&2
fi
if [[ "$DownloadUpgradeableInterval" -lt "$UpdateInterval" ]]; then
echo "note : the package index file synchronization interval is longer then the upgradeable package download interval." 1>&2
fi
declare -i AutocleanInterval=${AutocleanInterval:-DownloadUpgradeableInterval}
if ! [[ "$AutocleanInterval" -ge 0 ]]; then
echo "variable AutocleanInterval is not >= 0, exiting." 1>&2
exit 1
fi
# laptop check, on_ac_power returns:
# 0 (true) System is on mains power
# 1 (false) System is not on mains 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
if [ "$?" -eq 1 ]; then
echo "on_ac_power determined system to not be powered by mains, exiting." 1>&2
exit 0
fi
fi
# since we're working with timestamps in the directory, non-root users should not be able to change timestamps of files
# within the directory, if it looks like users can do this, the script exits.
# note: this only accomodates for the standard unix permissions model, ACL's could still allow a non-root user to change timestamps
# of files in the directory.
if ! [[ "$(stat -c %u /var/lib/apt/periodic 2>/dev/null)" == "0" && "$(stat -c %a /var/lib/apt/periodic 2>/dev/null)" == "755" ]]; then
echo "directory /var/lib/apt/periodic isnt owned by root or doesnt have permissions 755, exiting." 1>&2
exit 1
fi
UPDATE_STAMP="/var/lib/apt/periodic/update-stamp"
if check_stamp $UPDATE_STAMP $UpdateInterval; then
echo "--- running apt-get update ---" 1>&2
#not pipeing stderr to /dev/null since we want to see the errors if the command fails
#since this script is run by cron, errors of this command will be in the mail cron sends, which is usefull
apt-get -qq update
if [[ "$?" -eq 0 ]]; then
if which dbus-send >/dev/null; then
dbus-send --system / app.apt.dbus.updated boolean:true
fi
if ! update_stamp $UPDATE_STAMP; then
echo "updating timestamp of file '$UPDATE_STAMP' failed." 1>&2
fi
else
echo "" 1>&2
echo "'apt-get update' returned non-zero status, exiting." 1>&2
exit 1
fi
fi
DOWNLOAD_UPGRADEABLE_STAMP="/var/lib/apt/periodic/download-upgradeable-stamp"
if check_stamp $DOWNLOAD_UPGRADEABLE_STAMP $DownloadUpgradeableInterval; then
echo "--- running apt-get -d dist-upgrade ---" 1>&2
#not pipeing stderr to /dev/null since we want to see the errors if the command fails
#since this script is run by cron, errors of this command will be in the mail cron sends, which is usefull
apt-get -qq -d dist-upgrade
if [[ "$?" -eq 0 ]]; then
if ! update_stamp $DOWNLOAD_UPGRADEABLE_STAMP; then
echo "updating timestamp of file '$DOWNLOAD_UPGRADEABLE_STAMP' failed." 1>&2
fi
else
echo "" 1>&2
echo "'apt-get -d dist-upgrade' returned non-zero status, exiting." 1>&2
exit 1
fi
fi
AUTOCLEAN_STAMP="/var/lib/apt/periodic/autoclean-stamp"
if check_stamp $AUTOCLEAN_STAMP $AutocleanInterval; then
echo "--- running apt-get autoclean ---" 1>&2
#not pipeing stderr to /dev/null since we want to see the errors if the command fails
#since this script is run by cron, errors of this command will be in the mail cron sends, which is usefull
apt-get -qq autoclean
if [[ "$?" -eq 0 ]]; then
if ! update_stamp $AUTOCLEAN_STAMP; then
echo "updateing timestamp of file '$AUTOCLEAN_STAMP' failed." 1>&2
fi
else
echo "" 1>&2
echo "'apt-get autoclean' returned non-zero status." 1>&2
fi
fi
# check cache size
if ! check_size_constraints; then
echo "function check_size_constraints returned with a non-zero status!." 1>&2
fi
Reply to: