Merged with head of viewer-development
commit
106bb38ac7
12
.hgtags
12
.hgtags
|
|
@ -402,3 +402,15 @@ af6b711a97073431953b55ee808aaa09900c27e5 DRTVWR-276
|
|||
8302fefde6c8f4a64bfc7f04929f8bc85f5c6c7b DRTVWR-279
|
||||
c296133849d1f103c0e2abc41e6599daed00b67b DRTVWR-280
|
||||
40a2265058abc9fde4914c10185f916435818621 3.4.5-beta1
|
||||
5df4802bec93c8d0a509946d826bb4c50c5442ec DRTVWR-281
|
||||
7c1c33ba4cfd2d15ca51cc1ac440eca551331a4a DRTVWR-283
|
||||
6b9c7dbebef793230d64e1b452577c8b142d4143 3.4.5-beta2
|
||||
37947e4f771f001b551581bf7cd0051c3153beed DRTVWR-282
|
||||
6482cceb91cda68b799f3e6cdc66d33bf123547a DRTVWR-284
|
||||
ccf991e02dc2f63fb646324230d54832683f4a9b DRTVWR-286
|
||||
2d849850558a5a0324b398d1c102d30bcbdfb88f DRTVWR-287
|
||||
e06898df8644fe567bee94f817d03abc1c380993 3.4.5-beta3
|
||||
a676b4d6c037b39fe5b8e42cf8839a9303936089 DRTVWR-289
|
||||
28fa8b944a0c1869636ab00cc400f5aa71f6fa3c DRTVWR-290
|
||||
7f09bbc28c297f14b67961be7b6575445fa160e8 DRTVWR-291
|
||||
b23419a2748483c98f3b84b630468a21c88feba5 DRTVWR-292
|
||||
|
|
|
|||
|
|
@ -82,8 +82,7 @@ if (VIEWER)
|
|||
|
||||
if (LINUX)
|
||||
add_subdirectory(${VIEWER_PREFIX}linux_crash_logger)
|
||||
add_subdirectory(${VIEWER_PREFIX}linux_updater)
|
||||
add_dependencies(viewer linux-crash-logger-strip-target linux-updater)
|
||||
add_dependencies(viewer linux-crash-logger-strip-target)
|
||||
elseif (DARWIN)
|
||||
add_subdirectory(${VIEWER_PREFIX}mac_crash_logger)
|
||||
add_subdirectory(${VIEWER_PREFIX}mac_updater)
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
# -*- cmake -*-
|
||||
|
||||
project(linux_updater)
|
||||
|
||||
include(00-Common)
|
||||
include(CURL)
|
||||
include(CARes)
|
||||
include(OpenSSL)
|
||||
include(UI)
|
||||
include(LLCommon)
|
||||
include(LLVFS)
|
||||
include(LLXML)
|
||||
include(LLUI)
|
||||
include(Linking)
|
||||
|
||||
include_directories(
|
||||
${LLCOMMON_INCLUDE_DIRS}
|
||||
${LLVFS_INCLUDE_DIRS}
|
||||
${LLXML_INCLUDE_DIRS}
|
||||
${LLUI_INCLUDE_DIRS}
|
||||
${CURL_INCLUDE_DIRS}
|
||||
${CARES_INCLUDE_DIRS}
|
||||
${OPENSSL_INCLUDE_DIRS}
|
||||
${UI_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(linux_updater_SOURCE_FILES linux_updater.cpp)
|
||||
|
||||
set(linux_updater_HEADER_FILES CMakeLists.txt)
|
||||
|
||||
set_source_files_properties(${linux_updater_HEADER_FILES}
|
||||
PROPERTIES HEADER_FILES_ONLY TRUE)
|
||||
|
||||
list(APPEND linux_updater_SOURCE_FILES ${linux_updater_HEADER_FILES})
|
||||
|
||||
add_executable(linux-updater ${linux_updater_SOURCE_FILES})
|
||||
|
||||
target_link_libraries(linux-updater
|
||||
${CURL_LIBRARIES}
|
||||
${CARES_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${UI_LIBRARIES}
|
||||
${LLXML_LIBRARIES}
|
||||
${LLUI_LIBRARIES}
|
||||
${LLVFS_LIBRARIES}
|
||||
${LLCOMMON_LIBRARIES}
|
||||
)
|
||||
|
||||
add_custom_target(linux-updater-target ALL
|
||||
DEPENDS linux-updater)
|
||||
|
|
@ -1,926 +0,0 @@
|
|||
/**
|
||||
* @file linux_updater.cpp
|
||||
* @author Kyle Ambroff <ambroff@lindenlab.com>, Tofu Linden
|
||||
* @brief Viewer update program for unix platforms that support GTK+
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llerrorcontrol.h"
|
||||
#include "llfile.h"
|
||||
#include "lldir.h"
|
||||
#include "lldiriterator.h"
|
||||
|
||||
/*==========================================================================*|
|
||||
// IQA-490: Use of LLTrans -- by this program at least -- appears to be buggy.
|
||||
// With it, the 3.3.2 beta 1 linux-updater.bin crashes; without it seems stable.
|
||||
#include "llxmlnode.h"
|
||||
#include "lltrans.h"
|
||||
|*==========================================================================*/
|
||||
|
||||
static class LLTrans
|
||||
{
|
||||
public:
|
||||
LLTrans();
|
||||
static std::string getString(const std::string& key);
|
||||
|
||||
private:
|
||||
std::string _getString(const std::string& key) const;
|
||||
|
||||
typedef std::map<std::string, std::string> MessageMap;
|
||||
MessageMap mMessages;
|
||||
} sLLTransInstance;
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <map>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <gtk/gtk.h>
|
||||
}
|
||||
|
||||
const guint UPDATE_PROGRESS_TIMEOUT = 100;
|
||||
const guint UPDATE_PROGRESS_TEXT_TIMEOUT = 1000;
|
||||
const guint ROTATE_IMAGE_TIMEOUT = 8000;
|
||||
|
||||
typedef struct _updater_app_state {
|
||||
std::string app_name;
|
||||
std::string url;
|
||||
std::string file;
|
||||
std::string image_dir;
|
||||
std::string dest_dir;
|
||||
std::string strings_dirs;
|
||||
std::string strings_file;
|
||||
|
||||
LLDirIterator *image_dir_iter;
|
||||
|
||||
GtkWidget *window;
|
||||
GtkWidget *progress_bar;
|
||||
GtkWidget *image;
|
||||
|
||||
double progress_value;
|
||||
bool activity_mode;
|
||||
|
||||
guint image_rotation_timeout_id;
|
||||
guint progress_update_timeout_id;
|
||||
guint update_progress_text_timeout_id;
|
||||
|
||||
bool failure;
|
||||
} UpdaterAppState;
|
||||
|
||||
// List of entries from strings.xml to always replace
|
||||
static std::set<std::string> default_trans_args;
|
||||
void init_default_trans_args()
|
||||
{
|
||||
default_trans_args.insert("SECOND_LIFE"); // World
|
||||
default_trans_args.insert("APP_NAME");
|
||||
default_trans_args.insert("SECOND_LIFE_GRID");
|
||||
default_trans_args.insert("SUPPORT_SITE");
|
||||
}
|
||||
|
||||
bool translate_init(std::string comma_delim_path_list,
|
||||
std::string base_xml_name)
|
||||
{
|
||||
return true;
|
||||
/*==========================================================================*|
|
||||
init_default_trans_args();
|
||||
|
||||
// extract paths string vector from comma-delimited flat string
|
||||
std::vector<std::string> paths;
|
||||
LLStringUtil::getTokens(comma_delim_path_list, paths, ","); // split over ','
|
||||
|
||||
for(std::vector<std::string>::iterator it = paths.begin(), end_it = paths.end();
|
||||
it != end_it;
|
||||
++it)
|
||||
{
|
||||
(*it) = gDirUtilp->findSkinnedFilename(*it, base_xml_name);
|
||||
}
|
||||
|
||||
// suck the translation xml files into memory
|
||||
LLXMLNodePtr root;
|
||||
bool success = LLXMLNode::getLayeredXMLNode(root, paths);
|
||||
if (!success)
|
||||
{
|
||||
// couldn't load string table XML
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// get those strings out of the XML
|
||||
LLTrans::parseStrings(root, default_trans_args);
|
||||
return true;
|
||||
}
|
||||
|*==========================================================================*/
|
||||
}
|
||||
|
||||
|
||||
void updater_app_ui_init(void);
|
||||
void updater_app_quit(UpdaterAppState *app_state);
|
||||
void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state);
|
||||
std::string next_image_filename(std::string& image_path, LLDirIterator& iter);
|
||||
void display_error(GtkWidget *parent, std::string title, std::string message);
|
||||
BOOL install_package(std::string package_file, std::string destination);
|
||||
BOOL spawn_viewer(UpdaterAppState *app_state);
|
||||
|
||||
extern "C" {
|
||||
void on_window_closed(GtkWidget *sender, GdkEvent *event, gpointer state);
|
||||
gpointer worker_thread_cb(gpointer *data);
|
||||
int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow);
|
||||
gboolean rotate_image_cb(gpointer data);
|
||||
gboolean progress_update_timeout(gpointer data);
|
||||
gboolean update_progress_text_timeout(gpointer data);
|
||||
}
|
||||
|
||||
void updater_app_ui_init(UpdaterAppState *app_state)
|
||||
{
|
||||
GtkWidget *vbox;
|
||||
GtkWidget *summary_label;
|
||||
GtkWidget *description_label;
|
||||
GtkWidget *frame;
|
||||
|
||||
llassert(app_state != NULL);
|
||||
|
||||
// set up window and main container
|
||||
std::string window_title = LLTrans::getString("UpdaterWindowTitle");
|
||||
app_state->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title(GTK_WINDOW(app_state->window),
|
||||
window_title.c_str());
|
||||
gtk_window_set_resizable(GTK_WINDOW(app_state->window), FALSE);
|
||||
gtk_window_set_position(GTK_WINDOW(app_state->window),
|
||||
GTK_WIN_POS_CENTER_ALWAYS);
|
||||
|
||||
gtk_container_set_border_width(GTK_CONTAINER(app_state->window), 12);
|
||||
g_signal_connect(G_OBJECT(app_state->window), "delete-event",
|
||||
G_CALLBACK(on_window_closed), app_state);
|
||||
|
||||
vbox = gtk_vbox_new(FALSE, 6);
|
||||
gtk_container_add(GTK_CONTAINER(app_state->window), vbox);
|
||||
|
||||
// set top label
|
||||
std::ostringstream label_ostr;
|
||||
label_ostr << "<big><b>"
|
||||
<< LLTrans::getString("UpdaterNowUpdating")
|
||||
<< "</b></big>";
|
||||
|
||||
summary_label = gtk_label_new(NULL);
|
||||
gtk_label_set_use_markup(GTK_LABEL(summary_label), TRUE);
|
||||
gtk_label_set_markup(GTK_LABEL(summary_label),
|
||||
label_ostr.str().c_str());
|
||||
gtk_misc_set_alignment(GTK_MISC(summary_label), 0, 0.5);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), summary_label, FALSE, FALSE, 0);
|
||||
|
||||
// create the description label
|
||||
description_label = gtk_label_new(LLTrans::getString("UpdaterUpdatingDescriptive").c_str());
|
||||
gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
|
||||
gtk_misc_set_alignment(GTK_MISC(description_label), 0, 0.5);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), description_label, FALSE, FALSE, 0);
|
||||
|
||||
// If an image path has been set, load the background images
|
||||
if (!app_state->image_dir.empty()) {
|
||||
frame = gtk_frame_new(NULL);
|
||||
gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
|
||||
|
||||
// load the first image
|
||||
app_state->image = gtk_image_new_from_file
|
||||
(next_image_filename(app_state->image_dir, *app_state->image_dir_iter).c_str());
|
||||
gtk_widget_set_size_request(app_state->image, 340, 310);
|
||||
gtk_container_add(GTK_CONTAINER(frame), app_state->image);
|
||||
|
||||
// rotate the images every 5 seconds
|
||||
app_state->image_rotation_timeout_id = g_timeout_add
|
||||
(ROTATE_IMAGE_TIMEOUT, rotate_image_cb, app_state);
|
||||
}
|
||||
|
||||
// set up progress bar, and update it roughly every 1/10 of a second
|
||||
app_state->progress_bar = gtk_progress_bar_new();
|
||||
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
|
||||
LLTrans::getString("UpdaterProgressBarTextWithEllipses").c_str());
|
||||
gtk_box_pack_start(GTK_BOX(vbox),
|
||||
app_state->progress_bar, FALSE, TRUE, 0);
|
||||
app_state->progress_update_timeout_id = g_timeout_add
|
||||
(UPDATE_PROGRESS_TIMEOUT, progress_update_timeout, app_state);
|
||||
app_state->update_progress_text_timeout_id = g_timeout_add
|
||||
(UPDATE_PROGRESS_TEXT_TIMEOUT, update_progress_text_timeout, app_state);
|
||||
|
||||
gtk_widget_show_all(app_state->window);
|
||||
}
|
||||
|
||||
gboolean rotate_image_cb(gpointer data)
|
||||
{
|
||||
UpdaterAppState *app_state;
|
||||
std::string filename;
|
||||
|
||||
llassert(data != NULL);
|
||||
app_state = (UpdaterAppState *) data;
|
||||
|
||||
filename = next_image_filename(app_state->image_dir, *app_state->image_dir_iter);
|
||||
|
||||
gdk_threads_enter();
|
||||
gtk_image_set_from_file(GTK_IMAGE(app_state->image), filename.c_str());
|
||||
gdk_threads_leave();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
std::string next_image_filename(std::string& image_path, LLDirIterator& iter)
|
||||
{
|
||||
std::string image_filename;
|
||||
iter.next(image_filename);
|
||||
return gDirUtilp->add(image_path, image_filename);
|
||||
}
|
||||
|
||||
void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data)
|
||||
{
|
||||
UpdaterAppState *app_state;
|
||||
|
||||
llassert(data != NULL);
|
||||
app_state = (UpdaterAppState *) data;
|
||||
|
||||
updater_app_quit(app_state);
|
||||
}
|
||||
|
||||
void updater_app_quit(UpdaterAppState *app_state)
|
||||
{
|
||||
if (app_state != NULL)
|
||||
{
|
||||
g_source_remove(app_state->progress_update_timeout_id);
|
||||
|
||||
if (!app_state->image_dir.empty())
|
||||
{
|
||||
g_source_remove(app_state->image_rotation_timeout_id);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
void display_error(GtkWidget *parent, std::string title, std::string message)
|
||||
{
|
||||
GtkWidget *dialog;
|
||||
|
||||
dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
|
||||
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
GTK_MESSAGE_ERROR,
|
||||
GTK_BUTTONS_OK,
|
||||
"%s", message.c_str());
|
||||
gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
|
||||
gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
gtk_widget_destroy(dialog);
|
||||
}
|
||||
|
||||
gpointer worker_thread_cb(gpointer data)
|
||||
{
|
||||
UpdaterAppState *app_state;
|
||||
CURL *curl;
|
||||
CURLcode result;
|
||||
FILE *package_file;
|
||||
GError *error = NULL;
|
||||
int fd;
|
||||
|
||||
//g_return_val_if_fail (data != NULL, NULL);
|
||||
app_state = (UpdaterAppState *) data;
|
||||
|
||||
try {
|
||||
|
||||
if(!app_state->url.empty())
|
||||
{
|
||||
char* tmp_local_filename = NULL;
|
||||
// create temporary file to store the package.
|
||||
fd = g_file_open_tmp
|
||||
("secondlife-update-XXXXXX", &tmp_local_filename, &error);
|
||||
if (error != NULL)
|
||||
{
|
||||
llerrs << "Unable to create temporary file: "
|
||||
<< error->message
|
||||
<< llendl;
|
||||
|
||||
g_error_free(error);
|
||||
throw 0;
|
||||
}
|
||||
|
||||
if(tmp_local_filename != NULL)
|
||||
{
|
||||
app_state->file = tmp_local_filename;
|
||||
g_free(tmp_local_filename);
|
||||
}
|
||||
|
||||
package_file = fdopen(fd, "wb");
|
||||
if (package_file == NULL)
|
||||
{
|
||||
llerrs << "Failed to create temporary file: "
|
||||
<< app_state->file.c_str()
|
||||
<< llendl;
|
||||
|
||||
gdk_threads_enter();
|
||||
display_error(app_state->window,
|
||||
LLTrans::getString("UpdaterFailDownloadTitle"),
|
||||
LLTrans::getString("UpdaterFailUpdateDescriptive"));
|
||||
gdk_threads_leave();
|
||||
throw 0;
|
||||
}
|
||||
|
||||
// initialize curl and start downloading the package
|
||||
llinfos << "Downloading package: " << app_state->url << llendl;
|
||||
|
||||
curl = curl_easy_init();
|
||||
if (curl == NULL)
|
||||
{
|
||||
llerrs << "Failed to initialize libcurl" << llendl;
|
||||
|
||||
gdk_threads_enter();
|
||||
display_error(app_state->window,
|
||||
LLTrans::getString("UpdaterFailDownloadTitle"),
|
||||
LLTrans::getString("UpdaterFailUpdateDescriptive"));
|
||||
gdk_threads_leave();
|
||||
throw 0;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
|
||||
&download_progress_cb);
|
||||
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
|
||||
|
||||
result = curl_easy_perform(curl);
|
||||
fclose(package_file);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if (result)
|
||||
{
|
||||
llerrs << "Failed to download update: "
|
||||
<< app_state->url
|
||||
<< llendl;
|
||||
|
||||
gdk_threads_enter();
|
||||
display_error(app_state->window,
|
||||
LLTrans::getString("UpdaterFailDownloadTitle"),
|
||||
LLTrans::getString("UpdaterFailUpdateDescriptive"));
|
||||
gdk_threads_leave();
|
||||
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
|
||||
// now pulse the progres bar back and forth while the package is
|
||||
// being unpacked
|
||||
gdk_threads_enter();
|
||||
std::string installing_msg = LLTrans::getString("UpdaterNowInstalling");
|
||||
gtk_progress_bar_set_text(
|
||||
GTK_PROGRESS_BAR(app_state->progress_bar),
|
||||
installing_msg.c_str());
|
||||
app_state->activity_mode = TRUE;
|
||||
gdk_threads_leave();
|
||||
|
||||
// *TODO: if the destination is not writable, terminate this
|
||||
// thread and show file chooser?
|
||||
if (!install_package(app_state->file.c_str(), app_state->dest_dir))
|
||||
{
|
||||
llwarns << "Failed to install package to destination: "
|
||||
<< app_state->dest_dir
|
||||
<< llendl;
|
||||
|
||||
gdk_threads_enter();
|
||||
display_error(app_state->window,
|
||||
LLTrans::getString("UpdaterFailInstallTitle"),
|
||||
LLTrans::getString("UpdaterFailUpdateDescriptive"));
|
||||
//"Failed to update " + app_state->app_name,
|
||||
gdk_threads_leave();
|
||||
throw 0;
|
||||
}
|
||||
|
||||
// try to spawn the new viewer
|
||||
if (!spawn_viewer(app_state))
|
||||
{
|
||||
llwarns << "Viewer was not installed properly in : "
|
||||
<< app_state->dest_dir
|
||||
<< llendl;
|
||||
|
||||
gdk_threads_enter();
|
||||
display_error(app_state->window,
|
||||
LLTrans::getString("UpdaterFailStartTitle"),
|
||||
LLTrans::getString("UpdaterFailUpdateDescriptive"));
|
||||
gdk_threads_leave();
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
app_state->failure = TRUE;
|
||||
}
|
||||
|
||||
gdk_threads_enter();
|
||||
updater_app_quit(app_state);
|
||||
gdk_threads_leave();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
gboolean less_anal_gspawnsync(gchar **argv,
|
||||
gchar **stderr_output,
|
||||
gint *child_exit_status,
|
||||
GError **spawn_error)
|
||||
{
|
||||
// store current SIGCHLD handler if there is one, replace with default
|
||||
// handler to make glib happy
|
||||
struct sigaction sigchld_backup;
|
||||
struct sigaction sigchld_appease_glib;
|
||||
sigchld_appease_glib.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sigchld_appease_glib.sa_mask);
|
||||
sigchld_appease_glib.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
|
||||
|
||||
gboolean rtn = g_spawn_sync(NULL,
|
||||
argv,
|
||||
NULL,
|
||||
(GSpawnFlags) (G_SPAWN_STDOUT_TO_DEV_NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
stderr_output,
|
||||
child_exit_status,
|
||||
spawn_error);
|
||||
|
||||
// restore SIGCHLD handler
|
||||
sigaction(SIGCHLD, &sigchld_backup, NULL);
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
|
||||
// perform a rename, or perform a (prompted) root rename if that fails
|
||||
int
|
||||
rename_with_sudo_fallback(const std::string& filename, const std::string& newname)
|
||||
{
|
||||
int rtncode = ::rename(filename.c_str(), newname.c_str());
|
||||
lldebugs << "rename result is: " << rtncode << " / " << errno << llendl;
|
||||
if (rtncode && (EACCES == errno || EPERM == errno || EXDEV == errno))
|
||||
{
|
||||
llinfos << "Permission problem in rename, or moving between different mount points. Retrying as a mv under a sudo." << llendl;
|
||||
// failed due to permissions, try again as a gksudo or kdesu mv wrapper hack
|
||||
char *sudo_cmd = NULL;
|
||||
sudo_cmd = g_find_program_in_path("gksudo");
|
||||
if (!sudo_cmd)
|
||||
{
|
||||
sudo_cmd = g_find_program_in_path("kdesu");
|
||||
}
|
||||
if (sudo_cmd)
|
||||
{
|
||||
char *mv_cmd = NULL;
|
||||
mv_cmd = g_find_program_in_path("mv");
|
||||
if (mv_cmd)
|
||||
{
|
||||
char *src_string_copy = g_strdup(filename.c_str());
|
||||
char *dst_string_copy = g_strdup(newname.c_str());
|
||||
char* argv[] =
|
||||
{
|
||||
sudo_cmd,
|
||||
mv_cmd,
|
||||
src_string_copy,
|
||||
dst_string_copy,
|
||||
NULL
|
||||
};
|
||||
|
||||
gchar *stderr_output = NULL;
|
||||
gint child_exit_status = 0;
|
||||
GError *spawn_error = NULL;
|
||||
if (!less_anal_gspawnsync(argv, &stderr_output,
|
||||
&child_exit_status, &spawn_error))
|
||||
{
|
||||
llwarns << "Failed to spawn child process: "
|
||||
<< spawn_error->message
|
||||
<< llendl;
|
||||
}
|
||||
else if (child_exit_status)
|
||||
{
|
||||
llwarns << "mv command failed: "
|
||||
<< (stderr_output ? stderr_output : "(no reason given)")
|
||||
<< llendl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// everything looks good, clear the error code
|
||||
rtncode = 0;
|
||||
}
|
||||
|
||||
g_free(src_string_copy);
|
||||
g_free(dst_string_copy);
|
||||
if (spawn_error) g_error_free(spawn_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rtncode;
|
||||
}
|
||||
|
||||
gboolean install_package(std::string package_file, std::string destination)
|
||||
{
|
||||
char *tar_cmd = NULL;
|
||||
std::ostringstream command;
|
||||
|
||||
// Find the absolute path to the 'tar' command.
|
||||
tar_cmd = g_find_program_in_path("tar");
|
||||
if (!tar_cmd)
|
||||
{
|
||||
llerrs << "`tar' was not found in $PATH" << llendl;
|
||||
return FALSE;
|
||||
}
|
||||
llinfos << "Found tar command: " << tar_cmd << llendl;
|
||||
|
||||
// Unpack the tarball in a temporary place first, then move it to
|
||||
// its final destination
|
||||
std::string tmp_dest_dir = gDirUtilp->getTempFilename();
|
||||
if (LLFile::mkdir(tmp_dest_dir, 0744))
|
||||
{
|
||||
llerrs << "Failed to create directory: "
|
||||
<< destination
|
||||
<< llendl;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char *package_file_string_copy = g_strdup(package_file.c_str());
|
||||
char *tmp_dest_dir_string_copy = g_strdup(tmp_dest_dir.c_str());
|
||||
gchar *argv[8] = {
|
||||
tar_cmd,
|
||||
const_cast<gchar*>("--strip"), const_cast<gchar*>("1"),
|
||||
const_cast<gchar*>("-xjf"),
|
||||
package_file_string_copy,
|
||||
const_cast<gchar*>("-C"), tmp_dest_dir_string_copy,
|
||||
NULL,
|
||||
};
|
||||
|
||||
llinfos << "Untarring package: " << package_file << llendl;
|
||||
|
||||
// store current SIGCHLD handler if there is one, replace with default
|
||||
// handler to make glib happy
|
||||
struct sigaction sigchld_backup;
|
||||
struct sigaction sigchld_appease_glib;
|
||||
sigchld_appease_glib.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sigchld_appease_glib.sa_mask);
|
||||
sigchld_appease_glib.sa_flags = 0;
|
||||
sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
|
||||
|
||||
gchar *stderr_output = NULL;
|
||||
gint child_exit_status = 0;
|
||||
GError *untar_error = NULL;
|
||||
if (!less_anal_gspawnsync(argv, &stderr_output,
|
||||
&child_exit_status, &untar_error))
|
||||
{
|
||||
llwarns << "Failed to spawn child process: "
|
||||
<< untar_error->message
|
||||
<< llendl;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (child_exit_status)
|
||||
{
|
||||
llwarns << "Untar command failed: "
|
||||
<< (stderr_output ? stderr_output : "(no reason given)")
|
||||
<< llendl;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_free(tar_cmd);
|
||||
g_free(package_file_string_copy);
|
||||
g_free(tmp_dest_dir_string_copy);
|
||||
g_free(stderr_output);
|
||||
if (untar_error) g_error_free(untar_error);
|
||||
|
||||
// move the existing package out of the way if it exists
|
||||
if (gDirUtilp->fileExists(destination))
|
||||
{
|
||||
std::string backup_dir = destination + ".backup";
|
||||
int oldcounter = 1;
|
||||
while (gDirUtilp->fileExists(backup_dir))
|
||||
{
|
||||
// find a foo.backup.N folder name that isn't taken yet
|
||||
backup_dir = destination + ".backup." + llformat("%d", oldcounter);
|
||||
++oldcounter;
|
||||
}
|
||||
|
||||
if (rename_with_sudo_fallback(destination, backup_dir))
|
||||
{
|
||||
llwarns << "Failed to move directory: '"
|
||||
<< destination << "' -> '" << backup_dir
|
||||
<< llendl;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// The package has been unpacked in a staging directory, now we just
|
||||
// need to move it to its destination.
|
||||
if (rename_with_sudo_fallback(tmp_dest_dir, destination))
|
||||
{
|
||||
llwarns << "Failed to move installation to the destination: "
|
||||
<< destination
|
||||
<< llendl;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// \0/ Success!
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean progress_update_timeout(gpointer data)
|
||||
{
|
||||
UpdaterAppState *app_state;
|
||||
|
||||
llassert(data != NULL);
|
||||
|
||||
app_state = (UpdaterAppState *) data;
|
||||
|
||||
gdk_threads_enter();
|
||||
if (app_state->activity_mode)
|
||||
{
|
||||
gtk_progress_bar_pulse
|
||||
(GTK_PROGRESS_BAR(app_state->progress_bar));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_progress_set_value(GTK_PROGRESS(app_state->progress_bar),
|
||||
app_state->progress_value);
|
||||
}
|
||||
gdk_threads_leave();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean update_progress_text_timeout(gpointer data)
|
||||
{
|
||||
UpdaterAppState *app_state;
|
||||
|
||||
llassert(data != NULL);
|
||||
app_state = (UpdaterAppState *) data;
|
||||
|
||||
if (app_state->activity_mode == TRUE)
|
||||
{
|
||||
// We no longer need this timeout, it will be removed.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!app_state->progress_value)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
std::string progress_text = llformat((LLTrans::getString("UpdaterProgressBarText")+" (%.0f%%)").c_str(), app_state->progress_value);
|
||||
|
||||
gdk_threads_enter();
|
||||
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
|
||||
progress_text.c_str());
|
||||
gdk_threads_leave();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int download_progress_cb(gpointer data,
|
||||
double t,
|
||||
double d,
|
||||
double utotal,
|
||||
double ulnow)
|
||||
{
|
||||
UpdaterAppState *app_state;
|
||||
|
||||
llassert(data != NULL);
|
||||
app_state = (UpdaterAppState *) data;
|
||||
|
||||
if (t <= 0.0)
|
||||
{
|
||||
app_state->progress_value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
app_state->progress_value = d * 100.0 / t;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL spawn_viewer(UpdaterAppState *app_state)
|
||||
{
|
||||
llassert(app_state != NULL);
|
||||
|
||||
std::string cmd = app_state->dest_dir + "/secondlife";
|
||||
GError *error = NULL;
|
||||
|
||||
// We want to spawn the Viewer on the same display as the updater app
|
||||
gboolean success = gdk_spawn_command_line_on_screen
|
||||
(gtk_widget_get_screen(app_state->window), cmd.c_str(), &error);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
llwarns << "Failed to launch viewer: " << error->message
|
||||
<< llendl;
|
||||
}
|
||||
|
||||
if (error) g_error_free(error);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void show_usage_and_exit()
|
||||
{
|
||||
std::cout << "Usage: linux-updater <--url URL | --file FILE> --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"
|
||||
<< "[--image-dir PATH]"
|
||||
<< std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if ((!strcmp(argv[i], "--url")) && (++i < argc))
|
||||
{
|
||||
app_state->url = argv[i];
|
||||
}
|
||||
else if ((!strcmp(argv[i], "--file")) && (++i < argc))
|
||||
{
|
||||
app_state->file = argv[i];
|
||||
}
|
||||
else if ((!strcmp(argv[i], "--name")) && (++i < argc))
|
||||
{
|
||||
app_state->app_name = argv[i];
|
||||
}
|
||||
else if ((!strcmp(argv[i], "--image-dir")) && (++i < argc))
|
||||
{
|
||||
app_state->image_dir = argv[i];
|
||||
app_state->image_dir_iter = new LLDirIterator(argv[i], "*.jpg");
|
||||
}
|
||||
else if ((!strcmp(argv[i], "--dest")) && (++i < argc))
|
||||
{
|
||||
app_state->dest_dir = argv[i];
|
||||
}
|
||||
else if ((!strcmp(argv[i], "--stringsdir")) && (++i < argc))
|
||||
{
|
||||
app_state->strings_dirs = argv[i];
|
||||
}
|
||||
else if ((!strcmp(argv[i], "--stringsfile")) && (++i < argc))
|
||||
{
|
||||
app_state->strings_file = argv[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// show usage, an invalid option was given.
|
||||
show_usage_and_exit();
|
||||
}
|
||||
}
|
||||
|
||||
if (app_state->app_name.empty()
|
||||
|| (app_state->url.empty() && app_state->file.empty())
|
||||
|| app_state->dest_dir.empty())
|
||||
{
|
||||
show_usage_and_exit();
|
||||
}
|
||||
|
||||
app_state->progress_value = 0.0;
|
||||
app_state->activity_mode = FALSE;
|
||||
app_state->failure = FALSE;
|
||||
|
||||
translate_init(app_state->strings_dirs, app_state->strings_file);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
UpdaterAppState* app_state = new UpdaterAppState;
|
||||
GThread *worker_thread;
|
||||
|
||||
parse_args_and_init(argc, argv, app_state);
|
||||
|
||||
// Initialize logger, and rename old log file
|
||||
gDirUtilp->initAppDirs("SecondLife");
|
||||
LLError::initForApplication
|
||||
(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
|
||||
std::string old_log_file = gDirUtilp->getExpandedFilename
|
||||
(LL_PATH_LOGS, "updater.log.old");
|
||||
std::string log_file =
|
||||
gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");
|
||||
LLFile::rename(log_file, old_log_file);
|
||||
LLError::logToFile(log_file);
|
||||
|
||||
// initialize gthreads and gtk+
|
||||
if (!g_thread_supported())
|
||||
{
|
||||
g_thread_init(NULL);
|
||||
gdk_threads_init();
|
||||
}
|
||||
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
// create UI
|
||||
updater_app_ui_init(app_state);
|
||||
|
||||
//llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl;
|
||||
|
||||
// create download thread
|
||||
worker_thread = g_thread_create
|
||||
(GThreadFunc(worker_thread_cb), app_state, FALSE, NULL);
|
||||
|
||||
gdk_threads_enter();
|
||||
gtk_main();
|
||||
gdk_threads_leave();
|
||||
|
||||
// Delete the file only if created from url download.
|
||||
if(!app_state->url.empty() && !app_state->file.empty())
|
||||
{
|
||||
if (gDirUtilp->fileExists(app_state->file))
|
||||
{
|
||||
LLFile::remove(app_state->file);
|
||||
}
|
||||
}
|
||||
|
||||
bool success = !app_state->failure;
|
||||
delete app_state->image_dir_iter;
|
||||
delete app_state;
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Dummy LLTrans implementation (IQA-490)
|
||||
*****************************************************************************/
|
||||
static LLTrans sStaticStrings;
|
||||
|
||||
// lookup
|
||||
std::string LLTrans::_getString(const std::string& key) const
|
||||
{
|
||||
MessageMap::const_iterator found = mMessages.find(key);
|
||||
if (found != mMessages.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
LL_WARNS("linux_updater") << "No message for key '" << key
|
||||
<< "' -- add to LLTrans::LLTrans() in linux_updater.cpp"
|
||||
<< LL_ENDL;
|
||||
return key;
|
||||
}
|
||||
|
||||
// static lookup
|
||||
std::string LLTrans::getString(const std::string& key)
|
||||
{
|
||||
return sLLTransInstance._getString(key);
|
||||
}
|
||||
|
||||
// initialization
|
||||
LLTrans::LLTrans()
|
||||
{
|
||||
typedef std::pair<const char*, const char*> Pair;
|
||||
static const Pair data[] =
|
||||
{
|
||||
Pair("UpdaterFailDownloadTitle",
|
||||
"Failed to download update"),
|
||||
Pair("UpdaterFailInstallTitle",
|
||||
"Failed to install update"),
|
||||
Pair("UpdaterFailStartTitle",
|
||||
"Failed to start viewer"),
|
||||
Pair("UpdaterFailUpdateDescriptive",
|
||||
"An error occurred while updating Second Life. "
|
||||
"Please download the latest version from www.secondlife.com."),
|
||||
Pair("UpdaterNowInstalling",
|
||||
"Installing Second Life..."),
|
||||
Pair("UpdaterNowUpdating",
|
||||
"Now updating Second Life..."),
|
||||
Pair("UpdaterProgressBarText",
|
||||
"Downloading update"),
|
||||
Pair("UpdaterProgressBarTextWithEllipses",
|
||||
"Downloading update..."),
|
||||
Pair("UpdaterUpdatingDescriptive",
|
||||
"Your Second Life Viewer is being updated to the latest release. "
|
||||
"This may take some time, so please be patient."),
|
||||
Pair("UpdaterWindowTitle",
|
||||
"Second Life Update")
|
||||
};
|
||||
|
||||
BOOST_FOREACH(Pair pair, data)
|
||||
{
|
||||
mMessages[pair.first] = pair.second;
|
||||
}
|
||||
}
|
||||
|
|
@ -580,8 +580,13 @@ size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void
|
|||
const size_t body_size(op->mReqBody->size());
|
||||
if (body_size <= op->mCurlBodyPos)
|
||||
{
|
||||
LL_WARNS("HttpCore") << "Request body position beyond body size. Aborting request."
|
||||
<< LL_ENDL;
|
||||
if (body_size < op->mCurlBodyPos)
|
||||
{
|
||||
// Warn but continue if the read position moves beyond end-of-body
|
||||
// for some reason.
|
||||
LL_WARNS("HttpCore") << "Request body position beyond body size. Truncating request body."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,18 +48,37 @@ private:
|
|||
set_volume_func_t mSetVolumeFunc;
|
||||
set_mute_func_t mSetMuteFunc;
|
||||
|
||||
// tests if running on Vista, 7, 8 + once in CTOR
|
||||
bool isWindowsVistaOrHigher();
|
||||
|
||||
F32 mVolume;
|
||||
F32 mPan;
|
||||
bool mSystemIsVistaOrHigher;
|
||||
};
|
||||
VolumeCatcherImpl::VolumeCatcherImpl()
|
||||
: mVolume(1.0f), // default volume is max
|
||||
mPan(0.f) // default pan is centered
|
||||
|
||||
bool VolumeCatcherImpl::isWindowsVistaOrHigher()
|
||||
{
|
||||
HMODULE handle = ::LoadLibrary(L"winmm.dll");
|
||||
if(handle)
|
||||
OSVERSIONINFO osvi;
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
GetVersionEx(&osvi);
|
||||
return osvi.dwMajorVersion >= 6;
|
||||
}
|
||||
|
||||
VolumeCatcherImpl::VolumeCatcherImpl()
|
||||
: mVolume(1.0f), // default volume is max
|
||||
mPan(0.f) // default pan is centered
|
||||
{
|
||||
mSystemIsVistaOrHigher = isWindowsVistaOrHigher();
|
||||
|
||||
if ( ! mSystemIsVistaOrHigher )
|
||||
{
|
||||
mSetVolumeFunc = (set_volume_func_t)::GetProcAddress(handle, "setPluginVolume");
|
||||
mSetMuteFunc = (set_mute_func_t)::GetProcAddress(handle, "setPluginMute");
|
||||
HMODULE handle = ::LoadLibrary(L"winmm.dll");
|
||||
if(handle)
|
||||
{
|
||||
mSetVolumeFunc = (set_volume_func_t)::GetProcAddress(handle, "setPluginVolume");
|
||||
mSetMuteFunc = (set_mute_func_t)::GetProcAddress(handle, "setPluginMute");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,18 +86,29 @@ VolumeCatcherImpl::~VolumeCatcherImpl()
|
|||
{
|
||||
}
|
||||
|
||||
|
||||
void VolumeCatcherImpl::setVolume(F32 volume)
|
||||
{
|
||||
mVolume = volume;
|
||||
|
||||
if (mSetMuteFunc)
|
||||
if ( mSystemIsVistaOrHigher )
|
||||
{
|
||||
mSetMuteFunc(volume == 0.f);
|
||||
// set both left/right to same volume
|
||||
// TODO: use pan value to set independently
|
||||
DWORD left_channel = (DWORD)(mVolume * 65535.0f);
|
||||
DWORD right_channel = (DWORD)(mVolume * 65535.0f);
|
||||
DWORD hw_volume = left_channel << 16 | right_channel;
|
||||
::waveOutSetVolume(NULL, hw_volume);
|
||||
}
|
||||
if (mSetVolumeFunc)
|
||||
else
|
||||
{
|
||||
mSetVolumeFunc(mVolume);
|
||||
if (mSetMuteFunc)
|
||||
{
|
||||
mSetMuteFunc(volume == 0.f);
|
||||
}
|
||||
if (mSetVolumeFunc)
|
||||
{
|
||||
mSetVolumeFunc(mVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1858,7 +1858,6 @@ if (LINUX)
|
|||
set(COPY_INPUT_DEPENDENCIES
|
||||
${VIEWER_BINARY_NAME}
|
||||
linux-crash-logger
|
||||
linux-updater
|
||||
SLPlugin
|
||||
media_plugin_webkit
|
||||
media_plugin_gstreamer010
|
||||
|
|
|
|||
|
|
@ -23,9 +23,6 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
float calcDirectionalLight(vec3 n, vec3 l);
|
||||
float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight);
|
||||
|
||||
uniform mat3 normal_matrix;
|
||||
uniform mat4 texture_matrix0;
|
||||
uniform mat4 modelview_matrix;
|
||||
|
|
@ -45,6 +42,42 @@ uniform vec3 light_direction[8];
|
|||
uniform vec3 light_attenuation[8];
|
||||
uniform vec3 light_diffuse[8];
|
||||
|
||||
//===================================================================================================
|
||||
//declare these here explicitly to separate them from atmospheric lighting elsewhere to work around
|
||||
//drivers that are picky about functions being declared but not defined even if they aren't called
|
||||
float calcDirectionalLight(vec3 n, vec3 l)
|
||||
{
|
||||
float a = max(dot(n,l),0.0);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight)
|
||||
{
|
||||
//get light vector
|
||||
vec3 lv = lp.xyz-v;
|
||||
|
||||
//get distance
|
||||
float d = length(lv);
|
||||
|
||||
//normalize light vector
|
||||
lv *= 1.0/d;
|
||||
|
||||
//distance attenuation
|
||||
float da = clamp(1.0/(la * d), 0.0, 1.0);
|
||||
|
||||
// spotlight coefficient.
|
||||
float spot = max(dot(-ln, lv), is_pointlight);
|
||||
da *= spot*spot; // GL_SPOT_EXPONENT=2
|
||||
|
||||
//angular attenuation
|
||||
da *= calcDirectionalLight(n, lv);
|
||||
|
||||
return da;
|
||||
}
|
||||
//====================================================================================================
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
//transform vertex
|
||||
|
|
|
|||
|
|
@ -524,6 +524,7 @@ F32 LLDrawable::updateXform(BOOL undamped)
|
|||
dist_squared = dist_vec_squared(new_pos, target_pos);
|
||||
|
||||
LLQuaternion new_rot = nlerp(lerp_amt, old_rot, target_rot);
|
||||
// FIXME: This can be negative! It is be possible for some rots to 'cancel out' pos or size changes.
|
||||
dist_squared += (1.f - dot(new_rot, target_rot)) * 10.f;
|
||||
|
||||
LLVector3 new_scale = lerp(old_scale, target_scale, lerp_amt);
|
||||
|
|
@ -549,7 +550,12 @@ F32 LLDrawable::updateXform(BOOL undamped)
|
|||
}
|
||||
else
|
||||
{
|
||||
dist_squared = dist_vec_squared(old_pos, target_pos);
|
||||
// The following fixes MAINT-1742 but breaks vehicles similar to MAINT-2275
|
||||
// dist_squared = dist_vec_squared(old_pos, target_pos);
|
||||
|
||||
// The following fixes MAINT-2247 but causes MAINT-2275
|
||||
//dist_squared += (1.f - dot(old_rot, target_rot)) * 10.f;
|
||||
//dist_squared += dist_vec_squared(old_scale, target_scale);
|
||||
}
|
||||
|
||||
LLVector3 vec = mCurrentScale-target_scale;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* $LicenseInfo:firstyear=2007&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
* Copyright (C) 2012, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
|
|
|
|||
|
|
@ -186,8 +186,11 @@ private:
|
|||
// Populate the menu with items like "New Skin", "New Pants", etc.
|
||||
static void populateCreateWearableSubmenus(LLMenuGL* menu)
|
||||
{
|
||||
LLView* menu_clothes = gMenuHolder->getChildView("COF.Gear.New_Clothes", FALSE);
|
||||
LLView* menu_bp = gMenuHolder->getChildView("COF.Geear.New_Body_Parts", FALSE);
|
||||
// MAINT-2276...these menus are created as dummies because they are not available
|
||||
// when this function is called. This prevents their parent from popping up later.
|
||||
//
|
||||
//LLView* menu_clothes = gMenuHolder->getChildView("COF.Gear.New_Clothes", FALSE);
|
||||
//LLView* menu_bp = gMenuHolder->getChildView("COF.Geear.New_Body_Parts", FALSE);
|
||||
|
||||
for (U8 i = LLWearableType::WT_SHAPE; i != (U8) LLWearableType::WT_COUNT; ++i)
|
||||
{
|
||||
|
|
@ -200,8 +203,11 @@ private:
|
|||
p.on_click.function_name = "Wearable.Create";
|
||||
p.on_click.parameter = LLSD(type_name);
|
||||
|
||||
LLView* parent = LLWearableType::getAssetType(type) == LLAssetType::AT_CLOTHING ?
|
||||
menu_clothes : menu_bp;
|
||||
//LLView* parent = LLWearableType::getAssetType(type) == LLAssetType::AT_CLOTHING ? menu_clothes : menu_bp;
|
||||
// This is a work-around for MAINT-2276 wherein the parent toggleable menu does not appear
|
||||
// It puts everything under one menu, but that menu appears, which is better than not.
|
||||
//
|
||||
LLView* parent = menu;
|
||||
LLUICtrlFactory::create<LLMenuItemCallGL>(p, parent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3689,13 +3689,14 @@ public:
|
|||
|
||||
if (status)
|
||||
{
|
||||
LL_WARNS("Texture") << "Successfully delivered asset metrics to grid."
|
||||
<< LL_ENDL;
|
||||
LL_DEBUGS("Texture") << "Successfully delivered asset metrics to grid."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Texture") << "Error delivering asset metrics to grid. Reason: "
|
||||
<< status.toString() << LL_ENDL;
|
||||
LL_WARNS("Texture") << "Error delivering asset metrics to grid. Status: "
|
||||
<< status.toHex()
|
||||
<< ", Reason: " << status.toString() << LL_ENDL;
|
||||
}
|
||||
}
|
||||
}; // end class AssetReportHandler
|
||||
|
|
@ -3895,11 +3896,15 @@ private:
|
|||
|
||||
|
||||
LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) :
|
||||
LLCore::HttpHandler(),
|
||||
mFetcher(fetcher),
|
||||
mTextureCache(cache),
|
||||
mImageDecodeThread(imagedecodethread),
|
||||
mHttpHeaders(NULL),
|
||||
mHttpPolicyClass(fetcher->getPolicyClass())
|
||||
mHttpPolicyClass(fetcher->getPolicyClass()),
|
||||
mNbCurlCompleted(0),
|
||||
mTempIndex(0),
|
||||
mHistoryListIndex(0)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
|
@ -3925,6 +3930,7 @@ void LLTextureFetchDebugger::init()
|
|||
mDecodingTime = -1.f;
|
||||
mHTTPTime = -1.f;
|
||||
mGLCreationTime = -1.f;
|
||||
|
||||
mTotalFetchingTime = 0.f;
|
||||
mRefetchVisCacheTime = -1.f;
|
||||
mRefetchVisHTTPTime = -1.f;
|
||||
|
|
@ -3951,6 +3957,9 @@ void LLTextureFetchDebugger::init()
|
|||
mFreezeHistory = FALSE;
|
||||
mStopDebug = FALSE;
|
||||
mClearHistory = FALSE;
|
||||
mRefetchNonVis = FALSE;
|
||||
|
||||
mNbCurlRequests = 0;
|
||||
|
||||
if (! mHttpHeaders)
|
||||
{
|
||||
|
|
@ -4024,7 +4033,8 @@ bool LLTextureFetchDebugger::processStartDebug(F32 max_time)
|
|||
S32 pending = 0;
|
||||
pending += LLAppViewer::getTextureCache()->update(1);
|
||||
pending += LLAppViewer::getImageDecodeThread()->update(1);
|
||||
pending += LLAppViewer::getTextureFetch()->update(1);
|
||||
// pending += LLAppViewer::getTextureFetch()->update(1); // This causes infinite recursion in some cases
|
||||
pending += mNbCurlRequests;
|
||||
if(!pending)
|
||||
{
|
||||
break;
|
||||
|
|
@ -4314,7 +4324,6 @@ void LLTextureFetchDebugger::debugHTTP()
|
|||
{
|
||||
mFetchingHistory[i].mCurlState = FetchEntry::CURL_NOT_DONE;
|
||||
mFetchingHistory[i].mCurlReceivedSize = 0;
|
||||
mFetchingHistory[i].mHTTPFailCount = 0;
|
||||
mFetchingHistory[i].mFormattedImage = NULL;
|
||||
}
|
||||
mNbCurlRequests = 0;
|
||||
|
|
@ -4338,8 +4347,6 @@ S32 LLTextureFetchDebugger::fillCurlQueue()
|
|||
S32 size = mFetchingHistory.size();
|
||||
for (S32 i = 0 ; i < size ; i++)
|
||||
{
|
||||
mNbCurlRequests++;
|
||||
|
||||
if (mFetchingHistory[i].mCurlState != FetchEntry::CURL_NOT_DONE)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -4365,15 +4372,22 @@ S32 LLTextureFetchDebugger::fillCurlQueue()
|
|||
mFetchingHistory[i].mHttpHandle = handle;
|
||||
mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS;
|
||||
mNbCurlRequests++;
|
||||
// Hack
|
||||
if (mNbCurlRequests == HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline
|
||||
if (mNbCurlRequests >= HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
// Failed to queue request, log it and mark it done.
|
||||
LLCore::HttpStatus status(mFetcher->getHttpRequest().getStatus());
|
||||
|
||||
LL_WARNS("Texture") << "Couldn't issue HTTP request in debugger for texture "
|
||||
<< mFetchingHistory[i].mID
|
||||
<< ", status: " << status.toHex()
|
||||
<< " reason: " << status.toString()
|
||||
<< LL_ENDL;
|
||||
mFetchingHistory[i].mCurlState = FetchEntry::CURL_DONE;
|
||||
}
|
||||
}
|
||||
//llinfos << "Fetch Debugger : Having " << mNbCurlRequests << " requests through the curl thread." << llendl;
|
||||
|
|
@ -4727,14 +4741,13 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon
|
|||
|
||||
LLCore::HttpStatus status(response->getStatus());
|
||||
mNbCurlRequests--;
|
||||
mNbCurlCompleted++;
|
||||
fetch.mCurlState = FetchEntry::CURL_DONE;
|
||||
if (status)
|
||||
{
|
||||
const bool partial(par_status == status);
|
||||
LLCore::BufferArray * ba(response->getBody()); // *Not* holding reference to body
|
||||
|
||||
fetch.mCurlState = FetchEntry::CURL_DONE;
|
||||
mNbCurlCompleted++;
|
||||
|
||||
S32 data_size = ba ? ba->size() : 0;
|
||||
fetch.mCurlReceivedSize += data_size;
|
||||
//llinfos << "Fetch Debugger : got results for " << fetch.mID << ", data_size = " << data_size << ", received = " << fetch.mCurlReceivedSize << ", requested = " << fetch.mRequestedSize << ", partial = " << partial << llendl;
|
||||
|
|
@ -4766,17 +4779,6 @@ void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpRespon
|
|||
llinfos << "Fetch Debugger : CURL GET FAILED, ID = " << fetch.mID
|
||||
<< ", status: " << status.toHex()
|
||||
<< " reason: " << status.toString() << llendl;
|
||||
fetch.mHTTPFailCount++;
|
||||
if(fetch.mHTTPFailCount < 5)
|
||||
{
|
||||
// Fetch will have to be redone
|
||||
fetch.mCurlState = FetchEntry::CURL_NOT_DONE;
|
||||
}
|
||||
else //skip
|
||||
{
|
||||
fetch.mCurlState = FetchEntry::CURL_DONE;
|
||||
mNbCurlCompleted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -451,7 +451,6 @@ private:
|
|||
LLPointer<LLImageRaw> mRawImage;
|
||||
e_curl_state mCurlState;
|
||||
S32 mCurlReceivedSize;
|
||||
S32 mHTTPFailCount;
|
||||
LLCore::HttpHandle mHttpHandle;
|
||||
|
||||
FetchEntry() :
|
||||
|
|
@ -467,7 +466,6 @@ private:
|
|||
mFetchedSize(f_size),
|
||||
mDecodedSize(d_size),
|
||||
mNeedsAux(false),
|
||||
mHTTPFailCount(0),
|
||||
mHttpHandle(LLCORE_HTTP_HANDLE_INVALID)
|
||||
{}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -160,11 +160,6 @@ void agent_push_backward( EKeystate s )
|
|||
camera_move_backward(s);
|
||||
return;
|
||||
}
|
||||
else if (gAgentAvatarp->isSitting())
|
||||
{
|
||||
gAgentCamera.changeCameraToThirdPerson();
|
||||
return;
|
||||
}
|
||||
agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1540,17 +1540,6 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use
|
|||
// Actually extract the data.
|
||||
if (parcel)
|
||||
{
|
||||
if (sequence_id == SELECTED_PARCEL_SEQ_ID
|
||||
&& parcel->getLocalID() != INVALID_PARCEL_ID
|
||||
&& parcel->getLocalID() != local_id)
|
||||
{
|
||||
// The parcel has a valid parcel ID but it doesn't match the parcel
|
||||
// for the data received.
|
||||
llinfos << "Expecting data for parcel " << parcel->getLocalID() \
|
||||
<< " but got data for parcel " << local_id << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
parcel->init(owner_id,
|
||||
FALSE, FALSE, FALSE,
|
||||
claim_date, claim_price_per_meter, rent_price_per_meter,
|
||||
|
|
|
|||
|
|
@ -2025,11 +2025,11 @@ BOOL LLViewerShaderMgr::loadShadersObject()
|
|||
if (success)
|
||||
{
|
||||
gObjectPreviewProgram.mName = "Simple Shader";
|
||||
gObjectPreviewProgram.mFeatures.calculatesLighting = true;
|
||||
gObjectPreviewProgram.mFeatures.calculatesLighting = false;
|
||||
gObjectPreviewProgram.mFeatures.calculatesAtmospherics = false;
|
||||
gObjectPreviewProgram.mFeatures.hasGamma = true;
|
||||
gObjectPreviewProgram.mFeatures.hasGamma = false;
|
||||
gObjectPreviewProgram.mFeatures.hasAtmospherics = false;
|
||||
gObjectPreviewProgram.mFeatures.hasLighting = true;
|
||||
gObjectPreviewProgram.mFeatures.hasLighting = false;
|
||||
gObjectPreviewProgram.mFeatures.mIndexedTextureChannels = 0;
|
||||
gObjectPreviewProgram.mFeatures.disableTextureIndex = true;
|
||||
gObjectPreviewProgram.mShaderFiles.clear();
|
||||
|
|
@ -2037,6 +2037,7 @@ BOOL LLViewerShaderMgr::loadShadersObject()
|
|||
gObjectPreviewProgram.mShaderFiles.push_back(make_pair("objects/previewF.glsl", GL_FRAGMENT_SHADER_ARB));
|
||||
gObjectPreviewProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT];
|
||||
success = gObjectPreviewProgram.createShader(NULL, NULL);
|
||||
gObjectPreviewProgram.mFeatures.hasLighting = true;
|
||||
}
|
||||
|
||||
if (success)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -152,19 +152,6 @@
|
|||
width="80">
|
||||
Mid
|
||||
</text>
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
follows="left|top"
|
||||
halign="center"
|
||||
height="12"
|
||||
layout="topleft"
|
||||
left_delta="87"
|
||||
name="ShadersPrefText3"
|
||||
top_delta="0"
|
||||
width="80">
|
||||
High
|
||||
</text>
|
||||
<text
|
||||
type="string"
|
||||
length="1"
|
||||
|
|
|
|||
|
|
@ -969,7 +969,6 @@ class LinuxManifest(ViewerManifest):
|
|||
if self.prefix(src="", dst="bin"):
|
||||
self.path("secondlife-bin","do-not-directly-run-secondlife-bin")
|
||||
self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin")
|
||||
self.path("../linux_updater/linux-updater", "linux-updater.bin")
|
||||
self.path2basename("../llplugin/slplugin", "SLPlugin")
|
||||
self.path2basename("../viewer_components/updater/scripts/linux", "update_install")
|
||||
self.end_prefix("bin")
|
||||
|
|
@ -1018,9 +1017,7 @@ class LinuxManifest(ViewerManifest):
|
|||
else:
|
||||
installer_name += '_' + self.channel_oneword().upper()
|
||||
|
||||
if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():
|
||||
print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
|
||||
self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
|
||||
self.strip_binaries()
|
||||
|
||||
# Fix access permissions
|
||||
self.run_command("""
|
||||
|
|
@ -1055,6 +1052,11 @@ class LinuxManifest(ViewerManifest):
|
|||
'dst': self.get_dst_prefix(),
|
||||
'inst': self.build_path_of(installer_name)})
|
||||
|
||||
def strip_binaries(self):
|
||||
if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():
|
||||
print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
|
||||
self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
|
||||
|
||||
class Linux_i686Manifest(LinuxManifest):
|
||||
def construct(self):
|
||||
super(Linux_i686Manifest, self).construct()
|
||||
|
|
@ -1142,9 +1144,7 @@ class Linux_i686Manifest(LinuxManifest):
|
|||
self.path("libvivoxplatform.so")
|
||||
self.end_prefix("lib")
|
||||
|
||||
if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():
|
||||
print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build"
|
||||
self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure
|
||||
self.strip_binaries()
|
||||
|
||||
|
||||
class Linux_x86_64Manifest(LinuxManifest):
|
||||
|
|
|
|||
|
|
@ -1,10 +1,217 @@
|
|||
#! /bin/bash
|
||||
INSTALL_DIR=$(cd "$(dirname "$0")/.." ; pwd)
|
||||
export LD_LIBRARY_PATH="$INSTALL_DIR/lib"
|
||||
bin/linux-updater.bin --file "$1" --dest "$INSTALL_DIR" --name "Second Life Viewer" --stringsdir "$INSTALL_DIR/skins/default/xui/en" --stringsfile "strings.xml"
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
then echo $3 >> "$2"
|
||||
# @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
|
||||
|
||||
rm -f "$1"
|
||||
# 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- &
|
||||
|
|
|
|||
Loading…
Reference in New Issue