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

Revision 25617, 10.4 KB checked in by jdreed, 12 years ago (diff)
In auto-update: * Inhibit dpkg-preconfigure during auto-updates (Trac: #1020)
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 $pidfile
148    rm -f /var/run/athena-nologin
149    save_state
150    exit
151}
152trap finish EXIT
153
154v() {
155  [ "$VERBOSE" = "y" ] && echo "Running" "$@" >&3
156  echo "** Running:" "$@"
157  "$@"
158}
159
160
161# Allow hesiod cluster info to specify the debathena apt release.
162# (Will do nothing if debathena-clusterinfo isn't installed.)
163[ -x /usr/sbin/save-cluster-info ] && v /usr/sbin/save-cluster-info
164cinfo=/var/run/athena-clusterinfo.sh
165slist=/etc/apt/sources.list.d/debathena.clusterinfo.list
166if [ -r "$cinfo" ] && ( [ ! -e "$slist" ] || [ -w "$slist" ] ); then
167  echo "** Updating debathena.clusterinfo.list"
168  [ -e "$slist" ] && rm "$slist"
169  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)"
170  if [ -n "$dsource" ]; then
171    (. $cinfo
172     echo "# This file is automatically updated by debathena-auto-update"
173     echo "# based on your Hesiod cluster information. If you want to"
174     echo "# make changes, do so in another file."
175     echo
176     case $APT_RELEASE in
177       production)  ;;
178       proposed)    echo "$dsource" | awk '$3 !~ /-/ {$3 = $3 "-proposed"; print}' ;;
179       development) echo "$dsource" | awk '$3 != /-/ {$3 = $3 "-proposed"; print}'
180                    echo "$dsource" | awk '$3 != /-/ {$3 = $3 "-development"; print}' ;;
181     esac
182    ) > $slist
183  else
184    echo "Never mind, I can't figure out which sources.list line is Debathena's"
185  fi
186else
187  echo "** Skipping update of debathena.clusterinfo.list"
188fi
189
190# Tell apt not to expect user input during package installation.
191export DEBIAN_FRONTEND=noninteractive
192
193# Set conservative defaults in case file is missing
194UPDATE_FORCE_CONFFILE=old
195RUN_UPDATE_HOOK=no
196# Process defaults file
197[ -f /etc/default/debathena-auto-update ] && . /etc/default/debathena-auto-update
198# On cluster machines, force our desired settings
199# Ignore /etc/default/debathena-auto-update
200if dpkg-query --showformat '${Status}\n' -W "debathena-cluster" 2>/dev/null | grep -q ' installed$'; then
201    UPDATE_FORCE_CONFFILE=new
202    RUN_UPDATE_HOOK=yes
203fi
204
205UPDATE_HOOK_URL="https://athena10.mit.edu/update-hook/debathena-update-hook.sh"
206UPDATE_HOOK_SUM="https://athena10.mit.edu/update-hook/debathena-update-hook-sha256sum"
207MITCA="/usr/share/debathena-auto-update/mitCA.crt"
208UPDATE_HOOK="/var/run/debathena-update-hook.sh"
209
210rm -f $UPDATE_HOOK
211if [ "$RUN_UPDATE_HOOK" = "yes" ] && \
212   curl -sf -o $UPDATE_HOOK --cacert $MITCA $UPDATE_HOOK_URL; then
213   chmod 500 $UPDATE_HOOK
214   SHA256SUM="$(curl -sf --cacert $MITCA $UPDATE_HOOK_SUM)"
215   rv=$?
216   if [ $rv != 0 ]; then
217       complain "Failed to retrieve $UPDATE_HOOK_SUM (curl returned $rv)"
218       exit
219   fi
220   LOCALSUM="$(sha256sum $UPDATE_HOOK | awk '{print $1}')"
221   if [ "$SHA256SUM" != "$LOCALSUM" ]; then
222       complain "bad update hook checksum ($SHA256SUM != $LOCALSUM)"
223       exit
224   fi     
225   if ! [ -f "/var/lib/athena-update-hooks/$SHA256SUM" ]; then
226       if ! v $UPDATE_HOOK; then
227           complain "update hook returned non-zero status"
228           exit
229       else
230           touch "/var/lib/athena-update-hooks/$SHA256SUM"
231       fi
232   fi
233fi
234
235echo "Running aptitude install"
236if ! v aptitude --quiet --assume-yes install; then
237  # Don't fail, because make dpkg --configure -a will save us 
238  echo "ERROR: aptitude install failed, but continuing anyway"
239fi
240 
241
242# Configure any unconfigured packages (Trac #407)
243if ! v dpkg --configure -a; then
244  complain "Failed to configure unconfigured packages."
245  exit
246fi 
247
248# A recently configured package may want a reboot
249save_success "Rebooted after dpkg --configure -a"
250maybe_reboot
251
252# Ensure that the mirrors aren't broken
253urls=$(cat /etc/apt/sources.list /etc/apt/sources.list.d/*.list | grep -v ^# | grep -v ^$ | awk '{print $2}' | sort | uniq)
254failed=""
255for u in $urls; do
256    # We don't (yet) know how to verify other URI schemes
257    if ! echo $u | grep -q "^http:"; then
258        continue
259    fi
260    curl -m 60 -sfL -o /dev/null $u
261    if [ $? != 0 ]; then
262        if [ -z "$failed" ]; then
263            failed=$u
264        else
265            failed="$failed $u"
266        fi
267    fi
268done
269if [ -n "$failed" ]; then
270    warn "Failed to contact mirror(s): $failed"
271    exit
272fi
273
274# Update the aptitude cache.
275if ! v aptitude --quiet --assume-yes update; then
276  complain "aptitude update failed"
277  exit
278fi
279
280# If new licenses were installed since the last update, deal.
281licenses=/usr/share/debathena-license-config/debathena-license-selections
282if [ -f /var/lib/debathena-license-config/reconfigure_required ]; then
283    rm -f /var/lib/debathena-license-config/reconfigure_required
284    if [ -f $licenses ]; then
285        for p in $(awk '{print $1}' $licenses); do
286            if dpkg-query --showformat '${Status}\n' -W $p 2>/dev/null | grep -q ' installed$'; then
287                if ! v dpkg-reconfigure -fnoninteractive $p; then
288                    # Don't fail here
289                    complain "Failed to dpkg-reconfigure $p"
290                fi
291            fi
292        done
293    else
294        complain "Could not find $licenses"
295        exit
296    fi
297fi
298
299
300# Exit quietly (except for perhaps rebooting) if there are no upgrades
301# to take.
302pattern='^0 packages upgraded, 0 newly installed, 0 to remove'
303if v aptitude --simulate --assume-yes full-upgrade | grep -q "$pattern"; then
304  echo "Nothing to do!"
305  save_success "No updates"
306  maybe_reboot
307  exit
308fi
309
310# Download packages first.
311if ! v withtimeout aptitude --quiet --assume-yes --download-only full-upgrade; then
312  complain "download failed"
313  exit
314fi
315
316# If the debathena-reactivate package is installed, call into the
317# login snapshot script to create a root snapshot for the next update.
318if [ -x /usr/sbin/athena-login-snapshot ]; then
319  echo "** Creating root snapshot"
320  /usr/sbin/athena-login-snapshot update-start
321fi
322
323APTITUDE_OPTS=
324case $UPDATE_FORCE_CONFFILE in
325    old)
326        APTITUDE_OPTS="-o Dpkg::Options::=--force-confold"
327        export UCF_FORCE_CONFFOLD=1
328        ;;
329    new)
330        APTITUDE_OPTS="-o Dpkg::Options::=--force-confnew"
331        export UCF_FORCE_CONFFNEW=1
332        ;;
333    *)
334        complain "Invalid value for UPDATE_FORCE_CONFFILE"
335        exit
336        ;;
337esac
338
339
340# Perform the update.  In some corner cases, aptitude might decide
341# that the best course of action is to remove the Debathena
342# metapackage, so be paranoid about that.
343v aptitude --quiet --assume-yes keep-all
344v withtimeout aptitude --quiet --assume-yes --download-only dist-upgrade
345if result=$(aptitude -F "%p" search '?action(remove) (^debathena-cluster$ | ^debathena-workstation$ | ^debathena-login$ | ^debathena-standard$ | ^openafs-modules-~D^linux-image-)')
346  [ -n "$result" ]; then
347  echo "** metapackages would be removed by the update, aborting:" $result
348  v aptitude --quiet --assume-yes keep-all
349  complain "metapackages would be removed by update:" $result
350elif result=$(aptitude -F "%p" search '~b')
351  [ -n "$result" ]; then
352  echo "** packages would be broken by the update, aborting:" $result
353  v aptitude --quiet --assume-yes keep-all
354  complain "packages would be broken by update:" $result
355else
356  if v aptitude $APTITUDE_OPTS --quiet --assume-yes install; then
357    # Successful update, reset $updlast
358    updlast=$(date +"%s")
359    save_success "Successful update"
360  fi
361fi
362
363# Finally, update the apt-file cache
364v apt-file update
365
366if [ -x /usr/sbin/athena-login-snapshot ]; then
367  echo "** Cleaning up root snapshot"
368  /usr/sbin/athena-login-snapshot update-end
369fi
370
371maybe_reboot
372exit
Note: See TracBrowser for help on using the repository browser.