summaryrefslogtreecommitdiff
path: root/indra/viewer_components/updater/scripts/linux/update_install
blob: a9df9042fda2a029d55cbef46f707926b06cafc9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#! /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="$*" &
         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- &