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

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