source: trunk/debathena/config/auto-update/debian/athena-auto-update @ 25621

Revision 25621, 10.5 KB checked in by kaduk, 12 years ago (diff)
Emit proper comment string for apt.conf files
Line 
1#!/bin/sh
2
3complain() {
4  logger -t "athena-auto-update" -p user.notice "$*"
5  updstatus="failed"
6  updmsg="$*"
7}
8
9# NOTE: Unlike complain(), calling warn() without subsequently
10#       calling "exit" is not supported and its behavior is undefined
11warn() {
12  # If we've already had a warning once, then fail
13  if [ "$updstatus" = "warning" ]; then
14      complain "$*"
15  else
16      updstatus="warning"
17      updmsg="$*"
18  fi
19}
20
21save_success() {
22  # We used to check for 'warning' here for non-fatal warnings.
23  # There is no longer any such thing.
24  updstatus="ok"
25  updmsg="$*"
26}
27
28save_state() {
29  rm -f $statfile
30  echo "$updlast|$(date +"%s")|$updstatus|$updmsg" > $statfile
31
32
33maybe_reboot() {
34  if [ "$SKIP_REBOOT" = "y" ]; then
35      return
36  fi
37  if [ -f /var/run/reboot-required ]; then
38    # A package wants us to reboot the machine.  Do so if no one is
39    # logged in.  Be paranoid about stale utmp entries.
40    ttys=$(w -h -s | awk '{print $2}')
41    for tty in $ttys; do
42      pids=$(ps --no-heading -j -t "$tty" 2>/dev/null \
43             | awk '($1 == $3) {print $1}')
44      if [ -n "$pids" ]; then
45        return
46      fi
47    done
48    # screen processes count as logins.
49    if pgrep '^screen' > /dev/null; then
50      return
51    fi
52    save_state
53    reboot
54    exit
55  fi
56}
57
58withtimeout () {
59  if dpkg-query --showformat '${Status}\n' -W timeout 2>/dev/null | grep -q ' installed$'; then
60    # tct timeout
61    timeout 1810 timeout -15 1800 "$@"
62  else
63    # coreutils timeout
64    timeout -k 10s 30m "$@"
65  fi
66}
67
68SKIP_REBOOT="n"
69DEBUG="n"
70VERBOSE="n"
71while getopts "nvd" opt; do
72    case "$opt" in
73        d) DEBUG="y";;
74        v) VERBOSE="y";;
75        n) SKIP_REBOOT="y";;
76        \?)
77            echo "Usage: $0 [ -d ] [ -n ] [ -v ]"
78            ;;
79    esac
80done
81
82[ "$DEBUG" = "y" ] && VERBOSE="y"
83
84if [ 0 != "$(id -u)" ]; then
85  echo "This script must be run as root." >&2
86  exit 1
87fi
88
89# Don't run updates during a cluster login.
90# Unless forced
91if [ -e /var/run/athena-login ] && [ "$DEBUG" != "y" ]; then
92  exit 0
93fi
94
95# Avoid confusing the system by running two updates at once.
96pidfile=/var/run/athena-update.pid
97if [ -e $pidfile ]; then
98  if ! kill -0 "$(cat $pidfile)" 2>/dev/null; then
99    rm -f $pidfile
100  fi
101fi
102(set -o noclobber; echo $$ > $pidfile) 2>/dev/null || exit
103
104trap 'rm -f $pidfile' EXIT
105
106statfile="/var/lib/athena-update-status"
107updstatus="unknown"
108updmsg=""
109updlast=$(date +"%s")
110
111# Get the last successful update
112if [ -f $statfile ]; then
113    updlast=$(awk -F\| '{print $1;}' $statfile)
114    updstatus=$(awk -F\| '{print $3;}' $statfile)
115fi
116
117# Make sure nothing expects input on stdin.
118exec </dev/null
119
120# Save a reference to STDOUT
121exec 3>&1
122
123# Redirect further output to a log file.
124exec >>/var/log/athena-update 2>&1
125
126# Write a log header now and a footer at exit.
127# Also write a target for cluster's /etc/nologin symlink.
128echo "-----"
129echo "** Beginning Athena auto-update at $(date)"
130
131cat > /var/run/athena-nologin << NOLOGIN
132This system is currently taking software updates.
133Please log in to a different system.
134NOLOGIN
135
136echo "Inhibiting dpkg-preconfigure to work around aptitude bugs..."
137cat > /etc/apt/apt.conf.d/99debathena-inhibit-preconfigure <<APTCONF
138// This file should only exist while an auto-update is in progress.
139// It will be removed without notice.  You have been warned.
140#clear DPkg::Pre-Install-Pkgs;
141APTCONF
142
143finish() {
144    echo "** Ending Athena auto-update at $(date)"
145    echo "-----"
146    echo
147    rm -f /etc/apt/apt.conf.d/99debathena-inhibit-preconfigure
148    rm -f $pidfile
149    rm -f /var/run/athena-nologin
150    save_state
151    exit
152}
153trap finish EXIT
154
155v() {
156  [ "$VERBOSE" = "y" ] && echo "Running" "$@" >&3
157  echo "** Running:" "$@"
158  "$@"
159}
160
161
162# Allow hesiod cluster info to specify the debathena apt release.
163# (Will do nothing if debathena-clusterinfo isn't installed.)
164[ -x /usr/sbin/save-cluster-info ] && v /usr/sbin/save-cluster-info
165cinfo=/var/run/athena-clusterinfo.sh
166slist=/etc/apt/sources.list.d/debathena.clusterinfo.list
167if [ -r "$cinfo" ] && ( [ ! -e "$slist" ] || [ -w "$slist" ] ); then
168  echo "** Updating debathena.clusterinfo.list"
169  [ -e "$slist" ] && rm "$slist"
170  dsource="$(egrep -h '^deb(-src)? http://debathena\.mit\.edu/apt ' /etc/apt/sources.list /etc/apt/sources.list.d/*.list 2>/dev/null | sort -u)"
171  if [ -n "$dsource" ]; then
172    (. $cinfo
173     echo "# This file is automatically updated by debathena-auto-update"
174     echo "# based on your Hesiod cluster information. If you want to"
175     echo "# make changes, do so in another file."
176     echo
177     case $APT_RELEASE in
178       production)  ;;
179       proposed)    echo "$dsource" | awk '$3 !~ /-/ {$3 = $3 "-proposed"; print}' ;;
180       development) echo "$dsource" | awk '$3 != /-/ {$3 = $3 "-proposed"; print}'
181                    echo "$dsource" | awk '$3 != /-/ {$3 = $3 "-development"; print}' ;;
182     esac
183    ) > $slist
184  else
185    echo "Never mind, I can't figure out which sources.list line is Debathena's"
186  fi
187else
188  echo "** Skipping update of debathena.clusterinfo.list"
189fi
190
191# Tell apt not to expect user input during package installation.
192export DEBIAN_FRONTEND=noninteractive
193
194# Set conservative defaults in case file is missing
195UPDATE_FORCE_CONFFILE=old
196RUN_UPDATE_HOOK=no
197# Process defaults file
198[ -f /etc/default/debathena-auto-update ] && . /etc/default/debathena-auto-update
199# On cluster machines, force our desired settings
200# Ignore /etc/default/debathena-auto-update
201if dpkg-query --showformat '${Status}\n' -W "debathena-cluster" 2>/dev/null | grep -q ' installed$'; then
202    UPDATE_FORCE_CONFFILE=new
203    RUN_UPDATE_HOOK=yes
204fi
205
206UPDATE_HOOK_URL="https://athena10.mit.edu/update-hook/debathena-update-hook.sh"
207UPDATE_HOOK_SUM="https://athena10.mit.edu/update-hook/debathena-update-hook-sha256sum"
208MITCA="/usr/share/debathena-auto-update/mitCA.crt"
209UPDATE_HOOK="/var/run/debathena-update-hook.sh"
210
211rm -f $UPDATE_HOOK
212if [ "$RUN_UPDATE_HOOK" = "yes" ] && \
213   curl -sf -o $UPDATE_HOOK --cacert $MITCA $UPDATE_HOOK_URL; then
214   chmod 500 $UPDATE_HOOK
215   SHA256SUM="$(curl -sf --cacert $MITCA $UPDATE_HOOK_SUM)"
216   rv=$?
217   if [ $rv != 0 ]; then
218       complain "Failed to retrieve $UPDATE_HOOK_SUM (curl returned $rv)"
219       exit
220   fi
221   LOCALSUM="$(sha256sum $UPDATE_HOOK | awk '{print $1}')"
222   if [ "$SHA256SUM" != "$LOCALSUM" ]; then
223       complain "bad update hook checksum ($SHA256SUM != $LOCALSUM)"
224       exit
225   fi     
226   if ! [ -f "/var/lib/athena-update-hooks/$SHA256SUM" ]; then
227       if ! v $UPDATE_HOOK; then
228           complain "update hook returned non-zero status"
229           exit
230       else
231           touch "/var/lib/athena-update-hooks/$SHA256SUM"
232       fi
233   fi
234fi
235
236echo "Running aptitude install"
237if ! v aptitude --quiet --assume-yes install; then
238  # Don't fail, because make dpkg --configure -a will save us 
239  echo "ERROR: aptitude install failed, but continuing anyway"
240fi
241 
242
243# Configure any unconfigured packages (Trac #407)
244if ! v dpkg --configure -a; then
245  complain "Failed to configure unconfigured packages."
246  exit
247fi 
248
249# A recently configured package may want a reboot
250save_success "Rebooted after dpkg --configure -a"
251maybe_reboot
252
253# Ensure that the mirrors aren't broken
254urls=$(cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list | grep -v ^# | grep -v ^$ | awk '{print $2}' | sort | uniq)
255failed=""
256for u in $urls; do
257    # We don't (yet) know how to verify other URI schemes
258    if ! echo $u | grep -q "^http:"; then
259        continue
260    fi
261    curl -m 60 -sfL -o /dev/null $u
262    if [ $? != 0 ]; then
263        if [ -z "$failed" ]; then
264            failed=$u
265        else
266            failed="$failed $u"
267        fi
268    fi
269done
270if [ -n "$failed" ]; then
271    warn "Failed to contact mirror(s): $failed"
272    exit
273fi
274
275# Update the aptitude cache.
276if ! v aptitude --quiet --assume-yes update; then
277  complain "aptitude update failed"
278  exit
279fi
280
281# If new licenses were installed since the last update, deal.
282licenses=/usr/share/debathena-license-config/debathena-license-selections
283if [ -f /var/lib/debathena-license-config/reconfigure_required ]; then
284    rm -f /var/lib/debathena-license-config/reconfigure_required
285    if [ -f $licenses ]; then
286        for p in $(awk '{print $1}' $licenses); do
287            if dpkg-query --showformat '${Status}\n' -W $p 2>/dev/null | grep -q ' installed$'; then
288                if ! v dpkg-reconfigure -fnoninteractive $p; then
289                    # Don't fail here
290                    complain "Failed to dpkg-reconfigure $p"
291                fi
292            fi
293        done
294    else
295        complain "Could not find $licenses"
296        exit
297    fi
298fi
299
300
301# Exit quietly (except for perhaps rebooting) if there are no upgrades
302# to take.
303pattern='^0 packages upgraded, 0 newly installed, 0 to remove'
304if v aptitude --simulate --assume-yes full-upgrade | grep -q "$pattern"; then
305  echo "Nothing to do!"
306  save_success "No updates"
307  maybe_reboot
308  exit
309fi
310
311# Download packages first.
312if ! v withtimeout aptitude --quiet --assume-yes --download-only full-upgrade; then
313  complain "download failed"
314  exit
315fi
316
317# If the debathena-reactivate package is installed, call into the
318# login snapshot script to create a root snapshot for the next update.
319if [ -x /usr/sbin/athena-login-snapshot ]; then
320  echo "** Creating root snapshot"
321  /usr/sbin/athena-login-snapshot update-start
322fi
323
324APTITUDE_OPTS=
325case $UPDATE_FORCE_CONFFILE in
326    old)
327        APTITUDE_OPTS="-o Dpkg::Options::=--force-confold"
328        export UCF_FORCE_CONFFOLD=1
329        ;;
330    new)
331        APTITUDE_OPTS="-o Dpkg::Options::=--force-confnew"
332        export UCF_FORCE_CONFFNEW=1
333        ;;
334    *)
335        complain "Invalid value for UPDATE_FORCE_CONFFILE"
336        exit
337        ;;
338esac
339
340
341# Perform the update.  In some corner cases, aptitude might decide
342# that the best course of action is to remove the Debathena
343# metapackage, so be paranoid about that.
344v aptitude --quiet --assume-yes keep-all
345v withtimeout aptitude --quiet --assume-yes --download-only dist-upgrade
346if result=$(aptitude -F "%p" search '?action(remove) (^debathena-cluster$ | ^debathena-workstation$ | ^debathena-login$ | ^debathena-standard$ | ^openafs-modules-~D^linux-image-)')
347  [ -n "$result" ]; then
348  echo "** metapackages would be removed by the update, aborting:" $result
349  v aptitude --quiet --assume-yes keep-all
350  complain "metapackages would be removed by update:" $result
351elif result=$(aptitude -F "%p" search '~b')
352  [ -n "$result" ]; then
353  echo "** packages would be broken by the update, aborting:" $result
354  v aptitude --quiet --assume-yes keep-all
355  complain "packages would be broken by update:" $result
356else
357  if v aptitude $APTITUDE_OPTS --quiet --assume-yes install; then
358    # Successful update, reset $updlast
359    updlast=$(date +"%s")
360    save_success "Successful update"
361  fi
362fi
363
364# Finally, update the apt-file cache
365v apt-file update
366
367if [ -x /usr/sbin/athena-login-snapshot ]; then
368  echo "** Cleaning up root snapshot"
369  /usr/sbin/athena-login-snapshot update-end
370fi
371
372maybe_reboot
373exit
Note: See TracBrowser for help on using the repository browser.