221 lines
7.3 KiB
Bash
Executable File
221 lines
7.3 KiB
Bash
Executable File
#! /bin/bash
|
|
|
|
# @file update_install
|
|
# @author Nat Goodspeed
|
|
# @date 2013-01-09
|
|
# @brief Update the containing Second Life application bundle to the version in
|
|
# the specified tarball.
|
|
#
|
|
# This bash implementation is derived from the previous linux-updater.bin
|
|
# application.
|
|
#
|
|
# $LicenseInfo:firstyear=2013&license=viewerlgpl$
|
|
# Copyright (c) 2013, Linden Research, Inc.
|
|
# $/LicenseInfo$
|
|
|
|
# ****************************************************************************
|
|
# script parameters
|
|
# ****************************************************************************
|
|
tarball="$1" # the file to install
|
|
markerfile="$2" # create this file on failure
|
|
mandatory="$3" # what to write to markerfile on failure
|
|
|
|
# ****************************************************************************
|
|
# helper functions
|
|
# ****************************************************************************
|
|
# empty array
|
|
cleanups=()
|
|
|
|
# add a cleanup action to execute on exit
|
|
function cleanup {
|
|
# wacky bash syntax for appending to array
|
|
cleanups[${#cleanups[*]}]="$*"
|
|
}
|
|
|
|
# called implicitly on exit
|
|
function onexit {
|
|
for action in "${cleanups[@]}"
|
|
do # don't quote, support actions consisting of multiple words
|
|
$action
|
|
done
|
|
}
|
|
trap 'onexit' EXIT
|
|
|
|
# write to log file
|
|
function log {
|
|
# our log file will be open as stderr -- but until we set up that
|
|
# redirection, logging to stderr is better than nothing
|
|
echo "$*" 1>&2
|
|
}
|
|
|
|
# We display status by leaving one background xmessage process running. This
|
|
# is the pid of that process.
|
|
statuspid=""
|
|
|
|
function clear_message {
|
|
[ -n "$statuspid" ] && kill $statuspid
|
|
statuspid=""
|
|
}
|
|
|
|
# make sure we remove any message box we might have put up
|
|
cleanup clear_message
|
|
|
|
# can we use zenity, or must we fall back to xmessage?
|
|
zenpath="$(which zenity)"
|
|
if [ -n "$zenpath" ]
|
|
then # zenity on PATH and is executable
|
|
# display a message box and continue
|
|
function status {
|
|
# clear any previous message
|
|
clear_message
|
|
# put up a new zenity box and capture its pid
|
|
## "$zenpath" --info --title "Second Life Viewer Updater" \
|
|
## --width=320 --height=120 --text="$*" &
|
|
# MAINT-2333: use bouncing progress bar
|
|
"$zenpath" --progress --pulsate --no-cancel --title "Second Life Viewer Updater" \
|
|
--width=320 --height=120 --text "$*" </dev/null &
|
|
statuspid=$!
|
|
}
|
|
|
|
# display an error box and wait for user
|
|
function errorbox {
|
|
"$zenpath" --error --title "Second Life Viewer Updater" \
|
|
--width=320 --height=120 --text="$*"
|
|
}
|
|
|
|
else # no zenity, use xmessage instead
|
|
# display a message box and continue
|
|
function status {
|
|
# clear any previous message
|
|
clear_message
|
|
# put up a new xmessage and capture its pid
|
|
xmessage -buttons OK:2 -center "$*" &
|
|
statuspid=$!
|
|
}
|
|
|
|
# display an error box and wait for user
|
|
function errorbox {
|
|
xmessage -buttons OK:2 -center "$*"
|
|
}
|
|
fi
|
|
|
|
# display an error box and terminate
|
|
function fail {
|
|
# Log the message
|
|
log "$@"
|
|
# tell subsequent viewer things went south
|
|
echo "$mandatory" > "$markerfile"
|
|
# add boilerplate
|
|
errorbox "An error occurred while updating Second Life:
|
|
$*
|
|
Please download the latest viewer from www.secondlife.com."
|
|
exit 1
|
|
}
|
|
|
|
# Find a graphical sudo program and define mysudo function. On error, $? is
|
|
# nonzero; output is in $err instead of being written to stdout/stderr.
|
|
gksudo="$(which gksudo)"
|
|
kdesu="$(which kdesu)"
|
|
if [ -n "$gksudo" ]
|
|
then function mysudo {
|
|
# gksudo allows you to specify description
|
|
err="$("$gksudo" --description "Second Life Viewer Updater" "$@" 2>&1)"
|
|
}
|
|
elif [ -n "$kdesu" ]
|
|
then function mysudo {
|
|
err="$("$kdesu" "$@" 2>&1)"
|
|
}
|
|
else # couldn't find either one, just try it anyway
|
|
function mysudo {
|
|
err="$("$@" 2>&1)"
|
|
}
|
|
fi
|
|
|
|
# Move directories, using mysudo if we think it necessary. On error, $? is
|
|
# nonzero; output is in $err instead of being written to stdout/stderr.
|
|
function sudo_mv {
|
|
# If we have write permission to both parent directories, shouldn't need
|
|
# sudo.
|
|
if [ -w "$(dirname "$1")" -a -w "$(dirname "$2")" ]
|
|
then err="$(mv "$@" 2>&1)"
|
|
else # use available sudo program; mysudo sets $? and $err
|
|
mysudo mv "$@"
|
|
fi
|
|
}
|
|
|
|
# ****************************************************************************
|
|
# main script logic
|
|
# ****************************************************************************
|
|
mydir="$(dirname "$0")"
|
|
# We happen to know that the viewer specifies a marker-file pathname within
|
|
# the logs directory.
|
|
logsdir="$(dirname "$markerfile")"
|
|
logname="$logsdir/updater.log"
|
|
|
|
# move aside old updater.log; we're about to create a new one
|
|
[ -f "$logname" ] && mv "$logname" "$logname.old"
|
|
|
|
# Set up redirections for this script such that stderr is logged. (But first
|
|
# move the previous stderr to file descriptor 3.)
|
|
exec 3>&2- 2> "$logname"
|
|
|
|
# Rather than setting up a special pipeline to timestamp every line of stderr,
|
|
# produce header lines into log file indicating timestamp and the arguments
|
|
# with which we were invoked.
|
|
date 1>&2
|
|
log "$0 $*"
|
|
|
|
# Log every command we execute, along with any stderr it might produce
|
|
set -x
|
|
|
|
status 'Installing Second Life...'
|
|
|
|
# Creating tempdir under /tmp means it's possible that tempdir is on a
|
|
# different filesystem than INSTALL_DIR. One is tempted to create tempdir on a
|
|
# path derived from `dirname INSTALL_DIR` -- but it seems modern 'mv' can
|
|
# handle moving across filesystems??
|
|
tempdir="$(mktemp -d)"
|
|
tempinstall="$tempdir/install"
|
|
# capture the actual error message, if any
|
|
err="$(mkdir -p "$tempinstall" 2>&1)" || fail "$err"
|
|
cleanup rm -rf "$tempdir"
|
|
|
|
# If we already knew the name of the tarball's top-level directory, we could
|
|
# just move that when all was said and done. Since we don't, untarring to the
|
|
# 'install' subdir with --strip 1 effectively renames that top-level
|
|
# directory.
|
|
# untar failures tend to be voluminous -- don't even try to capture, just log
|
|
tar --strip 1 -xjf "$tarball" -C "$tempinstall" || fail "Untar command failed"
|
|
|
|
INSTALL_DIR="$(cd "$mydir/.." ; pwd)"
|
|
|
|
# Considering we're launched from a subdirectory of INSTALL_DIR, would be
|
|
# surprising if it did NOT already exist...
|
|
if [ -e "$INSTALL_DIR" ]
|
|
then backup="$INSTALL_DIR.backup"
|
|
backupn=1
|
|
while [ -e "$backup" ]
|
|
do backup="$INSTALL_DIR.backup.$backupn"
|
|
((backupn += 1))
|
|
done
|
|
# on error, fail with actual error message from sudo_mv: permissions,
|
|
# cross-filesystem mv, ...?
|
|
sudo_mv "$INSTALL_DIR" "$backup" || fail "$err"
|
|
fi
|
|
# We unpacked the tarball into tempinstall. Move that.
|
|
if ! sudo_mv "$tempinstall" "$INSTALL_DIR"
|
|
then # If we failed to move the temp install to INSTALL_DIR, try to restore
|
|
# INSTALL_DIR from backup. Save $err because next sudo_mv will trash it!
|
|
realerr="$err"
|
|
sudo_mv "$backup" "$INSTALL_DIR"
|
|
fail "$realerr"
|
|
fi
|
|
|
|
# Removing the tarball here, rather than with a 'cleanup' action, means we
|
|
# only remove it if we succeeded.
|
|
rm -f "$tarball"
|
|
|
|
# Launch the updated viewer. Restore original stderr from file descriptor 3,
|
|
# though -- otherwise updater.log gets cluttered with the viewer log!
|
|
"$INSTALL_DIR/secondlife" 2>&3- &
|