From e48c70392cffb047c7471221b7c6fcbf32d41d06 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 30 Mar 2016 11:41:29 -0700 Subject: SL-321: prototype python launcher --- indra/newview/CMakeLists.txt | 2 ++ indra/newview/Info-SecondLife.plist | 2 +- indra/newview/viewer_manifest.py | 14 ++-------- indra/viewer_components/manager/SL_Launcher | 41 +++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 13 deletions(-) create mode 100755 indra/viewer_components/manager/SL_Launcher diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 05483c4608..3056b5d749 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2051,6 +2051,8 @@ endif (LINUX) if (DARWIN) # These all get set with PROPERTIES set(product "Second Life") + # this is the setting for the Python wrapper, see SL-322 and WRAPPER line in Info-SecondLife.plist + set(MACOSX_WRAPPER_EXECUTABLE_NAME "SL_Launcher") set(MACOSX_BUNDLE_INFO_STRING "Second Life Viewer") set(MACOSX_BUNDLE_ICON_FILE "secondlife.icns") set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.secondlife.indra.viewer") diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 9b8136a827..8aabd6818b 100755 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion English CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} + ${MACOSX_WRAPPER_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f7992dba90..60042706e4 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -703,6 +703,8 @@ class Darwin_i386_Manifest(ViewerManifest): if self.prefix(dst="MacOS"): self.path2basename("../viewer_components/updater/scripts/darwin", "*.py") + #this copies over the python wrapper script, see SL-322 + self.path2basename("../viewer_components/manager","SL_Launcher") self.end_prefix() # most everything goes in the Resources directory @@ -885,12 +887,6 @@ class Darwin_i386_Manifest(ViewerManifest): self.run_command('strip -S %(viewer_binary)r' % { 'viewer_binary' : self.dst_path_of('Contents/MacOS/Second Life')}) - def copy_finish(self): - # Force executable permissions to be set for scripts - # see CHOP-223 and http://mercurial.selenic.com/bts/issue1802 - for script in 'Contents/MacOS/update_install.py',: - self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script)) - def package_finish(self): global CHANNEL_VENDOR_BASE # MBW -- If the mounted volume name changes, it breaks the .DS_Store's background image and icon positioning. @@ -1091,12 +1087,6 @@ class LinuxManifest(ViewerManifest): self.path("featuretable_linux.txt") - def copy_finish(self): - # Force executable permissions to be set for scripts - # see CHOP-223 and http://mercurial.selenic.com/bts/issue1802 - for script in 'secondlife', 'bin/update_install': - self.run_command("chmod +x %r" % os.path.join(self.get_dst_prefix(), script)) - def package_finish(self): installer_name = self.installer_base_name() diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher new file mode 100755 index 0000000000..3c5d45d6e5 --- /dev/null +++ b/indra/viewer_components/manager/SL_Launcher @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +import argparse +import os +import sys +import subprocess +import Tkinter as tk + +viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),"Second Life") + +#to prove we are launching from the script, launch a Tkinter window first +root = tk.Tk() +w = tk.Label(root, text=viewer_binary) +w.pack() +root.after(10000, lambda: root.destroy()) # Destroy the widget after 10 seconds +root.mainloop() + +parser = argparse.ArgumentParser() +#parser.add_argument('--f', action='store_const', const=42) +args = parser.parse_known_args(sys.argv) +args_list_to_pass = args[1][1:] +args_list_to_pass.insert(0,viewer_binary) +#print args_list_to_pass + +viewer_process = subprocess.Popen(args_list_to_pass) \ No newline at end of file -- cgit v1.2.3 From c1951b49a6e6f625a4b5894313f98f57443b94c8 Mon Sep 17 00:00:00 2001 From: "coyot@coyot-sager-PC" Date: Thu, 7 Apr 2016 16:04:24 +0100 Subject: SL-321: Changes for VMP Windows Prototype --- indra/newview/CMakeLists.txt | 16 +++++++++ .../installers/windows/installer_template.nsi | 10 +++--- indra/newview/viewer_manifest.py | 4 ++- indra/viewer_components/manager/SL_Launcher | 40 +++++++++++++++++----- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3056b5d749..c81de43068 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1782,6 +1782,22 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/Debug/fmodexL.dll ) endif (FMODEX) + + get_filename_component(PYTHON_DIRECTORY ${PYTHON_EXECUTABLE} DIRECTORY) + + # http://pythonhosted.org/PyInstaller/#options + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/SL_Launcher.exe + COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe + ARGS + --onefile + --log-level WARN + --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + ${CMAKE_SOURCE_DIR}/viewer_components/manager/SL_Launcher + COMMENT "Performing pyinstaller compile of SL_Launcher" + ) + + add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/SL_Launcher.exe) add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index b8677fd9e4..d833af8742 100755 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -276,7 +276,7 @@ StrCpy $SHORTCUT_LANG_PARAM "--set InstallLanguage $(LanguageCode)" CreateDirectory "$SMPROGRAMS\$INSTSHORTCUT" SetOutPath "$INSTDIR" CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\$INSTSHORTCUT.lnk" \ - "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" + "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "%%SOURCE%%\icons\release\secondlife.ico" WriteINIStr "$SMPROGRAMS\$INSTSHORTCUT\SL Create Account.url" \ @@ -294,15 +294,15 @@ CreateShortCut "$SMPROGRAMS\$INSTSHORTCUT\Uninstall $INSTSHORTCUT.lnk" \ # Other shortcuts SetOutPath "$INSTDIR" CreateShortCut "$DESKTOP\$INSTSHORTCUT.lnk" \ - "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" + "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "%%SOURCE%%\icons\release\secondlife.ico" CreateShortCut "$INSTDIR\$INSTSHORTCUT.lnk" \ - "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" + "$INSTDIR\$INSTEXE" "$SHORTCUT_LANG_PARAM" "%%SOURCE%%\icons\release\secondlife.ico" CreateShortCut "$INSTDIR\Uninstall $INSTSHORTCUT.lnk" \ '"$INSTDIR\uninst.exe"' '' -# Create *.bat file to specify lang params on first run from installer - see MAINT-5259 +# Create *.bat file to specify lang params on first run from installer - see MAINT-5259S FileOpen $9 "$INSTDIR\autorun.bat" w -FileWrite $9 'start "$INSTDIR\$INSTEXE" "$INSTDIR\$INSTEXE" $SHORTCUT_LANG_PARAM$\r$\n' +FileWrite $9 'start "$INSTDIR\$INSTEXE" /d "$INSTDIR" "$INSTDIR\$INSTEXE" $SHORTCUT_LANG_PARAM$\r$\n' FileClose $9 # Write registry diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 60042706e4..f56dcd3f34 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -338,6 +338,8 @@ class Windows_i686_Manifest(ViewerManifest): if self.is_packaging_viewer(): # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) + # include the compiled launcher script so that it gets included in the file_list + self.path(src='%s/SL_Launcher.exe' % self.args['configuration'], dst="SL_Launcher.exe") # Plugin host application self.path2basename(os.path.join(os.pardir, @@ -611,7 +613,7 @@ class Windows_i686_Manifest(ViewerManifest): substitution_strings['installer_file'] = installer_file version_vars = """ - !define INSTEXE "%(final_exe)s" + !define INSTEXE "SL_Launcher.exe" !define VERSION "%(version_short)s" !define VERSION_LONG "%(version)s" !define VERSION_DASHES "%(version_dashes)s" diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index 3c5d45d6e5..fb07d3b991 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -22,7 +22,36 @@ import sys import subprocess import Tkinter as tk -viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),"Second Life") +parser = argparse.ArgumentParser() +#parser.add_argument('--f', action='store_const', const=42) +args = parser.parse_known_args(sys.argv) +args_list_to_pass = args[1][1:] +args_list_to_pass.insert(0,viewer_binary) +#print "COYOT: arrrrrghs to pass", args_list_to_pass + +cwd = os.path.dirname(os.path.realpath(__file__)) + +executable_name = "" +if sys.platform.startswith('darwin'): + executable_name = "Second Life" +elif sys.platform.startswith("win") or sys.platform.startswith("cyg"): + if os.path.isfile(os.path.join(cwd,"SecondLifeViewer.exe")): + executable_name = "SecondLifeViewer.exe" + elif os.path.isfile(os.path.join(cwd,"SecondLifeTest.exe")): + executable_name = "SecondLifeTest.exe" + else: + #unsupported by prototypeS + sys.exit("Can't find Windows viewer binary") +elif sys.platform.startswith("linux"): + executable_name = "secondlife" +else: + #SL doesn't run on VMS or punch cards + sys.exit("Unsupported platform") + +#print "COYOT: executable name ", executable_name +#print "COYOT: path ", os.path.dirname(os.path.abspath(sys.argv[0])) + +viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),executable_name) #to prove we are launching from the script, launch a Tkinter window first root = tk.Tk() @@ -31,11 +60,4 @@ w.pack() root.after(10000, lambda: root.destroy()) # Destroy the widget after 10 seconds root.mainloop() -parser = argparse.ArgumentParser() -#parser.add_argument('--f', action='store_const', const=42) -args = parser.parse_known_args(sys.argv) -args_list_to_pass = args[1][1:] -args_list_to_pass.insert(0,viewer_binary) -#print args_list_to_pass - -viewer_process = subprocess.Popen(args_list_to_pass) \ No newline at end of file +viewer_process = subprocess.Popen(args_list_to_pass) -- cgit v1.2.3 From 0494a502aafac73800352dc26c4eb3a27b02a8ce Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 7 Apr 2016 12:08:33 -0700 Subject: fix tabs --- indra/newview/CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c81de43068..d3b4057c8c 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1786,18 +1786,18 @@ if (WINDOWS) get_filename_component(PYTHON_DIRECTORY ${PYTHON_EXECUTABLE} DIRECTORY) # http://pythonhosted.org/PyInstaller/#options - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/SL_Launcher.exe - COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe - ARGS - --onefile - --log-level WARN - --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - ${CMAKE_SOURCE_DIR}/viewer_components/manager/SL_Launcher - COMMENT "Performing pyinstaller compile of SL_Launcher" - ) - - add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/SL_Launcher.exe) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/SL_Launcher.exe + COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe + ARGS + --onefile + --log-level WARN + --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + ${CMAKE_SOURCE_DIR}/viewer_components/manager/SL_Launcher + COMMENT "Performing pyinstaller compile of SL_Launcher" +) + +add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/SL_Launcher.exe) add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat -- cgit v1.2.3 From 41fa1e31581f716296f4616de4312f4bd17514f1 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 5 Apr 2016 13:50:16 -0400 Subject: upgrade llphysicsextenstions to work around a new clang recursion check --- autobuild.xml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 6c29d5cb18..43bad77d25 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -1528,11 +1528,11 @@ archive hash - 468e88a527e610804c3eecf07f4ed70b + 01a7cc9d0e56238a9abedd7a41ccd0a3 hash_algorithm md5 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llphysicsextensions-source_llphysicsextensions-update/rev/298369/arch/Darwin/installer/llphysicsextensions_source-1.0.298369-darwin-298369.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llphysicsextensions/rev/313564/arch/Darwin/installer/llphysicsextensions_source-1.0.313564-darwin-313564.tar.bz2 name darwin @@ -1542,9 +1542,9 @@ archive hash - 793964e49c935b414c4bdbb8a0d14ad1 + c94dc7ab6efe59c0d5d04fc447257c57 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llphysicsextensions-source_llphysicsextensions-update/rev/298369/arch/Linux/installer/llphysicsextensions_source-1.0.298369-linux-298369.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llphysicsextensions/rev/313564/arch/Linux/installer/llphysicsextensions_source-1.0.313564-linux-313564.tar.bz2 name linux @@ -1554,16 +1554,16 @@ archive hash - 922aad5261aac150e5ce3c094e57f373 + 4a9dbeb437d0e1546b93d16073ff1442 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llphysicsextensions-source_llphysicsextensions-update/rev/298369/arch/CYGWIN/installer/llphysicsextensions_source-1.0.298369-windows-298369.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llphysicsextensions/rev/313564/arch/CYGWIN/installer/llphysicsextensions_source-1.0.313564-windows-313564.tar.bz2 name windows version - 1.0.298369 + 1.0.313564 llphysicsextensions_stub @@ -1582,11 +1582,11 @@ archive hash - 1175977a191ffc936fd0ccca433c8278 + c8c6e5867d1ead7ad452a3359b22cf44 hash_algorithm md5 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llphysicsextensions-stub_llphysicsextensions-update/rev/298370/arch/Darwin/installer/llphysicsextensions_stub-1.0.298370-darwin-298370.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llphysicsextensions/rev/313563/arch/Darwin/installer/llphysicsextensions_stub-1.0.313563-darwin-313563.tar.bz2 name darwin @@ -1596,9 +1596,9 @@ archive hash - d13d7927692eab2d6a63e36166b72a8a + e99afb25a4fd5b08c5cd3060ae9c1d59 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llphysicsextensions-stub_llphysicsextensions-update/rev/298370/arch/Linux/installer/llphysicsextensions_stub-1.0.298370-linux-298370.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llphysicsextensions/rev/313563/arch/Linux/installer/llphysicsextensions_stub-1.0.313563-linux-313563.tar.bz2 name linux @@ -1608,16 +1608,16 @@ archive hash - 9594f6fd79ee924fe675a4a23e30516e + 6f4307a35c692e44b872125d7932df8e url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llphysicsextensions-stub_llphysicsextensions-update/rev/298370/arch/CYGWIN/installer/llphysicsextensions_stub-1.0.298370-windows-298370.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llphysicsextensions/rev/313563/arch/CYGWIN/installer/llphysicsextensions_stub-1.0.313563-windows-313563.tar.bz2 name windows version - 1.0.298370 + 1.0.313563 mesa -- cgit v1.2.3 From db2fdaf469613a366dd2b469ebbf0d2f979c2ef5 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 8 Apr 2016 08:52:15 -0700 Subject: compiler shift fix --- indra/llkdu/llimagej2ckdu.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 6a8959517d..72919c9c40 100755 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1034,7 +1034,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = (kdu_int32)(sp->fval*scale16); val = (val+128)>>8; // May be faster than true rounding val += 128; - if (val & ((-1)<<8)) + if (val & (((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1052,7 +1052,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = sp->ival; val = (val+offset)>>downshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1075,7 +1075,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val += (1<<(KDU_FIX_POINT-8))>>1; val >>= (KDU_FIX_POINT-8); val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1094,7 +1094,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = (val+offset)>>downshift; val <<= upshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 256 - (1<ival; val = (val+offset)>>downshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } @@ -1132,7 +1132,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = sp->ival; val <<= upshift; val += 128; - if (val & ((-1)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 256 - (1< Date: Fri, 8 Apr 2016 09:57:18 -0700 Subject: fix paren --- indra/llkdu/llimagej2ckdu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp index 72919c9c40..282c859e9e 100755 --- a/indra/llkdu/llimagej2ckdu.cpp +++ b/indra/llkdu/llimagej2ckdu.cpp @@ -1034,7 +1034,7 @@ all necessary level shifting, type conversion, rounding and truncation. */ val = (kdu_int32)(sp->fval*scale16); val = (val+128)>>8; // May be faster than true rounding val += 128; - if (val & (((0xffffffffU)<<8)) + if (val & ((0xffffffffU)<<8)) { val = (val < 0 ? 0 : 255); } -- cgit v1.2.3 From 23863db4144fa72868f3712b8e798fea882b0cb6 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 8 Apr 2016 10:26:33 -0700 Subject: SL-321: move arg parsing past platform discovery --- indra/viewer_components/manager/SL_Launcher | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index fb07d3b991..e715546821 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -22,13 +22,6 @@ import sys import subprocess import Tkinter as tk -parser = argparse.ArgumentParser() -#parser.add_argument('--f', action='store_const', const=42) -args = parser.parse_known_args(sys.argv) -args_list_to_pass = args[1][1:] -args_list_to_pass.insert(0,viewer_binary) -#print "COYOT: arrrrrghs to pass", args_list_to_pass - cwd = os.path.dirname(os.path.realpath(__file__)) executable_name = "" @@ -53,6 +46,13 @@ else: viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),executable_name) +parser = argparse.ArgumentParser() +#parser.add_argument('--f', action='store_const', const=42) +args = parser.parse_known_args(sys.argv) +args_list_to_pass = args[1][1:] +args_list_to_pass.insert(0,viewer_binary) +#print "COYOT: arrrrrghs to pass", args_list_to_pass + #to prove we are launching from the script, launch a Tkinter window first root = tk.Tk() w = tk.Label(root, text=viewer_binary) -- cgit v1.2.3 From a670914c6d43dcde0677680375f53f8a67a89cf6 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 11 Apr 2016 07:36:19 -0700 Subject: fix unused variable compiler error --- indra/newview/llmaniprotate.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp index ed40483029..3a0f96cd37 100755 --- a/indra/newview/llmaniprotate.cpp +++ b/indra/newview/llmaniprotate.cpp @@ -67,9 +67,6 @@ const F32 RADIUS_PIXELS = 100.f; // size in screen space const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS; const F32 WIDTH_PIXELS = 8; const S32 CIRCLE_STEPS = 100; -const F32 DELTA = F_TWO_PI / CIRCLE_STEPS; -const F32 SIN_DELTA = sin( DELTA ); -const F32 COS_DELTA = cos( DELTA ); const F32 MAX_MANIP_SELECT_DISTANCE = 100.f; const F32 SNAP_ANGLE_INCREMENT = 5.625f; const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT; -- cgit v1.2.3 From c68def40b15c3f2a24f9ea6f5703a3513875f608 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 23 May 2016 10:31:09 -0700 Subject: debug print statement to be removed later --- indra/newview/viewer_manifest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2c694132ea..d91faddb1a 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -116,6 +116,7 @@ class ViewerManifest(LLManifest): settings_install['CmdLineChannel'] = settings_template['CmdLineChannel'].copy() settings_install['CmdLineChannel']['Value'] = self.channel_with_pkg_suffix() print "Set CmdLineChannel in settings_install.xml to '%s'" % self.channel_with_pkg_suffix() + print "COYOT: channel set or not: %s" % self.channel_with_pkg_suffix() if 'grid' in self.args and self.args['grid']: settings_install['CmdLineGridChoice'] = settings_template['CmdLineGridChoice'].copy() -- cgit v1.2.3 From 9908fcf4ac43537728d1b34e30650735cfb36147 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 25 May 2016 07:51:58 -0700 Subject: Backed out changeset 9adcab4d611b --- indra/newview/viewer_manifest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index d91faddb1a..2c694132ea 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -116,7 +116,6 @@ class ViewerManifest(LLManifest): settings_install['CmdLineChannel'] = settings_template['CmdLineChannel'].copy() settings_install['CmdLineChannel']['Value'] = self.channel_with_pkg_suffix() print "Set CmdLineChannel in settings_install.xml to '%s'" % self.channel_with_pkg_suffix() - print "COYOT: channel set or not: %s" % self.channel_with_pkg_suffix() if 'grid' in self.args and self.args['grid']: settings_install['CmdLineGridChoice'] = settings_template['CmdLineGridChoice'].copy() -- cgit v1.2.3 From 8bee0a61a5bd70183ace8ae6207f626d17875d51 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 16 Jun 2016 09:04:19 -0700 Subject: SL-407: create Tkinter UI --- .../manager/InstallerUserMessage.py | 258 +++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 indra/viewer_components/manager/InstallerUserMessage.py diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py new file mode 100644 index 0000000000..bef2bf28a6 --- /dev/null +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $LicenseInfo:firstyear=2013&license=viewerlgpl$ +# Copyright (c) 2013, Linden Research, Inc. +# $/LicenseInfo$ + +""" +@file InstallerUserMessage.py +@author coyot +@date 2016-05-16 +""" + +""" +This does everything the old updater/scripts/darwin/messageframe.py script did and some more bits. +Pushed up the manager directory to be multiplatform. +""" + +import os +import Queue +import threading +import time +import Tkinter as tk +import ttk + + +class InstallerUserMessage(tk.Tk): + #Goals for this class: + # Provide a uniform look and feel + # Provide an easy to use convenience class for other scripts + # Provide windows that automatically disappear when done (for differing notions of done) + # Provide a progress bar that isn't a glorified spinner, but based on download progress + #Non-goals: + # No claim to threadsafety is made or warranted. Your mileage may vary. + # Please consult a doctor if you experience thread pain. + def __init__(self, text="", title="", width=500, height=200, fillcolor='#487A7B', *args, **kwargs): + tk.Tk.__init__(self) + self.grid() + self.title(title) + self.choice = tk.BooleanVar() + self.config(background = 'black') + # background="..." doesn't work on MacOS for radiobuttons or progress bars + # http://tinyurl.com/tkmacbuttons + ttk.Style().configure('Linden.TLabel', foreground='#487A7B', background='black') + ttk.Style().configure('Linden.TButton', foreground='#487A7B', background='black') + ttk.Style().configure("black.Horizontal.TProgressbar", foreground='#487A7B', background='black') + + # The constants below are to adjust for typical overhead from the + # frame borders. + self.xp = (self.winfo_screenwidth() / 2) - (width / 2) - 8 + self.yp = (self.winfo_screenheight() / 2) - (height / 2) - 20 + self.geometry('{0}x{1}+{2}+{3}'.format(width, height, self.xp, self.yp)) + self.script_dir = os.path.dirname(os.path.realpath(__file__)) + self.icon_dir = os.path.abspath(os.path.join(self.script_dir, 'icons')) + #defines what to do when window is closed + self.protocol("WM_DELETE_WINDOW", self._delete_window) + + def _delete_window(self): + #capture and discard all destroy events before the choice is set + if not ((self.choice == None) or (self.choice == "")): + try: + self.destroy() + except: + #tk may try to destroy the same object twice + pass + + def set_colors(self, widget): + # #487A7B is "Linden Green" + widget.config(foreground = '#487A7B') + widget.config(background='black') + + def find_icon(self, icon_path = None, icon_name = None): + #we do this in each message, let's do it just once instead. + if not icon_path: + icon_path = self.icon_dir + icon_path = os.path.join(icon_path, icon_name) + if os.path.exists(icon_path): + icon = tk.PhotoImage(file=icon_path) + self.image_label = tk.Label(image = icon) + self.image_label.image = icon + else: + #default to text if image not available + self.image_label = tk.Label(text = "Second Life") + + def auto_resize(self, row_count = 0, column_count = 0, heavy_row = None, heavy_column = None): + #auto resize window to fit all rows and columns + #"heavy" gets extra weight + for x in range(column_count): + if x == heavy_column: + self.columnconfigure(x, weight = 2) + else: + self.columnconfigure(x, weight=1) + + for y in range(row_count): + if y == heavy_row: + self.rowconfigure(y, weight = 2) + else: + self.rowconfigure(x, weight=1) + + def basic_message(self, message, icon_path = None, icon_name = None): + #message: text to be displayed + #icon_path: directory holding the icon, defaults to icons subdir of script dir + #icon_name: filename of icon to be displayed + self.choice.set(True) + self.find_icon(icon_path, icon_name) + self.text_label = tk.Label(text = message) + self.set_colors(self.text_label) + self.set_colors(self.image_label) + #pad, direction and weight are all experimentally derived by retrying various values + self.image_label.grid(row = 1, column = 1, sticky = 'W') + self.text_label.grid(row = 1, column = 2, sticky = 'W', padx =100) + self.auto_resize(1, 2) + self.mainloop() + + def binary_choice_message(self, message, icon_path = None, icon_name = None, one = 'Yes', two = 'No'): + #one: first option, returns True + #two: second option, returns False + #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice + #usage: + # frame = InstallerUserMessage.InstallerUserMessage( ... ) + # frame = frame.binary_choice_message( ... ) + # (wait for user to click) + # value = frame.choice.get() + self.find_icon(icon_path, icon_name) + self.text_label = tk.Label(text = message) + #command registers the callback to the method named. We want the frame to go away once clicked. + #button 1 returns True/1, button 2 returns False/0 + self.button_one = ttk.Radiobutton(text = one, variable = self.choice, value = True, + command = self._delete_window, style = 'Linden.TButton') + self.button_two = ttk.Radiobutton(text = two, variable = self.choice, value = False, + command = self._delete_window, style = 'Linden.TButton') + self.set_colors(self.text_label) + self.set_colors(self.image_label) + #pad, direction and weight are all experimentally derived by retrying various values + self.image_label.grid(row = 1, column = 1, rowspan = 3, sticky = 'W') + self.text_label.grid(row = 1, column = 2, rowspan = 3) + self.button_one.grid(row = 1, column = 3, sticky = 'W', pady = 40) + self.button_two.grid(row = 2, column = 3, sticky = 'W', pady = 0) + self.auto_resize(row_count = 2, column_count = 3, heavy_column = 3) + #self.button_two.deselect() + self.update() + self.mainloop() + + def progress_bar(self, message = None, icon_path = None, icon_name = None, size = 0, interval = 100, pb_queue = None): + #Best effort attempt at a real progress bar + # This is what Tk calls "determinate mode" rather than "indeterminate mode" + #size: denominator of percent complete + #interval: frequency, in ms, of how often to poll the file for progress + #pb_queue: queue object used to send updates to the bar + self.find_icon(icon_path, icon_name) + self.text_label = tk.Label(text = message) + self.set_colors(self.text_label) + self.set_colors(self.image_label) + self.image_label.grid(row = 1, column = 1, sticky = 'NSEW') + self.text_label.grid(row = 2, column = 1, sticky = 'NSEW') + self.progress = ttk.Progressbar(self, style = 'black.Horizontal.TProgressbar', orient="horizontal", length=100, mode="determinate") + self.progress.grid(row = 3, column = 1, sticky = 'NSEW') + self.value = 0 + self.progress["maximum"] = size + self.auto_resize(1, 3) + self.queue = pb_queue + self.check_scheduler() + + def check_scheduler(self): + if self.value < self.progress["maximum"]: + self.check_queue() + self.after(100, self.check_scheduler) + + def check_queue(self): + while self.queue.qsize(): + try: + msg = float(self.queue.get(0)) + #custom signal, time to tear down + if msg == -1: + self.choice.set(True) + self.destroy() + else: + self.progress.step(msg) + self.value = msg + except Queue.Empty: + pass + +class ThreadedClient(threading.Thread): + #for test only, not part of the functional code + def __init__(self, queue): + threading.Thread.__init__(self) + self.queue = queue + + def run(self): + for x in range(1, 90, 10): + time.sleep(1) + print "run " + str(x) + self.queue.put(10) + #tkk progress bars wrap at exactly 100 percent, look full at 99% + print "leftovers" + self.queue.put(9) + time.sleep(5) + # -1 is a custom signal to the progress_bar to quit + self.queue.put(-1) + +if __name__ == "__main__": + #When run as a script, just test the InstallUserMessage. + #To proceed with the test, close the first window, select on the second. The third will close by itself. + from contextlib import closing + from multiprocessing import Process + + import sys + import tempfile + + def set_and_check(frame, value): + print "value: " + str(value) + frame.progress.step(value) + if frame.progress["value"] < frame.progress["maximum"]: + print "In Progress" + else: + print "Over now" + + #basic message window test + frame2 = InstallerUserMessage(text = "Something in the way she moves....", title = "Beatles Quotes for 100") + frame2.basic_message(message = "...attracts me like no other.", icon_name="head-sl-logo.gif") + print "Destroyed!" + sys.stdout.flush() + + #binary choice test. User destroys window when they select. + frame3 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200") + frame3.binary_choice_message(message = "And all I have to do is think of her.", icon_name="head-sl-logo.gif", + one = "Don't want to leave her now", two = 'You know I believe and how') + print frame3.choice.get() + sys.stdout.flush() + + #progress bar + queue = Queue.Queue() + thread = ThreadedClient(queue) + thread.start() + print "thread started" + + frame4 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 300") + frame4.progress_bar(message = "You're asking me will my love grow", icon_name="head-sl-logo.gif", size = 100, pb_queue = queue) + print "frame defined" + + frame4.mainloop() + + + + -- cgit v1.2.3 From dd18fb215f76b36c96da00a6516ada096b11262c Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 17 Jun 2016 08:49:26 -0700 Subject: SL-407: post review --- .../manager/InstallerUserMessage.py | 51 ++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index bef2bf28a6..cf0a9da2a1 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -46,7 +46,11 @@ class InstallerUserMessage(tk.Tk): #Non-goals: # No claim to threadsafety is made or warranted. Your mileage may vary. # Please consult a doctor if you experience thread pain. - def __init__(self, text="", title="", width=500, height=200, fillcolor='#487A7B', *args, **kwargs): + + #Linden standard green color, from Marketing + linden_green = "#487A7B" + + def __init__(self, text="", title="", width=500, height=200, icon_name = None, icon_path = None, **kwargs): tk.Tk.__init__(self) self.grid() self.title(title) @@ -56,15 +60,22 @@ class InstallerUserMessage(tk.Tk): # http://tinyurl.com/tkmacbuttons ttk.Style().configure('Linden.TLabel', foreground='#487A7B', background='black') ttk.Style().configure('Linden.TButton', foreground='#487A7B', background='black') - ttk.Style().configure("black.Horizontal.TProgressbar", foreground='#487A7B', background='black') + ttk.Style().configure("black.Horizontal.TProgressbar", foreground=InstallerUserMessage.linden_green, background='black') + #This bit of configuration centers the window on the screen # The constants below are to adjust for typical overhead from the # frame borders. self.xp = (self.winfo_screenwidth() / 2) - (width / 2) - 8 self.yp = (self.winfo_screenheight() / 2) - (height / 2) - 20 self.geometry('{0}x{1}+{2}+{3}'.format(width, height, self.xp, self.yp)) + + #find a few things self.script_dir = os.path.dirname(os.path.realpath(__file__)) self.icon_dir = os.path.abspath(os.path.join(self.script_dir, 'icons')) + + #finds the icon and creates the widget + self.find_icon(icon_path, icon_name) + #defines what to do when window is closed self.protocol("WM_DELETE_WINDOW", self._delete_window) @@ -79,7 +90,7 @@ class InstallerUserMessage(tk.Tk): def set_colors(self, widget): # #487A7B is "Linden Green" - widget.config(foreground = '#487A7B') + widget.config(foreground = InstallerUserMessage.linden_green) widget.config(background='black') def find_icon(self, icon_path = None, icon_name = None): @@ -110,22 +121,21 @@ class InstallerUserMessage(tk.Tk): else: self.rowconfigure(x, weight=1) - def basic_message(self, message, icon_path = None, icon_name = None): + def basic_message(self, message, icon_name = None): #message: text to be displayed #icon_path: directory holding the icon, defaults to icons subdir of script dir #icon_name: filename of icon to be displayed self.choice.set(True) - self.find_icon(icon_path, icon_name) self.text_label = tk.Label(text = message) self.set_colors(self.text_label) self.set_colors(self.image_label) #pad, direction and weight are all experimentally derived by retrying various values self.image_label.grid(row = 1, column = 1, sticky = 'W') self.text_label.grid(row = 1, column = 2, sticky = 'W', padx =100) - self.auto_resize(1, 2) + self.auto_resize(row_count = 1, column_count = 2) self.mainloop() - def binary_choice_message(self, message, icon_path = None, icon_name = None, one = 'Yes', two = 'No'): + def binary_choice_message(self, message, true = 'Yes', false = 'No'): #one: first option, returns True #two: second option, returns False #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice @@ -134,7 +144,7 @@ class InstallerUserMessage(tk.Tk): # frame = frame.binary_choice_message( ... ) # (wait for user to click) # value = frame.choice.get() - self.find_icon(icon_path, icon_name) + self.text_label = tk.Label(text = message) #command registers the callback to the method named. We want the frame to go away once clicked. #button 1 returns True/1, button 2 returns False/0 @@ -154,13 +164,12 @@ class InstallerUserMessage(tk.Tk): self.update() self.mainloop() - def progress_bar(self, message = None, icon_path = None, icon_name = None, size = 0, interval = 100, pb_queue = None): + def progress_bar(self, message = None, size = 0, interval = 100, pb_queue = None): #Best effort attempt at a real progress bar # This is what Tk calls "determinate mode" rather than "indeterminate mode" #size: denominator of percent complete #interval: frequency, in ms, of how often to poll the file for progress #pb_queue: queue object used to send updates to the bar - self.find_icon(icon_path, icon_name) self.text_label = tk.Label(text = message) self.set_colors(self.text_label) self.set_colors(self.image_label) @@ -170,7 +179,7 @@ class InstallerUserMessage(tk.Tk): self.progress.grid(row = 3, column = 1, sticky = 'NSEW') self.value = 0 self.progress["maximum"] = size - self.auto_resize(1, 3) + self.auto_resize(row_count = 1, column_count = 3) self.queue = pb_queue self.check_scheduler() @@ -191,7 +200,8 @@ class InstallerUserMessage(tk.Tk): self.progress.step(msg) self.value = msg except Queue.Empty: - pass + #nothing to do + return class ThreadedClient(threading.Thread): #for test only, not part of the functional code @@ -214,9 +224,6 @@ class ThreadedClient(threading.Thread): if __name__ == "__main__": #When run as a script, just test the InstallUserMessage. #To proceed with the test, close the first window, select on the second. The third will close by itself. - from contextlib import closing - from multiprocessing import Process - import sys import tempfile @@ -229,15 +236,15 @@ if __name__ == "__main__": print "Over now" #basic message window test - frame2 = InstallerUserMessage(text = "Something in the way she moves....", title = "Beatles Quotes for 100") - frame2.basic_message(message = "...attracts me like no other.", icon_name="head-sl-logo.gif") + frame2 = InstallerUserMessage(text = "Something in the way she moves....", title = "Beatles Quotes for 100", icon_name="head-sl-logo.gif") + frame2.basic_message(message = "...attracts me like no other.") print "Destroyed!" sys.stdout.flush() #binary choice test. User destroys window when they select. - frame3 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200") - frame3.binary_choice_message(message = "And all I have to do is think of her.", icon_name="head-sl-logo.gif", - one = "Don't want to leave her now", two = 'You know I believe and how') + frame3 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") + frame3.binary_choice_message(message = "And all I have to do is think of her.", + true = "Don't want to leave her now", false = 'You know I believe and how') print frame3.choice.get() sys.stdout.flush() @@ -247,8 +254,8 @@ if __name__ == "__main__": thread.start() print "thread started" - frame4 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 300") - frame4.progress_bar(message = "You're asking me will my love grow", icon_name="head-sl-logo.gif", size = 100, pb_queue = queue) + frame4 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 300", icon_name="head-sl-logo.gif") + frame4.progress_bar(message = "You're asking me will my love grow", size = 100, pb_queue = queue) print "frame defined" frame4.mainloop() -- cgit v1.2.3 From f38439f4761abb73f6c7c93d52697cd9fa9f8368 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 17 Jun 2016 08:53:46 -0700 Subject: SL-407: post review change testing --- indra/viewer_components/manager/InstallerUserMessage.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index cf0a9da2a1..e6dba78169 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -121,7 +121,7 @@ class InstallerUserMessage(tk.Tk): else: self.rowconfigure(x, weight=1) - def basic_message(self, message, icon_name = None): + def basic_message(self, message): #message: text to be displayed #icon_path: directory holding the icon, defaults to icons subdir of script dir #icon_name: filename of icon to be displayed @@ -148,9 +148,9 @@ class InstallerUserMessage(tk.Tk): self.text_label = tk.Label(text = message) #command registers the callback to the method named. We want the frame to go away once clicked. #button 1 returns True/1, button 2 returns False/0 - self.button_one = ttk.Radiobutton(text = one, variable = self.choice, value = True, + self.button_one = ttk.Radiobutton(text = true, variable = self.choice, value = True, command = self._delete_window, style = 'Linden.TButton') - self.button_two = ttk.Radiobutton(text = two, variable = self.choice, value = False, + self.button_two = ttk.Radiobutton(text = false, variable = self.choice, value = False, command = self._delete_window, style = 'Linden.TButton') self.set_colors(self.text_label) self.set_colors(self.image_label) -- cgit v1.2.3 From 53fe427e6fd2bf4701f98fb9896d69cfef9042b6 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 17 Jun 2016 09:58:28 -0700 Subject: SL-407: remove kwargs --- indra/viewer_components/manager/InstallerUserMessage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index e6dba78169..0ab7eafc78 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -50,7 +50,7 @@ class InstallerUserMessage(tk.Tk): #Linden standard green color, from Marketing linden_green = "#487A7B" - def __init__(self, text="", title="", width=500, height=200, icon_name = None, icon_path = None, **kwargs): + def __init__(self, text="", title="", width=500, height=200, icon_name = None, icon_path = None): tk.Tk.__init__(self) self.grid() self.title(title) -- cgit v1.2.3 From eb9cc511de84f2dd54478ffd517eb34bcc6e3d1d Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 17 Jun 2016 11:01:36 -0700 Subject: SL-321: retrofit launcher to use InstallerUserMessage --- indra/viewer_components/manager/SL_Launcher | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index e715546821..fd7d00be0f 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -15,12 +15,13 @@ # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, # COMPLETENESS OR PERFORMANCE. # $/LicenseInfo$ +# Copyright (c) 2013, Linden Research, Inc. import argparse import os import sys import subprocess -import Tkinter as tk +import InstallerUserMessage cwd = os.path.dirname(os.path.realpath(__file__)) @@ -51,13 +52,10 @@ parser = argparse.ArgumentParser() args = parser.parse_known_args(sys.argv) args_list_to_pass = args[1][1:] args_list_to_pass.insert(0,viewer_binary) -#print "COYOT: arrrrrghs to pass", args_list_to_pass +print "COYOT: arrrrrghs to pass", args_list_to_pass #to prove we are launching from the script, launch a Tkinter window first -root = tk.Tk() -w = tk.Label(root, text=viewer_binary) -w.pack() -root.after(10000, lambda: root.destroy()) # Destroy the widget after 10 seconds -root.mainloop() +frame2 = InstallerUserMessage(title = "Second Life") +frame2.basic_message(message = viewer_binary, icon_name="head-sl-logo.gif") -viewer_process = subprocess.Popen(args_list_to_pass) +#viewer_process = subprocess.Popen(args_list_to_pass) -- cgit v1.2.3 From 5d900a4d8d01ffa2113cc8c763075bcd0968560f Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 23 Jun 2016 08:05:31 -0700 Subject: SL-321 add trinary widget to InstallerUserMessage --- .../manager/InstallerUserMessage.py | 52 ++++++++++++++++++---- indra/viewer_components/manager/SL_Launcher | 1 - 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index 0ab7eafc78..2ec71df030 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -58,8 +58,8 @@ class InstallerUserMessage(tk.Tk): self.config(background = 'black') # background="..." doesn't work on MacOS for radiobuttons or progress bars # http://tinyurl.com/tkmacbuttons - ttk.Style().configure('Linden.TLabel', foreground='#487A7B', background='black') - ttk.Style().configure('Linden.TButton', foreground='#487A7B', background='black') + ttk.Style().configure('Linden.TLabel', foreground=InstallerUserMessage.linden_green, background='black') + ttk.Style().configure('Linden.TButton', foreground=InstallerUserMessage.linden_green, background='black') ttk.Style().configure("black.Horizontal.TProgressbar", foreground=InstallerUserMessage.linden_green, background='black') #This bit of configuration centers the window on the screen @@ -136,8 +136,8 @@ class InstallerUserMessage(tk.Tk): self.mainloop() def binary_choice_message(self, message, true = 'Yes', false = 'No'): - #one: first option, returns True - #two: second option, returns False + #true: first option, returns True + #false: second option, returns False #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice #usage: # frame = InstallerUserMessage.InstallerUserMessage( ... ) @@ -164,6 +164,38 @@ class InstallerUserMessage(tk.Tk): self.update() self.mainloop() + def trinary_choice_message(self, message, one = 1, two = 2, three = 3): + #one: first option, returns 1 + #two: second option, returns 2 + #three: third option, returns 3 + #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice + #usage: + # frame = InstallerUserMessage.InstallerUserMessage( ... ) + # frame = frame.binary_choice_message( ... ) + # (wait for user to click) + # value = frame.choice.get() + + self.text_label = tk.Label(text = message) + #command registers the callback to the method named. We want the frame to go away once clicked. + self.button_one = ttk.Radiobutton(text = one, variable = self.choice, value = 1, + command = self._delete_window, style = 'Linden.TButton') + self.button_two = ttk.Radiobutton(text = two, variable = self.choice, value = 2, + command = self._delete_window, style = 'Linden.TButton') + self.button_three = ttk.Radiobutton(text = three, variable = self.choice, value = 3, + command = self._delete_window, style = 'Linden.TButton') + self.set_colors(self.text_label) + self.set_colors(self.image_label) + #pad, direction and weight are all experimentally derived by retrying various values + self.image_label.grid(row = 1, column = 1, rowspan = 4, sticky = 'W') + self.text_label.grid(row = 1, column = 2, rowspan = 4, padx = 5) + self.button_one.grid(row = 1, column = 3, sticky = 'W', pady = 5) + self.button_two.grid(row = 2, column = 3, sticky = 'W', pady = 5) + self.button_three.grid(row = 3, column = 3, sticky = 'W', pady = 5) + self.auto_resize(row_count = 3, column_count = 3, heavy_column = 3) + #self.button_two.deselect() + self.update() + self.mainloop() + def progress_bar(self, message = None, size = 0, interval = 100, pb_queue = None): #Best effort attempt at a real progress bar # This is what Tk calls "determinate mode" rather than "indeterminate mode" @@ -248,6 +280,13 @@ if __name__ == "__main__": print frame3.choice.get() sys.stdout.flush() + #trinary choice test. User destroys window when they select. + frame3a = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") + frame3a.trinary_choice_message(message = "And all I have to do is think of her.", + one = "Don't want to leave her now", two = 'You know I believe and how', three = 'John is Dead') + print frame3a.choice.get() + sys.stdout.flush() + #progress bar queue = Queue.Queue() thread = ThreadedClient(queue) @@ -257,9 +296,4 @@ if __name__ == "__main__": frame4 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 300", icon_name="head-sl-logo.gif") frame4.progress_bar(message = "You're asking me will my love grow", size = 100, pb_queue = queue) print "frame defined" - frame4.mainloop() - - - - diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index fd7d00be0f..6eaccc8b13 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -34,7 +34,6 @@ elif sys.platform.startswith("win") or sys.platform.startswith("cyg"): elif os.path.isfile(os.path.join(cwd,"SecondLifeTest.exe")): executable_name = "SecondLifeTest.exe" else: - #unsupported by prototypeS sys.exit("Can't find Windows viewer binary") elif sys.platform.startswith("linux"): executable_name = "secondlife" -- cgit v1.2.3 From e01f6168993af377b2a625176efff95b498b5d21 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 27 Jun 2016 15:30:57 -0700 Subject: SL-323: multithreaded downloader with progress bar --- indra/viewer_components/manager/download_update.py | 96 ++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100755 indra/viewer_components/manager/download_update.py diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py new file mode 100755 index 0000000000..aa8555cd21 --- /dev/null +++ b/indra/viewer_components/manager/download_update.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $LicenseInfo:firstyear=2013&license=viewerlgpl$ +# Copyright (c) 2013, Linden Research, Inc. +# $/LicenseInfo$ + +""" +@file download_update.py +@author coyot +@date 2016-06-23 +""" + +""" +Performs a download of an update. In a separate script from update_manager so that we can +call it with subprocess. +""" + +import argparse +import InstallerUserMessage as IUM +import os +import Queue +import requests +import threading + + +def download_update(url = None, download_dir = None, size = None, progressbar = False, chunk_size = 1024): + #url to download from + #download_dir to download to + #total size (for progressbar) of download + #progressbar: whether to display one (not used for background downloads) + #chunk_size is in bytes + #in_queue is to communicate between download_update and the thread + #out_queue is to communicate between the thread and the progressbar + #if progressbar: + #out_queue = Queue.Queue() + #buffer_thread = ThreadedBuffer(size, in_queue, out_queue) + #buffer_thread.start() + #pb_thread = ThreadedBar(size, out_queue) + #pb_thread.start() + + queue = Queue.Queue() + filename = os.path.join(download_dir, url.split('/')[-1]) + req = requests.get(url, stream=True) + down_thread = ThreadedDownload(req, filename, chunk_size, progressbar, queue) + down_thread.start() + + if progressbar: + frame = IUM.InstallerUserMessage(title = "Second Life Downloader", icon_name="head-sl-logo.gif") + frame.progress_bar(message = "Download Progress", size = size, pb_queue = queue) + frame.mainloop() + +class ThreadedDownload(threading.Thread): + def __init__(self, req, filename, chunk_size, progressbar, in_queue): + threading.Thread.__init__(self) + self.req = req + self.filename = filename + self.chunk_size = int(chunk_size) + self.progressbar = progressbar + self.in_queue = in_queue + + def run(self): + with open(self.filename, 'wb') as fd: + for chunk in self.req.iter_content(self.chunk_size): + fd.write(chunk) + if self.progressbar: + self.in_queue.put(len(chunk)) + self.in_queue.put(-1) + +def main(): + parser = argparse.ArgumentParser("Download URI to directory") + parser.add_argument('--url', dest='url', help='URL of file to be downloaded', required=True) + parser.add_argument('--dir', dest='download_dir', help='directory to be downloaded to', required=True) + parser.add_argument('--pb', dest='progressbar', help='whether or not to show a progressbar', action="store_true", default = False) + parser.add_argument('--size', dest='size', help='size of download for progressbar') + parser.add_argument('--chunk_size', dest='chunk_size', help='max portion size of download to be loaded in memory in bytes.') + args = parser.parse_args() + + download_update(url = args.url, download_dir = args.download_dir, size = args.size, progressbar = args.progressbar, chunk_size = args.chunk_size) + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From cb90597b4e6fa64db706d6341039ade3c6d09a5b Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 27 Jun 2016 16:20:10 -0700 Subject: remove extraneous comments --- indra/viewer_components/manager/download_update.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py index aa8555cd21..71da5c97c2 100755 --- a/indra/viewer_components/manager/download_update.py +++ b/indra/viewer_components/manager/download_update.py @@ -43,14 +43,6 @@ def download_update(url = None, download_dir = None, size = None, progressbar = #total size (for progressbar) of download #progressbar: whether to display one (not used for background downloads) #chunk_size is in bytes - #in_queue is to communicate between download_update and the thread - #out_queue is to communicate between the thread and the progressbar - #if progressbar: - #out_queue = Queue.Queue() - #buffer_thread = ThreadedBuffer(size, in_queue, out_queue) - #buffer_thread.start() - #pb_thread = ThreadedBar(size, out_queue) - #pb_thread.start() queue = Queue.Queue() filename = os.path.join(download_dir, url.split('/')[-1]) -- cgit v1.2.3 From 9bc49fb4bd94814482846106954e198e504d802a Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 28 Jun 2016 11:34:05 -0700 Subject: SL-323: post review comments on downloader --- indra/viewer_components/manager/download_update.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py index 71da5c97c2..cd4e6680b0 100755 --- a/indra/viewer_components/manager/download_update.py +++ b/indra/viewer_components/manager/download_update.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# # Copyright (c) 2016, Linden Research, Inc. # # The following source code is PROPRIETARY AND CONFIDENTIAL. Use of @@ -14,8 +12,8 @@ # ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO # WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, # COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2013&license=viewerlgpl$ -# Copyright (c) 2013, Linden Research, Inc. +# $LicenseInfo:firstyear=2016&license=viewerlgpl$ +# Copyright (c) 2016, Linden Research, Inc. # $/LicenseInfo$ """ @@ -36,8 +34,10 @@ import Queue import requests import threading +#module default +CHUNK_SIZE = 1024 -def download_update(url = None, download_dir = None, size = None, progressbar = False, chunk_size = 1024): +def download_update(url = None, download_dir = None, size = None, progressbar = False, chunk_size = CHUNK_SIZE): #url to download from #download_dir to download to #total size (for progressbar) of download @@ -54,6 +54,9 @@ def download_update(url = None, download_dir = None, size = None, progressbar = frame = IUM.InstallerUserMessage(title = "Second Life Downloader", icon_name="head-sl-logo.gif") frame.progress_bar(message = "Download Progress", size = size, pb_queue = queue) frame.mainloop() + else: + #nothing for the main thread to do + down_thread.join() class ThreadedDownload(threading.Thread): def __init__(self, req, filename, chunk_size, progressbar, in_queue): @@ -78,7 +81,7 @@ def main(): parser.add_argument('--dir', dest='download_dir', help='directory to be downloaded to', required=True) parser.add_argument('--pb', dest='progressbar', help='whether or not to show a progressbar', action="store_true", default = False) parser.add_argument('--size', dest='size', help='size of download for progressbar') - parser.add_argument('--chunk_size', dest='chunk_size', help='max portion size of download to be loaded in memory in bytes.') + parser.add_argument('--chunk_size', dest='chunk_size', default=CHUNK_SIZE, help='max portion size of download to be loaded in memory in bytes.') args = parser.parse_args() download_update(url = args.url, download_dir = args.download_dir, size = args.size, progressbar = args.progressbar, chunk_size = args.chunk_size) -- cgit v1.2.3 From 924e80142fd3ef1454dab1e9a002e5be9e66db94 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 5 Jul 2016 08:06:03 -0700 Subject: SL-323: apply update code --- indra/viewer_components/manager/apply_update.py | 232 ++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100755 indra/viewer_components/manager/apply_update.py diff --git a/indra/viewer_components/manager/apply_update.py b/indra/viewer_components/manager/apply_update.py new file mode 100755 index 0000000000..1cf394e008 --- /dev/null +++ b/indra/viewer_components/manager/apply_update.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python + +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $LicenseInfo:firstyear=2016&license=viewerlgpl$ +# Copyright (c) 2016, Linden Research, Inc. +# $/LicenseInfo$ + +""" +@file apply_update.py +@author coyot +@date 2016-06-28 +""" + +""" +Applies an already downloaded update. +""" + +import argparse +import fnmatch +import InstallerUserMessage as IUM +import os +import os.path +import plistlib +import shutil +import subprocess +import sys +import tarfile +import tempfile + +LNX_REGEX = '*' + '.bz2' +MAC_REGEX = '*' + '.dmg' +MAC_APP_REGEX = '*' + '.app' +WIN_REGEX = '*' + '.exe' + +INSTALL_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + +BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer" +# Magic OS directory name that causes Cocoa viewer to crash on OS X 10.7.5 +# (see MAINT-3331) +STATE_DIR = os.path.join(os.environ["HOME"], "Library", "Saved Application State", + BUNDLE_IDENTIFIER + ".savedState") + +def silent_write(log_file_handle, text): + #if we have a log file, write. If not, do nothing. + if (log_file_handle): + #prepend text for easy grepping + log_file_handle.write("APPLY UPDATE: " + text + "\n") + +def get_filename(download_dir = None): + #given a directory that supposedly has the download, find the installable + for filename in os.listdir(download_dir): + if (fnmatch.fnmatch(filename, LNX_REGEX) + or fnmatch.fnmatch(filename, MAC_REGEX) + or fnmatch.fnmatch(filename, WIN_REGEX)): + return os.path.join(download_dir, filename) + else: + return None + +def try_dismount(log_file_handle = None, installable = None, tmpdir = None): + #best effort cleanup try to dismount the dmg file if we have mounted one + #the French judge gave it a 5.8 + try: + command = ["df", os.path.join(tmpdir, "Second Life Installer")] + output = subprocess.check_output(command) + mnt_dev = output.split('\n')[1].split()[0] + command = ["hdiutil", "detach", "-force", mnt_dev] + output = subprocess.check_output(command) + silent_write(log_file_handle, "hdiutil detach succeeded") + silent_write(log_file_handle, output) + except Exception, e: + silent_write(log_file_handle, "Could not detach dmg file %s. Error messages: %s" % (installable, e.message)) + +def apply_update(download_dir = None, platform_key = None, log_file_handle = None): + #for lnx and mac, returns path to newly installed viewer and "True" for Windows + #returns None on failure for all three + installable = get_filename(download_dir) + if not installable: + #could not find download + raise ValueError("Could not find installable in " + download_dir) + + if platform_key == 'lnx': + installed = apply_linux_update(installable, log_file_handle) + elif platform_key == 'mac': + installed = apply_mac_update(installable, log_file_handle) + elif platform_key == 'win': + installed = apply_windows_update(installable, log_file_handle) + else: + #wtf? + raise ValueError("Unknown Platform: " + platform_key) + + if not installed: + done_filename = os.path.join(os.path.dirname(installable), ".done") + open(done_filename, 'w+').close() + + return installed + +def apply_linux_update(installable = None, log_file_handle = None): + try: + #untar to tmpdir + tmpdir = tempfile.mkdtemp() + tar = tarfile.open(name = installable, mode="r:bz2") + tar.extractall(path = tmpdir) + #rename current install dir + shutil.move(INSTALL_DIR,install_dir + ".bak") + #mv new to current + shutil.move(tmpdir, INSTALL_DIR) + #delete tarball on success + os.remove(installable) + except Exception, e: + silent_write(log_file_handle, "Update failed due to " + repr(e)) + return None + return INSTALL_DIR + +def apply_mac_update(installable = None, log_file_handle = None): + #verify dmg file + try: + output = subprocess.check_output(["hdiutil", "verify", installable], stderr=subprocess.STDOUT) + silent_write(log_file_handle, "dmg verification succeeded") + silent_write(log_file_handle, output) + except Exception, e: + silent_write(log_file_handle, "Could not verify dmg file %s. Error messages: %s" % (installable, e.message)) + return None + #make temp dir and mount & attach dmg + tmpdir = tempfile.mkdtemp() + try: + output = subprocess.check_output(["hdiutil", "attach", installable, "-mountroot", tmpdir]) + silent_write(log_file_handle, "hdiutil attach succeeded") + silent_write(log_file_handle, output) + except Exception, e: + silent_write(log_file_handle, "Could not attach dmg file %s. Error messages: %s" % (installable, e.message)) + return None + #verify plist + appdir = None + for top_dir in os.listdir(tmpdir): + for appdir in os.listdir(os.path.join(tmpdir, top_dir)): + appdir = os.path.join(os.path.join(tmpdir, top_dir), appdir) + if fnmatch.fnmatch(appdir, MAC_APP_REGEX): + try: + plist = os.path.join(appdir, "Contents", "Info.plist") + CFBundleIdentifier = plistlib.readPlist(plist)["CFBundleIdentifier"] + except: + #there is no except for this try because there are multiple directories that legimately don't have what we are looking for + pass + if not appdir: + silent_write(log_file_handle, "Could not find app bundle in dmg %s." % (installable,)) + return None + if CFBundleIdentifier != BUNDLE_IDENTIFIER: + silent_write(log_file_handle, "Wrong or null bundle identifier for dmg %s. Bundle identifier: %s" % (installable, CFBundleIdentifier)) + try_dismount(log_file_handle, installable, tmpdir) + return None + #do the install, finally + # swap out old install directory + bundlename = os.path.basename(appdir) + #INSTALL_DIR is something like /Applications/Second Life Viewer.app/Contents/MacOS, need to jump up two levels + installed_test = os.path.dirname(INSTALL_DIR) + installed_test = os.path.dirname(installed_test) + if os.path.exists(installed_test): + silent_write(log_file_handle, "Updating %s" % installed_test) + swapped_out = os.path.join(tmpdir, INSTALL_DIR.lstrip('/')) + shutil.move(installed_test, swapped_out) + else: + silent_write(log_file_handle, "Installing %s" % installed_test) + + # copy over the new bits + try: + shutil.copytree(appdir, installed_test, symlinks=True) + retcode = 0 + except Exception, e: + # try to restore previous viewer + if os.path.exists(swapped_out): + silent_write(log_file_handle, "Install of %s failed, rolling back to previous viewer." % installable) + shutil.move(swapped_out, installed_test) + retcode = 1 + finally: + try_dismount(log_file_handle, installable, tmpdir) + if retcode: + return None + + #see MAINT-3331 + with allow_errno(errno.ENOENT): + shutil.rmtree(STATE_DIR) + + os.remove(installable) + return INSTALL_DIR + +def apply_windows_update(installable = None, log_file_handle = None): + #the windows install is just running the NSIS installer executable + #from VMP's perspective, it is a black box + try: + output = subprocess.check_output(installable, stderr=subprocess.STDOUT) + silent_write(log_file_handle, "Install of %s succeeded." % installable) + silent_write(log_file_handle, output) + except subprocess.CalledProcessError, cpe: + silent_write(log_file_handle, "%s failed with return code %s. Error messages: %s." % + (cpe.cmd, cpe.returncode, cpe.message)) + return None + return True + +def main(): + parser = argparse.ArgumentParser("Apply Downloaded Update") + parser.add_argument('--dir', dest = 'download_dir', help = 'directory to find installable', required=True) + parser.add_argument('--pkey', dest = 'platform_key', help =' OS: lnx|mac|win', required=True) + parser.add_argument('--log_file', dest = 'log_file', default = None, help = 'file to write messages to') + args = parser.parse_args() + + if args.log_file: + try: + f = open(args.log_file,'w+') + except: + print "%s could not be found or opened" % args.log_file + sys.exit(1) + + result = apply_update(download_dir = args.download_dir, platform_key = args.platform_key, log_file_handle = f) + if not result: + sys.exit("Update failed") + else: + sys.exit(0) + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 7e120a61d1ce670b2bab8ab6b1a814ed4af0dc12 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 5 Jul 2016 13:43:50 -0700 Subject: remove first version of apply_update --- indra/viewer_components/manager/apply_update.py | 232 ------------------------ 1 file changed, 232 deletions(-) delete mode 100755 indra/viewer_components/manager/apply_update.py diff --git a/indra/viewer_components/manager/apply_update.py b/indra/viewer_components/manager/apply_update.py deleted file mode 100755 index 1cf394e008..0000000000 --- a/indra/viewer_components/manager/apply_update.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2016&license=viewerlgpl$ -# Copyright (c) 2016, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file apply_update.py -@author coyot -@date 2016-06-28 -""" - -""" -Applies an already downloaded update. -""" - -import argparse -import fnmatch -import InstallerUserMessage as IUM -import os -import os.path -import plistlib -import shutil -import subprocess -import sys -import tarfile -import tempfile - -LNX_REGEX = '*' + '.bz2' -MAC_REGEX = '*' + '.dmg' -MAC_APP_REGEX = '*' + '.app' -WIN_REGEX = '*' + '.exe' - -INSTALL_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - -BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer" -# Magic OS directory name that causes Cocoa viewer to crash on OS X 10.7.5 -# (see MAINT-3331) -STATE_DIR = os.path.join(os.environ["HOME"], "Library", "Saved Application State", - BUNDLE_IDENTIFIER + ".savedState") - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - if (log_file_handle): - #prepend text for easy grepping - log_file_handle.write("APPLY UPDATE: " + text + "\n") - -def get_filename(download_dir = None): - #given a directory that supposedly has the download, find the installable - for filename in os.listdir(download_dir): - if (fnmatch.fnmatch(filename, LNX_REGEX) - or fnmatch.fnmatch(filename, MAC_REGEX) - or fnmatch.fnmatch(filename, WIN_REGEX)): - return os.path.join(download_dir, filename) - else: - return None - -def try_dismount(log_file_handle = None, installable = None, tmpdir = None): - #best effort cleanup try to dismount the dmg file if we have mounted one - #the French judge gave it a 5.8 - try: - command = ["df", os.path.join(tmpdir, "Second Life Installer")] - output = subprocess.check_output(command) - mnt_dev = output.split('\n')[1].split()[0] - command = ["hdiutil", "detach", "-force", mnt_dev] - output = subprocess.check_output(command) - silent_write(log_file_handle, "hdiutil detach succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not detach dmg file %s. Error messages: %s" % (installable, e.message)) - -def apply_update(download_dir = None, platform_key = None, log_file_handle = None): - #for lnx and mac, returns path to newly installed viewer and "True" for Windows - #returns None on failure for all three - installable = get_filename(download_dir) - if not installable: - #could not find download - raise ValueError("Could not find installable in " + download_dir) - - if platform_key == 'lnx': - installed = apply_linux_update(installable, log_file_handle) - elif platform_key == 'mac': - installed = apply_mac_update(installable, log_file_handle) - elif platform_key == 'win': - installed = apply_windows_update(installable, log_file_handle) - else: - #wtf? - raise ValueError("Unknown Platform: " + platform_key) - - if not installed: - done_filename = os.path.join(os.path.dirname(installable), ".done") - open(done_filename, 'w+').close() - - return installed - -def apply_linux_update(installable = None, log_file_handle = None): - try: - #untar to tmpdir - tmpdir = tempfile.mkdtemp() - tar = tarfile.open(name = installable, mode="r:bz2") - tar.extractall(path = tmpdir) - #rename current install dir - shutil.move(INSTALL_DIR,install_dir + ".bak") - #mv new to current - shutil.move(tmpdir, INSTALL_DIR) - #delete tarball on success - os.remove(installable) - except Exception, e: - silent_write(log_file_handle, "Update failed due to " + repr(e)) - return None - return INSTALL_DIR - -def apply_mac_update(installable = None, log_file_handle = None): - #verify dmg file - try: - output = subprocess.check_output(["hdiutil", "verify", installable], stderr=subprocess.STDOUT) - silent_write(log_file_handle, "dmg verification succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not verify dmg file %s. Error messages: %s" % (installable, e.message)) - return None - #make temp dir and mount & attach dmg - tmpdir = tempfile.mkdtemp() - try: - output = subprocess.check_output(["hdiutil", "attach", installable, "-mountroot", tmpdir]) - silent_write(log_file_handle, "hdiutil attach succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not attach dmg file %s. Error messages: %s" % (installable, e.message)) - return None - #verify plist - appdir = None - for top_dir in os.listdir(tmpdir): - for appdir in os.listdir(os.path.join(tmpdir, top_dir)): - appdir = os.path.join(os.path.join(tmpdir, top_dir), appdir) - if fnmatch.fnmatch(appdir, MAC_APP_REGEX): - try: - plist = os.path.join(appdir, "Contents", "Info.plist") - CFBundleIdentifier = plistlib.readPlist(plist)["CFBundleIdentifier"] - except: - #there is no except for this try because there are multiple directories that legimately don't have what we are looking for - pass - if not appdir: - silent_write(log_file_handle, "Could not find app bundle in dmg %s." % (installable,)) - return None - if CFBundleIdentifier != BUNDLE_IDENTIFIER: - silent_write(log_file_handle, "Wrong or null bundle identifier for dmg %s. Bundle identifier: %s" % (installable, CFBundleIdentifier)) - try_dismount(log_file_handle, installable, tmpdir) - return None - #do the install, finally - # swap out old install directory - bundlename = os.path.basename(appdir) - #INSTALL_DIR is something like /Applications/Second Life Viewer.app/Contents/MacOS, need to jump up two levels - installed_test = os.path.dirname(INSTALL_DIR) - installed_test = os.path.dirname(installed_test) - if os.path.exists(installed_test): - silent_write(log_file_handle, "Updating %s" % installed_test) - swapped_out = os.path.join(tmpdir, INSTALL_DIR.lstrip('/')) - shutil.move(installed_test, swapped_out) - else: - silent_write(log_file_handle, "Installing %s" % installed_test) - - # copy over the new bits - try: - shutil.copytree(appdir, installed_test, symlinks=True) - retcode = 0 - except Exception, e: - # try to restore previous viewer - if os.path.exists(swapped_out): - silent_write(log_file_handle, "Install of %s failed, rolling back to previous viewer." % installable) - shutil.move(swapped_out, installed_test) - retcode = 1 - finally: - try_dismount(log_file_handle, installable, tmpdir) - if retcode: - return None - - #see MAINT-3331 - with allow_errno(errno.ENOENT): - shutil.rmtree(STATE_DIR) - - os.remove(installable) - return INSTALL_DIR - -def apply_windows_update(installable = None, log_file_handle = None): - #the windows install is just running the NSIS installer executable - #from VMP's perspective, it is a black box - try: - output = subprocess.check_output(installable, stderr=subprocess.STDOUT) - silent_write(log_file_handle, "Install of %s succeeded." % installable) - silent_write(log_file_handle, output) - except subprocess.CalledProcessError, cpe: - silent_write(log_file_handle, "%s failed with return code %s. Error messages: %s." % - (cpe.cmd, cpe.returncode, cpe.message)) - return None - return True - -def main(): - parser = argparse.ArgumentParser("Apply Downloaded Update") - parser.add_argument('--dir', dest = 'download_dir', help = 'directory to find installable', required=True) - parser.add_argument('--pkey', dest = 'platform_key', help =' OS: lnx|mac|win', required=True) - parser.add_argument('--log_file', dest = 'log_file', default = None, help = 'file to write messages to') - args = parser.parse_args() - - if args.log_file: - try: - f = open(args.log_file,'w+') - except: - print "%s could not be found or opened" % args.log_file - sys.exit(1) - - result = apply_update(download_dir = args.download_dir, platform_key = args.platform_key, log_file_handle = f) - if not result: - sys.exit("Update failed") - else: - sys.exit(0) - - -if __name__ == "__main__": - main() -- cgit v1.2.3 From bb19a1e9cc2d11f1db89a92eb17f98641736cd1e Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 5 Jul 2016 13:44:43 -0700 Subject: SL-323: apply update code, v2 --- indra/viewer_components/manager/apply_update.py | 254 ++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100755 indra/viewer_components/manager/apply_update.py diff --git a/indra/viewer_components/manager/apply_update.py b/indra/viewer_components/manager/apply_update.py new file mode 100755 index 0000000000..362d57c94e --- /dev/null +++ b/indra/viewer_components/manager/apply_update.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python + +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $LicenseInfo:firstyear=2016&license=viewerlgpl$ +# Copyright (c) 2016, Linden Research, Inc. +# $/LicenseInfo$ + +""" +@file apply_update.py +@author coyot +@date 2016-06-28 +""" + +""" +Applies an already downloaded update. +""" + +import argparse +import errno +import fnmatch +import InstallerUserMessage as IUM +import os +import os.path +import plistlib +import shutil +import subprocess +import sys +import tarfile +import tempfile + +#fnmatch expressions +LNX_REGEX = '*' + '.bz2' +MAC_REGEX = '*' + '.dmg' +MAC_APP_REGEX = '*' + '.app' +WIN_REGEX = '*' + '.exe' + +#which install the updater is run from +INSTALL_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + +#whether the update is to the INSTALL_DIR or not. Most of the time this is the case. +IN_PLACE = True + +BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer" +# Magic OS directory name that causes Cocoa viewer to crash on OS X 10.7.5 +# (see MAINT-3331) +STATE_DIR = os.path.join(os.environ["HOME"], "Library", "Saved Application State", + BUNDLE_IDENTIFIER + ".savedState") + +def silent_write(log_file_handle, text): + #if we have a log file, write. If not, do nothing. + if (log_file_handle): + #prepend text for easy grepping + log_file_handle.write("APPLY UPDATE: " + text + "\n") + +def get_filename(download_dir = None): + #given a directory that supposedly has the download, find the installable + for filename in os.listdir(download_dir): + if (fnmatch.fnmatch(filename, LNX_REGEX) + or fnmatch.fnmatch(filename, MAC_REGEX) + or fnmatch.fnmatch(filename, WIN_REGEX)): + return os.path.join(download_dir, filename) + #someone gave us a bad directory + return None + +def try_dismount(log_file_handle = None, installable = None, tmpdir = None): + #best effort cleanup try to dismount the dmg file if we have mounted one + #the French judge gave it a 5.8 + try: + command = ["df", os.path.join(tmpdir, "Second Life Installer")] + output = subprocess.check_output(command) + #first word of second line of df output is the device name + mnt_dev = output.split('\n')[1].split()[0] + command = ["hdiutil", "detach", "-force", mnt_dev] + output = subprocess.check_output(command) + silent_write(log_file_handle, "hdiutil detach succeeded") + silent_write(log_file_handle, output) + except Exception, e: + silent_write(log_file_handle, "Could not detach dmg file %s. Error messages: %s" % (installable, e.message)) + +def apply_update(download_dir = None, platform_key = None, log_file_handle = None): + #for lnx and mac, returns path to newly installed viewer + #for win, return the name of the executable + #returns None on failure for all three + #throws an exception if it can't find an installable at all + + installable = get_filename(download_dir) + if not installable: + #could not find download + raise ValueError("Could not find installable in " + download_dir) + + if platform_key == 'lnx': + installed = apply_linux_update(installable, log_file_handle) + elif platform_key == 'mac': + installed = apply_mac_update(installable, log_file_handle) + elif platform_key == 'win': + installed = apply_windows_update(installable, log_file_handle) + else: + #wtf? + raise ValueError("Unknown Platform: " + platform_key) + + if not installed: + #only mark the download as done when everything is done + done_filename = os.path.join(os.path.dirname(installable), ".done") + open(done_filename, 'w+').close() + + return installed + +def apply_linux_update(installable = None, log_file_handle = None): + try: + #untar to tmpdir + tmpdir = tempfile.mkdtemp() + tar = tarfile.open(name = installable, mode="r:bz2") + tar.extractall(path = tmpdir) + if IN_PLACE: + #rename current install dir + shutil.move(INSTALL_DIR,install_dir + ".bak") + #mv new to current + shutil.move(tmpdir, INSTALL_DIR) + #delete tarball on success + os.remove(installable) + except Exception, e: + silent_write(log_file_handle, "Update failed due to " + repr(e)) + return None + return INSTALL_DIR + +def apply_mac_update(installable = None, log_file_handle = None): + #INSTALL_DIR is something like /Applications/Second Life Viewer.app/Contents/MacOS, need to jump up two levels for the install base + install_base = os.path.dirname(INSTALL_DIR) + install_base = os.path.dirname(install_base) + + #verify dmg file + try: + output = subprocess.check_output(["hdiutil", "verify", installable], stderr=subprocess.STDOUT) + silent_write(log_file_handle, "dmg verification succeeded") + silent_write(log_file_handle, output) + except Exception, e: + silent_write(log_file_handle, "Could not verify dmg file %s. Error messages: %s" % (installable, e.message)) + return None + #make temp dir and mount & attach dmg + tmpdir = tempfile.mkdtemp() + try: + output = subprocess.check_output(["hdiutil", "attach", installable, "-mountroot", tmpdir]) + silent_write(log_file_handle, "hdiutil attach succeeded") + silent_write(log_file_handle, output) + except Exception, e: + silent_write(log_file_handle, "Could not attach dmg file %s. Error messages: %s" % (installable, e.message)) + return None + #verify plist + mounted_appdir = None + for top_dir in os.listdir(tmpdir): + for appdir in os.listdir(os.path.join(tmpdir, top_dir)): + appdir = os.path.join(os.path.join(tmpdir, top_dir), appdir) + if fnmatch.fnmatch(appdir, MAC_APP_REGEX): + try: + plist = os.path.join(appdir, "Contents", "Info.plist") + CFBundleIdentifier = plistlib.readPlist(plist)["CFBundleIdentifier"] + mounted_appdir = appdir + except: + #there is no except for this try because there are multiple directories that legimately don't have what we are looking for + pass + if not mounted_appdir: + silent_write(log_file_handle, "Could not find app bundle in dmg %s." % (installable,)) + return None + if CFBundleIdentifier != BUNDLE_IDENTIFIER: + silent_write(log_file_handle, "Wrong or null bundle identifier for dmg %s. Bundle identifier: %s" % (installable, CFBundleIdentifier)) + try_dismount(log_file_handle, installable, tmpdir) + return None + #do the install, finally + if IN_PLACE: + # swap out old install directory + bundlename = os.path.basename(mounted_appdir) + silent_write(log_file_handle, "Updating %s" % bundlename) + swapped_out = os.path.join(tmpdir, INSTALL_DIR.lstrip('/')) + shutil.move(install_base, swapped_out) + else: + silent_write(log_file_handle, "Installing %s" % install_base) + + # copy over the new bits + try: + shutil.copytree(mounted_appdir, install_base, symlinks=True) + retcode = 0 + except Exception, e: + # try to restore previous viewer + if os.path.exists(swapped_out): + silent_write(log_file_handle, "Install of %s failed, rolling back to previous viewer." % installable) + shutil.move(swapped_out, installed_test) + retcode = 1 + finally: + try_dismount(log_file_handle, installable, tmpdir) + if retcode: + return None + + #see MAINT-3331 + try: + shutil.rmtree(STATE_DIR) + except Exception, e: + #if we fail to delete something that isn't there, that's okay + if e[0] == errno.ENOENT: + pass + else: + raise e + + os.remove(installable) + return install_base + +def apply_windows_update(installable = None, log_file_handle = None): + #the windows install is just running the NSIS installer executable + #from VMP's perspective, it is a black box + try: + output = subprocess.check_output(installable, stderr=subprocess.STDOUT) + silent_write(log_file_handle, "Install of %s succeeded." % installable) + silent_write(log_file_handle, output) + except subprocess.CalledProcessError, cpe: + silent_write(log_file_handle, "%s failed with return code %s. Error messages: %s." % + (cpe.cmd, cpe.returncode, cpe.message)) + return None + return installable + +def main(): + parser = argparse.ArgumentParser("Apply Downloaded Update") + parser.add_argument('--dir', dest = 'download_dir', help = 'directory to find installable', required = True) + parser.add_argument('--pkey', dest = 'platform_key', help =' OS: lnx|mac|win', required = True) + parser.add_argument('--in_place', action = 'store_false', help = 'This upgrade is for a different channel', default = True) + parser.add_argument('--log_file', dest = 'log_file', default = None, help = 'file to write messages to') + args = parser.parse_args() + + if args.log_file: + try: + f = open(args.log_file,'w+') + except: + print "%s could not be found or opened" % args.log_file + sys.exit(1) + + IN_PLACE = args.in_place + result = apply_update(download_dir = args.download_dir, platform_key = args.platform_key, log_file_handle = f) + if not result: + sys.exit("Update failed") + else: + sys.exit(0) + + +if __name__ == "__main__": + main() -- cgit v1.2.3 From 03bcad61115b7128b3b1a5ede3cd7f2bf309fd39 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 11 Jul 2016 11:24:45 -0700 Subject: SLS-323: integrate update manager with lanucher, various fixes, CMake changes --- indra/newview/CMakeLists.txt | 33 ++ .../manager/InstallerUserMessage.py | 14 +- indra/viewer_components/manager/SL_Launcher | 59 ++- indra/viewer_components/manager/apply_update.py | 29 +- indra/viewer_components/manager/download_update.py | 16 +- indra/viewer_components/manager/update_manager.py | 455 +++++++++++++++++++++ 6 files changed, 582 insertions(+), 24 deletions(-) create mode 100755 indra/viewer_components/manager/update_manager.py diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 767a280beb..9ee7b656c0 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1774,9 +1774,42 @@ if (WINDOWS) --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} ${CMAKE_SOURCE_DIR}/viewer_components/manager/SL_Launcher COMMENT "Performing pyinstaller compile of SL_Launcher" + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/apply_update.exe + COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe + ARGS + --onefile + --log-level WARN + --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + ${CMAKE_SOURCE_DIR}/viewer_components/manager/apply_update.py + COMMENT "Performing pyinstaller compile of updater" + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/download_update.exe + COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe + ARGS + --onefile + --log-level WARN + --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + ${CMAKE_SOURCE_DIR}/viewer_components/manager/download_update.py + COMMENT "Performing pyinstaller compile of update downloader" + + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/update_manager.exe + COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe + ARGS + --onefile + --log-level WARN + --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} + ${CMAKE_SOURCE_DIR}/viewer_components/manager/update_manager.py + COMMENT "Performing pyinstaller compile of update manager" ) add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/SL_Launcher.exe) +add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/apply_update.exe) +add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/download_update.exe) +add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/update_manager.exe) add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index 2ec71df030..f66af81d06 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -280,13 +280,6 @@ if __name__ == "__main__": print frame3.choice.get() sys.stdout.flush() - #trinary choice test. User destroys window when they select. - frame3a = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") - frame3a.trinary_choice_message(message = "And all I have to do is think of her.", - one = "Don't want to leave her now", two = 'You know I believe and how', three = 'John is Dead') - print frame3a.choice.get() - sys.stdout.flush() - #progress bar queue = Queue.Queue() thread = ThreadedClient(queue) @@ -297,3 +290,10 @@ if __name__ == "__main__": frame4.progress_bar(message = "You're asking me will my love grow", size = 100, pb_queue = queue) print "frame defined" frame4.mainloop() + + #trinary choice test. User destroys window when they select. + frame3a = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") + frame3a.trinary_choice_message(message = "And all I have to do is think of her.", + one = "Don't want to leave her now", two = 'You know I believe and how', three = 'John is Dead') + print frame3a.choice.get() + sys.stdout.flush() diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index 6eaccc8b13..ecf88a1105 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -18,10 +18,19 @@ # Copyright (c) 2013, Linden Research, Inc. import argparse +import InstallerUserMessage import os import sys import subprocess -import InstallerUserMessage +import update_manager + +def after_frame(my_message, timeout = 10000): + #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds + #note that this blocks the caller for the duration of timeout + frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") + #this is done before basic_message so that we aren't blocked by mainloop() + frame.after(timout, lambda: frame._delete_window) + frame.basic_message(message = my_message) cwd = os.path.dirname(os.path.realpath(__file__)) @@ -40,21 +49,47 @@ elif sys.platform.startswith("linux"): else: #SL doesn't run on VMS or punch cards sys.exit("Unsupported platform") + +#check for an update +#TODO -#print "COYOT: executable name ", executable_name -#print "COYOT: path ", os.path.dirname(os.path.abspath(sys.argv[0])) - +#find the viewer to be lauched viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),executable_name) parser = argparse.ArgumentParser() -#parser.add_argument('--f', action='store_const', const=42) args = parser.parse_known_args(sys.argv) args_list_to_pass = args[1][1:] -args_list_to_pass.insert(0,viewer_binary) -print "COYOT: arrrrrghs to pass", args_list_to_pass - -#to prove we are launching from the script, launch a Tkinter window first -frame2 = InstallerUserMessage(title = "Second Life") -frame2.basic_message(message = viewer_binary, icon_name="head-sl-logo.gif") +#make a copy by value, not by reference +command = list(args_list_to_pass) -#viewer_process = subprocess.Popen(args_list_to_pass) +(success, state, condition) = update_manager.update_manager() +# From update_manager: +# (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) +# (False, 'download', version): we failed to download the new version +# (False, 'apply', version): we failed to apply the new version +# (True, None, None): No update found +# (True, 'in place', True): update applied in place +# (True, 'in place', path_to_new_launcher): Update applied by a new install to a new location +# (True, 'background', True): background download initiated +#These boil down three cases: +# Success is False, then pop up a message and launch the current viewer +# No update, update succeeded in place in foreground, or background update started: silently launch the current viewer channel +# Updated succeed to a different channel, launch that viewer and exit +if not success: + msg = 'Update failed in the %s process. Please check logs. Viewer will launch starting momentarily.' + after_frame(msg) + command.insert(0,viewer_binary) + viewer_process = subprocess.Popen(command) + #at the moment, we just exit here. Later, the crash monitor will be launched at this point +elif (success == True and + (state == None + or (state == 'background' and condition == True) + or (state == 'in_place' and condition == True))): + command.insert(0,viewer_binary) + viewer_process = subprocess.Popen(command) + #at the moment, we just exit here. Later, the crash monitor will be launched at this point +else: + #'condition' is the path to the new launcher. + command.insert(0,condition) + viewer_process = subprocess.Popen(command) + sys.exit(0) diff --git a/indra/viewer_components/manager/apply_update.py b/indra/viewer_components/manager/apply_update.py index 362d57c94e..643e4ad2bc 100755 --- a/indra/viewer_components/manager/apply_update.py +++ b/indra/viewer_components/manager/apply_update.py @@ -33,12 +33,15 @@ import InstallerUserMessage as IUM import os import os.path import plistlib +import re import shutil import subprocess import sys import tarfile import tempfile +#Module level variables + #fnmatch expressions LNX_REGEX = '*' + '.bz2' MAC_REGEX = '*' + '.dmg' @@ -65,6 +68,9 @@ def silent_write(log_file_handle, text): def get_filename(download_dir = None): #given a directory that supposedly has the download, find the installable + #if you are on platform X and you give the updater a directory with an installable + #for platform Y, you are either trying something fancy or get what you deserve + #or both for filename in os.listdir(download_dir): if (fnmatch.fnmatch(filename, LNX_REGEX) or fnmatch.fnmatch(filename, MAC_REGEX) @@ -77,10 +83,14 @@ def try_dismount(log_file_handle = None, installable = None, tmpdir = None): #best effort cleanup try to dismount the dmg file if we have mounted one #the French judge gave it a 5.8 try: + #use the df command to find the device name + #Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on + #/dev/disk1s2 2047936 643280 1404656 32% 80408 175582 31% /private/tmp/mnt/Second Life Installer command = ["df", os.path.join(tmpdir, "Second Life Installer")] output = subprocess.check_output(command) #first word of second line of df output is the device name mnt_dev = output.split('\n')[1].split()[0] + #do the dismount command = ["hdiutil", "detach", "-force", mnt_dev] output = subprocess.check_output(command) silent_write(log_file_handle, "hdiutil detach succeeded") @@ -88,17 +98,20 @@ def try_dismount(log_file_handle = None, installable = None, tmpdir = None): except Exception, e: silent_write(log_file_handle, "Could not detach dmg file %s. Error messages: %s" % (installable, e.message)) -def apply_update(download_dir = None, platform_key = None, log_file_handle = None): +def apply_update(download_dir = None, platform_key = None, log_file_handle = None, in_place = True): #for lnx and mac, returns path to newly installed viewer #for win, return the name of the executable #returns None on failure for all three #throws an exception if it can't find an installable at all + IN_PLACE = in_place + installable = get_filename(download_dir) if not installable: - #could not find download + #could not find the download raise ValueError("Could not find installable in " + download_dir) + #apply update using the platform specific tools if platform_key == 'lnx': installed = apply_linux_update(installable, log_file_handle) elif platform_key == 'mac': @@ -225,7 +238,17 @@ def apply_windows_update(installable = None, log_file_handle = None): silent_write(log_file_handle, "%s failed with return code %s. Error messages: %s." % (cpe.cmd, cpe.returncode, cpe.message)) return None - return installable + #Due to the black box nature of the install, we have to derive the application path from the + #name of the installable. This is essentially reverse-engineering app_name()/app_name_oneword() + #in viewer_manifest.py + #the format of the filename is: Second_Life_{Project Name}_A-B-C-XXXXXX_i686_Setup.exe + #which deploys to C:\Program Files (x86)\SecondLifeProjectName\ + #so we want all but the last four phrases and tack on Viewer if there is no project + if re.search('Project', installable): + winstall = os.path.join("C:\\Program Files (x86)\\", "".join(installable.split("_")[:-3])) + else: + winstall = os.path.join("C:\\Program Files (x86)\\", "".join(installable.split("_")[:-3])+"Viewer") + return winstall def main(): parser = argparse.ArgumentParser("Apply Downloaded Update") diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py index cd4e6680b0..23f784c6c1 100755 --- a/indra/viewer_components/manager/download_update.py +++ b/indra/viewer_components/manager/download_update.py @@ -42,9 +42,10 @@ def download_update(url = None, download_dir = None, size = None, progressbar = #download_dir to download to #total size (for progressbar) of download #progressbar: whether to display one (not used for background downloads) - #chunk_size is in bytes + #chunk_size is in bytes, amount to download at once queue = Queue.Queue() + #the url split provides the basename of the filename filename = os.path.join(download_dir, url.split('/')[-1]) req = requests.get(url, stream=True) down_thread = ThreadedDownload(req, filename, chunk_size, progressbar, queue) @@ -60,6 +61,11 @@ def download_update(url = None, download_dir = None, size = None, progressbar = class ThreadedDownload(threading.Thread): def __init__(self, req, filename, chunk_size, progressbar, in_queue): + #req is a python request object + #target filename to download to + #chunk_size is in bytes, amount to download at once + #progressbar: whether to display one (not used for background downloads) + #in_queue mediates communication between this thread and the progressbar threading.Thread.__init__(self) self.req = req self.filename = filename @@ -69,13 +75,19 @@ class ThreadedDownload(threading.Thread): def run(self): with open(self.filename, 'wb') as fd: + #keep downloading until we run out of chunks, then download the last bit for chunk in self.req.iter_content(self.chunk_size): fd.write(chunk) if self.progressbar: - self.in_queue.put(len(chunk)) + #this will increment the progress bar by len(chunk)/size units + self.in_queue.put(len(chunk)) + #signal value saying to the progress bar that it is done and can destroy itself + #if len(chunk) is ever -1, we get to file a bug against Python self.in_queue.put(-1) def main(): + #main method is for standalone use such as support and QA + #VMP will import this module and run download_update directly parser = argparse.ArgumentParser("Download URI to directory") parser.add_argument('--url', dest='url', help='URL of file to be downloaded', required=True) parser.add_argument('--dir', dest='download_dir', help='directory to be downloaded to', required=True) diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py new file mode 100755 index 0000000000..ec6df17a6c --- /dev/null +++ b/indra/viewer_components/manager/update_manager.py @@ -0,0 +1,455 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ +# Copyright (c) 2013, Linden Research, Inc. + +""" +@file update_manager.py +@author coyot +@date 2016-05-16 +""" + +from llbase import llrest +from llbase import llsd +from urlparse import urljoin + +import apply_update +import download_update +import errno +import fnmatch +import hashlib +import InstallerUserMessage +import json +import os +import platform +import re +import shutil +import subprocess +import sys +import tempfile +import thread +import urllib + +def silent_write(log_file_handle, text): + #if we have a log file, write. If not, do nothing. + #this is so we don't have to keep trapping for an exception with a None handle + #oh and because it is best effort, it is also a holey_write ;) + if (log_file_handle): + #prepend text for easy grepping + log_file_handle.write("UPDATE MANAGER: " + text + "\n") + +def after_frame(my_message, timeout = 10000): + #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds + #note that this blocks the caller for the duration of timeout + frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") + #this is done before basic_message so that we aren't blocked by mainloop() + frame.after(timout, lambda: frame._delete_window) + frame.basic_message(message = my_message) + +def convert_version_file_style(version): + #converts a version string a.b.c.d to a_b_c_d as used in downloaded filenames + #re will throw a TypeError if it gets None, just return that. + try: + pattern = re.compile('\.') + return pattern.sub('_', version) + except TypeError, te: + return None + +def get_platform_key(): + #this is the name that is inserted into the VVM URI + #and carried forward through the rest of the updater to determine + #platform specific actions as appropriate + platform_dict = {'Darwin':'mac', 'Linux':'lnx', 'Windows':'win'} + platform_uname = platform.system() + try: + return platform_dict[platform_uname] + except KeyError: + return None + +def get_summary(platform_name, launcher_path): + #get the contents of the summary.json file. + #for linux and windows, this file is in the same directory as the script + #for mac, the script is in ../Contents/MacOS/ and the file is in ../Contents/Resources/ + script_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) + if (platform_name == 'mac'): + summary_dir = os.path.abspath(os.path.join(script_dir, "../Resources")) + else: + summary_dir = script_dir + summary_file = os.path.join(summary_dir,"summary.json") + with open(summary_file) as summary_handle: + return json.load(summary_handle) + +def get_parent_path(platform_name): + #find the parent of the logs and user_settings directories + if (platform_name == 'mac'): + settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife') + elif (platform_name == 'lnx'): + settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife') + #using list format of join is important here because the Windows pathsep in a string escapes the next char + elif (platform_name == 'win'): + settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife') + else: + settings_dir = None + return settings_dir + +def make_download_dir(parent_dir, new_version): + #make a canonical download dir if it does not already exist + #format: ../user_settings/downloads/1.2.3.456789 + #we do this so that multiple viewers on the same host can update separately + #this also functions as a getter + try: + download_dir = os.path.join(parent_dir, "downloads", new_version) + os.makedirs(download_dir) + except OSError, hell: + #Directory already exists, that's okay. Other OSErrors are not okay. + if hell[0] == errno.EEXIST: + pass + else: + raise hell + return download_dir + +def check_for_completed_download(download_dir): + #there will be two files on completion, the download and a marker file called "".done"" + #for optional upgrades, there may also be a .skip file to skip this particular upgrade + #or .next to install on next run + completed = None + marker_regex = '*' + '.done' + skip_regex = '*' + '.skip' + next_regex = '*' + '.next' + for filename in os.listdir(download_dir): + if fnmatch.fnmatch(filename, marker_regex): + completed = 'done' + elif fnmatch.fnmatch(filename, skip_regex): + completed = 'skip' + elif fnmatch.fnmatch(filename, next_regex): + #so we don't skip infinitely + os.remove(filename) + completed = 'next' + if not completed: + #cleanup + shutil.rmtree(download_dir) + return completed + +def get_settings(log_file_handle, parent_dir): + #return the settings file parsed into a dict + try: + settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) + settings = llsd.parse((open(settings_file)).read()) + except llsd.LLSDParseError as lpe: + silent_write(log_file_handle, "Could not parse settings file %s" % lpe) + return None + return settings + +def get_log_file_handle(parent_dir): + #return a write handle on the log file + #plus log rotation and not dying on failure + log_file = os.path.join(parent_dir, 'update_manager.log') + old_file = log_file + '.old' + #if someone's log files are present but not writable, they've screwed up their install. + if os.access(log_file, os.W_OK): + if os.access(old_file, os.W_OK): + os.unlink(old_file) + os.rename(log_file, old_file) + elif not os.path.exists(log_file): + #reimplement TOUCH(1) in Python + #perms default to 644 which is fine + open(log_file, 'w+').close() + try: + f = open(log_file,'w+') + except Exception as e: + #we don't have a log file to write to, make a best effort and sally onward + print "Could not open update manager log file %s" % log_file + f = None + return f + +def make_VVM_UUID_hash(platform_key): + #NOTE: There is no python library support for a persistent machine specific UUID + # AND all three platforms do this a different way, so exec'ing out is really the best we can do + #Lastly, this is a best effort service. If we fail, we should still carry on with the update + uuid = None + if (platform_key == 'lnx'): + uuid = subprocess.check_output(['/usr/bin/hostid']).rstrip() + elif (platform_key == 'mac'): + #this is absurdly baroque + #/usr/sbin/system_profiler SPHardwareDataType | fgrep 'Serial' | awk '{print $NF}' + uuid = subprocess.check_output(["/usr/sbin/system_profiler", "SPHardwareDataType"]) + #findall[0] does the grep for the value we are looking for: "Serial Number (system): XXXXXXXX" + #split(:)[1] gets us the XXXXXXX part + #lstrip shaves off the leading space that was after the colon + uuid = re.split(":", re.findall('Serial Number \(system\): \S*', uuid)[0])[1].lstrip() + elif (platform_key == 'win'): + # wmic csproduct get UUID | grep -v UUID + uuid = subprocess.check_output(['wmic','csproduct','get','UUID']) + #outputs in two rows: + #UUID + #XXXXXXX-XXXX... + uuid = re.split('\n',uuid)[1].rstrip() + if uuid is not None: + return hashlib.md5(uuid).hexdigest() + else: + #fake it + return hashlib.md5(str(uuid.uuid1())).hexdigest() + +def query_vvm(log_file_handle, platform_key, settings, summary_dict): + result_data = None + #URI template /update/v1.1/channelname/version/platformkey/platformversion/willing-to-test/uniqueid + #https://wiki.lindenlab.com/wiki/Viewer_Version_Manager_REST_API#Viewer_Update_Query + base_URI = 'https://update.secondlife.com/update/' + channelname = summary_dict['Channel'] + #this is kind of a mess because the settings value a) in a map and b) is both the cohort and the version + version = summary_dict['Version'] + platform_version = platform.release() + #this will always return something usable, error handling in method + hashed_UUID = make_VVM_UUID_hash(platform_key) + #note that this will not normally be in a settings.xml file and is only here for test builds. + #for test builds, add this key to the ../user_settings/settings.xml + """ + test + + Comment + Tell update manager you aren't willing to test. + Type + String + Value + testno + + + """ + try: + test_ok = settings['test']['Value'] + except KeyError as ke: + #normal case, no testing key + test_ok = 'testok' + UUID = make_VVM_UUID_hash(platform_key) + #because urljoin can't be arsed to take multiple elements + query_string = '/v1.0/' + channelname + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID + VVMService = llrest.SimpleRESTService(name='VVM', baseurl=base_URI) + try: + result_data = VVMService.get(query_string) + except RESTError as re: + silent_write.write(log_file_handle, "Failed to query VVM using %s failed as %s" % (urljoin(base_URI,query_string, re))) + return None + return result_data + +def download(url = None, version = None, download_dir = None, size = 0, background = False): + download_tries = 0 + download_success = False + #for background execution + path_to_downloader = os.path.join(os.path.dirname(os.path.realpath(__file__)), "download_update.py") + #three strikes and you're out + while download_tries < 3 and not download_success: + #323: Check for a partial update of the required update; in either event, display an alert that a download is required, initiate the download, and then install and launch + if download_tries == 0: + after_frame(message = "Downloading new version " + version + " Please wait.") + else: + after_frame(message = "Trying again to download new version " + version + " Please wait.") + if not background: + try: + download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True) + download_success = True + except: + download_tries += 1 + silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") + else: + try: + #Python does not have a facility to multithread a method, so we make the method a standalone + #and subprocess that + subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size= %s" % (url, download_dir, size)) + download_success = True + except: + download_tries += 1 + silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") + if not download_success: + silent_write(log_file_handle, "Failed to download new version " + version) + after_frame(message = "Failed to download new version " + version + " Please check connectivity.") + return False + return True + +def install(platform_key = None, download_dir = None, log_file_handle = None, in_place = None, downloaded = None): + #user said no to this one + if downloaded != 'skip': + after_frame(message = "New version downloaded. Installing now, please wait.") + success = apply_update.apply_update(download_dir, platform_key, log_file_handle, in_place) + if success: + silent_write(log_file_handle, "successfully updated to " + version) + shutil.rmtree(download_dir) + #this is either True for in place or the path to the new install for not in place + return success + else: + after_frame(message = "Failed to apply " + version) + silent_write(log_file_handle, "Failed to update viewer to " + version) + return False + +def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None, platform_key = None, log_file_handle = None, in_place = None): + #extracted to a method because we do it twice in update_manager() and this makes the logic clearer + if not downloaded: + #do the download, exit if we fail + if not download(url = url, version = version, download_dir = download_dir, size = size): + return (False, 'download', version) + #do the install + path_to_new_launcher = install(platform_key = platform_key, download_dir = download_dir, + log_file_handle = log_file_handle, in_place = in_place, downloaded = downloaded) + if path_to_new_launcher: + #if we succeed, propagate the success type upwards + if in_place: + return (True, 'in place', True) + else: + return (True, 'in place', path_to_new_launcher) + else: + #propagate failure + return (False, 'apply', version) + +def update_manager(): + #comments that begin with '323:' are steps taken from the algorithm in the description of SL-323. + # Note that in the interest of efficiency, such as determining download success once at the top + # The code does follow precisely the same order as the algorithm. + #return values rather than exit codes. All of them are to communicate with launcher + #we print just before we return so that __main__ outputs something - returns are swallowed + # (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) + # (False, 'download', version): we failed to download the new version + # (False, 'apply', version): we failed to apply the new version + # (True, None, None): No update found + # (True, 'in place, True): update applied in place + # (True, 'in place', path_to_new_launcher): Update applied by a new install to a new location + # (True, 'background', True): background download initiated + + #setup and getting initial parameters + platform_key = get_platform_key() + parent_dir = get_parent_path(platform_key) + log_file_handle = get_log_file_handle(parent_dir) + + #check to see if user has install rights + #get the owner of the install and the current user + script_owner_id = os.stat(os.path.realpath(__file__)).st_uid + user_id = os.geteuid() + #if we are on lnx or mac, we can pretty print the IDs as names using the pwd module + #win does not provide this support and Python will throw an ImportError there, so just use raw IDs + if script_owner_id != user_id: + if platform_key != 'win': + import pwd + script_owner_name = pwd.getpwuid(script_owner_id)[0] + username = pwd.getpwuid(user_id)[0] + else: + username = user_id + script_owner_name = script_owner_id + silent_write(log_file_handle, "Upgrade notification attempted by userid " + username) + frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") + frame.binary_choice_message(message = "Second Life was installed by userid " + script_owner_name + + ". Do you have privileges to install?", true = "Yes", false = 'No') + if not frame.choice.get(): + silent_write(log_file_handle, "Upgrade attempt declined by userid " + username) + after_frame(message = "Please find a system admin to upgrade Second Life") + print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) + return (False, 'setup', None) + + settings = get_settings(log_file_handle, parent_dir) + if settings is None: + silent_write(log_file_handle, "Failed to load viewer settings") + print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) + return (False, 'setup', None) + + #323: If a complete download of that update is found, check the update preference: + #settings['UpdaterServiceSetting'] = 0 is manual install + """ + UpdaterServiceSetting + + Comment + Configure updater service. + Type + U32 + Value + 0 + + """ + try: + install_automatically = settings['UpdaterServiceSetting']['Value'] + #because, for some godforsaken reason, we delete the setting rather than changing the value + except KeyError: + install_automatically = 1 + + #get channel and version + try: + summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__))) + except: + silent_write(log_file_handle, "Could not obtain channel and version, exiting.") + print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) + return (False, 'setup', None) + + #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. + result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict) + #nothing to do or error + if not result_data: + silent_write.write(og_file_handle, "No update found.") + print "Update manager exited with (%s, %s, %s)" % (True, None, None) + return (True, None, None) + + #get download directory, if there are perm issues or similar problems, give up + try: + download_dir = make_download_dir(parent_dir, result_data['version']) + except Exception, e: + print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) + return (False, 'setup', None) + + #if the channel name of the response is the same as the channel we are launched from, the update is "in place" + #and launcher will launch the viewer in this install location. Otherwise, it will launch the Launcher from + #the new location and kill itself. + in_place = (summary_dict['Channel'] == result_data['channel']) + + #determine if we've tried this download before + downloaded = check_for_completed_download(download_dir) + + #323: If the response indicates that there is a required update: + if result_data['required'] or (not result_data['required'] and install_automatically): + #323: Check for a completed download of the required update; if found, display an alert, install the required update, and launch the newly installed viewer. + #323: If [optional download and] Install Automatically: display an alert, install the update and launch updated viewer. + return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, + size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place) + else: + #323: If the update response indicates that there is an optional update: + #323: Check to see if the optional update has already been downloaded. + #323: If a complete download of that update is found, check the update preference: + #note: automatic install handled above as the steps are the same as required upgrades + #323: If Install Manually: display a message with the update information and ask the user whether or not to install the update with three choices: + #323: Skip this update: create a marker that subsequent launches should not prompt for this update as long as it is optional, + # but leave the download in place so that if it becomes required it will be there. + #323: Install next time: create a marker that skips the prompt and installs on the next launch + #323: Install and launch now: do it. + if downloaded is not None and downloaded != 'skip': + frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") + #The choices are reordered slightly to encourage immediate install and slightly discourage skipping + frame.trinary_message(message = "Please make a selection", + one = "Install new version now.", two = 'Install the next time the viewer is launched.', three = 'Skip this update.') + choice = frame.choice.get() + if choice == 1: + return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, + size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place) + elif choice == 2: + tempfile.mkstmp(suffix = ".next", dir = download_dir) + return (True, None, None) + else: + tempfile.mkstmp(suffix = ".skip", dir = download_dir) + return (True, None, None) + else: + #multithread a download + download(url = result_data['url'], version = result_data['version'], download_dir = download_dir, size = result_data['size'], background = True) + print "Update manager exited with (%s, %s, %s)" % (True, 'background', True) + return (True, 'background', True) + + +if __name__ == '__main__': + #there is no argument parsing or other main() work to be done + update_manager() -- cgit v1.2.3 From f915751736bf2e40c56f7499d2a111f19dc7a6c0 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 11 Jul 2016 12:52:44 -0700 Subject: fix closing parens in CMakeLists.txt --- indra/newview/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9ee7b656c0..cd7c58e97b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1774,7 +1774,8 @@ if (WINDOWS) --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} ${CMAKE_SOURCE_DIR}/viewer_components/manager/SL_Launcher COMMENT "Performing pyinstaller compile of SL_Launcher" - +) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/apply_update.exe COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe @@ -1784,7 +1785,8 @@ if (WINDOWS) --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} ${CMAKE_SOURCE_DIR}/viewer_components/manager/apply_update.py COMMENT "Performing pyinstaller compile of updater" - +) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/download_update.exe COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe @@ -1794,7 +1796,8 @@ if (WINDOWS) --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} ${CMAKE_SOURCE_DIR}/viewer_components/manager/download_update.py COMMENT "Performing pyinstaller compile of update downloader" - +) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/update_manager.exe COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe -- cgit v1.2.3 From 8bf0890f212ced40e24995f8b4353c7cc7ad2053 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 11 Jul 2016 19:00:56 -0700 Subject: fix cmake add_custom_target --- indra/newview/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index cd7c58e97b..132b57eb44 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1810,9 +1810,9 @@ if (WINDOWS) ) add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/SL_Launcher.exe) -add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/apply_update.exe) -add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/download_update.exe) -add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/update_manager.exe) +add_custom_target(compile_w_viewer_updater ALL DEPENDS ${CMAKE_CFG_INTDIR}/apply_update.exe) +add_custom_target(compile_w_viewer_downloader ALL DEPENDS ${CMAKE_CFG_INTDIR}/download_update.exe) +add_custom_target(compile_w_viewer_update_manager ALL DEPENDS ${CMAKE_CFG_INTDIR}/update_manager.exe) add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat -- cgit v1.2.3 From 9c2633cba85f8dae95ff2b748a027dcfb7729848 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 13 Jul 2016 07:36:12 -0700 Subject: SL-323: adding in unit tests --- indra/viewer_components/manager/SL_Launcher | 90 +- .../manager/tests/data/settings.xml | 1184 ++++++++++++++++++++ indra/viewer_components/manager/tests/summary.json | 1 + .../manager/tests/test_InstallerError.py | 39 + .../tests/test_check_for_completed_download.py | 53 + .../tests/test_convert_version_file_style.py | 55 + .../manager/tests/test_get_filename.py | 60 + .../manager/tests/test_get_log_file_handle.py | 63 ++ .../manager/tests/test_get_parent_path.py | 79 ++ .../manager/tests/test_get_platform_key.py | 40 + .../manager/tests/test_get_settings.py | 82 ++ .../manager/tests/test_make_VVM_UUID_hash.py | 42 + .../manager/tests/test_make_download_dir.py | 42 + .../manager/tests/test_query_vvm.py | 67 ++ .../manager/tests/test_silent_write.py | 43 + .../manager/tests/test_summary.py | 39 + .../manager/tests/with_setup_args.py | 66 ++ indra/viewer_components/manager/update_manager.py | 71 +- 18 files changed, 2093 insertions(+), 23 deletions(-) create mode 100644 indra/viewer_components/manager/tests/data/settings.xml create mode 100644 indra/viewer_components/manager/tests/summary.json create mode 100644 indra/viewer_components/manager/tests/test_InstallerError.py create mode 100644 indra/viewer_components/manager/tests/test_check_for_completed_download.py create mode 100644 indra/viewer_components/manager/tests/test_convert_version_file_style.py create mode 100644 indra/viewer_components/manager/tests/test_get_filename.py create mode 100644 indra/viewer_components/manager/tests/test_get_log_file_handle.py create mode 100644 indra/viewer_components/manager/tests/test_get_parent_path.py create mode 100644 indra/viewer_components/manager/tests/test_get_platform_key.py create mode 100644 indra/viewer_components/manager/tests/test_get_settings.py create mode 100644 indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py create mode 100644 indra/viewer_components/manager/tests/test_make_download_dir.py create mode 100644 indra/viewer_components/manager/tests/test_query_vvm.py create mode 100644 indra/viewer_components/manager/tests/test_silent_write.py create mode 100644 indra/viewer_components/manager/tests/test_summary.py create mode 100644 indra/viewer_components/manager/tests/with_setup_args.py diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index ecf88a1105..1d4c19fa86 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -18,8 +18,11 @@ # Copyright (c) 2013, Linden Research, Inc. import argparse +import collections import InstallerUserMessage +import llsd import os +import platform import sys import subprocess import update_manager @@ -31,7 +34,88 @@ def after_frame(my_message, timeout = 10000): #this is done before basic_message so that we aren't blocked by mainloop() frame.after(timout, lambda: frame._delete_window) frame.basic_message(message = my_message) + +def get_cmd_line(): + platform_name = platform.system() + #find the parent of the logs and user_settings directories + if (platform_name == 'mac'): + settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'Resources/app_settings/cmd_line.xml') + elif (platform_name == 'lnx'): + settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') + #using list format of join is important here because the Windows pathsep in a string escapes the next char + elif (platform_name == 'win'): + settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') + else: + settings_dir = None + + try: + cmd_line = llsd.parse((open(settings_file)).read()) + except: + cmd_line = None + + return cmd_line + +def get_settings(): + #return the settings file parsed into a dict + try: + settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) + settings = llsd.parse((open(settings_file)).read()) + except llsd.LLSDParseError as lpe: + silent_write(log_file_handle, "Could not parse settings file %s" % lpe) + return None + return settings + +def capture_vmp_args(arg_list = None, cmd_line = None): + #expected input format: arg_list = ['--set', 'foo', 'bar', '-X', '-Y', 'qux'] + #take a copy of the viewer parameters that are of interest to VMP. + #the regex for a parameter is -- {opt1} {opt2} + cli_overrides = {} + cmd_line = get_cmd_line() + + vmp_params = {'--channel':'channel', '--settings':'settings', '--update-service':'update-service', '--set':'set'} + #the settings set with --set. All such settings have only one argument. + vmp_setters = ('UpdaterMaximumBandwidth', 'UpdaterServiceCheckPeriod', 'UpdaterServicePath', 'UpdaterServiceSetting', 'UpdaterServiceURL', 'UpdaterWillingToTest') + + #Here turn the list into a queue, popping off the left as we go. Note that deque() makes a copy by value, not by reference + #Because of the complexity introduced by the uncertainty of how many options a parameter can take, this is far less complicated code than the more + #pythonic (x,y) = since we will sometimes have (x), sometimes (x,y) and sometimes (x,y,z) + #also, because the pop is destructive, we prevent ourselves from iterating back over list elements that iterator methods would peek ahead at + vmp_queue = collections.deque(arg_list) + while (len(vmp_queue)): + param = vmp_queue.popleft() + #if it is not one of ours, pop through args until we get to the next parameter + if param in vmp_params.keys(): + if param == '--set': + setting_name = vmp_queue.popleft() + setting_value = vmp_queue.popleft() + if setting_name in vmp_setters: + cli_overrides[vmp_params[param]] = (setting_name, setting_value) + else: + #find out how many args this parameter has + count = cmd_line[param]['count'] + param_args = [] + if count: + for argh in range(1,count): + param_args.append(vmp_queue.popleft()) + #the parameter name is the key, the (possibly empty) list of args is the value + cli_overrides[vmp_params[param]] = param_args + + #to prevent KeyErrors on missing keys, set the remainder to None + for key in vmp_params: + if key != '--set': + try: + cli_overrides[key] + except KeyError: + cli_overrides[key] = None + else: + for arg in vmp_setters: + try: + cli_overrides[key][arg] + except KeyError: + cli_overrides[key][arg] = None + return cli_overrides + cwd = os.path.dirname(os.path.realpath(__file__)) executable_name = "" @@ -58,11 +142,15 @@ viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),execu parser = argparse.ArgumentParser() args = parser.parse_known_args(sys.argv) +print args[1] +sys.exit() +#args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname args_list_to_pass = args[1][1:] +vmp_args = capture_vmp_args(args_list_to_pass) #make a copy by value, not by reference command = list(args_list_to_pass) -(success, state, condition) = update_manager.update_manager() +(success, state, condition) = update_manager.update_manager(cli_overrides) # From update_manager: # (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) # (False, 'download', version): we failed to download the new version diff --git a/indra/viewer_components/manager/tests/data/settings.xml b/indra/viewer_components/manager/tests/data/settings.xml new file mode 100644 index 0000000000..07e420dcb3 --- /dev/null +++ b/indra/viewer_components/manager/tests/data/settings.xml @@ -0,0 +1,1184 @@ + + + AllowMultipleViewers + + Comment + Allow multiple viewers. + Type + Boolean + Value + 1 + + AllowTapTapHoldRun + + Comment + Tapping a direction key twice and holding it down makes avatar run + Type + Boolean + Value + 0 + + AppearanceCameraMovement + + Comment + When entering appearance editing mode, camera zooms in on currently selected portion of avatar + Type + Boolean + Value + 0 + + AudioLevelMedia + + Comment + Audio level of Quicktime movies + Type + F32 + Value + 0.699999988079071044921875 + + AudioLevelMic + + Comment + Audio level of microphone input + Type + F32 + Value + 0.1749999970197677612304688 + + AudioLevelMusic + + Comment + Audio level of streaming music + Type + F32 + Value + 0 + + AudioLevelSFX + + Comment + Audio level of in-world sound effects + Type + F32 + Value + 0.699999988079071044921875 + + AudioLevelVoice + + Comment + Audio level of voice chat + Type + F32 + Value + 1 + + AudioStreamingMedia + + Comment + Enable streaming + Type + Boolean + Value + 0 + + AvatarAxisDeadZone0 + + Comment + Avatar axis 0 dead zone. + Type + F32 + Value + 0.1000000014901161193847656 + + AvatarAxisDeadZone1 + + Comment + Avatar axis 1 dead zone. + Type + F32 + Value + 0.1000000014901161193847656 + + AvatarAxisDeadZone2 + + Comment + Avatar axis 2 dead zone. + Type + F32 + Value + 0.1000000014901161193847656 + + AvatarAxisDeadZone3 + + Comment + Avatar axis 3 dead zone. + Type + F32 + Value + 1 + + AvatarAxisDeadZone4 + + Comment + Avatar axis 4 dead zone. + Type + F32 + Value + 0.01999999955296516418457031 + + AvatarAxisDeadZone5 + + Comment + Avatar axis 5 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + AvatarAxisScale3 + + Comment + Avatar axis 3 scaler. + Type + F32 + Value + 0 + + AvatarAxisScale4 + + Comment + Avatar axis 4 scaler. + Type + F32 + Value + 2 + + AvatarAxisScale5 + + Comment + Avatar axis 5 scaler. + Type + F32 + Value + 2 + + AvatarFeathering + + Comment + Avatar feathering (less is softer) + Type + F32 + Value + 6 + + AvatarFileName + + Comment + Alternative avatar file name + Type + String + Value + avatar_lad_tentacles.xml + + BuildAxisDeadZone0 + + Comment + Build axis 0 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone1 + + Comment + Build axis 1 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone2 + + Comment + Build axis 2 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone3 + + Comment + Build axis 3 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone4 + + Comment + Build axis 4 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisDeadZone5 + + Comment + Build axis 5 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + BuildAxisScale0 + + Comment + Build axis 0 scaler. + Type + F32 + Value + 6 + + BuildAxisScale1 + + Comment + Build axis 1 scaler. + Type + F32 + Value + 6 + + BuildAxisScale2 + + Comment + Build axis 2 scaler. + Type + F32 + Value + 6 + + BuildAxisScale3 + + Comment + Build axis 3 scaler. + Type + F32 + Value + 6 + + BuildAxisScale4 + + Comment + Build axis 4 scaler. + Type + F32 + Value + 6 + + BuildAxisScale5 + + Comment + Build axis 5 scaler. + Type + F32 + Value + 6 + + BuildFeathering + + Comment + Build feathering (less is softer) + Type + F32 + Value + 12 + + BulkChangeEveryoneCopy + + Comment + Bulk changed objects can be copied by everyone + Type + Boolean + Value + 1 + + BulkChangeNextOwnerCopy + + Comment + Bulk changed objects can be copied by next owner + Type + Boolean + Value + 1 + + BulkChangeNextOwnerModify + + Comment + Bulk changed objects can be modified by next owner + Type + Boolean + Value + 1 + + BulkChangeShareWithGroup + + Comment + Bulk changed objects are shared with the currently active group + Type + Boolean + Value + 1 + + CacheValidateCounter + + Comment + Used to distribute cache validation + Type + U32 + Value + 122 + + CameraPosOnLogout + + Comment + Camera position when last logged out (global coordinates) + Type + Vector3D + Value + + 288290.4477181434631347656 + 275988.5277819633483886719 + 49.10921102762222290039062 + + + ClickToWalk + + Comment + Click in world to walk to location + Type + Boolean + Value + 0 + + ConversationSortOrder + + Comment + Specifies sort key for conversations + Type + U32 + Value + 0 + + CurrentGrid + + Comment + Currently Selected Grid + Type + String + Value + util.agni.lindenlab.com + + Cursor3D + + Comment + Treat Joystick values as absolute positions (not deltas). + Type + Boolean + Value + 0 + + FirstLoginThisInstall + + Comment + Specifies that you have not logged in with the viewer since you performed a clean install + Type + Boolean + Value + 0 + + FirstRunThisInstall + + Comment + Specifies that you have not run the viewer since you performed a clean install + Type + Boolean + Value + 0 + + FlycamAxisDeadZone0 + + Comment + Flycam axis 0 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone1 + + Comment + Flycam axis 1 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone2 + + Comment + Flycam axis 2 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone3 + + Comment + Flycam axis 3 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone4 + + Comment + Flycam axis 4 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone5 + + Comment + Flycam axis 5 dead zone. + Type + F32 + Value + 0.009999999776482582092285156 + + FlycamAxisDeadZone6 + + Comment + Flycam axis 6 dead zone. + Type + F32 + Value + 1 + + FlycamAxisScale0 + + Comment + Flycam axis 0 scaler. + Type + F32 + Value + 42 + + FlycamAxisScale1 + + Comment + Flycam axis 1 scaler. + Type + F32 + Value + 40 + + FlycamAxisScale2 + + Comment + Flycam axis 2 scaler. + Type + F32 + Value + 40 + + FlycamAxisScale3 + + Comment + Flycam axis 3 scaler. + Type + F32 + Value + 0 + + FlycamAxisScale4 + + Comment + Flycam axis 4 scaler. + Type + F32 + Value + 2 + + FlycamAxisScale5 + + Comment + Flycam axis 5 scaler. + Type + F32 + Value + 3 + + FlycamAxisScale6 + + Comment + Flycam axis 6 scaler. + Type + F32 + Value + 0 + + FlycamFeathering + + Comment + Flycam feathering (less is softer) + Type + F32 + Value + 5 + + FocusPosOnLogout + + Comment + Camera focus point when last logged out (global coordinates) + Type + Vector3D + Value + + 288287.8830481640761718154 + 275991.5973855691263452172 + 47.96361158013021963597566 + + + ForceShowGrid + + Comment + Always show grid dropdown on login screen + Type + Boolean + Value + 1 + + HttpProxyType + + Comment + Proxy type to use for HTTP operations + Type + String + Value + None + + JoystickInitialized + + Comment + Whether or not a joystick has been detected and initiailized. + Type + String + Value + UnknownDevice + + LSLFindCaseInsensitivity + + Comment + Use case insensitivity when searching in LSL editor + Type + Boolean + Value + 1 + + LastFeatureVersion + + Comment + [DO NOT MODIFY] Feature Table Version number for tracking rendering system changes + Type + S32 + Value + 37 + + LastGPUString + + Comment + [DO NOT MODIFY] previous GPU id string for tracking hardware changes + Type + String + Value + NVIDIA Corporation NVIDIA GeForce GT 750M OpenGL Engine + + LastPrefTab + + Comment + Last selected tab in preferences window + Type + S32 + Value + 1 + + LastRunVersion + + Comment + Version number of last instance of the viewer that you ran + Type + String + Value + Second Life Project Bento 5.0.0.315657 + + LocalCacheVersion + + Comment + Version number of cache + Type + S32 + Value + 7 + + LoginLocation + + Comment + Default Login location ('last', 'home') preference + Type + String + Value + home + + MapScale + + Comment + World map zoom level (pixels per region) + Type + F32 + Value + 256 + + MaxJointsPerMeshObject + + Comment + Maximum joints per rigged mesh object + Type + U32 + Value + 51 + + MediaEnablePopups + + Comment + If true, enable targeted links and javascript in media to open new media browser windows without a prompt. + Type + Boolean + Value + 1 + + MediaShowOnOthers + + Comment + Whether or not to show media on other avatars + Type + Boolean + Value + 1 + + MigrateCacheDirectory + + Comment + Check for old version of disk cache to migrate to current location + Type + Boolean + Value + 0 + + NavBarShowParcelProperties + + Comment + Show parcel property icons in navigation bar + Type + Boolean + Value + 0 + + NextLoginLocation + + Comment + Location to log into for this session - set from command line or the login panel, cleared following a successfull login. + Type + String + Value + home + + NotificationConferenceIMOptions + + Comment + + Specifies how the UI responds to Conference IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + none + + NotificationFriendIMOptions + + Comment + + Specifies how the UI responds to Friend IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + openconversations + + NotificationGroupChatOptions + + Comment + + Specifies how the UI responds to Group Chat Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + none + + NotificationNearbyChatOptions + + Comment + + Specifies how the UI responds to Nearby Chat Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + none + + NotificationNonFriendIMOptions + + Comment + + Specifies how the UI responds to Non Friend IM Notifications. + Allowed values: [openconversations,toast,flash,noaction] + + Type + String + Value + openconversations + + NumSessions + + Comment + Number of successful logins to Second Life + Type + S32 + Value + 1674 + + PlayTypingAnim + + Comment + Your avatar plays the typing animation whenever you type in the chat bar + Type + Boolean + Value + 0 + + PoolSizeExpCache + + Comment + Coroutine Pool size for ExpCache + Type + U32 + Value + 5 + + PreferredBrowserBehavior + + Comment + Use system browser for any links (0), use builtin browser for SL links and system one for others (1) or use builtin browser only (2). + Type + U32 + Value + 0 + + PreferredMaturity + + Comment + Setting for the user's preferred maturity level (consts in indra_constants.h) + Type + U32 + Value + 42 + + PresetGraphicActive + + Comment + Name of currently selected preference + Type + String + Value + Default + + ProbeHardwareOnStartup + + Comment + Query current hardware configuration on application startup + Type + Boolean + Value + 0 + + QAMode + + Comment + Enable Testing Features. + Type + Boolean + Value + 1 + + RenderAnisotropic + + Comment + Render textures using anisotropic filtering + Type + Boolean + Value + 1 + + RenderAvatarCloth + + Comment + Controls if avatars use wavy cloth + Type + Boolean + Value + 0 + + RenderAvatarLODFactor + + Comment + Controls level of detail of avatars (multiplier for current screen area when calculated level of detail) + Type + F32 + Value + 1 + + RenderFSAASamples + + Comment + Number of samples to use for FSAA (0 = no AA). + Type + U32 + Value + 2 + + RenderFarClip + + Comment + Distance of far clip plane from camera (meters) + Type + F32 + Value + 128 + + RenderMaxPartCount + + Comment + Maximum number of particles to display on screen + Type + S32 + Value + 2048 + + RenderQualityPerformance + + Comment + Which graphics settings you've chosen + Type + U32 + Value + 4 + + RenderReflectionDetail + + Comment + Detail of reflection render pass. + Type + S32 + Value + 0 + + RenderTerrainLODFactor + + Comment + Controls level of detail of terrain (multiplier for current screen area when calculated level of detail) + Type + F32 + Value + 2 + + RenderVBOEnable + + Comment + Use GL Vertex Buffer Objects + Type + Boolean + Value + 0 + + RenderVolumeLODFactor + + Comment + Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) + Type + F32 + Value + 1.125 + + ShowAdvancedGraphicsSettings + + Comment + Show advanced graphics settings + Type + Boolean + Value + 1 + + ShowBanLines + + Comment + Show in-world ban/access borders + Type + Boolean + Value + 0 + + ShowStartLocation + + Comment + Display starting location menu on login screen + Type + Boolean + Value + 1 + + SkeletonFileName + + Comment + Alternative skeleton file name + Type + String + Value + avatar_skeleton_tentacles.xml + + SkyPresetName + + Comment + Sky preset to use. May be superseded by region settings or by a day cycle (see DayCycleName). + Type + String + Value + Sunset + + SnapshotConfigURL + + Comment + URL to fetch Snapshot Sharing configuration data from. + Type + String + Value + http://photos.apps.avatarsunited.com/viewer_config + + SnapshotFormat + + Comment + Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP) + Type + S32 + Value + 1 + + SnapshotQuality + + Comment + Quality setting of postcard JPEGs (0 = worst, 100 = best) + Type + S32 + Value + 100 + + SpellCheck + + Comment + Enable spellchecking on line and text editors + Type + Boolean + Value + 0 + + SpellCheckDictionary + + Comment + Current primary and secondary dictionaries used for spell checking + Type + String + Value + English (United States) + + TextureMemory + + Comment + Amount of memory to use for textures in MB (0 = autodetect) + Type + S32 + Value + 256 + + UseDayCycle + + Comment + Whether to use use a day cycle or a fixed sky. + Type + Boolean + Value + 0 + + UseDebugMenus + + Comment + Turns on "Debug" menu + Type + Boolean + Value + 1 + + UseEnvironmentFromRegion + + Comment + Choose whether to use the region's environment settings, or override them with the local settings. + Type + Boolean + Value + 0 + + VFSOldSize + + Comment + [DO NOT MODIFY] Controls resizing of local file cache + Type + U32 + Value + 102 + + VFSSalt + + Comment + [DO NOT MODIFY] Controls local file caching behavior + Type + U32 + Value + 260093998 + + VersionChannelName + + Comment + Version information generated by running the viewer + Type + String + Value + Second Life Release + + VertexShaderEnable + + Comment + Enable/disable all GLSL shaders (debug) + Type + Boolean + Value + 1 + + VoiceInputAudioDevice + + Comment + Audio input device to use for voice + Type + String + Value + C-Media USB Audio Device + + VoiceOutputAudioDevice + + Comment + Audio output device to use for voice + Type + String + Value + C-Media USB Audio Device + + WLSkyDetail + + Comment + Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies. + Type + U32 + Value + 48 + + WebProfileFloaterRect + + Comment + Web profile floater dimensions + Type + Rect + Value + + 1189 + 957 + 1674 + 277 + + + WindowHeight + + Comment + SL viewer window height + Type + U32 + Value + 1000 + + WindowWidth + + Comment + SL viewer window width + Type + U32 + Value + 1626 + + WindowX + + Comment + X coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) + Type + S32 + Value + 50 + + WindowY + + Comment + Y coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) + Type + S32 + Value + 50 + + + diff --git a/indra/viewer_components/manager/tests/summary.json b/indra/viewer_components/manager/tests/summary.json new file mode 100644 index 0000000000..b78859d427 --- /dev/null +++ b/indra/viewer_components/manager/tests/summary.json @@ -0,0 +1 @@ +{"Type":"viewer","Version":"4.0.5.315117","Channel":"Second Life Release"} diff --git a/indra/viewer_components/manager/tests/test_InstallerError.py b/indra/viewer_components/manager/tests/test_InstallerError.py new file mode 100644 index 0000000000..d722208b7f --- /dev/null +++ b/indra/viewer_components/manager/tests/test_InstallerError.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_InstallerError.py +@author coyot +@date 2016-06-01 +""" + +from nose.tools import assert_equal + +import InstallerError +import os + +def test_InstallerError(): + try: + #try to make our own homedir, this will fail on all three platforms + homedir = os.path.abspath(os.path.expanduser('~')) + os.mkdir(homedir) + except OSError, oe: + ie = InstallerError.InstallerError(oe, "Installer failed to create a homedir that already exists.") + + assert_equal( str(ie), + "[Errno [Errno 17] File exists: '%s'] Installer failed to create a homedir that already exists." % homedir) diff --git a/indra/viewer_components/manager/tests/test_check_for_completed_download.py b/indra/viewer_components/manager/tests/test_check_for_completed_download.py new file mode 100644 index 0000000000..388bc900e9 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_check_for_completed_download.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_check_for_completed_download.py +@author coyot +@date 2016-06-03 +""" + +from nose.tools import * +from nose import with_setup + +import os +import shutil +import tempfile +import update_manager +import with_setup_args + +def check_for_completed_download_setup(): + tmpdir1 = tempfile.mkdtemp(prefix = 'test1') + tmpdir2 = tempfile.mkdtemp(prefix = 'test2') + tempfile.mkstemp(suffix = '.done', dir = tmpdir1) + + return [tmpdir1,tmpdir2], {} + +def check_for_completed_download_teardown(tmpdir1,tmpdir2): + shutil.rmtree(tmpdir1, ignore_errors = True) + shutil.rmtree(tmpdir2, ignore_errors = True) + +@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown) +def test_completed_check_for_completed_download(tmpdir1,tmpdir2): + assert_equal(update_manager.check_for_completed_download(tmpdir1), 'done'), "Failed to find completion marker" + +@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown) +def test_incomplete_check_for_completed_download(tmpdir1,tmpdir2): + #should return False + incomplete = not update_manager.check_for_completed_download(tmpdir2) + assert incomplete, "False positive, should not mark complete without a marker" \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_convert_version_file_style.py b/indra/viewer_components/manager/tests/test_convert_version_file_style.py new file mode 100644 index 0000000000..d700f91b84 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_convert_version_file_style.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_convert_version_file_style.py +@author coyot +@date 2016-06-01 +""" + +from nose.tools import assert_equal + +import update_manager + +def test_normal_form(): + version = '1.2.3.456789' + golden = '1_2_3_456789' + converted = update_manager.convert_version_file_style(version) + + assert_equal(golden, converted) + +def test_short_form(): + version = '1.23' + golden = '1_23' + converted = update_manager.convert_version_file_style(version) + + assert_equal(golden, converted) + +def test_idempotent(): + version = '123' + golden = '123' + converted = update_manager.convert_version_file_style(version) + + assert_equal(golden, converted) + +def test_none(): + version = None + golden = None + converted = update_manager.convert_version_file_style(version) + + assert_equal(golden, converted) \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_filename.py b/indra/viewer_components/manager/tests/test_get_filename.py new file mode 100644 index 0000000000..95771d75dc --- /dev/null +++ b/indra/viewer_components/manager/tests/test_get_filename.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_get_filename.py +@author coyot +@date 2016-06-30 +""" + +from nose.tools import * +from nose import with_setup + +import os +import shutil +import tempfile +import apply_update +import with_setup_args + +def get_filename_setup(): + tmpdir1 = tempfile.mkdtemp(prefix = 'lnx') + tmpdir2 = tempfile.mkdtemp(prefix = 'mac') + tmpdir3 = tempfile.mkdtemp(prefix = 'win') + tmpdir4 = tempfile.mkdtemp(prefix = 'bad') + tempfile.mkstemp(suffix = '.bz2', dir = tmpdir1) + tempfile.mkstemp(suffix = '.dmg', dir = tmpdir2) + tempfile.mkstemp(suffix = '.exe', dir = tmpdir3) + + return [tmpdir1,tmpdir2, tmpdir3, tmpdir4], {} + +def get_filename_teardown(tmpdir1,tmpdir2, tmpdir3, tmpdir4): + shutil.rmtree(tmpdir1, ignore_errors = True) + shutil.rmtree(tmpdir2, ignore_errors = True) + shutil.rmtree(tmpdir3, ignore_errors = True) + shutil.rmtree(tmpdir4, ignore_errors = True) + +@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) +def test_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): + assert_is_not_none(apply_update.get_filename(tmpdir1)), "Failed to find installable" + assert_is_not_none(apply_update.get_filename(tmpdir2)), "Failed to find installable" + assert_is_not_none(apply_update.get_filename(tmpdir3)), "Failed to find installable" + +@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) +def test_missing_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): + not_found = not apply_update.get_filename(tmpdir4) + assert not_found, "False positive, should not find an installable in an empty dir" \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_log_file_handle.py b/indra/viewer_components/manager/tests/test_get_log_file_handle.py new file mode 100644 index 0000000000..c5b3c89550 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_get_log_file_handle.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_get_log_file_handle.py +@author coyot +@date 2016-06-08 +""" + +from nose.tools import * + +import os +import shutil +import tempfile +import update_manager +import with_setup_args + +def get_log_file_handle_setup(): + tmpdir1 = tempfile.mkdtemp(prefix = 'test1') + tmpdir2 = tempfile.mkdtemp(prefix = 'test2') + log_file_path = os.path.abspath(os.path.join(tmpdir1,"update_manager.log")) + #not using tempfile because we want a particular filename + open(log_file_path, 'w+').close + + return [tmpdir1,tmpdir2,log_file_path], {} + +def get_log_file_handle_teardown(tmpdir1,tmpdir2,log_file_path): + shutil.rmtree(tmpdir1, ignore_errors = True) + shutil.rmtree(tmpdir2, ignore_errors = True) + +@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown) +def test_existing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): + handle = update_manager.get_log_file_handle(tmpdir1) + if not handle: + print "Failed to find existing log file" + assert False + elif not os.path.exists(os.path.abspath(log_file_path+".old")): + print "Failed to rotate update manager log" + assert False + assert True + +@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown) +def test_missing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): + handle = update_manager.get_log_file_handle(tmpdir2) + if not os.path.exists(log_file_path): + print "Failed to touch new log file" + assert False + assert True \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_parent_path.py b/indra/viewer_components/manager/tests/test_get_parent_path.py new file mode 100644 index 0000000000..3cfd72310e --- /dev/null +++ b/indra/viewer_components/manager/tests/test_get_parent_path.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_get_parent_path.py +@author coyot +@date 2016-06-02 +""" + +from nose.tools import * +from nose import with_setup + +import os +import shutil +import update_manager +import with_setup_args + +def get_parent_path_setup(): + key = update_manager.get_platform_key() + try: + if key == 'mac': + settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife') + elif key == 'lnx': + settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife') + elif key == 'win': + settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife') + else: + raise Exception("Invalid Platform Key") + + #preserve existing settings dir if any + if os.path.exists(settings_dir): + old_dir = settings_dir + ".tmp" + if os.path.exists(old_dir): + shutil.rmtree(old_dir, ignore_errors = True) + os.rename(settings_dir, old_dir) + os.makedirs(settings_dir) + except Exception, e: + print "get_parent_path_setup failed due to: %s" % str(e) + assert False + + #this is we don't have to rediscover settings_dir for test and teardown + return [settings_dir], {} + +def get_parent_path_teardown(settings_dir): + try: + shutil.rmtree(settings_dir, ignore_errors = True) + #restore previous settings dir if any + old_dir = settings_dir + ".tmp" + if os.path.exists(old_dir): + os.rename(old_dir, settings_dir) + except: + #cleanup is best effort + pass + +@with_setup_args.with_setup_args(get_parent_path_setup, get_parent_path_teardown) +def test_get_parent_path(settings_dir): + key = update_manager.get_platform_key() + got_settings_dir = update_manager.get_parent_path(key) + + assert settings_dir, "test_get_parent_path failed to obtain parent path" + + assert_equal(settings_dir, got_settings_dir) + + \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_platform_key.py b/indra/viewer_components/manager/tests/test_get_platform_key.py new file mode 100644 index 0000000000..37c570532c --- /dev/null +++ b/indra/viewer_components/manager/tests/test_get_platform_key.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_get_platform_key.py +@author coyot +@date 2016-06-01 +""" + +from nose.tools import assert_equal + +import platform +import update_manager + +def test_get_platform_key(): + key = update_manager.get_platform_key() + if key == 'mac': + assert_equal(platform.system(),'Darwin') + elif key == 'lnx': + assert_equal(platform.system(),'Linux') + elif key == 'win': + assert_equal(platform.system(),'Windows') + else: + assert_equal(key, None) + \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_settings.py b/indra/viewer_components/manager/tests/test_get_settings.py new file mode 100644 index 0000000000..46b779cbd5 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_get_settings.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_get_settings.py +@author coyot +@date 2016-06-03 +""" + +from nose.tools import * +from nose import with_setup + +import os +import shutil +import update_manager +import with_setup_args + +def get_settings_setup(): + try: + key = update_manager.get_platform_key() + settings_dir = os.path.join(update_manager.get_parent_path(key), "user_settings") + print settings_dir + + #preserve existing settings dir if any + if os.path.exists(settings_dir): + old_dir = settings_dir + ".tmp" + if os.path.exists(old_dir): + shutil.rmtree(old_dir, ignore_errors = True) + os.rename(settings_dir, old_dir) + os.makedirs(settings_dir) + + #the data subdir of the tests dir that this script is in + data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data") + #the test settings file + settings_file = os.path.join(data_dir, "settings.xml") + shutil.copyfile(settings_file, os.path.join(settings_dir, "settings.xml")) + + except Exception, e: + print "get_settings_setup failed due to: %s" % str(e) + assert False + + #this is we don't have to rediscover settings_dir for test and teardown + return [settings_dir], {} + +def get_settings_teardown(settings_dir): + try: + shutil.rmtree(settings_dir, ignore_errors = True) + #restore previous settings dir if any + old_dir = settings_dir + ".tmp" + if os.path.exists(old_dir): + os.rename(old_dir, settings_dir) + except: + #cleanup is best effort + pass + +@with_setup_args.with_setup_args(get_settings_setup, get_settings_teardown) +def test_get_settings(settings_dir): + key = update_manager.get_platform_key() + parent = update_manager.get_parent_path(key) + log_file = update_manager.get_log_file_handle(parent) + got_settings = update_manager.get_settings(log_file, parent) + + assert got_settings, "test_get_settings failed to find a settings.xml file" + + #test one key just to make sure it parsed + assert_equal(got_settings['CurrentGrid']['Value'], 'util.agni.lindenlab.com') + diff --git a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py new file mode 100644 index 0000000000..513502a6ca --- /dev/null +++ b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_make_VVM_UUID_hash.py +@author coyot +@date 2016-06-03 +""" + +from nose.tools import * + +import update_manager + +def test_make_VVM_UUID_hash(): + #because the method returns different results on different hosts + #it is not easy to unit test it reliably. + #About the best we can do is check for the exception from subprocess + key = update_manager.get_platform_key() + try: + UUID_hash = update_manager.make_VVM_UUID_hash(key) + except Exception, e: + print "Test failed due to: %s" % str(e) + assert False + + #make_UUID_hash returned None + assert UUID_hash, "make_UUID_hash failed to make a hash." + diff --git a/indra/viewer_components/manager/tests/test_make_download_dir.py b/indra/viewer_components/manager/tests/test_make_download_dir.py new file mode 100644 index 0000000000..5198a6de05 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_make_download_dir.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_make_download_dir.py +@author coyot +@date 2016-06-03 +""" + +from nose.tools import * + +import update_manager + +def test_make_download_dir(): + key = update_manager.get_platform_key() + path = update_manager.get_parent_path(key) + version = '1.2.3.456789' + try: + download_dir = update_manager.make_download_dir(path, version) + except OSError, e: + print "make_download_dir failed to eat OSError %s" % str(e) + assert False + except Exception, e: + print "make_download_dir raised an unexpected exception %s" % str(e) + assert False + + assert download_dir, "make_download_dir returned None for path %s and version %s" % (path, version) \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_query_vvm.py b/indra/viewer_components/manager/tests/test_query_vvm.py new file mode 100644 index 0000000000..40a0f7b215 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_query_vvm.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_query_vvm.py +@author coyot +@date 2016-06-08 +""" + +from nose.tools import * + +import os +import re +import shutil +import tempfile +import update_manager +import with_setup_args + +def query_vvm_setup(): + tmpdir1 = tempfile.mkdtemp(prefix = 'test1') + handle = update_manager.get_log_file_handle(tmpdir1) + + return [tmpdir1,handle], {} + +def query_vvm_teardown(tmpdir1, handle): + shutil.rmtree(tmpdir1, ignore_errors = True) + +@with_setup_args.with_setup_args(query_vvm_setup, query_vvm_teardown) +def test_query_vvm(tmpdir1, handle): + key = update_manager.get_platform_key() + parent = update_manager.get_parent_path(key) + settings = update_manager.get_settings(handle, parent) + launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__)))) + summary = update_manager.get_summary(key, launcher_path) + + #for unit testing purposes, just testing a value from results. If no update, then None and it falls through + #for formal QA see: + # https://docs.google.com/document/d/1WNjOPdKlq0j_7s7gdNe_3QlyGnQDa3bFNvtyVM6Hx8M/edit + # https://wiki.lindenlab.com/wiki/Login_Test#Test_Viewer_Updater + #for test plans on all cases, as it requires setting up a fake VVM service + + try: + results = update_manager.query_vvm(handle, key, settings, summary) + except Exception, e: + print "query_vvm threw unexpected exception %s" % str(e) + assert False + + if results: + pattern = re.compile('Second Life') + assert pattern.search(results['channel']), "Bad results returned %s" % str(results) + + assert True diff --git a/indra/viewer_components/manager/tests/test_silent_write.py b/indra/viewer_components/manager/tests/test_silent_write.py new file mode 100644 index 0000000000..a55665799f --- /dev/null +++ b/indra/viewer_components/manager/tests/test_silent_write.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_silent_write.py +@author coyot +@date 2016-06-02 +""" + +from nose.tools import * + +import tempfile +import update_manager + +def test_silent_write_to_file(): + test_log = tempfile.TemporaryFile() + try: + update_manager.silent_write(test_log, "This is a test.") + except Exception, e: + print "Test failed due to: %s" % str(e) + assert False + +def test_silent_write_to_null(): + try: + update_manager.silent_write(None, "This is a test.") + except Exception, e: + print "Test failed due to: %s" % str(e) + assert False \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_summary.py b/indra/viewer_components/manager/tests/test_summary.py new file mode 100644 index 0000000000..b318012b54 --- /dev/null +++ b/indra/viewer_components/manager/tests/test_summary.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ + +""" +@file test_summary.py +@author coyot +@date 2016-06-02 +""" + +from nose.tools import * + +import os.path +import tempfile +import update_manager + +def test_get_summary(): + key = update_manager.get_platform_key() + #launcher is one dir above tests + launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__)))) + summary_json = update_manager.get_summary(key, launcher_path) + + #we aren't testing the JSON library, one key pair is enough + #so we will use the one pair that is actually a constant + assert_equal(summary_json['Type'],'viewer') \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/with_setup_args.py b/indra/viewer_components/manager/tests/with_setup_args.py new file mode 100644 index 0000000000..38350ab8c4 --- /dev/null +++ b/indra/viewer_components/manager/tests/with_setup_args.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# $LicenseInfo:firstyear=2016&license=internal$ +# +# Copyright (c) 2016, Linden Research, Inc. +# +# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of +# this source code is governed by the Linden Lab Source Code Disclosure +# Agreement ("Agreement") previously entered between you and Linden +# Lab. By accessing, using, copying, modifying or distributing this +# software, you acknowledge that you have been informed of your +# obligations under the Agreement and agree to abide by those obligations. +# +# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +# COMPLETENESS OR PERFORMANCE. +# $/LicenseInfo$ +# +# Taken from: https://gist.github.com/garyvdm/392ae20c673c7ee58d76 + +""" +@file with_setup_args.py +@author garyvdm +@date 2016-06-02 +""" + + +def with_setup_args(setup, teardown=None): + """Decorator to add setup and/or teardown methods to a test function:: + @with_setup_args(setup, teardown) + def test_something(): + " ... " + The setup function should return (args, kwargs) which will be passed to + test function, and teardown function. + Note that `with_setup_args` is useful *only* for test functions, not for test + methods or inside of TestCase subclasses. + """ + def decorate(func): + args = [] + kwargs = {} + + def test_wrapped(): + func(*args, **kwargs) + + test_wrapped.__name__ = func.__name__ + + def setup_wrapped(): + a, k = setup() + args.extend(a) + kwargs.update(k) + if hasattr(func, 'setup'): + func.setup() + test_wrapped.setup = setup_wrapped + + if teardown: + def teardown_wrapped(): + if hasattr(func, 'teardown'): + func.teardown() + teardown(*args, **kwargs) + + test_wrapped.teardown = teardown_wrapped + else: + if hasattr(func, 'teardown'): + test_wrapped.teardown = func.teardown() + return test_wrapped + return decorate \ No newline at end of file diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index ec6df17a6c..a7e0a19aef 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -204,11 +204,14 @@ def make_VVM_UUID_hash(platform_key): #fake it return hashlib.md5(str(uuid.uuid1())).hexdigest() -def query_vvm(log_file_handle, platform_key, settings, summary_dict): +def query_vvm(log_file_handle = None, platform_key = None, settings = None, summary_dict = None, UpdaterServiceURL = None, UpdaterWillingToTest = None): result_data = None #URI template /update/v1.1/channelname/version/platformkey/platformversion/willing-to-test/uniqueid #https://wiki.lindenlab.com/wiki/Viewer_Version_Manager_REST_API#Viewer_Update_Query - base_URI = 'https://update.secondlife.com/update/' + if UpdaterServiceURL: + baseURI = UpdaterServiceURL + else: + base_URI = 'https://update.secondlife.com/update/' channelname = summary_dict['Channel'] #this is kind of a mess because the settings value a) in a map and b) is both the cohort and the version version = summary_dict['Version'] @@ -229,11 +232,17 @@ def query_vvm(log_file_handle, platform_key, settings, summary_dict): """ - try: - test_ok = settings['test']['Value'] - except KeyError as ke: - #normal case, no testing key - test_ok = 'testok' + if UpdaterWillingToTest is not None: + if UpdaterWillingToTest: + test_ok = 'testok' + else: + test_ok = 'testno' + else: + try: + test_ok = settings['test']['Value'] + except KeyError: + #normal case, no testing key + test_ok = 'testok' UUID = make_VVM_UUID_hash(platform_key) #because urljoin can't be arsed to take multiple elements query_string = '/v1.0/' + channelname + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID @@ -245,7 +254,7 @@ def query_vvm(log_file_handle, platform_key, settings, summary_dict): return None return result_data -def download(url = None, version = None, download_dir = None, size = 0, background = False): +def download(url = None, version = None, download_dir = None, size = 0, background = False, chunk_size = 1024): download_tries = 0 download_success = False #for background execution @@ -259,7 +268,7 @@ def download(url = None, version = None, download_dir = None, size = 0, backgrou after_frame(message = "Trying again to download new version " + version + " Please wait.") if not background: try: - download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True) + download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True, chunk_size = chunk_size) download_success = True except: download_tries += 1 @@ -268,7 +277,7 @@ def download(url = None, version = None, download_dir = None, size = 0, backgrou try: #Python does not have a facility to multithread a method, so we make the method a standalone #and subprocess that - subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size= %s" % (url, download_dir, size)) + subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size = %s --chunk_size = %s" % (url, download_dir, size, chunk_size)) download_success = True except: download_tries += 1 @@ -294,11 +303,12 @@ def install(platform_key = None, download_dir = None, log_file_handle = None, in silent_write(log_file_handle, "Failed to update viewer to " + version) return False -def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None, platform_key = None, log_file_handle = None, in_place = None): +def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None, + platform_key = None, log_file_handle = None, in_place = None, chunk_size = 1024): #extracted to a method because we do it twice in update_manager() and this makes the logic clearer if not downloaded: #do the download, exit if we fail - if not download(url = url, version = version, download_dir = download_dir, size = size): + if not download(url = url, version = version, download_dir = download_dir, size = size, chunk_size = chunk_size): return (False, 'download', version) #do the install path_to_new_launcher = install(platform_key = platform_key, download_dir = download_dir, @@ -313,7 +323,8 @@ def download_and_install(downloaded = None, url = None, version = None, download #propagate failure return (False, 'apply', version) -def update_manager(): +def update_manager(cli_overrides = None): + #cli_overrides is a dict where the keys are specific parameters of interest and the values are the arguments to #comments that begin with '323:' are steps taken from the algorithm in the description of SL-323. # Note that in the interest of efficiency, such as determining download success once at the top # The code does follow precisely the same order as the algorithm. @@ -356,7 +367,11 @@ def update_manager(): print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) return (False, 'setup', None) - settings = get_settings(log_file_handle, parent_dir) + if cli_overrides['settings'] is not None: + settings = get_settings(log_file_handle, cli_overrides['settings']) + else: + settings = get_settings(log_file_handle, parent_dir) + if settings is None: silent_write(log_file_handle, "Failed to load viewer settings") print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) @@ -375,22 +390,34 @@ def update_manager(): 0 """ - try: - install_automatically = settings['UpdaterServiceSetting']['Value'] - #because, for some godforsaken reason, we delete the setting rather than changing the value - except KeyError: - install_automatically = 1 + if cli_overrides['set']['UpdaterServiceSetting'] is not None: + install_automatically = cli_overrides['set']['UpdaterServiceSetting'] + else: + try: + install_automatically = settings['UpdaterServiceSetting']['Value'] + #because, for some godforsaken reason, we delete the setting rather than changing the value + except KeyError: + install_automatically = 1 + + #use default chunk size if none is given + if cli_overrides['set']['UpdaterMaximumBandwidth ']: + chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth '] + else: + chunk_size = 1024 #get channel and version try: summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__))) + if cli_overrides['channel']: + summary_dict['Channel'] = cli_overrides['channel'] except: silent_write(log_file_handle, "Could not obtain channel and version, exiting.") print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) return (False, 'setup', None) #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. - result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict) + UpdaterServiceURL = cli_overrides['update-service'] + result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict, UpdaterServiceURL) #nothing to do or error if not result_data: silent_write.write(og_file_handle, "No update found.") @@ -417,7 +444,7 @@ def update_manager(): #323: Check for a completed download of the required update; if found, display an alert, install the required update, and launch the newly installed viewer. #323: If [optional download and] Install Automatically: display an alert, install the update and launch updated viewer. return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, - size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place) + size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size) else: #323: If the update response indicates that there is an optional update: #323: Check to see if the optional update has already been downloaded. @@ -436,7 +463,7 @@ def update_manager(): choice = frame.choice.get() if choice == 1: return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, - size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place) + size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size) elif choice == 2: tempfile.mkstmp(suffix = ".next", dir = download_dir) return (True, None, None) -- cgit v1.2.3 From 49b8a1c82d1b12138fa1177db52efa9698c923f5 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 13 Jul 2016 08:19:49 -0700 Subject: include manager directory in CMake list --- indra/viewer_components/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt index 74c9b4568d..2030afacec 100644 --- a/indra/viewer_components/CMakeLists.txt +++ b/indra/viewer_components/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(login) add_subdirectory(updater) +add_subdirectory(manager) -- cgit v1.2.3 From adc67912d097aa032b7e7e76f6e87e8e6c54525e Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 13 Jul 2016 09:37:53 -0700 Subject: include manager directory in viewer manifest, not CMake --- indra/newview/viewer_manifest.py | 1 + indra/viewer_components/CMakeLists.txt | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f476adda4f..9194a2161a 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -714,6 +714,7 @@ class Darwin_i386_Manifest(ViewerManifest): self.path2basename("../viewer_components/updater/scripts/darwin", "*.py") #this copies over the python wrapper script, see SL-322 self.path2basename("../viewer_components/manager","SL_Launcher") + self.path2basename("../viewer_components/manager","*.py") self.end_prefix() # most everything goes in the Resources directory diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt index 2030afacec..74c9b4568d 100644 --- a/indra/viewer_components/CMakeLists.txt +++ b/indra/viewer_components/CMakeLists.txt @@ -2,4 +2,3 @@ add_subdirectory(login) add_subdirectory(updater) -add_subdirectory(manager) -- cgit v1.2.3 From 58d8b3a11edc9ad0edeaf11737e693f3dc5ad318 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 13 Jul 2016 17:31:14 -0700 Subject: SL-323: add llsd python module --- indra/viewer_components/manager/SL_Launcher | 3 + indra/viewer_components/manager/llsd.py | 1052 +++++++++++++++++++++++++++ 2 files changed, 1055 insertions(+) create mode 100755 indra/viewer_components/manager/llsd.py diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index 1d4c19fa86..a96f2392a7 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -20,6 +20,9 @@ import argparse import collections import InstallerUserMessage +#NOTA BENE: +# For POSIX platforms, llsd.py will be imported from the same directory. +# For Windows, llsd.py will be compiled into the executable by pyinstaller import llsd import os import platform diff --git a/indra/viewer_components/manager/llsd.py b/indra/viewer_components/manager/llsd.py new file mode 100755 index 0000000000..4527b115f9 --- /dev/null +++ b/indra/viewer_components/manager/llsd.py @@ -0,0 +1,1052 @@ +"""\ +@file llsd.py +@brief Types as well as parsing and formatting functions for handling LLSD. + +$LicenseInfo:firstyear=2006&license=mit$ + +Copyright (c) 2006-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +import datetime +import base64 +import string +import struct +import time +import types +import re + +from indra.util.fastest_elementtree import ElementTreeError, fromstring +from indra.base import lluuid + +# cllsd.c in server/server-1.25 has memory leaks, +# so disabling cllsd for now +#try: +# import cllsd +#except ImportError: +# cllsd = None +cllsd = None + +int_regex = re.compile(r"[-+]?\d+") +real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") +alpha_regex = re.compile(r"[a-zA-Z]+") +date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" + r"(?P\d{2}):(?P\d{2}):(?P\d{2})" + r"(?P(\.\d+)?)Z") +#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" + +class LLSDParseError(Exception): + pass + +class LLSDSerializationError(TypeError): + pass + + +class binary(str): + pass + +class uri(str): + pass + + +BOOL_TRUE = ('1', '1.0', 'true') +BOOL_FALSE = ('0', '0.0', 'false', '') + + +def format_datestr(v): + """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" + if hasattr(v, 'microsecond'): + return v.isoformat() + 'Z' + else: + return v.strftime('%Y-%m-%dT%H:%M:%SZ') + +def parse_datestr(datestr): + """Parses a datetime object from the string format shared by xml and notation serializations.""" + if datestr == "": + return datetime.datetime(1970, 1, 1) + + match = re.match(date_regex, datestr) + if not match: + raise LLSDParseError("invalid date string '%s'." % datestr) + + year = int(match.group('year')) + month = int(match.group('month')) + day = int(match.group('day')) + hour = int(match.group('hour')) + minute = int(match.group('minute')) + second = int(match.group('second')) + seconds_float = match.group('second_float') + microsecond = 0 + if seconds_float: + microsecond = int(float('0' + seconds_float) * 1e6) + return datetime.datetime(year, month, day, hour, minute, second, microsecond) + + +def bool_to_python(node): + val = node.text or '' + if val in BOOL_TRUE: + return True + else: + return False + +def int_to_python(node): + val = node.text or '' + if not val.strip(): + return 0 + return int(val) + +def real_to_python(node): + val = node.text or '' + if not val.strip(): + return 0.0 + return float(val) + +def uuid_to_python(node): + return lluuid.UUID(node.text) + +def str_to_python(node): + return node.text or '' + +def bin_to_python(node): + return binary(base64.decodestring(node.text or '')) + +def date_to_python(node): + val = node.text or '' + if not val: + val = "1970-01-01T00:00:00Z" + return parse_datestr(val) + + +def uri_to_python(node): + val = node.text or '' + if not val: + return None + return uri(val) + +def map_to_python(node): + result = {} + for index in range(len(node))[::2]: + result[node[index].text] = to_python(node[index+1]) + return result + +def array_to_python(node): + return [to_python(child) for child in node] + + +NODE_HANDLERS = dict( + undef=lambda x: None, + boolean=bool_to_python, + integer=int_to_python, + real=real_to_python, + uuid=uuid_to_python, + string=str_to_python, + binary=bin_to_python, + date=date_to_python, + uri=uri_to_python, + map=map_to_python, + array=array_to_python, + ) + +def to_python(node): + return NODE_HANDLERS[node.tag](node) + +class Nothing(object): + pass + + +class LLSDXMLFormatter(object): + def __init__(self): + self.type_map = { + type(None) : self.UNDEF, + bool : self.BOOLEAN, + int : self.INTEGER, + long : self.INTEGER, + float : self.REAL, + lluuid.UUID : self.UUID, + binary : self.BINARY, + str : self.STRING, + unicode : self.STRING, + uri : self.URI, + datetime.datetime : self.DATE, + datetime.date : self.DATE, + list : self.ARRAY, + tuple : self.ARRAY, + types.GeneratorType : self.ARRAY, + dict : self.MAP, + LLSD : self.LLSD + } + + def elt(self, name, contents=None): + if(contents is None or contents is ''): + return "<%s />" % (name,) + else: + if type(contents) is unicode: + contents = contents.encode('utf-8') + return "<%s>%s" % (name, contents, name) + + def xml_esc(self, v): + if type(v) is unicode: + v = v.encode('utf-8') + return v.replace('&', '&').replace('<', '<').replace('>', '>') + + def LLSD(self, v): + return self.generate(v.thing) + def UNDEF(self, v): + return self.elt('undef') + def BOOLEAN(self, v): + if v: + return self.elt('boolean', 'true') + else: + return self.elt('boolean', 'false') + def INTEGER(self, v): + return self.elt('integer', v) + def REAL(self, v): + return self.elt('real', v) + def UUID(self, v): + if(v.isNull()): + return self.elt('uuid') + else: + return self.elt('uuid', v) + def BINARY(self, v): + return self.elt('binary', base64.encodestring(v)) + def STRING(self, v): + return self.elt('string', self.xml_esc(v)) + def URI(self, v): + return self.elt('uri', self.xml_esc(str(v))) + def DATE(self, v): + return self.elt('date', format_datestr(v)) + def ARRAY(self, v): + return self.elt('array', ''.join([self.generate(item) for item in v])) + def MAP(self, v): + return self.elt( + 'map', + ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) + for key, value in v.items()])) + + typeof = type + def generate(self, something): + t = self.typeof(something) + if self.type_map.has_key(t): + return self.type_map[t](something) + else: + raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( + t, something)) + + def _format(self, something): + return '' + self.elt("llsd", self.generate(something)) + + def format(self, something): + if cllsd: + return cllsd.llsd_to_xml(something) + return self._format(something) + +_g_xml_formatter = None +def format_xml(something): + global _g_xml_formatter + if _g_xml_formatter is None: + _g_xml_formatter = LLSDXMLFormatter() + return _g_xml_formatter.format(something) + +class LLSDXMLPrettyFormatter(LLSDXMLFormatter): + def __init__(self, indent_atom = None): + # Call the super class constructor so that we have the type map + super(LLSDXMLPrettyFormatter, self).__init__() + + # Override the type map to use our specialized formatters to + # emit the pretty output. + self.type_map[list] = self.PRETTY_ARRAY + self.type_map[tuple] = self.PRETTY_ARRAY + self.type_map[types.GeneratorType] = self.PRETTY_ARRAY, + self.type_map[dict] = self.PRETTY_MAP + + # Private data used for indentation. + self._indent_level = 1 + if indent_atom is None: + self._indent_atom = ' ' + else: + self._indent_atom = indent_atom + + def _indent(self): + "Return an indentation based on the atom and indentation level." + return self._indent_atom * self._indent_level + + def PRETTY_ARRAY(self, v): + rv = [] + rv.append('\n') + self._indent_level = self._indent_level + 1 + rv.extend(["%s%s\n" % + (self._indent(), + self.generate(item)) + for item in v]) + self._indent_level = self._indent_level - 1 + rv.append(self._indent()) + rv.append('') + return ''.join(rv) + + def PRETTY_MAP(self, v): + rv = [] + rv.append('\n') + self._indent_level = self._indent_level + 1 + keys = v.keys() + keys.sort() + rv.extend(["%s%s\n%s%s\n" % + (self._indent(), + self.elt('key', key), + self._indent(), + self.generate(v[key])) + for key in keys]) + self._indent_level = self._indent_level - 1 + rv.append(self._indent()) + rv.append('') + return ''.join(rv) + + def format(self, something): + data = [] + data.append('\n') + data.append(self.generate(something)) + data.append('\n') + return '\n'.join(data) + +def format_pretty_xml(something): + """@brief Serialize a python object as 'pretty' llsd xml. + + The output conforms to the LLSD DTD, unlike the output from the + standard python xml.dom DOM::toprettyxml() method which does not + preserve significant whitespace. + This function is not necessarily suited for serializing very large + objects. It is not optimized by the cllsd module, and sorts on + dict (llsd map) keys alphabetically to ease human reading. + """ + return LLSDXMLPrettyFormatter().format(something) + +class LLSDNotationFormatter(object): + def __init__(self): + self.type_map = { + type(None) : self.UNDEF, + bool : self.BOOLEAN, + int : self.INTEGER, + long : self.INTEGER, + float : self.REAL, + lluuid.UUID : self.UUID, + binary : self.BINARY, + str : self.STRING, + unicode : self.STRING, + uri : self.URI, + datetime.datetime : self.DATE, + datetime.date : self.DATE, + list : self.ARRAY, + tuple : self.ARRAY, + types.GeneratorType : self.ARRAY, + dict : self.MAP, + LLSD : self.LLSD + } + + def LLSD(self, v): + return self.generate(v.thing) + def UNDEF(self, v): + return '!' + def BOOLEAN(self, v): + if v: + return 'true' + else: + return 'false' + def INTEGER(self, v): + return "i%s" % v + def REAL(self, v): + return "r%s" % v + def UUID(self, v): + return "u%s" % v + def BINARY(self, v): + return 'b64"' + base64.encodestring(v) + '"' + def STRING(self, v): + if isinstance(v, unicode): + v = v.encode('utf-8') + return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") + def URI(self, v): + return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') + def DATE(self, v): + return 'd"%s"' % format_datestr(v) + def ARRAY(self, v): + return "[%s]" % ','.join([self.generate(item) for item in v]) + def MAP(self, v): + def fix(key): + if isinstance(key, unicode): + return key.encode('utf-8') + return key + return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) + for key, value in v.items()]) + + def generate(self, something): + t = type(something) + handler = self.type_map.get(t) + if handler: + return handler(something) + else: + try: + return self.ARRAY(iter(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % (t, something)) + + def format(self, something): + return self.generate(something) + +def format_notation(something): + return LLSDNotationFormatter().format(something) + +def _hex_as_nybble(hex): + if (hex >= '0') and (hex <= '9'): + return ord(hex) - ord('0') + elif (hex >= 'a') and (hex <='f'): + return 10 + ord(hex) - ord('a') + elif (hex >= 'A') and (hex <='F'): + return 10 + ord(hex) - ord('A'); + +class LLSDBinaryParser(object): + def __init__(self): + pass + + def parse(self, buffer, ignore_binary = False): + """ + This is the basic public interface for parsing. + + @param buffer the binary data to parse in an indexable sequence. + @param ignore_binary parser throws away data in llsd binary nodes. + @return returns a python object. + """ + self._buffer = buffer + self._index = 0 + self._keep_binary = not ignore_binary + return self._parse() + + def _parse(self): + cc = self._buffer[self._index] + self._index += 1 + if cc == '{': + return self._parse_map() + elif cc == '[': + return self._parse_array() + elif cc == '!': + return None + elif cc == '0': + return False + elif cc == '1': + return True + elif cc == 'i': + # 'i' = integer + idx = self._index + self._index += 4 + return struct.unpack("!i", self._buffer[idx:idx+4])[0] + elif cc == ('r'): + # 'r' = real number + idx = self._index + self._index += 8 + return struct.unpack("!d", self._buffer[idx:idx+8])[0] + elif cc == 'u': + # 'u' = uuid + idx = self._index + self._index += 16 + return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16]) + elif cc == 's': + # 's' = string + return self._parse_string() + elif cc in ("'", '"'): + # delimited/escaped string + return self._parse_string_delim(cc) + elif cc == 'l': + # 'l' = uri + return uri(self._parse_string()) + elif cc == ('d'): + # 'd' = date in seconds since epoch + idx = self._index + self._index += 8 + seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0] + return datetime.datetime.fromtimestamp(seconds) + elif cc == 'b': + binary = self._parse_string() + if self._keep_binary: + return binary + # *NOTE: maybe have a binary placeholder which has the + # length. + return None + else: + raise LLSDParseError("invalid binary token at byte %d: %d" % ( + self._index - 1, ord(cc))) + + def _parse_map(self): + rv = {} + size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] + self._index += 4 + count = 0 + cc = self._buffer[self._index] + self._index += 1 + key = '' + while (cc != '}') and (count < size): + if cc == 'k': + key = self._parse_string() + elif cc in ("'", '"'): + key = self._parse_string_delim(cc) + else: + raise LLSDParseError("invalid map key at byte %d." % ( + self._index - 1,)) + value = self._parse() + rv[key] = value + count += 1 + cc = self._buffer[self._index] + self._index += 1 + if cc != '}': + raise LLSDParseError("invalid map close token at byte %d." % ( + self._index,)) + return rv + + def _parse_array(self): + rv = [] + size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] + self._index += 4 + count = 0 + cc = self._buffer[self._index] + while (cc != ']') and (count < size): + rv.append(self._parse()) + count += 1 + cc = self._buffer[self._index] + if cc != ']': + raise LLSDParseError("invalid array close token at byte %d." % ( + self._index,)) + self._index += 1 + return rv + + def _parse_string(self): + size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] + self._index += 4 + rv = self._buffer[self._index:self._index+size] + self._index += size + return rv + + def _parse_string_delim(self, delim): + list = [] + found_escape = False + found_hex = False + found_digit = False + byte = 0 + while True: + cc = self._buffer[self._index] + self._index += 1 + if found_escape: + if found_hex: + if found_digit: + found_escape = False + found_hex = False + found_digit = False + byte <<= 4 + byte |= _hex_as_nybble(cc) + list.append(chr(byte)) + byte = 0 + else: + found_digit = True + byte = _hex_as_nybble(cc) + elif cc == 'x': + found_hex = True + else: + if cc == 'a': + list.append('\a') + elif cc == 'b': + list.append('\b') + elif cc == 'f': + list.append('\f') + elif cc == 'n': + list.append('\n') + elif cc == 'r': + list.append('\r') + elif cc == 't': + list.append('\t') + elif cc == 'v': + list.append('\v') + else: + list.append(cc) + found_escape = False + elif cc == '\\': + found_escape = True + elif cc == delim: + break + else: + list.append(cc) + return ''.join(list) + +class LLSDNotationParser(object): + """ Parse LLSD notation: + map: { string:object, string:object } + array: [ object, object, object ] + undef: ! + boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE + integer: i#### + real: r#### + uuid: u#### + string: "g\'day" | 'have a "nice" day' | s(size)"raw data" + uri: l"escaped" + date: d"YYYY-MM-DDTHH:MM:SS.FFZ" + binary: b##"ff3120ab1" | b(size)"raw data" + """ + def __init__(self): + pass + + def parse(self, buffer, ignore_binary = False): + """ + This is the basic public interface for parsing. + + @param buffer the notation string to parse. + @param ignore_binary parser throws away data in llsd binary nodes. + @return returns a python object. + """ + if buffer == "": + return False + + self._buffer = buffer + self._index = 0 + return self._parse() + + def _parse(self): + cc = self._buffer[self._index] + self._index += 1 + if cc == '{': + return self._parse_map() + elif cc == '[': + return self._parse_array() + elif cc == '!': + return None + elif cc == '0': + return False + elif cc == '1': + return True + elif cc in ('F', 'f'): + self._skip_alpha() + return False + elif cc in ('T', 't'): + self._skip_alpha() + return True + elif cc == 'i': + # 'i' = integer + return self._parse_integer() + elif cc == ('r'): + # 'r' = real number + return self._parse_real() + elif cc == 'u': + # 'u' = uuid + return self._parse_uuid() + elif cc in ("'", '"', 's'): + return self._parse_string(cc) + elif cc == 'l': + # 'l' = uri + delim = self._buffer[self._index] + self._index += 1 + val = uri(self._parse_string(delim)) + if len(val) == 0: + return None + return val + elif cc == ('d'): + # 'd' = date in seconds since epoch + return self._parse_date() + elif cc == 'b': + return self._parse_binary() + else: + raise LLSDParseError("invalid token at index %d: %d" % ( + self._index - 1, ord(cc))) + + def _parse_binary(self): + i = self._index + if self._buffer[i:i+2] == '64': + q = self._buffer[i+2] + e = self._buffer.find(q, i+3) + try: + return base64.decodestring(self._buffer[i+3:e]) + finally: + self._index = e + 1 + else: + raise LLSDParseError('random horrible binary format not supported') + + def _parse_map(self): + """ map: { string:object, string:object } """ + rv = {} + cc = self._buffer[self._index] + self._index += 1 + key = '' + found_key = False + while (cc != '}'): + if not found_key: + if cc in ("'", '"', 's'): + key = self._parse_string(cc) + found_key = True + elif cc.isspace() or cc == ',': + cc = self._buffer[self._index] + self._index += 1 + else: + raise LLSDParseError("invalid map key at byte %d." % ( + self._index - 1,)) + elif cc.isspace() or cc == ':': + cc = self._buffer[self._index] + self._index += 1 + continue + else: + self._index += 1 + value = self._parse() + rv[key] = value + found_key = False + cc = self._buffer[self._index] + self._index += 1 + + return rv + + def _parse_array(self): + """ array: [ object, object, object ] """ + rv = [] + cc = self._buffer[self._index] + while (cc != ']'): + if cc.isspace() or cc == ',': + self._index += 1 + cc = self._buffer[self._index] + continue + rv.append(self._parse()) + cc = self._buffer[self._index] + + if cc != ']': + raise LLSDParseError("invalid array close token at index %d." % ( + self._index,)) + self._index += 1 + return rv + + def _parse_uuid(self): + match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:]) + if not match: + raise LLSDParseError("invalid uuid token at index %d." % self._index) + + (start, end) = match.span() + start += self._index + end += self._index + self._index = end + return lluuid.UUID(self._buffer[start:end]) + + def _skip_alpha(self): + match = re.match(alpha_regex, self._buffer[self._index:]) + if match: + self._index += match.end() + + def _parse_date(self): + delim = self._buffer[self._index] + self._index += 1 + datestr = self._parse_string(delim) + return parse_datestr(datestr) + + def _parse_real(self): + match = re.match(real_regex, self._buffer[self._index:]) + if not match: + raise LLSDParseError("invalid real token at index %d." % self._index) + + (start, end) = match.span() + start += self._index + end += self._index + self._index = end + return float( self._buffer[start:end] ) + + def _parse_integer(self): + match = re.match(int_regex, self._buffer[self._index:]) + if not match: + raise LLSDParseError("invalid integer token at index %d." % self._index) + + (start, end) = match.span() + start += self._index + end += self._index + self._index = end + return int( self._buffer[start:end] ) + + def _parse_string(self, delim): + """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """ + rv = "" + + if delim in ("'", '"'): + rv = self._parse_string_delim(delim) + elif delim == 's': + rv = self._parse_string_raw() + else: + raise LLSDParseError("invalid string token at index %d." % self._index) + + return rv + + + def _parse_string_delim(self, delim): + """ string: "g'day 'un" | 'have a "nice" day' """ + list = [] + found_escape = False + found_hex = False + found_digit = False + byte = 0 + while True: + cc = self._buffer[self._index] + self._index += 1 + if found_escape: + if found_hex: + if found_digit: + found_escape = False + found_hex = False + found_digit = False + byte <<= 4 + byte |= _hex_as_nybble(cc) + list.append(chr(byte)) + byte = 0 + else: + found_digit = True + byte = _hex_as_nybble(cc) + elif cc == 'x': + found_hex = True + else: + if cc == 'a': + list.append('\a') + elif cc == 'b': + list.append('\b') + elif cc == 'f': + list.append('\f') + elif cc == 'n': + list.append('\n') + elif cc == 'r': + list.append('\r') + elif cc == 't': + list.append('\t') + elif cc == 'v': + list.append('\v') + else: + list.append(cc) + found_escape = False + elif cc == '\\': + found_escape = True + elif cc == delim: + break + else: + list.append(cc) + return ''.join(list) + + def _parse_string_raw(self): + """ string: s(size)"raw data" """ + # Read the (size) portion. + cc = self._buffer[self._index] + self._index += 1 + if cc != '(': + raise LLSDParseError("invalid string token at index %d." % self._index) + + rparen = self._buffer.find(')', self._index) + if rparen == -1: + raise LLSDParseError("invalid string token at index %d." % self._index) + + size = int(self._buffer[self._index:rparen]) + + self._index = rparen + 1 + delim = self._buffer[self._index] + self._index += 1 + if delim not in ("'", '"'): + raise LLSDParseError("invalid string token at index %d." % self._index) + + rv = self._buffer[self._index:(self._index + size)] + self._index += size + cc = self._buffer[self._index] + self._index += 1 + if cc != delim: + raise LLSDParseError("invalid string token at index %d." % self._index) + + return rv + +def format_binary(something): + return '\n' + _format_binary_recurse(something) + +def _format_binary_recurse(something): + def _format_list(something): + array_builder = [] + array_builder.append('[' + struct.pack('!i', len(something))) + for item in something: + array_builder.append(_format_binary_recurse(item)) + array_builder.append(']') + return ''.join(array_builder) + + if something is None: + return '!' + elif isinstance(something, LLSD): + return _format_binary_recurse(something.thing) + elif isinstance(something, bool): + if something: + return '1' + else: + return '0' + elif isinstance(something, (int, long)): + return 'i' + struct.pack('!i', something) + elif isinstance(something, float): + return 'r' + struct.pack('!d', something) + elif isinstance(something, lluuid.UUID): + return 'u' + something._bits + elif isinstance(something, binary): + return 'b' + struct.pack('!i', len(something)) + something + elif isinstance(something, str): + return 's' + struct.pack('!i', len(something)) + something + elif isinstance(something, unicode): + something = something.encode('utf-8') + return 's' + struct.pack('!i', len(something)) + something + elif isinstance(something, uri): + return 'l' + struct.pack('!i', len(something)) + something + elif isinstance(something, datetime.datetime): + seconds_since_epoch = time.mktime(something.timetuple()) + return 'd' + struct.pack('!d', seconds_since_epoch) + elif isinstance(something, (list, tuple)): + return _format_list(something) + elif isinstance(something, dict): + map_builder = [] + map_builder.append('{' + struct.pack('!i', len(something))) + for key, value in something.items(): + if isinstance(key, unicode): + key = key.encode('utf-8') + map_builder.append('k' + struct.pack('!i', len(key)) + key) + map_builder.append(_format_binary_recurse(value)) + map_builder.append('}') + return ''.join(map_builder) + else: + try: + return _format_list(list(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % + (type(something), something)) + + +def parse_binary(binary): + if binary.startswith(''): + just_binary = binary.split('\n', 1)[1] + else: + just_binary = binary + return LLSDBinaryParser().parse(just_binary) + +def parse_xml(something): + try: + return to_python(fromstring(something)[0]) + except ElementTreeError, err: + raise LLSDParseError(*err.args) + +def parse_notation(something): + return LLSDNotationParser().parse(something) + +def parse(something): + try: + something = string.lstrip(something) #remove any pre-trailing whitespace + if something.startswith(''): + return parse_binary(something) + # This should be better. + elif something.startswith('<'): + return parse_xml(something) + else: + return parse_notation(something) + except KeyError, e: + raise Exception('LLSD could not be parsed: %s' % (e,)) + +class LLSD(object): + def __init__(self, thing=None): + self.thing = thing + + def __str__(self): + return self.toXML(self.thing) + + parse = staticmethod(parse) + toXML = staticmethod(format_xml) + toPrettyXML = staticmethod(format_pretty_xml) + toBinary = staticmethod(format_binary) + toNotation = staticmethod(format_notation) + + +undef = LLSD(None) + +XML_MIME_TYPE = 'application/llsd+xml' +BINARY_MIME_TYPE = 'application/llsd+binary' + +# register converters for llsd in mulib, if it is available +try: + from mulib import stacked, mu + stacked.NoProducer() # just to exercise stacked + mu.safe_load(None) # just to exercise mu +except: + # mulib not available, don't print an error message since this is normal + pass +else: + mu.add_parser(parse, XML_MIME_TYPE) + mu.add_parser(parse, 'application/llsd+binary') + + def llsd_convert_xml(llsd_stuff, request): + request.write(format_xml(llsd_stuff)) + + def llsd_convert_binary(llsd_stuff, request): + request.write(format_binary(llsd_stuff)) + + for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]: + stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE) + stacked.add_producer(typ, llsd_convert_xml, 'application/xml') + stacked.add_producer(typ, llsd_convert_xml, 'text/xml') + + stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') + + stacked.add_producer(LLSD, llsd_convert_xml, '*/*') + + # in case someone is using the legacy mu.xml wrapper, we need to + # tell mu to produce application/xml or application/llsd+xml + # (based on the accept header) from raw xml. Phoenix 2008-07-21 + stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE) + stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml') + + + +# mulib wsgi stuff +# try: +# from mulib import mu, adapters +# +# # try some known attributes from mulib to be ultra-sure we've imported it +# mu.get_current +# adapters.handlers +# except: +# # mulib not available, don't print an error message since this is normal +# pass +# else: +# def llsd_xml_handler(content_type): +# def handle_llsd_xml(env, start_response): +# llsd_stuff, _ = mu.get_current(env) +# result = format_xml(llsd_stuff) +# start_response("200 OK", [('Content-Type', content_type)]) +# env['mu.negotiated_type'] = content_type +# yield result +# return handle_llsd_xml +# +# def llsd_binary_handler(content_type): +# def handle_llsd_binary(env, start_response): +# llsd_stuff, _ = mu.get_current(env) +# result = format_binary(llsd_stuff) +# start_response("200 OK", [('Content-Type', content_type)]) +# env['mu.negotiated_type'] = content_type +# yield result +# return handle_llsd_binary +# +# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse + +# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: +# for content_type in (XML_MIME_TYPE, 'application/xml'): +# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type) +# +# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE) +# +# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*') -- cgit v1.2.3 From 68832f56ba09541f01a246574b141e3ab5fb62b8 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 14 Jul 2016 08:37:23 -0700 Subject: add dependent modules --- indra/newview/viewer_manifest.py | 4 +- indra/viewer_components/manager/base/llsd.py | 1052 ++++++++++++++++++++ indra/viewer_components/manager/base/lluuid.py | 319 ++++++ indra/viewer_components/manager/llsd.py | 1052 -------------------- .../manager/util/fastest_elementtree.py | 64 ++ 5 files changed, 1438 insertions(+), 1053 deletions(-) create mode 100755 indra/viewer_components/manager/base/llsd.py create mode 100755 indra/viewer_components/manager/base/lluuid.py delete mode 100755 indra/viewer_components/manager/llsd.py create mode 100755 indra/viewer_components/manager/util/fastest_elementtree.py diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 9194a2161a..ac9b8d8ebe 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -712,9 +712,11 @@ class Darwin_i386_Manifest(ViewerManifest): if self.prefix(dst="MacOS"): self.path2basename("../viewer_components/updater/scripts/darwin", "*.py") - #this copies over the python wrapper script, see SL-322 + #this copies over the python wrapper script and associated utilities, see SL-321, SL-322 and SL-323 self.path2basename("../viewer_components/manager","SL_Launcher") self.path2basename("../viewer_components/manager","*.py") + self.path2basename("../viewer_components/manager/base","*py") + self.path2basename("../viewer_components/manager/util","*py") self.end_prefix() # most everything goes in the Resources directory diff --git a/indra/viewer_components/manager/base/llsd.py b/indra/viewer_components/manager/base/llsd.py new file mode 100755 index 0000000000..4527b115f9 --- /dev/null +++ b/indra/viewer_components/manager/base/llsd.py @@ -0,0 +1,1052 @@ +"""\ +@file llsd.py +@brief Types as well as parsing and formatting functions for handling LLSD. + +$LicenseInfo:firstyear=2006&license=mit$ + +Copyright (c) 2006-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +import datetime +import base64 +import string +import struct +import time +import types +import re + +from indra.util.fastest_elementtree import ElementTreeError, fromstring +from indra.base import lluuid + +# cllsd.c in server/server-1.25 has memory leaks, +# so disabling cllsd for now +#try: +# import cllsd +#except ImportError: +# cllsd = None +cllsd = None + +int_regex = re.compile(r"[-+]?\d+") +real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") +alpha_regex = re.compile(r"[a-zA-Z]+") +date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" + r"(?P\d{2}):(?P\d{2}):(?P\d{2})" + r"(?P(\.\d+)?)Z") +#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" + +class LLSDParseError(Exception): + pass + +class LLSDSerializationError(TypeError): + pass + + +class binary(str): + pass + +class uri(str): + pass + + +BOOL_TRUE = ('1', '1.0', 'true') +BOOL_FALSE = ('0', '0.0', 'false', '') + + +def format_datestr(v): + """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" + if hasattr(v, 'microsecond'): + return v.isoformat() + 'Z' + else: + return v.strftime('%Y-%m-%dT%H:%M:%SZ') + +def parse_datestr(datestr): + """Parses a datetime object from the string format shared by xml and notation serializations.""" + if datestr == "": + return datetime.datetime(1970, 1, 1) + + match = re.match(date_regex, datestr) + if not match: + raise LLSDParseError("invalid date string '%s'." % datestr) + + year = int(match.group('year')) + month = int(match.group('month')) + day = int(match.group('day')) + hour = int(match.group('hour')) + minute = int(match.group('minute')) + second = int(match.group('second')) + seconds_float = match.group('second_float') + microsecond = 0 + if seconds_float: + microsecond = int(float('0' + seconds_float) * 1e6) + return datetime.datetime(year, month, day, hour, minute, second, microsecond) + + +def bool_to_python(node): + val = node.text or '' + if val in BOOL_TRUE: + return True + else: + return False + +def int_to_python(node): + val = node.text or '' + if not val.strip(): + return 0 + return int(val) + +def real_to_python(node): + val = node.text or '' + if not val.strip(): + return 0.0 + return float(val) + +def uuid_to_python(node): + return lluuid.UUID(node.text) + +def str_to_python(node): + return node.text or '' + +def bin_to_python(node): + return binary(base64.decodestring(node.text or '')) + +def date_to_python(node): + val = node.text or '' + if not val: + val = "1970-01-01T00:00:00Z" + return parse_datestr(val) + + +def uri_to_python(node): + val = node.text or '' + if not val: + return None + return uri(val) + +def map_to_python(node): + result = {} + for index in range(len(node))[::2]: + result[node[index].text] = to_python(node[index+1]) + return result + +def array_to_python(node): + return [to_python(child) for child in node] + + +NODE_HANDLERS = dict( + undef=lambda x: None, + boolean=bool_to_python, + integer=int_to_python, + real=real_to_python, + uuid=uuid_to_python, + string=str_to_python, + binary=bin_to_python, + date=date_to_python, + uri=uri_to_python, + map=map_to_python, + array=array_to_python, + ) + +def to_python(node): + return NODE_HANDLERS[node.tag](node) + +class Nothing(object): + pass + + +class LLSDXMLFormatter(object): + def __init__(self): + self.type_map = { + type(None) : self.UNDEF, + bool : self.BOOLEAN, + int : self.INTEGER, + long : self.INTEGER, + float : self.REAL, + lluuid.UUID : self.UUID, + binary : self.BINARY, + str : self.STRING, + unicode : self.STRING, + uri : self.URI, + datetime.datetime : self.DATE, + datetime.date : self.DATE, + list : self.ARRAY, + tuple : self.ARRAY, + types.GeneratorType : self.ARRAY, + dict : self.MAP, + LLSD : self.LLSD + } + + def elt(self, name, contents=None): + if(contents is None or contents is ''): + return "<%s />" % (name,) + else: + if type(contents) is unicode: + contents = contents.encode('utf-8') + return "<%s>%s" % (name, contents, name) + + def xml_esc(self, v): + if type(v) is unicode: + v = v.encode('utf-8') + return v.replace('&', '&').replace('<', '<').replace('>', '>') + + def LLSD(self, v): + return self.generate(v.thing) + def UNDEF(self, v): + return self.elt('undef') + def BOOLEAN(self, v): + if v: + return self.elt('boolean', 'true') + else: + return self.elt('boolean', 'false') + def INTEGER(self, v): + return self.elt('integer', v) + def REAL(self, v): + return self.elt('real', v) + def UUID(self, v): + if(v.isNull()): + return self.elt('uuid') + else: + return self.elt('uuid', v) + def BINARY(self, v): + return self.elt('binary', base64.encodestring(v)) + def STRING(self, v): + return self.elt('string', self.xml_esc(v)) + def URI(self, v): + return self.elt('uri', self.xml_esc(str(v))) + def DATE(self, v): + return self.elt('date', format_datestr(v)) + def ARRAY(self, v): + return self.elt('array', ''.join([self.generate(item) for item in v])) + def MAP(self, v): + return self.elt( + 'map', + ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) + for key, value in v.items()])) + + typeof = type + def generate(self, something): + t = self.typeof(something) + if self.type_map.has_key(t): + return self.type_map[t](something) + else: + raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( + t, something)) + + def _format(self, something): + return '' + self.elt("llsd", self.generate(something)) + + def format(self, something): + if cllsd: + return cllsd.llsd_to_xml(something) + return self._format(something) + +_g_xml_formatter = None +def format_xml(something): + global _g_xml_formatter + if _g_xml_formatter is None: + _g_xml_formatter = LLSDXMLFormatter() + return _g_xml_formatter.format(something) + +class LLSDXMLPrettyFormatter(LLSDXMLFormatter): + def __init__(self, indent_atom = None): + # Call the super class constructor so that we have the type map + super(LLSDXMLPrettyFormatter, self).__init__() + + # Override the type map to use our specialized formatters to + # emit the pretty output. + self.type_map[list] = self.PRETTY_ARRAY + self.type_map[tuple] = self.PRETTY_ARRAY + self.type_map[types.GeneratorType] = self.PRETTY_ARRAY, + self.type_map[dict] = self.PRETTY_MAP + + # Private data used for indentation. + self._indent_level = 1 + if indent_atom is None: + self._indent_atom = ' ' + else: + self._indent_atom = indent_atom + + def _indent(self): + "Return an indentation based on the atom and indentation level." + return self._indent_atom * self._indent_level + + def PRETTY_ARRAY(self, v): + rv = [] + rv.append('\n') + self._indent_level = self._indent_level + 1 + rv.extend(["%s%s\n" % + (self._indent(), + self.generate(item)) + for item in v]) + self._indent_level = self._indent_level - 1 + rv.append(self._indent()) + rv.append('') + return ''.join(rv) + + def PRETTY_MAP(self, v): + rv = [] + rv.append('\n') + self._indent_level = self._indent_level + 1 + keys = v.keys() + keys.sort() + rv.extend(["%s%s\n%s%s\n" % + (self._indent(), + self.elt('key', key), + self._indent(), + self.generate(v[key])) + for key in keys]) + self._indent_level = self._indent_level - 1 + rv.append(self._indent()) + rv.append('') + return ''.join(rv) + + def format(self, something): + data = [] + data.append('\n') + data.append(self.generate(something)) + data.append('\n') + return '\n'.join(data) + +def format_pretty_xml(something): + """@brief Serialize a python object as 'pretty' llsd xml. + + The output conforms to the LLSD DTD, unlike the output from the + standard python xml.dom DOM::toprettyxml() method which does not + preserve significant whitespace. + This function is not necessarily suited for serializing very large + objects. It is not optimized by the cllsd module, and sorts on + dict (llsd map) keys alphabetically to ease human reading. + """ + return LLSDXMLPrettyFormatter().format(something) + +class LLSDNotationFormatter(object): + def __init__(self): + self.type_map = { + type(None) : self.UNDEF, + bool : self.BOOLEAN, + int : self.INTEGER, + long : self.INTEGER, + float : self.REAL, + lluuid.UUID : self.UUID, + binary : self.BINARY, + str : self.STRING, + unicode : self.STRING, + uri : self.URI, + datetime.datetime : self.DATE, + datetime.date : self.DATE, + list : self.ARRAY, + tuple : self.ARRAY, + types.GeneratorType : self.ARRAY, + dict : self.MAP, + LLSD : self.LLSD + } + + def LLSD(self, v): + return self.generate(v.thing) + def UNDEF(self, v): + return '!' + def BOOLEAN(self, v): + if v: + return 'true' + else: + return 'false' + def INTEGER(self, v): + return "i%s" % v + def REAL(self, v): + return "r%s" % v + def UUID(self, v): + return "u%s" % v + def BINARY(self, v): + return 'b64"' + base64.encodestring(v) + '"' + def STRING(self, v): + if isinstance(v, unicode): + v = v.encode('utf-8') + return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") + def URI(self, v): + return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') + def DATE(self, v): + return 'd"%s"' % format_datestr(v) + def ARRAY(self, v): + return "[%s]" % ','.join([self.generate(item) for item in v]) + def MAP(self, v): + def fix(key): + if isinstance(key, unicode): + return key.encode('utf-8') + return key + return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) + for key, value in v.items()]) + + def generate(self, something): + t = type(something) + handler = self.type_map.get(t) + if handler: + return handler(something) + else: + try: + return self.ARRAY(iter(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % (t, something)) + + def format(self, something): + return self.generate(something) + +def format_notation(something): + return LLSDNotationFormatter().format(something) + +def _hex_as_nybble(hex): + if (hex >= '0') and (hex <= '9'): + return ord(hex) - ord('0') + elif (hex >= 'a') and (hex <='f'): + return 10 + ord(hex) - ord('a') + elif (hex >= 'A') and (hex <='F'): + return 10 + ord(hex) - ord('A'); + +class LLSDBinaryParser(object): + def __init__(self): + pass + + def parse(self, buffer, ignore_binary = False): + """ + This is the basic public interface for parsing. + + @param buffer the binary data to parse in an indexable sequence. + @param ignore_binary parser throws away data in llsd binary nodes. + @return returns a python object. + """ + self._buffer = buffer + self._index = 0 + self._keep_binary = not ignore_binary + return self._parse() + + def _parse(self): + cc = self._buffer[self._index] + self._index += 1 + if cc == '{': + return self._parse_map() + elif cc == '[': + return self._parse_array() + elif cc == '!': + return None + elif cc == '0': + return False + elif cc == '1': + return True + elif cc == 'i': + # 'i' = integer + idx = self._index + self._index += 4 + return struct.unpack("!i", self._buffer[idx:idx+4])[0] + elif cc == ('r'): + # 'r' = real number + idx = self._index + self._index += 8 + return struct.unpack("!d", self._buffer[idx:idx+8])[0] + elif cc == 'u': + # 'u' = uuid + idx = self._index + self._index += 16 + return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16]) + elif cc == 's': + # 's' = string + return self._parse_string() + elif cc in ("'", '"'): + # delimited/escaped string + return self._parse_string_delim(cc) + elif cc == 'l': + # 'l' = uri + return uri(self._parse_string()) + elif cc == ('d'): + # 'd' = date in seconds since epoch + idx = self._index + self._index += 8 + seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0] + return datetime.datetime.fromtimestamp(seconds) + elif cc == 'b': + binary = self._parse_string() + if self._keep_binary: + return binary + # *NOTE: maybe have a binary placeholder which has the + # length. + return None + else: + raise LLSDParseError("invalid binary token at byte %d: %d" % ( + self._index - 1, ord(cc))) + + def _parse_map(self): + rv = {} + size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] + self._index += 4 + count = 0 + cc = self._buffer[self._index] + self._index += 1 + key = '' + while (cc != '}') and (count < size): + if cc == 'k': + key = self._parse_string() + elif cc in ("'", '"'): + key = self._parse_string_delim(cc) + else: + raise LLSDParseError("invalid map key at byte %d." % ( + self._index - 1,)) + value = self._parse() + rv[key] = value + count += 1 + cc = self._buffer[self._index] + self._index += 1 + if cc != '}': + raise LLSDParseError("invalid map close token at byte %d." % ( + self._index,)) + return rv + + def _parse_array(self): + rv = [] + size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] + self._index += 4 + count = 0 + cc = self._buffer[self._index] + while (cc != ']') and (count < size): + rv.append(self._parse()) + count += 1 + cc = self._buffer[self._index] + if cc != ']': + raise LLSDParseError("invalid array close token at byte %d." % ( + self._index,)) + self._index += 1 + return rv + + def _parse_string(self): + size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] + self._index += 4 + rv = self._buffer[self._index:self._index+size] + self._index += size + return rv + + def _parse_string_delim(self, delim): + list = [] + found_escape = False + found_hex = False + found_digit = False + byte = 0 + while True: + cc = self._buffer[self._index] + self._index += 1 + if found_escape: + if found_hex: + if found_digit: + found_escape = False + found_hex = False + found_digit = False + byte <<= 4 + byte |= _hex_as_nybble(cc) + list.append(chr(byte)) + byte = 0 + else: + found_digit = True + byte = _hex_as_nybble(cc) + elif cc == 'x': + found_hex = True + else: + if cc == 'a': + list.append('\a') + elif cc == 'b': + list.append('\b') + elif cc == 'f': + list.append('\f') + elif cc == 'n': + list.append('\n') + elif cc == 'r': + list.append('\r') + elif cc == 't': + list.append('\t') + elif cc == 'v': + list.append('\v') + else: + list.append(cc) + found_escape = False + elif cc == '\\': + found_escape = True + elif cc == delim: + break + else: + list.append(cc) + return ''.join(list) + +class LLSDNotationParser(object): + """ Parse LLSD notation: + map: { string:object, string:object } + array: [ object, object, object ] + undef: ! + boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE + integer: i#### + real: r#### + uuid: u#### + string: "g\'day" | 'have a "nice" day' | s(size)"raw data" + uri: l"escaped" + date: d"YYYY-MM-DDTHH:MM:SS.FFZ" + binary: b##"ff3120ab1" | b(size)"raw data" + """ + def __init__(self): + pass + + def parse(self, buffer, ignore_binary = False): + """ + This is the basic public interface for parsing. + + @param buffer the notation string to parse. + @param ignore_binary parser throws away data in llsd binary nodes. + @return returns a python object. + """ + if buffer == "": + return False + + self._buffer = buffer + self._index = 0 + return self._parse() + + def _parse(self): + cc = self._buffer[self._index] + self._index += 1 + if cc == '{': + return self._parse_map() + elif cc == '[': + return self._parse_array() + elif cc == '!': + return None + elif cc == '0': + return False + elif cc == '1': + return True + elif cc in ('F', 'f'): + self._skip_alpha() + return False + elif cc in ('T', 't'): + self._skip_alpha() + return True + elif cc == 'i': + # 'i' = integer + return self._parse_integer() + elif cc == ('r'): + # 'r' = real number + return self._parse_real() + elif cc == 'u': + # 'u' = uuid + return self._parse_uuid() + elif cc in ("'", '"', 's'): + return self._parse_string(cc) + elif cc == 'l': + # 'l' = uri + delim = self._buffer[self._index] + self._index += 1 + val = uri(self._parse_string(delim)) + if len(val) == 0: + return None + return val + elif cc == ('d'): + # 'd' = date in seconds since epoch + return self._parse_date() + elif cc == 'b': + return self._parse_binary() + else: + raise LLSDParseError("invalid token at index %d: %d" % ( + self._index - 1, ord(cc))) + + def _parse_binary(self): + i = self._index + if self._buffer[i:i+2] == '64': + q = self._buffer[i+2] + e = self._buffer.find(q, i+3) + try: + return base64.decodestring(self._buffer[i+3:e]) + finally: + self._index = e + 1 + else: + raise LLSDParseError('random horrible binary format not supported') + + def _parse_map(self): + """ map: { string:object, string:object } """ + rv = {} + cc = self._buffer[self._index] + self._index += 1 + key = '' + found_key = False + while (cc != '}'): + if not found_key: + if cc in ("'", '"', 's'): + key = self._parse_string(cc) + found_key = True + elif cc.isspace() or cc == ',': + cc = self._buffer[self._index] + self._index += 1 + else: + raise LLSDParseError("invalid map key at byte %d." % ( + self._index - 1,)) + elif cc.isspace() or cc == ':': + cc = self._buffer[self._index] + self._index += 1 + continue + else: + self._index += 1 + value = self._parse() + rv[key] = value + found_key = False + cc = self._buffer[self._index] + self._index += 1 + + return rv + + def _parse_array(self): + """ array: [ object, object, object ] """ + rv = [] + cc = self._buffer[self._index] + while (cc != ']'): + if cc.isspace() or cc == ',': + self._index += 1 + cc = self._buffer[self._index] + continue + rv.append(self._parse()) + cc = self._buffer[self._index] + + if cc != ']': + raise LLSDParseError("invalid array close token at index %d." % ( + self._index,)) + self._index += 1 + return rv + + def _parse_uuid(self): + match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:]) + if not match: + raise LLSDParseError("invalid uuid token at index %d." % self._index) + + (start, end) = match.span() + start += self._index + end += self._index + self._index = end + return lluuid.UUID(self._buffer[start:end]) + + def _skip_alpha(self): + match = re.match(alpha_regex, self._buffer[self._index:]) + if match: + self._index += match.end() + + def _parse_date(self): + delim = self._buffer[self._index] + self._index += 1 + datestr = self._parse_string(delim) + return parse_datestr(datestr) + + def _parse_real(self): + match = re.match(real_regex, self._buffer[self._index:]) + if not match: + raise LLSDParseError("invalid real token at index %d." % self._index) + + (start, end) = match.span() + start += self._index + end += self._index + self._index = end + return float( self._buffer[start:end] ) + + def _parse_integer(self): + match = re.match(int_regex, self._buffer[self._index:]) + if not match: + raise LLSDParseError("invalid integer token at index %d." % self._index) + + (start, end) = match.span() + start += self._index + end += self._index + self._index = end + return int( self._buffer[start:end] ) + + def _parse_string(self, delim): + """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """ + rv = "" + + if delim in ("'", '"'): + rv = self._parse_string_delim(delim) + elif delim == 's': + rv = self._parse_string_raw() + else: + raise LLSDParseError("invalid string token at index %d." % self._index) + + return rv + + + def _parse_string_delim(self, delim): + """ string: "g'day 'un" | 'have a "nice" day' """ + list = [] + found_escape = False + found_hex = False + found_digit = False + byte = 0 + while True: + cc = self._buffer[self._index] + self._index += 1 + if found_escape: + if found_hex: + if found_digit: + found_escape = False + found_hex = False + found_digit = False + byte <<= 4 + byte |= _hex_as_nybble(cc) + list.append(chr(byte)) + byte = 0 + else: + found_digit = True + byte = _hex_as_nybble(cc) + elif cc == 'x': + found_hex = True + else: + if cc == 'a': + list.append('\a') + elif cc == 'b': + list.append('\b') + elif cc == 'f': + list.append('\f') + elif cc == 'n': + list.append('\n') + elif cc == 'r': + list.append('\r') + elif cc == 't': + list.append('\t') + elif cc == 'v': + list.append('\v') + else: + list.append(cc) + found_escape = False + elif cc == '\\': + found_escape = True + elif cc == delim: + break + else: + list.append(cc) + return ''.join(list) + + def _parse_string_raw(self): + """ string: s(size)"raw data" """ + # Read the (size) portion. + cc = self._buffer[self._index] + self._index += 1 + if cc != '(': + raise LLSDParseError("invalid string token at index %d." % self._index) + + rparen = self._buffer.find(')', self._index) + if rparen == -1: + raise LLSDParseError("invalid string token at index %d." % self._index) + + size = int(self._buffer[self._index:rparen]) + + self._index = rparen + 1 + delim = self._buffer[self._index] + self._index += 1 + if delim not in ("'", '"'): + raise LLSDParseError("invalid string token at index %d." % self._index) + + rv = self._buffer[self._index:(self._index + size)] + self._index += size + cc = self._buffer[self._index] + self._index += 1 + if cc != delim: + raise LLSDParseError("invalid string token at index %d." % self._index) + + return rv + +def format_binary(something): + return '\n' + _format_binary_recurse(something) + +def _format_binary_recurse(something): + def _format_list(something): + array_builder = [] + array_builder.append('[' + struct.pack('!i', len(something))) + for item in something: + array_builder.append(_format_binary_recurse(item)) + array_builder.append(']') + return ''.join(array_builder) + + if something is None: + return '!' + elif isinstance(something, LLSD): + return _format_binary_recurse(something.thing) + elif isinstance(something, bool): + if something: + return '1' + else: + return '0' + elif isinstance(something, (int, long)): + return 'i' + struct.pack('!i', something) + elif isinstance(something, float): + return 'r' + struct.pack('!d', something) + elif isinstance(something, lluuid.UUID): + return 'u' + something._bits + elif isinstance(something, binary): + return 'b' + struct.pack('!i', len(something)) + something + elif isinstance(something, str): + return 's' + struct.pack('!i', len(something)) + something + elif isinstance(something, unicode): + something = something.encode('utf-8') + return 's' + struct.pack('!i', len(something)) + something + elif isinstance(something, uri): + return 'l' + struct.pack('!i', len(something)) + something + elif isinstance(something, datetime.datetime): + seconds_since_epoch = time.mktime(something.timetuple()) + return 'd' + struct.pack('!d', seconds_since_epoch) + elif isinstance(something, (list, tuple)): + return _format_list(something) + elif isinstance(something, dict): + map_builder = [] + map_builder.append('{' + struct.pack('!i', len(something))) + for key, value in something.items(): + if isinstance(key, unicode): + key = key.encode('utf-8') + map_builder.append('k' + struct.pack('!i', len(key)) + key) + map_builder.append(_format_binary_recurse(value)) + map_builder.append('}') + return ''.join(map_builder) + else: + try: + return _format_list(list(something)) + except TypeError: + raise LLSDSerializationError( + "Cannot serialize unknown type: %s (%s)" % + (type(something), something)) + + +def parse_binary(binary): + if binary.startswith(''): + just_binary = binary.split('\n', 1)[1] + else: + just_binary = binary + return LLSDBinaryParser().parse(just_binary) + +def parse_xml(something): + try: + return to_python(fromstring(something)[0]) + except ElementTreeError, err: + raise LLSDParseError(*err.args) + +def parse_notation(something): + return LLSDNotationParser().parse(something) + +def parse(something): + try: + something = string.lstrip(something) #remove any pre-trailing whitespace + if something.startswith(''): + return parse_binary(something) + # This should be better. + elif something.startswith('<'): + return parse_xml(something) + else: + return parse_notation(something) + except KeyError, e: + raise Exception('LLSD could not be parsed: %s' % (e,)) + +class LLSD(object): + def __init__(self, thing=None): + self.thing = thing + + def __str__(self): + return self.toXML(self.thing) + + parse = staticmethod(parse) + toXML = staticmethod(format_xml) + toPrettyXML = staticmethod(format_pretty_xml) + toBinary = staticmethod(format_binary) + toNotation = staticmethod(format_notation) + + +undef = LLSD(None) + +XML_MIME_TYPE = 'application/llsd+xml' +BINARY_MIME_TYPE = 'application/llsd+binary' + +# register converters for llsd in mulib, if it is available +try: + from mulib import stacked, mu + stacked.NoProducer() # just to exercise stacked + mu.safe_load(None) # just to exercise mu +except: + # mulib not available, don't print an error message since this is normal + pass +else: + mu.add_parser(parse, XML_MIME_TYPE) + mu.add_parser(parse, 'application/llsd+binary') + + def llsd_convert_xml(llsd_stuff, request): + request.write(format_xml(llsd_stuff)) + + def llsd_convert_binary(llsd_stuff, request): + request.write(format_binary(llsd_stuff)) + + for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]: + stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE) + stacked.add_producer(typ, llsd_convert_xml, 'application/xml') + stacked.add_producer(typ, llsd_convert_xml, 'text/xml') + + stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') + + stacked.add_producer(LLSD, llsd_convert_xml, '*/*') + + # in case someone is using the legacy mu.xml wrapper, we need to + # tell mu to produce application/xml or application/llsd+xml + # (based on the accept header) from raw xml. Phoenix 2008-07-21 + stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE) + stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml') + + + +# mulib wsgi stuff +# try: +# from mulib import mu, adapters +# +# # try some known attributes from mulib to be ultra-sure we've imported it +# mu.get_current +# adapters.handlers +# except: +# # mulib not available, don't print an error message since this is normal +# pass +# else: +# def llsd_xml_handler(content_type): +# def handle_llsd_xml(env, start_response): +# llsd_stuff, _ = mu.get_current(env) +# result = format_xml(llsd_stuff) +# start_response("200 OK", [('Content-Type', content_type)]) +# env['mu.negotiated_type'] = content_type +# yield result +# return handle_llsd_xml +# +# def llsd_binary_handler(content_type): +# def handle_llsd_binary(env, start_response): +# llsd_stuff, _ = mu.get_current(env) +# result = format_binary(llsd_stuff) +# start_response("200 OK", [('Content-Type', content_type)]) +# env['mu.negotiated_type'] = content_type +# yield result +# return handle_llsd_binary +# +# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse + +# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: +# for content_type in (XML_MIME_TYPE, 'application/xml'): +# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type) +# +# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE) +# +# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*') diff --git a/indra/viewer_components/manager/base/lluuid.py b/indra/viewer_components/manager/base/lluuid.py new file mode 100755 index 0000000000..7413ffe10d --- /dev/null +++ b/indra/viewer_components/manager/base/lluuid.py @@ -0,0 +1,319 @@ +"""\ +@file lluuid.py +@brief UUID parser/generator. + +$LicenseInfo:firstyear=2004&license=mit$ + +Copyright (c) 2004-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +import random, socket, string, time, re +import uuid +try: + # Python 2.6 + from hashlib import md5 +except ImportError: + # Python 2.5 and earlier + from md5 import new as md5 + +def _int2binstr(i,l): + s='' + for a in range(l): + s=chr(i&0xFF)+s + i>>=8 + return s + +def _binstr2int(s): + i = long(0) + for c in s: + i = (i<<8) + ord(c) + return i + +class UUID(object): + """ + A class which represents a 16 byte integer. Stored as a 16 byte 8 + bit character string. + + The string version is to be of the form: + AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) + where A=network address, B=timestamp, C=random. + """ + + NULL_STR = "00000000-0000-0000-0000-000000000000" + + # the UUIDREGEX_STRING is helpful for parsing UUID's in text + hex_wildcard = r"[0-9a-fA-F]" + word = hex_wildcard + r"{4,4}-" + long_word = hex_wildcard + r"{8,8}-" + very_long_word = hex_wildcard + r"{12,12}" + UUID_REGEX_STRING = long_word + word + word + word + very_long_word + uuid_regex = re.compile(UUID_REGEX_STRING) + + rand = random.Random() + ip = '' + try: + ip = socket.gethostbyname(socket.gethostname()) + except(socket.gaierror, socket.error): + # no ip address, so just default to somewhere in 10.x.x.x + ip = '10' + for i in range(3): + ip += '.' + str(rand.randrange(1,254)) + hexip = ''.join(["%04x" % long(i) for i in ip.split('.')]) + lastid = '' + + def __init__(self, possible_uuid=None): + """ + Initialize to first valid UUID in argument (if a string), + or to null UUID if none found or argument is not supplied. + + If the argument is a UUID, the constructed object will be a copy of it. + """ + self._bits = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + if possible_uuid is None: + return + + if isinstance(possible_uuid, type(self)): + self.set(possible_uuid) + return + + uuid_match = UUID.uuid_regex.search(possible_uuid) + if uuid_match: + uuid_string = uuid_match.group() + s = string.replace(uuid_string, '-', '') + self._bits = _int2binstr(string.atol(s[:8],16),4) + \ + _int2binstr(string.atol(s[8:16],16),4) + \ + _int2binstr(string.atol(s[16:24],16),4) + \ + _int2binstr(string.atol(s[24:],16),4) + + def __len__(self): + """ + Used by the len() builtin. + """ + return 36 + + def __nonzero__(self): + return self._bits != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + + def __str__(self): + uuid_string = self.toString() + return uuid_string + + __repr__ = __str__ + + def __getitem__(self, index): + return str(self)[index] + + def __eq__(self, other): + if isinstance(other, (str, unicode)): + return other == str(self) + return self._bits == getattr(other, '_bits', '') + + def __ne__(self, other): + return not self.__eq__(other) + + def __le__(self, other): + return self._bits <= other._bits + + def __ge__(self, other): + return self._bits >= other._bits + + def __lt__(self, other): + return self._bits < other._bits + + def __gt__(self, other): + return self._bits > other._bits + + def __hash__(self): + return hash(self._bits) + + def set(self, uuid): + self._bits = uuid._bits + + def setFromString(self, uuid_string): + """ + Given a string version of a uuid, set self bits + appropriately. Returns self. + """ + s = string.replace(uuid_string, '-', '') + self._bits = _int2binstr(string.atol(s[:8],16),4) + \ + _int2binstr(string.atol(s[8:16],16),4) + \ + _int2binstr(string.atol(s[16:24],16),4) + \ + _int2binstr(string.atol(s[24:],16),4) + return self + + def setFromMemoryDump(self, gdb_string): + """ + We expect to get gdb_string as four hex units. eg: + 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 + Which will be translated to: + db547d14-1b3f4bc3-9b984f71-d22f890a + Returns self. + """ + s = string.replace(gdb_string, '0x', '') + s = string.replace(s, ' ', '') + t = '' + for i in range(8,40,8): + for j in range(0,8,2): + t = t + s[i-j-2:i-j] + self.setFromString(t) + + def toString(self): + """ + Return as a string matching the LL standard + AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) + where A=network address, B=timestamp, C=random. + """ + return uuid_bits_to_string(self._bits) + + def getAsString(self): + """ + Return a different string representation of the form + AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) + where A=network address, B=timestamp, C=random. + """ + i1 = _binstr2int(self._bits[0:4]) + i2 = _binstr2int(self._bits[4:8]) + i3 = _binstr2int(self._bits[8:12]) + i4 = _binstr2int(self._bits[12:16]) + return '%08lx-%08lx-%08lx-%08lx' % (i1,i2,i3,i4) + + def generate(self): + """ + Generate a new uuid. This algorithm is slightly different + from c++ implementation for portability reasons. + Returns self. + """ + m = md5() + m.update(uuid.uuid1().bytes) + self._bits = m.digest() + return self + + def isNull(self): + """ + Returns 1 if the uuid is null - ie, equal to default uuid. + """ + return (self._bits == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0") + + def xor(self, rhs): + """ + xors self with rhs. + """ + v1 = _binstr2int(self._bits[0:4]) ^ _binstr2int(rhs._bits[0:4]) + v2 = _binstr2int(self._bits[4:8]) ^ _binstr2int(rhs._bits[4:8]) + v3 = _binstr2int(self._bits[8:12]) ^ _binstr2int(rhs._bits[8:12]) + v4 = _binstr2int(self._bits[12:16]) ^ _binstr2int(rhs._bits[12:16]) + self._bits = _int2binstr(v1,4) + \ + _int2binstr(v2,4) + \ + _int2binstr(v3,4) + \ + _int2binstr(v4,4) + + +# module-level null constant +NULL = UUID() + +def printTranslatedMemory(four_hex_uints): + """ + We expect to get the string as four hex units. eg: + 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 + Which will be translated to: + db547d14-1b3f4bc3-9b984f71-d22f890a + """ + uuid = UUID() + uuid.setFromMemoryDump(four_hex_uints) + print uuid.toString() + +def isUUID(id_str): + """ + This function returns: + - 1 if the string passed is a UUID + - 0 is the string passed is not a UUID + - None if it neither of the if's below is satisfied + """ + if not id_str or len(id_str) < 5 or len(id_str) > 36: + return 0 + + if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str): + return 1 + + return None + +def isPossiblyID(id_str): + """ + This function returns 1 if the string passed has some uuid-like + characteristics. Otherwise returns 0. + """ + + is_uuid = isUUID(id_str) + if is_uuid is not None: + return is_uuid + + # build a string which matches every character. + hex_wildcard = r"[0-9a-fA-F]" + chars = len(id_str) + next = min(chars, 8) + matcher = hex_wildcard+"{"+str(next)+","+str(next)+"}" + chars = chars - next + if chars > 0: + matcher = matcher + "-" + chars = chars - 1 + for block in range(3): + next = max(min(chars, 4), 0) + if next: + matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" + chars = chars - next + if chars > 0: + matcher = matcher + "-" + chars = chars - 1 + if chars > 0: + next = min(chars, 12) + matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" + #print matcher + uuid_matcher = re.compile(matcher) + if uuid_matcher.match(id_str): + return 1 + return 0 + +def uuid_bits_to_string(bits): + i1 = _binstr2int(bits[0:4]) + i2 = _binstr2int(bits[4:6]) + i3 = _binstr2int(bits[6:8]) + i4 = _binstr2int(bits[8:10]) + i5 = _binstr2int(bits[10:12]) + i6 = _binstr2int(bits[12:16]) + return '%08lx-%04lx-%04lx-%04lx-%04lx%08lx' % (i1,i2,i3,i4,i5,i6) + +def uuid_bits_to_uuid(bits): + return UUID(uuid_bits_to_string(bits)) + + +try: + from mulib import stacked + stacked.NoProducer() # just to exercise stacked +except: + #print "Couldn't import mulib.stacked, not registering UUID converter" + pass +else: + def convertUUID(uuid, req): + req.write(str(uuid)) + + stacked.add_producer(UUID, convertUUID, "*/*") + stacked.add_producer(UUID, convertUUID, "text/html") diff --git a/indra/viewer_components/manager/llsd.py b/indra/viewer_components/manager/llsd.py deleted file mode 100755 index 4527b115f9..0000000000 --- a/indra/viewer_components/manager/llsd.py +++ /dev/null @@ -1,1052 +0,0 @@ -"""\ -@file llsd.py -@brief Types as well as parsing and formatting functions for handling LLSD. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import datetime -import base64 -import string -import struct -import time -import types -import re - -from indra.util.fastest_elementtree import ElementTreeError, fromstring -from indra.base import lluuid - -# cllsd.c in server/server-1.25 has memory leaks, -# so disabling cllsd for now -#try: -# import cllsd -#except ImportError: -# cllsd = None -cllsd = None - -int_regex = re.compile(r"[-+]?\d+") -real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") -alpha_regex = re.compile(r"[a-zA-Z]+") -date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" - r"(?P\d{2}):(?P\d{2}):(?P\d{2})" - r"(?P(\.\d+)?)Z") -#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" - -class LLSDParseError(Exception): - pass - -class LLSDSerializationError(TypeError): - pass - - -class binary(str): - pass - -class uri(str): - pass - - -BOOL_TRUE = ('1', '1.0', 'true') -BOOL_FALSE = ('0', '0.0', 'false', '') - - -def format_datestr(v): - """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" - if hasattr(v, 'microsecond'): - return v.isoformat() + 'Z' - else: - return v.strftime('%Y-%m-%dT%H:%M:%SZ') - -def parse_datestr(datestr): - """Parses a datetime object from the string format shared by xml and notation serializations.""" - if datestr == "": - return datetime.datetime(1970, 1, 1) - - match = re.match(date_regex, datestr) - if not match: - raise LLSDParseError("invalid date string '%s'." % datestr) - - year = int(match.group('year')) - month = int(match.group('month')) - day = int(match.group('day')) - hour = int(match.group('hour')) - minute = int(match.group('minute')) - second = int(match.group('second')) - seconds_float = match.group('second_float') - microsecond = 0 - if seconds_float: - microsecond = int(float('0' + seconds_float) * 1e6) - return datetime.datetime(year, month, day, hour, minute, second, microsecond) - - -def bool_to_python(node): - val = node.text or '' - if val in BOOL_TRUE: - return True - else: - return False - -def int_to_python(node): - val = node.text or '' - if not val.strip(): - return 0 - return int(val) - -def real_to_python(node): - val = node.text or '' - if not val.strip(): - return 0.0 - return float(val) - -def uuid_to_python(node): - return lluuid.UUID(node.text) - -def str_to_python(node): - return node.text or '' - -def bin_to_python(node): - return binary(base64.decodestring(node.text or '')) - -def date_to_python(node): - val = node.text or '' - if not val: - val = "1970-01-01T00:00:00Z" - return parse_datestr(val) - - -def uri_to_python(node): - val = node.text or '' - if not val: - return None - return uri(val) - -def map_to_python(node): - result = {} - for index in range(len(node))[::2]: - result[node[index].text] = to_python(node[index+1]) - return result - -def array_to_python(node): - return [to_python(child) for child in node] - - -NODE_HANDLERS = dict( - undef=lambda x: None, - boolean=bool_to_python, - integer=int_to_python, - real=real_to_python, - uuid=uuid_to_python, - string=str_to_python, - binary=bin_to_python, - date=date_to_python, - uri=uri_to_python, - map=map_to_python, - array=array_to_python, - ) - -def to_python(node): - return NODE_HANDLERS[node.tag](node) - -class Nothing(object): - pass - - -class LLSDXMLFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def elt(self, name, contents=None): - if(contents is None or contents is ''): - return "<%s />" % (name,) - else: - if type(contents) is unicode: - contents = contents.encode('utf-8') - return "<%s>%s" % (name, contents, name) - - def xml_esc(self, v): - if type(v) is unicode: - v = v.encode('utf-8') - return v.replace('&', '&').replace('<', '<').replace('>', '>') - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return self.elt('undef') - def BOOLEAN(self, v): - if v: - return self.elt('boolean', 'true') - else: - return self.elt('boolean', 'false') - def INTEGER(self, v): - return self.elt('integer', v) - def REAL(self, v): - return self.elt('real', v) - def UUID(self, v): - if(v.isNull()): - return self.elt('uuid') - else: - return self.elt('uuid', v) - def BINARY(self, v): - return self.elt('binary', base64.encodestring(v)) - def STRING(self, v): - return self.elt('string', self.xml_esc(v)) - def URI(self, v): - return self.elt('uri', self.xml_esc(str(v))) - def DATE(self, v): - return self.elt('date', format_datestr(v)) - def ARRAY(self, v): - return self.elt('array', ''.join([self.generate(item) for item in v])) - def MAP(self, v): - return self.elt( - 'map', - ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) - for key, value in v.items()])) - - typeof = type - def generate(self, something): - t = self.typeof(something) - if self.type_map.has_key(t): - return self.type_map[t](something) - else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - t, something)) - - def _format(self, something): - return '' + self.elt("llsd", self.generate(something)) - - def format(self, something): - if cllsd: - return cllsd.llsd_to_xml(something) - return self._format(something) - -_g_xml_formatter = None -def format_xml(something): - global _g_xml_formatter - if _g_xml_formatter is None: - _g_xml_formatter = LLSDXMLFormatter() - return _g_xml_formatter.format(something) - -class LLSDXMLPrettyFormatter(LLSDXMLFormatter): - def __init__(self, indent_atom = None): - # Call the super class constructor so that we have the type map - super(LLSDXMLPrettyFormatter, self).__init__() - - # Override the type map to use our specialized formatters to - # emit the pretty output. - self.type_map[list] = self.PRETTY_ARRAY - self.type_map[tuple] = self.PRETTY_ARRAY - self.type_map[types.GeneratorType] = self.PRETTY_ARRAY, - self.type_map[dict] = self.PRETTY_MAP - - # Private data used for indentation. - self._indent_level = 1 - if indent_atom is None: - self._indent_atom = ' ' - else: - self._indent_atom = indent_atom - - def _indent(self): - "Return an indentation based on the atom and indentation level." - return self._indent_atom * self._indent_level - - def PRETTY_ARRAY(self, v): - rv = [] - rv.append('\n') - self._indent_level = self._indent_level + 1 - rv.extend(["%s%s\n" % - (self._indent(), - self.generate(item)) - for item in v]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('') - return ''.join(rv) - - def PRETTY_MAP(self, v): - rv = [] - rv.append('\n') - self._indent_level = self._indent_level + 1 - keys = v.keys() - keys.sort() - rv.extend(["%s%s\n%s%s\n" % - (self._indent(), - self.elt('key', key), - self._indent(), - self.generate(v[key])) - for key in keys]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('') - return ''.join(rv) - - def format(self, something): - data = [] - data.append('\n') - data.append(self.generate(something)) - data.append('\n') - return '\n'.join(data) - -def format_pretty_xml(something): - """@brief Serialize a python object as 'pretty' llsd xml. - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - This function is not necessarily suited for serializing very large - objects. It is not optimized by the cllsd module, and sorts on - dict (llsd map) keys alphabetically to ease human reading. - """ - return LLSDXMLPrettyFormatter().format(something) - -class LLSDNotationFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return '!' - def BOOLEAN(self, v): - if v: - return 'true' - else: - return 'false' - def INTEGER(self, v): - return "i%s" % v - def REAL(self, v): - return "r%s" % v - def UUID(self, v): - return "u%s" % v - def BINARY(self, v): - return 'b64"' + base64.encodestring(v) + '"' - def STRING(self, v): - if isinstance(v, unicode): - v = v.encode('utf-8') - return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") - def URI(self, v): - return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') - def DATE(self, v): - return 'd"%s"' % format_datestr(v) - def ARRAY(self, v): - return "[%s]" % ','.join([self.generate(item) for item in v]) - def MAP(self, v): - def fix(key): - if isinstance(key, unicode): - return key.encode('utf-8') - return key - return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) - for key, value in v.items()]) - - def generate(self, something): - t = type(something) - handler = self.type_map.get(t) - if handler: - return handler(something) - else: - try: - return self.ARRAY(iter(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % (t, something)) - - def format(self, something): - return self.generate(something) - -def format_notation(something): - return LLSDNotationFormatter().format(something) - -def _hex_as_nybble(hex): - if (hex >= '0') and (hex <= '9'): - return ord(hex) - ord('0') - elif (hex >= 'a') and (hex <='f'): - return 10 + ord(hex) - ord('a') - elif (hex >= 'A') and (hex <='F'): - return 10 + ord(hex) - ord('A'); - -class LLSDBinaryParser(object): - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the binary data to parse in an indexable sequence. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - self._buffer = buffer - self._index = 0 - self._keep_binary = not ignore_binary - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc == 'i': - # 'i' = integer - idx = self._index - self._index += 4 - return struct.unpack("!i", self._buffer[idx:idx+4])[0] - elif cc == ('r'): - # 'r' = real number - idx = self._index - self._index += 8 - return struct.unpack("!d", self._buffer[idx:idx+8])[0] - elif cc == 'u': - # 'u' = uuid - idx = self._index - self._index += 16 - return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16]) - elif cc == 's': - # 's' = string - return self._parse_string() - elif cc in ("'", '"'): - # delimited/escaped string - return self._parse_string_delim(cc) - elif cc == 'l': - # 'l' = uri - return uri(self._parse_string()) - elif cc == ('d'): - # 'd' = date in seconds since epoch - idx = self._index - self._index += 8 - seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0] - return datetime.datetime.fromtimestamp(seconds) - elif cc == 'b': - binary = self._parse_string() - if self._keep_binary: - return binary - # *NOTE: maybe have a binary placeholder which has the - # length. - return None - else: - raise LLSDParseError("invalid binary token at byte %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_map(self): - rv = {} - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - self._index += 1 - key = '' - while (cc != '}') and (count < size): - if cc == 'k': - key = self._parse_string() - elif cc in ("'", '"'): - key = self._parse_string_delim(cc) - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - value = self._parse() - rv[key] = value - count += 1 - cc = self._buffer[self._index] - self._index += 1 - if cc != '}': - raise LLSDParseError("invalid map close token at byte %d." % ( - self._index,)) - return rv - - def _parse_array(self): - rv = [] - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - while (cc != ']') and (count < size): - rv.append(self._parse()) - count += 1 - cc = self._buffer[self._index] - if cc != ']': - raise LLSDParseError("invalid array close token at byte %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_string(self): - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - rv = self._buffer[self._index:self._index+size] - self._index += size - return rv - - def _parse_string_delim(self, delim): - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - -class LLSDNotationParser(object): - """ Parse LLSD notation: - map: { string:object, string:object } - array: [ object, object, object ] - undef: ! - boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE - integer: i#### - real: r#### - uuid: u#### - string: "g\'day" | 'have a "nice" day' | s(size)"raw data" - uri: l"escaped" - date: d"YYYY-MM-DDTHH:MM:SS.FFZ" - binary: b##"ff3120ab1" | b(size)"raw data" - """ - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the notation string to parse. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - if buffer == "": - return False - - self._buffer = buffer - self._index = 0 - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc in ('F', 'f'): - self._skip_alpha() - return False - elif cc in ('T', 't'): - self._skip_alpha() - return True - elif cc == 'i': - # 'i' = integer - return self._parse_integer() - elif cc == ('r'): - # 'r' = real number - return self._parse_real() - elif cc == 'u': - # 'u' = uuid - return self._parse_uuid() - elif cc in ("'", '"', 's'): - return self._parse_string(cc) - elif cc == 'l': - # 'l' = uri - delim = self._buffer[self._index] - self._index += 1 - val = uri(self._parse_string(delim)) - if len(val) == 0: - return None - return val - elif cc == ('d'): - # 'd' = date in seconds since epoch - return self._parse_date() - elif cc == 'b': - return self._parse_binary() - else: - raise LLSDParseError("invalid token at index %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_binary(self): - i = self._index - if self._buffer[i:i+2] == '64': - q = self._buffer[i+2] - e = self._buffer.find(q, i+3) - try: - return base64.decodestring(self._buffer[i+3:e]) - finally: - self._index = e + 1 - else: - raise LLSDParseError('random horrible binary format not supported') - - def _parse_map(self): - """ map: { string:object, string:object } """ - rv = {} - cc = self._buffer[self._index] - self._index += 1 - key = '' - found_key = False - while (cc != '}'): - if not found_key: - if cc in ("'", '"', 's'): - key = self._parse_string(cc) - found_key = True - elif cc.isspace() or cc == ',': - cc = self._buffer[self._index] - self._index += 1 - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - elif cc.isspace() or cc == ':': - cc = self._buffer[self._index] - self._index += 1 - continue - else: - self._index += 1 - value = self._parse() - rv[key] = value - found_key = False - cc = self._buffer[self._index] - self._index += 1 - - return rv - - def _parse_array(self): - """ array: [ object, object, object ] """ - rv = [] - cc = self._buffer[self._index] - while (cc != ']'): - if cc.isspace() or cc == ',': - self._index += 1 - cc = self._buffer[self._index] - continue - rv.append(self._parse()) - cc = self._buffer[self._index] - - if cc != ']': - raise LLSDParseError("invalid array close token at index %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_uuid(self): - match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid uuid token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return lluuid.UUID(self._buffer[start:end]) - - def _skip_alpha(self): - match = re.match(alpha_regex, self._buffer[self._index:]) - if match: - self._index += match.end() - - def _parse_date(self): - delim = self._buffer[self._index] - self._index += 1 - datestr = self._parse_string(delim) - return parse_datestr(datestr) - - def _parse_real(self): - match = re.match(real_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid real token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return float( self._buffer[start:end] ) - - def _parse_integer(self): - match = re.match(int_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid integer token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return int( self._buffer[start:end] ) - - def _parse_string(self, delim): - """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """ - rv = "" - - if delim in ("'", '"'): - rv = self._parse_string_delim(delim) - elif delim == 's': - rv = self._parse_string_raw() - else: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - - - def _parse_string_delim(self, delim): - """ string: "g'day 'un" | 'have a "nice" day' """ - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - - def _parse_string_raw(self): - """ string: s(size)"raw data" """ - # Read the (size) portion. - cc = self._buffer[self._index] - self._index += 1 - if cc != '(': - raise LLSDParseError("invalid string token at index %d." % self._index) - - rparen = self._buffer.find(')', self._index) - if rparen == -1: - raise LLSDParseError("invalid string token at index %d." % self._index) - - size = int(self._buffer[self._index:rparen]) - - self._index = rparen + 1 - delim = self._buffer[self._index] - self._index += 1 - if delim not in ("'", '"'): - raise LLSDParseError("invalid string token at index %d." % self._index) - - rv = self._buffer[self._index:(self._index + size)] - self._index += size - cc = self._buffer[self._index] - self._index += 1 - if cc != delim: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - -def format_binary(something): - return '\n' + _format_binary_recurse(something) - -def _format_binary_recurse(something): - def _format_list(something): - array_builder = [] - array_builder.append('[' + struct.pack('!i', len(something))) - for item in something: - array_builder.append(_format_binary_recurse(item)) - array_builder.append(']') - return ''.join(array_builder) - - if something is None: - return '!' - elif isinstance(something, LLSD): - return _format_binary_recurse(something.thing) - elif isinstance(something, bool): - if something: - return '1' - else: - return '0' - elif isinstance(something, (int, long)): - return 'i' + struct.pack('!i', something) - elif isinstance(something, float): - return 'r' + struct.pack('!d', something) - elif isinstance(something, lluuid.UUID): - return 'u' + something._bits - elif isinstance(something, binary): - return 'b' + struct.pack('!i', len(something)) + something - elif isinstance(something, str): - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, unicode): - something = something.encode('utf-8') - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, uri): - return 'l' + struct.pack('!i', len(something)) + something - elif isinstance(something, datetime.datetime): - seconds_since_epoch = time.mktime(something.timetuple()) - return 'd' + struct.pack('!d', seconds_since_epoch) - elif isinstance(something, (list, tuple)): - return _format_list(something) - elif isinstance(something, dict): - map_builder = [] - map_builder.append('{' + struct.pack('!i', len(something))) - for key, value in something.items(): - if isinstance(key, unicode): - key = key.encode('utf-8') - map_builder.append('k' + struct.pack('!i', len(key)) + key) - map_builder.append(_format_binary_recurse(value)) - map_builder.append('}') - return ''.join(map_builder) - else: - try: - return _format_list(list(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % - (type(something), something)) - - -def parse_binary(binary): - if binary.startswith(''): - just_binary = binary.split('\n', 1)[1] - else: - just_binary = binary - return LLSDBinaryParser().parse(just_binary) - -def parse_xml(something): - try: - return to_python(fromstring(something)[0]) - except ElementTreeError, err: - raise LLSDParseError(*err.args) - -def parse_notation(something): - return LLSDNotationParser().parse(something) - -def parse(something): - try: - something = string.lstrip(something) #remove any pre-trailing whitespace - if something.startswith(''): - return parse_binary(something) - # This should be better. - elif something.startswith('<'): - return parse_xml(something) - else: - return parse_notation(something) - except KeyError, e: - raise Exception('LLSD could not be parsed: %s' % (e,)) - -class LLSD(object): - def __init__(self, thing=None): - self.thing = thing - - def __str__(self): - return self.toXML(self.thing) - - parse = staticmethod(parse) - toXML = staticmethod(format_xml) - toPrettyXML = staticmethod(format_pretty_xml) - toBinary = staticmethod(format_binary) - toNotation = staticmethod(format_notation) - - -undef = LLSD(None) - -XML_MIME_TYPE = 'application/llsd+xml' -BINARY_MIME_TYPE = 'application/llsd+binary' - -# register converters for llsd in mulib, if it is available -try: - from mulib import stacked, mu - stacked.NoProducer() # just to exercise stacked - mu.safe_load(None) # just to exercise mu -except: - # mulib not available, don't print an error message since this is normal - pass -else: - mu.add_parser(parse, XML_MIME_TYPE) - mu.add_parser(parse, 'application/llsd+binary') - - def llsd_convert_xml(llsd_stuff, request): - request.write(format_xml(llsd_stuff)) - - def llsd_convert_binary(llsd_stuff, request): - request.write(format_binary(llsd_stuff)) - - for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]: - stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE) - stacked.add_producer(typ, llsd_convert_xml, 'application/xml') - stacked.add_producer(typ, llsd_convert_xml, 'text/xml') - - stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') - - stacked.add_producer(LLSD, llsd_convert_xml, '*/*') - - # in case someone is using the legacy mu.xml wrapper, we need to - # tell mu to produce application/xml or application/llsd+xml - # (based on the accept header) from raw xml. Phoenix 2008-07-21 - stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE) - stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml') - - - -# mulib wsgi stuff -# try: -# from mulib import mu, adapters -# -# # try some known attributes from mulib to be ultra-sure we've imported it -# mu.get_current -# adapters.handlers -# except: -# # mulib not available, don't print an error message since this is normal -# pass -# else: -# def llsd_xml_handler(content_type): -# def handle_llsd_xml(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_xml(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_xml -# -# def llsd_binary_handler(content_type): -# def handle_llsd_binary(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_binary(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_binary -# -# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse - -# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: -# for content_type in (XML_MIME_TYPE, 'application/xml'): -# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type) -# -# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE) -# -# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*') diff --git a/indra/viewer_components/manager/util/fastest_elementtree.py b/indra/viewer_components/manager/util/fastest_elementtree.py new file mode 100755 index 0000000000..4fcf662dd9 --- /dev/null +++ b/indra/viewer_components/manager/util/fastest_elementtree.py @@ -0,0 +1,64 @@ +"""\ +@file fastest_elementtree.py +@brief Concealing some gnarly import logic in here. This should export the interface of elementtree. + +$LicenseInfo:firstyear=2008&license=mit$ + +Copyright (c) 2008-2009, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +# The parsing exception raised by the underlying library depends +# on the ElementTree implementation we're using, so we provide an +# alias here. +# +# Use ElementTreeError as the exception type for catching parsing +# errors. + + +# Using cElementTree might cause some unforeseen problems, so here's a +# convenient off switch. +use_celementree = True + +try: + if not use_celementree: + raise ImportError() + # Python 2.3 and 2.4. + from cElementTree import * + ElementTreeError = SyntaxError +except ImportError: + try: + if not use_celementree: + raise ImportError() + # Python 2.5 and above. + from xml.etree.cElementTree import * + ElementTreeError = SyntaxError + except ImportError: + # Pure Python code. + try: + # Python 2.3 and 2.4. + from elementtree.ElementTree import * + except ImportError: + # Python 2.5 and above. + from xml.etree.ElementTree import * + + # The pure Python ElementTree module uses Expat for parsing. + from xml.parsers.expat import ExpatError as ElementTreeError -- cgit v1.2.3 From 4428ee77c29fb2e0af50bf6d650abfeb33329b3f Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 21 Jul 2016 10:18:58 -0700 Subject: SL-323: make changes to include llbase as an autobuild pkg, undelete files that will be deleted with MAINT-6585 and no need to copy local files in viewer-manifest. --- autobuild.xml | 52 + indra/cmake/LLBase.cmake | 4 + indra/newview/CMakeLists.txt | 1 + indra/newview/viewer_manifest.py | 2 - indra/viewer_components/manager/base/llsd.py | 1052 -------------------- indra/viewer_components/manager/base/lluuid.py | 319 ------ .../manager/util/fastest_elementtree.py | 64 -- 7 files changed, 57 insertions(+), 1437 deletions(-) create mode 100644 indra/cmake/LLBase.cmake delete mode 100755 indra/viewer_components/manager/base/llsd.py delete mode 100755 indra/viewer_components/manager/base/lluuid.py delete mode 100755 indra/viewer_components/manager/util/fastest_elementtree.py diff --git a/autobuild.xml b/autobuild.xml index 072dfa678a..1c04fb85fe 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -1465,6 +1465,58 @@ version 0.0.1 + llbase + + copyright + Copyright (c) 2010, Linden Research, Inc. + license + mit + license_file + LICENSES/llbase-license.txt + name + llbase + platforms + + darwin + + archive + + hash + 313f5c8edd328e05c59cbe980512f84c + url + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/317790/arch/Darwin/installer/llbase-0.8.6.317790-darwin-317790.tar.bz2 + + name + darwin + + linux + + archive + + hash + e85171c70b57dd086b956ba36e6f2306 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/317790/arch/Linux/installer/llbase-0.8.6.317790-linux-317790.tar.bz2 + + name + linux + + windows + + archive + + hash + 07b0d64d812cfaf3025b799cea5fe446 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/317790/arch/CYGWIN/installer/llbase-0.8.6.0-windows-317790.tar.bz2 + + name + windows + + + version + 0.8.6.317790 + llceflib copyright diff --git a/indra/cmake/LLBase.cmake b/indra/cmake/LLBase.cmake new file mode 100644 index 0000000000..76e3c688a3 --- /dev/null +++ b/indra/cmake/LLBase.cmake @@ -0,0 +1,4 @@ +# -*- cmake -*- +include(Prebuilt) + +use_prebuilt_binary(llbase) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 132b57eb44..31e3711569 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -17,6 +17,7 @@ include(GooglePerfTools) include(Hunspell) include(JsonCpp) include(LLAppearance) +include(LLBase) include(LLAudio) include(LLCharacter) include(LLCommon) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index ac9b8d8ebe..d3ab05b9bd 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -715,8 +715,6 @@ class Darwin_i386_Manifest(ViewerManifest): #this copies over the python wrapper script and associated utilities, see SL-321, SL-322 and SL-323 self.path2basename("../viewer_components/manager","SL_Launcher") self.path2basename("../viewer_components/manager","*.py") - self.path2basename("../viewer_components/manager/base","*py") - self.path2basename("../viewer_components/manager/util","*py") self.end_prefix() # most everything goes in the Resources directory diff --git a/indra/viewer_components/manager/base/llsd.py b/indra/viewer_components/manager/base/llsd.py deleted file mode 100755 index 4527b115f9..0000000000 --- a/indra/viewer_components/manager/base/llsd.py +++ /dev/null @@ -1,1052 +0,0 @@ -"""\ -@file llsd.py -@brief Types as well as parsing and formatting functions for handling LLSD. - -$LicenseInfo:firstyear=2006&license=mit$ - -Copyright (c) 2006-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import datetime -import base64 -import string -import struct -import time -import types -import re - -from indra.util.fastest_elementtree import ElementTreeError, fromstring -from indra.base import lluuid - -# cllsd.c in server/server-1.25 has memory leaks, -# so disabling cllsd for now -#try: -# import cllsd -#except ImportError: -# cllsd = None -cllsd = None - -int_regex = re.compile(r"[-+]?\d+") -real_regex = re.compile(r"[-+]?(\d+(\.\d*)?|\d*\.\d+)([eE][-+]?\d+)?") -alpha_regex = re.compile(r"[a-zA-Z]+") -date_regex = re.compile(r"(?P\d{4})-(?P\d{2})-(?P\d{2})T" - r"(?P\d{2}):(?P\d{2}):(?P\d{2})" - r"(?P(\.\d+)?)Z") -#date: d"YYYY-MM-DDTHH:MM:SS.FFFFFFZ" - -class LLSDParseError(Exception): - pass - -class LLSDSerializationError(TypeError): - pass - - -class binary(str): - pass - -class uri(str): - pass - - -BOOL_TRUE = ('1', '1.0', 'true') -BOOL_FALSE = ('0', '0.0', 'false', '') - - -def format_datestr(v): - """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" - if hasattr(v, 'microsecond'): - return v.isoformat() + 'Z' - else: - return v.strftime('%Y-%m-%dT%H:%M:%SZ') - -def parse_datestr(datestr): - """Parses a datetime object from the string format shared by xml and notation serializations.""" - if datestr == "": - return datetime.datetime(1970, 1, 1) - - match = re.match(date_regex, datestr) - if not match: - raise LLSDParseError("invalid date string '%s'." % datestr) - - year = int(match.group('year')) - month = int(match.group('month')) - day = int(match.group('day')) - hour = int(match.group('hour')) - minute = int(match.group('minute')) - second = int(match.group('second')) - seconds_float = match.group('second_float') - microsecond = 0 - if seconds_float: - microsecond = int(float('0' + seconds_float) * 1e6) - return datetime.datetime(year, month, day, hour, minute, second, microsecond) - - -def bool_to_python(node): - val = node.text or '' - if val in BOOL_TRUE: - return True - else: - return False - -def int_to_python(node): - val = node.text or '' - if not val.strip(): - return 0 - return int(val) - -def real_to_python(node): - val = node.text or '' - if not val.strip(): - return 0.0 - return float(val) - -def uuid_to_python(node): - return lluuid.UUID(node.text) - -def str_to_python(node): - return node.text or '' - -def bin_to_python(node): - return binary(base64.decodestring(node.text or '')) - -def date_to_python(node): - val = node.text or '' - if not val: - val = "1970-01-01T00:00:00Z" - return parse_datestr(val) - - -def uri_to_python(node): - val = node.text or '' - if not val: - return None - return uri(val) - -def map_to_python(node): - result = {} - for index in range(len(node))[::2]: - result[node[index].text] = to_python(node[index+1]) - return result - -def array_to_python(node): - return [to_python(child) for child in node] - - -NODE_HANDLERS = dict( - undef=lambda x: None, - boolean=bool_to_python, - integer=int_to_python, - real=real_to_python, - uuid=uuid_to_python, - string=str_to_python, - binary=bin_to_python, - date=date_to_python, - uri=uri_to_python, - map=map_to_python, - array=array_to_python, - ) - -def to_python(node): - return NODE_HANDLERS[node.tag](node) - -class Nothing(object): - pass - - -class LLSDXMLFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def elt(self, name, contents=None): - if(contents is None or contents is ''): - return "<%s />" % (name,) - else: - if type(contents) is unicode: - contents = contents.encode('utf-8') - return "<%s>%s" % (name, contents, name) - - def xml_esc(self, v): - if type(v) is unicode: - v = v.encode('utf-8') - return v.replace('&', '&').replace('<', '<').replace('>', '>') - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return self.elt('undef') - def BOOLEAN(self, v): - if v: - return self.elt('boolean', 'true') - else: - return self.elt('boolean', 'false') - def INTEGER(self, v): - return self.elt('integer', v) - def REAL(self, v): - return self.elt('real', v) - def UUID(self, v): - if(v.isNull()): - return self.elt('uuid') - else: - return self.elt('uuid', v) - def BINARY(self, v): - return self.elt('binary', base64.encodestring(v)) - def STRING(self, v): - return self.elt('string', self.xml_esc(v)) - def URI(self, v): - return self.elt('uri', self.xml_esc(str(v))) - def DATE(self, v): - return self.elt('date', format_datestr(v)) - def ARRAY(self, v): - return self.elt('array', ''.join([self.generate(item) for item in v])) - def MAP(self, v): - return self.elt( - 'map', - ''.join(["%s%s" % (self.elt('key', self.xml_esc(str(key))), self.generate(value)) - for key, value in v.items()])) - - typeof = type - def generate(self, something): - t = self.typeof(something) - if self.type_map.has_key(t): - return self.type_map[t](something) - else: - raise LLSDSerializationError("Cannot serialize unknown type: %s (%s)" % ( - t, something)) - - def _format(self, something): - return '' + self.elt("llsd", self.generate(something)) - - def format(self, something): - if cllsd: - return cllsd.llsd_to_xml(something) - return self._format(something) - -_g_xml_formatter = None -def format_xml(something): - global _g_xml_formatter - if _g_xml_formatter is None: - _g_xml_formatter = LLSDXMLFormatter() - return _g_xml_formatter.format(something) - -class LLSDXMLPrettyFormatter(LLSDXMLFormatter): - def __init__(self, indent_atom = None): - # Call the super class constructor so that we have the type map - super(LLSDXMLPrettyFormatter, self).__init__() - - # Override the type map to use our specialized formatters to - # emit the pretty output. - self.type_map[list] = self.PRETTY_ARRAY - self.type_map[tuple] = self.PRETTY_ARRAY - self.type_map[types.GeneratorType] = self.PRETTY_ARRAY, - self.type_map[dict] = self.PRETTY_MAP - - # Private data used for indentation. - self._indent_level = 1 - if indent_atom is None: - self._indent_atom = ' ' - else: - self._indent_atom = indent_atom - - def _indent(self): - "Return an indentation based on the atom and indentation level." - return self._indent_atom * self._indent_level - - def PRETTY_ARRAY(self, v): - rv = [] - rv.append('\n') - self._indent_level = self._indent_level + 1 - rv.extend(["%s%s\n" % - (self._indent(), - self.generate(item)) - for item in v]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('') - return ''.join(rv) - - def PRETTY_MAP(self, v): - rv = [] - rv.append('\n') - self._indent_level = self._indent_level + 1 - keys = v.keys() - keys.sort() - rv.extend(["%s%s\n%s%s\n" % - (self._indent(), - self.elt('key', key), - self._indent(), - self.generate(v[key])) - for key in keys]) - self._indent_level = self._indent_level - 1 - rv.append(self._indent()) - rv.append('') - return ''.join(rv) - - def format(self, something): - data = [] - data.append('\n') - data.append(self.generate(something)) - data.append('\n') - return '\n'.join(data) - -def format_pretty_xml(something): - """@brief Serialize a python object as 'pretty' llsd xml. - - The output conforms to the LLSD DTD, unlike the output from the - standard python xml.dom DOM::toprettyxml() method which does not - preserve significant whitespace. - This function is not necessarily suited for serializing very large - objects. It is not optimized by the cllsd module, and sorts on - dict (llsd map) keys alphabetically to ease human reading. - """ - return LLSDXMLPrettyFormatter().format(something) - -class LLSDNotationFormatter(object): - def __init__(self): - self.type_map = { - type(None) : self.UNDEF, - bool : self.BOOLEAN, - int : self.INTEGER, - long : self.INTEGER, - float : self.REAL, - lluuid.UUID : self.UUID, - binary : self.BINARY, - str : self.STRING, - unicode : self.STRING, - uri : self.URI, - datetime.datetime : self.DATE, - datetime.date : self.DATE, - list : self.ARRAY, - tuple : self.ARRAY, - types.GeneratorType : self.ARRAY, - dict : self.MAP, - LLSD : self.LLSD - } - - def LLSD(self, v): - return self.generate(v.thing) - def UNDEF(self, v): - return '!' - def BOOLEAN(self, v): - if v: - return 'true' - else: - return 'false' - def INTEGER(self, v): - return "i%s" % v - def REAL(self, v): - return "r%s" % v - def UUID(self, v): - return "u%s" % v - def BINARY(self, v): - return 'b64"' + base64.encodestring(v) + '"' - def STRING(self, v): - if isinstance(v, unicode): - v = v.encode('utf-8') - return "'%s'" % v.replace("\\", "\\\\").replace("'", "\\'") - def URI(self, v): - return 'l"%s"' % str(v).replace("\\", "\\\\").replace('"', '\\"') - def DATE(self, v): - return 'd"%s"' % format_datestr(v) - def ARRAY(self, v): - return "[%s]" % ','.join([self.generate(item) for item in v]) - def MAP(self, v): - def fix(key): - if isinstance(key, unicode): - return key.encode('utf-8') - return key - return "{%s}" % ','.join(["'%s':%s" % (fix(key).replace("\\", "\\\\").replace("'", "\\'"), self.generate(value)) - for key, value in v.items()]) - - def generate(self, something): - t = type(something) - handler = self.type_map.get(t) - if handler: - return handler(something) - else: - try: - return self.ARRAY(iter(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % (t, something)) - - def format(self, something): - return self.generate(something) - -def format_notation(something): - return LLSDNotationFormatter().format(something) - -def _hex_as_nybble(hex): - if (hex >= '0') and (hex <= '9'): - return ord(hex) - ord('0') - elif (hex >= 'a') and (hex <='f'): - return 10 + ord(hex) - ord('a') - elif (hex >= 'A') and (hex <='F'): - return 10 + ord(hex) - ord('A'); - -class LLSDBinaryParser(object): - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the binary data to parse in an indexable sequence. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - self._buffer = buffer - self._index = 0 - self._keep_binary = not ignore_binary - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc == 'i': - # 'i' = integer - idx = self._index - self._index += 4 - return struct.unpack("!i", self._buffer[idx:idx+4])[0] - elif cc == ('r'): - # 'r' = real number - idx = self._index - self._index += 8 - return struct.unpack("!d", self._buffer[idx:idx+8])[0] - elif cc == 'u': - # 'u' = uuid - idx = self._index - self._index += 16 - return lluuid.uuid_bits_to_uuid(self._buffer[idx:idx+16]) - elif cc == 's': - # 's' = string - return self._parse_string() - elif cc in ("'", '"'): - # delimited/escaped string - return self._parse_string_delim(cc) - elif cc == 'l': - # 'l' = uri - return uri(self._parse_string()) - elif cc == ('d'): - # 'd' = date in seconds since epoch - idx = self._index - self._index += 8 - seconds = struct.unpack("!d", self._buffer[idx:idx+8])[0] - return datetime.datetime.fromtimestamp(seconds) - elif cc == 'b': - binary = self._parse_string() - if self._keep_binary: - return binary - # *NOTE: maybe have a binary placeholder which has the - # length. - return None - else: - raise LLSDParseError("invalid binary token at byte %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_map(self): - rv = {} - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - self._index += 1 - key = '' - while (cc != '}') and (count < size): - if cc == 'k': - key = self._parse_string() - elif cc in ("'", '"'): - key = self._parse_string_delim(cc) - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - value = self._parse() - rv[key] = value - count += 1 - cc = self._buffer[self._index] - self._index += 1 - if cc != '}': - raise LLSDParseError("invalid map close token at byte %d." % ( - self._index,)) - return rv - - def _parse_array(self): - rv = [] - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - count = 0 - cc = self._buffer[self._index] - while (cc != ']') and (count < size): - rv.append(self._parse()) - count += 1 - cc = self._buffer[self._index] - if cc != ']': - raise LLSDParseError("invalid array close token at byte %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_string(self): - size = struct.unpack("!i", self._buffer[self._index:self._index+4])[0] - self._index += 4 - rv = self._buffer[self._index:self._index+size] - self._index += size - return rv - - def _parse_string_delim(self, delim): - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - -class LLSDNotationParser(object): - """ Parse LLSD notation: - map: { string:object, string:object } - array: [ object, object, object ] - undef: ! - boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE - integer: i#### - real: r#### - uuid: u#### - string: "g\'day" | 'have a "nice" day' | s(size)"raw data" - uri: l"escaped" - date: d"YYYY-MM-DDTHH:MM:SS.FFZ" - binary: b##"ff3120ab1" | b(size)"raw data" - """ - def __init__(self): - pass - - def parse(self, buffer, ignore_binary = False): - """ - This is the basic public interface for parsing. - - @param buffer the notation string to parse. - @param ignore_binary parser throws away data in llsd binary nodes. - @return returns a python object. - """ - if buffer == "": - return False - - self._buffer = buffer - self._index = 0 - return self._parse() - - def _parse(self): - cc = self._buffer[self._index] - self._index += 1 - if cc == '{': - return self._parse_map() - elif cc == '[': - return self._parse_array() - elif cc == '!': - return None - elif cc == '0': - return False - elif cc == '1': - return True - elif cc in ('F', 'f'): - self._skip_alpha() - return False - elif cc in ('T', 't'): - self._skip_alpha() - return True - elif cc == 'i': - # 'i' = integer - return self._parse_integer() - elif cc == ('r'): - # 'r' = real number - return self._parse_real() - elif cc == 'u': - # 'u' = uuid - return self._parse_uuid() - elif cc in ("'", '"', 's'): - return self._parse_string(cc) - elif cc == 'l': - # 'l' = uri - delim = self._buffer[self._index] - self._index += 1 - val = uri(self._parse_string(delim)) - if len(val) == 0: - return None - return val - elif cc == ('d'): - # 'd' = date in seconds since epoch - return self._parse_date() - elif cc == 'b': - return self._parse_binary() - else: - raise LLSDParseError("invalid token at index %d: %d" % ( - self._index - 1, ord(cc))) - - def _parse_binary(self): - i = self._index - if self._buffer[i:i+2] == '64': - q = self._buffer[i+2] - e = self._buffer.find(q, i+3) - try: - return base64.decodestring(self._buffer[i+3:e]) - finally: - self._index = e + 1 - else: - raise LLSDParseError('random horrible binary format not supported') - - def _parse_map(self): - """ map: { string:object, string:object } """ - rv = {} - cc = self._buffer[self._index] - self._index += 1 - key = '' - found_key = False - while (cc != '}'): - if not found_key: - if cc in ("'", '"', 's'): - key = self._parse_string(cc) - found_key = True - elif cc.isspace() or cc == ',': - cc = self._buffer[self._index] - self._index += 1 - else: - raise LLSDParseError("invalid map key at byte %d." % ( - self._index - 1,)) - elif cc.isspace() or cc == ':': - cc = self._buffer[self._index] - self._index += 1 - continue - else: - self._index += 1 - value = self._parse() - rv[key] = value - found_key = False - cc = self._buffer[self._index] - self._index += 1 - - return rv - - def _parse_array(self): - """ array: [ object, object, object ] """ - rv = [] - cc = self._buffer[self._index] - while (cc != ']'): - if cc.isspace() or cc == ',': - self._index += 1 - cc = self._buffer[self._index] - continue - rv.append(self._parse()) - cc = self._buffer[self._index] - - if cc != ']': - raise LLSDParseError("invalid array close token at index %d." % ( - self._index,)) - self._index += 1 - return rv - - def _parse_uuid(self): - match = re.match(lluuid.UUID.uuid_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid uuid token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return lluuid.UUID(self._buffer[start:end]) - - def _skip_alpha(self): - match = re.match(alpha_regex, self._buffer[self._index:]) - if match: - self._index += match.end() - - def _parse_date(self): - delim = self._buffer[self._index] - self._index += 1 - datestr = self._parse_string(delim) - return parse_datestr(datestr) - - def _parse_real(self): - match = re.match(real_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid real token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return float( self._buffer[start:end] ) - - def _parse_integer(self): - match = re.match(int_regex, self._buffer[self._index:]) - if not match: - raise LLSDParseError("invalid integer token at index %d." % self._index) - - (start, end) = match.span() - start += self._index - end += self._index - self._index = end - return int( self._buffer[start:end] ) - - def _parse_string(self, delim): - """ string: "g\'day" | 'have a "nice" day' | s(size)"raw data" """ - rv = "" - - if delim in ("'", '"'): - rv = self._parse_string_delim(delim) - elif delim == 's': - rv = self._parse_string_raw() - else: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - - - def _parse_string_delim(self, delim): - """ string: "g'day 'un" | 'have a "nice" day' """ - list = [] - found_escape = False - found_hex = False - found_digit = False - byte = 0 - while True: - cc = self._buffer[self._index] - self._index += 1 - if found_escape: - if found_hex: - if found_digit: - found_escape = False - found_hex = False - found_digit = False - byte <<= 4 - byte |= _hex_as_nybble(cc) - list.append(chr(byte)) - byte = 0 - else: - found_digit = True - byte = _hex_as_nybble(cc) - elif cc == 'x': - found_hex = True - else: - if cc == 'a': - list.append('\a') - elif cc == 'b': - list.append('\b') - elif cc == 'f': - list.append('\f') - elif cc == 'n': - list.append('\n') - elif cc == 'r': - list.append('\r') - elif cc == 't': - list.append('\t') - elif cc == 'v': - list.append('\v') - else: - list.append(cc) - found_escape = False - elif cc == '\\': - found_escape = True - elif cc == delim: - break - else: - list.append(cc) - return ''.join(list) - - def _parse_string_raw(self): - """ string: s(size)"raw data" """ - # Read the (size) portion. - cc = self._buffer[self._index] - self._index += 1 - if cc != '(': - raise LLSDParseError("invalid string token at index %d." % self._index) - - rparen = self._buffer.find(')', self._index) - if rparen == -1: - raise LLSDParseError("invalid string token at index %d." % self._index) - - size = int(self._buffer[self._index:rparen]) - - self._index = rparen + 1 - delim = self._buffer[self._index] - self._index += 1 - if delim not in ("'", '"'): - raise LLSDParseError("invalid string token at index %d." % self._index) - - rv = self._buffer[self._index:(self._index + size)] - self._index += size - cc = self._buffer[self._index] - self._index += 1 - if cc != delim: - raise LLSDParseError("invalid string token at index %d." % self._index) - - return rv - -def format_binary(something): - return '\n' + _format_binary_recurse(something) - -def _format_binary_recurse(something): - def _format_list(something): - array_builder = [] - array_builder.append('[' + struct.pack('!i', len(something))) - for item in something: - array_builder.append(_format_binary_recurse(item)) - array_builder.append(']') - return ''.join(array_builder) - - if something is None: - return '!' - elif isinstance(something, LLSD): - return _format_binary_recurse(something.thing) - elif isinstance(something, bool): - if something: - return '1' - else: - return '0' - elif isinstance(something, (int, long)): - return 'i' + struct.pack('!i', something) - elif isinstance(something, float): - return 'r' + struct.pack('!d', something) - elif isinstance(something, lluuid.UUID): - return 'u' + something._bits - elif isinstance(something, binary): - return 'b' + struct.pack('!i', len(something)) + something - elif isinstance(something, str): - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, unicode): - something = something.encode('utf-8') - return 's' + struct.pack('!i', len(something)) + something - elif isinstance(something, uri): - return 'l' + struct.pack('!i', len(something)) + something - elif isinstance(something, datetime.datetime): - seconds_since_epoch = time.mktime(something.timetuple()) - return 'd' + struct.pack('!d', seconds_since_epoch) - elif isinstance(something, (list, tuple)): - return _format_list(something) - elif isinstance(something, dict): - map_builder = [] - map_builder.append('{' + struct.pack('!i', len(something))) - for key, value in something.items(): - if isinstance(key, unicode): - key = key.encode('utf-8') - map_builder.append('k' + struct.pack('!i', len(key)) + key) - map_builder.append(_format_binary_recurse(value)) - map_builder.append('}') - return ''.join(map_builder) - else: - try: - return _format_list(list(something)) - except TypeError: - raise LLSDSerializationError( - "Cannot serialize unknown type: %s (%s)" % - (type(something), something)) - - -def parse_binary(binary): - if binary.startswith(''): - just_binary = binary.split('\n', 1)[1] - else: - just_binary = binary - return LLSDBinaryParser().parse(just_binary) - -def parse_xml(something): - try: - return to_python(fromstring(something)[0]) - except ElementTreeError, err: - raise LLSDParseError(*err.args) - -def parse_notation(something): - return LLSDNotationParser().parse(something) - -def parse(something): - try: - something = string.lstrip(something) #remove any pre-trailing whitespace - if something.startswith(''): - return parse_binary(something) - # This should be better. - elif something.startswith('<'): - return parse_xml(something) - else: - return parse_notation(something) - except KeyError, e: - raise Exception('LLSD could not be parsed: %s' % (e,)) - -class LLSD(object): - def __init__(self, thing=None): - self.thing = thing - - def __str__(self): - return self.toXML(self.thing) - - parse = staticmethod(parse) - toXML = staticmethod(format_xml) - toPrettyXML = staticmethod(format_pretty_xml) - toBinary = staticmethod(format_binary) - toNotation = staticmethod(format_notation) - - -undef = LLSD(None) - -XML_MIME_TYPE = 'application/llsd+xml' -BINARY_MIME_TYPE = 'application/llsd+binary' - -# register converters for llsd in mulib, if it is available -try: - from mulib import stacked, mu - stacked.NoProducer() # just to exercise stacked - mu.safe_load(None) # just to exercise mu -except: - # mulib not available, don't print an error message since this is normal - pass -else: - mu.add_parser(parse, XML_MIME_TYPE) - mu.add_parser(parse, 'application/llsd+binary') - - def llsd_convert_xml(llsd_stuff, request): - request.write(format_xml(llsd_stuff)) - - def llsd_convert_binary(llsd_stuff, request): - request.write(format_binary(llsd_stuff)) - - for typ in [LLSD, dict, list, tuple, str, int, long, float, bool, unicode, type(None)]: - stacked.add_producer(typ, llsd_convert_xml, XML_MIME_TYPE) - stacked.add_producer(typ, llsd_convert_xml, 'application/xml') - stacked.add_producer(typ, llsd_convert_xml, 'text/xml') - - stacked.add_producer(typ, llsd_convert_binary, 'application/llsd+binary') - - stacked.add_producer(LLSD, llsd_convert_xml, '*/*') - - # in case someone is using the legacy mu.xml wrapper, we need to - # tell mu to produce application/xml or application/llsd+xml - # (based on the accept header) from raw xml. Phoenix 2008-07-21 - stacked.add_producer(mu.xml, mu.produce_raw, XML_MIME_TYPE) - stacked.add_producer(mu.xml, mu.produce_raw, 'application/xml') - - - -# mulib wsgi stuff -# try: -# from mulib import mu, adapters -# -# # try some known attributes from mulib to be ultra-sure we've imported it -# mu.get_current -# adapters.handlers -# except: -# # mulib not available, don't print an error message since this is normal -# pass -# else: -# def llsd_xml_handler(content_type): -# def handle_llsd_xml(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_xml(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_xml -# -# def llsd_binary_handler(content_type): -# def handle_llsd_binary(env, start_response): -# llsd_stuff, _ = mu.get_current(env) -# result = format_binary(llsd_stuff) -# start_response("200 OK", [('Content-Type', content_type)]) -# env['mu.negotiated_type'] = content_type -# yield result -# return handle_llsd_binary -# -# adapters.DEFAULT_PARSERS[XML_MIME_TYPE] = parse - -# for typ in [LLSD, dict, list, tuple, str, int, float, bool, unicode, type(None)]: -# for content_type in (XML_MIME_TYPE, 'application/xml'): -# adapters.handlers.set_handler(typ, llsd_xml_handler(content_type), content_type) -# -# adapters.handlers.set_handler(typ, llsd_binary_handler(BINARY_MIME_TYPE), BINARY_MIME_TYPE) -# -# adapters.handlers.set_handler(LLSD, llsd_xml_handler(XML_MIME_TYPE), '*/*') diff --git a/indra/viewer_components/manager/base/lluuid.py b/indra/viewer_components/manager/base/lluuid.py deleted file mode 100755 index 7413ffe10d..0000000000 --- a/indra/viewer_components/manager/base/lluuid.py +++ /dev/null @@ -1,319 +0,0 @@ -"""\ -@file lluuid.py -@brief UUID parser/generator. - -$LicenseInfo:firstyear=2004&license=mit$ - -Copyright (c) 2004-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -import random, socket, string, time, re -import uuid -try: - # Python 2.6 - from hashlib import md5 -except ImportError: - # Python 2.5 and earlier - from md5 import new as md5 - -def _int2binstr(i,l): - s='' - for a in range(l): - s=chr(i&0xFF)+s - i>>=8 - return s - -def _binstr2int(s): - i = long(0) - for c in s: - i = (i<<8) + ord(c) - return i - -class UUID(object): - """ - A class which represents a 16 byte integer. Stored as a 16 byte 8 - bit character string. - - The string version is to be of the form: - AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - - NULL_STR = "00000000-0000-0000-0000-000000000000" - - # the UUIDREGEX_STRING is helpful for parsing UUID's in text - hex_wildcard = r"[0-9a-fA-F]" - word = hex_wildcard + r"{4,4}-" - long_word = hex_wildcard + r"{8,8}-" - very_long_word = hex_wildcard + r"{12,12}" - UUID_REGEX_STRING = long_word + word + word + word + very_long_word - uuid_regex = re.compile(UUID_REGEX_STRING) - - rand = random.Random() - ip = '' - try: - ip = socket.gethostbyname(socket.gethostname()) - except(socket.gaierror, socket.error): - # no ip address, so just default to somewhere in 10.x.x.x - ip = '10' - for i in range(3): - ip += '.' + str(rand.randrange(1,254)) - hexip = ''.join(["%04x" % long(i) for i in ip.split('.')]) - lastid = '' - - def __init__(self, possible_uuid=None): - """ - Initialize to first valid UUID in argument (if a string), - or to null UUID if none found or argument is not supplied. - - If the argument is a UUID, the constructed object will be a copy of it. - """ - self._bits = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - if possible_uuid is None: - return - - if isinstance(possible_uuid, type(self)): - self.set(possible_uuid) - return - - uuid_match = UUID.uuid_regex.search(possible_uuid) - if uuid_match: - uuid_string = uuid_match.group() - s = string.replace(uuid_string, '-', '') - self._bits = _int2binstr(string.atol(s[:8],16),4) + \ - _int2binstr(string.atol(s[8:16],16),4) + \ - _int2binstr(string.atol(s[16:24],16),4) + \ - _int2binstr(string.atol(s[24:],16),4) - - def __len__(self): - """ - Used by the len() builtin. - """ - return 36 - - def __nonzero__(self): - return self._bits != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" - - def __str__(self): - uuid_string = self.toString() - return uuid_string - - __repr__ = __str__ - - def __getitem__(self, index): - return str(self)[index] - - def __eq__(self, other): - if isinstance(other, (str, unicode)): - return other == str(self) - return self._bits == getattr(other, '_bits', '') - - def __ne__(self, other): - return not self.__eq__(other) - - def __le__(self, other): - return self._bits <= other._bits - - def __ge__(self, other): - return self._bits >= other._bits - - def __lt__(self, other): - return self._bits < other._bits - - def __gt__(self, other): - return self._bits > other._bits - - def __hash__(self): - return hash(self._bits) - - def set(self, uuid): - self._bits = uuid._bits - - def setFromString(self, uuid_string): - """ - Given a string version of a uuid, set self bits - appropriately. Returns self. - """ - s = string.replace(uuid_string, '-', '') - self._bits = _int2binstr(string.atol(s[:8],16),4) + \ - _int2binstr(string.atol(s[8:16],16),4) + \ - _int2binstr(string.atol(s[16:24],16),4) + \ - _int2binstr(string.atol(s[24:],16),4) - return self - - def setFromMemoryDump(self, gdb_string): - """ - We expect to get gdb_string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 - Which will be translated to: - db547d14-1b3f4bc3-9b984f71-d22f890a - Returns self. - """ - s = string.replace(gdb_string, '0x', '') - s = string.replace(s, ' ', '') - t = '' - for i in range(8,40,8): - for j in range(0,8,2): - t = t + s[i-j-2:i-j] - self.setFromString(t) - - def toString(self): - """ - Return as a string matching the LL standard - AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - return uuid_bits_to_string(self._bits) - - def getAsString(self): - """ - Return a different string representation of the form - AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC (a 128-bit number in hex) - where A=network address, B=timestamp, C=random. - """ - i1 = _binstr2int(self._bits[0:4]) - i2 = _binstr2int(self._bits[4:8]) - i3 = _binstr2int(self._bits[8:12]) - i4 = _binstr2int(self._bits[12:16]) - return '%08lx-%08lx-%08lx-%08lx' % (i1,i2,i3,i4) - - def generate(self): - """ - Generate a new uuid. This algorithm is slightly different - from c++ implementation for portability reasons. - Returns self. - """ - m = md5() - m.update(uuid.uuid1().bytes) - self._bits = m.digest() - return self - - def isNull(self): - """ - Returns 1 if the uuid is null - ie, equal to default uuid. - """ - return (self._bits == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0") - - def xor(self, rhs): - """ - xors self with rhs. - """ - v1 = _binstr2int(self._bits[0:4]) ^ _binstr2int(rhs._bits[0:4]) - v2 = _binstr2int(self._bits[4:8]) ^ _binstr2int(rhs._bits[4:8]) - v3 = _binstr2int(self._bits[8:12]) ^ _binstr2int(rhs._bits[8:12]) - v4 = _binstr2int(self._bits[12:16]) ^ _binstr2int(rhs._bits[12:16]) - self._bits = _int2binstr(v1,4) + \ - _int2binstr(v2,4) + \ - _int2binstr(v3,4) + \ - _int2binstr(v4,4) - - -# module-level null constant -NULL = UUID() - -def printTranslatedMemory(four_hex_uints): - """ - We expect to get the string as four hex units. eg: - 0x147d54db 0xc34b3f1b 0x714f989b 0x0a892fd2 - Which will be translated to: - db547d14-1b3f4bc3-9b984f71-d22f890a - """ - uuid = UUID() - uuid.setFromMemoryDump(four_hex_uints) - print uuid.toString() - -def isUUID(id_str): - """ - This function returns: - - 1 if the string passed is a UUID - - 0 is the string passed is not a UUID - - None if it neither of the if's below is satisfied - """ - if not id_str or len(id_str) < 5 or len(id_str) > 36: - return 0 - - if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str): - return 1 - - return None - -def isPossiblyID(id_str): - """ - This function returns 1 if the string passed has some uuid-like - characteristics. Otherwise returns 0. - """ - - is_uuid = isUUID(id_str) - if is_uuid is not None: - return is_uuid - - # build a string which matches every character. - hex_wildcard = r"[0-9a-fA-F]" - chars = len(id_str) - next = min(chars, 8) - matcher = hex_wildcard+"{"+str(next)+","+str(next)+"}" - chars = chars - next - if chars > 0: - matcher = matcher + "-" - chars = chars - 1 - for block in range(3): - next = max(min(chars, 4), 0) - if next: - matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" - chars = chars - next - if chars > 0: - matcher = matcher + "-" - chars = chars - 1 - if chars > 0: - next = min(chars, 12) - matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}" - #print matcher - uuid_matcher = re.compile(matcher) - if uuid_matcher.match(id_str): - return 1 - return 0 - -def uuid_bits_to_string(bits): - i1 = _binstr2int(bits[0:4]) - i2 = _binstr2int(bits[4:6]) - i3 = _binstr2int(bits[6:8]) - i4 = _binstr2int(bits[8:10]) - i5 = _binstr2int(bits[10:12]) - i6 = _binstr2int(bits[12:16]) - return '%08lx-%04lx-%04lx-%04lx-%04lx%08lx' % (i1,i2,i3,i4,i5,i6) - -def uuid_bits_to_uuid(bits): - return UUID(uuid_bits_to_string(bits)) - - -try: - from mulib import stacked - stacked.NoProducer() # just to exercise stacked -except: - #print "Couldn't import mulib.stacked, not registering UUID converter" - pass -else: - def convertUUID(uuid, req): - req.write(str(uuid)) - - stacked.add_producer(UUID, convertUUID, "*/*") - stacked.add_producer(UUID, convertUUID, "text/html") diff --git a/indra/viewer_components/manager/util/fastest_elementtree.py b/indra/viewer_components/manager/util/fastest_elementtree.py deleted file mode 100755 index 4fcf662dd9..0000000000 --- a/indra/viewer_components/manager/util/fastest_elementtree.py +++ /dev/null @@ -1,64 +0,0 @@ -"""\ -@file fastest_elementtree.py -@brief Concealing some gnarly import logic in here. This should export the interface of elementtree. - -$LicenseInfo:firstyear=2008&license=mit$ - -Copyright (c) 2008-2009, Linden Research, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -$/LicenseInfo$ -""" - -# The parsing exception raised by the underlying library depends -# on the ElementTree implementation we're using, so we provide an -# alias here. -# -# Use ElementTreeError as the exception type for catching parsing -# errors. - - -# Using cElementTree might cause some unforeseen problems, so here's a -# convenient off switch. -use_celementree = True - -try: - if not use_celementree: - raise ImportError() - # Python 2.3 and 2.4. - from cElementTree import * - ElementTreeError = SyntaxError -except ImportError: - try: - if not use_celementree: - raise ImportError() - # Python 2.5 and above. - from xml.etree.cElementTree import * - ElementTreeError = SyntaxError - except ImportError: - # Pure Python code. - try: - # Python 2.3 and 2.4. - from elementtree.ElementTree import * - except ImportError: - # Python 2.5 and above. - from xml.etree.ElementTree import * - - # The pure Python ElementTree module uses Expat for parsing. - from xml.parsers.expat import ExpatError as ElementTreeError -- cgit v1.2.3 From 1224697a40a469bebd794c5816d6cc9c34e1000a Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 22 Jul 2016 13:23:59 -0700 Subject: SL-323: Changes to viewer manifest to move llbase to where Launcher can find them --- indra/newview/viewer_manifest.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index d3ab05b9bd..aaad79387f 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -355,7 +355,7 @@ class Windows_i686_Manifest(ViewerManifest): 'llplugin', 'slplugin', self.args['configuration']), "slplugin.exe") - self.path2basename("../viewer_components/updater/scripts/windows", "update_install.bat") + #note, launcher and friends do not need viewer_manifest in Windows as the scripts are compiled into executables # Get shared libs from the shared libs staging directory if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), dst=""): @@ -712,9 +712,10 @@ class Darwin_i386_Manifest(ViewerManifest): if self.prefix(dst="MacOS"): self.path2basename("../viewer_components/updater/scripts/darwin", "*.py") - #this copies over the python wrapper script and associated utilities, see SL-321, SL-322 and SL-323 + #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 self.path2basename("../viewer_components/manager","SL_Launcher") self.path2basename("../viewer_components/manager","*.py") + self.path2basename("../../../../../packages/llbase","*") self.end_prefix() # most everything goes in the Resources directory @@ -1067,7 +1068,10 @@ class LinuxManifest(ViewerManifest): self.path("secondlife-bin","do-not-directly-run-secondlife-bin") self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin") self.path2basename("../llplugin/slplugin", "SLPlugin") - self.path2basename("../viewer_components/updater/scripts/linux", "update_install") + #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 + self.path2basename("../viewer_components/manager","SL_Launcher") + self.path2basename("../viewer_components/manager","*.py") + self.path2basename("../packages/llbase","*") self.end_prefix("bin") if self.prefix("res-sdl"): -- cgit v1.2.3 From f44829f2aefe8c5fae9fba6bb838500719e1067e Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 27 Jul 2016 15:44:56 -0700 Subject: SL-321: basic launcher/updater integration test fixes --- autobuild.xml | 14 +-- indra/newview/viewer_manifest.py | 20 ++++- indra/viewer_components/manager/SL_Launcher | 59 +++++++------ indra/viewer_components/manager/update_manager.py | 101 +++++++++++++--------- 4 files changed, 119 insertions(+), 75 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 1c04fb85fe..469f1c32f5 100755 --- a/autobuild.xml +++ b/autobuild.xml @@ -1482,9 +1482,9 @@ archive hash - 313f5c8edd328e05c59cbe980512f84c + 13998879705aa1af36c6ea8f480901e3 url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/317790/arch/Darwin/installer/llbase-0.8.6.317790-darwin-317790.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/318106/arch/Darwin/installer/llbase-0.8.6.318106-darwin-318106.tar.bz2 name darwin @@ -1494,9 +1494,9 @@ archive hash - e85171c70b57dd086b956ba36e6f2306 + 80ef3f9f6bde28787b02837ace2ad84c url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/317790/arch/Linux/installer/llbase-0.8.6.317790-linux-317790.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/318106/arch/Linux/installer/llbase-0.8.6.318106-linux-318106.tar.bz2 name linux @@ -1506,16 +1506,16 @@ archive hash - 07b0d64d812cfaf3025b799cea5fe446 + 57837daafa60f98a4d9fa4dfe53d995e url - http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/317790/arch/CYGWIN/installer/llbase-0.8.6.0-windows-317790.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/llbase/rev/318106/arch/CYGWIN/installer/llbase-0.8.6.0-windows-318106.tar.bz2 name windows version - 0.8.6.317790 + 0.8.6.318106 llceflib diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index aaad79387f..3572b7dba8 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -27,6 +27,7 @@ Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA $/LicenseInfo$ """ import sys +import os import os.path import shutil import errno @@ -702,6 +703,7 @@ class Darwin_i386_Manifest(ViewerManifest): pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") + llbasedir = os.path.join(pkgdir, os.pardir) if self.prefix(src="", dst="Contents"): # everything goes in Contents self.path("Info.plist", dst="Info.plist") @@ -715,8 +717,14 @@ class Darwin_i386_Manifest(ViewerManifest): #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 self.path2basename("../viewer_components/manager","SL_Launcher") self.path2basename("../viewer_components/manager","*.py") - self.path2basename("../../../../../packages/llbase","*") - self.end_prefix() + llbase_path = os.path.join(self.get_dst_prefix(),'llbase') + if not os.path.exists(llbase_path): + os.makedirs(llbase_path) + if self.prefix(dst="llbase"): + self.path2basename("../packages/llbase","*.py") + self.path2basename("../packages/llbase","_cllsd.so") + self.end_prefix() + self.end_prefix() # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): @@ -1071,7 +1079,13 @@ class LinuxManifest(ViewerManifest): #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 self.path2basename("../viewer_components/manager","SL_Launcher") self.path2basename("../viewer_components/manager","*.py") - self.path2basename("../packages/llbase","*") + llbase_path = os.path.join(self.get_dst_prefix(),'llbase') + if not os.path.exists(llbase_path): + os.makedirs(llbase_path) + if self.prefix(dst="llbase"): + self.path2basename("../packages/llbase","*.py") + self.path2basename("../packages/llbase","_cllsd.so") + self.end_prefix() self.end_prefix("bin") if self.prefix("res-sdl"): diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index a96f2392a7..dde7cd9c2e 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -17,43 +17,51 @@ # $/LicenseInfo$ # Copyright (c) 2013, Linden Research, Inc. +import os +import sys + +#module globals +log_file_handle = None +cwd = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(cwd, 'llbase')) + import argparse import collections import InstallerUserMessage #NOTA BENE: # For POSIX platforms, llsd.py will be imported from the same directory. # For Windows, llsd.py will be compiled into the executable by pyinstaller -import llsd -import os +from llbase import llsd import platform -import sys import subprocess import update_manager -def after_frame(my_message, timeout = 10000): - #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds - #note that this blocks the caller for the duration of timeout - frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - #this is done before basic_message so that we aren't blocked by mainloop() - frame.after(timout, lambda: frame._delete_window) - frame.basic_message(message = my_message) - + +def silent_write(log_file_handle, text): + #if we have a log file, write. If not, do nothing. + #this is so we don't have to keep trapping for an exception with a None handle + #oh and because it is best effort, it is also a holey_write ;) + if (log_file_handle): + #prepend text for easy grepping + log_file_handle.write("SL LAUNCHER: " + text + "\n") + def get_cmd_line(): platform_name = platform.system() #find the parent of the logs and user_settings directories - if (platform_name == 'mac'): + if (platform_name == 'Darwin'): settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'Resources/app_settings/cmd_line.xml') - elif (platform_name == 'lnx'): + elif (platform_name == 'Linux'): settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') #using list format of join is important here because the Windows pathsep in a string escapes the next char - elif (platform_name == 'win'): + elif (platform_name == 'Windows'): settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') else: - settings_dir = None + settings_file = None try: cmd_line = llsd.parse((open(settings_file)).read()) except: + silent_write(log_file_handle, "Could not parse settings file %s" % settings_file) cmd_line = None return cmd_line @@ -68,7 +76,6 @@ def get_settings(): return None return settings - def capture_vmp_args(arg_list = None, cmd_line = None): #expected input format: arg_list = ['--set', 'foo', 'bar', '-X', '-Y', 'qux'] #take a copy of the viewer parameters that are of interest to VMP. @@ -96,7 +103,8 @@ def capture_vmp_args(arg_list = None, cmd_line = None): cli_overrides[vmp_params[param]] = (setting_name, setting_value) else: #find out how many args this parameter has - count = cmd_line[param]['count'] + no_dashes = vmp_params[param] + count = cmd_line[no_dashes]['count'] param_args = [] if count: for argh in range(1,count): @@ -112,6 +120,7 @@ def capture_vmp_args(arg_list = None, cmd_line = None): except KeyError: cli_overrides[key] = None else: + cli_overrides["--set"] = {} for arg in vmp_setters: try: cli_overrides[key][arg] @@ -119,7 +128,10 @@ def capture_vmp_args(arg_list = None, cmd_line = None): cli_overrides[key][arg] = None return cli_overrides -cwd = os.path.dirname(os.path.realpath(__file__)) +#main entry point +#this and a few other update manager methods really should be refactored into a util lib +parent_dir = update_manager.get_parent_path(update_manager.get_platform_key()) +log_file_handle = update_manager.get_log_file_handle(parent_dir) executable_name = "" if sys.platform.startswith('darwin'): @@ -137,23 +149,18 @@ else: #SL doesn't run on VMS or punch cards sys.exit("Unsupported platform") -#check for an update -#TODO - #find the viewer to be lauched viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),executable_name) parser = argparse.ArgumentParser() args = parser.parse_known_args(sys.argv) -print args[1] -sys.exit() #args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname args_list_to_pass = args[1][1:] vmp_args = capture_vmp_args(args_list_to_pass) #make a copy by value, not by reference command = list(args_list_to_pass) -(success, state, condition) = update_manager.update_manager(cli_overrides) +(success, state, condition) = update_manager.update_manager(vmp_args) # From update_manager: # (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) # (False, 'download', version): we failed to download the new version @@ -167,8 +174,8 @@ command = list(args_list_to_pass) # No update, update succeeded in place in foreground, or background update started: silently launch the current viewer channel # Updated succeed to a different channel, launch that viewer and exit if not success: - msg = 'Update failed in the %s process. Please check logs. Viewer will launch starting momentarily.' - after_frame(msg) + msg = 'Update failed in the %s process. Please check logs. Viewer will launch starting momentarily.' % state + update_manager.after_frame(msg) command.insert(0,viewer_binary) viewer_process = subprocess.Popen(command) #at the moment, we just exit here. Later, the crash monitor will be launched at this point diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index a7e0a19aef..398c8bb55d 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -1,29 +1,35 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ -# Copyright (c) 2013, Linden Research, Inc. - -""" -@file update_manager.py +"""\ +@file update_manager.py @author coyot -@date 2016-05-16 +@date 2016-05-16 +@brief executes viewer update checking and manages downloading and applying of updates + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from llbase import llrest +from llbase.llrest import RESTError from llbase import llsd from urlparse import urljoin @@ -55,9 +61,9 @@ def silent_write(log_file_handle, text): def after_frame(my_message, timeout = 10000): #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds #note that this blocks the caller for the duration of timeout - frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") + frame = InstallerUserMessage.InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") #this is done before basic_message so that we aren't blocked by mainloop() - frame.after(timout, lambda: frame._delete_window) + frame.after(timeout, lambda: frame._delete_window) frame.basic_message(message = my_message) def convert_version_file_style(version): @@ -146,8 +152,10 @@ def check_for_completed_download(download_dir): def get_settings(log_file_handle, parent_dir): #return the settings file parsed into a dict + print str(parent_dir) try: settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) + print "Settings file: " + str(settings_file) settings = llsd.parse((open(settings_file)).read()) except llsd.LLSDParseError as lpe: silent_write(log_file_handle, "Could not parse settings file %s" % lpe) @@ -213,7 +221,7 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ else: base_URI = 'https://update.secondlife.com/update/' channelname = summary_dict['Channel'] - #this is kind of a mess because the settings value a) in a map and b) is both the cohort and the version + #this is kind of a mess because the settings value is a) in a map and b) is both the cohort and the version version = summary_dict['Version'] platform_version = platform.release() #this will always return something usable, error handling in method @@ -250,7 +258,7 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ try: result_data = VVMService.get(query_string) except RESTError as re: - silent_write.write(log_file_handle, "Failed to query VVM using %s failed as %s" % (urljoin(base_URI,query_string, re))) + silent_write(log_file_handle, "Failed to query VVM using %s failed as %s" % (urljoin(base_URI,query_string), re)) return None return result_data @@ -342,6 +350,8 @@ def update_manager(cli_overrides = None): platform_key = get_platform_key() parent_dir = get_parent_path(platform_key) log_file_handle = get_log_file_handle(parent_dir) + settings = None + print "parent dir: " + str(parent_dir) #check to see if user has install rights #get the owner of the install and the current user @@ -367,10 +377,12 @@ def update_manager(cli_overrides = None): print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) return (False, 'setup', None) - if cli_overrides['settings'] is not None: - settings = get_settings(log_file_handle, cli_overrides['settings']) - else: - settings = get_settings(log_file_handle, parent_dir) + if cli_overrides is not None: + if '--settings' in cli_overrides.keys(): + if cli_overrides['--settings'] is not None: + settings = get_settings(log_file_handle, cli_overrides['--settings']) + else: + settings = get_settings(log_file_handle, parent_dir) if settings is None: silent_write(log_file_handle, "Failed to load viewer settings") @@ -379,7 +391,7 @@ def update_manager(cli_overrides = None): #323: If a complete download of that update is found, check the update preference: #settings['UpdaterServiceSetting'] = 0 is manual install - """ + """ssh://hg@bitbucket.org/lindenlab/viewer-release-maint-6585 UpdaterServiceSetting Comment @@ -390,8 +402,10 @@ def update_manager(cli_overrides = None): 0 """ - if cli_overrides['set']['UpdaterServiceSetting'] is not None: - install_automatically = cli_overrides['set']['UpdaterServiceSetting'] + if cli_overrides is not None: + if '--set' in cli_overrides.keys(): + if 'UpdaterServiceSetting' in cli_overrides['--set'].keys(): + install_automatically = cli_overrides['--set']['UpdaterServiceSetting'] else: try: install_automatically = settings['UpdaterServiceSetting']['Value'] @@ -399,28 +413,37 @@ def update_manager(cli_overrides = None): except KeyError: install_automatically = 1 - #use default chunk size if none is given - if cli_overrides['set']['UpdaterMaximumBandwidth ']: - chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth '] + #use default chunk size if none is given + if cli_overrides is not None: + if '--set' in cli_overrides.keys(): + if 'UpdaterMaximumBandwidth' in cli_overrides['--set'].keys(): + chunk_size = cli_overrides['--set']['UpdaterMaximumBandwidth'] else: chunk_size = 1024 #get channel and version try: summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__))) - if cli_overrides['channel']: - summary_dict['Channel'] = cli_overrides['channel'] - except: + if cli_overrides is not None: + if 'channel' in cli_overrides.keys(): + summary_dict['Channel'] = cli_overrides['channel'] + except Exception, e: silent_write(log_file_handle, "Could not obtain channel and version, exiting.") + silent_write(log_file_handle, e.message) print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) return (False, 'setup', None) #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. - UpdaterServiceURL = cli_overrides['update-service'] + if cli_overrides is not None: + if '--update-service' in cli_overrides.keys(): + UpdaterServiceURL = cli_overrides['--update-service'] + else: + #tells query_vvm to use the default + UpdaterServiceURL = None result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict, UpdaterServiceURL) #nothing to do or error if not result_data: - silent_write.write(og_file_handle, "No update found.") + silent_write(log_file_handle, "No update found.") print "Update manager exited with (%s, %s, %s)" % (True, None, None) return (True, None, None) -- cgit v1.2.3 From 2bb4214866ddd17578ff094b83f3bcf5a46053c0 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 28 Jul 2016 07:57:34 -0700 Subject: trivial change to bump build --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad040fb077..6eddb9aa9e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Second Life Viewer ==================== - + This project manages the source code for the [Second Life](https://www.secondlife.com) Viewer. -- cgit v1.2.3 From fc7c0645fa9c9d47c1462387a3de82a2a4474467 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 28 Jul 2016 08:50:52 -0700 Subject: SL-321: add in resource files, more CLI handling fixes --- indra/viewer_components/Resources/README | 9 ++++ indra/viewer_components/Resources/summary.json | 1 + indra/viewer_components/manager/InstallerError.py | 49 +++++++++++++++++++++ indra/viewer_components/manager/SL_Launcher | 5 ++- indra/viewer_components/manager/icons/SL_Logo.gif | Bin 0 -> 1322 bytes indra/viewer_components/manager/icons/SL_Logo.png | Bin 0 -> 1484 bytes .../manager/icons/head-sl-logo.gif | Bin 0 -> 960 bytes .../manager/icons/head-sl-logo.png | Bin 0 -> 2316 bytes indra/viewer_components/manager/update_manager.py | 11 ++++- 9 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 indra/viewer_components/Resources/README create mode 100644 indra/viewer_components/Resources/summary.json create mode 100644 indra/viewer_components/manager/InstallerError.py create mode 100644 indra/viewer_components/manager/icons/SL_Logo.gif create mode 100644 indra/viewer_components/manager/icons/SL_Logo.png create mode 100644 indra/viewer_components/manager/icons/head-sl-logo.gif create mode 100644 indra/viewer_components/manager/icons/head-sl-logo.png diff --git a/indra/viewer_components/Resources/README b/indra/viewer_components/Resources/README new file mode 100644 index 0000000000..e1b35730d4 --- /dev/null +++ b/indra/viewer_components/Resources/README @@ -0,0 +1,9 @@ +This directory only exists as a place for the summary.json file to exist when the unit tests are run on a Mac, where the file goes to a sibling directory of the scripts dir. In Linux and Windows, the JSON file goes into the same directory as the script. + +See: + +test_get_summary.py +update_manager.get_summary() + +for more details +- coyot 201606.02 diff --git a/indra/viewer_components/Resources/summary.json b/indra/viewer_components/Resources/summary.json new file mode 100644 index 0000000000..b78859d427 --- /dev/null +++ b/indra/viewer_components/Resources/summary.json @@ -0,0 +1 @@ +{"Type":"viewer","Version":"4.0.5.315117","Channel":"Second Life Release"} diff --git a/indra/viewer_components/manager/InstallerError.py b/indra/viewer_components/manager/InstallerError.py new file mode 100644 index 0000000000..3b199ea231 --- /dev/null +++ b/indra/viewer_components/manager/InstallerError.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python + +"""\ +@file InstallerError.py +@author coyot +@date 2016-05-16 +@brief custom exception class for VMP + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ +""" + +""" +usage: + +>>> import InstallerError +>>> import os +>>> try: +... os.mkdir('/tmp') +... except OSError, oe: +... ie = InstallerError.InstallerError(oe, "foo") +... raise ie + +Traceback (most recent call last): + File "", line 5, in +InstallerError.InstallerError: [Errno [Errno 17] File exists: '/tmp'] foo +""" + +class InstallerError(OSError): + def __init___(self, message): + Exception.__init__(self, message) diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index dde7cd9c2e..0403e01cec 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -106,11 +106,12 @@ def capture_vmp_args(arg_list = None, cmd_line = None): no_dashes = vmp_params[param] count = cmd_line[no_dashes]['count'] param_args = [] - if count: - for argh in range(1,count): + if count > 0: + for argh in range(0,count): param_args.append(vmp_queue.popleft()) #the parameter name is the key, the (possibly empty) list of args is the value cli_overrides[vmp_params[param]] = param_args + print "cli override param %s vmp_param %s args %s count %s" % (param, vmp_params[param], param_args, count) #to prevent KeyErrors on missing keys, set the remainder to None for key in vmp_params: diff --git a/indra/viewer_components/manager/icons/SL_Logo.gif b/indra/viewer_components/manager/icons/SL_Logo.gif new file mode 100644 index 0000000000..c24d6b08cb Binary files /dev/null and b/indra/viewer_components/manager/icons/SL_Logo.gif differ diff --git a/indra/viewer_components/manager/icons/SL_Logo.png b/indra/viewer_components/manager/icons/SL_Logo.png new file mode 100644 index 0000000000..5e376c72f9 Binary files /dev/null and b/indra/viewer_components/manager/icons/SL_Logo.png differ diff --git a/indra/viewer_components/manager/icons/head-sl-logo.gif b/indra/viewer_components/manager/icons/head-sl-logo.gif new file mode 100644 index 0000000000..d635348dcc Binary files /dev/null and b/indra/viewer_components/manager/icons/head-sl-logo.gif differ diff --git a/indra/viewer_components/manager/icons/head-sl-logo.png b/indra/viewer_components/manager/icons/head-sl-logo.png new file mode 100644 index 0000000000..5c214e96d1 Binary files /dev/null and b/indra/viewer_components/manager/icons/head-sl-logo.png differ diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index 398c8bb55d..ff0f69a1b1 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -252,8 +252,15 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ #normal case, no testing key test_ok = 'testok' UUID = make_VVM_UUID_hash(platform_key) + print repr(channelname) + print repr(version) + print repr(platform_key) + print repr(platform_version) + print repr(test_ok) + print repr(UUID) #because urljoin can't be arsed to take multiple elements - query_string = '/v1.0/' + channelname + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID + #channelname is a list because although it can only be one word, it is a kind of argument and viewer args can take multiple keywords. + query_string = '/v1.0/' + channelname[0] + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID VVMService = llrest.SimpleRESTService(name='VVM', baseurl=base_URI) try: result_data = VVMService.get(query_string) @@ -351,7 +358,7 @@ def update_manager(cli_overrides = None): parent_dir = get_parent_path(platform_key) log_file_handle = get_log_file_handle(parent_dir) settings = None - print "parent dir: " + str(parent_dir) + print "cli_overrides: " + str(cli_overrides) #check to see if user has install rights #get the owner of the install and the current user -- cgit v1.2.3 From 7f78051620743a8fb6c3797255f8f12444c3eb5b Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 28 Jul 2016 11:04:35 -0700 Subject: SL-321: fix viewer_manifest to not run strip on python files in the Linux build --- indra/newview/viewer_manifest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 3572b7dba8..502c054730 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -1155,7 +1155,8 @@ class LinuxManifest(ViewerManifest): 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 + # makes some small assumptions about our packaged dir structure + self.run_command(r"find %(d)r/bin %(d)r/lib -type f \! -name \*.py \! -name SL_Launcher \! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) class Linux_i686_Manifest(LinuxManifest): def construct(self): -- cgit v1.2.3 From 398fabf10c47c984c179e599af14fd27be4816cf Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 28 Jul 2016 11:38:13 -0700 Subject: SL-321: fix commithook issues in test files --- .../manager/tests/test_InstallerError.py | 40 ++++++++++++--------- .../tests/test_check_for_completed_download.py | 38 +++++++++++--------- .../tests/test_convert_version_file_style.py | 42 ++++++++++++---------- .../manager/tests/test_get_filename.py | 38 +++++++++++--------- .../manager/tests/test_get_log_file_handle.py | 39 +++++++++++--------- .../manager/tests/test_get_parent_path.py | 38 +++++++++++--------- .../manager/tests/test_get_platform_key.py | 38 +++++++++++--------- .../manager/tests/test_get_settings.py | 37 ++++++++++--------- .../manager/tests/test_make_VVM_UUID_hash.py | 37 ++++++++++--------- .../manager/tests/test_make_download_dir.py | 39 +++++++++++--------- .../manager/tests/test_query_vvm.py | 36 +++++++++++-------- .../manager/tests/test_summary.py | 38 +++++++++++--------- .../manager/tests/with_setup_args.py | 42 +++++++++++----------- 13 files changed, 284 insertions(+), 218 deletions(-) diff --git a/indra/viewer_components/manager/tests/test_InstallerError.py b/indra/viewer_components/manager/tests/test_InstallerError.py index d722208b7f..9139447932 100644 --- a/indra/viewer_components/manager/tests/test_InstallerError.py +++ b/indra/viewer_components/manager/tests/test_InstallerError.py @@ -1,27 +1,33 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ - -""" +"""\ @file test_InstallerError.py @author coyot @date 2016-06-01 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ + from nose.tools import assert_equal import InstallerError diff --git a/indra/viewer_components/manager/tests/test_check_for_completed_download.py b/indra/viewer_components/manager/tests/test_check_for_completed_download.py index 388bc900e9..bcaaef4c3f 100644 --- a/indra/viewer_components/manager/tests/test_check_for_completed_download.py +++ b/indra/viewer_components/manager/tests/test_check_for_completed_download.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_check_for_completed_download.py @author coyot @date 2016-06-03 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -50,4 +56,4 @@ def test_completed_check_for_completed_download(tmpdir1,tmpdir2): def test_incomplete_check_for_completed_download(tmpdir1,tmpdir2): #should return False incomplete = not update_manager.check_for_completed_download(tmpdir2) - assert incomplete, "False positive, should not mark complete without a marker" \ No newline at end of file + assert incomplete, "False positive, should not mark complete without a marker" diff --git a/indra/viewer_components/manager/tests/test_convert_version_file_style.py b/indra/viewer_components/manager/tests/test_convert_version_file_style.py index d700f91b84..244c22c458 100644 --- a/indra/viewer_components/manager/tests/test_convert_version_file_style.py +++ b/indra/viewer_components/manager/tests/test_convert_version_file_style.py @@ -1,27 +1,33 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ - -""" +"""\ @file test_convert_version_file_style.py @author coyot @date 2016-06-01 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ + from nose.tools import assert_equal import update_manager @@ -52,4 +58,4 @@ def test_none(): golden = None converted = update_manager.convert_version_file_style(version) - assert_equal(golden, converted) \ No newline at end of file + assert_equal(golden, converted) diff --git a/indra/viewer_components/manager/tests/test_get_filename.py b/indra/viewer_components/manager/tests/test_get_filename.py index 95771d75dc..efb6349374 100644 --- a/indra/viewer_components/manager/tests/test_get_filename.py +++ b/indra/viewer_components/manager/tests/test_get_filename.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_get_filename.py @author coyot @date 2016-06-30 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -57,4 +63,4 @@ def test_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): @with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) def test_missing_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): not_found = not apply_update.get_filename(tmpdir4) - assert not_found, "False positive, should not find an installable in an empty dir" \ No newline at end of file + assert not_found, "False positive, should not find an installable in an empty dir" diff --git a/indra/viewer_components/manager/tests/test_get_log_file_handle.py b/indra/viewer_components/manager/tests/test_get_log_file_handle.py index c5b3c89550..5ea821acf7 100644 --- a/indra/viewer_components/manager/tests/test_get_log_file_handle.py +++ b/indra/viewer_components/manager/tests/test_get_log_file_handle.py @@ -1,25 +1,30 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ - """ @file test_get_log_file_handle.py @author coyot @date 2016-06-08 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -60,4 +65,4 @@ def test_missing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): if not os.path.exists(log_file_path): print "Failed to touch new log file" assert False - assert True \ No newline at end of file + assert True diff --git a/indra/viewer_components/manager/tests/test_get_parent_path.py b/indra/viewer_components/manager/tests/test_get_parent_path.py index 3cfd72310e..6e4b9dfaff 100644 --- a/indra/viewer_components/manager/tests/test_get_parent_path.py +++ b/indra/viewer_components/manager/tests/test_get_parent_path.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_get_parent_path.py @author coyot @date 2016-06-02 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -75,5 +81,3 @@ def test_get_parent_path(settings_dir): assert settings_dir, "test_get_parent_path failed to obtain parent path" assert_equal(settings_dir, got_settings_dir) - - \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_platform_key.py b/indra/viewer_components/manager/tests/test_get_platform_key.py index 37c570532c..eeaca1dff7 100644 --- a/indra/viewer_components/manager/tests/test_get_platform_key.py +++ b/indra/viewer_components/manager/tests/test_get_platform_key.py @@ -1,25 +1,30 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ - """ @file test_get_platform_key.py @author coyot @date 2016-06-01 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import assert_equal @@ -37,4 +42,3 @@ def test_get_platform_key(): assert_equal(platform.system(),'Windows') else: assert_equal(key, None) - \ No newline at end of file diff --git a/indra/viewer_components/manager/tests/test_get_settings.py b/indra/viewer_components/manager/tests/test_get_settings.py index 46b779cbd5..7efcf62673 100644 --- a/indra/viewer_components/manager/tests/test_get_settings.py +++ b/indra/viewer_components/manager/tests/test_get_settings.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_get_settings.py @author coyot @date 2016-06-03 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -79,4 +85,3 @@ def test_get_settings(settings_dir): #test one key just to make sure it parsed assert_equal(got_settings['CurrentGrid']['Value'], 'util.agni.lindenlab.com') - diff --git a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py index 513502a6ca..5939e5806a 100644 --- a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py +++ b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_make_VVM_UUID_hash.py @author coyot @date 2016-06-03 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -39,4 +45,3 @@ def test_make_VVM_UUID_hash(): #make_UUID_hash returned None assert UUID_hash, "make_UUID_hash failed to make a hash." - diff --git a/indra/viewer_components/manager/tests/test_make_download_dir.py b/indra/viewer_components/manager/tests/test_make_download_dir.py index 5198a6de05..e3f9cb83fe 100644 --- a/indra/viewer_components/manager/tests/test_make_download_dir.py +++ b/indra/viewer_components/manager/tests/test_make_download_dir.py @@ -1,25 +1,30 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ - """ @file test_make_download_dir.py @author coyot @date 2016-06-03 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -39,4 +44,4 @@ def test_make_download_dir(): print "make_download_dir raised an unexpected exception %s" % str(e) assert False - assert download_dir, "make_download_dir returned None for path %s and version %s" % (path, version) \ No newline at end of file + assert download_dir, "make_download_dir returned None for path %s and version %s" % (path, version) diff --git a/indra/viewer_components/manager/tests/test_query_vvm.py b/indra/viewer_components/manager/tests/test_query_vvm.py index 40a0f7b215..79c8ede480 100644 --- a/indra/viewer_components/manager/tests/test_query_vvm.py +++ b/indra/viewer_components/manager/tests/test_query_vvm.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_query_vvm.py @author coyot @date 2016-06-08 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * diff --git a/indra/viewer_components/manager/tests/test_summary.py b/indra/viewer_components/manager/tests/test_summary.py index b318012b54..f2f2af7347 100644 --- a/indra/viewer_components/manager/tests/test_summary.py +++ b/indra/viewer_components/manager/tests/test_summary.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_summary.py @author coyot @date 2016-06-02 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -36,4 +42,4 @@ def test_get_summary(): #we aren't testing the JSON library, one key pair is enough #so we will use the one pair that is actually a constant - assert_equal(summary_json['Type'],'viewer') \ No newline at end of file + assert_equal(summary_json['Type'],'viewer') diff --git a/indra/viewer_components/manager/tests/with_setup_args.py b/indra/viewer_components/manager/tests/with_setup_args.py index 38350ab8c4..94d33aa5fe 100644 --- a/indra/viewer_components/manager/tests/with_setup_args.py +++ b/indra/viewer_components/manager/tests/with_setup_args.py @@ -1,29 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ -# -# Taken from: https://gist.github.com/garyvdm/392ae20c673c7ee58d76 - """ @file with_setup_args.py @author garyvdm @date 2016-06-02 -""" +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ +""" def with_setup_args(setup, teardown=None): """Decorator to add setup and/or teardown methods to a test function:: @@ -63,4 +65,4 @@ def with_setup_args(setup, teardown=None): if hasattr(func, 'teardown'): test_wrapped.teardown = func.teardown() return test_wrapped - return decorate \ No newline at end of file + return decorate -- cgit v1.2.3 From 2afcff5b013c778f55af77f27932e10792986a05 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 28 Jul 2016 11:44:01 -0700 Subject: SL-321: one more commithook issue in test file --- .../manager/tests/test_silent_write.py | 38 +++++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/indra/viewer_components/manager/tests/test_silent_write.py b/indra/viewer_components/manager/tests/test_silent_write.py index a55665799f..ad286787e2 100644 --- a/indra/viewer_components/manager/tests/test_silent_write.py +++ b/indra/viewer_components/manager/tests/test_silent_write.py @@ -1,25 +1,31 @@ #!/usr/bin/env python -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ """ @file test_silent_write.py @author coyot @date 2016-06-02 + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ """ from nose.tools import * @@ -40,4 +46,4 @@ def test_silent_write_to_null(): update_manager.silent_write(None, "This is a test.") except Exception, e: print "Test failed due to: %s" % str(e) - assert False \ No newline at end of file + assert False -- cgit v1.2.3 From 7ed9c85a1a28e494adbd2cef85b186e45dba2dc2 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 15 Aug 2016 14:48:09 -0700 Subject: SL-323: fixes to Tkinter race condition, post --channel and --settings testing, contains debugging statements to be removed after all testing complete --- .../manager/InstallerUserMessage.py | 13 +++- indra/viewer_components/manager/SL_Launcher | 8 +- indra/viewer_components/manager/download_update.py | 2 + indra/viewer_components/manager/update_manager.py | 87 ++++++++++++++-------- 4 files changed, 77 insertions(+), 33 deletions(-) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index f66af81d06..4f81aa9cd1 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -78,11 +78,17 @@ class InstallerUserMessage(tk.Tk): #defines what to do when window is closed self.protocol("WM_DELETE_WINDOW", self._delete_window) + + #callback id + self.id = -1 def _delete_window(self): #capture and discard all destroy events before the choice is set if not ((self.choice == None) or (self.choice == "")): try: + #initialized value. If we have an outstanding callback, kill it before killing ourselves + if self.id != -1: + self.after_cancel(self.id) self.destroy() except: #tk may try to destroy the same object twice @@ -217,8 +223,11 @@ class InstallerUserMessage(tk.Tk): def check_scheduler(self): if self.value < self.progress["maximum"]: - self.check_queue() - self.after(100, self.check_scheduler) + self.check_queue() + self.id = self.after(100, self.check_scheduler) + else: + #prevent a race condition between polling and the widget destruction + self.after_cancel(self.id) def check_queue(self): while self.queue.qsize(): diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index 0403e01cec..8da8dc236b 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -31,7 +31,12 @@ import InstallerUserMessage #NOTA BENE: # For POSIX platforms, llsd.py will be imported from the same directory. # For Windows, llsd.py will be compiled into the executable by pyinstaller -from llbase import llsd +try: + from llbase import llsd +except: + #if Windows, this is expected, if not, we're dead + if os.name == 'nt': + pass import platform import subprocess import update_manager @@ -158,6 +163,7 @@ args = parser.parse_known_args(sys.argv) #args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname args_list_to_pass = args[1][1:] vmp_args = capture_vmp_args(args_list_to_pass) +print "vmp args: " + repr(vmp_args) #make a copy by value, not by reference command = list(args_list_to_pass) diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py index 23f784c6c1..a5e365fa37 100755 --- a/indra/viewer_components/manager/download_update.py +++ b/indra/viewer_components/manager/download_update.py @@ -45,6 +45,8 @@ def download_update(url = None, download_dir = None, size = None, progressbar = #chunk_size is in bytes, amount to download at once queue = Queue.Queue() + if not os.path.exists(download_dir): + os.makedirs(download_dir) #the url split provides the basename of the filename filename = os.path.join(download_dir, url.split('/')[-1]) req = requests.get(url, stream=True) diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index ff0f69a1b1..6e79e83605 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -28,9 +28,21 @@ Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA $/LicenseInfo$ """ -from llbase import llrest -from llbase.llrest import RESTError -from llbase import llsd +import os + +#NOTA BENE: +# For POSIX platforms, llbase will be imported from the same directory. +# For Windows, llbase will be compiled into the executable by pyinstaller +try: + from llbase import llrest + from llbase.llrest import RESTError + from llbase import llsd +except: + #if Windows, this is expected, if not, we're dead + if os.name == 'nt': + pass + +from copy import deepcopy from urlparse import urljoin import apply_update @@ -40,7 +52,6 @@ import fnmatch import hashlib import InstallerUserMessage import json -import os import platform import re import shutil @@ -58,13 +69,14 @@ def silent_write(log_file_handle, text): #prepend text for easy grepping log_file_handle.write("UPDATE MANAGER: " + text + "\n") -def after_frame(my_message, timeout = 10000): +def after_frame(message, timeout = 10000): #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds #note that this blocks the caller for the duration of timeout frame = InstallerUserMessage.InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") #this is done before basic_message so that we aren't blocked by mainloop() - frame.after(timeout, lambda: frame._delete_window) - frame.basic_message(message = my_message) + #frame.after(timeout, lambda: frame._delete_window) + frame.after(timeout, lambda: frame.destroy()) + frame.basic_message(message = message) def convert_version_file_style(version): #converts a version string a.b.c.d to a_b_c_d as used in downloaded filenames @@ -155,6 +167,10 @@ def get_settings(log_file_handle, parent_dir): print str(parent_dir) try: settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) + #this happens when the path to settings file happens on the command line + #we get a full path and don't need to munge it + if not os.path.exists(settings_file): + settings_file = parent_dir print "Settings file: " + str(settings_file) settings = llsd.parse((open(settings_file)).read()) except llsd.LLSDParseError as lpe: @@ -223,9 +239,15 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ channelname = summary_dict['Channel'] #this is kind of a mess because the settings value is a) in a map and b) is both the cohort and the version version = summary_dict['Version'] - platform_version = platform.release() + #we need to use the dotted versions of the platform versions in order to be compatible with VVM rules and arithmetic + if platform_key == 'win': + platform_version = platform.win32_ver()[1] + elif platform_key == 'mac': + platform_version = platform.mac_ver()[0] + else: + platform_version = platform.release() #this will always return something usable, error handling in method - hashed_UUID = make_VVM_UUID_hash(platform_key) + UUID = str(make_VVM_UUID_hash(platform_key)) #note that this will not normally be in a settings.xml file and is only here for test builds. #for test builds, add this key to the ../user_settings/settings.xml """ @@ -251,16 +273,10 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ except KeyError: #normal case, no testing key test_ok = 'testok' - UUID = make_VVM_UUID_hash(platform_key) - print repr(channelname) - print repr(version) - print repr(platform_key) - print repr(platform_version) - print repr(test_ok) - print repr(UUID) #because urljoin can't be arsed to take multiple elements - #channelname is a list because although it can only be one word, it is a kind of argument and viewer args can take multiple keywords. - query_string = '/v1.0/' + channelname[0] + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID + #channelname is a list because although it is only one string, it is a kind of argument and viewer args can take multiple keywords. + query_string = urllib.quote('v1.0/' + channelname[0] + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID) + silent_write(log_file_handle, "About to query VVM: %s" % base_URI + query_string) VVMService = llrest.SimpleRESTService(name='VVM', baseurl=base_URI) try: result_data = VVMService.get(query_string) @@ -269,25 +285,28 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ return None return result_data -def download(url = None, version = None, download_dir = None, size = 0, background = False, chunk_size = 1024): +def download(url = None, version = None, download_dir = None, size = 0, background = False, chunk_size = None, log_file_handle = None): download_tries = 0 download_success = False + if not chunk_size: + chunk_size = 1024 #for background execution path_to_downloader = os.path.join(os.path.dirname(os.path.realpath(__file__)), "download_update.py") #three strikes and you're out while download_tries < 3 and not download_success: #323: Check for a partial update of the required update; in either event, display an alert that a download is required, initiate the download, and then install and launch if download_tries == 0: - after_frame(message = "Downloading new version " + version + " Please wait.") + after_frame(message = "Downloading new version " + version + " Please wait.", timeout = 5000) else: - after_frame(message = "Trying again to download new version " + version + " Please wait.") + after_frame(message = "Trying again to download new version " + version + " Please wait.", timeout = 5000) if not background: try: download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True, chunk_size = chunk_size) download_success = True - except: + except Exception, e: download_tries += 1 silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") + silent_write(log_file_handle, "Logging download exception: %s" % e.message) else: try: #Python does not have a facility to multithread a method, so we make the method a standalone @@ -308,6 +327,9 @@ def install(platform_key = None, download_dir = None, log_file_handle = None, in if downloaded != 'skip': after_frame(message = "New version downloaded. Installing now, please wait.") success = apply_update.apply_update(download_dir, platform_key, log_file_handle, in_place) + print download_dir + print success + version = download_dir.split('/')[-1] if success: silent_write(log_file_handle, "successfully updated to " + version) shutil.rmtree(download_dir) @@ -323,7 +345,7 @@ def download_and_install(downloaded = None, url = None, version = None, download #extracted to a method because we do it twice in update_manager() and this makes the logic clearer if not downloaded: #do the download, exit if we fail - if not download(url = url, version = version, download_dir = download_dir, size = size, chunk_size = chunk_size): + if not download(url = url, version = version, download_dir = download_dir, size = size, chunk_size = chunk_size, log_file_handle = log_file_handle): return (False, 'download', version) #do the install path_to_new_launcher = install(platform_key = platform_key, download_dir = download_dir, @@ -385,9 +407,10 @@ def update_manager(cli_overrides = None): return (False, 'setup', None) if cli_overrides is not None: - if '--settings' in cli_overrides.keys(): - if cli_overrides['--settings'] is not None: - settings = get_settings(log_file_handle, cli_overrides['--settings']) + print "update manager settings file: " + str(cli_overrides['settings']) + if 'settings' in cli_overrides.keys(): + if cli_overrides['settings'] is not None: + settings = get_settings(log_file_handle, cli_overrides['settings'][0]) else: settings = get_settings(log_file_handle, parent_dir) @@ -398,7 +421,7 @@ def update_manager(cli_overrides = None): #323: If a complete download of that update is found, check the update preference: #settings['UpdaterServiceSetting'] = 0 is manual install - """ssh://hg@bitbucket.org/lindenlab/viewer-release-maint-6585 + """ UpdaterServiceSetting Comment @@ -431,9 +454,11 @@ def update_manager(cli_overrides = None): #get channel and version try: summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__))) + #we send the override to the VVM, but retain the summary.json version for in_place computations + channel_override_summary = deepcopy(summary_dict) if cli_overrides is not None: if 'channel' in cli_overrides.keys(): - summary_dict['Channel'] = cli_overrides['channel'] + channel_override_summary['Channel'] = cli_overrides['channel'] except Exception, e: silent_write(log_file_handle, "Could not obtain channel and version, exiting.") silent_write(log_file_handle, e.message) @@ -447,7 +472,8 @@ def update_manager(cli_overrides = None): else: #tells query_vvm to use the default UpdaterServiceURL = None - result_data = query_vvm(log_file_handle, platform_key, settings, summary_dict, UpdaterServiceURL) + result_data = query_vvm(log_file_handle, platform_key, settings, channel_override_summary, UpdaterServiceURL) + #nothing to do or error if not result_data: silent_write(log_file_handle, "No update found.") @@ -465,6 +491,7 @@ def update_manager(cli_overrides = None): #and launcher will launch the viewer in this install location. Otherwise, it will launch the Launcher from #the new location and kill itself. in_place = (summary_dict['Channel'] == result_data['channel']) + print "summary %s, result %s, in_place %s" % (summary_dict['Channel'], result_data['channel'], in_place) #determine if we've tried this download before downloaded = check_for_completed_download(download_dir) @@ -502,7 +529,7 @@ def update_manager(cli_overrides = None): return (True, None, None) else: #multithread a download - download(url = result_data['url'], version = result_data['version'], download_dir = download_dir, size = result_data['size'], background = True) + download(url = result_data['url'], version = result_data['version'], download_dir = download_dir, size = result_data['size'], background = True, log_file_handle = log_file_handle) print "Update manager exited with (%s, %s, %s)" % (True, 'background', True) return (True, 'background', True) -- cgit v1.2.3 From 5811b4721f8b2d848efeb37f2dff14f00bb7fc67 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 16 Aug 2016 08:40:39 -0700 Subject: SL-323: fixed update service redirect handling --- indra/viewer_components/manager/SL_Launcher | 2 -- indra/viewer_components/manager/update_manager.py | 33 +++++++++++------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index 8da8dc236b..1184a4c794 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -116,7 +116,6 @@ def capture_vmp_args(arg_list = None, cmd_line = None): param_args.append(vmp_queue.popleft()) #the parameter name is the key, the (possibly empty) list of args is the value cli_overrides[vmp_params[param]] = param_args - print "cli override param %s vmp_param %s args %s count %s" % (param, vmp_params[param], param_args, count) #to prevent KeyErrors on missing keys, set the remainder to None for key in vmp_params: @@ -163,7 +162,6 @@ args = parser.parse_known_args(sys.argv) #args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname args_list_to_pass = args[1][1:] vmp_args = capture_vmp_args(args_list_to_pass) -print "vmp args: " + repr(vmp_args) #make a copy by value, not by reference command = list(args_list_to_pass) diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index 6e79e83605..80b0f087ab 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -164,14 +164,12 @@ def check_for_completed_download(download_dir): def get_settings(log_file_handle, parent_dir): #return the settings file parsed into a dict - print str(parent_dir) try: settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) #this happens when the path to settings file happens on the command line #we get a full path and don't need to munge it if not os.path.exists(settings_file): settings_file = parent_dir - print "Settings file: " + str(settings_file) settings = llsd.parse((open(settings_file)).read()) except llsd.LLSDParseError as lpe: silent_write(log_file_handle, "Could not parse settings file %s" % lpe) @@ -230,14 +228,19 @@ def make_VVM_UUID_hash(platform_key): def query_vvm(log_file_handle = None, platform_key = None, settings = None, summary_dict = None, UpdaterServiceURL = None, UpdaterWillingToTest = None): result_data = None + baseURI = None #URI template /update/v1.1/channelname/version/platformkey/platformversion/willing-to-test/uniqueid #https://wiki.lindenlab.com/wiki/Viewer_Version_Manager_REST_API#Viewer_Update_Query + #note that the only two valid options are: + # # version-phx0.damballah.lindenlab.com + # # version-qa.secondlife-staging.com if UpdaterServiceURL: - baseURI = UpdaterServiceURL + #we can't really expect the users to put the protocol or base dir on, they will give us a host + base_URI = urljoin('https://' + UpdaterServiceURL[0], '/update/') else: base_URI = 'https://update.secondlife.com/update/' channelname = summary_dict['Channel'] - #this is kind of a mess because the settings value is a) in a map and b) is both the cohort and the version + #this is kind of a mess because the settings value is a) in a map and b) is both the cohort and the version in one string version = summary_dict['Version'] #we need to use the dotted versions of the platform versions in order to be compatible with VVM rules and arithmetic if platform_key == 'win': @@ -327,8 +330,6 @@ def install(platform_key = None, download_dir = None, log_file_handle = None, in if downloaded != 'skip': after_frame(message = "New version downloaded. Installing now, please wait.") success = apply_update.apply_update(download_dir, platform_key, log_file_handle, in_place) - print download_dir - print success version = download_dir.split('/')[-1] if success: silent_write(log_file_handle, "successfully updated to " + version) @@ -380,7 +381,6 @@ def update_manager(cli_overrides = None): parent_dir = get_parent_path(platform_key) log_file_handle = get_log_file_handle(parent_dir) settings = None - print "cli_overrides: " + str(cli_overrides) #check to see if user has install rights #get the owner of the install and the current user @@ -407,12 +407,11 @@ def update_manager(cli_overrides = None): return (False, 'setup', None) if cli_overrides is not None: - print "update manager settings file: " + str(cli_overrides['settings']) if 'settings' in cli_overrides.keys(): if cli_overrides['settings'] is not None: settings = get_settings(log_file_handle, cli_overrides['settings'][0]) - else: - settings = get_settings(log_file_handle, parent_dir) + else: + settings = get_settings(log_file_handle, parent_dir) if settings is None: silent_write(log_file_handle, "Failed to load viewer settings") @@ -433,9 +432,9 @@ def update_manager(cli_overrides = None): """ if cli_overrides is not None: - if '--set' in cli_overrides.keys(): - if 'UpdaterServiceSetting' in cli_overrides['--set'].keys(): - install_automatically = cli_overrides['--set']['UpdaterServiceSetting'] + if 'set' in cli_overrides.keys(): + if 'UpdaterServiceSetting' in cli_overrides['set'].keys(): + install_automatically = cli_overrides['set']['UpdaterServiceSetting'] else: try: install_automatically = settings['UpdaterServiceSetting']['Value'] @@ -445,9 +444,9 @@ def update_manager(cli_overrides = None): #use default chunk size if none is given if cli_overrides is not None: - if '--set' in cli_overrides.keys(): - if 'UpdaterMaximumBandwidth' in cli_overrides['--set'].keys(): - chunk_size = cli_overrides['--set']['UpdaterMaximumBandwidth'] + if 'set' in cli_overrides.keys(): + if 'UpdaterMaximumBandwidth' in cli_overrides['set'].keys(): + chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth'] else: chunk_size = 1024 @@ -468,7 +467,7 @@ def update_manager(cli_overrides = None): #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. if cli_overrides is not None: if '--update-service' in cli_overrides.keys(): - UpdaterServiceURL = cli_overrides['--update-service'] + UpdaterServiceURL = cli_overrides['update-service'] else: #tells query_vvm to use the default UpdaterServiceURL = None -- cgit v1.2.3 From 4a124b8b9d709c3ae1856666bb29f28e90ab5d05 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 16 Aug 2016 09:17:07 -0700 Subject: SL-323: logging improvements --- indra/viewer_components/manager/SL_Launcher | 5 +++-- indra/viewer_components/manager/update_manager.py | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher index 1184a4c794..257543187c 100755 --- a/indra/viewer_components/manager/SL_Launcher +++ b/indra/viewer_components/manager/SL_Launcher @@ -48,7 +48,8 @@ def silent_write(log_file_handle, text): #oh and because it is best effort, it is also a holey_write ;) if (log_file_handle): #prepend text for easy grepping - log_file_handle.write("SL LAUNCHER: " + text + "\n") + timestamp = datetime.utcnow().strftime("%Y-%m-%D %H:%M:%S") + log_file_handle.write(timestamp + " LAUNCHER: " + text + "\n") def get_cmd_line(): platform_name = platform.system() @@ -136,7 +137,7 @@ def capture_vmp_args(arg_list = None, cmd_line = None): #main entry point #this and a few other update manager methods really should be refactored into a util lib parent_dir = update_manager.get_parent_path(update_manager.get_platform_key()) -log_file_handle = update_manager.get_log_file_handle(parent_dir) +log_file_handle = update_manager.get_log_file_handle(parent_dir, 'launcher.log') executable_name = "" if sys.platform.startswith('darwin'): diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index 80b0f087ab..647e7a77e6 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -43,6 +43,7 @@ except: pass from copy import deepcopy +from datetime import datetime from urlparse import urljoin import apply_update @@ -67,7 +68,8 @@ def silent_write(log_file_handle, text): #oh and because it is best effort, it is also a holey_write ;) if (log_file_handle): #prepend text for easy grepping - log_file_handle.write("UPDATE MANAGER: " + text + "\n") + timestamp = datetime.utcnow().strftime("%Y-%m-%D %H:%M:%S") + log_file_handle.write(timestamp + " UPDATE MANAGER: " + text + "\n") def after_frame(message, timeout = 10000): #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds @@ -176,10 +178,12 @@ def get_settings(log_file_handle, parent_dir): return None return settings -def get_log_file_handle(parent_dir): +def get_log_file_handle(parent_dir, filename = None): #return a write handle on the log file #plus log rotation and not dying on failure - log_file = os.path.join(parent_dir, 'update_manager.log') + if not filename: + return None + log_file = os.path.join(parent_dir, filename) old_file = log_file + '.old' #if someone's log files are present but not writable, they've screwed up their install. if os.access(log_file, os.W_OK): @@ -234,6 +238,7 @@ def query_vvm(log_file_handle = None, platform_key = None, settings = None, summ #note that the only two valid options are: # # version-phx0.damballah.lindenlab.com # # version-qa.secondlife-staging.com + print "updater service host: " + repr(UpdaterServiceURL) if UpdaterServiceURL: #we can't really expect the users to put the protocol or base dir on, they will give us a host base_URI = urljoin('https://' + UpdaterServiceURL[0], '/update/') @@ -379,7 +384,7 @@ def update_manager(cli_overrides = None): #setup and getting initial parameters platform_key = get_platform_key() parent_dir = get_parent_path(platform_key) - log_file_handle = get_log_file_handle(parent_dir) + log_file_handle = get_log_file_handle(parent_dir, 'update_manager.log') settings = None #check to see if user has install rights @@ -466,10 +471,12 @@ def update_manager(cli_overrides = None): #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. if cli_overrides is not None: - if '--update-service' in cli_overrides.keys(): + if 'update-service' in cli_overrides.keys(): UpdaterServiceURL = cli_overrides['update-service'] + else: + #tells query_vvm to use the default + UpdaterServiceURL = None else: - #tells query_vvm to use the default UpdaterServiceURL = None result_data = query_vvm(log_file_handle, platform_key, settings, channel_override_summary, UpdaterServiceURL) -- cgit v1.2.3 From 3e55d8c77a21977f4023a82ebef3fed9879247bc Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 17 Aug 2016 08:20:12 -0700 Subject: SL-323: add icon support for macs, text flow in Tkinter windows --- indra/newview/viewer_manifest.py | 6 +++++ indra/newview/vmp_icons/SL_Logo.gif | Bin 0 -> 1322 bytes indra/newview/vmp_icons/SL_Logo.png | Bin 0 -> 1484 bytes indra/newview/vmp_icons/head-sl-logo.gif | Bin 0 -> 960 bytes indra/newview/vmp_icons/head-sl-logo.png | Bin 0 -> 2316 bytes .../manager/InstallerUserMessage.py | 26 ++++++++++++++------- indra/viewer_components/manager/update_manager.py | 2 ++ 7 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 indra/newview/vmp_icons/SL_Logo.gif create mode 100644 indra/newview/vmp_icons/SL_Logo.png create mode 100644 indra/newview/vmp_icons/head-sl-logo.gif create mode 100644 indra/newview/vmp_icons/head-sl-logo.png diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 502c054730..03a727468e 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -767,6 +767,12 @@ class Darwin_i386_Manifest(ViewerManifest): self.path("uk.lproj") self.path("zh-Hans.lproj") + #VMP icons + if self.prefix("vmp_icons"): + self.path("*.png") + self.path("*.gif") + self.end_prefix("vmp_icons") + def path_optional(src, dst): """ For a number of our self.path() calls, not only do we want diff --git a/indra/newview/vmp_icons/SL_Logo.gif b/indra/newview/vmp_icons/SL_Logo.gif new file mode 100644 index 0000000000..c24d6b08cb Binary files /dev/null and b/indra/newview/vmp_icons/SL_Logo.gif differ diff --git a/indra/newview/vmp_icons/SL_Logo.png b/indra/newview/vmp_icons/SL_Logo.png new file mode 100644 index 0000000000..5e376c72f9 Binary files /dev/null and b/indra/newview/vmp_icons/SL_Logo.png differ diff --git a/indra/newview/vmp_icons/head-sl-logo.gif b/indra/newview/vmp_icons/head-sl-logo.gif new file mode 100644 index 0000000000..d635348dcc Binary files /dev/null and b/indra/newview/vmp_icons/head-sl-logo.gif differ diff --git a/indra/newview/vmp_icons/head-sl-logo.png b/indra/newview/vmp_icons/head-sl-logo.png new file mode 100644 index 0000000000..5c214e96d1 Binary files /dev/null and b/indra/newview/vmp_icons/head-sl-logo.png differ diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py index 4f81aa9cd1..8002399659 100644 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ b/indra/viewer_components/manager/InstallerUserMessage.py @@ -50,7 +50,7 @@ class InstallerUserMessage(tk.Tk): #Linden standard green color, from Marketing linden_green = "#487A7B" - def __init__(self, text="", title="", width=500, height=200, icon_name = None, icon_path = None): + def __init__(self, text="", title="", width=500, height=200, wraplength = 400, icon_name = None, icon_path = None): tk.Tk.__init__(self) self.grid() self.title(title) @@ -71,7 +71,8 @@ class InstallerUserMessage(tk.Tk): #find a few things self.script_dir = os.path.dirname(os.path.realpath(__file__)) - self.icon_dir = os.path.abspath(os.path.join(self.script_dir, 'icons')) + self.contents_dir = os.path.dirname(self.script_dir) + self.icon_dir = os.path.abspath(os.path.join(self.contents_dir, 'Resources/vmp_icons')) #finds the icon and creates the widget self.find_icon(icon_path, icon_name) @@ -104,6 +105,7 @@ class InstallerUserMessage(tk.Tk): if not icon_path: icon_path = self.icon_dir icon_path = os.path.join(icon_path, icon_name) + print icon_path if os.path.exists(icon_path): icon = tk.PhotoImage(file=icon_path) self.image_label = tk.Label(image = icon) @@ -222,12 +224,16 @@ class InstallerUserMessage(tk.Tk): self.check_scheduler() def check_scheduler(self): - if self.value < self.progress["maximum"]: - self.check_queue() - self.id = self.after(100, self.check_scheduler) - else: - #prevent a race condition between polling and the widget destruction - self.after_cancel(self.id) + try: + if self.value < self.progress["maximum"]: + self.check_queue() + self.id = self.after(100, self.check_scheduler) + else: + #prevent a race condition between polling and the widget destruction + self.after_cancel(self.id) + except tk.TclError: + #we're already dead, just die quietly + pass def check_queue(self): while self.queue.qsize(): @@ -264,7 +270,7 @@ class ThreadedClient(threading.Thread): if __name__ == "__main__": #When run as a script, just test the InstallUserMessage. - #To proceed with the test, close the first window, select on the second. The third will close by itself. + #To proceed with the test, close the first window, select on the second and fourth. The third will close by itself. import sys import tempfile @@ -278,6 +284,8 @@ if __name__ == "__main__": #basic message window test frame2 = InstallerUserMessage(text = "Something in the way she moves....", title = "Beatles Quotes for 100", icon_name="head-sl-logo.gif") + print frame2.contents_dir + print frame2.icon_dir frame2.basic_message(message = "...attracts me like no other.") print "Destroyed!" sys.stdout.flush() diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py index 647e7a77e6..7962568119 100755 --- a/indra/viewer_components/manager/update_manager.py +++ b/indra/viewer_components/manager/update_manager.py @@ -452,6 +452,8 @@ def update_manager(cli_overrides = None): if 'set' in cli_overrides.keys(): if 'UpdaterMaximumBandwidth' in cli_overrides['set'].keys(): chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth'] + else: + chunk_size = 1024 else: chunk_size = 1024 -- cgit v1.2.3 From 8ae320edf1bf17a762b0477584b13b61ea94922c Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 17 Aug 2016 08:50:43 -0700 Subject: SL-323: remove vmp icons from viewer_components subtree --- indra/viewer_components/manager/icons/SL_Logo.gif | Bin 1322 -> 0 bytes indra/viewer_components/manager/icons/SL_Logo.png | Bin 1484 -> 0 bytes indra/viewer_components/manager/icons/head-sl-logo.gif | Bin 960 -> 0 bytes indra/viewer_components/manager/icons/head-sl-logo.png | Bin 2316 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 indra/viewer_components/manager/icons/SL_Logo.gif delete mode 100644 indra/viewer_components/manager/icons/SL_Logo.png delete mode 100644 indra/viewer_components/manager/icons/head-sl-logo.gif delete mode 100644 indra/viewer_components/manager/icons/head-sl-logo.png diff --git a/indra/viewer_components/manager/icons/SL_Logo.gif b/indra/viewer_components/manager/icons/SL_Logo.gif deleted file mode 100644 index c24d6b08cb..0000000000 Binary files a/indra/viewer_components/manager/icons/SL_Logo.gif and /dev/null differ diff --git a/indra/viewer_components/manager/icons/SL_Logo.png b/indra/viewer_components/manager/icons/SL_Logo.png deleted file mode 100644 index 5e376c72f9..0000000000 Binary files a/indra/viewer_components/manager/icons/SL_Logo.png and /dev/null differ diff --git a/indra/viewer_components/manager/icons/head-sl-logo.gif b/indra/viewer_components/manager/icons/head-sl-logo.gif deleted file mode 100644 index d635348dcc..0000000000 Binary files a/indra/viewer_components/manager/icons/head-sl-logo.gif and /dev/null differ diff --git a/indra/viewer_components/manager/icons/head-sl-logo.png b/indra/viewer_components/manager/icons/head-sl-logo.png deleted file mode 100644 index 5c214e96d1..0000000000 Binary files a/indra/viewer_components/manager/icons/head-sl-logo.png and /dev/null differ -- cgit v1.2.3 From c8c143e7741d2b93b589d770b64c265228293564 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 18 Aug 2016 13:05:30 -0700 Subject: SL-323: first pass at ripping out old updater --- indra/CMakeLists.txt | 2 +- indra/newview/CMakeLists.txt | 2 - indra/newview/llappviewer.cpp | 373 ---------- indra/newview/llappviewer.h | 19 - indra/newview/llappviewerwin32.cpp | 11 - indra/newview/llfloaterabout.cpp | 83 --- indra/newview/lllogininstance.cpp | 568 +-------------- indra/newview/lllogininstance.h | 13 - indra/newview/llstartup.cpp | 1 - indra/newview/llviewercontrol.cpp | 15 - indra/newview/llviewermenu.cpp | 17 - indra/newview/tests/lllogininstance_test.cpp | 137 ---- indra/viewer_components/CMakeLists.txt | 1 - indra/viewer_components/updater/CMakeLists.txt | 106 --- .../viewer_components/updater/llupdatechecker.cpp | 187 ----- indra/viewer_components/updater/llupdatechecker.h | 119 ---- .../updater/llupdatedownloader.cpp | 604 ---------------- .../viewer_components/updater/llupdatedownloader.h | 96 --- .../updater/llupdateinstaller.cpp | 98 --- .../viewer_components/updater/llupdateinstaller.h | 58 -- .../viewer_components/updater/llupdaterservice.cpp | 760 --------------------- indra/viewer_components/updater/llupdaterservice.h | 112 --- .../updater/scripts/darwin/janitor.py | 133 ---- .../updater/scripts/darwin/messageframe.py | 66 -- .../updater/scripts/darwin/update_install.py | 412 ----------- .../updater/scripts/linux/update_install | 220 ------ .../updater/scripts/windows/update_install.bat | 3 - .../updater/tests/llupdaterservice_test.cpp | 220 ------ 28 files changed, 4 insertions(+), 4432 deletions(-) delete mode 100644 indra/viewer_components/updater/CMakeLists.txt delete mode 100644 indra/viewer_components/updater/llupdatechecker.cpp delete mode 100644 indra/viewer_components/updater/llupdatechecker.h delete mode 100644 indra/viewer_components/updater/llupdatedownloader.cpp delete mode 100644 indra/viewer_components/updater/llupdatedownloader.h delete mode 100644 indra/viewer_components/updater/llupdateinstaller.cpp delete mode 100644 indra/viewer_components/updater/llupdateinstaller.h delete mode 100644 indra/viewer_components/updater/llupdaterservice.cpp delete mode 100644 indra/viewer_components/updater/llupdaterservice.h delete mode 100644 indra/viewer_components/updater/scripts/darwin/janitor.py delete mode 100644 indra/viewer_components/updater/scripts/darwin/messageframe.py delete mode 100755 indra/viewer_components/updater/scripts/darwin/update_install.py delete mode 100755 indra/viewer_components/updater/scripts/linux/update_install delete mode 100644 indra/viewer_components/updater/scripts/windows/update_install.bat delete mode 100644 indra/viewer_components/updater/tests/llupdaterservice_test.cpp diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 1afcb118bc..84c2a14c6e 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -63,7 +63,7 @@ if (LINUX) include(LLAppearanceUtility) add_subdirectory(${LLAPPEARANCEUTILITY_SRC_DIR} ${LLAPPEARANCEUTILITY_BIN_DIR}) endif (INSTALL_PROPRIETARY) - 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_dependencies(viewer mac-crash-logger) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 31e3711569..999165528a 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -83,7 +83,6 @@ include_directories( ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} ${LLLOGIN_INCLUDE_DIRS} - ${UPDATER_INCLUDE_DIRS} ${LIBS_PREBUILT_DIR}/include/collada ${LIBS_PREBUILD_DIR}/include/hunspell ${OPENAL_LIB_INCLUDE_DIRS} @@ -1959,7 +1958,6 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${PNG_PRELOAD_ARCHIVES} ${ZLIB_PRELOAD_ARCHIVES} ${URIPARSER_PRELOAD_ARCHIVES} - ${UPDATER_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ${LLAUDIO_LIBRARIES} ${LLCHARACTER_LIBRARIES} diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6bc1f67e32..cf783b9f2f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -93,7 +93,6 @@ #include "llvocache.h" #include "llvopartgroup.h" #include "llweb.h" -#include "llupdaterservice.h" #include "llfloatertexturefetchdebugger.h" #include "llspellcheck.h" #include "llscenemonitor.h" @@ -263,7 +262,6 @@ static LLAppViewerListener sAppViewerListener(LLAppViewer::instance); // viewer.cpp - these are only used in viewer, should be easily moved. #if LL_DARWIN -const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.indra.viewer"; extern void init_apple_menu(const char* product); #endif // LL_DARWIN @@ -289,8 +287,6 @@ S32 gLastExecDuration = -1; // (<0 indicates unknown) # define LL_PLATFORM_KEY "mac" #elif LL_LINUX # define LL_PLATFORM_KEY "lnx" -#elif LL_SOLARIS -# define LL_PLATFORM_KEY "sol" #else # error "Unknown Platform" #endif @@ -467,8 +463,6 @@ struct SettingsFiles : public LLInitParam::Block static std::string gWindowTitle; -LLAppViewer::LLUpdaterInfo *LLAppViewer::sUpdaterInfo = NULL ; - //---------------------------------------------------------------------------- // Metrics logging control constants //---------------------------------------------------------------------------- @@ -695,7 +689,6 @@ LLAppViewer::LLAppViewer() mRandomizeFramerate(LLCachedControl(gSavedSettings,"Randomize Framerate", FALSE)), mPeriodicSlowFrame(LLCachedControl(gSavedSettings,"Periodic Slow Frame", FALSE)), mFastTimerLogThread(NULL), - mUpdater(new LLUpdaterService()), mSettingsLocationList(NULL) { if(NULL != sInstance) @@ -724,7 +717,6 @@ LLAppViewer::LLAppViewer() // OK to write stuff to logs now, we've now crash reported if necessary // - LLLoginInstance::instance().setUpdaterService(mUpdater.get()); LLLoginInstance::instance().setPlatformInfo(gPlatform, getOSInfo().getOSVersionString()); } @@ -732,8 +724,6 @@ LLAppViewer::~LLAppViewer() { delete mSettingsLocationList; LLViewerEventRecorder::instance().~LLViewerEventRecorder(); - - LLLoginInstance::instance().setUpdaterService(0); destroyMainloopTimeout(); @@ -881,14 +871,6 @@ bool LLAppViewer::init() writeSystemInfo(); - // Initialize updater service (now that we have an io pump) - initUpdater(); - if(isQuitting()) - { - // Early out here because updater set the quitting flag. - return true; - } - ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -2888,224 +2870,6 @@ void LLAppViewer::initStrings() } } -namespace { - // *TODO - decide if there's a better place for these functions. - // do we need a file llupdaterui.cpp or something? -brad - - void apply_update_callback(LLSD const & notification, LLSD const & response) - { - LL_DEBUGS() << "LLUpdate user response: " << response << LL_ENDL; - if(response["OK_okcancelbuttons"].asBoolean()) - { - LL_INFOS() << "LLUpdate restarting viewer" << LL_ENDL; - static const bool install_if_ready = true; - // *HACK - this lets us launch the installer immediately for now - LLUpdaterService().startChecking(install_if_ready); - } - } - - void apply_update_ok_callback(LLSD const & notification, LLSD const & response) - { - LL_INFOS() << "LLUpdate restarting viewer" << LL_ENDL; - static const bool install_if_ready = true; - // *HACK - this lets us launch the installer immediately for now - LLUpdaterService().startChecking(install_if_ready); - } - - void on_update_downloaded(LLSD const & data) - { - std::string notification_name; - void (*apply_callback)(LLSD const &, LLSD const &) = NULL; - - /* Build up the notification name... - * it can be any of these, which are included here for the sake of grep: - * RequiredUpdateDownloadedDialog - * RequiredUpdateDownloadedVerboseDialog - * OtherChannelRequiredUpdateDownloadedDialog - * OtherChannelRequiredUpdateDownloadedVerbose - * DownloadBackgroundTip - * DownloadBackgroundDialog - * OtherChannelDownloadBackgroundTip - * OtherChannelDownloadBackgroundDialog - */ - { - LL_DEBUGS("UpdaterService") << "data = "; - std::ostringstream data_dump; - LLSDSerialize::toNotation(data, data_dump); - LL_CONT << data_dump.str() << LL_ENDL; - } - if(data["channel"].asString() != LLVersionInfo::getChannel()) - { - notification_name.append("OtherChannel"); - } - if(data["required"].asBoolean()) - { - if(LLStartUp::getStartupState() <= STATE_LOGIN_WAIT) - { - // The user never saw the progress bar. - apply_callback = &apply_update_ok_callback; - notification_name += "RequiredUpdateDownloadedVerboseDialog"; - } - else if(LLStartUp::getStartupState() < STATE_WORLD_INIT) - { - // The user is logging in but blocked. - apply_callback = &apply_update_ok_callback; - notification_name += "RequiredUpdateDownloadedDialog"; - } - else - { - // The user is already logged in; treat like an optional update. - apply_callback = &apply_update_callback; - notification_name += "DownloadBackgroundTip"; - } - } - else - { - apply_callback = &apply_update_callback; - if(LLStartUp::getStartupState() < STATE_STARTED) - { - // CHOP-262 we need to use a different notification - // method prior to login. - notification_name += "DownloadBackgroundDialog"; - } - else - { - notification_name += "DownloadBackgroundTip"; - } - } - - LLSD substitutions; - substitutions["VERSION"] = data["version"]; - std::string new_channel = data["channel"].asString(); - substitutions["NEW_CHANNEL"] = new_channel; - std::string info_url = data["info_url"].asString(); - if ( !info_url.empty() ) - { - substitutions["INFO_URL"] = info_url; - } - else - { - LL_WARNS("UpdaterService") << "no info url supplied - defaulting to hard coded release notes pattern" << LL_ENDL; - - // truncate version at the rightmost '.' - std::string version_short(data["version"]); - size_t short_length = version_short.rfind('.'); - if (short_length != std::string::npos) - { - version_short.resize(short_length); - } - - LLUIString relnotes_url("[RELEASE_NOTES_BASE_URL][CHANNEL_URL]/[VERSION_SHORT]"); - relnotes_url.setArg("[VERSION_SHORT]", version_short); - - // *TODO thread the update service's response through to this point - std::string const & channel = LLVersionInfo::getChannel(); - boost::shared_ptr channel_escaped(curl_escape(channel.c_str(), channel.size()), &curl_free); - - relnotes_url.setArg("[CHANNEL_URL]", channel_escaped.get()); - relnotes_url.setArg("[RELEASE_NOTES_BASE_URL]", LLTrans::getString("RELEASE_NOTES_BASE_URL")); - substitutions["INFO_URL"] = relnotes_url.getString(); - } - - LLNotificationsUtil::add(notification_name, substitutions, LLSD(), apply_callback); - } - - void install_error_callback(LLSD const & notification, LLSD const & response) - { - LLAppViewer::instance()->forceQuit(); - } - - bool notify_update(LLSD const & evt) - { - std::string notification_name; - switch (evt["type"].asInteger()) - { - case LLUpdaterService::DOWNLOAD_COMPLETE: - on_update_downloaded(evt); - break; - case LLUpdaterService::INSTALL_ERROR: - if(evt["required"].asBoolean()) { - LLNotificationsUtil::add("FailedRequiredUpdateInstall", LLSD(), LLSD(), &install_error_callback); - } else { - LLNotificationsUtil::add("FailedUpdateInstall"); - } - break; - default: - break; - } - - // let others also handle this event by default - return false; - } - - bool on_bandwidth_throttle(LLUpdaterService * updater, LLSD const & evt) - { - updater->setBandwidthLimit(evt.asInteger() * (1024/8)); - return false; // Let others receive this event. - }; -}; - -void LLAppViewer::initUpdater() -{ - // Initialize the updater service. - // Get Channel - // Get Version - - /***************************************************************** - * Previously, the url was derived from the settings - * UpdaterServiceURL - * UpdaterServicePath - * it is now obtained from the grid manager. The settings above - * are no longer used. - *****************************************************************/ - std::string channel = LLVersionInfo::getChannel(); - std::string version = LLVersionInfo::getVersion(); - - U32 check_period = gSavedSettings.getU32("UpdaterServiceCheckPeriod"); - bool willing_to_test; - LL_DEBUGS("UpdaterService") << "channel " << channel << LL_ENDL; - - if (LLVersionInfo::TEST_VIEWER == LLVersionInfo::getViewerMaturity()) - { - LL_INFOS("UpdaterService") << "Test build: overriding willing_to_test by sending testno" << LL_ENDL; - willing_to_test = false; - } - else - { - willing_to_test = gSavedSettings.getBOOL("UpdaterWillingToTest"); - } - unsigned char unique_id[MD5HEX_STR_SIZE]; - if ( ! llHashedUniqueID(unique_id) ) - { - if ( willing_to_test ) - { - LL_WARNS("UpdaterService") << "Unable to provide a unique id; overriding willing_to_test by sending testno" << LL_ENDL; - } - willing_to_test = false; - } - - mUpdater->setAppExitCallback(boost::bind(&LLAppViewer::forceQuit, this)); - mUpdater->initialize(channel, - version, - gPlatform, - getOSInfo().getOSVersionString(), - unique_id, - willing_to_test - ); - mUpdater->setCheckPeriod(check_period); - mUpdater->setBandwidthLimit((int)gSavedSettings.getF32("UpdaterMaximumBandwidth") * (1024/8)); - gSavedSettings.getControl("UpdaterMaximumBandwidth")->getSignal()-> - connect(boost::bind(&on_bandwidth_throttle, mUpdater.get(), _2)); - if(gSavedSettings.getU32("UpdaterServiceSetting")) - { - bool install_if_ready = true; - mUpdater->startChecking(install_if_ready); - } - - LLEventPump & updater_pump = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()); - updater_pump.listen("notify_update", ¬ify_update); -} - // // This function decides whether the client machine meets the minimum requirements to // run in a maximized window, per the consensus of davep, boa and nyx on 3/30/2011. @@ -5663,143 +5427,6 @@ void LLAppViewer::handleLoginComplete() mSavePerAccountSettings=true; } -void LLAppViewer::launchUpdater() -{ - LLSD query_map = LLSD::emptyMap(); - query_map["os"] = gPlatform; - - // *TODO change userserver to be grid on both viewer and sim, since - // userserver no longer exists. - query_map["userserver"] = LLGridManager::getInstance()->getGridId(); - query_map["channel"] = LLVersionInfo::getChannel(); - // *TODO constantize this guy - // *NOTE: This URL is also used in win_setup/lldownloader.cpp - LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); - - if(LLAppViewer::sUpdaterInfo) - { - delete LLAppViewer::sUpdaterInfo; - } - LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; - - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION ) - { - // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString()); - }; - -#if LL_WINDOWS - LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); - if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - // We're hosed, bail - LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; - return; - } - - LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; - - std::string updater_source = gDirUtilp->getAppRODataDir(); - updater_source += gDirUtilp->getDirDelimiter(); - updater_source += "updater.exe"; - - LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source - << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath - << LL_ENDL; - - - if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; - - return; - } - - LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; - - //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. - LLAppViewer::instance()->removeMarkerFiles(); // In case updater fails - - // *NOTE:Mani The updater is spawned as the last thing before the WinMain exit. - // see LLAppViewerWin32.cpp - -#elif LL_DARWIN - LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -bundleid \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LL_VERSION_BUNDLE_ID; - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // Run the auto-updater. - system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ - -#elif (LL_LINUX || LL_SOLARIS) && LL_GTK - // we tell the updater where to find the xml containing string - // translations which it can use for its own UI - std::string xml_strings_file = "strings.xml"; - std::vector xui_path_vec = - gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_strings_file); - std::string xml_search_paths; - const char* delim = ""; - // build comma-delimited list of xml paths to pass to updater - BOOST_FOREACH(std::string this_skin_path, xui_path_vec) - { - // Although we already have the full set of paths with the filename - // appended, the linux-updater.bin command-line switches require us to - // snip the filename OFF and pass it as a separate switch argument. :-P - LL_INFOS() << "Got a XUI path: " << this_skin_path << LL_ENDL; - xml_search_paths.append(delim); - xml_search_paths.append(gDirUtilp->getDirName(this_skin_path)); - delim = ","; - } - // build the overall command-line to run the updater correctly - LLAppViewer::sUpdaterInfo->mUpdateExePath = - gDirUtilp->getExecutableDir() + "/" + "linux-updater.bin" + - " --url \"" + update_url.asString() + "\"" + - " --name \"" + LLAppViewer::instance()->getSecondLifeTitle() + "\"" + - " --dest \"" + gDirUtilp->getAppRODataDir() + "\"" + - " --stringsdir \"" + xml_search_paths + "\"" + - " --stringsfile \"" + xml_strings_file + "\""; - - LL_INFOS("AppInit") << "Calling updater: " - << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // *TODO: we could use the gdk equivalent to ensure the updater - // gets started on the same screen. - GError *error = NULL; - if (!g_spawn_command_line_async(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), &error)) - { - LL_ERRS() << "Failed to launch updater: " - << error->message - << LL_ENDL; - } - if (error) { - g_error_free(error); - } -#else - OSMessageBox(LLTrans::getString("MBNoAutoUpdate"), LLStringUtil::null, OSMB_OK); -#endif - - // *REMOVE:Mani - Saving for reference... - // LLAppViewer::instance()->forceQuit(); -} - - //virtual void LLAppViewer::setMasterSystemAudioMute(bool mute) { diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index b5e674bd7b..35d85a9fb1 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -55,7 +55,6 @@ class LLTextureCache; class LLImageDecodeThread; class LLTextureFetch; class LLWatchdogTimeout; -class LLUpdaterService; class LLViewerJoystick; extern LLTrace::BlockTimerStatHandle FTM_FRAME; @@ -228,7 +227,6 @@ private: bool initThreads(); // Initialize viewer threads, return false on failure. bool initConfiguration(); // Initialize settings from the command line/config file. void initStrings(); // Initialize LLTrans machinery - void initUpdater(); // Initialize the updater service. bool initCache(); // Initialize local client cache. void checkMemory() ; @@ -310,27 +308,10 @@ private: LLAllocator mAlloc; LLFrameTimer mMemCheckTimer; - - boost::scoped_ptr mUpdater; // llcorehttp library init/shutdown helper LLAppCoreHttp mAppCoreHttp; - //--------------------------------------------- - //*NOTE: Mani - legacy updater stuff - // Still useable? -public: - - //some information for updater - typedef struct - { - std::string mUpdateExePath; - std::ostringstream mParams; - }LLUpdaterInfo ; - static LLUpdaterInfo *sUpdaterInfo ; - - void launchUpdater(); - //--------------------------------------------- }; // consts from viewer.h diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 4786f83bfd..6d879f6416 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -361,17 +361,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, delete viewer_app_ptr; viewer_app_ptr = NULL; - //start updater - if(LLAppViewer::sUpdaterInfo) - { - _spawnl(_P_NOWAIT, LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), LLAppViewer::sUpdaterInfo->mParams.str().c_str(), NULL); - - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - } - - - // (NVAPI) (6) We clean up. This is analogous to doing a free() if (hSession) { diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index bababca652..0d262e73ef 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -39,7 +39,6 @@ #include "llslurl.h" #include "llvoiceclient.h" #include "lluictrlfactory.h" -#include "llupdaterservice.h" #include "llviewertexteditor.h" #include "llviewercontrol.h" #include "llviewerstats.h" @@ -91,10 +90,6 @@ public: void onClickCopyToClipboard(); void onClickUpdateCheck(); - // checks state of updater service and starts a check outside of schedule. - // subscribes callback for closest state update - static void setUpdateListener(); - private: void setSupportText(const std::string& server_release_notes_url); @@ -103,9 +98,6 @@ private: // callback method for manual checks static bool callbackCheckUpdate(LLSD const & event); - - // listener name for update checks - static const std::string sCheckUpdateListenerName; static void startFetchServerReleaseNotes(); static void handleServerReleaseNotes(LLSD results); @@ -139,9 +131,6 @@ BOOL LLFloaterAbout::postBuild() getChild("copy_btn")->setCommitCallback( boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); - getChild("update_btn")->setCommitCallback( - boost::bind(&LLFloaterAbout::onClickUpdateCheck, this)); - static const LLUIColor about_color = LLUIColorTable::instance().getColor("TextFgReadOnlyColor"); if (gAgent.getRegion()) @@ -289,11 +278,6 @@ void LLFloaterAbout::onClickCopyToClipboard() support_widget->deselect(); } -void LLFloaterAbout::onClickUpdateCheck() -{ - setUpdateListener(); -} - void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) { #if LL_WINDOWS @@ -314,68 +298,6 @@ void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) FALSE, LLStyle::Params() .color(about_color)); } -///---------------------------------------------------------------------------- -/// Floater About Update-check related functions -///---------------------------------------------------------------------------- - -const std::string LLFloaterAbout::sCheckUpdateListenerName = "LLUpdateNotificationListener"; - -void LLFloaterAbout::showCheckUpdateNotification(S32 state) -{ - switch (state) - { - case LLUpdaterService::UP_TO_DATE: - LLNotificationsUtil::add("UpdateViewerUpToDate"); - break; - case LLUpdaterService::DOWNLOADING: - case LLUpdaterService::INSTALLING: - LLNotificationsUtil::add("UpdateDownloadInProgress"); - break; - case LLUpdaterService::TERMINAL: - // download complete, user triggered check after download pop-up appeared - LLNotificationsUtil::add("UpdateDownloadComplete"); - break; - default: - LLNotificationsUtil::add("UpdateCheckError"); - break; - } -} - -bool LLFloaterAbout::callbackCheckUpdate(LLSD const & event) -{ - if (!event.has("payload")) - { - return false; - } - - LLSD payload = event["payload"]; - if (payload.has("type") && payload["type"].asInteger() == LLUpdaterService::STATE_CHANGE) - { - LLEventPumps::instance().obtain("mainlooprepeater").stopListening(sCheckUpdateListenerName); - showCheckUpdateNotification(payload["state"].asInteger()); - } - return false; -} - -void LLFloaterAbout::setUpdateListener() -{ - LLUpdaterService update_service; - S32 service_state = update_service.getState(); - // Note: Do not set state listener before forceCheck() since it set's new state - if (update_service.forceCheck() || service_state == LLUpdaterService::CHECKING_FOR_UPDATE) - { - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainlooprepeater")); - if (mainloop.getListener(sCheckUpdateListenerName) == LLBoundListener()) // dummy listener - { - mainloop.listen(sCheckUpdateListenerName, boost::bind(&callbackCheckUpdate, _1)); - } - } - else - { - showCheckUpdateNotification(service_state); - } -} - ///---------------------------------------------------------------------------- /// LLFloaterAboutUtil ///---------------------------------------------------------------------------- @@ -386,8 +308,3 @@ void LLFloaterAboutUtil::registerFloater() } -void LLFloaterAboutUtil::checkUpdatesAndNotify() -{ - LLFloaterAbout::setUpdateListener(); -} - diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index b4d0bb6823..b9e9c12aec 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -50,13 +50,12 @@ #include "llwindow.h" #include "llviewerwindow.h" #include "llprogressview.h" -#if LL_LINUX || LL_SOLARIS +#if LL_LINUX #include "lltrans.h" #endif #include "llsecapi.h" #include "llstartup.h" #include "llmachineid.h" -#include "llupdaterservice.h" #include "llevents.h" #include "llappviewer.h" @@ -68,406 +67,11 @@ public: virtual ~Disposable() {} }; -namespace { - class MandatoryUpdateMachine: - public LLLoginInstance::Disposable - { - public: - MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService); - - void start(void); - - LLNotificationsInterface& getNotificationsInterface() const - { - return mLoginInstance.getNotificationsInterface(); - } - - private: - class State; - class CheckingForUpdate; - class Error; - class ReadyToInstall; - class StartingUpdaterService; - class WaitingForDownload; - - boost::scoped_ptr mState; - LLLoginInstance & mLoginInstance; - LLUpdaterService & mUpdaterService; - - void setCurrentState(State * newState); - }; - - - class MandatoryUpdateMachine::State { - public: - virtual ~State() {} - virtual void enter(void) {} - virtual void exit(void) {} - }; - - - class MandatoryUpdateMachine::CheckingForUpdate: - public MandatoryUpdateMachine::State - { - public: - CheckingForUpdate(MandatoryUpdateMachine & machine); - - virtual void enter(void); - virtual void exit(void); - - private: - LLTempBoundListener mConnection; - MandatoryUpdateMachine & mMachine; - LLProgressView * mProgressView; - - bool onEvent(LLSD const & event); - }; - - - class MandatoryUpdateMachine::Error: - public MandatoryUpdateMachine::State - { - public: - Error(MandatoryUpdateMachine & machine); - - virtual void enter(void); - virtual void exit(void); - void onButtonClicked(const LLSD &, const LLSD &); - - private: - MandatoryUpdateMachine & mMachine; - }; - - - class MandatoryUpdateMachine::ReadyToInstall: - public MandatoryUpdateMachine::State - { - public: - ReadyToInstall(MandatoryUpdateMachine & machine); - - virtual void enter(void); - virtual void exit(void); - - private: - //MandatoryUpdateMachine & mMachine; - }; - - - class MandatoryUpdateMachine::StartingUpdaterService: - public MandatoryUpdateMachine::State - { - public: - StartingUpdaterService(MandatoryUpdateMachine & machine); - - virtual void enter(void); - virtual void exit(void); - void onButtonClicked(const LLSD & uiform, const LLSD & result); - private: - MandatoryUpdateMachine & mMachine; - }; - - - class MandatoryUpdateMachine::WaitingForDownload: - public MandatoryUpdateMachine::State - { - public: - WaitingForDownload(MandatoryUpdateMachine & machine); - - virtual void enter(void); - virtual void exit(void); - - private: - LLTempBoundListener mConnection; - MandatoryUpdateMachine & mMachine; - LLProgressView * mProgressView; - - bool onEvent(LLSD const & event); - }; -} - static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; std::string construct_start_string(); - - -// MandatoryUpdateMachine -//----------------------------------------------------------------------------- - - -MandatoryUpdateMachine::MandatoryUpdateMachine(LLLoginInstance & loginInstance, LLUpdaterService & updaterService): - mLoginInstance(loginInstance), - mUpdaterService(updaterService) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::start(void) -{ - LL_INFOS() << "starting mandatory update machine" << LL_ENDL; - - if(mUpdaterService.isChecking()) { - switch(mUpdaterService.getState()) { - case LLUpdaterService::UP_TO_DATE: - mUpdaterService.stopChecking(); - mUpdaterService.startChecking(); - // Fall through. - case LLUpdaterService::INITIAL: - case LLUpdaterService::CHECKING_FOR_UPDATE: - setCurrentState(new CheckingForUpdate(*this)); - break; - case LLUpdaterService::TEMPORARY_ERROR: - setCurrentState(new Error(*this)); - break; - case LLUpdaterService::DOWNLOADING: - setCurrentState(new WaitingForDownload(*this)); - break; - case LLUpdaterService::TERMINAL: - if(LLUpdaterService::updateReadyToInstall()) { - setCurrentState(new ReadyToInstall(*this)); - } else { - setCurrentState(new Error(*this)); - } - break; - case LLUpdaterService::FAILURE: - setCurrentState(new Error(*this)); - break; - default: - llassert(!"unpossible case"); - break; - } - } else { - setCurrentState(new StartingUpdaterService(*this)); - } -} - - -void MandatoryUpdateMachine::setCurrentState(State * newStatePointer) -{ - { - boost::scoped_ptr newState(newStatePointer); - if(mState != 0) mState->exit(); - mState.swap(newState); - - // Old state will be deleted on exit from this block before the new state - // is entered. - } - if(mState != 0) mState->enter(); -} - - - -// MandatoryUpdateMachine::CheckingForUpdate -//----------------------------------------------------------------------------- - - -MandatoryUpdateMachine::CheckingForUpdate::CheckingForUpdate(MandatoryUpdateMachine & machine): - mMachine(machine) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::CheckingForUpdate::enter(void) -{ - LL_INFOS() << "entering checking for update" << LL_ENDL; - - mProgressView = gViewerWindow->getProgressView(); - mProgressView->setMessage("Looking for update..."); - mProgressView->setText("There is a required update for your Second Life installation."); - mProgressView->setPercent(0); - mProgressView->setVisible(true); - mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()). - listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::CheckingForUpdate::onEvent, this, _1)); -} - - -void MandatoryUpdateMachine::CheckingForUpdate::exit(void) -{ -} - - -bool MandatoryUpdateMachine::CheckingForUpdate::onEvent(LLSD const & event) -{ - if(event["type"].asInteger() == LLUpdaterService::STATE_CHANGE) { - switch(event["state"].asInteger()) { - case LLUpdaterService::DOWNLOADING: - mMachine.setCurrentState(new WaitingForDownload(mMachine)); - break; - case LLUpdaterService::TEMPORARY_ERROR: - case LLUpdaterService::UP_TO_DATE: - case LLUpdaterService::TERMINAL: - case LLUpdaterService::FAILURE: - mProgressView->setVisible(false); - mMachine.setCurrentState(new Error(mMachine)); - break; - case LLUpdaterService::INSTALLING: - llassert(!"can't possibly be installing"); - break; - default: - break; - } - } else { - ; // Ignore. - } - - return false; -} - - - -// MandatoryUpdateMachine::Error -//----------------------------------------------------------------------------- - - -MandatoryUpdateMachine::Error::Error(MandatoryUpdateMachine & machine): - mMachine(machine) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::Error::enter(void) -{ - LL_INFOS() << "entering error" << LL_ENDL; - mMachine.getNotificationsInterface().add("FailedRequiredUpdateInstall", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::Error::onButtonClicked, this, _1, _2)); -} - - -void MandatoryUpdateMachine::Error::exit(void) -{ - LLAppViewer::instance()->forceQuit(); -} - - -void MandatoryUpdateMachine::Error::onButtonClicked(const LLSD &, const LLSD &) -{ - mMachine.setCurrentState(0); -} - - - -// MandatoryUpdateMachine::ReadyToInstall -//----------------------------------------------------------------------------- - - -MandatoryUpdateMachine::ReadyToInstall::ReadyToInstall(MandatoryUpdateMachine & machine) //: - //mMachine(machine) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::ReadyToInstall::enter(void) -{ - LL_INFOS() << "entering ready to install" << LL_ENDL; - // Open update ready dialog. -} - - -void MandatoryUpdateMachine::ReadyToInstall::exit(void) -{ - // Restart viewer. -} - - - -// MandatoryUpdateMachine::StartingUpdaterService -//----------------------------------------------------------------------------- - - -MandatoryUpdateMachine::StartingUpdaterService::StartingUpdaterService(MandatoryUpdateMachine & machine): - mMachine(machine) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::StartingUpdaterService::enter(void) -{ - LL_INFOS() << "entering start update service" << LL_ENDL; - mMachine.getNotificationsInterface().add("UpdaterServiceNotRunning", LLSD(), LLSD(), boost::bind(&MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked, this, _1, _2)); -} - - -void MandatoryUpdateMachine::StartingUpdaterService::exit(void) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::StartingUpdaterService::onButtonClicked(const LLSD & uiform, const LLSD & result) -{ - if(result["OK_okcancelbuttons"].asBoolean()) { - mMachine.mUpdaterService.startChecking(false); - mMachine.setCurrentState(new CheckingForUpdate(mMachine)); - } else { - LLAppViewer::instance()->forceQuit(); - } -} - - - -// MandatoryUpdateMachine::WaitingForDownload -//----------------------------------------------------------------------------- - - -MandatoryUpdateMachine::WaitingForDownload::WaitingForDownload(MandatoryUpdateMachine & machine): - mMachine(machine), - mProgressView(0) -{ - ; // No op. -} - - -void MandatoryUpdateMachine::WaitingForDownload::enter(void) -{ - LL_INFOS() << "entering waiting for download" << LL_ENDL; - mProgressView = gViewerWindow->getProgressView(); - mProgressView->setMessage("Downloading update..."); - std::ostringstream stream; - stream << "There is a required update for your Second Life installation." << std::endl << - "Version " << mMachine.mUpdaterService.updatedVersion(); - mProgressView->setText(stream.str()); - mProgressView->setPercent(0); - mProgressView->setVisible(true); - mConnection = LLEventPumps::instance().obtain(LLUpdaterService::pumpName()). - listen("MandatoryUpdateMachine::CheckingForUpdate", boost::bind(&MandatoryUpdateMachine::WaitingForDownload::onEvent, this, _1)); -} - - -void MandatoryUpdateMachine::WaitingForDownload::exit(void) -{ - mProgressView->setVisible(false); -} - - -bool MandatoryUpdateMachine::WaitingForDownload::onEvent(LLSD const & event) -{ - switch(event["type"].asInteger()) { - case LLUpdaterService::DOWNLOAD_COMPLETE: - mMachine.setCurrentState(new ReadyToInstall(mMachine)); - break; - case LLUpdaterService::DOWNLOAD_ERROR: - mMachine.setCurrentState(new Error(mMachine)); - break; - case LLUpdaterService::PROGRESS: { - double downloadSize = event["download_size"].asReal(); - double bytesDownloaded = event["bytes_downloaded"].asReal(); - mProgressView->setPercent(100. * bytesDownloaded / downloadSize); - break; - } - default: - break; - } - - return false; -} - - - // LLLoginInstance //----------------------------------------------------------------------------- @@ -476,11 +80,9 @@ LLLoginInstance::LLLoginInstance() : mLoginModule(new LLLogin()), mNotifications(NULL), mLoginState("offline"), - mSkipOptionalUpdate(false), mAttemptComplete(false), mTransferRate(0.0f), - mDispatcher("LLLoginInstance", "change"), - mUpdaterService(0) + mDispatcher("LLLoginInstance", "change") { mLoginModule->getEventPump().listen("lllogininstance", boost::bind(&LLLoginInstance::handleLoginEvent, this, _1)); @@ -590,18 +192,11 @@ void LLLoginInstance::constructAuthParams(LLPointer user_credentia // (re)initialize the request params with creds. LLSD request_params = user_credential->getLoginParams(); - unsigned char hashed_unique_id_string[MD5HEX_STR_SIZE]; - if ( ! llHashedUniqueID(hashed_unique_id_string) ) - { - LL_WARNS() << "Not providing a unique id in request params" << LL_ENDL; - } request_params["start"] = construct_start_string(); - request_params["skipoptional"] = mSkipOptionalUpdate; request_params["agree_to_tos"] = false; // Always false here. Set true in request_params["read_critical"] = false; // handleTOSResponse request_params["last_exec_event"] = mLastExecEvent; request_params["last_exec_duration"] = mLastExecDuration; - request_params["mac"] = (char*)hashed_unique_id_string; request_params["version"] = LLVersionInfo::getVersion(); request_params["channel"] = LLVersionInfo::getChannel(); request_params["platform"] = mPlatform; @@ -698,19 +293,6 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) boost::bind(&LLLoginInstance::handleTOSResponse, this, _1, "read_critical")); } - else if(reason_response == "update" || gSavedSettings.getBOOL("ForceMandatoryUpdate")) - { - LL_INFOS() << "LLLoginInstance::handleLoginFailure update" << LL_ENDL; - - gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); - updateApp(true, message_response); - } - else if(reason_response == "optional") - { - LL_INFOS() << "LLLoginInstance::handleLoginFailure optional" << LL_ENDL; - - updateApp(false, message_response); - } else { LL_INFOS() << "LLLoginInstance::handleLoginFailure attemptComplete" << LL_ENDL; @@ -722,22 +304,7 @@ void LLLoginInstance::handleLoginSuccess(const LLSD& event) { LL_INFOS() << "LLLoginInstance::handleLoginSuccess" << LL_ENDL; - if(gSavedSettings.getBOOL("ForceMandatoryUpdate")) - { - LLSD response = event["data"]; - std::string message_response = response["message"].asString(); - - // Testing update... - gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); - - // Don't confuse startup by leaving login "online". - mLoginModule->disconnect(); - updateApp(true, message_response); - } - else - { - attemptComplete(); - } + attemptComplete(); } void LLLoginInstance::handleDisconnect(const LLSD& event) @@ -787,135 +354,6 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) return true; } - -void LLLoginInstance::updateApp(bool mandatory, const std::string& auth_msg) -{ - if(mandatory) - { - gViewerWindow->setShowProgress(false); - MandatoryUpdateMachine * machine = new MandatoryUpdateMachine(*this, *mUpdaterService); - mUpdateStateMachine.reset(machine); - machine->start(); - return; - } - - // store off config state, as we might quit soon - gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); - LLUIColorTable::instance().saveUserSettings(); - - std::ostringstream message; - std::string msg; - if (!auth_msg.empty()) - { - msg = "(" + auth_msg + ") \n"; - } - - LLSD args; - args["MESSAGE"] = msg; - - LLSD payload; - payload["mandatory"] = mandatory; - - /* - * We're constructing one of the following 9 strings here: - * "DownloadWindowsMandatory" - * "DownloadWindowsReleaseForDownload" - * "DownloadWindows" - * "DownloadMacMandatory" - * "DownloadMacReleaseForDownload" - * "DownloadMac" - * "DownloadLinuxMandatory" - * "DownloadLinuxReleaseForDownload" - * "DownloadLinux" - * - * I've called them out explicitly in this comment so that they can be grepped for. - */ - std::string notification_name = "Download"; - -#if LL_WINDOWS - notification_name += "Windows"; -#elif LL_DARWIN - notification_name += "Mac"; -#else - notification_name += "Linux"; -#endif - - if (mandatory) - { - notification_name += "Mandatory"; - } - else - { -#if LL_RELEASE_FOR_DOWNLOAD - notification_name += "ReleaseForDownload"; -#endif - } - - if(mNotifications) - { - mNotifications->add(notification_name, args, payload, - boost::bind(&LLLoginInstance::updateDialogCallback, this, _1, _2)); - - gViewerWindow->setShowProgress(false); - } -} - -bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - std::string update_exe_path; - bool mandatory = notification["payload"]["mandatory"].asBoolean(); - -#if !LL_RELEASE_FOR_DOWNLOAD - if (option == 2) - { - // This condition attempts to skip the - // update if using a dev build. - // The relog probably won't work if the - // update is mandatory. :) - - // *REMOVE:Mani - Saving for reference... - //LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); - mSkipOptionalUpdate = true; - reconnect(); - return false; - } -#endif - - if (option == 1) - { - // ...user doesn't want to do it - if (mandatory) - { - // Mandatory update, user chose to not to update... - // The login attemp is complete, startup should - // quit when detecting this. - attemptComplete(); - - // *REMOVE:Mani - Saving for reference... - //LLAppViewer::instance()->forceQuit(); - // // Bump them back to the login screen. - // //reset_login(); - } - else - { - // Optional update, user chose to skip - mSkipOptionalUpdate = true; - reconnect(); - } - return false; - } - - if(mUpdaterLauncher) - { - mUpdaterLauncher(); - } - - attemptComplete(); - - return false; -} - std::string construct_start_string() { std::string start; diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index c6773bbf68..dfd39e4752 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -34,7 +34,6 @@ class LLLogin; class LLEventStream; class LLNotificationsInterface; -class LLUpdaterService; // This class hosts the login module and is used to // negotiate user authentication attempts. @@ -60,10 +59,6 @@ public: // Only valid when authSuccess == true. const F64 getLastTransferRateBPS() { return mTransferRate; } - - // Whether to tell login to skip optional update request. - // False by default. - void setSkipOptionalUpdate(bool state) { mSkipOptionalUpdate = state; } void setSerialNumber(const std::string& sn) { mSerialNumber = sn; } void setLastExecEvent(int lee) { mLastExecEvent = lee; } void setLastExecDuration(S32 duration) { mLastExecDuration = duration; } @@ -72,10 +67,6 @@ public: void setNotificationsInterface(LLNotificationsInterface* ni) { mNotifications = ni; } LLNotificationsInterface& getNotificationsInterface() const { return *mNotifications; } - typedef boost::function UpdaterLauncherCallback; - void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; } - - void setUpdaterService(LLUpdaterService * updaterService) { mUpdaterService = updaterService; } private: void constructAuthParams(LLPointer user_credentials); void updateApp(bool mandatory, const std::string& message); @@ -97,7 +88,6 @@ private: std::string mLoginState; LLSD mRequestData; LLSD mResponseData; - bool mSkipOptionalUpdate; bool mAttemptComplete; F64 mTransferRate; std::string mSerialNumber; @@ -105,10 +95,7 @@ private: S32 mLastExecDuration; std::string mPlatform; std::string mPlatformVersion; - UpdaterLauncherCallback mUpdaterLauncher; LLEventDispatcher mDispatcher; - LLUpdaterService * mUpdaterService; - boost::scoped_ptr mUpdateStateMachine; }; #endif diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a2c8e7772e..62ca3434d7 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -1009,7 +1009,6 @@ bool idle_startup() login->setSerialNumber(LLAppViewer::instance()->getSerialNumber()); login->setLastExecEvent(gLastExecEvent); login->setLastExecDuration(gLastExecDuration); - login->setUpdaterLauncher(boost::bind(&LLAppViewer::launchUpdater, LLAppViewer::instance())); // This call to LLLoginInstance::connect() starts the // authentication process. diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 16f40fb747..2cf56582f5 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -74,7 +74,6 @@ #include "llspellcheck.h" #include "llslurl.h" #include "llstartup.h" -#include "llupdaterservice.h" // Third party library includes #include @@ -575,19 +574,6 @@ bool toggle_show_object_render_cost(const LLSD& newvalue) return true; } -void toggle_updater_service_active(const LLSD& new_value) -{ - if(new_value.asInteger()) - { - LLUpdaterService update_service; - if(!update_service.isChecking()) update_service.startChecking(); - } - else - { - LLUpdaterService().stopChecking(); - } -} - //////////////////////////////////////////////////////////////////////////// void settings_setup_listeners() @@ -735,7 +721,6 @@ void settings_setup_listeners() gSavedSettings.getControl("ShowNavbarNavigationPanel")->getSignal()->connect(boost::bind(&toggle_show_navigation_panel, _2)); gSavedSettings.getControl("ShowMiniLocationPanel")->getSignal()->connect(boost::bind(&toggle_show_mini_location_panel, _2)); gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2)); - gSavedSettings.getControl("UpdaterServiceSetting")->getSignal()->connect(boost::bind(&toggle_updater_service_active, _2)); gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2)); gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2)); gSavedSettings.getControl("SpellCheck")->getSignal()->connect(boost::bind(&handleSpellCheckChanged)); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 99a9ed1d75..1b1bbd5071 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -2119,22 +2119,6 @@ class LLAdvancedCheckShowObjectUpdates : public view_listener_t -/////////////////////// -// CHECK FOR UPDATES // -/////////////////////// - - - -class LLAdvancedCheckViewerUpdates : public view_listener_t -{ - bool handleEvent(const LLSD& userdata) - { - LLFloaterAboutUtil::checkUpdatesAndNotify(); - return true; - } -}; - - //////////////////// // COMPRESS IMAGE // //////////////////// @@ -8918,7 +8902,6 @@ void initialize_menus() // Advanced (toplevel) view_listener_t::addMenu(new LLAdvancedToggleShowObjectUpdates(), "Advanced.ToggleShowObjectUpdates"); view_listener_t::addMenu(new LLAdvancedCheckShowObjectUpdates(), "Advanced.CheckShowObjectUpdates"); - view_listener_t::addMenu(new LLAdvancedCheckViewerUpdates(), "Advanced.CheckViewerUpdates"); view_listener_t::addMenu(new LLAdvancedCompressImage(), "Advanced.CompressImage"); view_listener_t::addMenu(new LLAdvancedShowDebugSettings(), "Advanced.ShowDebugSettings"); view_listener_t::addMenu(new LLAdvancedEnableViewAdminOptions(), "Advanced.EnableViewAdminOptions"); diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 1b0334498e..b43aaeaa33 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -192,36 +192,6 @@ const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; } void LLAppViewer::forceQuit(void) {} LLAppViewer * LLAppViewer::sInstance = 0; -//----------------------------------------------------------------------------- -#include "llupdaterservice.h" - -std::string const & LLUpdaterService::pumpName(void) -{ - static std::string wakka = "wakka wakka wakka"; - return wakka; -} -bool LLUpdaterService::updateReadyToInstall(void) { return false; } -void LLUpdaterService::initialize(const std::string& channel, - const std::string& version, - const std::string& platform, - const std::string& platform_version, - const unsigned char uniqueid[MD5HEX_STR_SIZE], - const bool& willing_to_test - ) {} - -void LLUpdaterService::setCheckPeriod(unsigned int seconds) {} -void LLUpdaterService::startChecking(bool install_if_ready) {} -void LLUpdaterService::stopChecking() {} -bool LLUpdaterService::isChecking() { return false; } -LLUpdaterService::eUpdaterState LLUpdaterService::getState() { return INITIAL; } -std::string LLUpdaterService::updatedVersion() { return ""; } - -bool llHashedUniqueID(unsigned char* id) -{ - memcpy( id, "66666666666666666666666666666666", MD5HEX_STR_SIZE ); - return true; -} - //----------------------------------------------------------------------------- #include "llnotifications.h" #include "llfloaterreg.h" @@ -338,7 +308,6 @@ namespace tut gSavedSettings.declareBOOL("NoInventoryLibrary", FALSE, "", LLControlVariable::PERSIST_NO); gSavedSettings.declareBOOL("ConnectAsGod", FALSE, "", LLControlVariable::PERSIST_NO); gSavedSettings.declareBOOL("UseDebugMenus", FALSE, "", LLControlVariable::PERSIST_NO); - gSavedSettings.declareBOOL("ForceMandatoryUpdate", FALSE, "", LLControlVariable::PERSIST_NO); gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", LLControlVariable::PERSIST_NO); gSavedSettings.declareString("NextLoginLocation", "", "", LLControlVariable::PERSIST_NO); gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", LLControlVariable::PERSIST_NO); @@ -476,110 +445,4 @@ namespace tut logininstance->connect(test_uri, agentCredential); ensure_equals("Default for agree to tos", gLoginCreds["params"]["read_critical"].asBoolean(), false); } - - template<> template<> - void lllogininstance_object::test<3>() - { - set_test_name("Test Mandatory Update User Accepts"); - - // Part 1 - Mandatory Update, with User accepts response. - // Test connect with update needed. - logininstance->connect(agentCredential); - - ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); - - // Update needed failure response. - LLSD response; - response["state"] = "offline"; - response["change"] = "fail.login"; - response["progress"] = 0.0; - response["transfer_rate"] = 7; - response["data"]["reason"] = "update"; - gTestPump.post(response); - - ensure_equals("Notification added", notifications.addedCount(), 1); - - notifications.sendYesResponse(); - - ensure("Disconnected", !(logininstance->authSuccess())); - } - - template<> template<> - void lllogininstance_object::test<4>() - { - set_test_name("Test Mandatory Update User Decline"); - - // Test connect with update needed. - logininstance->connect(agentCredential); - - ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); - - // Update needed failure response. - LLSD response; - response["state"] = "offline"; - response["change"] = "fail.login"; - response["progress"] = 0.0; - response["transfer_rate"] = 7; - response["data"]["reason"] = "update"; - gTestPump.post(response); - - ensure_equals("Notification added", notifications.addedCount(), 1); - notifications.sendNoResponse(); - - ensure("Disconnected", !(logininstance->authSuccess())); - } - - template<> template<> - void lllogininstance_object::test<6>() - { - set_test_name("Test Optional Update User Accept"); - - // Part 3 - Mandatory Update, with bogus response. - // Test connect with update needed. - logininstance->connect(agentCredential); - - ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); - - // Update needed failure response. - LLSD response; - response["state"] = "offline"; - response["change"] = "fail.login"; - response["progress"] = 0.0; - response["transfer_rate"] = 7; - response["data"]["reason"] = "optional"; - gTestPump.post(response); - - ensure_equals("Notification added", notifications.addedCount(), 1); - notifications.sendYesResponse(); - - ensure("Disconnected", !(logininstance->authSuccess())); - } - - template<> template<> - void lllogininstance_object::test<7>() - { - set_test_name("Test Optional Update User Denies"); - - // Part 3 - Mandatory Update, with bogus response. - // Test connect with update needed. - logininstance->connect(agentCredential); - - ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI); - - // Update needed failure response. - LLSD response; - response["state"] = "offline"; - response["change"] = "fail.login"; - response["progress"] = 0.0; - response["transfer_rate"] = 7; - response["data"]["reason"] = "optional"; - gTestPump.post(response); - - ensure_equals("Notification added", notifications.addedCount(), 1); - notifications.sendNoResponse(); - - // User skips, should be reconnecting. - ensure_equals("reconnect uri", gLoginURI, VIEWERLOGIN_URI); - ensure_equals("skipping optional update", gLoginCreds["params"]["skipoptional"].asBoolean(), true); - } } diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt index 74c9b4568d..642dada7b2 100644 --- a/indra/viewer_components/CMakeLists.txt +++ b/indra/viewer_components/CMakeLists.txt @@ -1,4 +1,3 @@ # -*- cmake -*- add_subdirectory(login) -add_subdirectory(updater) diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt deleted file mode 100644 index 73e18aacb3..0000000000 --- a/indra/viewer_components/updater/CMakeLists.txt +++ /dev/null @@ -1,106 +0,0 @@ -# -*- cmake -*- - -project(updater_service) - -include(00-Common) -if(LL_TESTS) - include(LLAddBuildTest) -endif(LL_TESTS) -include(Boost) -include(CMakeCopyIfDifferent) -include(CURL) -include(LLCommon) -include(LLCoreHttp) -include(LLMessage) -include(LLPlugin) -include(LLVFS) - -include_directories( - ${LLCOMMON_INCLUDE_DIRS} - ${LLCOREHTTP_INCLUDE_DIRS} - ${LLMESSAGE_INCLUDE_DIRS} - ${LLPLUGIN_INCLUDE_DIRS} - ${LLVFS_INCLUDE_DIRS} - ${CURL_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/newview - ) -include_directories(SYSTEM - ${LLCOMMON_SYSTEM_INCLUDE_DIRS} - ) - -set(updater_service_SOURCE_FILES - llupdaterservice.cpp - llupdatechecker.cpp - llupdatedownloader.cpp - llupdateinstaller.cpp - ) - -set(updater_service_HEADER_FILES - llupdaterservice.h - llupdatechecker.h - llupdatedownloader.h - llupdateinstaller.h - ) - -set_source_files_properties(${updater_service_HEADER_FILES} - PROPERTIES HEADER_FILE_ONLY TRUE) - -set_source_files_properties( - llupdaterservice.cpp - PROPERTIES - COMPILE_DEFINITIONS "${VIEWER_CHANNEL_VERSION_DEFINES}" # see BuildVersion.cmake - ) - -list(APPEND - updater_service_SOURCE_FILES - ${updater_service_HEADER_FILES} - ) - -add_library(llupdaterservice - ${updater_service_SOURCE_FILES} - ) - -target_link_libraries(llupdaterservice - ${LLCOMMON_LIBRARIES} - ${LLMESSAGE_LIBRARIES} - ${LLCOREHTTP_LIBRARIES} - ${LLPLUGIN_LIBRARIES} - ${LLVFS_LIBRARIES} - ) - -if(LL_TESTS) -if (NOT LINUX) - SET(llupdater_service_TEST_SOURCE_FILES - llupdaterservice.cpp - ) - -set(test_libs - ${LLCOMMON_LIBRARIES} - ${BOOST_COROUTINE_LIBRARY} - ${BOOST_CONTEXT_LIBRARY} - ${BOOST_THREAD_LIBRARY} - ${BOOST_SYSTEM_LIBRARY}) - -set_source_files_properties( - llupdaterservice.cpp - PROPERTIES - LL_TEST_ADDITIONAL_LIBRARIES ${test_libs} -# *NOTE:Mani - I was trying to use the preprocessor seam to mock out -# llifstream (and other) llcommon classes. It didn't work -# because of the windows declspec(dllimport)attribute. -# LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream" - ) - - LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}" ${test_libs}) -endif (NOT LINUX) -endif(LL_TESTS) - -set(UPDATER_INCLUDE_DIRS - ${LIBS_OPEN_DIR}/viewer_components/updater - CACHE INTERNAL "" -) - -set(UPDATER_LIBRARIES - llupdaterservice - CACHE INTERNAL "" -) diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp deleted file mode 100644 index 1bb5e95740..0000000000 --- a/indra/viewer_components/updater/llupdatechecker.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/** - * @file llupdaterservice.cpp - * - * $LicenseInfo:firstyear=2010&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 "linden_common.h" -#include -#include -#include "llsd.h" -#include "llupdatechecker.h" -#include "lluri.h" -#include "llcorehttputil.h" -#if LL_DARWIN -#include -#endif - -#if LL_WINDOWS -#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally -#endif - - -class LLUpdateChecker::CheckError: - public std::runtime_error -{ -public: - CheckError(const char * message): - std::runtime_error(message) - { - ; // No op. - } -}; - - -// LLUpdateChecker -//----------------------------------------------------------------------------- -LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client): - mImplementation(new LLUpdateChecker::Implementation(client)) -{ - ; // No op. -} - -void LLUpdateChecker::checkVersion(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test) -{ - mImplementation->checkVersion(urlBase, channel, version, platform, platform_version, uniqueid, willing_to_test); -} - - -// LLUpdateChecker::Implementation -//----------------------------------------------------------------------------- -const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.1"; - - -LLUpdateChecker::Implementation::Implementation(LLUpdateChecker::Client & client): - mClient(client), - mInProgress(false), - mProtocol(sProtocolVersion) -{ - ; // No op. -} - - -LLUpdateChecker::Implementation::~Implementation() -{ - ; // No op. -} - - -void LLUpdateChecker::Implementation::checkVersion(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test) -{ - if (!mInProgress) - { - mInProgress = true; - - mUrlBase = urlBase; - mChannel = channel; - mVersion = version; - mPlatform = platform; - mPlatformVersion = platform_version; - memcpy(mUniqueId, uniqueid, MD5HEX_STR_SIZE); - mWillingToTest = willing_to_test; - - mProtocol = sProtocolVersion; - - std::string checkUrl = buildUrl(urlBase, channel, version, platform, platform_version, uniqueid, willing_to_test); - LL_INFOS("UpdaterService") << "checking for updates at " << checkUrl << LL_ENDL; - - LLCoros::instance().launch("LLUpdateChecker::Implementation::checkVersionCoro", - boost::bind(&Implementation::checkVersionCoro, this, checkUrl)); - - } - else - { - LL_WARNS("UpdaterService") << "attempting to restart a check when one is in progress; ignored" << LL_ENDL; - } -} - -void LLUpdateChecker::Implementation::checkVersionCoro(std::string url) -{ - LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); - LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t - httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("checkVersionCoro", httpPolicy)); - LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); - - LL_INFOS("checkVersionCoro") << "Getting update information from " << url << LL_ENDL; - - LLSD result = httpAdapter->getAndSuspend(httpRequest, url); - - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - - mInProgress = false; - - if (status != LLCore::HttpStatus(HTTP_OK)) - { - std::string server_error; - if (result.has("error_code")) - { - server_error += result["error_code"].asString(); - } - if (result.has("error_text")) - { - server_error += server_error.empty() ? "" : ": "; - server_error += result["error_text"].asString(); - } - - LL_WARNS("UpdaterService") << "response error " << status.getStatus() - << " " << status.toString() - << " (" << server_error << ")" - << LL_ENDL; - mClient.error(status.toString()); - return; - } - - result.erase(LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS); - mClient.response(result); -} - -std::string LLUpdateChecker::Implementation::buildUrl(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test) -{ - LLSD path; - path.append(mProtocol); - path.append(channel); - path.append(version); - path.append(platform); - path.append(platform_version); - path.append(willing_to_test ? "testok" : "testno"); - path.append((char*)uniqueid); - return LLURI::buildHTTP(urlBase, path).asString(); -} diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h deleted file mode 100644 index d10ea4cf42..0000000000 --- a/indra/viewer_components/updater/llupdatechecker.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * @file llupdatechecker.h - * - * $LicenseInfo:firstyear=2010&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$ - */ - -#ifndef LL_UPDATERCHECKER_H -#define LL_UPDATERCHECKER_H - - -#include - -#include "llmd5.h" -#include "lleventcoro.h" -#include "llcoros.h" - -// -// Implements asynchronous checking for updates. -// -class LLUpdateChecker { -public: - // - // The client interface implemented by a requestor checking for an update. - // - class Client - { - public: - // An error occurred while checking for an update. - virtual void error(std::string const & message) = 0; - - // A successful response was received from the viewer version manager - virtual void response(LLSD const & content) = 0; - }; - - // An exception that may be raised on check errors. - class CheckError; - - LLUpdateChecker(Client & client); - - // Check status of current app on the given host for the channel and version provided. - void checkVersion(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test); - -private: - class Implementation - { - public: - typedef boost::shared_ptr ptr_t; - - Implementation(Client & client); - ~Implementation(); - void checkVersion(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test - ); - - - private: - static const char * sLegacyProtocolVersion; - static const char * sProtocolVersion; - const char* mProtocol; - - Client & mClient; - bool mInProgress; - std::string mVersion; - std::string mUrlBase; - std::string mChannel; - std::string mPlatform; - std::string mPlatformVersion; - unsigned char mUniqueId[MD5HEX_STR_SIZE]; - bool mWillingToTest; - - std::string buildUrl(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test); - - void checkVersionCoro(std::string url); - - LOG_CLASS(LLUpdateChecker::Implementation); - }; - - - Implementation::ptr_t mImplementation; - //LLPointer mImplementation; -}; - -#endif diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp deleted file mode 100644 index 382689afa0..0000000000 --- a/indra/viewer_components/updater/llupdatedownloader.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/** - * @file llupdatedownloader.cpp - * - * $LicenseInfo:firstyear=2010&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 "linden_common.h" - -#include "llupdatedownloader.h" -#include "httpcommon.h" -#include -#include -#include -#include -#include "lldir.h" -#include "llevents.h" -#include "llfile.h" -#include "llmd5.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llthread.h" -#include "llupdaterservice.h" - -class LLUpdateDownloader::Implementation: - public LLThread -{ -public: - Implementation(LLUpdateDownloader::Client & client); - ~Implementation(); - void cancel(void); - void download(LLURI const & uri, - std::string const & hash, - std::string const & updateChannel, - std::string const & updateVersion, - std::string const & info_url, - bool required); - bool isDownloading(void); - size_t onHeader(void * header, size_t size); - size_t onBody(void * header, size_t size); - int onProgress(curl_off_t downloadSize, curl_off_t bytesDownloaded); - void resume(void); - void setBandwidthLimit(U64 bytesPerSecond); - -private: - curl_off_t mBandwidthLimit; - bool mCancelled; - LLUpdateDownloader::Client & mClient; - LLCore::LLHttp::CURL_ptr mCurl; - LLSD mDownloadData; - llofstream mDownloadStream; - unsigned char mDownloadPercent; - std::string mDownloadRecordPath; - curl_slist * mHeaderList; - - void initializeCurlGet(std::string const & url, bool processHeader); - void resumeDownloading(size_t startByte); - void run(void); - void startDownloading(LLURI const & uri, std::string const & hash); - void throwOnCurlError(CURLcode code); - bool validateDownload(const std::string& filePath); - bool validateOrRemove(const std::string& filePath); - - LOG_CLASS(LLUpdateDownloader::Implementation); -}; - - -namespace { - class DownloadError: - public std::runtime_error - { - public: - DownloadError(const char * message): - std::runtime_error(message) - { - ; // No op. - } - }; - - - const char * gSecondLifeUpdateRecord = "SecondLifeUpdateDownload.xml"; -}; - - - -// LLUpdateDownloader -//----------------------------------------------------------------------------- - - - -std::string LLUpdateDownloader::downloadMarkerPath(void) -{ - return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, gSecondLifeUpdateRecord); -} - - -LLUpdateDownloader::LLUpdateDownloader(Client & client): - mImplementation(new LLUpdateDownloader::Implementation(client)) -{ - ; // No op. -} - - -void LLUpdateDownloader::cancel(void) -{ - mImplementation->cancel(); -} - - -void LLUpdateDownloader::download(LLURI const & uri, - std::string const & hash, - std::string const & updateChannel, - std::string const & updateVersion, - std::string const & info_url, - bool required) -{ - mImplementation->download(uri, hash, updateChannel, updateVersion, info_url, required); -} - - -bool LLUpdateDownloader::isDownloading(void) -{ - return mImplementation->isDownloading(); -} - - -void LLUpdateDownloader::resume(void) -{ - mImplementation->resume(); -} - - -void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) -{ - mImplementation->setBandwidthLimit(bytesPerSecond); -} - - - -// LLUpdateDownloader::Implementation -//----------------------------------------------------------------------------- - - -namespace { - size_t write_function(void * data, size_t blockSize, size_t blocks, void * downloader) - { - size_t bytes = blockSize * blocks; - return reinterpret_cast(downloader)->onBody(data, bytes); - } - - - size_t header_function(void * data, size_t blockSize, size_t blocks, void * downloader) - { - size_t bytes = blockSize * blocks; - return reinterpret_cast(downloader)->onHeader(data, bytes); - } - - - int xferinfo_callback(void * downloader, - curl_off_t dowloadTotal, - curl_off_t downloadNow, - curl_off_t uploadTotal, - curl_off_t uploadNow) - { - return reinterpret_cast(downloader)-> - onProgress(dowloadTotal, downloadNow); - } -} - - -LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client): - LLThread("LLUpdateDownloader"), - mBandwidthLimit(0), - mCancelled(false), - mClient(client), - mCurl(), - mDownloadPercent(0), - mHeaderList(0) -{ - CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case. - llverify(code == CURLE_OK); // TODO: real error handling here. -} - - -LLUpdateDownloader::Implementation::~Implementation() -{ - if(isDownloading()) - { - cancel(); - shutdown(); - } - else - { - ; // No op. - } - mCurl.reset(); -} - - -void LLUpdateDownloader::Implementation::cancel(void) -{ - mCancelled = true; -} - - -void LLUpdateDownloader::Implementation::download(LLURI const & uri, - std::string const & hash, - std::string const & updateChannel, - std::string const & updateVersion, - std::string const & info_url, - bool required) -{ - if(isDownloading()) mClient.downloadError("download in progress"); - - mDownloadRecordPath = downloadMarkerPath(); - mDownloadData = LLSD(); - mDownloadData["required"] = required; - mDownloadData["update_channel"] = updateChannel; - mDownloadData["update_version"] = updateVersion; - if (!info_url.empty()) - { - mDownloadData["info_url"] = info_url; - } - try - { - startDownloading(uri, hash); - } - catch(DownloadError const & e) - { - mClient.downloadError(e.what()); - } -} - - -bool LLUpdateDownloader::Implementation::isDownloading(void) -{ - return !isStopped(); -} - - -void LLUpdateDownloader::Implementation::resume(void) -{ - mCancelled = false; - - if(isDownloading()) - { - mClient.downloadError("download in progress"); - } - - mDownloadRecordPath = downloadMarkerPath(); - llifstream dataStream(mDownloadRecordPath.c_str()); - if(!dataStream) - { - mClient.downloadError("no download marker"); - return; - } - - LLSDSerialize::fromXMLDocument(mDownloadData, dataStream); - - if(!mDownloadData.asBoolean()) - { - mClient.downloadError("no download information in marker"); - return; - } - - std::string filePath = mDownloadData["path"].asString(); - try - { - if(LLFile::isfile(filePath)) - { - llstat fileStatus; - LLFile::stat(filePath, &fileStatus); - if(fileStatus.st_size != mDownloadData["size"].asInteger()) - { - resumeDownloading(fileStatus.st_size); - } - else if(!validateOrRemove(filePath)) - { - download(LLURI(mDownloadData["url"].asString()), - mDownloadData["hash"].asString(), - mDownloadData["update_channel"].asString(), - mDownloadData["update_version"].asString(), - mDownloadData["info_url"].asString(), - mDownloadData["required"].asBoolean()); - } - else - { - mClient.downloadComplete(mDownloadData); - } - } - else - { - download(LLURI(mDownloadData["url"].asString()), - mDownloadData["hash"].asString(), - mDownloadData["update_channel"].asString(), - mDownloadData["update_version"].asString(), - mDownloadData["info_url"].asString(), - mDownloadData["required"].asBoolean()); - } - } - catch(DownloadError & e) - { - mClient.downloadError(e.what()); - } -} - - -void LLUpdateDownloader::Implementation::setBandwidthLimit(U64 bytesPerSecond) -{ - if((mBandwidthLimit != bytesPerSecond) && isDownloading() && !mDownloadData["required"].asBoolean()) - { - llassert(static_cast(mCurl)); - mBandwidthLimit = bytesPerSecond; - CURLcode code = curl_easy_setopt(mCurl.get(), CURLOPT_MAX_RECV_SPEED_LARGE, &mBandwidthLimit); - if(code != CURLE_OK) - { - LL_WARNS("UpdaterService") << "unable to change dowload bandwidth" << LL_ENDL; - } - } - else - { - mBandwidthLimit = bytesPerSecond; - } -} - - -size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size) -{ - char const * headerPtr = reinterpret_cast (buffer); - std::string header(headerPtr, headerPtr + size); - size_t colonPosition = header.find(':'); - if(colonPosition == std::string::npos) return size; // HTML response; ignore. - - if(header.substr(0, colonPosition) == "Content-Length") { - try { - size_t firstDigitPos = header.find_first_of("0123456789", colonPosition); - size_t lastDigitPos = header.find_last_of("0123456789"); - std::string contentLength = header.substr(firstDigitPos, lastDigitPos - firstDigitPos + 1); - size_t size = boost::lexical_cast(contentLength); - LL_INFOS("UpdaterService") << "download size is " << size << LL_ENDL; - - mDownloadData["size"] = LLSD(LLSD::Integer(size)); - llofstream odataStream(mDownloadRecordPath.c_str()); - LLSDSerialize::toPrettyXML(mDownloadData, odataStream); - } catch (std::exception const & e) { - LL_WARNS("UpdaterService") << "unable to read content length (" - << e.what() << ")" << LL_ENDL; - } - } else { - ; // No op. - } - - return size; -} - - -size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size) -{ - if(mCancelled) return 0; // Forces a write error which will halt curl thread. - if((size == 0) || (buffer == 0)) return 0; - - mDownloadStream.write(static_cast(buffer), size); - if(mDownloadStream.bad()) { - return 0; - } else { - return size; - } -} - - -int LLUpdateDownloader::Implementation::onProgress(curl_off_t downloadSize, curl_off_t bytesDownloaded) -{ - int downloadPercent = static_cast(100.0 * ((double) bytesDownloaded / (double) downloadSize)); - if(downloadPercent > mDownloadPercent) { - mDownloadPercent = downloadPercent; - - LLSD event; - event["pump"] = LLUpdaterService::pumpName(); - LLSD payload; - payload["type"] = LLSD(LLUpdaterService::PROGRESS); - payload["download_size"] = (LLSD::Integer) downloadSize; - payload["bytes_downloaded"] = (LLSD::Integer) bytesDownloaded; - event["payload"] = payload; - LLEventPumps::instance().obtain("mainlooprepeater").post(event); - - LL_INFOS("UpdaterService") << "progress event " << payload << LL_ENDL; - } else { - ; // Keep events to a reasonalbe number. - } - - return 0; -} - - -void LLUpdateDownloader::Implementation::run(void) -{ - CURLcode code = curl_easy_perform(mCurl.get()); - mDownloadStream.close(); - if(code == CURLE_OK) - { - LLFile::remove(mDownloadRecordPath); - if(validateOrRemove(mDownloadData["path"])) - { - LL_INFOS("UpdaterService") << "download successful" << LL_ENDL; - mClient.downloadComplete(mDownloadData); - } - else - { - mClient.downloadError("failed hash check"); - } - } - else if(mCancelled && (code == CURLE_WRITE_ERROR)) - { - LL_INFOS("UpdaterService") << "download canceled by user" << LL_ENDL; - // Do not call back client. - } - else - { - LL_WARNS("UpdaterService") << "download failed with error '" << - curl_easy_strerror(code) << "'" << LL_ENDL; - LLFile::remove(mDownloadRecordPath); - if(mDownloadData.has("path")) - { - std::string filePath = mDownloadData["path"].asString(); - LL_INFOS("UpdaterService") << "removing " << filePath << LL_ENDL; - LLFile::remove(filePath); - } - mClient.downloadError("curl error"); - } - - if(mHeaderList) - { - curl_slist_free_all(mHeaderList); - mHeaderList = 0; - } -} - - -void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & url, bool processHeader) -{ - if(!mCurl) - { - mCurl = LLCore::LLHttp::createEasyHandle(); - } - else - { - curl_easy_reset(mCurl.get()); - } - - if(!mCurl) - { - throw DownloadError("failed to initialize curl"); - } - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOSIGNAL, true)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_FOLLOWLOCATION, true)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_WRITEFUNCTION, &write_function)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_WRITEDATA, this)); - if(processHeader) - { - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HEADERFUNCTION, &header_function)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HEADERDATA, this)); - } - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPGET, true)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_URL, url.c_str())); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_XFERINFOFUNCTION, &xferinfo_callback)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_XFERINFODATA, this)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOPROGRESS, 0)); - // if it's a required update set the bandwidth limit to 0 (unlimited) - curl_off_t limit = mDownloadData["required"].asBoolean() ? 0 : mBandwidthLimit; - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_MAX_RECV_SPEED_LARGE, limit)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str())); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_SSL_VERIFYHOST, 2)); - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_SSL_VERIFYPEER, 1)); - - mDownloadPercent = 0; -} - - -void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte) -{ - LL_INFOS("UpdaterService") << "resuming download from " << mDownloadData["url"].asString() - << " at byte " << startByte << LL_ENDL; - - initializeCurlGet(mDownloadData["url"].asString(), false); - - // The header 'Range: bytes n-' will request the bytes remaining in the - // source begining with byte n and ending with the last byte. - boost::format rangeHeaderFormat("Range: bytes=%u-"); - rangeHeaderFormat % startByte; - mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str()); - if(mHeaderList == 0) - { - throw DownloadError("cannot add Range header"); - } - throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPHEADER, mHeaderList)); - - mDownloadStream.open(mDownloadData["path"].asString().c_str(), - std::ios_base::out | std::ios_base::binary | std::ios_base::app); - start(); -} - - -void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std::string const & hash) -{ - mDownloadData["url"] = uri.asString(); - mDownloadData["hash"] = hash; - mDownloadData["current_version"] = ll_get_version(); - LLSD path = uri.pathArray(); - if(path.size() == 0) throw DownloadError("no file path"); - std::string fileName = path[path.size() - 1].asString(); - std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName); - mDownloadData["path"] = filePath; - - LL_INFOS("UpdaterService") << "downloading " << filePath - << " from " << uri.asString() << LL_ENDL; - LL_INFOS("UpdaterService") << "hash of file is " << hash << LL_ENDL; - - llofstream dataStream(mDownloadRecordPath.c_str()); - LLSDSerialize::toPrettyXML(mDownloadData, dataStream); - - mDownloadStream.open(filePath.c_str(), std::ios_base::out | std::ios_base::binary); - initializeCurlGet(uri.asString(), true); - start(); -} - - -void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code) -{ - if(code != CURLE_OK) { - const char * errorString = curl_easy_strerror(code); - if(errorString != 0) { - throw DownloadError(curl_easy_strerror(code)); - } else { - throw DownloadError("unknown curl error"); - } - } else { - ; // No op. - } -} - -bool LLUpdateDownloader::Implementation::validateOrRemove(const std::string& filePath) -{ - bool valid = validateDownload(filePath); - if (! valid) - { - LL_INFOS("UpdaterService") << "removing " << filePath << LL_ENDL; - LLFile::remove(filePath); - } - return valid; -} - -bool LLUpdateDownloader::Implementation::validateDownload(const std::string& filePath) -{ - llifstream fileStream(filePath.c_str(), std::ios_base::in | std::ios_base::binary); - if(!fileStream) - { - LL_INFOS("UpdaterService") << "can't open " << filePath << ", invalid" << LL_ENDL; - return false; - } - - std::string hash = mDownloadData["hash"].asString(); - if (! hash.empty()) - { - char digest[33]; - LLMD5(fileStream).hex_digest(digest); - if (hash == digest) - { - LL_INFOS("UpdaterService") << "verified hash " << hash - << " for downloaded " << filePath << LL_ENDL; - return true; - } - else - { - LL_WARNS("UpdaterService") << "download hash mismatch for " - << filePath << ": expected " << hash - << " but computed " << digest << LL_ENDL; - return false; - } - } - else - { - LL_INFOS("UpdaterService") << "no hash specified for " << filePath - << ", unverified" << LL_ENDL; - return true; // No hash check provided. - } -} diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h deleted file mode 100644 index f759988f12..0000000000 --- a/indra/viewer_components/updater/llupdatedownloader.h +++ /dev/null @@ -1,96 +0,0 @@ -/** - * @file llupdatedownloader.h - * - * $LicenseInfo:firstyear=2010&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$ - */ - -#ifndef LL_UPDATE_DOWNLOADER_H -#define LL_UPDATE_DOWNLOADER_H - - -#include -#include -#include "lluri.h" - - -// -// An asynchronous download service for fetching updates. -// -class LLUpdateDownloader -{ -public: - class Client; - class Implementation; - - // Returns the path to the download marker file containing details of the - // latest download. - static std::string downloadMarkerPath(void); - - LLUpdateDownloader(Client & client); - - // Cancel any in progress download; a no op if none is in progress. The - // client will not receive a complete or error callback. - void cancel(void); - - // Start a new download. - void download(LLURI const & uri, - std::string const & hash, - std::string const & updateChannel, - std::string const & updateVersion, - std::string const & info_url, - bool required=false); - - // Returns true if a download is in progress. - bool isDownloading(void); - - // Resume a partial download. - void resume(void); - - // Set a limit on the dowload rate. - void setBandwidthLimit(U64 bytesPerSecond); - -private: - boost::shared_ptr mImplementation; -}; - - -// -// An interface to be implemented by clients initiating a update download. -// -class LLUpdateDownloader::Client { -public: - - // The download has completed successfully. - // data is a map containing the following items: - // url - source (remote) location - // hash - the md5 sum that should match the installer file. - // path - destination (local) location - // required - boolean indicating if this is a required update. - // size - the size of the installer in bytes - virtual void downloadComplete(LLSD const & data) = 0; - - // The download failed. - virtual void downloadError(std::string const & message) = 0; -}; - - -#endif diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp deleted file mode 100644 index a0e2c0b362..0000000000 --- a/indra/viewer_components/updater/llupdateinstaller.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @file llupdateinstaller.cpp - * - * $LicenseInfo:firstyear=2010&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 "linden_common.h" -#include -#include "llapr.h" -#include "llprocess.h" -#include "llupdateinstaller.h" -#include "lldir.h" -#include "llsd.h" - -#if defined(LL_WINDOWS) -#pragma warning(disable: 4702) // disable 'unreachable code' so we can use lexical_cast (really!). -#endif -#include - - -namespace { - class RelocateError {}; - - - std::string copy_to_temp(std::string const & path) - { - std::string scriptFile = gDirUtilp->getBaseFileName(path); - std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile); - apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp); - if(status != APR_SUCCESS) throw RelocateError(); - - return newPath; - } -} - - -int ll_install_update(std::string const & script, - std::string const & updatePath, - bool required, - LLInstallScriptMode mode) -{ - std::string actualScriptPath; - switch(mode) { - case LL_COPY_INSTALL_SCRIPT_TO_TEMP: - try { - actualScriptPath = copy_to_temp(script); - } - catch (RelocateError &) { - return -1; - } - break; - case LL_RUN_INSTALL_SCRIPT_IN_PLACE: - actualScriptPath = script; - break; - default: - llassert(!"unpossible copy mode"); - } - - LL_INFOS("Updater") << "UpdateInstaller: installing " << updatePath << " using " << - actualScriptPath << LL_ENDL; - - LLProcess::Params params; - params.executable = actualScriptPath; - params.args.add(updatePath); - params.args.add(ll_install_failed_marker_path()); - params.args.add(boost::lexical_cast(required)); - params.autokill = false; - return LLProcess::create(params)? 0 : -1; -} - - -std::string const & ll_install_failed_marker_path(void) -{ - static std::string path; - if(path.empty()) { - path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeInstallFailed.marker"); - } - return path; -} diff --git a/indra/viewer_components/updater/llupdateinstaller.h b/indra/viewer_components/updater/llupdateinstaller.h deleted file mode 100644 index fe5b1d19b5..0000000000 --- a/indra/viewer_components/updater/llupdateinstaller.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file llupdateinstaller.h - * - * $LicenseInfo:firstyear=2010&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$ - */ - -#ifndef LL_UPDATE_INSTALLER_H -#define LL_UPDATE_INSTALLER_H - - -#include - - -enum LLInstallScriptMode { - LL_RUN_INSTALL_SCRIPT_IN_PLACE, - LL_COPY_INSTALL_SCRIPT_TO_TEMP -}; - -// -// Launch the installation script. -// -// The updater will overwrite the current installation, so it is highly recommended -// that the current application terminate once this function is called. -// -int ll_install_update( - std::string const & script, // Script to execute. - std::string const & updatePath, // Path to update file. - bool required, // Is the update required. - LLInstallScriptMode mode=LL_COPY_INSTALL_SCRIPT_TO_TEMP); // Run in place or copy to temp? - - -// -// Returns the path which points to the failed install marker file, should it -// exist. -// -std::string const & ll_install_failed_marker_path(void); - - -#endif diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp deleted file mode 100644 index 788955a1b2..0000000000 --- a/indra/viewer_components/updater/llupdaterservice.cpp +++ /dev/null @@ -1,760 +0,0 @@ -/** - * @file llupdaterservice.cpp - * - * $LicenseInfo:firstyear=2010&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 "linden_common.h" - -#include "llupdaterservice.h" - -#include "llupdatedownloader.h" -#include "llevents.h" -#include "lltimer.h" -#include "llupdatechecker.h" -#include "llupdateinstaller.h" - -#include -#include -#include "lldir.h" -#include "llsdserialize.h" -#include "llfile.h" -#include "llviewernetwork.h" - -#if LL_WINDOWS -#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally -#endif - -#if ! defined(LL_VIEWER_VERSION_MAJOR) \ - || ! defined(LL_VIEWER_VERSION_MINOR) \ - || ! defined(LL_VIEWER_VERSION_PATCH) \ - || ! defined(LL_VIEWER_VERSION_BUILD) -#error "Version information is undefined" -#endif - -namespace -{ - boost::weak_ptr gUpdater; - - const std::string UPDATE_MARKER_FILENAME("SecondLifeUpdateReady.xml"); - std::string update_marker_path() - { - return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - UPDATE_MARKER_FILENAME); - } - - std::string install_script_path(void) - { -#ifdef LL_WINDOWS - std::string scriptFile = "update_install.bat"; -#elif LL_DARWIN - std::string scriptFile = "update_install.py"; -#else - std::string scriptFile = "update_install"; -#endif - return gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, scriptFile); - } - - LLInstallScriptMode install_script_mode(void) - { -#ifdef LL_WINDOWS - return LL_COPY_INSTALL_SCRIPT_TO_TEMP; -#else - // This is important on Mac because update_install.py looks at its own - // script pathname to discover the viewer app bundle to update. - return LL_RUN_INSTALL_SCRIPT_IN_PLACE; -#endif - }; - -} - -class LLUpdaterServiceImpl : - public LLUpdateChecker::Client, - public LLUpdateDownloader::Client -{ - static const std::string sListenerName; - - std::string mProtocolVersion; - std::string mChannel; - std::string mVersion; - std::string mPlatform; - std::string mPlatformVersion; - unsigned char mUniqueId[MD5HEX_STR_SIZE]; - bool mWillingToTest; - - unsigned int mCheckPeriod; - bool mIsChecking; - bool mIsDownloading; - - LLUpdateChecker mUpdateChecker; - LLUpdateDownloader mUpdateDownloader; - LLTimer mTimer; - - LLUpdaterService::app_exit_callback_t mAppExitCallback; - - LLUpdaterService::eUpdaterState mState; - - LOG_CLASS(LLUpdaterServiceImpl); - -public: - LLUpdaterServiceImpl(); - virtual ~LLUpdaterServiceImpl(); - - void initialize(const std::string& channel, - const std::string& version, - const std::string& platform, - const std::string& platform_version, - const unsigned char uniqueid[MD5HEX_STR_SIZE], - const bool& willing_to_test - ); - - void setCheckPeriod(unsigned int seconds); - void setBandwidthLimit(U64 bytesPerSecond); - - void startChecking(bool install_if_ready); - void stopChecking(); - bool forceCheck(); - bool isChecking(); - LLUpdaterService::eUpdaterState getState(); - - void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;} - std::string updatedVersion(void); - - bool checkForInstall(bool launchInstaller); // Test if a local install is ready. - bool checkForResume(); // Test for resumeable d/l. - - // LLUpdateChecker::Client: - virtual void error(std::string const & message); - - // A successful response was received from the viewer version manager - virtual void response(LLSD const & content); - - // LLUpdateDownloader::Client - void downloadComplete(LLSD const & data); - void downloadError(std::string const & message); - - bool onMainLoop(LLSD const & event); - -private: - std::string mNewChannel; - std::string mNewVersion; - - void restartTimer(unsigned int seconds); - void setState(LLUpdaterService::eUpdaterState state); - void stopTimer(); -}; - -const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl"; - -LLUpdaterServiceImpl::LLUpdaterServiceImpl() : - mIsChecking(false), - mIsDownloading(false), - mCheckPeriod(0), - mUpdateChecker(*this), - mUpdateDownloader(*this), - mState(LLUpdaterService::INITIAL) -{ -} - -LLUpdaterServiceImpl::~LLUpdaterServiceImpl() -{ - LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL; - LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); -} - -void LLUpdaterServiceImpl::initialize(const std::string& channel, - const std::string& version, - const std::string& platform, - const std::string& platform_version, - const unsigned char uniqueid[MD5HEX_STR_SIZE], - const bool& willing_to_test) -{ - if(mIsChecking || mIsDownloading) - { - throw LLUpdaterService::UsageError("LLUpdaterService::initialize call " - "while updater is running."); - } - - mChannel = channel; - mVersion = version; - mPlatform = platform; - mPlatformVersion = platform_version; - memcpy(mUniqueId, uniqueid, MD5HEX_STR_SIZE); - mWillingToTest = willing_to_test; - LL_DEBUGS("UpdaterService") - << "\n channel: " << mChannel - << "\n version: " << mVersion - << "\n uniqueid: " << mUniqueId - << "\n willing: " << ( mWillingToTest ? "testok" : "testno" ) - << LL_ENDL; -} - -void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds) -{ - mCheckPeriod = seconds; -} - -void LLUpdaterServiceImpl::setBandwidthLimit(U64 bytesPerSecond) -{ - mUpdateDownloader.setBandwidthLimit(bytesPerSecond); -} - -void LLUpdaterServiceImpl::startChecking(bool install_if_ready) -{ - if(mChannel.empty() || mVersion.empty()) - { - throw LLUpdaterService::UsageError("Set params before call to " - "LLUpdaterService::startCheck()."); - } - - mIsChecking = true; - - // Check to see if an install is ready. - bool has_install = checkForInstall(install_if_ready); - if(!has_install) - { - checkForResume(); // will set mIsDownloading to true if resuming - - if(!mIsDownloading) - { - setState(LLUpdaterService::CHECKING_FOR_UPDATE); - - // Checking can only occur during the mainloop. - // reset the timer to 0 so that the next mainloop event - // triggers a check; - restartTimer(0); - } - else - { - setState(LLUpdaterService::DOWNLOADING); - } - } -} - -void LLUpdaterServiceImpl::stopChecking() -{ - if(mIsChecking) - { - mIsChecking = false; - stopTimer(); - } - - if(mIsDownloading) - { - mUpdateDownloader.cancel(); - mIsDownloading = false; - } - - setState(LLUpdaterService::TERMINAL); -} - -bool LLUpdaterServiceImpl::forceCheck() -{ - if (!mIsDownloading && getState() != LLUpdaterService::CHECKING_FOR_UPDATE) - { - if (mIsChecking) - { - // Service is running, just reset the timer - if (mTimer.getStarted()) - { - mTimer.setTimerExpirySec(0); - setState(LLUpdaterService::CHECKING_FOR_UPDATE); - return true; - } - } - else if (!mChannel.empty() && !mVersion.empty()) - { - // one time check - bool has_install = checkForInstall(false); - if (!has_install) - { - std::string query_url = LLGridManager::getInstance()->getUpdateServiceURL(); - if (!query_url.empty()) - { - setState(LLUpdaterService::CHECKING_FOR_UPDATE); - mUpdateChecker.checkVersion(query_url, mChannel, mVersion, - mPlatform, mPlatformVersion, mUniqueId, - mWillingToTest); - return true; - } - else - { - LL_WARNS("UpdaterService") - << "No updater service defined for grid '" << LLGridManager::getInstance()->getGrid() << LL_ENDL; - } - } - } - } - return false; -} - -bool LLUpdaterServiceImpl::isChecking() -{ - return mIsChecking; -} - -LLUpdaterService::eUpdaterState LLUpdaterServiceImpl::getState() -{ - return mState; -} - -std::string LLUpdaterServiceImpl::updatedVersion(void) -{ - return mNewVersion; -} - -bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller) -{ - bool foundInstall = false; // return true if install is found. - - llifstream update_marker(update_marker_path().c_str(), - std::ios::in | std::ios::binary); - - if(update_marker.is_open()) - { - // Found an update info - now lets see if its valid. - LLSD update_info; - LLSDSerialize::fromXMLDocument(update_info, update_marker); - update_marker.close(); - - // Get the path to the installer file. - std::string path(update_info.get("path")); - std::string downloader_version(update_info["current_version"]); - if (downloader_version != ll_get_version()) - { - // This viewer is not the same version as the one that downloaded - // the update. Do not install this update. - LL_INFOS("UpdaterService") << "ignoring update downloaded by " - << "different viewer version " - << downloader_version << LL_ENDL; - if (! path.empty()) - { - LL_INFOS("UpdaterService") << "removing " << path << LL_ENDL; - LLFile::remove(path); - LLFile::remove(update_marker_path()); - } - - foundInstall = false; - } - else if (path.empty()) - { - LL_WARNS("UpdaterService") << "Marker file " << update_marker_path() - << " 'path' entry empty, ignoring" << LL_ENDL; - foundInstall = false; - } - else if (! LLFile::isfile(path)) - { - LL_WARNS("UpdaterService") << "Nonexistent installer " << path - << ", ignoring" << LL_ENDL; - foundInstall = false; - } - else - { - if(launchInstaller) - { - setState(LLUpdaterService::INSTALLING); - - LLFile::remove(update_marker_path()); - - int result = ll_install_update(install_script_path(), - path, - update_info["required"].asBoolean(), - install_script_mode()); - - if((result == 0) && mAppExitCallback) - { - mAppExitCallback(); - } - else if(result != 0) - { - LL_WARNS("UpdaterService") << "failed to run update install script" << LL_ENDL; - } - else - { - ; // No op. - } - } - - foundInstall = true; - } - } - return foundInstall; -} - -bool LLUpdaterServiceImpl::checkForResume() -{ - bool result = false; - std::string download_marker_path = mUpdateDownloader.downloadMarkerPath(); - if(LLFile::isfile(download_marker_path)) - { - llifstream download_marker_stream(download_marker_path.c_str(), - std::ios::in | std::ios::binary); - if(download_marker_stream.is_open()) - { - LLSD download_info; - LLSDSerialize::fromXMLDocument(download_info, download_marker_stream); - download_marker_stream.close(); - std::string downloader_version(download_info["current_version"]); - if (downloader_version == ll_get_version()) - { - mIsDownloading = true; - mNewVersion = download_info["update_version"].asString(); - mNewChannel = download_info["update_channel"].asString(); - mUpdateDownloader.resume(); - result = true; - } - else - { - // The viewer that started this download is not the same as this viewer; ignore. - LL_INFOS("UpdaterService") << "ignoring partial download " - << "from different viewer version " - << downloader_version << LL_ENDL; - std::string path = download_info["path"].asString(); - if(!path.empty()) - { - LL_INFOS("UpdaterService") << "removing " << path << LL_ENDL; - LLFile::remove(path); - } - LLFile::remove(download_marker_path); - } - } - } - return result; -} - -void LLUpdaterServiceImpl::error(std::string const & message) -{ - setState(LLUpdaterService::TEMPORARY_ERROR); - if(mIsChecking) - { - restartTimer(mCheckPeriod); - } -} - -// A successful response was received from the viewer version manager -void LLUpdaterServiceImpl::response(LLSD const & content) -{ - if(!content.asBoolean()) // an empty response means "no update" - { - LL_INFOS("UpdaterService") << "up to date" << LL_ENDL; - if(mIsChecking) - { - restartTimer(mCheckPeriod); - } - - setState(LLUpdaterService::UP_TO_DATE); - } - else if ( content.isMap() && content.has("url") ) - { - // there is an update available... - stopTimer(); - mNewChannel = content["channel"].asString(); - if (mNewChannel.empty()) - { - LL_INFOS("UpdaterService") << "no channel supplied, assuming current channel" << LL_ENDL; - mNewChannel = mChannel; - } - mNewVersion = content["version"].asString(); - mIsDownloading = true; - setState(LLUpdaterService::DOWNLOADING); - BOOL required = content["required"].asBoolean(); - LLURI url(content["url"].asString()); - std::string more_info = content["more_info"].asString(); - LL_DEBUGS("UpdaterService") - << "Starting download of " - << ( required ? "required" : "optional" ) << " update" - << " to channel '" << mNewChannel << "' version " << mNewVersion - << " more info '" << more_info << "'" - << LL_ENDL; - mUpdateDownloader.download(url, content["hash"].asString(), mNewChannel, mNewVersion, more_info, required); - } - else - { - LL_WARNS("UpdaterService") << "Invalid update query response ignored; retry in " - << mCheckPeriod << " seconds" << LL_ENDL; - setState(LLUpdaterService::TEMPORARY_ERROR); - if (mIsChecking) - { - restartTimer(mCheckPeriod); - } - } -} - -void LLUpdaterServiceImpl::downloadComplete(LLSD const & data) -{ - mIsDownloading = false; - - // Save out the download data to the SecondLifeUpdateReady - // marker file. - llofstream update_marker(update_marker_path().c_str()); - LLSDSerialize::toPrettyXML(data, update_marker); - - LLSD event; - event["pump"] = LLUpdaterService::pumpName(); - LLSD payload; - payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE); - payload["required"] = data["required"]; - payload["version"] = mNewVersion; - payload["channel"] = mNewChannel; - payload["info_url"] = data["info_url"]; - event["payload"] = payload; - LL_DEBUGS("UpdaterService") - << "Download complete " - << ( data["required"].asBoolean() ? "required" : "optional" ) - << " channel " << mNewChannel - << " version " << mNewVersion - << " info " << data["info_url"].asString() - << LL_ENDL; - - LLEventPumps::instance().obtain("mainlooprepeater").post(event); - - setState(LLUpdaterService::TERMINAL); -} - -void LLUpdaterServiceImpl::downloadError(std::string const & message) -{ - LL_INFOS("UpdaterService") << "Error downloading: " << message << LL_ENDL; - - mIsDownloading = false; - - // Restart the timer on error - if(mIsChecking) - { - restartTimer(mCheckPeriod); - } - - LLSD event; - event["pump"] = LLUpdaterService::pumpName(); - LLSD payload; - payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_ERROR); - payload["message"] = message; - event["payload"] = payload; - LLEventPumps::instance().obtain("mainlooprepeater").post(event); - - setState(LLUpdaterService::FAILURE); -} - -void LLUpdaterServiceImpl::restartTimer(unsigned int seconds) -{ - LL_INFOS("UpdaterService") << "will check for update again in " << - seconds << " seconds" << LL_ENDL; - mTimer.start(); - mTimer.setTimerExpirySec((F32)seconds); - LLEventPumps::instance().obtain("mainloop").listen( - sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1)); -} - -void LLUpdaterServiceImpl::setState(LLUpdaterService::eUpdaterState state) -{ - if(state != mState) - { - mState = state; - - LLSD event; - event["pump"] = LLUpdaterService::pumpName(); - LLSD payload; - payload["type"] = LLSD(LLUpdaterService::STATE_CHANGE); - payload["state"] = state; - event["payload"] = payload; - LLEventPumps::instance().obtain("mainlooprepeater").post(event); - - LL_INFOS("UpdaterService") << "setting state to " << state << LL_ENDL; - } - else - { - ; // State unchanged; noop. - } -} - -void LLUpdaterServiceImpl::stopTimer() -{ - mTimer.stop(); - LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); -} - -bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event) -{ - if(mTimer.getStarted() && mTimer.hasExpired()) - { - stopTimer(); - - // Check for failed install. - if(LLFile::isfile(ll_install_failed_marker_path())) - { - LL_DEBUGS("UpdaterService") << "found marker " << ll_install_failed_marker_path() << LL_ENDL; - int requiredValue = 0; - { - llifstream stream(ll_install_failed_marker_path().c_str()); - stream >> requiredValue; - if(stream.fail()) - { - requiredValue = 0; - } - } - // TODO: notify the user. - LL_WARNS("UpdaterService") << "last install attempt failed" << LL_ENDL;; - LLFile::remove(ll_install_failed_marker_path()); - - LLSD event; - event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR); - event["required"] = LLSD(requiredValue); - LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event); - - setState(LLUpdaterService::TERMINAL); - } - else - { - std::string query_url = LLGridManager::getInstance()->getUpdateServiceURL(); - if ( !query_url.empty() ) - { - mUpdateChecker.checkVersion(query_url, mChannel, mVersion, - mPlatform, mPlatformVersion, mUniqueId, - mWillingToTest); - setState(LLUpdaterService::CHECKING_FOR_UPDATE); - } - else - { - LL_WARNS("UpdaterService") - << "No updater service defined for grid '" << LLGridManager::getInstance()->getGrid() - << "' will check again in " << mCheckPeriod << " seconds" - << LL_ENDL; - // Because the grid can be changed after the viewer is started (when the first check takes place) - // but before the user logs in, the next check may be on a different grid, so set the retry timer - // even though this check did not happen. The default time is once an hour, and if we're not - // doing the check anyway the performance impact is completely insignificant. - restartTimer(mCheckPeriod); - } - } - } - else - { - // Keep on waiting... - } - - return false; -} - - -//----------------------------------------------------------------------- -// Facade interface - -std::string const & LLUpdaterService::pumpName(void) -{ - static std::string name("updater_service"); - return name; -} - -bool LLUpdaterService::updateReadyToInstall(void) -{ - return LLFile::isfile(update_marker_path()); -} - -LLUpdaterService::LLUpdaterService() -{ - if(gUpdater.expired()) - { - mImpl = - boost::shared_ptr(new LLUpdaterServiceImpl()); - gUpdater = mImpl; - } - else - { - mImpl = gUpdater.lock(); - } -} - -LLUpdaterService::~LLUpdaterService() -{ -} - -void LLUpdaterService::initialize(const std::string& channel, - const std::string& version, - const std::string& platform, - const std::string& platform_version, - const unsigned char uniqueid[MD5HEX_STR_SIZE], - const bool& willing_to_test -) -{ - mImpl->initialize(channel, version, platform, platform_version, uniqueid, willing_to_test); -} - -void LLUpdaterService::setCheckPeriod(unsigned int seconds) -{ - mImpl->setCheckPeriod(seconds); -} - -void LLUpdaterService::setBandwidthLimit(U64 bytesPerSecond) -{ - mImpl->setBandwidthLimit(bytesPerSecond); -} - -void LLUpdaterService::startChecking(bool install_if_ready) -{ - mImpl->startChecking(install_if_ready); -} - -void LLUpdaterService::stopChecking() -{ - mImpl->stopChecking(); -} - -bool LLUpdaterService::forceCheck() -{ - return mImpl->forceCheck(); -} - -bool LLUpdaterService::isChecking() -{ - return mImpl->isChecking(); -} - -LLUpdaterService::eUpdaterState LLUpdaterService::getState() -{ - return mImpl->getState(); -} - -void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) -{ - return mImpl->setAppExitCallback(aecb); -} - -std::string LLUpdaterService::updatedVersion(void) -{ - return mImpl->updatedVersion(); -} - - -std::string const & ll_get_version(void) { - static std::string version(""); - - if (version.empty()) { - std::ostringstream stream; - stream << LL_VIEWER_VERSION_MAJOR << "." - << LL_VIEWER_VERSION_MINOR << "." - << LL_VIEWER_VERSION_PATCH << "." - << LL_VIEWER_VERSION_BUILD; - version = stream.str(); - } - - return version; -} - diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h deleted file mode 100644 index 95bbe1695c..0000000000 --- a/indra/viewer_components/updater/llupdaterservice.h +++ /dev/null @@ -1,112 +0,0 @@ -/** - * @file llupdaterservice.h - * - * $LicenseInfo:firstyear=2010&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$ - */ - -#ifndef LL_UPDATERSERVICE_H -#define LL_UPDATERSERVICE_H - -#include -#include -#include "llhasheduniqueid.h" - -class LLUpdaterServiceImpl; - -class LLUpdaterService -{ -public: - class UsageError: public std::runtime_error - { - public: - UsageError(const std::string& msg) : std::runtime_error(msg) {} - }; - - // Name of the event pump through which update events will be delivered. - static std::string const & pumpName(void); - - // Returns true if an update has been completely downloaded and is now ready to install. - static bool updateReadyToInstall(void); - - // Type codes for events posted by this service. Stored the event's 'type' element. - enum eUpdaterEvent { - INVALID, - DOWNLOAD_COMPLETE, - DOWNLOAD_ERROR, - INSTALL_ERROR, - PROGRESS, - STATE_CHANGE - }; - - enum eUpdaterState { - INITIAL, - CHECKING_FOR_UPDATE, - TEMPORARY_ERROR, - DOWNLOADING, - INSTALLING, - UP_TO_DATE, - TERMINAL, - FAILURE - }; - - LLUpdaterService(); - ~LLUpdaterService(); - - void initialize(const std::string& channel, - const std::string& version, - const std::string& platform, - const std::string& platform_version, - const unsigned char uniqueid[MD5HEX_STR_SIZE], - const bool& willing_to_test - ); - - void setCheckPeriod(unsigned int seconds); - void setBandwidthLimit(U64 bytesPerSecond); - - void startChecking(bool install_if_ready = false); - void stopChecking(); - bool forceCheck(); - bool isChecking(); - eUpdaterState getState(); - - typedef boost::function app_exit_callback_t; - template - void setAppExitCallback(F const &callable) - { - app_exit_callback_t aecb = callable; - setImplAppExitCallback(aecb); - } - - // If an update is or has been downloaded, this method will return the - // version string for that update. An empty string will be returned - // otherwise. - std::string updatedVersion(void); - -private: - boost::shared_ptr mImpl; - void setImplAppExitCallback(app_exit_callback_t aecb); -}; - -// Returns the full version as a string. -std::string const & ll_get_version(void); - -#endif // LL_UPDATERSERVICE_H diff --git a/indra/viewer_components/updater/scripts/darwin/janitor.py b/indra/viewer_components/updater/scripts/darwin/janitor.py deleted file mode 100644 index cdf33df731..0000000000 --- a/indra/viewer_components/updater/scripts/darwin/janitor.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/python -"""\ -@file janitor.py -@author Nat Goodspeed -@date 2011-09-14 -@brief Janitor class to clean up arbitrary resources - -2013-01-04 cloned from vita because it's exactly what update_install.py needs. - -$LicenseInfo:firstyear=2011&license=viewerlgpl$ -Copyright (c) 2011, Linden Research, Inc. -$/LicenseInfo$ -""" - -import sys -import functools -import itertools - -class Janitor(object): - """ - Usage: - - Basic: - self.janitor = Janitor(sys.stdout) # report cleanup actions on stdout - ... - self.janitor.later(os.remove, some_temp_file) - self.janitor.later(os.remove, some_other_file) - ... - self.janitor.cleanup() # perform cleanup actions - - Context Manager: - with Janitor() as janitor: # clean up quietly - ... - janitor.later(shutil.rmtree, some_temp_directory) - ... - # exiting 'with' block performs cleanup - - Test Class: - class TestMySoftware(unittest.TestCase, Janitor): - def __init__(self): - Janitor.__init__(self) # quiet cleanup - ... - - def setUp(self): - ... - self.later(os.rename, saved_file, original_location) - ... - - def tearDown(self): - Janitor.tearDown(self) # calls cleanup() - ... - # Or, if you have no other tearDown() logic for - # TestMySoftware, you can omit the TestMySoftware.tearDown() - # def entirely and let it inherit Janitor.tearDown(). - """ - def __init__(self, stream=None): - """ - If you pass stream= (e.g.) sys.stdout or sys.stderr, Janitor will - report its cleanup operations as it performs them. If you don't, it - will perform them quietly -- unless one or more of the actions throws - an exception, in which case you'll get output on stderr. - """ - self.stream = stream - self.cleanups = [] - - def later(self, func, *args, **kwds): - """ - Pass the callable you want to call at cleanup() time, plus any - positional or keyword args you want to pass it. - """ - # Get a name string for 'func' - try: - # A free function has a __name__ - name = func.__name__ - except AttributeError: - try: - # A class object (even builtin objects like ints!) support - # __class__.__name__ - name = func.__class__.__name__ - except AttributeError: - # Shrug! Just use repr() to get a string describing this func. - name = repr(func) - # Construct a description of this operation in Python syntax from - # args, kwds. - desc = "%s(%s)" % \ - (name, ", ".join(itertools.chain((repr(a) for a in args), - ("%s=%r" % (k, v) for (k, v) in kwds.iteritems())))) - # Use functools.partial() to bind passed args and keywords to the - # passed func so we get a nullary callable that does what caller - # wants. - bound = functools.partial(func, *args, **kwds) - self.cleanups.append((desc, bound)) - - def cleanup(self): - """ - Perform all the actions saved with later() calls. - """ - # Typically one allocates resource A, then allocates resource B that - # depends on it. In such a scenario it's appropriate to delete B - # before A -- so perform cleanup actions in reverse order. (This is - # the same strategy used by atexit().) - while self.cleanups: - # Until our list is empty, pop the last pair. - desc, bound = self.cleanups.pop(-1) - - # If requested, report the action. - if self.stream is not None: - print >>self.stream, desc - - try: - # Call the bound callable - bound() - except Exception, err: - # This is cleanup. Report the problem but continue. - print >>(self.stream or sys.stderr), "Calling %s\nraised %s: %s" % \ - (desc, err.__class__.__name__, err) - - def tearDown(self): - """ - If a unittest.TestCase subclass (or a nose test class) adds Janitor as - one of its base classes, and has no other tearDown() logic, let it - inherit Janitor.tearDown(). - """ - self.cleanup() - - def __enter__(self): - return self - - def __exit__(self, type, value, tb): - # Perform cleanup no matter how we exit this 'with' statement - self.cleanup() - # Propagate any exception from the 'with' statement, don't swallow it - return False diff --git a/indra/viewer_components/updater/scripts/darwin/messageframe.py b/indra/viewer_components/updater/scripts/darwin/messageframe.py deleted file mode 100644 index 8f58848882..0000000000 --- a/indra/viewer_components/updater/scripts/darwin/messageframe.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python -"""\ -@file messageframe.py -@author Nat Goodspeed -@date 2013-01-03 -@brief Define MessageFrame class for popping up messages from a command-line - script. - -$LicenseInfo:firstyear=2013&license=viewerlgpl$ -Copyright (c) 2013, Linden Research, Inc. -$/LicenseInfo$ -""" - -import Tkinter as tk -import os - -# Tricky way to obtain the filename of the main script (default title string) -import __main__ - -# This class is intended for displaying messages from a command-line script. -# Getting the base class right took a bit of trial and error. -# If you derive from tk.Frame, the destroy() method doesn't actually close it. -# If you derive from tk.Toplevel, it pops up a separate Tk frame too. destroy() -# closes this frame, but not that one. -# Deriving from tk.Tk appears to do the right thing. -class MessageFrame(tk.Tk): - def __init__(self, text="", title=os.path.splitext(os.path.basename(__main__.__file__))[0], - width=320, height=120): - tk.Tk.__init__(self) - self.grid() - self.title(title) - self.var = tk.StringVar() - self.var.set(text) - self.msg = tk.Label(self, textvariable=self.var) - self.msg.grid() - # from http://stackoverflow.com/questions/3352918/how-to-center-a-window-on-the-screen-in-tkinter : - self.update_idletasks() - - # The constants below are to adjust for typical overhead from the - # frame borders. - xp = (self.winfo_screenwidth() / 2) - (width / 2) - 8 - yp = (self.winfo_screenheight() / 2) - (height / 2) - 20 - self.geometry('{0}x{1}+{2}+{3}'.format(width, height, xp, yp)) - self.update() - - def set(self, text): - self.var.set(text) - self.update() - -if __name__ == "__main__": - # When run as a script, just test the MessageFrame. - import sys - import time - - frame = MessageFrame("something in the way she moves....") - time.sleep(3) - frame.set("smaller") - time.sleep(3) - frame.set("""this has -several -lines""") - time.sleep(3) - frame.destroy() - print "Destroyed!" - sys.stdout.flush() - time.sleep(3) diff --git a/indra/viewer_components/updater/scripts/darwin/update_install.py b/indra/viewer_components/updater/scripts/darwin/update_install.py deleted file mode 100755 index 08f4f0ebb9..0000000000 --- a/indra/viewer_components/updater/scripts/darwin/update_install.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/usr/bin/python -"""\ -@file update_install.py -@author Nat Goodspeed -@date 2012-12-20 -@brief Update the containing Second Life application bundle to the version in - the specified disk image file. - - This Python implementation is derived from the previous mac-updater - application, a funky mix of C++, classic C and Objective-C. - -$LicenseInfo:firstyear=2012&license=viewerlgpl$ -Copyright (c) 2012, Linden Research, Inc. -$/LicenseInfo$ -""" - -import os -import sys -import cgitb -from contextlib import contextmanager -import errno -import glob -import plistlib -import re -import shutil -import subprocess -import tempfile -import time -from janitor import Janitor -from messageframe import MessageFrame -import Tkinter, tkMessageBox - -TITLE = "Second Life Viewer Updater" -# Magic bundle identifier used by all Second Life viewer bundles -BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer" -# Magic OS directory name that causes Cocoa viewer to crash on OS X 10.7.5 -# (see MAINT-3331) -STATE_DIR = os.path.join( - os.environ["HOME"], "Library", "Saved Application State", - BUNDLE_IDENTIFIER + ".savedState") - -# Global handle to the MessageFrame so we can update message -FRAME = None -# Global handle to logfile, once it's open -LOGF = None - -# **************************************************************************** -# Logging and messaging -# -# This script is normally run implicitly by the old viewer to update to the -# new viewer. Its UI consists of a MessageFrame and possibly a Tk error box. -# Log details to updater.log -- especially uncaught exceptions! -# **************************************************************************** -def log(message): - """write message only to LOGF (also called by status() and fail())""" - # If we don't even have LOGF open yet, at least write to Console log - logf = LOGF or sys.stderr - logf.writelines((time.strftime("%Y-%m-%dT%H:%M:%SZ ", time.gmtime()), message, '\n')) - logf.flush() - -def status(message): - """display and log normal progress message""" - log(message) - - global FRAME - if not FRAME: - FRAME = MessageFrame(message, TITLE) - else: - FRAME.set(message) - -def fail(message): - """log message, produce error box, then terminate with nonzero rc""" - log(message) - - # If we haven't yet called status() (we don't yet have a FRAME), perform a - # bit of trickery to bypass the spurious "main window" that Tkinter would - # otherwise pop up if the first call is showerror(). - if not FRAME: - root = Tkinter.Tk() - root.withdraw() - - # If we do have a LOGF available, mention it in the error box. - if LOGF: - message = "%s\n(Updater log in %s)" % (message, LOGF.name) - - # We explicitly specify the WARNING icon because, at least on the Tkinter - # bundled with the system-default Python 2.7 on Mac OS X 10.7.4, the - # ERROR, QUESTION and INFO icons are all the silly Tk rocket ship. At - # least WARNING has an exclamation in a yellow triangle, even though - # overlaid by a smaller image of the rocket ship. - tkMessageBox.showerror(TITLE, -"""An error occurred while updating Second Life: -%s -Please download the latest viewer from www.secondlife.com.""" % message, - icon=tkMessageBox.WARNING) - sys.exit(1) - -def exception(err): - """call fail() with an exception instance""" - fail("%s exception: %s" % (err.__class__.__name__, str(err))) - -def excepthook(type, value, traceback): - """ - Store this hook function into sys.excepthook until we have a logfile. - """ - # At least in older Python versions, it could be tricky to produce a - # string from 'type' and 'value'. For instance, an OSError exception would - # pass type=OSError and value=some_tuple. Empirically, this funky - # expression seems to work. - exception(type(*value)) -sys.excepthook = excepthook - -class ExceptHook(object): - """ - Store an instance of this class into sys.excepthook once we have a logfile - open. - """ - def __init__(self, logfile): - # There's no magic to the cgitb.enable() function -- it merely stores - # an instance of cgitb.Hook into sys.excepthook, passing enable()'s - # params into Hook.__init__(). Sadly, enable() doesn't forward all its - # params using (*args, **kwds) syntax -- another story. But the point - # is that all the goodness is in the cgitb.Hook class. Capture an - # instance. - self.hook = cgitb.Hook(file=logfile, format="text") - - def __call__(self, type, value, traceback): - # produce nice text traceback to logfile - self.hook(type, value, traceback) - # Now display an error box. - excepthook(type, value, traceback) - -def write_marker(markerfile, markertext): - log("writing %r to %s" % (markertext, markerfile)) - try: - with open(markerfile, "w") as markerf: - markerf.write(markertext) - except IOError, err: - # write_marker() is invoked by fail(), and fail() is invoked by other - # error-handling functions. If we try to invoke any of those, we'll - # get infinite recursion. If for any reason we can't write markerfile, - # try to log it -- otherwise shrug. - log("%s exception: %s" % (err.__class__.__name__, err)) - -# **************************************************************************** -# Utility -# **************************************************************************** -@contextmanager -def allow_errno(errn): - """ - Execute body of 'with' statement, accepting OSError with specific errno - 'errn'. Propagate any other exception, or an OSError with any other errno. - """ - try: - # run the body of the 'with' statement - yield - except OSError, err: - # unless errno == passed errn, re-raise the exception - if err.errno != errn: - raise - -# **************************************************************************** -# Main script logic -# **************************************************************************** -def main(dmgfile, markerfile, markertext): - # Should we fail, we're supposed to write 'markertext' to 'markerfile'. - # Wrap the fail() function so we do that. - global fail - oldfail = fail - def fail(message): - write_marker(markerfile, markertext) - oldfail(message) - - try: - # Starting with the Cocoafied viewer, we'll find viewer logs in - # ~/Library/Application Support/$CFBundleIdentifier/logs rather than in - # ~/Library/Application Support/SecondLife/logs as before. This could be - # obnoxious -- but we Happen To Know that markerfile is a path specified - # within the viewer's logs directory. Use that. - logsdir = os.path.dirname(markerfile) - - # Move the old updater.log file out of the way - logname = os.path.join(logsdir, "updater.log") - # Nonexistence is okay. Anything else, not so much. - with allow_errno(errno.ENOENT): - os.rename(logname, logname + ".old") - - # Open new updater.log. - global LOGF - LOGF = open(logname, "w") - - # Now that LOGF is in fact open for business, use it to log any further - # uncaught exceptions. - sys.excepthook = ExceptHook(LOGF) - - # log how this script was invoked - log(' '.join(repr(arg) for arg in sys.argv)) - - # prepare for other cleanup - with Janitor(LOGF) as janitor: - - # Under some circumstances, this script seems to be invoked with a - # nonexistent pathname. Check for that. - if not os.path.isfile(dmgfile): - fail(dmgfile + " has been deleted") - - # Try to derive the name of the running viewer app bundle from our - # own pathname. (Hopefully the old viewer won't copy this script - # to a temp dir before running!) - # Somewhat peculiarly, this script is currently packaged in - # Appname.app/Contents/MacOS with the viewer executable. But even - # if we decide to move it to Appname.app/Contents/Resources, we'll - # still find Appname.app two levels up from dirname(__file__). - appdir = os.path.abspath(os.path.join(os.path.dirname(__file__), - os.pardir, os.pardir)) - if not appdir.endswith(".app"): - # This can happen if either this script has been copied before - # being executed, or if it's in an unexpected place in the app - # bundle. - fail(appdir + " is not an application directory") - - # We need to install into appdir's parent directory -- can we? - installdir = os.path.abspath(os.path.join(appdir, os.pardir)) - if not os.access(installdir, os.W_OK): - fail("Can't modify " + installdir) - - # invent a temporary directory - tempdir = tempfile.mkdtemp() - log("created " + tempdir) - # clean it up when we leave - janitor.later(shutil.rmtree, tempdir) - - status("Mounting image...") - - mntdir = os.path.join(tempdir, "mnt") - log("mkdir " + mntdir) - os.mkdir(mntdir) - command = ["hdiutil", "attach", dmgfile, "-mountpoint", mntdir] - log(' '.join(command)) - # Instantiating subprocess.Popen launches a child process with the - # specified command line. stdout=PIPE passes a pipe to its stdout. - hdiutil = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=LOGF) - # Popen.communicate() reads that pipe until the child process - # terminates, returning (stdout, stderr) output. Select just stdout. - hdiutil_out = hdiutil.communicate()[0] - if hdiutil.returncode != 0: - fail("Couldn't mount " + dmgfile) - # hdiutil should report the devnode. Find that. - found = re.search(r"/dev/[^ ]*\b", hdiutil_out) - if not found: - # If we don't spot the devnode, log it and continue -- we only - # use it to detach it. Don't fail the whole update if we can't - # clean up properly. - log("Couldn't spot devnode in hdiutil output:\n" + hdiutil_out) - else: - # If we do spot the devnode, detach it when done. - janitor.later(subprocess.call, ["hdiutil", "detach", found.group(0)], - stdout=LOGF, stderr=subprocess.STDOUT) - - status("Searching for app bundle...") - - for candidate in glob.glob(os.path.join(mntdir, "*.app")): - log("Considering " + candidate) - try: - # By convention, a valid Mac app bundle has a - # Contents/Info.plist file containing at least - # CFBundleIdentifier. - CFBundleIdentifier = \ - plistlib.readPlist(os.path.join(candidate, "Contents", - "Info.plist"))["CFBundleIdentifier"] - except Exception, err: - # might be IOError, xml.parsers.expat.ExpatError, KeyError - # Any of these means it's not a valid app bundle. Instead - # of aborting, just skip this candidate and continue. - log("%s not a valid app bundle: %s: %s" % - (candidate, err.__class__.__name__, err)) - continue - - if CFBundleIdentifier == BUNDLE_IDENTIFIER: - break - - log("unrecognized CFBundleIdentifier: " + CFBundleIdentifier) - - else: - fail("Could not find Second Life viewer in " + dmgfile) - - # Here 'candidate' is the new viewer to install - log("Found " + candidate) - - # This logic was changed to make Mac updates behave more like - # Windows. Most of the time, the user doesn't change the name of - # the app bundle on our .dmg installer (e.g. "Second Life Beta - # Viewer.app"). Most of the time, the version manager directs a - # given viewer to update to another .dmg containing an app bundle - # with THE SAME name. In that case, everything behaves as usual. - - # The case that was changed is when the version manager offers (or - # mandates) an update to a .dmg containing a different app bundle - # name. This can happen, for instance, to a user who's downloaded - # a "project beta" viewer, and the project subsequently publishes - # a Release Candidate viewer. Say the project beta's app bundle - # name is something like "Second Life Beta Neato.app". Anyone - # launching that viewer will be offered an update to the - # corresponding Release Candidate viewer -- which will be built as - # a release viewer, with app bundle name "Second Life Viewer.app". - - # On Windows, we run the NSIS installer, which will update/replace - # the embedded install directory name, e.g. Second Life Viewer. - # But the Mac installer used to locate the app bundle name in the - # mounted .dmg file, then ignore that name, copying its contents - # into the app bundle directory of the running viewer. That is, - # we'd install the Release Candidate from the .dmg's "Second - # Life.app" into "/Applications/Second Life Beta Neato.app". This - # is undesired behavior. - - # Instead, having found the app bundle name on the mounted .dmg, - # we try to install that app bundle name into the parent directory - # of the running app bundle. - - # Are we installing a different app bundle name? If so, call it - # out, both in the log and for the user -- this is an odd case. - # (Presumably they've already agreed to a similar notification in - # the viewer before the viewer launched this script, but still.) - bundlename = os.path.basename(candidate) - if os.path.basename(appdir) == bundlename: - # updating the running app bundle, which we KNOW exists - appexists = True - else: - # installing some other app bundle - newapp = os.path.join(installdir, bundlename) - appexists = os.path.exists(newapp) - message = "Note: %s %s %s" % \ - (appdir, "updating" if appexists else "installing new", newapp) - status(message) - # okay, we have no further need of the name of the running app - # bundle. - appdir = newapp - - status("Preparing to copy files...") - - if appexists: - # move old viewer to temp location in case copy from .dmg fails - aside = os.path.join(tempdir, os.path.basename(appdir)) - log("mv %r %r" % (appdir, aside)) - # Use shutil.move() instead of os.rename(). move() first tries - # os.rename(), but falls back to shutil.copytree() if the dest is - # on a different filesystem. - shutil.move(appdir, aside) - - status("Copying files...") - - # shutil.copytree()'s target must not already exist. But we just - # moved appdir out of the way. - log("cp -p %r %r" % (candidate, appdir)) - try: - # The viewer app bundle does include internal symlinks. Keep them - # as symlinks. - shutil.copytree(candidate, appdir, symlinks=True) - except Exception, err: - # copy failed -- try to restore previous viewer before crumping - type, value, traceback = sys.exc_info() - if appexists: - log("exception response: mv %r %r" % (aside, appdir)) - shutil.move(aside, appdir) - # let our previously-set sys.excepthook handle this - raise type, value, traceback - - status("Cleaning up...") - - log("touch " + appdir) - os.utime(appdir, None) # set to current time - - # MAINT-3331: remove STATE_DIR. Empirically, this resolves a - # persistent, mysterious crash after updating our viewer on an OS - # X 10.7.5 system. - log("rm -rf '%s'" % STATE_DIR) - with allow_errno(errno.ENOENT): - shutil.rmtree(STATE_DIR) - - command = ["open", appdir] - log(' '.join(command)) - subprocess.check_call(command, stdout=LOGF, stderr=subprocess.STDOUT) - - # If all the above succeeded, delete the .dmg file. We don't do this - # as a janitor.later() operation because we only want to do it if we - # get this far successfully. Note that this is out of the scope of the - # Janitor: we must detach the .dmg before removing it! - log("rm " + dmgfile) - os.remove(dmgfile) - - except Exception, err: - # Because we carefully set sys.excepthook -- and even modify it to log - # the problem once we have our log file open -- you might think we - # could just let exceptions propagate. But when we do that, on - # exception in this block, we FIRST restore the no-side-effects fail() - # and THEN implicitly call sys.excepthook(), which calls the (no-side- - # effects) fail(). Explicitly call sys.excepthook() BEFORE restoring - # fail(). Only then do we get the enriched fail() behavior. - sys.excepthook(*sys.exc_info()) - - finally: - # When we leave main() -- for whatever reason -- reset fail() the way - # it was before, because the bound markerfile, markertext params - # passed to this main() call are no longer applicable. - fail = oldfail - -if __name__ == "__main__": - # We expect this script to be invoked with: - # - the pathname to the .dmg we intend to install; - # - the pathname to an update-error marker file to create on failure; - # - the content to write into the marker file. - main(*sys.argv[1:]) diff --git a/indra/viewer_components/updater/scripts/linux/update_install b/indra/viewer_components/updater/scripts/linux/update_install deleted file mode 100755 index 03089f192e..0000000000 --- a/indra/viewer_components/updater/scripts/linux/update_install +++ /dev/null @@ -1,220 +0,0 @@ -#! /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 "$*" "$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- & diff --git a/indra/viewer_components/updater/scripts/windows/update_install.bat b/indra/viewer_components/updater/scripts/windows/update_install.bat deleted file mode 100644 index 96687226a8..0000000000 --- a/indra/viewer_components/updater/scripts/windows/update_install.bat +++ /dev/null @@ -1,3 +0,0 @@ -start /WAIT %1 /SKIP_DIALOGS -IF ERRORLEVEL 1 ECHO %3 > %2 -DEL %1 diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp deleted file mode 100644 index 759e41ef4c..0000000000 --- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/** - * @file llupdaterservice_test.cpp - * @brief Tests of llupdaterservice.cpp. - * - * $LicenseInfo:firstyear=2010&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$ - */ - -// Precompiled header -#include "linden_common.h" -// associated header -#include "../llupdaterservice.h" -#include "../llupdatechecker.h" -#include "../llupdatedownloader.h" -#include "../llupdateinstaller.h" - -#include "../../../test/lltut.h" -//#define DEBUG_ON -#include "../../../test/debug.h" - -#include "llevents.h" -#include "lldir.h" - -/***************************************************************************** -* MOCK'd -*****************************************************************************/ -LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client) -{} -void LLUpdateChecker::checkVersion(std::string const & urlBase, - std::string const & channel, - std::string const & version, - std::string const & platform, - std::string const & platform_version, - unsigned char uniqueid[MD5HEX_STR_SIZE], - bool willing_to_test) -{} -LLUpdateDownloader::LLUpdateDownloader(Client & ) {} -void LLUpdateDownloader::download(LLURI const & , std::string const &, std::string const &, std::string const &, std::string const &, bool){} - -class LLDir_Mock : public LLDir -{ - void initAppDirs(const std::string &app_name, - const std::string& app_read_only_data_dir = "") {} - U32 countFilesInDir(const std::string &dirname, const std::string &mask) - { - return 0; - } - - void getRandomFileInDir(const std::string &dirname, - const std::string &mask, - std::string &fname) {} - std::string getCurPath() { return ""; } - bool fileExists(const std::string &filename) const { return false; } - std::string getLLPluginLauncher() { return ""; } - std::string getLLPluginFilename(std::string base_name) { return ""; } - -} gDirUtil; -LLDir* gDirUtilp = &gDirUtil; -LLDir::LLDir() {} -LLDir::~LLDir() {} -S32 LLDir::deleteFilesInDir(const std::string &dirname, - const std::string &mask) -{ return 0; } - -void LLDir::setChatLogsDir(const std::string &path){} -void LLDir::setPerAccountChatLogsDir(const std::string &username){} -void LLDir::setLindenUserDir(const std::string &username){} -void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language){} -std::string LLDir::getSkinFolder() const { return "default"; } -std::string LLDir::getLanguage() const { return "en"; } -bool LLDir::setCacheDir(const std::string &path){ return true; } -void LLDir::dumpCurrentDirectories() {} -void LLDir::updatePerAccountChatLogsDir() {} - -#include "llviewernetwork.h" -LLGridManager::LLGridManager() : - mGrid("test.grid.lindenlab.com"), - mIsInProductionGrid(false) -{ -} -std::string LLGridManager::getUpdateServiceURL() -{ - return "https://update.secondlife.com/update"; -} -LLGridManager::~LLGridManager() -{ -} - - -std::string LLDir::getExpandedFilename(ELLPath location, - const std::string &filename) const -{ - return ""; -} - -std::string LLUpdateDownloader::downloadMarkerPath(void) -{ - return ""; -} - -void LLUpdateDownloader::resume(void) {} -void LLUpdateDownloader::cancel(void) {} -void LLUpdateDownloader::setBandwidthLimit(U64 bytesPerSecond) {} - -int ll_install_update(std::string const &, std::string const &, bool, LLInstallScriptMode) -{ - return 0; -} - -std::string const & ll_install_failed_marker_path() -{ - static std::string wubba; - return wubba; -} - -/* -#pragma warning(disable: 4273) -llus_mock_llifstream::llus_mock_llifstream(const std::string& _Filename, - ios_base::openmode _Mode, - int _Prot) : - std::basic_istream >(NULL,true) -{} - -llus_mock_llifstream::~llus_mock_llifstream() {} -bool llus_mock_llifstream::is_open() const {return true;} -void llus_mock_llifstream::close() {} -*/ - -/***************************************************************************** -* TUT -*****************************************************************************/ -namespace tut -{ - struct llupdaterservice_data - { - llupdaterservice_data() : - pumps(LLEventPumps::instance()), - test_url("dummy_url"), - test_channel("dummy_channel"), - test_version("dummy_version") - {} - LLEventPumps& pumps; - std::string test_url; - std::string test_channel; - std::string test_version; - }; - - typedef test_group llupdaterservice_group; - typedef llupdaterservice_group::object llupdaterservice_object; - llupdaterservice_group llupdaterservicegrp("LLUpdaterService"); - - template<> template<> - void llupdaterservice_object::test<1>() - { - DEBUG; - LLUpdaterService updater; - bool got_usage_error = false; - try - { - updater.startChecking(); - } - catch(LLUpdaterService::UsageError) - { - got_usage_error = true; - } - ensure("Caught start before params", got_usage_error); - } - - template<> template<> - void llupdaterservice_object::test<2>() - { - DEBUG; - LLUpdaterService updater; - bool got_usage_error = false; - try - { - unsigned char id1[MD5HEX_STR_SIZE] = "11111111111111111111111111111111"; - updater.initialize(test_channel, test_version, "win", "1.2.3", id1, true); - updater.startChecking(); - unsigned char id2[MD5HEX_STR_SIZE] = "22222222222222222222222222222222"; - updater.initialize(test_channel, test_version, "win", "4.5.6", id2, true); - } - catch(LLUpdaterService::UsageError) - { - got_usage_error = true; - } - ensure("Caught params while running", got_usage_error); - } - - template<> template<> - void llupdaterservice_object::test<3>() - { - DEBUG; - LLUpdaterService updater; - unsigned char id[MD5HEX_STR_SIZE] = "33333333333333333333333333333333"; - updater.initialize(test_channel, test_version, "win", "7.8.9", id, true); - updater.startChecking(); - ensure(updater.isChecking()); - updater.stopChecking(); - ensure(!updater.isChecking()); - } -} -- cgit v1.2.3 From 01ee14c0fc71e2b84d1f489b9856f87b667ad521 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 19 Aug 2016 12:12:48 -0700 Subject: SL-323: put back mac addr hash --- indra/newview/CMakeLists.txt | 1 + indra/newview/lllogininstance.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 999165528a..7a0df8d207 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2300,6 +2300,7 @@ if (LL_TESTS) set_source_files_properties( lllogininstance.cpp + llhasheduniqueid.cpp PROPERTIES LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}" ) diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index b9e9c12aec..374efa2986 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -192,11 +192,19 @@ void LLLoginInstance::constructAuthParams(LLPointer user_credentia // (re)initialize the request params with creds. LLSD request_params = user_credential->getLoginParams(); + unsigned char hashed_unique_id_string[MD5HEX_STR_SIZE]; + if ( ! llHashedUniqueID(hashed_unique_id_string) ) + { + + LL_WARNS() << "Not providing a unique id in request params" << LL_ENDL; + + } request_params["start"] = construct_start_string(); request_params["agree_to_tos"] = false; // Always false here. Set true in request_params["read_critical"] = false; // handleTOSResponse request_params["last_exec_event"] = mLastExecEvent; request_params["last_exec_duration"] = mLastExecDuration; + request_params["mac"] = (char*)hashed_unique_id_string; request_params["version"] = LLVersionInfo::getVersion(); request_params["channel"] = LLVersionInfo::getChannel(); request_params["platform"] = mPlatform; -- cgit v1.2.3 From 2699ef356aae4f31246ab6ccb6613676a2e2cc0f Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 22 Aug 2016 09:36:00 -0700 Subject: SL-323: ripping out old code completed --- indra/newview/CMakeLists.txt | 1 - indra/newview/tests/lllogininstance_test.cpp | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7a0df8d207..999165528a 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -2300,7 +2300,6 @@ if (LL_TESTS) set_source_files_properties( lllogininstance.cpp - llhasheduniqueid.cpp PROPERTIES LL_TEST_ADDITIONAL_LIBRARIES "${BOOST_SYSTEM_LIBRARY}" ) diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index b43aaeaa33..aa05897c0b 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -186,6 +186,13 @@ void LLUIColorTable::saveUserSettings(void)const {} #include "../llversioninfo.h" const std::string &LLVersionInfo::getVersion() { return VIEWERLOGIN_VERSION; } const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; } +const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null + +bool llHashedUniqueID(unsigned char* id) +{ + memcpy( id, "66666666666666666666666666666666", MD5HEX_STR_SIZE ); + return true; +} //----------------------------------------------------------------------------- #include "../llappviewer.h" -- cgit v1.2.3 From 069c938eb6ebfd77f6a415207331c66f72270e5f Mon Sep 17 00:00:00 2001 From: "coyot@coyot-sager-PC" Date: Tue, 28 Feb 2017 22:35:01 +0000 Subject: pull from rev d22beb597e52ecbf1c98f25d4489ea0425eda4b0 of sl-321 --- indra/viewer_components/manager/InstallerError.py | 49 - .../manager/InstallerUserMessage.py | 316 ------ indra/viewer_components/manager/SL_Launcher | 199 ---- indra/viewer_components/manager/apply_update.py | 277 ----- indra/viewer_components/manager/download_update.py | 105 -- .../manager/tests/data/settings.xml | 1184 -------------------- indra/viewer_components/manager/tests/summary.json | 1 - .../manager/tests/test_InstallerError.py | 45 - .../tests/test_check_for_completed_download.py | 59 - .../tests/test_convert_version_file_style.py | 61 - .../manager/tests/test_get_filename.py | 66 -- .../manager/tests/test_get_log_file_handle.py | 68 -- .../manager/tests/test_get_parent_path.py | 83 -- .../manager/tests/test_get_platform_key.py | 44 - .../manager/tests/test_get_settings.py | 87 -- .../manager/tests/test_make_VVM_UUID_hash.py | 47 - .../manager/tests/test_make_download_dir.py | 47 - .../manager/tests/test_query_vvm.py | 73 -- .../manager/tests/test_silent_write.py | 49 - .../manager/tests/test_summary.py | 45 - .../manager/tests/with_setup_args.py | 68 -- indra/viewer_components/manager/update_manager.py | 547 --------- 22 files changed, 3520 deletions(-) delete mode 100644 indra/viewer_components/manager/InstallerError.py delete mode 100644 indra/viewer_components/manager/InstallerUserMessage.py delete mode 100755 indra/viewer_components/manager/SL_Launcher delete mode 100755 indra/viewer_components/manager/apply_update.py delete mode 100755 indra/viewer_components/manager/download_update.py delete mode 100644 indra/viewer_components/manager/tests/data/settings.xml delete mode 100644 indra/viewer_components/manager/tests/summary.json delete mode 100644 indra/viewer_components/manager/tests/test_InstallerError.py delete mode 100644 indra/viewer_components/manager/tests/test_check_for_completed_download.py delete mode 100644 indra/viewer_components/manager/tests/test_convert_version_file_style.py delete mode 100644 indra/viewer_components/manager/tests/test_get_filename.py delete mode 100644 indra/viewer_components/manager/tests/test_get_log_file_handle.py delete mode 100644 indra/viewer_components/manager/tests/test_get_parent_path.py delete mode 100644 indra/viewer_components/manager/tests/test_get_platform_key.py delete mode 100644 indra/viewer_components/manager/tests/test_get_settings.py delete mode 100644 indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py delete mode 100644 indra/viewer_components/manager/tests/test_make_download_dir.py delete mode 100644 indra/viewer_components/manager/tests/test_query_vvm.py delete mode 100644 indra/viewer_components/manager/tests/test_silent_write.py delete mode 100644 indra/viewer_components/manager/tests/test_summary.py delete mode 100644 indra/viewer_components/manager/tests/with_setup_args.py delete mode 100755 indra/viewer_components/manager/update_manager.py diff --git a/indra/viewer_components/manager/InstallerError.py b/indra/viewer_components/manager/InstallerError.py deleted file mode 100644 index 3b199ea231..0000000000 --- a/indra/viewer_components/manager/InstallerError.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file InstallerError.py -@author coyot -@date 2016-05-16 -@brief custom exception class for VMP - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -""" -usage: - ->>> import InstallerError ->>> import os ->>> try: -... os.mkdir('/tmp') -... except OSError, oe: -... ie = InstallerError.InstallerError(oe, "foo") -... raise ie - -Traceback (most recent call last): - File "", line 5, in -InstallerError.InstallerError: [Errno [Errno 17] File exists: '/tmp'] foo -""" - -class InstallerError(OSError): - def __init___(self, message): - Exception.__init__(self, message) diff --git a/indra/viewer_components/manager/InstallerUserMessage.py b/indra/viewer_components/manager/InstallerUserMessage.py deleted file mode 100644 index 8002399659..0000000000 --- a/indra/viewer_components/manager/InstallerUserMessage.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python - -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2013&license=viewerlgpl$ -# Copyright (c) 2013, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file InstallerUserMessage.py -@author coyot -@date 2016-05-16 -""" - -""" -This does everything the old updater/scripts/darwin/messageframe.py script did and some more bits. -Pushed up the manager directory to be multiplatform. -""" - -import os -import Queue -import threading -import time -import Tkinter as tk -import ttk - - -class InstallerUserMessage(tk.Tk): - #Goals for this class: - # Provide a uniform look and feel - # Provide an easy to use convenience class for other scripts - # Provide windows that automatically disappear when done (for differing notions of done) - # Provide a progress bar that isn't a glorified spinner, but based on download progress - #Non-goals: - # No claim to threadsafety is made or warranted. Your mileage may vary. - # Please consult a doctor if you experience thread pain. - - #Linden standard green color, from Marketing - linden_green = "#487A7B" - - def __init__(self, text="", title="", width=500, height=200, wraplength = 400, icon_name = None, icon_path = None): - tk.Tk.__init__(self) - self.grid() - self.title(title) - self.choice = tk.BooleanVar() - self.config(background = 'black') - # background="..." doesn't work on MacOS for radiobuttons or progress bars - # http://tinyurl.com/tkmacbuttons - ttk.Style().configure('Linden.TLabel', foreground=InstallerUserMessage.linden_green, background='black') - ttk.Style().configure('Linden.TButton', foreground=InstallerUserMessage.linden_green, background='black') - ttk.Style().configure("black.Horizontal.TProgressbar", foreground=InstallerUserMessage.linden_green, background='black') - - #This bit of configuration centers the window on the screen - # The constants below are to adjust for typical overhead from the - # frame borders. - self.xp = (self.winfo_screenwidth() / 2) - (width / 2) - 8 - self.yp = (self.winfo_screenheight() / 2) - (height / 2) - 20 - self.geometry('{0}x{1}+{2}+{3}'.format(width, height, self.xp, self.yp)) - - #find a few things - self.script_dir = os.path.dirname(os.path.realpath(__file__)) - self.contents_dir = os.path.dirname(self.script_dir) - self.icon_dir = os.path.abspath(os.path.join(self.contents_dir, 'Resources/vmp_icons')) - - #finds the icon and creates the widget - self.find_icon(icon_path, icon_name) - - #defines what to do when window is closed - self.protocol("WM_DELETE_WINDOW", self._delete_window) - - #callback id - self.id = -1 - - def _delete_window(self): - #capture and discard all destroy events before the choice is set - if not ((self.choice == None) or (self.choice == "")): - try: - #initialized value. If we have an outstanding callback, kill it before killing ourselves - if self.id != -1: - self.after_cancel(self.id) - self.destroy() - except: - #tk may try to destroy the same object twice - pass - - def set_colors(self, widget): - # #487A7B is "Linden Green" - widget.config(foreground = InstallerUserMessage.linden_green) - widget.config(background='black') - - def find_icon(self, icon_path = None, icon_name = None): - #we do this in each message, let's do it just once instead. - if not icon_path: - icon_path = self.icon_dir - icon_path = os.path.join(icon_path, icon_name) - print icon_path - if os.path.exists(icon_path): - icon = tk.PhotoImage(file=icon_path) - self.image_label = tk.Label(image = icon) - self.image_label.image = icon - else: - #default to text if image not available - self.image_label = tk.Label(text = "Second Life") - - def auto_resize(self, row_count = 0, column_count = 0, heavy_row = None, heavy_column = None): - #auto resize window to fit all rows and columns - #"heavy" gets extra weight - for x in range(column_count): - if x == heavy_column: - self.columnconfigure(x, weight = 2) - else: - self.columnconfigure(x, weight=1) - - for y in range(row_count): - if y == heavy_row: - self.rowconfigure(y, weight = 2) - else: - self.rowconfigure(x, weight=1) - - def basic_message(self, message): - #message: text to be displayed - #icon_path: directory holding the icon, defaults to icons subdir of script dir - #icon_name: filename of icon to be displayed - self.choice.set(True) - self.text_label = tk.Label(text = message) - self.set_colors(self.text_label) - self.set_colors(self.image_label) - #pad, direction and weight are all experimentally derived by retrying various values - self.image_label.grid(row = 1, column = 1, sticky = 'W') - self.text_label.grid(row = 1, column = 2, sticky = 'W', padx =100) - self.auto_resize(row_count = 1, column_count = 2) - self.mainloop() - - def binary_choice_message(self, message, true = 'Yes', false = 'No'): - #true: first option, returns True - #false: second option, returns False - #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice - #usage: - # frame = InstallerUserMessage.InstallerUserMessage( ... ) - # frame = frame.binary_choice_message( ... ) - # (wait for user to click) - # value = frame.choice.get() - - self.text_label = tk.Label(text = message) - #command registers the callback to the method named. We want the frame to go away once clicked. - #button 1 returns True/1, button 2 returns False/0 - self.button_one = ttk.Radiobutton(text = true, variable = self.choice, value = True, - command = self._delete_window, style = 'Linden.TButton') - self.button_two = ttk.Radiobutton(text = false, variable = self.choice, value = False, - command = self._delete_window, style = 'Linden.TButton') - self.set_colors(self.text_label) - self.set_colors(self.image_label) - #pad, direction and weight are all experimentally derived by retrying various values - self.image_label.grid(row = 1, column = 1, rowspan = 3, sticky = 'W') - self.text_label.grid(row = 1, column = 2, rowspan = 3) - self.button_one.grid(row = 1, column = 3, sticky = 'W', pady = 40) - self.button_two.grid(row = 2, column = 3, sticky = 'W', pady = 0) - self.auto_resize(row_count = 2, column_count = 3, heavy_column = 3) - #self.button_two.deselect() - self.update() - self.mainloop() - - def trinary_choice_message(self, message, one = 1, two = 2, three = 3): - #one: first option, returns 1 - #two: second option, returns 2 - #three: third option, returns 3 - #usage is kind of opaque and relies on this object persisting after the window destruction to pass back choice - #usage: - # frame = InstallerUserMessage.InstallerUserMessage( ... ) - # frame = frame.binary_choice_message( ... ) - # (wait for user to click) - # value = frame.choice.get() - - self.text_label = tk.Label(text = message) - #command registers the callback to the method named. We want the frame to go away once clicked. - self.button_one = ttk.Radiobutton(text = one, variable = self.choice, value = 1, - command = self._delete_window, style = 'Linden.TButton') - self.button_two = ttk.Radiobutton(text = two, variable = self.choice, value = 2, - command = self._delete_window, style = 'Linden.TButton') - self.button_three = ttk.Radiobutton(text = three, variable = self.choice, value = 3, - command = self._delete_window, style = 'Linden.TButton') - self.set_colors(self.text_label) - self.set_colors(self.image_label) - #pad, direction and weight are all experimentally derived by retrying various values - self.image_label.grid(row = 1, column = 1, rowspan = 4, sticky = 'W') - self.text_label.grid(row = 1, column = 2, rowspan = 4, padx = 5) - self.button_one.grid(row = 1, column = 3, sticky = 'W', pady = 5) - self.button_two.grid(row = 2, column = 3, sticky = 'W', pady = 5) - self.button_three.grid(row = 3, column = 3, sticky = 'W', pady = 5) - self.auto_resize(row_count = 3, column_count = 3, heavy_column = 3) - #self.button_two.deselect() - self.update() - self.mainloop() - - def progress_bar(self, message = None, size = 0, interval = 100, pb_queue = None): - #Best effort attempt at a real progress bar - # This is what Tk calls "determinate mode" rather than "indeterminate mode" - #size: denominator of percent complete - #interval: frequency, in ms, of how often to poll the file for progress - #pb_queue: queue object used to send updates to the bar - self.text_label = tk.Label(text = message) - self.set_colors(self.text_label) - self.set_colors(self.image_label) - self.image_label.grid(row = 1, column = 1, sticky = 'NSEW') - self.text_label.grid(row = 2, column = 1, sticky = 'NSEW') - self.progress = ttk.Progressbar(self, style = 'black.Horizontal.TProgressbar', orient="horizontal", length=100, mode="determinate") - self.progress.grid(row = 3, column = 1, sticky = 'NSEW') - self.value = 0 - self.progress["maximum"] = size - self.auto_resize(row_count = 1, column_count = 3) - self.queue = pb_queue - self.check_scheduler() - - def check_scheduler(self): - try: - if self.value < self.progress["maximum"]: - self.check_queue() - self.id = self.after(100, self.check_scheduler) - else: - #prevent a race condition between polling and the widget destruction - self.after_cancel(self.id) - except tk.TclError: - #we're already dead, just die quietly - pass - - def check_queue(self): - while self.queue.qsize(): - try: - msg = float(self.queue.get(0)) - #custom signal, time to tear down - if msg == -1: - self.choice.set(True) - self.destroy() - else: - self.progress.step(msg) - self.value = msg - except Queue.Empty: - #nothing to do - return - -class ThreadedClient(threading.Thread): - #for test only, not part of the functional code - def __init__(self, queue): - threading.Thread.__init__(self) - self.queue = queue - - def run(self): - for x in range(1, 90, 10): - time.sleep(1) - print "run " + str(x) - self.queue.put(10) - #tkk progress bars wrap at exactly 100 percent, look full at 99% - print "leftovers" - self.queue.put(9) - time.sleep(5) - # -1 is a custom signal to the progress_bar to quit - self.queue.put(-1) - -if __name__ == "__main__": - #When run as a script, just test the InstallUserMessage. - #To proceed with the test, close the first window, select on the second and fourth. The third will close by itself. - import sys - import tempfile - - def set_and_check(frame, value): - print "value: " + str(value) - frame.progress.step(value) - if frame.progress["value"] < frame.progress["maximum"]: - print "In Progress" - else: - print "Over now" - - #basic message window test - frame2 = InstallerUserMessage(text = "Something in the way she moves....", title = "Beatles Quotes for 100", icon_name="head-sl-logo.gif") - print frame2.contents_dir - print frame2.icon_dir - frame2.basic_message(message = "...attracts me like no other.") - print "Destroyed!" - sys.stdout.flush() - - #binary choice test. User destroys window when they select. - frame3 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") - frame3.binary_choice_message(message = "And all I have to do is think of her.", - true = "Don't want to leave her now", false = 'You know I believe and how') - print frame3.choice.get() - sys.stdout.flush() - - #progress bar - queue = Queue.Queue() - thread = ThreadedClient(queue) - thread.start() - print "thread started" - - frame4 = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 300", icon_name="head-sl-logo.gif") - frame4.progress_bar(message = "You're asking me will my love grow", size = 100, pb_queue = queue) - print "frame defined" - frame4.mainloop() - - #trinary choice test. User destroys window when they select. - frame3a = InstallerUserMessage(text = "Something in the way she knows....", title = "Beatles Quotes for 200", icon_name="head-sl-logo.gif") - frame3a.trinary_choice_message(message = "And all I have to do is think of her.", - one = "Don't want to leave her now", two = 'You know I believe and how', three = 'John is Dead') - print frame3a.choice.get() - sys.stdout.flush() diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher deleted file mode 100755 index 257543187c..0000000000 --- a/indra/viewer_components/manager/SL_Launcher +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/env python - -# $LicenseInfo:firstyear=2016&license=internal$ -# -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $/LicenseInfo$ -# Copyright (c) 2013, Linden Research, Inc. - -import os -import sys - -#module globals -log_file_handle = None -cwd = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, os.path.join(cwd, 'llbase')) - -import argparse -import collections -import InstallerUserMessage -#NOTA BENE: -# For POSIX platforms, llsd.py will be imported from the same directory. -# For Windows, llsd.py will be compiled into the executable by pyinstaller -try: - from llbase import llsd -except: - #if Windows, this is expected, if not, we're dead - if os.name == 'nt': - pass -import platform -import subprocess -import update_manager - - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - #this is so we don't have to keep trapping for an exception with a None handle - #oh and because it is best effort, it is also a holey_write ;) - if (log_file_handle): - #prepend text for easy grepping - timestamp = datetime.utcnow().strftime("%Y-%m-%D %H:%M:%S") - log_file_handle.write(timestamp + " LAUNCHER: " + text + "\n") - -def get_cmd_line(): - platform_name = platform.system() - #find the parent of the logs and user_settings directories - if (platform_name == 'Darwin'): - settings_file = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'Resources/app_settings/cmd_line.xml') - elif (platform_name == 'Linux'): - settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') - #using list format of join is important here because the Windows pathsep in a string escapes the next char - elif (platform_name == 'Windows'): - settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'app_settings/cmd_line.xml') - else: - settings_file = None - - try: - cmd_line = llsd.parse((open(settings_file)).read()) - except: - silent_write(log_file_handle, "Could not parse settings file %s" % settings_file) - cmd_line = None - - return cmd_line - -def get_settings(): - #return the settings file parsed into a dict - try: - settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) - settings = llsd.parse((open(settings_file)).read()) - except llsd.LLSDParseError as lpe: - silent_write(log_file_handle, "Could not parse settings file %s" % lpe) - return None - return settings - -def capture_vmp_args(arg_list = None, cmd_line = None): - #expected input format: arg_list = ['--set', 'foo', 'bar', '-X', '-Y', 'qux'] - #take a copy of the viewer parameters that are of interest to VMP. - #the regex for a parameter is -- {opt1} {opt2} - cli_overrides = {} - cmd_line = get_cmd_line() - - vmp_params = {'--channel':'channel', '--settings':'settings', '--update-service':'update-service', '--set':'set'} - #the settings set with --set. All such settings have only one argument. - vmp_setters = ('UpdaterMaximumBandwidth', 'UpdaterServiceCheckPeriod', 'UpdaterServicePath', 'UpdaterServiceSetting', 'UpdaterServiceURL', 'UpdaterWillingToTest') - - #Here turn the list into a queue, popping off the left as we go. Note that deque() makes a copy by value, not by reference - #Because of the complexity introduced by the uncertainty of how many options a parameter can take, this is far less complicated code than the more - #pythonic (x,y) = since we will sometimes have (x), sometimes (x,y) and sometimes (x,y,z) - #also, because the pop is destructive, we prevent ourselves from iterating back over list elements that iterator methods would peek ahead at - vmp_queue = collections.deque(arg_list) - while (len(vmp_queue)): - param = vmp_queue.popleft() - #if it is not one of ours, pop through args until we get to the next parameter - if param in vmp_params.keys(): - if param == '--set': - setting_name = vmp_queue.popleft() - setting_value = vmp_queue.popleft() - if setting_name in vmp_setters: - cli_overrides[vmp_params[param]] = (setting_name, setting_value) - else: - #find out how many args this parameter has - no_dashes = vmp_params[param] - count = cmd_line[no_dashes]['count'] - param_args = [] - if count > 0: - for argh in range(0,count): - param_args.append(vmp_queue.popleft()) - #the parameter name is the key, the (possibly empty) list of args is the value - cli_overrides[vmp_params[param]] = param_args - - #to prevent KeyErrors on missing keys, set the remainder to None - for key in vmp_params: - if key != '--set': - try: - cli_overrides[key] - except KeyError: - cli_overrides[key] = None - else: - cli_overrides["--set"] = {} - for arg in vmp_setters: - try: - cli_overrides[key][arg] - except KeyError: - cli_overrides[key][arg] = None - return cli_overrides - -#main entry point -#this and a few other update manager methods really should be refactored into a util lib -parent_dir = update_manager.get_parent_path(update_manager.get_platform_key()) -log_file_handle = update_manager.get_log_file_handle(parent_dir, 'launcher.log') - -executable_name = "" -if sys.platform.startswith('darwin'): - executable_name = "Second Life" -elif sys.platform.startswith("win") or sys.platform.startswith("cyg"): - if os.path.isfile(os.path.join(cwd,"SecondLifeViewer.exe")): - executable_name = "SecondLifeViewer.exe" - elif os.path.isfile(os.path.join(cwd,"SecondLifeTest.exe")): - executable_name = "SecondLifeTest.exe" - else: - sys.exit("Can't find Windows viewer binary") -elif sys.platform.startswith("linux"): - executable_name = "secondlife" -else: - #SL doesn't run on VMS or punch cards - sys.exit("Unsupported platform") - -#find the viewer to be lauched -viewer_binary = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])),executable_name) - -parser = argparse.ArgumentParser() -args = parser.parse_known_args(sys.argv) -#args[1] looks like ['./SL_Launcher', '--set', 'foo', 'bar', '-X', '-Y', 'qux'], dump the progname -args_list_to_pass = args[1][1:] -vmp_args = capture_vmp_args(args_list_to_pass) -#make a copy by value, not by reference -command = list(args_list_to_pass) - -(success, state, condition) = update_manager.update_manager(vmp_args) -# From update_manager: -# (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) -# (False, 'download', version): we failed to download the new version -# (False, 'apply', version): we failed to apply the new version -# (True, None, None): No update found -# (True, 'in place', True): update applied in place -# (True, 'in place', path_to_new_launcher): Update applied by a new install to a new location -# (True, 'background', True): background download initiated -#These boil down three cases: -# Success is False, then pop up a message and launch the current viewer -# No update, update succeeded in place in foreground, or background update started: silently launch the current viewer channel -# Updated succeed to a different channel, launch that viewer and exit -if not success: - msg = 'Update failed in the %s process. Please check logs. Viewer will launch starting momentarily.' % state - update_manager.after_frame(msg) - command.insert(0,viewer_binary) - viewer_process = subprocess.Popen(command) - #at the moment, we just exit here. Later, the crash monitor will be launched at this point -elif (success == True and - (state == None - or (state == 'background' and condition == True) - or (state == 'in_place' and condition == True))): - command.insert(0,viewer_binary) - viewer_process = subprocess.Popen(command) - #at the moment, we just exit here. Later, the crash monitor will be launched at this point -else: - #'condition' is the path to the new launcher. - command.insert(0,condition) - viewer_process = subprocess.Popen(command) - sys.exit(0) diff --git a/indra/viewer_components/manager/apply_update.py b/indra/viewer_components/manager/apply_update.py deleted file mode 100755 index 643e4ad2bc..0000000000 --- a/indra/viewer_components/manager/apply_update.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2016&license=viewerlgpl$ -# Copyright (c) 2016, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file apply_update.py -@author coyot -@date 2016-06-28 -""" - -""" -Applies an already downloaded update. -""" - -import argparse -import errno -import fnmatch -import InstallerUserMessage as IUM -import os -import os.path -import plistlib -import re -import shutil -import subprocess -import sys -import tarfile -import tempfile - -#Module level variables - -#fnmatch expressions -LNX_REGEX = '*' + '.bz2' -MAC_REGEX = '*' + '.dmg' -MAC_APP_REGEX = '*' + '.app' -WIN_REGEX = '*' + '.exe' - -#which install the updater is run from -INSTALL_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - -#whether the update is to the INSTALL_DIR or not. Most of the time this is the case. -IN_PLACE = True - -BUNDLE_IDENTIFIER = "com.secondlife.indra.viewer" -# Magic OS directory name that causes Cocoa viewer to crash on OS X 10.7.5 -# (see MAINT-3331) -STATE_DIR = os.path.join(os.environ["HOME"], "Library", "Saved Application State", - BUNDLE_IDENTIFIER + ".savedState") - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - if (log_file_handle): - #prepend text for easy grepping - log_file_handle.write("APPLY UPDATE: " + text + "\n") - -def get_filename(download_dir = None): - #given a directory that supposedly has the download, find the installable - #if you are on platform X and you give the updater a directory with an installable - #for platform Y, you are either trying something fancy or get what you deserve - #or both - for filename in os.listdir(download_dir): - if (fnmatch.fnmatch(filename, LNX_REGEX) - or fnmatch.fnmatch(filename, MAC_REGEX) - or fnmatch.fnmatch(filename, WIN_REGEX)): - return os.path.join(download_dir, filename) - #someone gave us a bad directory - return None - -def try_dismount(log_file_handle = None, installable = None, tmpdir = None): - #best effort cleanup try to dismount the dmg file if we have mounted one - #the French judge gave it a 5.8 - try: - #use the df command to find the device name - #Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on - #/dev/disk1s2 2047936 643280 1404656 32% 80408 175582 31% /private/tmp/mnt/Second Life Installer - command = ["df", os.path.join(tmpdir, "Second Life Installer")] - output = subprocess.check_output(command) - #first word of second line of df output is the device name - mnt_dev = output.split('\n')[1].split()[0] - #do the dismount - command = ["hdiutil", "detach", "-force", mnt_dev] - output = subprocess.check_output(command) - silent_write(log_file_handle, "hdiutil detach succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not detach dmg file %s. Error messages: %s" % (installable, e.message)) - -def apply_update(download_dir = None, platform_key = None, log_file_handle = None, in_place = True): - #for lnx and mac, returns path to newly installed viewer - #for win, return the name of the executable - #returns None on failure for all three - #throws an exception if it can't find an installable at all - - IN_PLACE = in_place - - installable = get_filename(download_dir) - if not installable: - #could not find the download - raise ValueError("Could not find installable in " + download_dir) - - #apply update using the platform specific tools - if platform_key == 'lnx': - installed = apply_linux_update(installable, log_file_handle) - elif platform_key == 'mac': - installed = apply_mac_update(installable, log_file_handle) - elif platform_key == 'win': - installed = apply_windows_update(installable, log_file_handle) - else: - #wtf? - raise ValueError("Unknown Platform: " + platform_key) - - if not installed: - #only mark the download as done when everything is done - done_filename = os.path.join(os.path.dirname(installable), ".done") - open(done_filename, 'w+').close() - - return installed - -def apply_linux_update(installable = None, log_file_handle = None): - try: - #untar to tmpdir - tmpdir = tempfile.mkdtemp() - tar = tarfile.open(name = installable, mode="r:bz2") - tar.extractall(path = tmpdir) - if IN_PLACE: - #rename current install dir - shutil.move(INSTALL_DIR,install_dir + ".bak") - #mv new to current - shutil.move(tmpdir, INSTALL_DIR) - #delete tarball on success - os.remove(installable) - except Exception, e: - silent_write(log_file_handle, "Update failed due to " + repr(e)) - return None - return INSTALL_DIR - -def apply_mac_update(installable = None, log_file_handle = None): - #INSTALL_DIR is something like /Applications/Second Life Viewer.app/Contents/MacOS, need to jump up two levels for the install base - install_base = os.path.dirname(INSTALL_DIR) - install_base = os.path.dirname(install_base) - - #verify dmg file - try: - output = subprocess.check_output(["hdiutil", "verify", installable], stderr=subprocess.STDOUT) - silent_write(log_file_handle, "dmg verification succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not verify dmg file %s. Error messages: %s" % (installable, e.message)) - return None - #make temp dir and mount & attach dmg - tmpdir = tempfile.mkdtemp() - try: - output = subprocess.check_output(["hdiutil", "attach", installable, "-mountroot", tmpdir]) - silent_write(log_file_handle, "hdiutil attach succeeded") - silent_write(log_file_handle, output) - except Exception, e: - silent_write(log_file_handle, "Could not attach dmg file %s. Error messages: %s" % (installable, e.message)) - return None - #verify plist - mounted_appdir = None - for top_dir in os.listdir(tmpdir): - for appdir in os.listdir(os.path.join(tmpdir, top_dir)): - appdir = os.path.join(os.path.join(tmpdir, top_dir), appdir) - if fnmatch.fnmatch(appdir, MAC_APP_REGEX): - try: - plist = os.path.join(appdir, "Contents", "Info.plist") - CFBundleIdentifier = plistlib.readPlist(plist)["CFBundleIdentifier"] - mounted_appdir = appdir - except: - #there is no except for this try because there are multiple directories that legimately don't have what we are looking for - pass - if not mounted_appdir: - silent_write(log_file_handle, "Could not find app bundle in dmg %s." % (installable,)) - return None - if CFBundleIdentifier != BUNDLE_IDENTIFIER: - silent_write(log_file_handle, "Wrong or null bundle identifier for dmg %s. Bundle identifier: %s" % (installable, CFBundleIdentifier)) - try_dismount(log_file_handle, installable, tmpdir) - return None - #do the install, finally - if IN_PLACE: - # swap out old install directory - bundlename = os.path.basename(mounted_appdir) - silent_write(log_file_handle, "Updating %s" % bundlename) - swapped_out = os.path.join(tmpdir, INSTALL_DIR.lstrip('/')) - shutil.move(install_base, swapped_out) - else: - silent_write(log_file_handle, "Installing %s" % install_base) - - # copy over the new bits - try: - shutil.copytree(mounted_appdir, install_base, symlinks=True) - retcode = 0 - except Exception, e: - # try to restore previous viewer - if os.path.exists(swapped_out): - silent_write(log_file_handle, "Install of %s failed, rolling back to previous viewer." % installable) - shutil.move(swapped_out, installed_test) - retcode = 1 - finally: - try_dismount(log_file_handle, installable, tmpdir) - if retcode: - return None - - #see MAINT-3331 - try: - shutil.rmtree(STATE_DIR) - except Exception, e: - #if we fail to delete something that isn't there, that's okay - if e[0] == errno.ENOENT: - pass - else: - raise e - - os.remove(installable) - return install_base - -def apply_windows_update(installable = None, log_file_handle = None): - #the windows install is just running the NSIS installer executable - #from VMP's perspective, it is a black box - try: - output = subprocess.check_output(installable, stderr=subprocess.STDOUT) - silent_write(log_file_handle, "Install of %s succeeded." % installable) - silent_write(log_file_handle, output) - except subprocess.CalledProcessError, cpe: - silent_write(log_file_handle, "%s failed with return code %s. Error messages: %s." % - (cpe.cmd, cpe.returncode, cpe.message)) - return None - #Due to the black box nature of the install, we have to derive the application path from the - #name of the installable. This is essentially reverse-engineering app_name()/app_name_oneword() - #in viewer_manifest.py - #the format of the filename is: Second_Life_{Project Name}_A-B-C-XXXXXX_i686_Setup.exe - #which deploys to C:\Program Files (x86)\SecondLifeProjectName\ - #so we want all but the last four phrases and tack on Viewer if there is no project - if re.search('Project', installable): - winstall = os.path.join("C:\\Program Files (x86)\\", "".join(installable.split("_")[:-3])) - else: - winstall = os.path.join("C:\\Program Files (x86)\\", "".join(installable.split("_")[:-3])+"Viewer") - return winstall - -def main(): - parser = argparse.ArgumentParser("Apply Downloaded Update") - parser.add_argument('--dir', dest = 'download_dir', help = 'directory to find installable', required = True) - parser.add_argument('--pkey', dest = 'platform_key', help =' OS: lnx|mac|win', required = True) - parser.add_argument('--in_place', action = 'store_false', help = 'This upgrade is for a different channel', default = True) - parser.add_argument('--log_file', dest = 'log_file', default = None, help = 'file to write messages to') - args = parser.parse_args() - - if args.log_file: - try: - f = open(args.log_file,'w+') - except: - print "%s could not be found or opened" % args.log_file - sys.exit(1) - - IN_PLACE = args.in_place - result = apply_update(download_dir = args.download_dir, platform_key = args.platform_key, log_file_handle = f) - if not result: - sys.exit("Update failed") - else: - sys.exit(0) - - -if __name__ == "__main__": - main() diff --git a/indra/viewer_components/manager/download_update.py b/indra/viewer_components/manager/download_update.py deleted file mode 100755 index a5e365fa37..0000000000 --- a/indra/viewer_components/manager/download_update.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2016, Linden Research, Inc. -# -# The following source code is PROPRIETARY AND CONFIDENTIAL. Use of -# this source code is governed by the Linden Lab Source Code Disclosure -# Agreement ("Agreement") previously entered between you and Linden -# Lab. By accessing, using, copying, modifying or distributing this -# software, you acknowledge that you have been informed of your -# obligations under the Agreement and agree to abide by those obligations. -# -# ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO -# WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, -# COMPLETENESS OR PERFORMANCE. -# $LicenseInfo:firstyear=2016&license=viewerlgpl$ -# Copyright (c) 2016, Linden Research, Inc. -# $/LicenseInfo$ - -""" -@file download_update.py -@author coyot -@date 2016-06-23 -""" - -""" -Performs a download of an update. In a separate script from update_manager so that we can -call it with subprocess. -""" - -import argparse -import InstallerUserMessage as IUM -import os -import Queue -import requests -import threading - -#module default -CHUNK_SIZE = 1024 - -def download_update(url = None, download_dir = None, size = None, progressbar = False, chunk_size = CHUNK_SIZE): - #url to download from - #download_dir to download to - #total size (for progressbar) of download - #progressbar: whether to display one (not used for background downloads) - #chunk_size is in bytes, amount to download at once - - queue = Queue.Queue() - if not os.path.exists(download_dir): - os.makedirs(download_dir) - #the url split provides the basename of the filename - filename = os.path.join(download_dir, url.split('/')[-1]) - req = requests.get(url, stream=True) - down_thread = ThreadedDownload(req, filename, chunk_size, progressbar, queue) - down_thread.start() - - if progressbar: - frame = IUM.InstallerUserMessage(title = "Second Life Downloader", icon_name="head-sl-logo.gif") - frame.progress_bar(message = "Download Progress", size = size, pb_queue = queue) - frame.mainloop() - else: - #nothing for the main thread to do - down_thread.join() - -class ThreadedDownload(threading.Thread): - def __init__(self, req, filename, chunk_size, progressbar, in_queue): - #req is a python request object - #target filename to download to - #chunk_size is in bytes, amount to download at once - #progressbar: whether to display one (not used for background downloads) - #in_queue mediates communication between this thread and the progressbar - threading.Thread.__init__(self) - self.req = req - self.filename = filename - self.chunk_size = int(chunk_size) - self.progressbar = progressbar - self.in_queue = in_queue - - def run(self): - with open(self.filename, 'wb') as fd: - #keep downloading until we run out of chunks, then download the last bit - for chunk in self.req.iter_content(self.chunk_size): - fd.write(chunk) - if self.progressbar: - #this will increment the progress bar by len(chunk)/size units - self.in_queue.put(len(chunk)) - #signal value saying to the progress bar that it is done and can destroy itself - #if len(chunk) is ever -1, we get to file a bug against Python - self.in_queue.put(-1) - -def main(): - #main method is for standalone use such as support and QA - #VMP will import this module and run download_update directly - parser = argparse.ArgumentParser("Download URI to directory") - parser.add_argument('--url', dest='url', help='URL of file to be downloaded', required=True) - parser.add_argument('--dir', dest='download_dir', help='directory to be downloaded to', required=True) - parser.add_argument('--pb', dest='progressbar', help='whether or not to show a progressbar', action="store_true", default = False) - parser.add_argument('--size', dest='size', help='size of download for progressbar') - parser.add_argument('--chunk_size', dest='chunk_size', default=CHUNK_SIZE, help='max portion size of download to be loaded in memory in bytes.') - args = parser.parse_args() - - download_update(url = args.url, download_dir = args.download_dir, size = args.size, progressbar = args.progressbar, chunk_size = args.chunk_size) - - -if __name__ == "__main__": - main() diff --git a/indra/viewer_components/manager/tests/data/settings.xml b/indra/viewer_components/manager/tests/data/settings.xml deleted file mode 100644 index 07e420dcb3..0000000000 --- a/indra/viewer_components/manager/tests/data/settings.xml +++ /dev/null @@ -1,1184 +0,0 @@ - - - AllowMultipleViewers - - Comment - Allow multiple viewers. - Type - Boolean - Value - 1 - - AllowTapTapHoldRun - - Comment - Tapping a direction key twice and holding it down makes avatar run - Type - Boolean - Value - 0 - - AppearanceCameraMovement - - Comment - When entering appearance editing mode, camera zooms in on currently selected portion of avatar - Type - Boolean - Value - 0 - - AudioLevelMedia - - Comment - Audio level of Quicktime movies - Type - F32 - Value - 0.699999988079071044921875 - - AudioLevelMic - - Comment - Audio level of microphone input - Type - F32 - Value - 0.1749999970197677612304688 - - AudioLevelMusic - - Comment - Audio level of streaming music - Type - F32 - Value - 0 - - AudioLevelSFX - - Comment - Audio level of in-world sound effects - Type - F32 - Value - 0.699999988079071044921875 - - AudioLevelVoice - - Comment - Audio level of voice chat - Type - F32 - Value - 1 - - AudioStreamingMedia - - Comment - Enable streaming - Type - Boolean - Value - 0 - - AvatarAxisDeadZone0 - - Comment - Avatar axis 0 dead zone. - Type - F32 - Value - 0.1000000014901161193847656 - - AvatarAxisDeadZone1 - - Comment - Avatar axis 1 dead zone. - Type - F32 - Value - 0.1000000014901161193847656 - - AvatarAxisDeadZone2 - - Comment - Avatar axis 2 dead zone. - Type - F32 - Value - 0.1000000014901161193847656 - - AvatarAxisDeadZone3 - - Comment - Avatar axis 3 dead zone. - Type - F32 - Value - 1 - - AvatarAxisDeadZone4 - - Comment - Avatar axis 4 dead zone. - Type - F32 - Value - 0.01999999955296516418457031 - - AvatarAxisDeadZone5 - - Comment - Avatar axis 5 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - AvatarAxisScale3 - - Comment - Avatar axis 3 scaler. - Type - F32 - Value - 0 - - AvatarAxisScale4 - - Comment - Avatar axis 4 scaler. - Type - F32 - Value - 2 - - AvatarAxisScale5 - - Comment - Avatar axis 5 scaler. - Type - F32 - Value - 2 - - AvatarFeathering - - Comment - Avatar feathering (less is softer) - Type - F32 - Value - 6 - - AvatarFileName - - Comment - Alternative avatar file name - Type - String - Value - avatar_lad_tentacles.xml - - BuildAxisDeadZone0 - - Comment - Build axis 0 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone1 - - Comment - Build axis 1 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone2 - - Comment - Build axis 2 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone3 - - Comment - Build axis 3 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone4 - - Comment - Build axis 4 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisDeadZone5 - - Comment - Build axis 5 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - BuildAxisScale0 - - Comment - Build axis 0 scaler. - Type - F32 - Value - 6 - - BuildAxisScale1 - - Comment - Build axis 1 scaler. - Type - F32 - Value - 6 - - BuildAxisScale2 - - Comment - Build axis 2 scaler. - Type - F32 - Value - 6 - - BuildAxisScale3 - - Comment - Build axis 3 scaler. - Type - F32 - Value - 6 - - BuildAxisScale4 - - Comment - Build axis 4 scaler. - Type - F32 - Value - 6 - - BuildAxisScale5 - - Comment - Build axis 5 scaler. - Type - F32 - Value - 6 - - BuildFeathering - - Comment - Build feathering (less is softer) - Type - F32 - Value - 12 - - BulkChangeEveryoneCopy - - Comment - Bulk changed objects can be copied by everyone - Type - Boolean - Value - 1 - - BulkChangeNextOwnerCopy - - Comment - Bulk changed objects can be copied by next owner - Type - Boolean - Value - 1 - - BulkChangeNextOwnerModify - - Comment - Bulk changed objects can be modified by next owner - Type - Boolean - Value - 1 - - BulkChangeShareWithGroup - - Comment - Bulk changed objects are shared with the currently active group - Type - Boolean - Value - 1 - - CacheValidateCounter - - Comment - Used to distribute cache validation - Type - U32 - Value - 122 - - CameraPosOnLogout - - Comment - Camera position when last logged out (global coordinates) - Type - Vector3D - Value - - 288290.4477181434631347656 - 275988.5277819633483886719 - 49.10921102762222290039062 - - - ClickToWalk - - Comment - Click in world to walk to location - Type - Boolean - Value - 0 - - ConversationSortOrder - - Comment - Specifies sort key for conversations - Type - U32 - Value - 0 - - CurrentGrid - - Comment - Currently Selected Grid - Type - String - Value - util.agni.lindenlab.com - - Cursor3D - - Comment - Treat Joystick values as absolute positions (not deltas). - Type - Boolean - Value - 0 - - FirstLoginThisInstall - - Comment - Specifies that you have not logged in with the viewer since you performed a clean install - Type - Boolean - Value - 0 - - FirstRunThisInstall - - Comment - Specifies that you have not run the viewer since you performed a clean install - Type - Boolean - Value - 0 - - FlycamAxisDeadZone0 - - Comment - Flycam axis 0 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone1 - - Comment - Flycam axis 1 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone2 - - Comment - Flycam axis 2 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone3 - - Comment - Flycam axis 3 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone4 - - Comment - Flycam axis 4 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone5 - - Comment - Flycam axis 5 dead zone. - Type - F32 - Value - 0.009999999776482582092285156 - - FlycamAxisDeadZone6 - - Comment - Flycam axis 6 dead zone. - Type - F32 - Value - 1 - - FlycamAxisScale0 - - Comment - Flycam axis 0 scaler. - Type - F32 - Value - 42 - - FlycamAxisScale1 - - Comment - Flycam axis 1 scaler. - Type - F32 - Value - 40 - - FlycamAxisScale2 - - Comment - Flycam axis 2 scaler. - Type - F32 - Value - 40 - - FlycamAxisScale3 - - Comment - Flycam axis 3 scaler. - Type - F32 - Value - 0 - - FlycamAxisScale4 - - Comment - Flycam axis 4 scaler. - Type - F32 - Value - 2 - - FlycamAxisScale5 - - Comment - Flycam axis 5 scaler. - Type - F32 - Value - 3 - - FlycamAxisScale6 - - Comment - Flycam axis 6 scaler. - Type - F32 - Value - 0 - - FlycamFeathering - - Comment - Flycam feathering (less is softer) - Type - F32 - Value - 5 - - FocusPosOnLogout - - Comment - Camera focus point when last logged out (global coordinates) - Type - Vector3D - Value - - 288287.8830481640761718154 - 275991.5973855691263452172 - 47.96361158013021963597566 - - - ForceShowGrid - - Comment - Always show grid dropdown on login screen - Type - Boolean - Value - 1 - - HttpProxyType - - Comment - Proxy type to use for HTTP operations - Type - String - Value - None - - JoystickInitialized - - Comment - Whether or not a joystick has been detected and initiailized. - Type - String - Value - UnknownDevice - - LSLFindCaseInsensitivity - - Comment - Use case insensitivity when searching in LSL editor - Type - Boolean - Value - 1 - - LastFeatureVersion - - Comment - [DO NOT MODIFY] Feature Table Version number for tracking rendering system changes - Type - S32 - Value - 37 - - LastGPUString - - Comment - [DO NOT MODIFY] previous GPU id string for tracking hardware changes - Type - String - Value - NVIDIA Corporation NVIDIA GeForce GT 750M OpenGL Engine - - LastPrefTab - - Comment - Last selected tab in preferences window - Type - S32 - Value - 1 - - LastRunVersion - - Comment - Version number of last instance of the viewer that you ran - Type - String - Value - Second Life Project Bento 5.0.0.315657 - - LocalCacheVersion - - Comment - Version number of cache - Type - S32 - Value - 7 - - LoginLocation - - Comment - Default Login location ('last', 'home') preference - Type - String - Value - home - - MapScale - - Comment - World map zoom level (pixels per region) - Type - F32 - Value - 256 - - MaxJointsPerMeshObject - - Comment - Maximum joints per rigged mesh object - Type - U32 - Value - 51 - - MediaEnablePopups - - Comment - If true, enable targeted links and javascript in media to open new media browser windows without a prompt. - Type - Boolean - Value - 1 - - MediaShowOnOthers - - Comment - Whether or not to show media on other avatars - Type - Boolean - Value - 1 - - MigrateCacheDirectory - - Comment - Check for old version of disk cache to migrate to current location - Type - Boolean - Value - 0 - - NavBarShowParcelProperties - - Comment - Show parcel property icons in navigation bar - Type - Boolean - Value - 0 - - NextLoginLocation - - Comment - Location to log into for this session - set from command line or the login panel, cleared following a successfull login. - Type - String - Value - home - - NotificationConferenceIMOptions - - Comment - - Specifies how the UI responds to Conference IM Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - none - - NotificationFriendIMOptions - - Comment - - Specifies how the UI responds to Friend IM Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - openconversations - - NotificationGroupChatOptions - - Comment - - Specifies how the UI responds to Group Chat Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - none - - NotificationNearbyChatOptions - - Comment - - Specifies how the UI responds to Nearby Chat Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - none - - NotificationNonFriendIMOptions - - Comment - - Specifies how the UI responds to Non Friend IM Notifications. - Allowed values: [openconversations,toast,flash,noaction] - - Type - String - Value - openconversations - - NumSessions - - Comment - Number of successful logins to Second Life - Type - S32 - Value - 1674 - - PlayTypingAnim - - Comment - Your avatar plays the typing animation whenever you type in the chat bar - Type - Boolean - Value - 0 - - PoolSizeExpCache - - Comment - Coroutine Pool size for ExpCache - Type - U32 - Value - 5 - - PreferredBrowserBehavior - - Comment - Use system browser for any links (0), use builtin browser for SL links and system one for others (1) or use builtin browser only (2). - Type - U32 - Value - 0 - - PreferredMaturity - - Comment - Setting for the user's preferred maturity level (consts in indra_constants.h) - Type - U32 - Value - 42 - - PresetGraphicActive - - Comment - Name of currently selected preference - Type - String - Value - Default - - ProbeHardwareOnStartup - - Comment - Query current hardware configuration on application startup - Type - Boolean - Value - 0 - - QAMode - - Comment - Enable Testing Features. - Type - Boolean - Value - 1 - - RenderAnisotropic - - Comment - Render textures using anisotropic filtering - Type - Boolean - Value - 1 - - RenderAvatarCloth - - Comment - Controls if avatars use wavy cloth - Type - Boolean - Value - 0 - - RenderAvatarLODFactor - - Comment - Controls level of detail of avatars (multiplier for current screen area when calculated level of detail) - Type - F32 - Value - 1 - - RenderFSAASamples - - Comment - Number of samples to use for FSAA (0 = no AA). - Type - U32 - Value - 2 - - RenderFarClip - - Comment - Distance of far clip plane from camera (meters) - Type - F32 - Value - 128 - - RenderMaxPartCount - - Comment - Maximum number of particles to display on screen - Type - S32 - Value - 2048 - - RenderQualityPerformance - - Comment - Which graphics settings you've chosen - Type - U32 - Value - 4 - - RenderReflectionDetail - - Comment - Detail of reflection render pass. - Type - S32 - Value - 0 - - RenderTerrainLODFactor - - Comment - Controls level of detail of terrain (multiplier for current screen area when calculated level of detail) - Type - F32 - Value - 2 - - RenderVBOEnable - - Comment - Use GL Vertex Buffer Objects - Type - Boolean - Value - 0 - - RenderVolumeLODFactor - - Comment - Controls level of detail of primitives (multiplier for current screen area when calculated level of detail) - Type - F32 - Value - 1.125 - - ShowAdvancedGraphicsSettings - - Comment - Show advanced graphics settings - Type - Boolean - Value - 1 - - ShowBanLines - - Comment - Show in-world ban/access borders - Type - Boolean - Value - 0 - - ShowStartLocation - - Comment - Display starting location menu on login screen - Type - Boolean - Value - 1 - - SkeletonFileName - - Comment - Alternative skeleton file name - Type - String - Value - avatar_skeleton_tentacles.xml - - SkyPresetName - - Comment - Sky preset to use. May be superseded by region settings or by a day cycle (see DayCycleName). - Type - String - Value - Sunset - - SnapshotConfigURL - - Comment - URL to fetch Snapshot Sharing configuration data from. - Type - String - Value - http://photos.apps.avatarsunited.com/viewer_config - - SnapshotFormat - - Comment - Save snapshots in this format (0 = PNG, 1 = JPEG, 2 = BMP) - Type - S32 - Value - 1 - - SnapshotQuality - - Comment - Quality setting of postcard JPEGs (0 = worst, 100 = best) - Type - S32 - Value - 100 - - SpellCheck - - Comment - Enable spellchecking on line and text editors - Type - Boolean - Value - 0 - - SpellCheckDictionary - - Comment - Current primary and secondary dictionaries used for spell checking - Type - String - Value - English (United States) - - TextureMemory - - Comment - Amount of memory to use for textures in MB (0 = autodetect) - Type - S32 - Value - 256 - - UseDayCycle - - Comment - Whether to use use a day cycle or a fixed sky. - Type - Boolean - Value - 0 - - UseDebugMenus - - Comment - Turns on "Debug" menu - Type - Boolean - Value - 1 - - UseEnvironmentFromRegion - - Comment - Choose whether to use the region's environment settings, or override them with the local settings. - Type - Boolean - Value - 0 - - VFSOldSize - - Comment - [DO NOT MODIFY] Controls resizing of local file cache - Type - U32 - Value - 102 - - VFSSalt - - Comment - [DO NOT MODIFY] Controls local file caching behavior - Type - U32 - Value - 260093998 - - VersionChannelName - - Comment - Version information generated by running the viewer - Type - String - Value - Second Life Release - - VertexShaderEnable - - Comment - Enable/disable all GLSL shaders (debug) - Type - Boolean - Value - 1 - - VoiceInputAudioDevice - - Comment - Audio input device to use for voice - Type - String - Value - C-Media USB Audio Device - - VoiceOutputAudioDevice - - Comment - Audio output device to use for voice - Type - String - Value - C-Media USB Audio Device - - WLSkyDetail - - Comment - Controls vertex detail on the WindLight sky. Lower numbers will give better performance and uglier skies. - Type - U32 - Value - 48 - - WebProfileFloaterRect - - Comment - Web profile floater dimensions - Type - Rect - Value - - 1189 - 957 - 1674 - 277 - - - WindowHeight - - Comment - SL viewer window height - Type - U32 - Value - 1000 - - WindowWidth - - Comment - SL viewer window width - Type - U32 - Value - 1626 - - WindowX - - Comment - X coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) - Type - S32 - Value - 50 - - WindowY - - Comment - Y coordinate of upper left corner of SL viewer window, relative to upper left corner of primary display (pixels) - Type - S32 - Value - 50 - - - diff --git a/indra/viewer_components/manager/tests/summary.json b/indra/viewer_components/manager/tests/summary.json deleted file mode 100644 index b78859d427..0000000000 --- a/indra/viewer_components/manager/tests/summary.json +++ /dev/null @@ -1 +0,0 @@ -{"Type":"viewer","Version":"4.0.5.315117","Channel":"Second Life Release"} diff --git a/indra/viewer_components/manager/tests/test_InstallerError.py b/indra/viewer_components/manager/tests/test_InstallerError.py deleted file mode 100644 index 9139447932..0000000000 --- a/indra/viewer_components/manager/tests/test_InstallerError.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file test_InstallerError.py -@author coyot -@date 2016-06-01 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - - -from nose.tools import assert_equal - -import InstallerError -import os - -def test_InstallerError(): - try: - #try to make our own homedir, this will fail on all three platforms - homedir = os.path.abspath(os.path.expanduser('~')) - os.mkdir(homedir) - except OSError, oe: - ie = InstallerError.InstallerError(oe, "Installer failed to create a homedir that already exists.") - - assert_equal( str(ie), - "[Errno [Errno 17] File exists: '%s'] Installer failed to create a homedir that already exists." % homedir) diff --git a/indra/viewer_components/manager/tests/test_check_for_completed_download.py b/indra/viewer_components/manager/tests/test_check_for_completed_download.py deleted file mode 100644 index bcaaef4c3f..0000000000 --- a/indra/viewer_components/manager/tests/test_check_for_completed_download.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_check_for_completed_download.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import tempfile -import update_manager -import with_setup_args - -def check_for_completed_download_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'test1') - tmpdir2 = tempfile.mkdtemp(prefix = 'test2') - tempfile.mkstemp(suffix = '.done', dir = tmpdir1) - - return [tmpdir1,tmpdir2], {} - -def check_for_completed_download_teardown(tmpdir1,tmpdir2): - shutil.rmtree(tmpdir1, ignore_errors = True) - shutil.rmtree(tmpdir2, ignore_errors = True) - -@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown) -def test_completed_check_for_completed_download(tmpdir1,tmpdir2): - assert_equal(update_manager.check_for_completed_download(tmpdir1), 'done'), "Failed to find completion marker" - -@with_setup_args.with_setup_args(check_for_completed_download_setup, check_for_completed_download_teardown) -def test_incomplete_check_for_completed_download(tmpdir1,tmpdir2): - #should return False - incomplete = not update_manager.check_for_completed_download(tmpdir2) - assert incomplete, "False positive, should not mark complete without a marker" diff --git a/indra/viewer_components/manager/tests/test_convert_version_file_style.py b/indra/viewer_components/manager/tests/test_convert_version_file_style.py deleted file mode 100644 index 244c22c458..0000000000 --- a/indra/viewer_components/manager/tests/test_convert_version_file_style.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file test_convert_version_file_style.py -@author coyot -@date 2016-06-01 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - - -from nose.tools import assert_equal - -import update_manager - -def test_normal_form(): - version = '1.2.3.456789' - golden = '1_2_3_456789' - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) - -def test_short_form(): - version = '1.23' - golden = '1_23' - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) - -def test_idempotent(): - version = '123' - golden = '123' - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) - -def test_none(): - version = None - golden = None - converted = update_manager.convert_version_file_style(version) - - assert_equal(golden, converted) diff --git a/indra/viewer_components/manager/tests/test_get_filename.py b/indra/viewer_components/manager/tests/test_get_filename.py deleted file mode 100644 index efb6349374..0000000000 --- a/indra/viewer_components/manager/tests/test_get_filename.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_get_filename.py -@author coyot -@date 2016-06-30 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import tempfile -import apply_update -import with_setup_args - -def get_filename_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'lnx') - tmpdir2 = tempfile.mkdtemp(prefix = 'mac') - tmpdir3 = tempfile.mkdtemp(prefix = 'win') - tmpdir4 = tempfile.mkdtemp(prefix = 'bad') - tempfile.mkstemp(suffix = '.bz2', dir = tmpdir1) - tempfile.mkstemp(suffix = '.dmg', dir = tmpdir2) - tempfile.mkstemp(suffix = '.exe', dir = tmpdir3) - - return [tmpdir1,tmpdir2, tmpdir3, tmpdir4], {} - -def get_filename_teardown(tmpdir1,tmpdir2, tmpdir3, tmpdir4): - shutil.rmtree(tmpdir1, ignore_errors = True) - shutil.rmtree(tmpdir2, ignore_errors = True) - shutil.rmtree(tmpdir3, ignore_errors = True) - shutil.rmtree(tmpdir4, ignore_errors = True) - -@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) -def test_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): - assert_is_not_none(apply_update.get_filename(tmpdir1)), "Failed to find installable" - assert_is_not_none(apply_update.get_filename(tmpdir2)), "Failed to find installable" - assert_is_not_none(apply_update.get_filename(tmpdir3)), "Failed to find installable" - -@with_setup_args.with_setup_args(get_filename_setup, get_filename_teardown) -def test_missing_get_filename(tmpdir1, tmpdir2, tmpdir3, tmpdir4): - not_found = not apply_update.get_filename(tmpdir4) - assert not_found, "False positive, should not find an installable in an empty dir" diff --git a/indra/viewer_components/manager/tests/test_get_log_file_handle.py b/indra/viewer_components/manager/tests/test_get_log_file_handle.py deleted file mode 100644 index 5ea821acf7..0000000000 --- a/indra/viewer_components/manager/tests/test_get_log_file_handle.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python - -""" -@file test_get_log_file_handle.py -@author coyot -@date 2016-06-08 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * - -import os -import shutil -import tempfile -import update_manager -import with_setup_args - -def get_log_file_handle_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'test1') - tmpdir2 = tempfile.mkdtemp(prefix = 'test2') - log_file_path = os.path.abspath(os.path.join(tmpdir1,"update_manager.log")) - #not using tempfile because we want a particular filename - open(log_file_path, 'w+').close - - return [tmpdir1,tmpdir2,log_file_path], {} - -def get_log_file_handle_teardown(tmpdir1,tmpdir2,log_file_path): - shutil.rmtree(tmpdir1, ignore_errors = True) - shutil.rmtree(tmpdir2, ignore_errors = True) - -@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown) -def test_existing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): - handle = update_manager.get_log_file_handle(tmpdir1) - if not handle: - print "Failed to find existing log file" - assert False - elif not os.path.exists(os.path.abspath(log_file_path+".old")): - print "Failed to rotate update manager log" - assert False - assert True - -@with_setup_args.with_setup_args(get_log_file_handle_setup, get_log_file_handle_teardown) -def test_missing_get_log_file_handle(tmpdir1,tmpdir2,log_file_path): - handle = update_manager.get_log_file_handle(tmpdir2) - if not os.path.exists(log_file_path): - print "Failed to touch new log file" - assert False - assert True diff --git a/indra/viewer_components/manager/tests/test_get_parent_path.py b/indra/viewer_components/manager/tests/test_get_parent_path.py deleted file mode 100644 index 6e4b9dfaff..0000000000 --- a/indra/viewer_components/manager/tests/test_get_parent_path.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_get_parent_path.py -@author coyot -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import update_manager -import with_setup_args - -def get_parent_path_setup(): - key = update_manager.get_platform_key() - try: - if key == 'mac': - settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife') - elif key == 'lnx': - settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife') - elif key == 'win': - settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife') - else: - raise Exception("Invalid Platform Key") - - #preserve existing settings dir if any - if os.path.exists(settings_dir): - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - shutil.rmtree(old_dir, ignore_errors = True) - os.rename(settings_dir, old_dir) - os.makedirs(settings_dir) - except Exception, e: - print "get_parent_path_setup failed due to: %s" % str(e) - assert False - - #this is we don't have to rediscover settings_dir for test and teardown - return [settings_dir], {} - -def get_parent_path_teardown(settings_dir): - try: - shutil.rmtree(settings_dir, ignore_errors = True) - #restore previous settings dir if any - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - os.rename(old_dir, settings_dir) - except: - #cleanup is best effort - pass - -@with_setup_args.with_setup_args(get_parent_path_setup, get_parent_path_teardown) -def test_get_parent_path(settings_dir): - key = update_manager.get_platform_key() - got_settings_dir = update_manager.get_parent_path(key) - - assert settings_dir, "test_get_parent_path failed to obtain parent path" - - assert_equal(settings_dir, got_settings_dir) diff --git a/indra/viewer_components/manager/tests/test_get_platform_key.py b/indra/viewer_components/manager/tests/test_get_platform_key.py deleted file mode 100644 index eeaca1dff7..0000000000 --- a/indra/viewer_components/manager/tests/test_get_platform_key.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python - -""" -@file test_get_platform_key.py -@author coyot -@date 2016-06-01 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import assert_equal - -import platform -import update_manager - -def test_get_platform_key(): - key = update_manager.get_platform_key() - if key == 'mac': - assert_equal(platform.system(),'Darwin') - elif key == 'lnx': - assert_equal(platform.system(),'Linux') - elif key == 'win': - assert_equal(platform.system(),'Windows') - else: - assert_equal(key, None) diff --git a/indra/viewer_components/manager/tests/test_get_settings.py b/indra/viewer_components/manager/tests/test_get_settings.py deleted file mode 100644 index 7efcf62673..0000000000 --- a/indra/viewer_components/manager/tests/test_get_settings.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_get_settings.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * -from nose import with_setup - -import os -import shutil -import update_manager -import with_setup_args - -def get_settings_setup(): - try: - key = update_manager.get_platform_key() - settings_dir = os.path.join(update_manager.get_parent_path(key), "user_settings") - print settings_dir - - #preserve existing settings dir if any - if os.path.exists(settings_dir): - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - shutil.rmtree(old_dir, ignore_errors = True) - os.rename(settings_dir, old_dir) - os.makedirs(settings_dir) - - #the data subdir of the tests dir that this script is in - data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "data") - #the test settings file - settings_file = os.path.join(data_dir, "settings.xml") - shutil.copyfile(settings_file, os.path.join(settings_dir, "settings.xml")) - - except Exception, e: - print "get_settings_setup failed due to: %s" % str(e) - assert False - - #this is we don't have to rediscover settings_dir for test and teardown - return [settings_dir], {} - -def get_settings_teardown(settings_dir): - try: - shutil.rmtree(settings_dir, ignore_errors = True) - #restore previous settings dir if any - old_dir = settings_dir + ".tmp" - if os.path.exists(old_dir): - os.rename(old_dir, settings_dir) - except: - #cleanup is best effort - pass - -@with_setup_args.with_setup_args(get_settings_setup, get_settings_teardown) -def test_get_settings(settings_dir): - key = update_manager.get_platform_key() - parent = update_manager.get_parent_path(key) - log_file = update_manager.get_log_file_handle(parent) - got_settings = update_manager.get_settings(log_file, parent) - - assert got_settings, "test_get_settings failed to find a settings.xml file" - - #test one key just to make sure it parsed - assert_equal(got_settings['CurrentGrid']['Value'], 'util.agni.lindenlab.com') diff --git a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py b/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py deleted file mode 100644 index 5939e5806a..0000000000 --- a/indra/viewer_components/manager/tests/test_make_VVM_UUID_hash.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_make_VVM_UUID_hash.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * - -import update_manager - -def test_make_VVM_UUID_hash(): - #because the method returns different results on different hosts - #it is not easy to unit test it reliably. - #About the best we can do is check for the exception from subprocess - key = update_manager.get_platform_key() - try: - UUID_hash = update_manager.make_VVM_UUID_hash(key) - except Exception, e: - print "Test failed due to: %s" % str(e) - assert False - - #make_UUID_hash returned None - assert UUID_hash, "make_UUID_hash failed to make a hash." diff --git a/indra/viewer_components/manager/tests/test_make_download_dir.py b/indra/viewer_components/manager/tests/test_make_download_dir.py deleted file mode 100644 index e3f9cb83fe..0000000000 --- a/indra/viewer_components/manager/tests/test_make_download_dir.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python - -""" -@file test_make_download_dir.py -@author coyot -@date 2016-06-03 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * - -import update_manager - -def test_make_download_dir(): - key = update_manager.get_platform_key() - path = update_manager.get_parent_path(key) - version = '1.2.3.456789' - try: - download_dir = update_manager.make_download_dir(path, version) - except OSError, e: - print "make_download_dir failed to eat OSError %s" % str(e) - assert False - except Exception, e: - print "make_download_dir raised an unexpected exception %s" % str(e) - assert False - - assert download_dir, "make_download_dir returned None for path %s and version %s" % (path, version) diff --git a/indra/viewer_components/manager/tests/test_query_vvm.py b/indra/viewer_components/manager/tests/test_query_vvm.py deleted file mode 100644 index 79c8ede480..0000000000 --- a/indra/viewer_components/manager/tests/test_query_vvm.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_query_vvm.py -@author coyot -@date 2016-06-08 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * - -import os -import re -import shutil -import tempfile -import update_manager -import with_setup_args - -def query_vvm_setup(): - tmpdir1 = tempfile.mkdtemp(prefix = 'test1') - handle = update_manager.get_log_file_handle(tmpdir1) - - return [tmpdir1,handle], {} - -def query_vvm_teardown(tmpdir1, handle): - shutil.rmtree(tmpdir1, ignore_errors = True) - -@with_setup_args.with_setup_args(query_vvm_setup, query_vvm_teardown) -def test_query_vvm(tmpdir1, handle): - key = update_manager.get_platform_key() - parent = update_manager.get_parent_path(key) - settings = update_manager.get_settings(handle, parent) - launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__)))) - summary = update_manager.get_summary(key, launcher_path) - - #for unit testing purposes, just testing a value from results. If no update, then None and it falls through - #for formal QA see: - # https://docs.google.com/document/d/1WNjOPdKlq0j_7s7gdNe_3QlyGnQDa3bFNvtyVM6Hx8M/edit - # https://wiki.lindenlab.com/wiki/Login_Test#Test_Viewer_Updater - #for test plans on all cases, as it requires setting up a fake VVM service - - try: - results = update_manager.query_vvm(handle, key, settings, summary) - except Exception, e: - print "query_vvm threw unexpected exception %s" % str(e) - assert False - - if results: - pattern = re.compile('Second Life') - assert pattern.search(results['channel']), "Bad results returned %s" % str(results) - - assert True diff --git a/indra/viewer_components/manager/tests/test_silent_write.py b/indra/viewer_components/manager/tests/test_silent_write.py deleted file mode 100644 index ad286787e2..0000000000 --- a/indra/viewer_components/manager/tests/test_silent_write.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_silent_write.py -@author coyot -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * - -import tempfile -import update_manager - -def test_silent_write_to_file(): - test_log = tempfile.TemporaryFile() - try: - update_manager.silent_write(test_log, "This is a test.") - except Exception, e: - print "Test failed due to: %s" % str(e) - assert False - -def test_silent_write_to_null(): - try: - update_manager.silent_write(None, "This is a test.") - except Exception, e: - print "Test failed due to: %s" % str(e) - assert False diff --git a/indra/viewer_components/manager/tests/test_summary.py b/indra/viewer_components/manager/tests/test_summary.py deleted file mode 100644 index f2f2af7347..0000000000 --- a/indra/viewer_components/manager/tests/test_summary.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python - - -""" -@file test_summary.py -@author coyot -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -from nose.tools import * - -import os.path -import tempfile -import update_manager - -def test_get_summary(): - key = update_manager.get_platform_key() - #launcher is one dir above tests - launcher_path = os.path.dirname(os.path.dirname(os.path.abspath(os.path.realpath(__file__)))) - summary_json = update_manager.get_summary(key, launcher_path) - - #we aren't testing the JSON library, one key pair is enough - #so we will use the one pair that is actually a constant - assert_equal(summary_json['Type'],'viewer') diff --git a/indra/viewer_components/manager/tests/with_setup_args.py b/indra/viewer_components/manager/tests/with_setup_args.py deleted file mode 100644 index 94d33aa5fe..0000000000 --- a/indra/viewer_components/manager/tests/with_setup_args.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python - -""" -@file with_setup_args.py -@author garyvdm -@date 2016-06-02 - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -def with_setup_args(setup, teardown=None): - """Decorator to add setup and/or teardown methods to a test function:: - @with_setup_args(setup, teardown) - def test_something(): - " ... " - The setup function should return (args, kwargs) which will be passed to - test function, and teardown function. - Note that `with_setup_args` is useful *only* for test functions, not for test - methods or inside of TestCase subclasses. - """ - def decorate(func): - args = [] - kwargs = {} - - def test_wrapped(): - func(*args, **kwargs) - - test_wrapped.__name__ = func.__name__ - - def setup_wrapped(): - a, k = setup() - args.extend(a) - kwargs.update(k) - if hasattr(func, 'setup'): - func.setup() - test_wrapped.setup = setup_wrapped - - if teardown: - def teardown_wrapped(): - if hasattr(func, 'teardown'): - func.teardown() - teardown(*args, **kwargs) - - test_wrapped.teardown = teardown_wrapped - else: - if hasattr(func, 'teardown'): - test_wrapped.teardown = func.teardown() - return test_wrapped - return decorate diff --git a/indra/viewer_components/manager/update_manager.py b/indra/viewer_components/manager/update_manager.py deleted file mode 100755 index 7962568119..0000000000 --- a/indra/viewer_components/manager/update_manager.py +++ /dev/null @@ -1,547 +0,0 @@ -#!/usr/bin/env python - -"""\ -@file update_manager.py -@author coyot -@date 2016-05-16 -@brief executes viewer update checking and manages downloading and applying of updates - -$LicenseInfo:firstyear=2016&license=viewerlgpl$ -Second Life Viewer Source Code -Copyright (C) 2016, 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$ -""" - -import os - -#NOTA BENE: -# For POSIX platforms, llbase will be imported from the same directory. -# For Windows, llbase will be compiled into the executable by pyinstaller -try: - from llbase import llrest - from llbase.llrest import RESTError - from llbase import llsd -except: - #if Windows, this is expected, if not, we're dead - if os.name == 'nt': - pass - -from copy import deepcopy -from datetime import datetime -from urlparse import urljoin - -import apply_update -import download_update -import errno -import fnmatch -import hashlib -import InstallerUserMessage -import json -import platform -import re -import shutil -import subprocess -import sys -import tempfile -import thread -import urllib - -def silent_write(log_file_handle, text): - #if we have a log file, write. If not, do nothing. - #this is so we don't have to keep trapping for an exception with a None handle - #oh and because it is best effort, it is also a holey_write ;) - if (log_file_handle): - #prepend text for easy grepping - timestamp = datetime.utcnow().strftime("%Y-%m-%D %H:%M:%S") - log_file_handle.write(timestamp + " UPDATE MANAGER: " + text + "\n") - -def after_frame(message, timeout = 10000): - #pop up a InstallerUserMessage.basic_message that kills itself after timeout milliseconds - #note that this blocks the caller for the duration of timeout - frame = InstallerUserMessage.InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - #this is done before basic_message so that we aren't blocked by mainloop() - #frame.after(timeout, lambda: frame._delete_window) - frame.after(timeout, lambda: frame.destroy()) - frame.basic_message(message = message) - -def convert_version_file_style(version): - #converts a version string a.b.c.d to a_b_c_d as used in downloaded filenames - #re will throw a TypeError if it gets None, just return that. - try: - pattern = re.compile('\.') - return pattern.sub('_', version) - except TypeError, te: - return None - -def get_platform_key(): - #this is the name that is inserted into the VVM URI - #and carried forward through the rest of the updater to determine - #platform specific actions as appropriate - platform_dict = {'Darwin':'mac', 'Linux':'lnx', 'Windows':'win'} - platform_uname = platform.system() - try: - return platform_dict[platform_uname] - except KeyError: - return None - -def get_summary(platform_name, launcher_path): - #get the contents of the summary.json file. - #for linux and windows, this file is in the same directory as the script - #for mac, the script is in ../Contents/MacOS/ and the file is in ../Contents/Resources/ - script_dir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) - if (platform_name == 'mac'): - summary_dir = os.path.abspath(os.path.join(script_dir, "../Resources")) - else: - summary_dir = script_dir - summary_file = os.path.join(summary_dir,"summary.json") - with open(summary_file) as summary_handle: - return json.load(summary_handle) - -def get_parent_path(platform_name): - #find the parent of the logs and user_settings directories - if (platform_name == 'mac'): - settings_dir = os.path.join(os.path.expanduser('~'),'Library','Application Support','SecondLife') - elif (platform_name == 'lnx'): - settings_dir = os.path.join(os.path.expanduser('~'),'.secondlife') - #using list format of join is important here because the Windows pathsep in a string escapes the next char - elif (platform_name == 'win'): - settings_dir = os.path.join(os.path.expanduser('~'),'AppData','Roaming','SecondLife') - else: - settings_dir = None - return settings_dir - -def make_download_dir(parent_dir, new_version): - #make a canonical download dir if it does not already exist - #format: ../user_settings/downloads/1.2.3.456789 - #we do this so that multiple viewers on the same host can update separately - #this also functions as a getter - try: - download_dir = os.path.join(parent_dir, "downloads", new_version) - os.makedirs(download_dir) - except OSError, hell: - #Directory already exists, that's okay. Other OSErrors are not okay. - if hell[0] == errno.EEXIST: - pass - else: - raise hell - return download_dir - -def check_for_completed_download(download_dir): - #there will be two files on completion, the download and a marker file called "".done"" - #for optional upgrades, there may also be a .skip file to skip this particular upgrade - #or .next to install on next run - completed = None - marker_regex = '*' + '.done' - skip_regex = '*' + '.skip' - next_regex = '*' + '.next' - for filename in os.listdir(download_dir): - if fnmatch.fnmatch(filename, marker_regex): - completed = 'done' - elif fnmatch.fnmatch(filename, skip_regex): - completed = 'skip' - elif fnmatch.fnmatch(filename, next_regex): - #so we don't skip infinitely - os.remove(filename) - completed = 'next' - if not completed: - #cleanup - shutil.rmtree(download_dir) - return completed - -def get_settings(log_file_handle, parent_dir): - #return the settings file parsed into a dict - try: - settings_file = os.path.abspath(os.path.join(parent_dir,'user_settings','settings.xml')) - #this happens when the path to settings file happens on the command line - #we get a full path and don't need to munge it - if not os.path.exists(settings_file): - settings_file = parent_dir - settings = llsd.parse((open(settings_file)).read()) - except llsd.LLSDParseError as lpe: - silent_write(log_file_handle, "Could not parse settings file %s" % lpe) - return None - return settings - -def get_log_file_handle(parent_dir, filename = None): - #return a write handle on the log file - #plus log rotation and not dying on failure - if not filename: - return None - log_file = os.path.join(parent_dir, filename) - old_file = log_file + '.old' - #if someone's log files are present but not writable, they've screwed up their install. - if os.access(log_file, os.W_OK): - if os.access(old_file, os.W_OK): - os.unlink(old_file) - os.rename(log_file, old_file) - elif not os.path.exists(log_file): - #reimplement TOUCH(1) in Python - #perms default to 644 which is fine - open(log_file, 'w+').close() - try: - f = open(log_file,'w+') - except Exception as e: - #we don't have a log file to write to, make a best effort and sally onward - print "Could not open update manager log file %s" % log_file - f = None - return f - -def make_VVM_UUID_hash(platform_key): - #NOTE: There is no python library support for a persistent machine specific UUID - # AND all three platforms do this a different way, so exec'ing out is really the best we can do - #Lastly, this is a best effort service. If we fail, we should still carry on with the update - uuid = None - if (platform_key == 'lnx'): - uuid = subprocess.check_output(['/usr/bin/hostid']).rstrip() - elif (platform_key == 'mac'): - #this is absurdly baroque - #/usr/sbin/system_profiler SPHardwareDataType | fgrep 'Serial' | awk '{print $NF}' - uuid = subprocess.check_output(["/usr/sbin/system_profiler", "SPHardwareDataType"]) - #findall[0] does the grep for the value we are looking for: "Serial Number (system): XXXXXXXX" - #split(:)[1] gets us the XXXXXXX part - #lstrip shaves off the leading space that was after the colon - uuid = re.split(":", re.findall('Serial Number \(system\): \S*', uuid)[0])[1].lstrip() - elif (platform_key == 'win'): - # wmic csproduct get UUID | grep -v UUID - uuid = subprocess.check_output(['wmic','csproduct','get','UUID']) - #outputs in two rows: - #UUID - #XXXXXXX-XXXX... - uuid = re.split('\n',uuid)[1].rstrip() - if uuid is not None: - return hashlib.md5(uuid).hexdigest() - else: - #fake it - return hashlib.md5(str(uuid.uuid1())).hexdigest() - -def query_vvm(log_file_handle = None, platform_key = None, settings = None, summary_dict = None, UpdaterServiceURL = None, UpdaterWillingToTest = None): - result_data = None - baseURI = None - #URI template /update/v1.1/channelname/version/platformkey/platformversion/willing-to-test/uniqueid - #https://wiki.lindenlab.com/wiki/Viewer_Version_Manager_REST_API#Viewer_Update_Query - #note that the only two valid options are: - # # version-phx0.damballah.lindenlab.com - # # version-qa.secondlife-staging.com - print "updater service host: " + repr(UpdaterServiceURL) - if UpdaterServiceURL: - #we can't really expect the users to put the protocol or base dir on, they will give us a host - base_URI = urljoin('https://' + UpdaterServiceURL[0], '/update/') - else: - base_URI = 'https://update.secondlife.com/update/' - channelname = summary_dict['Channel'] - #this is kind of a mess because the settings value is a) in a map and b) is both the cohort and the version in one string - version = summary_dict['Version'] - #we need to use the dotted versions of the platform versions in order to be compatible with VVM rules and arithmetic - if platform_key == 'win': - platform_version = platform.win32_ver()[1] - elif platform_key == 'mac': - platform_version = platform.mac_ver()[0] - else: - platform_version = platform.release() - #this will always return something usable, error handling in method - UUID = str(make_VVM_UUID_hash(platform_key)) - #note that this will not normally be in a settings.xml file and is only here for test builds. - #for test builds, add this key to the ../user_settings/settings.xml - """ - test - - Comment - Tell update manager you aren't willing to test. - Type - String - Value - testno - - - """ - if UpdaterWillingToTest is not None: - if UpdaterWillingToTest: - test_ok = 'testok' - else: - test_ok = 'testno' - else: - try: - test_ok = settings['test']['Value'] - except KeyError: - #normal case, no testing key - test_ok = 'testok' - #because urljoin can't be arsed to take multiple elements - #channelname is a list because although it is only one string, it is a kind of argument and viewer args can take multiple keywords. - query_string = urllib.quote('v1.0/' + channelname[0] + '/' + version + '/' + platform_key + '/' + platform_version + '/' + test_ok + '/' + UUID) - silent_write(log_file_handle, "About to query VVM: %s" % base_URI + query_string) - VVMService = llrest.SimpleRESTService(name='VVM', baseurl=base_URI) - try: - result_data = VVMService.get(query_string) - except RESTError as re: - silent_write(log_file_handle, "Failed to query VVM using %s failed as %s" % (urljoin(base_URI,query_string), re)) - return None - return result_data - -def download(url = None, version = None, download_dir = None, size = 0, background = False, chunk_size = None, log_file_handle = None): - download_tries = 0 - download_success = False - if not chunk_size: - chunk_size = 1024 - #for background execution - path_to_downloader = os.path.join(os.path.dirname(os.path.realpath(__file__)), "download_update.py") - #three strikes and you're out - while download_tries < 3 and not download_success: - #323: Check for a partial update of the required update; in either event, display an alert that a download is required, initiate the download, and then install and launch - if download_tries == 0: - after_frame(message = "Downloading new version " + version + " Please wait.", timeout = 5000) - else: - after_frame(message = "Trying again to download new version " + version + " Please wait.", timeout = 5000) - if not background: - try: - download_update.download_update(url = url, download_dir = download_dir, size = size, progressbar = True, chunk_size = chunk_size) - download_success = True - except Exception, e: - download_tries += 1 - silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") - silent_write(log_file_handle, "Logging download exception: %s" % e.message) - else: - try: - #Python does not have a facility to multithread a method, so we make the method a standalone - #and subprocess that - subprocess.call(path_to_downloader, "--url = %s --dir = %s --pb --size = %s --chunk_size = %s" % (url, download_dir, size, chunk_size)) - download_success = True - except: - download_tries += 1 - silent_write(log_file_handle, "Failed to download new version " + version + ". Trying again.") - if not download_success: - silent_write(log_file_handle, "Failed to download new version " + version) - after_frame(message = "Failed to download new version " + version + " Please check connectivity.") - return False - return True - -def install(platform_key = None, download_dir = None, log_file_handle = None, in_place = None, downloaded = None): - #user said no to this one - if downloaded != 'skip': - after_frame(message = "New version downloaded. Installing now, please wait.") - success = apply_update.apply_update(download_dir, platform_key, log_file_handle, in_place) - version = download_dir.split('/')[-1] - if success: - silent_write(log_file_handle, "successfully updated to " + version) - shutil.rmtree(download_dir) - #this is either True for in place or the path to the new install for not in place - return success - else: - after_frame(message = "Failed to apply " + version) - silent_write(log_file_handle, "Failed to update viewer to " + version) - return False - -def download_and_install(downloaded = None, url = None, version = None, download_dir = None, size = None, - platform_key = None, log_file_handle = None, in_place = None, chunk_size = 1024): - #extracted to a method because we do it twice in update_manager() and this makes the logic clearer - if not downloaded: - #do the download, exit if we fail - if not download(url = url, version = version, download_dir = download_dir, size = size, chunk_size = chunk_size, log_file_handle = log_file_handle): - return (False, 'download', version) - #do the install - path_to_new_launcher = install(platform_key = platform_key, download_dir = download_dir, - log_file_handle = log_file_handle, in_place = in_place, downloaded = downloaded) - if path_to_new_launcher: - #if we succeed, propagate the success type upwards - if in_place: - return (True, 'in place', True) - else: - return (True, 'in place', path_to_new_launcher) - else: - #propagate failure - return (False, 'apply', version) - -def update_manager(cli_overrides = None): - #cli_overrides is a dict where the keys are specific parameters of interest and the values are the arguments to - #comments that begin with '323:' are steps taken from the algorithm in the description of SL-323. - # Note that in the interest of efficiency, such as determining download success once at the top - # The code does follow precisely the same order as the algorithm. - #return values rather than exit codes. All of them are to communicate with launcher - #we print just before we return so that __main__ outputs something - returns are swallowed - # (False, 'setup', None): error occurred before we knew what the update was (e.g., in setup or parsing) - # (False, 'download', version): we failed to download the new version - # (False, 'apply', version): we failed to apply the new version - # (True, None, None): No update found - # (True, 'in place, True): update applied in place - # (True, 'in place', path_to_new_launcher): Update applied by a new install to a new location - # (True, 'background', True): background download initiated - - #setup and getting initial parameters - platform_key = get_platform_key() - parent_dir = get_parent_path(platform_key) - log_file_handle = get_log_file_handle(parent_dir, 'update_manager.log') - settings = None - - #check to see if user has install rights - #get the owner of the install and the current user - script_owner_id = os.stat(os.path.realpath(__file__)).st_uid - user_id = os.geteuid() - #if we are on lnx or mac, we can pretty print the IDs as names using the pwd module - #win does not provide this support and Python will throw an ImportError there, so just use raw IDs - if script_owner_id != user_id: - if platform_key != 'win': - import pwd - script_owner_name = pwd.getpwuid(script_owner_id)[0] - username = pwd.getpwuid(user_id)[0] - else: - username = user_id - script_owner_name = script_owner_id - silent_write(log_file_handle, "Upgrade notification attempted by userid " + username) - frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - frame.binary_choice_message(message = "Second Life was installed by userid " + script_owner_name - + ". Do you have privileges to install?", true = "Yes", false = 'No') - if not frame.choice.get(): - silent_write(log_file_handle, "Upgrade attempt declined by userid " + username) - after_frame(message = "Please find a system admin to upgrade Second Life") - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - if cli_overrides is not None: - if 'settings' in cli_overrides.keys(): - if cli_overrides['settings'] is not None: - settings = get_settings(log_file_handle, cli_overrides['settings'][0]) - else: - settings = get_settings(log_file_handle, parent_dir) - - if settings is None: - silent_write(log_file_handle, "Failed to load viewer settings") - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - #323: If a complete download of that update is found, check the update preference: - #settings['UpdaterServiceSetting'] = 0 is manual install - """ - UpdaterServiceSetting - - Comment - Configure updater service. - Type - U32 - Value - 0 - - """ - if cli_overrides is not None: - if 'set' in cli_overrides.keys(): - if 'UpdaterServiceSetting' in cli_overrides['set'].keys(): - install_automatically = cli_overrides['set']['UpdaterServiceSetting'] - else: - try: - install_automatically = settings['UpdaterServiceSetting']['Value'] - #because, for some godforsaken reason, we delete the setting rather than changing the value - except KeyError: - install_automatically = 1 - - #use default chunk size if none is given - if cli_overrides is not None: - if 'set' in cli_overrides.keys(): - if 'UpdaterMaximumBandwidth' in cli_overrides['set'].keys(): - chunk_size = cli_overrides['set']['UpdaterMaximumBandwidth'] - else: - chunk_size = 1024 - else: - chunk_size = 1024 - - #get channel and version - try: - summary_dict = get_summary(platform_key, os.path.abspath(os.path.realpath(__file__))) - #we send the override to the VVM, but retain the summary.json version for in_place computations - channel_override_summary = deepcopy(summary_dict) - if cli_overrides is not None: - if 'channel' in cli_overrides.keys(): - channel_override_summary['Channel'] = cli_overrides['channel'] - except Exception, e: - silent_write(log_file_handle, "Could not obtain channel and version, exiting.") - silent_write(log_file_handle, e.message) - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - #323: On launch, the Viewer Manager should query the Viewer Version Manager update api. - if cli_overrides is not None: - if 'update-service' in cli_overrides.keys(): - UpdaterServiceURL = cli_overrides['update-service'] - else: - #tells query_vvm to use the default - UpdaterServiceURL = None - else: - UpdaterServiceURL = None - result_data = query_vvm(log_file_handle, platform_key, settings, channel_override_summary, UpdaterServiceURL) - - #nothing to do or error - if not result_data: - silent_write(log_file_handle, "No update found.") - print "Update manager exited with (%s, %s, %s)" % (True, None, None) - return (True, None, None) - - #get download directory, if there are perm issues or similar problems, give up - try: - download_dir = make_download_dir(parent_dir, result_data['version']) - except Exception, e: - print "Update manager exited with (%s, %s, %s)" % (False, 'setup', None) - return (False, 'setup', None) - - #if the channel name of the response is the same as the channel we are launched from, the update is "in place" - #and launcher will launch the viewer in this install location. Otherwise, it will launch the Launcher from - #the new location and kill itself. - in_place = (summary_dict['Channel'] == result_data['channel']) - print "summary %s, result %s, in_place %s" % (summary_dict['Channel'], result_data['channel'], in_place) - - #determine if we've tried this download before - downloaded = check_for_completed_download(download_dir) - - #323: If the response indicates that there is a required update: - if result_data['required'] or (not result_data['required'] and install_automatically): - #323: Check for a completed download of the required update; if found, display an alert, install the required update, and launch the newly installed viewer. - #323: If [optional download and] Install Automatically: display an alert, install the update and launch updated viewer. - return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, - size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size) - else: - #323: If the update response indicates that there is an optional update: - #323: Check to see if the optional update has already been downloaded. - #323: If a complete download of that update is found, check the update preference: - #note: automatic install handled above as the steps are the same as required upgrades - #323: If Install Manually: display a message with the update information and ask the user whether or not to install the update with three choices: - #323: Skip this update: create a marker that subsequent launches should not prompt for this update as long as it is optional, - # but leave the download in place so that if it becomes required it will be there. - #323: Install next time: create a marker that skips the prompt and installs on the next launch - #323: Install and launch now: do it. - if downloaded is not None and downloaded != 'skip': - frame = InstallerUserMessage(title = "Second Life Installer", icon_name="head-sl-logo.gif") - #The choices are reordered slightly to encourage immediate install and slightly discourage skipping - frame.trinary_message(message = "Please make a selection", - one = "Install new version now.", two = 'Install the next time the viewer is launched.', three = 'Skip this update.') - choice = frame.choice.get() - if choice == 1: - return download_and_install(downloaded = downloaded, url = result_data['url'], version = result_data['version'], download_dir = download_dir, - size = result_data['size'], platform_key = platform_key, log_file_handle = log_file_handle, in_place = in_place, chunk_size = chunk_size) - elif choice == 2: - tempfile.mkstmp(suffix = ".next", dir = download_dir) - return (True, None, None) - else: - tempfile.mkstmp(suffix = ".skip", dir = download_dir) - return (True, None, None) - else: - #multithread a download - download(url = result_data['url'], version = result_data['version'], download_dir = download_dir, size = result_data['size'], background = True, log_file_handle = log_file_handle) - print "Update manager exited with (%s, %s, %s)" % (True, 'background', True) - return (True, 'background', True) - - -if __name__ == '__main__': - #there is no argument parsing or other main() work to be done - update_manager() -- cgit v1.2.3 From a50a2e170d4cf99507d2dd974aed7f73424376cf Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 1 Mar 2017 11:01:40 -0800 Subject: SL-321: fix merge removal of mIsFirstRun definition, remove pyinstaller from viewer cmake, now in VMP package cmake --- indra/newview/CMakeLists.txt | 50 -------------------------------------------- indra/newview/llappviewer.h | 2 ++ 2 files changed, 2 insertions(+), 50 deletions(-) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 394a9c8578..9badc95a72 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1758,56 +1758,6 @@ if (WINDOWS) endif (FMODEX) get_filename_component(PYTHON_DIRECTORY ${PYTHON_EXECUTABLE} DIRECTORY) - - # http://pythonhosted.org/PyInstaller/#options - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/SL_Launcher.exe - COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe - ARGS - --onefile - --log-level WARN - --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - ${CMAKE_SOURCE_DIR}/viewer_components/manager/SL_Launcher - COMMENT "Performing pyinstaller compile of SL_Launcher" -) - - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/apply_update.exe - COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe - ARGS - --onefile - --log-level WARN - --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - ${CMAKE_SOURCE_DIR}/viewer_components/manager/apply_update.py - COMMENT "Performing pyinstaller compile of updater" -) - - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/download_update.exe - COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe - ARGS - --onefile - --log-level WARN - --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - ${CMAKE_SOURCE_DIR}/viewer_components/manager/download_update.py - COMMENT "Performing pyinstaller compile of update downloader" -) - - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/update_manager.exe - COMMAND ${PYTHON_DIRECTORY}/Scripts/pyinstaller.exe - ARGS - --onefile - --log-level WARN - --distpath ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} - ${CMAKE_SOURCE_DIR}/viewer_components/manager/update_manager.py - COMMENT "Performing pyinstaller compile of update manager" -) - -add_custom_target(compile_w_viewer_launcher ALL DEPENDS ${CMAKE_CFG_INTDIR}/SL_Launcher.exe) -add_custom_target(compile_w_viewer_updater ALL DEPENDS ${CMAKE_CFG_INTDIR}/apply_update.exe) -add_custom_target(compile_w_viewer_downloader ALL DEPENDS ${CMAKE_CFG_INTDIR}/download_update.exe) -add_custom_target(compile_w_viewer_update_manager ALL DEPENDS ${CMAKE_CFG_INTDIR}/update_manager.exe) add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 91d79c4260..272209fc79 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -313,6 +313,8 @@ private: // llcorehttp library init/shutdown helper LLAppCoreHttp mAppCoreHttp; + bool mIsFirstRun; + }; // consts from viewer.h -- cgit v1.2.3 From 9ec3b1bb039bdb40fce6aee66ce68ce8cabfb785 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 1 Mar 2017 13:19:34 -0800 Subject: SL-321: fix merge removal of llhasheduniqueid from from llloginstance_test's include path --- indra/newview/tests/lllogininstance_test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 55dcb83567..5397feeab8 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -31,6 +31,9 @@ #include "../llviewernetwork.h" #include "../lllogininstance.h" + // Needed for Auth Test + #include "../llhasheduniqueid.h" + // STL headers // std headers // external library headers -- cgit v1.2.3 From 49552a874c659a7e4864e276f4250d498676a559 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 1 Mar 2017 23:28:11 -0800 Subject: SL-321: remove redundant const declaration of MD5HEX_STR_SIZE in llversioninfo, remove extraneous python variable assignment in CMakeLists, run tests with INFO --- indra/cmake/run_build_test.py | 2 +- indra/newview/CMakeLists.txt | 2 -- indra/newview/tests/lllogininstance_test.cpp | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/indra/cmake/run_build_test.py b/indra/cmake/run_build_test.py index f02c5e98b4..210e43b232 100755 --- a/indra/cmake/run_build_test.py +++ b/indra/cmake/run_build_test.py @@ -312,7 +312,7 @@ if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("-d", "--debug", dest="loglevel", action="store_const", - const=logging.DEBUG, default=logging.WARNING) + const=logging.DEBUG, default=logging.INFO) parser.add_argument("-D", "--define", dest="vars", default=[], action="append", metavar="VAR=value", help="Add VAR=value to the env variables defined") diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 9badc95a72..fe360171fb 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1756,8 +1756,6 @@ if (WINDOWS) ${SHARED_LIB_STAGING_DIR}/Debug/fmodexL.dll ) endif (FMODEX) - - get_filename_component(PYTHON_DIRECTORY ${PYTHON_EXECUTABLE} DIRECTORY) add_custom_command( OUTPUT ${CMAKE_CFG_INTDIR}/copy_touched.bat diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 5397feeab8..3b7ec48d61 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -190,7 +190,6 @@ void LLUIColorTable::saveUserSettings(void)const {} #include "../llversioninfo.h" const std::string &LLVersionInfo::getVersion() { return VIEWERLOGIN_VERSION; } const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; } -const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null bool llHashedUniqueID(unsigned char* id) { -- cgit v1.2.3 From 9c5332b301c0e17cd0133944670f79fd485cf7fe Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 6 Mar 2017 14:43:57 -0800 Subject: SL-321: reintegration of autobuild as package, build changes for mac integration --- autobuild.xml | 46 ++++++++++++++++++++++++++++++ indra/newview/CMakeLists.txt | 1 + indra/newview/llversioninfo.cpp | 1 + indra/newview/tests/llversioninfo_test.cpp | 6 +++- indra/newview/viewer_manifest.py | 46 +++++++++++------------------- 5 files changed, 70 insertions(+), 30 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index a655c7cae2..2f7ad05ecd 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3023,6 +3023,52 @@ version 0.8.0.1 + viewer-manager + + copyright + Copyright (c) 2000-2012, Linden Research, Inc. + description + Linden Lab Viewer Management Process suite. + license + Proprietary + license_file + LICENSE + name + viewer-manager + platforms + + darwin64 + + archive + + hash + b7a7d155a70413bce9a7a25ad54c37e7 + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3073/7235/viewer_manager-1.0-darwin64-503061.tar.bz2 + + name + darwin64 + + windows + + archive + + hash + 0c1a369a3e5ed6363a306e6135797851 + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3074/7241/viewer_manager-1.0-windows-503061.tar.bz2 + + name + windows + + + source + https://bitbucket.org/lindenlab/vmp-standalone + source_type + hg + version + 1.0 + vlc-bin copyright diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fe360171fb..7a62d8baec 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -47,6 +47,7 @@ include(TemplateCheck) include(UI) include(UnixInstall) include(ViewerMiscLibs) +include(ViewerManager) include(VisualLeakDetector) include(ZLIB) include(URIPARSER) diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index 375dce485d..bf9d98ee91 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -64,6 +64,7 @@ S32 LLVersionInfo::getPatch() //static S32 LLVersionInfo::getBuild() { + std::cout << "What we have IN llversioninfo: " << LL_VIEWER_VERSION_BUILD << std::endl; return LL_VIEWER_VERSION_BUILD; } diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp index f1f69f33f1..2f7a4e9601 100644 --- a/indra/newview/tests/llversioninfo_test.cpp +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -29,6 +29,8 @@ #include "../llversioninfo.h" + #include + // LL_VIEWER_CHANNEL is a macro defined on the compiler command line. The // macro expands to the string name of the channel, but without quotes. We // need to turn it into a quoted string. This macro trick does that. @@ -81,7 +83,9 @@ namespace tut template<> template<> void versioninfo_object_t::test<1>() - { + { + std::cout << "What we parsed from CMake: " << LL_VIEWER_VERSION_BUILD << std::endl; + std::cout << "What we get from llversioninfo: " << LLVersionInfo::getBuild() << std::endl; ensure_equals("Major version", LLVersionInfo::getMajor(), LL_VIEWER_VERSION_MAJOR); diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 35d63c33fc..1dc178dcf8 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -223,7 +223,7 @@ class ViewerManifest(LLManifest): return channel_type def channel_variant_app_suffix(self): - # get any part of the compiled channel name after the CHANNEL_VENDOR_BASE + # get any part of the channel name after the CHANNEL_VENDOR_BASE suffix=self.channel_variant() # by ancient convention, we don't use Release in the app name if self.channel_type() == 'release': @@ -346,15 +346,12 @@ class WindowsManifest(ViewerManifest): if self.is_packaging_viewer(): # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) - # include the compiled launcher script so that it gets included in the file_list - self.path(src='%s/SL_Launcher.exe' % self.args['configuration'], dst="SL_Launcher.exe") # Plugin host application self.path2basename(os.path.join(os.pardir, 'llplugin', 'slplugin', self.args['configuration']), "slplugin.exe") - #note, launcher and friends do not need viewer_manifest in Windows as the scripts are compiled into executables # Get shared libs from the shared libs staging directory if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), dst=""): @@ -709,28 +706,29 @@ class DarwinManifest(ViewerManifest): pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") - llbasedir = os.path.join(pkgdir, os.pardir) + vmpdir = os.path.join(pkgdir, "VMP") + llbasedir = os.path.join(pkgdir, "VMP") if self.prefix(src="", dst="Contents"): # everything goes in Contents self.path("Info.plist", dst="Info.plist") # copy additional libs in /Contents/MacOS/ self.path(os.path.join(relpkgdir, "libndofdev.dylib"), dst="Resources/libndofdev.dylib") - self.path(os.path.join(relpkgdir, "libhunspell-1.3.0.dylib"), dst="Resources/libhunspell-1.3.0.dylib") + self.path(os.path.join(relpkgdir, "libhunspell-1.3.0.dylib"), dst="Resources/libhunspell-1.3.0.dylib") if self.prefix(dst="MacOS"): - self.path2basename("../viewer_components/updater/scripts/darwin", "*.py") #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 - self.path2basename("../viewer_components/manager","SL_Launcher") - self.path2basename("../viewer_components/manager","*.py") + self.path2basename(vmpdir,"SL_Launcher") + self.path2basename(vmpdir,"*.py") llbase_path = os.path.join(self.get_dst_prefix(),'llbase') if not os.path.exists(llbase_path): os.makedirs(llbase_path) + #before, we only needed llbase at build time. With VMP, we need it at run time. if self.prefix(dst="llbase"): - self.path2basename("../packages/llbase","*.py") - self.path2basename("../packages/llbase","_cllsd.so") + self.path2basename(llbasedir,"*.py") + self.path2basename(llbasedir,"_cllsd.so") self.end_prefix() - self.end_prefix() + self.end_prefix() # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): @@ -749,6 +747,12 @@ class DarwinManifest(ViewerManifest): self.path("secondlife.icns") self.end_prefix(icon_path) + #VMP Tkinter icons + if self.prefix("vmp_icons"): + self.path("*.png") + self.path("*.gif") + self.end_prefix("vmp_icons") + self.path("SecondLife.nib") # Translations @@ -773,12 +777,6 @@ class DarwinManifest(ViewerManifest): self.path("uk.lproj") self.path("zh-Hans.lproj") - #VMP icons - if self.prefix("vmp_icons"): - self.path("*.png") - self.path("*.gif") - self.end_prefix("vmp_icons") - def path_optional(src, dst): """ For a number of our self.path() calls, not only do we want @@ -1145,17 +1143,7 @@ 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.path2basename("../llplugin/slplugin", "SLPlugin") - #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 - self.path2basename("../viewer_components/manager","SL_Launcher") - self.path2basename("../viewer_components/manager","*.py") - llbase_path = os.path.join(self.get_dst_prefix(),'llbase') - if not os.path.exists(llbase_path): - os.makedirs(llbase_path) - if self.prefix(dst="llbase"): - self.path2basename("../packages/llbase","*.py") - self.path2basename("../packages/llbase","_cllsd.so") - self.end_prefix() + self.path2basename("../llplugin/slplugin", "SLPlugin") self.end_prefix("bin") if self.prefix("res-sdl"): -- cgit v1.2.3 From b69831b691e72f925c6097cf8bfa529be23b806d Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 6 Mar 2017 15:36:50 -0800 Subject: SL-321: add new ViewerManager.cmake file, knowingly overriding commit hooks --- indra/cmake/ViewerManager.cmake | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 indra/cmake/ViewerManager.cmake diff --git a/indra/cmake/ViewerManager.cmake b/indra/cmake/ViewerManager.cmake new file mode 100644 index 0000000000..793ed78b6c --- /dev/null +++ b/indra/cmake/ViewerManager.cmake @@ -0,0 +1,2 @@ +include (Prebuilt) +use_prebuilt_binary(viewer-manager) \ No newline at end of file -- cgit v1.2.3 From cf8dc0d97a2a34c038d1bedc5d0fd7a1badbcd86 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 7 Mar 2017 15:05:42 -0800 Subject: SL-321: reintegration of autobuild as package, build changes for linux and windows integration --- indra/newview/viewer_manifest.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 1dc178dcf8..8c51719031 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -346,6 +346,13 @@ class WindowsManifest(ViewerManifest): if self.is_packaging_viewer(): # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) + # include the compiled launcher scripts so that it gets included in the file_list + self.path(src='%s/apply_update.exe' % self.args['configuration'], dst="apply_update.exe") + self.path(src='%s/download_update.exe' % self.args['configuration'], dst="download_update.exe") + self.path(src='%s/SL_Launcher.exe' % self.args['configuration'], dst="SL_Launcher.exe") + self.path(src='%s/update_manager.exe' % self.args['configuration'], dst="update_manager.exe") + #IUM is not normally executed directly, just imported. No exe needed. + self.path2basename("../viewer_components/manager","InstallerUserMessage.py") # Plugin host application self.path2basename(os.path.join(os.pardir, @@ -677,7 +684,17 @@ class WindowsManifest(ViewerManifest): if not python or python == "${PYTHON}": python = 'python' if os.path.exists(sign_py): + print "about to run signing of: ", self.dst_path_of(installer_file).replace('\\', '\\\\\\\\') self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\\', '\\\\\\\\'))) + #Unlike the viewer binary, the VMP filenames are invariant with respect to version, os, etc. + print "about to run signing of: ", self.dst_path_of("apply_update.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("apply_update.exe").replace('\\', '\\\\\\\\'))) + print "about to run signing of: ", self.dst_path_of("download_update.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("download_update.exe").replace('\\', '\\\\\\\\'))) + print "about to run signing of: ", self.dst_path_of("SL_Launcher.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("SL_Launcher.exe").replace('\\', '\\\\\\\\'))) + print "about to run signing of: ", self.dst_path_of("update_manager.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("update_manager.exe").replace('\\', '\\\\\\\\'))) else: print "Skipping code signing,", sign_py, "does not exist" self.created_path(self.dst_path_of(installer_file)) @@ -1143,7 +1160,16 @@ 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.path2basename("../llplugin/slplugin", "SLPlugin") + self.path2basename("../llplugin/slplugin", "SLPlugin") + #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 + self.path2basename("../viewer_components/manager","SL_Launcher") + self.path2basename("../viewer_components/manager","*.py") + llbase_path = os.path.join(self.get_dst_prefix(),'llbase') + if not os.path.exists(llbase_path): + os.makedirs(llbase_path) + if self.prefix(dst="llbase"): + self.path2basename("../packages/llbase","*.py") + self.path2basename("../packages/llbase","_cllsd.so") self.end_prefix("bin") if self.prefix("res-sdl"): -- cgit v1.2.3 From c69db33ded0a860060a9702495cfcddc26008d09 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 8 Mar 2017 12:34:11 -0800 Subject: SL-321: fix windows pathing, add icons --- indra/newview/viewer_manifest.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 8c51719031..4fbdc52e30 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -342,17 +342,23 @@ class WindowsManifest(ViewerManifest): pkgdir = os.path.join(self.args['build'], os.pardir, 'packages') relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") + vmpdir = os.path.join(pkgdir, "VMP") if self.is_packaging_viewer(): # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) # include the compiled launcher scripts so that it gets included in the file_list - self.path(src='%s/apply_update.exe' % self.args['configuration'], dst="apply_update.exe") - self.path(src='%s/download_update.exe' % self.args['configuration'], dst="download_update.exe") - self.path(src='%s/SL_Launcher.exe' % self.args['configuration'], dst="SL_Launcher.exe") - self.path(src='%s/update_manager.exe' % self.args['configuration'], dst="update_manager.exe") + self.path(src='%s/apply_update.exe' % vmpdir, dst="apply_update.exe") + self.path(src='%s/download_update.exe' % vmpdir, dst="download_update.exe") + self.path(src='%s/SL_Launcher.exe' % vmpdir, dst="SL_Launcher.exe") + self.path(src='%s/update_manager.exe' % vmpdir, dst="update_manager.exe") #IUM is not normally executed directly, just imported. No exe needed. - self.path2basename("../viewer_components/manager","InstallerUserMessage.py") + self.path2basename(vmpdir,"InstallerUserMessage.py") + #VMP Tkinter icons + if self.prefix("vmp_icons"): + self.path("*.png") + self.path("*.gif") + self.end_prefix("vmp_icons") # Plugin host application self.path2basename(os.path.join(os.pardir, -- cgit v1.2.3 From 2e011225754192cc35af8438d898da55a8596904 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 9 Mar 2017 10:28:16 -0800 Subject: SL-321: add local llbase support for Windows --- indra/newview/viewer_manifest.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 4fbdc52e30..da641e920c 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -343,23 +343,36 @@ class WindowsManifest(ViewerManifest): relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") + llbasedir = os.path.join(pkgdir, "VMP") if self.is_packaging_viewer(): # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) + # include the compiled launcher scripts so that it gets included in the file_list self.path(src='%s/apply_update.exe' % vmpdir, dst="apply_update.exe") self.path(src='%s/download_update.exe' % vmpdir, dst="download_update.exe") self.path(src='%s/SL_Launcher.exe' % vmpdir, dst="SL_Launcher.exe") self.path(src='%s/update_manager.exe' % vmpdir, dst="update_manager.exe") + #IUM is not normally executed directly, just imported. No exe needed. self.path2basename(vmpdir,"InstallerUserMessage.py") + #VMP Tkinter icons if self.prefix("vmp_icons"): self.path("*.png") self.path("*.gif") self.end_prefix("vmp_icons") + #before, we only needed llbase at build time. With VMP, we need it at run time. + llbase_path = os.path.join(self.get_dst_prefix(),'llbase') + if not os.path.exists(llbase_path): + os.makedirs(llbase_path) + if self.prefix(dst="llbase"): + self.path2basename(llbasedir,"*.py") + self.path2basename(llbasedir,"_cllsd.so") + self.end_prefix() + # Plugin host application self.path2basename(os.path.join(os.pardir, 'llplugin', 'slplugin', self.args['configuration']), -- cgit v1.2.3 From 8cbaaa364f6bfd01aeb4280689e2f883a5ffed53 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 10 Mar 2017 09:23:35 -0800 Subject: SL-321: add new VMP package, fix llbase path --- autobuild.xml | 8 ++++---- indra/newview/viewer_manifest.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 2f7ad05ecd..3b204f6778 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - b7a7d155a70413bce9a7a25ad54c37e7 + 7a3628c40ece889413cc6991d31752ab url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3073/7235/viewer_manager-1.0-darwin64-503061.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3208/7750/viewer_manager-1.0-darwin64-503197.tar.bz2 name darwin64 @@ -3054,9 +3054,9 @@ archive hash - 0c1a369a3e5ed6363a306e6135797851 + 607b45e6d5e206594fcf8e7ed5783d49 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3074/7241/viewer_manager-1.0-windows-503061.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3209/7756/viewer_manager-1.0-windows-503197.tar.bz2 name windows diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index da641e920c..60ee7728ab 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -343,7 +343,7 @@ class WindowsManifest(ViewerManifest): relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") - llbasedir = os.path.join(pkgdir, "VMP") + llbasedir = os.path.join(pkgdir, "llbase") if self.is_packaging_viewer(): # Find secondlife-bin.exe in the 'configuration' dir, then rename it to the result of final_exe. @@ -743,7 +743,7 @@ class DarwinManifest(ViewerManifest): relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") - llbasedir = os.path.join(pkgdir, "VMP") + llbasedir = os.path.join(pkgdir, "llbase") if self.prefix(src="", dst="Contents"): # everything goes in Contents self.path("Info.plist", dst="Info.plist") -- cgit v1.2.3 From faa36236ed7600108af1e4f01f313fcac8f0bca2 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 13 Mar 2017 20:23:31 -0700 Subject: SL-361: update VMP package --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 3b204f6778..ea690837ae 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - 7a3628c40ece889413cc6991d31752ab + 62d55bc21b56dbd7a7f06429d008b81f url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3208/7750/viewer_manager-1.0-darwin64-503197.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3341/8290/viewer_manager-1.0-darwin64-503329.tar.bz2 name darwin64 @@ -3054,9 +3054,9 @@ archive hash - 607b45e6d5e206594fcf8e7ed5783d49 + b97eb0662b558be20df9b11031d38274 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3209/7756/viewer_manager-1.0-windows-503197.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3340/8284/viewer_manager-1.0-windows-503329.tar.bz2 name windows -- cgit v1.2.3 From 3052fa4241e2eba08ab7a68479d979726f6ccd8e Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 13 Mar 2017 23:26:15 -0700 Subject: SL-361: update VMP package --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index ea690837ae..30de9a674e 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - 62d55bc21b56dbd7a7f06429d008b81f + 0a1efab5a2b620af2490bebaf634af44 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3341/8290/viewer_manager-1.0-darwin64-503329.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3347/8316/viewer_manager-1.0-darwin64-503336.tar.bz2 name darwin64 @@ -3054,9 +3054,9 @@ archive hash - b97eb0662b558be20df9b11031d38274 + 816d310423874dc7c2efd1001b651a42 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3340/8284/viewer_manager-1.0-windows-503329.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3349/8322/viewer_manager-1.0-windows-503336.tar.bz2 name windows -- cgit v1.2.3 From 07ef9137c89bb2d1a8535553f30f6cd3a3526a60 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 14 Mar 2017 15:36:20 -0700 Subject: SL-321: add new VMP package --- autobuild.xml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 30de9a674e..b81988b938 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,21 +3042,33 @@ archive hash - 0a1efab5a2b620af2490bebaf634af44 + a052f61016dba6afce7d50fc9babd0d8 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3347/8316/viewer_manager-1.0-darwin64-503336.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3412/8596/viewer_manager-1.0-darwin64-503400.tar.bz2 name darwin64 + linux + + archive + + hash + 05ae56a3713705187ffe7307559e0a03 + url + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3411/8590/viewer_manager-1.0-linux-503400.tar.bz2 + + name + linux + windows archive hash - 816d310423874dc7c2efd1001b651a42 + 8c84007e7bd10e39c091a529a39340a9 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3349/8322/viewer_manager-1.0-windows-503336.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3413/8602/viewer_manager-1.0-windows-503400.tar.bz2 name windows -- cgit v1.2.3 From 96790c80566c3ddfb4f6ca6817bd5691e6a7186e Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 14 Mar 2017 17:40:23 -0700 Subject: SL-321: add new VMP package 503410 --- autobuild.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index b81988b938..4387740ebd 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - a052f61016dba6afce7d50fc9babd0d8 + e0375687eb78f0b3b4d30194583a871a url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3412/8596/viewer_manager-1.0-darwin64-503400.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3422/8654/viewer_manager-1.0-darwin64-503410.tar.bz2 name darwin64 @@ -3054,9 +3054,9 @@ archive hash - 05ae56a3713705187ffe7307559e0a03 + 8795127b83ead8bc99168df4a42830da url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3411/8590/viewer_manager-1.0-linux-503400.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3421/8648/viewer_manager-1.0-linux-503410.tar.bz2 name linux @@ -3066,9 +3066,9 @@ archive hash - 8c84007e7bd10e39c091a529a39340a9 + 28f00eea2162c1094408d284a4c35efa url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3413/8602/viewer_manager-1.0-windows-503400.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3423/8660/viewer_manager-1.0-windows-503410.tar.bz2 name windows -- cgit v1.2.3 From ca9935f1306d33382cc2cabc747a561d96692b61 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 14 Mar 2017 20:49:33 -0700 Subject: SL-321: add new VMP package 503417 --- autobuild.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 4387740ebd..63fac36752 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - e0375687eb78f0b3b4d30194583a871a + 1fd8b5e57be0677d28383b95ea559223 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3422/8654/viewer_manager-1.0-darwin64-503410.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3429/8692/viewer_manager-1.0-darwin64-503417.tar.bz2 name darwin64 @@ -3054,9 +3054,9 @@ archive hash - 8795127b83ead8bc99168df4a42830da + 8c7f32f85850248809ae811ba8e47d81 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3421/8648/viewer_manager-1.0-linux-503410.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3428/8686/viewer_manager-1.0-linux-503417.tar.bz2 name linux @@ -3066,9 +3066,9 @@ archive hash - 28f00eea2162c1094408d284a4c35efa + 692da90cb43d1fdc21c534facc017b81 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3423/8660/viewer_manager-1.0-windows-503410.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3430/8698/viewer_manager-1.0-windows-503417.tar.bz2 name windows -- cgit v1.2.3 From 455412c3958f85b202c9f2662695543da39e7db5 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 15 Mar 2017 09:41:13 -0700 Subject: SL-321: add new VMP package 503461 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 63fac36752..e226c452dd 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - 1fd8b5e57be0677d28383b95ea559223 + 786dc981a37eefa403ac5ad02d2700e1 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3429/8692/viewer_manager-1.0-darwin64-503417.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3473/8879/viewer_manager-1.0-darwin64-503461.tar.bz2 name darwin64 @@ -3066,9 +3066,9 @@ archive hash - 692da90cb43d1fdc21c534facc017b81 + 5fbb43e8f6aaf4dbf18d1cf501cb28b3 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3430/8698/viewer_manager-1.0-windows-503417.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3474/8885/viewer_manager-1.0-windows-503461.tar.bz2 name windows -- cgit v1.2.3 From ed5fda3271acf7917ea3ccc3aedab6f5a9401953 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 15 Mar 2017 18:49:24 -0700 Subject: SL-321: add new VMP package 50513 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index e226c452dd..59fdea21b8 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3042,9 +3042,9 @@ archive hash - 786dc981a37eefa403ac5ad02d2700e1 + db1da9e5c06338c2f37d781b42d41e54 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3473/8879/viewer_manager-1.0-darwin64-503461.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3525/9029/viewer_manager-1.0-darwin64-503513.tar.bz2 name darwin64 @@ -3066,9 +3066,9 @@ archive hash - 5fbb43e8f6aaf4dbf18d1cf501cb28b3 + a431b47b3d6fd8798ae6f99867d8f058 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3474/8885/viewer_manager-1.0-windows-503461.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3526/9035/viewer_manager-1.0-windows-503513.tar.bz2 name windows -- cgit v1.2.3 From 4992654682255a6cace1c5ee86b4b8bf28fde15d Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 16 Mar 2017 15:45:52 -0700 Subject: merge cleanup --- indra/newview/llappviewer.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 242266eac5..b4cb094877 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5500,20 +5500,6 @@ void LLAppViewer::handleLoginComplete() mSavePerAccountSettings=true; } -/** -* Check if user is running a new version of the viewer. -* Display the Release Notes if it's not overriden by the "UpdaterShowReleaseNotes" setting. -*/ -void LLAppViewer::showReleaseNotesIfRequired() -{ - if (LLVersionInfo::getChannelAndVersion() != gLastRunVersion - && gSavedSettings.getBOOL("UpdaterShowReleaseNotes") - && !gSavedSettings.getBOOL("FirstLoginThisInstall")) - { - LLSD info(getViewerInfo()); - LLWeb::loadURLInternal(info["VIEWER_RELEASE_NOTES_URL"]); - } -} //virtual void LLAppViewer::setMasterSystemAudioMute(bool mute) { -- cgit v1.2.3 From f9a7960d181e2a800b79d08a523534b6ca044e7a Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 21 Mar 2017 07:43:36 -0700 Subject: update VMP package 503604 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 350e6831cc..e480716b38 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3026,9 +3026,9 @@ archive hash - db1da9e5c06338c2f37d781b42d41e54 + 9be0f78616ad2be08fc62dfe6c399839 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3525/9029/viewer_manager-1.0-darwin64-503513.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3616/9365/viewer_manager-1.0-darwin64-503604.tar.bz2 name darwin64 @@ -3050,9 +3050,9 @@ archive hash - a431b47b3d6fd8798ae6f99867d8f058 + 27166e0af5ee896e2efbd97bd5bb8b3e url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3526/9035/viewer_manager-1.0-windows-503513.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3617/9371/viewer_manager-1.0-windows-503604.tar.bz2 name windows -- cgit v1.2.3 From 5cb34738574f6dff15a51258dbf4fc43b57099ae Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 21 Mar 2017 13:58:22 -0700 Subject: SL-321 MAINT-7192: add viewer bitness VMM fetch logic to VMP, including HD Graphcis test --- indra/newview/viewer_manifest.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index af97bb4728..770567f13d 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -121,6 +121,24 @@ class ViewerManifest(LLManifest): settings_install['CmdLineGridChoice']['Value'] = self.grid() print "Set CmdLineGridChoice in settings_install.xml to '%s'" % self.grid() + #do not need to test for existence. If no platform is passed, llmanifest computes a default in get_default_platform + #the choice of value names (lnx, mac, win32, win) is dictated by the VMM API + summary_json_platform = "" + if 'linux' in self.args['platform']: + summary_json_platform = 'lnx' + elif 'darwin' in self.args['platform']: + summary_json_platform = 'mac' + elif 'windows' in self.args['platform']: + #default case + summary_json_platform = 'win' + if 'arch' in self.args and self.args['arch']: + if 'i686' in self.args['arch']: + summary_json_platform = 'win32' + #we really shouldn't be here, something is very wrong at this point + else: + summary_json_platform = 'None' + + # put_in_file(src=) need not be an actual pathname; it # only needs to be non-empty self.put_in_file(llsd.format_pretty_xml(settings_install), @@ -184,7 +202,10 @@ class ViewerManifest(LLManifest): self.path("gpu_table.txt") #summary.json. Standard with exception handling is fine. If we can't open a new file for writing, we have worse problems - summary_dict = {"Type":"viewer","Version":'.'.join(self.args['version']),"Channel":self.channel_with_pkg_suffix()} + #platform is computed above with other arg parsing + summary_dict = {"Type":"viewer","Version":'.'.join(self.args['version']), + "Channel":self.channel_with_pkg_suffix(), + "Platform":summary_json_platform} with open(os.path.join(os.pardir,'summary.json'), 'w') as summary_handle: json.dump(summary_dict,summary_handle) -- cgit v1.2.3 From 43f5297dfaea24360b2fe11beb1e9913724ea871 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 21 Mar 2017 14:48:45 -0700 Subject: update VMP package 503633 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index e480716b38..1aebbf2b53 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3026,9 +3026,9 @@ archive hash - 9be0f78616ad2be08fc62dfe6c399839 + 519bbd83a77bfeda499b45b8a84092b3 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3616/9365/viewer_manager-1.0-darwin64-503604.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3645/9477/viewer_manager-1.0-darwin64-503633.tar.bz2 name darwin64 @@ -3050,9 +3050,9 @@ archive hash - 27166e0af5ee896e2efbd97bd5bb8b3e + b0d9c7c04078a923a6b6f47286c42d7c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3617/9371/viewer_manager-1.0-windows-503604.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3646/9483/viewer_manager-1.0-windows-503633.tar.bz2 name windows -- cgit v1.2.3 From 59a4eacb425bbae34d7213e6293c76b6861b97e3 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 22 Mar 2017 12:13:36 -0700 Subject: SL-321: upgrade to VMP package 503622 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 1aebbf2b53..9f9bdc677e 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3026,9 +3026,9 @@ archive hash - 519bbd83a77bfeda499b45b8a84092b3 + 348e055c13ce6171cba40ac2733eaa49 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3645/9477/viewer_manager-1.0-darwin64-503633.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3674/9596/viewer_manager-1.0-darwin64-503662.tar.bz2 name darwin64 @@ -3050,9 +3050,9 @@ archive hash - b0d9c7c04078a923a6b6f47286c42d7c + b4ba150897c71af585a2dd9aee61101a url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3646/9483/viewer_manager-1.0-windows-503633.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3675/9602/viewer_manager-1.0-windows-503662.tar.bz2 name windows -- cgit v1.2.3 From 7107eb8db4f703fa64a2a01392ba8ca843d9be95 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 22 Mar 2017 14:52:30 -0700 Subject: remove stray debug message --- indra/newview/llversioninfo.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index bf9d98ee91..375dce485d 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -64,7 +64,6 @@ S32 LLVersionInfo::getPatch() //static S32 LLVersionInfo::getBuild() { - std::cout << "What we have IN llversioninfo: " << LL_VIEWER_VERSION_BUILD << std::endl; return LL_VIEWER_VERSION_BUILD; } -- cgit v1.2.3 From d320d3501791bd66b7949b9650db447656e817f1 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 24 Mar 2017 13:32:57 -0700 Subject: SL-321: upgrade VMP package 503733 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 9f9bdc677e..84aed54b0e 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3026,9 +3026,9 @@ archive hash - 348e055c13ce6171cba40ac2733eaa49 + 62e8464a3d020075266e3f8dbb4e89be url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3674/9596/viewer_manager-1.0-darwin64-503662.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3745/9922/viewer_manager-1.0-darwin64-503733.tar.bz2 name darwin64 @@ -3050,9 +3050,9 @@ archive hash - b4ba150897c71af585a2dd9aee61101a + 1e5cd892526b78ba174feb65b0653d4b url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3675/9602/viewer_manager-1.0-windows-503662.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3746/9929/viewer_manager-1.0-windows-503733.tar.bz2 name windows -- cgit v1.2.3 From 7ad8cd7979f591c802a8dfcc1f5d936558dd665c Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 28 Mar 2017 09:40:29 -0700 Subject: dummy commit to bump build --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6eddb9aa9e..b80277370a 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,3 @@ To download the current default version, visit [the download page](https://secondlife.com/support/downloads). For even newer versions try [the Alternate Viewers page](https://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers) - -- cgit v1.2.3 From bf121a1e55a0475c4614946b9c87d1d0af586e2b Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 29 Mar 2017 08:25:46 -0700 Subject: dummy commit to bump build --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b80277370a..6eddb9aa9e 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,4 @@ To download the current default version, visit [the download page](https://secondlife.com/support/downloads). For even newer versions try [the Alternate Viewers page](https://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers) + -- cgit v1.2.3 From 94924e4bc5b89e235122d0c1fcdf917d04374b5a Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 29 Mar 2017 11:45:28 -0700 Subject: dummy commit to bump build (pyinstaller dev tip now in both 32 and 64) --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6eddb9aa9e..b80277370a 100644 --- a/README.md +++ b/README.md @@ -14,4 +14,3 @@ To download the current default version, visit [the download page](https://secondlife.com/support/downloads). For even newer versions try [the Alternate Viewers page](https://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers) - -- cgit v1.2.3 From c417b2637a866fb4f47bd60c201cdf24d060251e Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 29 Mar 2017 13:13:26 -0700 Subject: SL-323: cherry pick menu changes lost in v64 merge, viewer-management-process-sl-321 rev 36267 --- indra/newview/llfloaterabout.cpp | 96 ++++++++++++++++++++++ .../newview/skins/default/xui/en/notifications.xml | 5 +- 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index de2d595fb1..1b85f645e8 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -62,6 +62,7 @@ #include "llsdutil_math.h" #include "lleventapi.h" #include "llcorehttputil.h" +#include "lldir.h" #if LL_WINDOWS #include "lldxhardware.h" @@ -89,6 +90,7 @@ public: static LLSD getInfo(); void onClickCopyToClipboard(); void onClickUpdateCheck(); + void setUpdateListener(); private: void setSupportText(const std::string& server_release_notes_url); @@ -98,6 +100,9 @@ private: // callback method for manual checks static bool callbackCheckUpdate(LLSD const & event); + + // listener name for update checks + static const std::string sCheckUpdateListenerName; static void startFetchServerReleaseNotes(); static void fetchServerReleaseNotesCoro(const std::string& cap_url); @@ -131,6 +136,9 @@ BOOL LLFloaterAbout::postBuild() getChild("copy_btn")->setCommitCallback( boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); + + getChild("update_btn")->setCommitCallback( + boost::bind(&LLFloaterAbout::setUpdateListener, this)); static const LLUIColor about_color = LLUIColorTable::instance().getColor("TextFgReadOnlyColor"); @@ -326,6 +334,94 @@ void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) FALSE, LLStyle::Params() .color(about_color)); } +//This is bound as a callback in postBuild() +void LLFloaterAbout::setUpdateListener() +{ + typedef std::vector vec; + + //There are four possibilities: + //no downloads directory or version directory in "getOSUserAppDir()/downloads" + // => no update + //version directory exists and .done file is not present + // => download in progress + //version directory exists and .done file exists + // => update ready for install + //version directory, .done file and either .skip or .next file exists + // => update deferred + BOOL downloads = false; + std::string downloadDir = ""; + BOOL done = false; + BOOL next = false; + BOOL skip = false; + + LLSD info(LLFloaterAbout::getInfo()); + std::string version = info["VIEWER_VERSION_STR"].asString(); + std::string appDir = gDirUtilp->getOSUserAppDir(); + + //drop down two directory levels so we aren't searching for markers among the log files and crash dumps + //or among other possible viewer upgrade directories if the resident is running multiple viewer versions + //we should end up with a path like ../downloads/1.2.3.456789 + vec file_vec = gDirUtilp->getFilesInDir(appDir); + + for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter) + { + if ( (iter->rfind("downloads") ) ) + { + vec dir_vec = gDirUtilp->getFilesInDir(*iter); + for(vec::const_iterator dir_iter=dir_vec.begin(); dir_iter!=dir_vec.end(); ++dir_iter) + { + if ( (dir_iter->rfind(version))) + { + downloads = true; + downloadDir = *dir_iter; + } + } + } + } + + if ( downloads ) + { + for(vec::const_iterator iter=file_vec.begin(); iter!=file_vec.end(); ++iter) + { + if ( (iter->rfind(version))) + { + if ( (iter->rfind(".done") ) ) + { + done = true; + } + else if ( (iter->rfind(".next") ) ) + { + next = true; + } + else if ( (iter->rfind(".skip") ) ) + { + skip = true; + } + } + } + } + + if ( ! downloads) + { + LLNotificationsUtil::add("UpdateViewerUpToDate"); + } + else + { + if ( ! done ) + { + LLNotificationsUtil::add("UpdateDownloadInProgress"); + } + else if ( !next and !skip ) + { + LLNotificationsUtil::add("UpdateDownloadComplete"); + } + else //done and there is a next or skip + { + LLNotificationsUtil::add("UpdateDeferred"); + } + } +} + ///---------------------------------------------------------------------------- /// LLFloaterAboutUtil ///---------------------------------------------------------------------------- diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index c4190a4940..2a320c84fb 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -4029,10 +4029,9 @@ An update was downloaded. It will be installed during restart. -An error occurred while checking for update. -Please try again later. +An update was downloaded that you previously chose to skip or defer to the next start up. confirm Date: Wed, 2 Nov 2016 18:05:32 +0100 Subject: SL-323: activate viewer menu and floater checks for updates via VMP --- indra/newview/llfloaterabout.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 1b85f645e8..171858e472 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -90,7 +90,7 @@ public: static LLSD getInfo(); void onClickCopyToClipboard(); void onClickUpdateCheck(); - void setUpdateListener(); + static void setUpdateListener(); private: void setSupportText(const std::string& server_release_notes_url); @@ -138,7 +138,7 @@ BOOL LLFloaterAbout::postBuild() boost::bind(&LLFloaterAbout::onClickCopyToClipboard, this)); getChild("update_btn")->setCommitCallback( - boost::bind(&LLFloaterAbout::setUpdateListener, this)); + boost::bind(&LLFloaterAbout::onClickUpdateCheck, this)); static const LLUIColor about_color = LLUIColorTable::instance().getColor("TextFgReadOnlyColor"); @@ -314,6 +314,11 @@ void LLFloaterAbout::onClickCopyToClipboard() support_widget->deselect(); } +void LLFloaterAbout::onClickUpdateCheck() +{ + setUpdateListener(); +} + void LLFloaterAbout::setSupportText(const std::string& server_release_notes_url) { #if LL_WINDOWS @@ -401,17 +406,17 @@ void LLFloaterAbout::setUpdateListener() } } - if ( ! downloads) + if ( !downloads ) { LLNotificationsUtil::add("UpdateViewerUpToDate"); } else { - if ( ! done ) + if ( !done ) { LLNotificationsUtil::add("UpdateDownloadInProgress"); } - else if ( !next and !skip ) + else if ( (!next) && (!skip) ) { LLNotificationsUtil::add("UpdateDownloadComplete"); } @@ -429,6 +434,10 @@ void LLFloaterAboutUtil::registerFloater() { LLFloaterReg::add("sl_about", "floater_about.xml", &LLFloaterReg::build); +} +void LLFloaterAboutUtil::checkUpdatesAndNotify() +{ + LLFloaterAbout::setUpdateListener(); } -- cgit v1.2.3 From 5db5f6148aad0f5e561012f0ec9d1a62b8e54a19 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 13 Feb 2017 14:32:50 -0500 Subject: SL-409 - initial cleanup, mostly indentation fixes and removing unused or inaccessible code --- indra/llmessage/llassetstorage.cpp | 2373 +++++++++++++++----------------- indra/llmessage/llassetstorage.h | 105 +- indra/newview/llviewerassetstorage.cpp | 575 ++++---- indra/newview/llviewerassetstorage.h | 1 - 4 files changed, 1437 insertions(+), 1617 deletions(-) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index bcf4e52b8f..b85417c23f 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -60,64 +60,60 @@ static LLTrace::CountStatHandle<> sFailedDownloadCount("faileddownloads", "Numbe const LLUUID CATEGORIZE_LOST_AND_FOUND_ID(std::string("00000000-0000-0000-0000-000000000010")); -const U64 TOXIC_ASSET_LIFETIME = (120 * 1000000); // microseconds - -LLTempAssetStorage::~LLTempAssetStorage() -{ -} +const U64 TOXIC_ASSET_LIFETIME = (120 * 1000000); // microseconds ///---------------------------------------------------------------------------- /// LLAssetInfo ///---------------------------------------------------------------------------- LLAssetInfo::LLAssetInfo( void ) -: mDescription(), - mName(), - mUuid(), - mCreatorID(), - mType( LLAssetType::AT_NONE ) + : mDescription(), + mName(), + mUuid(), + mCreatorID(), + mType( LLAssetType::AT_NONE ) { } LLAssetInfo::LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id, - LLAssetType::EType type, const char* name, - const char* desc ) -: mUuid( object_id ), - mCreatorID( creator_id ), - mType( type ) + LLAssetType::EType type, const char* name, + const char* desc ) + : mUuid( object_id ), + mCreatorID( creator_id ), + mType( type ) { - setName( name ); - setDescription( desc ); + setName( name ); + setDescription( desc ); } LLAssetInfo::LLAssetInfo( const LLNameValue& nv ) { - setFromNameValue( nv ); + setFromNameValue( nv ); } // make sure the name is short enough, and strip all pipes since they // are reserved characters in our inventory tracking system. void LLAssetInfo::setName( const std::string& name ) { - if( !name.empty() ) - { - mName.assign( name, 0, llmin((U32)name.size(), (U32)DB_INV_ITEM_NAME_STR_LEN) ); - mName.erase( std::remove(mName.begin(), mName.end(), '|'), - mName.end() ); - } + if( !name.empty() ) + { + mName.assign( name, 0, llmin((U32)name.size(), (U32)DB_INV_ITEM_NAME_STR_LEN) ); + mName.erase( std::remove(mName.begin(), mName.end(), '|'), + mName.end() ); + } } // make sure the name is short enough, and strip all pipes since they // are reserved characters in our inventory tracking system. void LLAssetInfo::setDescription( const std::string& desc ) { - if( !desc.empty() ) - { - mDescription.assign( desc, 0, llmin((U32)desc.size(), - (U32)DB_INV_ITEM_DESC_STR_LEN) ); - mDescription.erase( std::remove(mDescription.begin(), - mDescription.end(), '|'), - mDescription.end() ); - } + if( !desc.empty() ) + { + mDescription.assign( desc, 0, llmin((U32)desc.size(), + (U32)DB_INV_ITEM_DESC_STR_LEN) ); + mDescription.erase( std::remove(mDescription.begin(), + mDescription.end(), '|'), + mDescription.end() ); + } } // Assets (aka potential inventory items) can be applied to an @@ -130,31 +126,31 @@ void LLAssetInfo::setDescription( const std::string& desc ) // value=||| void LLAssetInfo::setFromNameValue( const LLNameValue& nv ) { - std::string str; - std::string buf; - std::string::size_type pos1; - std::string::size_type pos2; - - // convert the name to useful information - str.assign( nv.mName ); - pos1 = str.find('|'); - buf.assign( str, 0, pos1++ ); - mType = LLAssetType::lookup( buf ); - buf.assign( str, pos1, std::string::npos ); - mUuid.set( buf ); - - // convert the value to useful information - str.assign( nv.getAsset() ); - pos1 = str.find('|'); - buf.assign( str, 0, pos1++ ); - mCreatorID.set( buf ); - pos2 = str.find( '|', pos1 ); - buf.assign( str, pos1, (pos2++) - pos1 ); - setName( buf ); - buf.assign( str, pos2, std::string::npos ); - setDescription( buf ); - LL_DEBUGS("AssetStorage") << "uuid: " << mUuid << LL_ENDL; - LL_DEBUGS("AssetStorage") << "creator: " << mCreatorID << LL_ENDL; + std::string str; + std::string buf; + std::string::size_type pos1; + std::string::size_type pos2; + + // convert the name to useful information + str.assign( nv.mName ); + pos1 = str.find('|'); + buf.assign( str, 0, pos1++ ); + mType = LLAssetType::lookup( buf ); + buf.assign( str, pos1, std::string::npos ); + mUuid.set( buf ); + + // convert the value to useful information + str.assign( nv.getAsset() ); + pos1 = str.find('|'); + buf.assign( str, 0, pos1++ ); + mCreatorID.set( buf ); + pos2 = str.find( '|', pos1 ); + buf.assign( str, pos1, (pos2++) - pos1 ); + setName( buf ); + buf.assign( str, pos2, std::string::npos ); + setDescription( buf ); + LL_DEBUGS("AssetStorage") << "uuid: " << mUuid << LL_ENDL; + LL_DEBUGS("AssetStorage") << "creator: " << mCreatorID << LL_ENDL; } ///---------------------------------------------------------------------------- @@ -162,15 +158,15 @@ void LLAssetInfo::setFromNameValue( const LLNameValue& nv ) ///---------------------------------------------------------------------------- LLBaseDownloadRequest::LLBaseDownloadRequest(const LLUUID &uuid, const LLAssetType::EType type) -: mUUID(uuid), -mType(type), -mDownCallback(NULL), -mUserData(NULL), -mHost(), -mIsTemp(FALSE), -mIsPriority(FALSE), -mDataSentInFirstPacket(FALSE), -mDataIsInVFS(FALSE) + : mUUID(uuid), + mType(type), + mDownCallback(NULL), + mUserData(NULL), + mHost(), + mIsTemp(FALSE), + mIsPriority(FALSE), + mDataSentInFirstPacket(FALSE), + mDataIsInVFS(FALSE) { // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been // running a message system loop. @@ -194,12 +190,12 @@ LLBaseDownloadRequest* LLBaseDownloadRequest::getCopy() ///---------------------------------------------------------------------------- LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type) -: LLBaseDownloadRequest(uuid, type), - mUpCallback( NULL ), - mInfoCallback( NULL ), - mIsLocal(FALSE), - mIsUserWaiting(FALSE), - mTimeout(LL_ASSET_STORAGE_TIMEOUT) + : LLBaseDownloadRequest(uuid, type), + mUpCallback( NULL ), + mInfoCallback( NULL ), + mIsLocal(FALSE), + mIsUserWaiting(FALSE), + mTimeout(LL_ASSET_STORAGE_TIMEOUT) { } @@ -211,31 +207,31 @@ LLAssetRequest::~LLAssetRequest() // virtual LLSD LLAssetRequest::getTerseDetails() const { - LLSD sd; - sd["asset_id"] = getUUID(); - sd["type_long"] = LLAssetType::lookupHumanReadable(getType()); - sd["type"] = LLAssetType::lookup(getType()); - sd["time"] = mTime.value(); - time_t timestamp = (time_t) mTime.value(); - std::ostringstream time_string; - time_string << ctime(×tamp); - sd["time_string"] = time_string.str(); - return sd; + LLSD sd; + sd["asset_id"] = getUUID(); + sd["type_long"] = LLAssetType::lookupHumanReadable(getType()); + sd["type"] = LLAssetType::lookup(getType()); + sd["time"] = mTime.value(); + time_t timestamp = (time_t) mTime.value(); + std::ostringstream time_string; + time_string << ctime(×tamp); + sd["time_string"] = time_string.str(); + return sd; } // virtual LLSD LLAssetRequest::getFullDetails() const { - LLSD sd = getTerseDetails(); - sd["host"] = mHost.getIPandPort(); - sd["requesting_agent"] = mRequestingAgentID; - sd["is_temp"] = mIsTemp; - sd["is_local"] = mIsLocal; - sd["is_priority"] = mIsPriority; - sd["data_send_in_first_packet"] = mDataSentInFirstPacket; - sd["data_is_in_vfs"] = mDataIsInVFS; - - return sd; + LLSD sd = getTerseDetails(); + sd["host"] = mHost.getIPandPort(); + sd["requesting_agent"] = mRequestingAgentID; + sd["is_temp"] = mIsTemp; + sd["is_local"] = mIsLocal; + sd["is_priority"] = mIsPriority; + sd["data_send_in_first_packet"] = mDataSentInFirstPacket; + sd["data_is_in_vfs"] = mDataIsInVFS; + + return sd; } LLBaseDownloadRequest* LLAssetRequest::getCopy() @@ -248,7 +244,7 @@ LLBaseDownloadRequest* LLAssetRequest::getCopy() ///---------------------------------------------------------------------------- LLInvItemRequest::LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType type) -: LLBaseDownloadRequest(uuid, type) + : LLBaseDownloadRequest(uuid, type) { } @@ -267,9 +263,9 @@ LLBaseDownloadRequest* LLInvItemRequest::getCopy() ///---------------------------------------------------------------------------- LLEstateAssetRequest::LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType atype, - EstateAssetType etype) -: LLBaseDownloadRequest(uuid, atype), - mEstateAssetType(etype) + EstateAssetType etype) + : LLBaseDownloadRequest(uuid, atype), + mEstateAssetType(etype) { } @@ -299,152 +295,150 @@ LLBaseDownloadRequest* LLEstateAssetRequest::getCopy() LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host) { - _init(msg, xfer, vfs, static_vfs, upstream_host); + _init(msg, xfer, vfs, static_vfs, upstream_host); } - LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs) + LLVFS *vfs, LLVFS *static_vfs) { - _init(msg, xfer, vfs, static_vfs, LLHost()); + _init(msg, xfer, vfs, static_vfs, LLHost()); } - void LLAssetStorage::_init(LLMessageSystem *msg, - LLXferManager *xfer, - LLVFS *vfs, - LLVFS *static_vfs, - const LLHost &upstream_host) + LLXferManager *xfer, + LLVFS *vfs, + LLVFS *static_vfs, + const LLHost &upstream_host) { - mShutDown = FALSE; - mMessageSys = msg; - mXferManager = xfer; - mVFS = vfs; - mStaticVFS = static_vfs; - - setUpstream(upstream_host); - msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this); + mShutDown = FALSE; + mMessageSys = msg; + mXferManager = xfer; + mVFS = vfs; + mStaticVFS = static_vfs; + + setUpstream(upstream_host); + msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this); } LLAssetStorage::~LLAssetStorage() { - mShutDown = TRUE; - - _cleanupRequests(TRUE, LL_ERR_CIRCUIT_GONE); - - if (gMessageSystem) - { - // Warning! This won't work if there's more than one asset storage. - // unregister our callbacks with the message system - gMessageSystem->setHandlerFuncFast(_PREHASH_AssetUploadComplete, NULL, NULL); - } - - // Clear the toxic asset map - mToxicAssetMap.clear(); + mShutDown = TRUE; + + _cleanupRequests(TRUE, LL_ERR_CIRCUIT_GONE); + + if (gMessageSystem) + { + // Warning! This won't work if there's more than one asset storage. + // unregister our callbacks with the message system + gMessageSystem->setHandlerFuncFast(_PREHASH_AssetUploadComplete, NULL, NULL); + } + + // Clear the toxic asset map + mToxicAssetMap.clear(); } void LLAssetStorage::setUpstream(const LLHost &upstream_host) { - LL_DEBUGS("AppInit") << "AssetStorage: Setting upstream provider to " << upstream_host << LL_ENDL; - - mUpstreamHost = upstream_host; + LL_DEBUGS("AppInit") << "AssetStorage: Setting upstream provider to " << upstream_host << LL_ENDL; + + mUpstreamHost = upstream_host; } void LLAssetStorage::checkForTimeouts() { - _cleanupRequests(FALSE, LL_ERR_TCP_TIMEOUT); + _cleanupRequests(FALSE, LL_ERR_TCP_TIMEOUT); } void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) { - F64Seconds mt_secs = LLMessageSystem::getMessageTimeSeconds(); - - request_list_t timed_out; - S32 rt; - for (rt = 0; rt < RT_COUNT; rt++) - { - request_list_t* requests = getRequestList((ERequestType)rt); - for (request_list_t::iterator iter = requests->begin(); - iter != requests->end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* tmp = *curiter; - // if all is true, we want to clean up everything - // otherwise just check for timed out requests - // EXCEPT for upload timeouts - if (all - || ((RT_DOWNLOAD == rt) - && LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime))) - { - LL_WARNS() << "Asset " << getRequestName((ERequestType)rt) << " request " - << (all ? "aborted" : "timed out") << " for " - << tmp->getUUID() << "." - << LLAssetType::lookup(tmp->getType()) << LL_ENDL; - - timed_out.push_front(tmp); - iter = requests->erase(curiter); - } - } - } - - LLAssetInfo info; - for (request_list_t::iterator iter = timed_out.begin(); - iter != timed_out.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* tmp = *curiter; - if (tmp->mUpCallback) - { - tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error, LL_EXSTAT_NONE); - } - if (tmp->mDownCallback) - { - tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LL_EXSTAT_NONE); - } - if (tmp->mInfoCallback) - { - tmp->mInfoCallback(&info, tmp->mUserData, error); - } - delete tmp; - } + F64Seconds mt_secs = LLMessageSystem::getMessageTimeSeconds(); + + request_list_t timed_out; + S32 rt; + for (rt = 0; rt < RT_COUNT; rt++) + { + request_list_t* requests = getRequestList((ERequestType)rt); + for (request_list_t::iterator iter = requests->begin(); + iter != requests->end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* tmp = *curiter; + // if all is true, we want to clean up everything + // otherwise just check for timed out requests + // EXCEPT for upload timeouts + if (all + || ((RT_DOWNLOAD == rt) + && LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime))) + { + LL_WARNS() << "Asset " << getRequestName((ERequestType)rt) << " request " + << (all ? "aborted" : "timed out") << " for " + << tmp->getUUID() << "." + << LLAssetType::lookup(tmp->getType()) << LL_ENDL; + + timed_out.push_front(tmp); + iter = requests->erase(curiter); + } + } + } + + LLAssetInfo info; + for (request_list_t::iterator iter = timed_out.begin(); + iter != timed_out.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* tmp = *curiter; + if (tmp->mUpCallback) + { + tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error, LL_EXSTAT_NONE); + } + if (tmp->mDownCallback) + { + tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error, LL_EXSTAT_NONE); + } + if (tmp->mInfoCallback) + { + tmp->mInfoCallback(&info, tmp->mUserData, error); + } + delete tmp; + } } BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type) { - return mStaticVFS->getExists(uuid, type) || mVFS->getExists(uuid, type); + return mStaticVFS->getExists(uuid, type) || mVFS->getExists(uuid, type); } bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, - LLGetAssetCallback callback, void *user_data) -{ - if (user_data) - { - // The *user_data should not be passed without a callback to clean it up. - llassert(callback != NULL); - } - - BOOL exists = mStaticVFS->getExists(uuid, type); - if (exists) - { - LLVFile file(mStaticVFS, uuid, type); - U32 size = file.getSize(); - if (size > 0) - { - // we've already got the file - if (callback) - { - callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); - } - return true; - } - else - { - LL_WARNS() << "Asset vfile " << uuid << ":" << type - << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL; - } - } - return false; + LLGetAssetCallback callback, void *user_data) +{ + if (user_data) + { + // The *user_data should not be passed without a callback to clean it up. + llassert(callback != NULL); + } + + BOOL exists = mStaticVFS->getExists(uuid, type); + if (exists) + { + LLVFile file(mStaticVFS, uuid, type); + U32 size = file.getSize(); + if (size > 0) + { + // we've already got the file + if (callback) + { + callback(mStaticVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + } + return true; + } + else + { + LL_WARNS() << "Asset vfile " << uuid << ":" << type + << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL; + } + } + return false; } /////////////////////////////////////////////////////////////////////////// @@ -452,526 +446,497 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse /////////////////////////////////////////////////////////////////////////// // IW - uuid is passed by value to avoid side effects, please don't re-add & -void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data, BOOL is_priority) -{ - LL_DEBUGS("AssetStorage") << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << LL_ENDL; - - LL_DEBUGS("AssetStorage") << "ASSET_TRACE requesting " << uuid << " type " << LLAssetType::lookup(type) << LL_ENDL; - - if (user_data) - { - // The *user_data should not be passed without a callback to clean it up. - llassert(callback != NULL); - } - - if (mShutDown) - { - LL_DEBUGS("AssetStorage") << "ASSET_TRACE cancelled " << uuid << " type " << LLAssetType::lookup(type) << " shutting down" << LL_ENDL; - - if (callback) - { - add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_NONE); - } - return; - } - - if (uuid.isNull()) - { - // Special case early out for NULL uuid and for shutting down - if (callback) - { - add(sFailedDownloadCount, 1); - callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID); - } - return; - } - - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback(uuid,type,callback,user_data)) - { - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in static VFS" << LL_ENDL; - return; - } - - BOOL exists = mVFS->getExists(uuid, type); - LLVFile file(mVFS, uuid, type); - U32 size = exists ? file.getSize() : 0; - - if (size > 0) - { - // we've already got the file - // theoretically, partial files w/o a pending request shouldn't happen - // unless there's a weird error - if (callback) - { - callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); - } - - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in VFS" << LL_ENDL; - } - else - { - if (exists) - { - LL_WARNS() << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; - file.remove(); - } - - BOOL duplicate = FALSE; - - // check to see if there's a pending download of this uuid already - for (request_list_t::iterator iter = mPendingDownloads.begin(); - iter != mPendingDownloads.end(); ++iter ) - { - LLAssetRequest *tmp = *iter; - if ((type == tmp->getType()) && (uuid == tmp->getUUID())) - { - if (callback == tmp->mDownCallback && user_data == tmp->mUserData) - { - // this is a duplicate from the same subsystem - throw it away - LL_WARNS() << "Discarding duplicate request for asset " << uuid - << "." << LLAssetType::lookup(type) << LL_ENDL; - return; - } - - // this is a duplicate request - // queue the request, but don't actually ask for it again - duplicate = TRUE; - } - } - if (duplicate) - { - LL_DEBUGS("AssetStorage") << "Adding additional non-duplicate request for asset " << uuid - << "." << LLAssetType::lookup(type) << LL_ENDL; - } - - // This can be overridden by subclasses - _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority); - } +void LLAssetStorage::getAssetData(const LLUUID uuid, + LLAssetType::EType type, + LLGetAssetCallback callback, + void *user_data, + BOOL is_priority) +{ + LL_DEBUGS("AssetStorage") << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << LL_ENDL; + + LL_DEBUGS("AssetStorage") << "ASSET_TRACE requesting " << uuid << " type " << LLAssetType::lookup(type) << LL_ENDL; + + if (user_data) + { + // The *user_data should not be passed without a callback to clean it up. + llassert(callback != NULL); + } + + if (mShutDown) + { + LL_DEBUGS("AssetStorage") << "ASSET_TRACE cancelled " << uuid << " type " << LLAssetType::lookup(type) << " shutting down" << LL_ENDL; + + if (callback) + { + add(sFailedDownloadCount, 1); + callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_NONE); + } + return; + } + + if (uuid.isNull()) + { + // Special case early out for NULL uuid and for shutting down + if (callback) + { + add(sFailedDownloadCount, 1); + callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID); + } + return; + } + + // Try static VFS first. + if (findInStaticVFSAndInvokeCallback(uuid,type,callback,user_data)) + { + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in static VFS" << LL_ENDL; + return; + } + + BOOL exists = mVFS->getExists(uuid, type); + LLVFile file(mVFS, uuid, type); + U32 size = exists ? file.getSize() : 0; + + if (size > 0) + { + // we've already got the file + // theoretically, partial files w/o a pending request shouldn't happen + // unless there's a weird error + if (callback) + { + callback(mVFS, uuid, type, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + } + + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << uuid << " found in VFS" << LL_ENDL; + } + else + { + if (exists) + { + LL_WARNS() << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; + file.remove(); + } + + BOOL duplicate = FALSE; + + // check to see if there's a pending download of this uuid already + for (request_list_t::iterator iter = mPendingDownloads.begin(); + iter != mPendingDownloads.end(); ++iter ) + { + LLAssetRequest *tmp = *iter; + if ((type == tmp->getType()) && (uuid == tmp->getUUID())) + { + if (callback == tmp->mDownCallback && user_data == tmp->mUserData) + { + // this is a duplicate from the same subsystem - throw it away + LL_WARNS() << "Discarding duplicate request for asset " << uuid + << "." << LLAssetType::lookup(type) << LL_ENDL; + return; + } + + // this is a duplicate request + // queue the request, but don't actually ask for it again + duplicate = TRUE; + } + } + if (duplicate) + { + LL_DEBUGS("AssetStorage") << "Adding additional non-duplicate request for asset " << uuid + << "." << LLAssetType::lookup(type) << LL_ENDL; + } + + // This can be overridden by subclasses + _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority); + } } -// -// *NOTE: Logic here is replicated in LLViewerAssetStorage::_queueDataRequest. -// Changes here may need to be replicated in the viewer's derived class. -// -void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype, - LLGetAssetCallback callback, - void *user_data, BOOL duplicate, - BOOL is_priority) -{ - if (mUpstreamHost.isOk()) - { - // stash the callback info so we can find it after we get the response message - LLAssetRequest *req = new LLAssetRequest(uuid, atype); - req->mDownCallback = callback; - req->mUserData = user_data; - req->mIsPriority = is_priority; - - mPendingDownloads.push_back(req); - - if (!duplicate) - { - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsAsset spa; - spa.setAsset(uuid, atype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(uuid, atype); - tpvf.setCallback(downloadCompleteCallback, *req); - - //LL_INFOS() << "Starting transfer for " << uuid << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); - ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - } - } - else - { - // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; - if (callback) - { - add(sFailedDownloadCount, 1); - callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); - } - } -} - - void LLAssetStorage::downloadCompleteCallback( - S32 result, - const LLUUID& file_id, - LLAssetType::EType file_type, - LLBaseDownloadRequest* user_data, LLExtStat ext_status) -{ - LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << file_id << " downloadCompleteCallback" << LL_ENDL; - - LL_DEBUGS("AssetStorage") << "LLAssetStorage::downloadCompleteCallback() for " << file_id - << "," << LLAssetType::lookup(file_type) << LL_ENDL; - LLAssetRequest* req = (LLAssetRequest*)user_data; - if(!req) - { - LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without" - "a valid request." << LL_ENDL; - return; - } - if (!gAssetStorage) - { - LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; - return; - } - - LLUUID callback_id; - LLAssetType::EType callback_type; - - // Inefficient since we're doing a find through a list that may have thousands of elements. - // This is due for refactoring; we will probably change mPendingDownloads into a set. - request_list_t::iterator download_iter = std::find(gAssetStorage->mPendingDownloads.begin(), - gAssetStorage->mPendingDownloads.end(), - req); - - if (download_iter != gAssetStorage->mPendingDownloads.end()) - { - callback_id = file_id; - callback_type = file_type; - } - else - { - // either has already been deleted by _cleanupRequests or it's a transfer. - callback_id = req->getUUID(); - callback_type = req->getType(); - } - - if (LL_ERR_NOERR == result) - { - // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, callback_id, callback_type); - if (vfile.getSize() <= 0) - { - LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; - - result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; - vfile.remove(); - } - } - - // find and callback ALL pending requests for this UUID - // SJB: We process the callbacks in reverse order, I do not know if this is important, - // but I didn't want to mess with it. - request_list_t requests; - for (request_list_t::iterator iter = gAssetStorage->mPendingDownloads.begin(); - iter != gAssetStorage->mPendingDownloads.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* tmp = *curiter; - if ((tmp->getUUID() == file_id) && (tmp->getType()== file_type)) - { - requests.push_front(tmp); - iter = gAssetStorage->mPendingDownloads.erase(curiter); - } - } - for (request_list_t::iterator iter = requests.begin(); - iter != requests.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* tmp = *curiter; - if (tmp->mDownCallback) - { - if (result != LL_ERR_NOERR) - { - add(sFailedDownloadCount, 1); - } - tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result, ext_status); - } - delete tmp; - } -} - -void LLAssetStorage::getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id, - const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype, - LLGetAssetCallback callback, void *user_data, BOOL is_priority) -{ - LL_DEBUGS() << "LLAssetStorage::getEstateAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << ", estatetype " << etype << LL_ENDL; - - // - // Probably will get rid of this early out? - // - if (asset_id.isNull()) - { - // Special case early out for NULL uuid - if (callback) - { - add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID); - } - return; - } - - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback(asset_id,atype,callback,user_data)) - { - return; - } - - BOOL exists = mVFS->getExists(asset_id, atype); - LLVFile file(mVFS, asset_id, atype); - U32 size = exists ? file.getSize() : 0; - - if (size > 0) - { - // we've already got the file - // theoretically, partial files w/o a pending request shouldn't happen - // unless there's a weird error - if (callback) - { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); - } - } - else - { - if (exists) - { - LL_WARNS() << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; - file.remove(); - } - - // See whether we should talk to the object's originating sim, or the upstream provider. - LLHost source_host; - if (object_sim.isOk()) - { - source_host = object_sim; - } - else - { - source_host = mUpstreamHost; - } - if (source_host.isOk()) - { - // stash the callback info so we can find it after we get the response message - LLEstateAssetRequest req(asset_id, atype, etype); - req.mDownCallback = callback; - req.mUserData = user_data; - req.mIsPriority = is_priority; - - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsEstate spe; - spe.setAgentSession(agent_id, session_id); - spe.setEstateAssetType(etype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(asset_id, atype); - tpvf.setCallback(downloadEstateAssetCompleteCallback, req); - - LL_DEBUGS("AssetStorage") << "Starting transfer for " << asset_id << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET); - ttcp->requestTransfer(spe, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - } - else - { - // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; - if (callback) - { - add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); - } - } - } + S32 result, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, + LLExtStat ext_status) +{ + LL_DEBUGS("AssetStorage") << "ASSET_TRACE asset " << file_id << " downloadCompleteCallback" << LL_ENDL; + + LL_DEBUGS("AssetStorage") << "LLAssetStorage::downloadCompleteCallback() for " << file_id + << "," << LLAssetType::lookup(file_type) << LL_ENDL; + LLAssetRequest* req = (LLAssetRequest*)user_data; + if(!req) + { + LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without" + "a valid request." << LL_ENDL; + return; + } + if (!gAssetStorage) + { + LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; + return; + } + + LLUUID callback_id; + LLAssetType::EType callback_type; + + // Inefficient since we're doing a find through a list that may have thousands of elements. + // This is due for refactoring; we will probably change mPendingDownloads into a set. + request_list_t::iterator download_iter = std::find(gAssetStorage->mPendingDownloads.begin(), + gAssetStorage->mPendingDownloads.end(), + req); + + if (download_iter != gAssetStorage->mPendingDownloads.end()) + { + callback_id = file_id; + callback_type = file_type; + } + else + { + // either has already been deleted by _cleanupRequests or it's a transfer. + callback_id = req->getUUID(); + callback_type = req->getType(); + } + + if (LL_ERR_NOERR == result) + { + // we might have gotten a zero-size file + LLVFile vfile(gAssetStorage->mVFS, callback_id, callback_type); + if (vfile.getSize() <= 0) + { + LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; + + result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; + vfile.remove(); + } + } + + // find and callback ALL pending requests for this UUID + // SJB: We process the callbacks in reverse order, I do not know if this is important, + // but I didn't want to mess with it. + request_list_t requests; + for (request_list_t::iterator iter = gAssetStorage->mPendingDownloads.begin(); + iter != gAssetStorage->mPendingDownloads.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* tmp = *curiter; + if ((tmp->getUUID() == file_id) && (tmp->getType()== file_type)) + { + requests.push_front(tmp); + iter = gAssetStorage->mPendingDownloads.erase(curiter); + } + } + for (request_list_t::iterator iter = requests.begin(); + iter != requests.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* tmp = *curiter; + if (tmp->mDownCallback) + { + if (result != LL_ERR_NOERR) + { + add(sFailedDownloadCount, 1); + } + tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result, ext_status); + } + delete tmp; + } +} + +void LLAssetStorage::getEstateAsset( + const LLHost &object_sim, + const LLUUID &agent_id, + const LLUUID &session_id, + const LLUUID &asset_id, + LLAssetType::EType atype, + EstateAssetType etype, + LLGetAssetCallback callback, + void *user_data, + BOOL is_priority) +{ + LL_DEBUGS() << "LLAssetStorage::getEstateAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << ", estatetype " << etype << LL_ENDL; + + // + // Probably will get rid of this early out? + // + if (asset_id.isNull()) + { + // Special case early out for NULL uuid + if (callback) + { + add(sFailedDownloadCount, 1); + callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE, LL_EXSTAT_NULL_UUID); + } + return; + } + + // Try static VFS first. + if (findInStaticVFSAndInvokeCallback(asset_id,atype,callback,user_data)) + { + return; + } + + BOOL exists = mVFS->getExists(asset_id, atype); + LLVFile file(mVFS, asset_id, atype); + U32 size = exists ? file.getSize() : 0; + + if (size > 0) + { + // we've already got the file + // theoretically, partial files w/o a pending request shouldn't happen + // unless there's a weird error + if (callback) + { + callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + } + } + else + { + if (exists) + { + LL_WARNS() << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; + file.remove(); + } + + // See whether we should talk to the object's originating sim, or the upstream provider. + LLHost source_host; + if (object_sim.isOk()) + { + source_host = object_sim; + } + else + { + source_host = mUpstreamHost; + } + if (source_host.isOk()) + { + // stash the callback info so we can find it after we get the response message + LLEstateAssetRequest req(asset_id, atype, etype); + req.mDownCallback = callback; + req.mUserData = user_data; + req.mIsPriority = is_priority; + + // send request message to our upstream data provider + // Create a new asset transfer. + LLTransferSourceParamsEstate spe; + spe.setAgentSession(agent_id, session_id); + spe.setEstateAssetType(etype); + + // Set our destination file, and the completion callback. + LLTransferTargetParamsVFile tpvf; + tpvf.setAsset(asset_id, atype); + tpvf.setCallback(downloadEstateAssetCompleteCallback, req); + + LL_DEBUGS("AssetStorage") << "Starting transfer for " << asset_id << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET); + ttcp->requestTransfer(spe, tpvf, 100.f + (is_priority ? 1.f : 0.f)); + } + else + { + // uh-oh, we shouldn't have gotten here + LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; + if (callback) + { + add(sFailedDownloadCount, 1); + callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + } + } + } } void LLAssetStorage::downloadEstateAssetCompleteCallback( - S32 result, - const LLUUID& file_id, - LLAssetType::EType file_type, - LLBaseDownloadRequest* user_data, - LLExtStat ext_status) -{ - LLEstateAssetRequest *req = (LLEstateAssetRequest*)user_data; - if(!req) - { - LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" - " without a valid request." << LL_ENDL; - return; - } - if (!gAssetStorage) - { - LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" - " without any asset system, aborting!" << LL_ENDL; - return; - } - - req->setUUID(file_id); - req->setType(file_type); - if (LL_ERR_NOERR == result) - { - // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType()); - if (vfile.getSize() <= 0) - { - LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; - - result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; - vfile.remove(); - } - } - - if (result != LL_ERR_NOERR) - { - add(sFailedDownloadCount, 1); - } - req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result, ext_status); -} - -void LLAssetStorage::getInvItemAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id, - const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id, - const LLUUID &asset_id, LLAssetType::EType atype, - LLGetAssetCallback callback, void *user_data, BOOL is_priority) -{ - LL_DEBUGS() << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << LL_ENDL; - - // - // Probably will get rid of this early out? - // - //if (asset_id.isNull()) - //{ - // // Special case early out for NULL uuid - // if (callback) - // { - // callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE); - // } - // return; - //} - - bool exists = false; - U32 size = 0; - - if(asset_id.notNull()) - { - // Try static VFS first. - if (findInStaticVFSAndInvokeCallback( asset_id, atype, callback, user_data)) - { - return; - } - - exists = mVFS->getExists(asset_id, atype); - LLVFile file(mVFS, asset_id, atype); - size = exists ? file.getSize() : 0; - if(exists && size < 1) - { - LL_WARNS() << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; - file.remove(); - } - - } - - if (size > 0) - { - // we've already got the file - // theoretically, partial files w/o a pending request shouldn't happen - // unless there's a weird error - if (callback) - { - callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); - } - } - else - { - // See whether we should talk to the object's originating sim, - // or the upstream provider. - LLHost source_host; - if (object_sim.isOk()) - { - source_host = object_sim; - } - else - { - source_host = mUpstreamHost; - } - if (source_host.isOk()) - { - // stash the callback info so we can find it after we get the response message - LLInvItemRequest req(asset_id, atype); - req.mDownCallback = callback; - req.mUserData = user_data; - req.mIsPriority = is_priority; - - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsInvItem spi; - spi.setAgentSession(agent_id, session_id); - spi.setInvItem(owner_id, task_id, item_id); - spi.setAsset(asset_id, atype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(asset_id, atype); - tpvf.setCallback(downloadInvItemCompleteCallback, req); - - LL_DEBUGS("AssetStorage") << "Starting transfer for inventory asset " - << item_id << " owned by " << owner_id << "," << task_id - << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET); - ttcp->requestTransfer(spi, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - } - else - { - // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; - if (callback) - { - add(sFailedDownloadCount, 1); - callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); - } - } - } + S32 result, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, + LLExtStat ext_status) +{ + LLEstateAssetRequest *req = (LLEstateAssetRequest*)user_data; + if(!req) + { + LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" + " without a valid request." << LL_ENDL; + return; + } + if (!gAssetStorage) + { + LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" + " without any asset system, aborting!" << LL_ENDL; + return; + } + + req->setUUID(file_id); + req->setType(file_type); + if (LL_ERR_NOERR == result) + { + // we might have gotten a zero-size file + LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType()); + if (vfile.getSize() <= 0) + { + LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; + + result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; + vfile.remove(); + } + } + + if (result != LL_ERR_NOERR) + { + add(sFailedDownloadCount, 1); + } + req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result, ext_status); +} + +void LLAssetStorage::getInvItemAsset( + const LLHost &object_sim, + const LLUUID &agent_id, + const LLUUID &session_id, + const LLUUID &owner_id, + const LLUUID &task_id, + const LLUUID &item_id, + const LLUUID &asset_id, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data, + BOOL is_priority) +{ + LL_DEBUGS() << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << LL_ENDL; + + // + // Probably will get rid of this early out? + // + //if (asset_id.isNull()) + //{ + // // Special case early out for NULL uuid + // if (callback) + // { + // callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE); + // } + // return; + //} + + bool exists = false; + U32 size = 0; + + if(asset_id.notNull()) + { + // Try static VFS first. + if (findInStaticVFSAndInvokeCallback( asset_id, atype, callback, user_data)) + { + return; + } + + exists = mVFS->getExists(asset_id, atype); + LLVFile file(mVFS, asset_id, atype); + size = exists ? file.getSize() : 0; + if(exists && size < 1) + { + LL_WARNS() << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; + file.remove(); + } + + } + + if (size > 0) + { + // we've already got the file + // theoretically, partial files w/o a pending request shouldn't happen + // unless there's a weird error + if (callback) + { + callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR, LL_EXSTAT_VFS_CACHED); + } + } + else + { + // See whether we should talk to the object's originating sim, + // or the upstream provider. + LLHost source_host; + if (object_sim.isOk()) + { + source_host = object_sim; + } + else + { + source_host = mUpstreamHost; + } + if (source_host.isOk()) + { + // stash the callback info so we can find it after we get the response message + LLInvItemRequest req(asset_id, atype); + req.mDownCallback = callback; + req.mUserData = user_data; + req.mIsPriority = is_priority; + + // send request message to our upstream data provider + // Create a new asset transfer. + LLTransferSourceParamsInvItem spi; + spi.setAgentSession(agent_id, session_id); + spi.setInvItem(owner_id, task_id, item_id); + spi.setAsset(asset_id, atype); + + // Set our destination file, and the completion callback. + LLTransferTargetParamsVFile tpvf; + tpvf.setAsset(asset_id, atype); + tpvf.setCallback(downloadInvItemCompleteCallback, req); + + LL_DEBUGS("AssetStorage") << "Starting transfer for inventory asset " + << item_id << " owned by " << owner_id << "," << task_id + << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET); + ttcp->requestTransfer(spi, tpvf, 100.f + (is_priority ? 1.f : 0.f)); + } + else + { + // uh-oh, we shouldn't have gotten here + LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; + if (callback) + { + add(sFailedDownloadCount, 1); + callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + } + } + } } void LLAssetStorage::downloadInvItemCompleteCallback( - S32 result, - const LLUUID& file_id, - LLAssetType::EType file_type, - LLBaseDownloadRequest* user_data, - LLExtStat ext_status) -{ - LLInvItemRequest *req = (LLInvItemRequest*)user_data; - if(!req) - { - LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" - " without a valid request." << LL_ENDL; - return; - } - if (!gAssetStorage) - { - LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; - return; - } - - req->setUUID(file_id); - req->setType(file_type); - if (LL_ERR_NOERR == result) - { - // we might have gotten a zero-size file - LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType()); - if (vfile.getSize() <= 0) - { - LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; - - result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; - vfile.remove(); - } - } - - if (result != LL_ERR_NOERR) - { - add(sFailedDownloadCount, 1); - } - req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result, ext_status); + S32 result, + const LLUUID& file_id, + LLAssetType::EType file_type, + LLBaseDownloadRequest* user_data, + LLExtStat ext_status) +{ + LLInvItemRequest *req = (LLInvItemRequest*)user_data; + if(!req) + { + LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" + " without a valid request." << LL_ENDL; + return; + } + if (!gAssetStorage) + { + LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; + return; + } + + req->setUUID(file_id); + req->setType(file_type); + if (LL_ERR_NOERR == result) + { + // we might have gotten a zero-size file + LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType()); + if (vfile.getSize() <= 0) + { + LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; + + result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; + vfile.remove(); + } + } + + if (result != LL_ERR_NOERR) + { + add(sFailedDownloadCount, 1); + } + req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result, ext_status); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -979,654 +944,558 @@ void LLAssetStorage::downloadInvItemCompleteCallback( ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // static -void LLAssetStorage::uploadCompleteCallback(const LLUUID& uuid, void *user_data, S32 result, LLExtStat ext_status) // StoreAssetData callback (fixed) -{ - if (!gAssetStorage) - { - LL_WARNS() << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << LL_ENDL; - return; - } - LLAssetRequest *req = (LLAssetRequest *)user_data; - BOOL success = TRUE; - - if (result) - { - LL_WARNS() << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << LL_ENDL; - success = FALSE; - } - - // we're done grabbing the file, tell the client - gAssetStorage->mMessageSys->newMessageFast(_PREHASH_AssetUploadComplete); - gAssetStorage->mMessageSys->nextBlockFast(_PREHASH_AssetBlock); - gAssetStorage->mMessageSys->addUUIDFast(_PREHASH_UUID, uuid); - gAssetStorage->mMessageSys->addS8Fast(_PREHASH_Type, req->getType()); - gAssetStorage->mMessageSys->addBOOLFast(_PREHASH_Success, success); - gAssetStorage->mMessageSys->sendReliable(req->mHost); - - delete req; +void LLAssetStorage::uploadCompleteCallback( + const LLUUID& uuid, + void *user_data, + S32 result, + LLExtStat ext_status) // StoreAssetData callback (fixed) +{ + if (!gAssetStorage) + { + LL_WARNS() << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << LL_ENDL; + return; + } + LLAssetRequest *req = (LLAssetRequest *)user_data; + BOOL success = TRUE; + + if (result) + { + LL_WARNS() << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << LL_ENDL; + success = FALSE; + } + + // we're done grabbing the file, tell the client + gAssetStorage->mMessageSys->newMessageFast(_PREHASH_AssetUploadComplete); + gAssetStorage->mMessageSys->nextBlockFast(_PREHASH_AssetBlock); + gAssetStorage->mMessageSys->addUUIDFast(_PREHASH_UUID, uuid); + gAssetStorage->mMessageSys->addS8Fast(_PREHASH_Type, req->getType()); + gAssetStorage->mMessageSys->addBOOLFast(_PREHASH_Success, success); + gAssetStorage->mMessageSys->sendReliable(req->mHost); + + delete req; } void LLAssetStorage::processUploadComplete(LLMessageSystem *msg, void **user_data) { - LLAssetStorage *this_ptr = (LLAssetStorage *)user_data; - LLUUID uuid; - S8 asset_type_s8; - LLAssetType::EType asset_type; - BOOL success = FALSE; - - msg->getUUIDFast(_PREHASH_AssetBlock, _PREHASH_UUID, uuid); - msg->getS8Fast(_PREHASH_AssetBlock, _PREHASH_Type, asset_type_s8); - msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success); - - asset_type = (LLAssetType::EType)asset_type_s8; - this_ptr->_callUploadCallbacks(uuid, asset_type, success, LL_EXSTAT_NONE); + LLAssetStorage *this_ptr = (LLAssetStorage *)user_data; + LLUUID uuid; + S8 asset_type_s8; + LLAssetType::EType asset_type; + BOOL success = FALSE; + + msg->getUUIDFast(_PREHASH_AssetBlock, _PREHASH_UUID, uuid); + msg->getS8Fast(_PREHASH_AssetBlock, _PREHASH_Type, asset_type_s8); + msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success); + + asset_type = (LLAssetType::EType)asset_type_s8; + this_ptr->_callUploadCallbacks(uuid, asset_type, success, LL_EXSTAT_NONE); } void LLAssetStorage::_callUploadCallbacks(const LLUUID &uuid, LLAssetType::EType asset_type, BOOL success, LLExtStat ext_status ) { - // SJB: We process the callbacks in reverse order, I do not know if this is important, - // but I didn't want to mess with it. - request_list_t requests; - for (request_list_t::iterator iter = mPendingUploads.begin(); - iter != mPendingUploads.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* req = *curiter; - if ((req->getUUID() == uuid) && (req->getType() == asset_type)) - { - requests.push_front(req); - iter = mPendingUploads.erase(curiter); - } - } - for (request_list_t::iterator iter = mPendingLocalUploads.begin(); - iter != mPendingLocalUploads.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* req = *curiter; - if ((req->getUUID() == uuid) && (req->getType() == asset_type)) - { - requests.push_front(req); - iter = mPendingLocalUploads.erase(curiter); - } - } - for (request_list_t::iterator iter = requests.begin(); - iter != requests.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* req = *curiter; - if (req->mUpCallback) - { - req->mUpCallback(uuid, req->mUserData, (success ? LL_ERR_NOERR : LL_ERR_ASSET_REQUEST_FAILED ), ext_status ); - } - delete req; - } + // SJB: We process the callbacks in reverse order, I do not know if this is important, + // but I didn't want to mess with it. + request_list_t requests; + for (request_list_t::iterator iter = mPendingUploads.begin(); + iter != mPendingUploads.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* req = *curiter; + if ((req->getUUID() == uuid) && (req->getType() == asset_type)) + { + requests.push_front(req); + iter = mPendingUploads.erase(curiter); + } + } + for (request_list_t::iterator iter = mPendingLocalUploads.begin(); + iter != mPendingLocalUploads.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* req = *curiter; + if ((req->getUUID() == uuid) && (req->getType() == asset_type)) + { + requests.push_front(req); + iter = mPendingLocalUploads.erase(curiter); + } + } + for (request_list_t::iterator iter = requests.begin(); + iter != requests.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* req = *curiter; + if (req->mUpCallback) + { + req->mUpCallback(uuid, req->mUserData, (success ? LL_ERR_NOERR : LL_ERR_ASSET_REQUEST_FAILED ), ext_status ); + } + delete req; + } } LLAssetStorage::request_list_t* LLAssetStorage::getRequestList(LLAssetStorage::ERequestType rt) { - switch (rt) - { - case RT_DOWNLOAD: - return &mPendingDownloads; - case RT_UPLOAD: - return &mPendingUploads; - case RT_LOCALUPLOAD: - return &mPendingLocalUploads; - default: - LL_WARNS() << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; - return NULL; - } + switch (rt) + { + case RT_DOWNLOAD: + return &mPendingDownloads; + case RT_UPLOAD: + return &mPendingUploads; + case RT_LOCALUPLOAD: + return &mPendingLocalUploads; + default: + LL_WARNS() << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; + return NULL; + } } const LLAssetStorage::request_list_t* LLAssetStorage::getRequestList(LLAssetStorage::ERequestType rt) const { - switch (rt) - { - case RT_DOWNLOAD: - return &mPendingDownloads; - case RT_UPLOAD: - return &mPendingUploads; - case RT_LOCALUPLOAD: - return &mPendingLocalUploads; - default: - LL_WARNS() << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; - return NULL; - } + switch (rt) + { + case RT_DOWNLOAD: + return &mPendingDownloads; + case RT_UPLOAD: + return &mPendingUploads; + case RT_LOCALUPLOAD: + return &mPendingLocalUploads; + default: + LL_WARNS() << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; + return NULL; + } } // static std::string LLAssetStorage::getRequestName(LLAssetStorage::ERequestType rt) { - switch (rt) - { - case RT_DOWNLOAD: - return "download"; - case RT_UPLOAD: - return "upload"; - case RT_LOCALUPLOAD: - return "localupload"; - default: - LL_WARNS() << "Unable to find request name for request type '" << rt << "'" << LL_ENDL; - return ""; - } + switch (rt) + { + case RT_DOWNLOAD: + return "download"; + case RT_UPLOAD: + return "upload"; + case RT_LOCALUPLOAD: + return "localupload"; + default: + LL_WARNS() << "Unable to find request name for request type '" << rt << "'" << LL_ENDL; + return ""; + } } S32 LLAssetStorage::getNumPending(LLAssetStorage::ERequestType rt) const { - const request_list_t* requests = getRequestList(rt); - S32 num_pending = -1; - if (requests) - { - num_pending = requests->size(); - } - return num_pending; + const request_list_t* requests = getRequestList(rt); + S32 num_pending = -1; + if (requests) + { + num_pending = requests->size(); + } + return num_pending; } S32 LLAssetStorage::getNumPendingDownloads() const { - return getNumPending(RT_DOWNLOAD); + return getNumPending(RT_DOWNLOAD); } S32 LLAssetStorage::getNumPendingUploads() const { - return getNumPending(RT_UPLOAD); + return getNumPending(RT_UPLOAD); } S32 LLAssetStorage::getNumPendingLocalUploads() { - return getNumPending(RT_LOCALUPLOAD); + return getNumPending(RT_LOCALUPLOAD); } -// virtual LLSD LLAssetStorage::getPendingDetails(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const + LLAssetType::EType asset_type, + const std::string& detail_prefix) const { - const request_list_t* requests = getRequestList(rt); - LLSD sd; - sd["requests"] = getPendingDetailsImpl(requests, asset_type, detail_prefix); - return sd; + const request_list_t* requests = getRequestList(rt); + LLSD sd; + sd["requests"] = getPendingDetailsImpl(requests, asset_type, detail_prefix); + return sd; } -// virtual LLSD LLAssetStorage::getPendingDetailsImpl(const LLAssetStorage::request_list_t* requests, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const -{ - LLSD details; - if (requests) - { - request_list_t::const_iterator it = requests->begin(); - request_list_t::const_iterator end = requests->end(); - for ( ; it != end; ++it) - { - LLAssetRequest* req = *it; - if ( (LLAssetType::AT_NONE == asset_type) - || (req->getType() == asset_type) ) - { - LLSD row = req->getTerseDetails(); - - std::ostringstream detail; - detail << detail_prefix << "/" << LLAssetType::lookup(req->getType()) - << "/" << req->getUUID(); - row["detail"] = LLURI(detail.str()); - - details.append(row); - } - } - } - return details; + LLAssetType::EType asset_type, + const std::string& detail_prefix) const +{ + LLSD details; + if (requests) + { + request_list_t::const_iterator it = requests->begin(); + request_list_t::const_iterator end = requests->end(); + for ( ; it != end; ++it) + { + LLAssetRequest* req = *it; + if ( (LLAssetType::AT_NONE == asset_type) + || (req->getType() == asset_type) ) + { + LLSD row = req->getTerseDetails(); + + std::ostringstream detail; + detail << detail_prefix << "/" << LLAssetType::lookup(req->getType()) + << "/" << req->getUUID(); + row["detail"] = LLURI(detail.str()); + + details.append(row); + } + } + } + return details; } // static const LLAssetRequest* LLAssetStorage::findRequest(const LLAssetStorage::request_list_t* requests, - LLAssetType::EType asset_type, - const LLUUID& asset_id) -{ - if (requests) - { - // Search the requests list for the asset. - request_list_t::const_iterator iter = requests->begin(); - request_list_t::const_iterator end = requests->end(); - for (; iter != end; ++iter) - { - const LLAssetRequest* req = *iter; - if (asset_type == req->getType() && - asset_id == req->getUUID() ) - { - return req; - } - } - } - return NULL; + LLAssetType::EType asset_type, + const LLUUID& asset_id) +{ + if (requests) + { + // Search the requests list for the asset. + request_list_t::const_iterator iter = requests->begin(); + request_list_t::const_iterator end = requests->end(); + for (; iter != end; ++iter) + { + const LLAssetRequest* req = *iter; + if (asset_type == req->getType() && + asset_id == req->getUUID() ) + { + return req; + } + } + } + return NULL; } // static LLAssetRequest* LLAssetStorage::findRequest(LLAssetStorage::request_list_t* requests, - LLAssetType::EType asset_type, - const LLUUID& asset_id) -{ - if (requests) - { - // Search the requests list for the asset. - request_list_t::iterator iter = requests->begin(); - request_list_t::iterator end = requests->end(); - for (; iter != end; ++iter) - { - LLAssetRequest* req = *iter; - if (asset_type == req->getType() && - asset_id == req->getUUID() ) - { - return req; - } - } - } - return NULL; + LLAssetType::EType asset_type, + const LLUUID& asset_id) +{ + if (requests) + { + // Search the requests list for the asset. + request_list_t::iterator iter = requests->begin(); + request_list_t::iterator end = requests->end(); + for (; iter != end; ++iter) + { + LLAssetRequest* req = *iter; + if (asset_type == req->getType() && + asset_id == req->getUUID() ) + { + return req; + } + } + } + return NULL; } -// virtual LLSD LLAssetStorage::getPendingRequest(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const + LLAssetType::EType asset_type, + const LLUUID& asset_id) const { - const request_list_t* requests = getRequestList(rt); - return getPendingRequestImpl(requests, asset_type, asset_id); + const request_list_t* requests = getRequestList(rt); + return getPendingRequestImpl(requests, asset_type, asset_id); } -// virtual LLSD LLAssetStorage::getPendingRequestImpl(const LLAssetStorage::request_list_t* requests, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const + LLAssetType::EType asset_type, + const LLUUID& asset_id) const { - LLSD sd; - const LLAssetRequest* req = findRequest(requests, asset_type, asset_id); - if (req) - { - sd = req->getFullDetails(); - } - return sd; + LLSD sd; + const LLAssetRequest* req = findRequest(requests, asset_type, asset_id); + if (req) + { + sd = req->getFullDetails(); + } + return sd; } -// virtual bool LLAssetStorage::deletePendingRequest(LLAssetStorage::ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) + LLAssetType::EType asset_type, + const LLUUID& asset_id) { - request_list_t* requests = getRequestList(rt); - if (deletePendingRequestImpl(requests, asset_type, asset_id)) - { - LL_DEBUGS("AssetStorage") << "Asset " << getRequestName(rt) << " request for " - << asset_id << "." << LLAssetType::lookup(asset_type) - << " removed from pending queue." << LL_ENDL; - return true; - } - return false; + request_list_t* requests = getRequestList(rt); + if (deletePendingRequestImpl(requests, asset_type, asset_id)) + { + LL_DEBUGS("AssetStorage") << "Asset " << getRequestName(rt) << " request for " + << asset_id << "." << LLAssetType::lookup(asset_type) + << " removed from pending queue." << LL_ENDL; + return true; + } + return false; } -// virtual bool LLAssetStorage::deletePendingRequestImpl(LLAssetStorage::request_list_t* requests, - LLAssetType::EType asset_type, - const LLUUID& asset_id) -{ - LLAssetRequest* req = findRequest(requests, asset_type, asset_id); - if (req) - { - // Remove the request from this list. - requests->remove(req); - S32 error = LL_ERR_TCP_TIMEOUT; - // Run callbacks. - if (req->mUpCallback) - { - req->mUpCallback(req->getUUID(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED); - } - if (req->mDownCallback) - { - add(sFailedDownloadCount, 1); - req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED); - } - if (req->mInfoCallback) - { - LLAssetInfo info; - req->mInfoCallback(&info, req->mUserData, error); - } - delete req; - return true; - } - - return false; + LLAssetType::EType asset_type, + const LLUUID& asset_id) +{ + LLAssetRequest* req = findRequest(requests, asset_type, asset_id); + if (req) + { + // Remove the request from this list. + requests->remove(req); + S32 error = LL_ERR_TCP_TIMEOUT; + // Run callbacks. + if (req->mUpCallback) + { + req->mUpCallback(req->getUUID(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED); + } + if (req->mDownCallback) + { + add(sFailedDownloadCount, 1); + req->mDownCallback(mVFS, req->getUUID(), req->getType(), req->mUserData, error, LL_EXSTAT_REQUEST_DROPPED); + } + if (req->mInfoCallback) + { + LLAssetInfo info; + req->mInfoCallback(&info, req->mUserData, error); + } + delete req; + return true; + } + + return false; } // static const char* LLAssetStorage::getErrorString(S32 status) { - switch( status ) - { - case LL_ERR_NOERR: - return "No error"; + switch( status ) + { + case LL_ERR_NOERR: + return "No error"; - case LL_ERR_ASSET_REQUEST_FAILED: - return "Asset request: failed"; + case LL_ERR_ASSET_REQUEST_FAILED: + return "Asset request: failed"; - case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE: - return "Asset request: non-existent file"; + case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE: + return "Asset request: non-existent file"; - case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE: - return "Asset request: asset not found in database"; + case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE: + return "Asset request: asset not found in database"; - case LL_ERR_EOF: - return "End of file"; + case LL_ERR_EOF: + return "End of file"; - case LL_ERR_CANNOT_OPEN_FILE: - return "Cannot open file"; + case LL_ERR_CANNOT_OPEN_FILE: + return "Cannot open file"; - case LL_ERR_FILE_NOT_FOUND: - return "File not found"; + case LL_ERR_FILE_NOT_FOUND: + return "File not found"; - case LL_ERR_TCP_TIMEOUT: - return "File transfer timeout"; + case LL_ERR_TCP_TIMEOUT: + return "File transfer timeout"; - case LL_ERR_CIRCUIT_GONE: - return "Circuit gone"; + case LL_ERR_CIRCUIT_GONE: + return "Circuit gone"; - case LL_ERR_PRICE_MISMATCH: - return "Viewer and server do not agree on price"; + case LL_ERR_PRICE_MISMATCH: + return "Viewer and server do not agree on price"; - default: - return "Unknown status"; - } + default: + return "Unknown status"; + } } - - -void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32, LLExtStat), void *user_data, BOOL is_priority) +void LLAssetStorage::getAssetData(const LLUUID uuid, + LLAssetType::EType type, + void (*callback)(const char*, + const LLUUID&, + void *, + S32, + LLExtStat), + void *user_data, + BOOL is_priority) { - // check for duplicates here, since we're about to fool the normal duplicate checker - for (request_list_t::iterator iter = mPendingDownloads.begin(); - iter != mPendingDownloads.end(); ) - { - LLAssetRequest* tmp = *iter++; - if (type == tmp->getType() && - uuid == tmp->getUUID() && - legacyGetDataCallback == tmp->mDownCallback && - callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback && - user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData) - { - // this is a duplicate from the same subsystem - throw it away - LL_DEBUGS("AssetStorage") << "Discarding duplicate request for UUID " << uuid << LL_ENDL; - return; - } - } - - - LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest; - - legacy->mDownCallback = callback; - legacy->mUserData = user_data; - - getAssetData(uuid, type, legacyGetDataCallback, (void **)legacy, - is_priority); -} + // check for duplicates here, since we're about to fool the normal duplicate checker + for (request_list_t::iterator iter = mPendingDownloads.begin(); + iter != mPendingDownloads.end(); ) + { + LLAssetRequest* tmp = *iter++; + if (type == tmp->getType() && + uuid == tmp->getUUID() && + legacyGetDataCallback == tmp->mDownCallback && + callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback && + user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData) + { + // this is a duplicate from the same subsystem - throw it away + LL_DEBUGS("AssetStorage") << "Discarding duplicate request for UUID " << uuid << LL_ENDL; + return; + } + } + + + LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest; -// static -void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status, LLExtStat ext_status) -{ - LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data; - std::string filename; - - // Check if the asset is marked toxic, and don't load bad stuff - BOOL toxic = gAssetStorage->isAssetToxic( uuid ); - - if ( !status - && !toxic ) - { - LLVFile file(vfs, uuid, type); - - std::string uuid_str; - - uuid.toString(uuid_str); - filename = llformat("%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type)); - - LLFILE* fp = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */ - if (fp) - { - const S32 buf_size = 65536; - U8 copy_buf[buf_size]; - while (file.read(copy_buf, buf_size)) /* Flawfinder: ignore */ - { - if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1) - { - // return a bad file error if we can't write the whole thing - status = LL_ERR_CANNOT_OPEN_FILE; - } - } - - fclose(fp); - } - else - { - status = LL_ERR_CANNOT_OPEN_FILE; - } - } - - if (status != LL_ERR_NOERR) - { - add(sFailedDownloadCount, 1); - } - legacy->mDownCallback(filename.c_str(), uuid, legacy->mUserData, status, ext_status); - delete legacy; -} - -// this is overridden on the viewer and the sim, so it doesn't really do anything -// virtual -void LLAssetStorage::storeAssetData( - const LLTransactionID& tid, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool store_local, - bool user_waiting, - F64Seconds timeout) -{ - LL_WARNS() << "storeAssetData: wrong version called" << LL_ENDL; - // LLAssetStorage metric: Virtual base call - reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 1" ); -} - -// virtual -// this does nothing, viewer and sim both override this. -void LLAssetStorage::storeAssetData( - const LLUUID& asset_id, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file , - bool is_priority, - bool store_local, - const LLUUID& requesting_agent_id, - bool user_waiting, - F64Seconds timeout) -{ - LL_WARNS() << "storeAssetData: wrong version called" << LL_ENDL; - // LLAssetStorage metric: Virtual base call - reportMetric( asset_id, asset_type, LLStringUtil::null, requesting_agent_id, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 2" ); -} + legacy->mDownCallback = callback; + legacy->mUserData = user_data; -// virtual -// this does nothing, viewer and sim both override this. -void LLAssetStorage::storeAssetData( - const std::string& filename, - const LLUUID& asset_id, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting, - F64Seconds timeout) -{ - LL_WARNS() << "storeAssetData: wrong version called" << LL_ENDL; - // LLAssetStorage metric: Virtual base call - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 3" ); + getAssetData(uuid, type, legacyGetDataCallback, (void **)legacy, + is_priority); } -// virtual -// this does nothing, viewer and sim both override this. -void LLAssetStorage::storeAssetData( - const std::string& filename, - const LLTransactionID &transactoin_id, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting, - F64Seconds timeout) -{ - LL_WARNS() << "storeAssetData: wrong version called" << LL_ENDL; - // LLAssetStorage metric: Virtual base call - reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_BAD_FUNCTION, __FILE__, __LINE__, "Illegal call to base: LLAssetStorage::storeAssetData 4" ); +// static +void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, + const LLUUID &uuid, + LLAssetType::EType type, + void *user_data, + S32 status, + LLExtStat ext_status) +{ + LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data; + std::string filename; + + // Check if the asset is marked toxic, and don't load bad stuff + BOOL toxic = gAssetStorage->isAssetToxic( uuid ); + + if ( !status + && !toxic ) + { + LLVFile file(vfs, uuid, type); + + std::string uuid_str; + + uuid.toString(uuid_str); + filename = llformat("%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type)); + + LLFILE* fp = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */ + if (fp) + { + const S32 buf_size = 65536; + U8 copy_buf[buf_size]; + while (file.read(copy_buf, buf_size)) /* Flawfinder: ignore */ + { + if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1) + { + // return a bad file error if we can't write the whole thing + status = LL_ERR_CANNOT_OPEN_FILE; + } + } + + fclose(fp); + } + else + { + status = LL_ERR_CANNOT_OPEN_FILE; + } + } + + if (status != LL_ERR_NOERR) + { + add(sFailedDownloadCount, 1); + } + legacy->mDownCallback(filename.c_str(), uuid, legacy->mUserData, status, ext_status); + delete legacy; } // static void LLAssetStorage::legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status) { - LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data; - if (legacy && legacy->mUpCallback) - { - legacy->mUpCallback(uuid, legacy->mUserData, status, ext_status); - } - delete legacy; + LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data; + if (legacy && legacy->mUpCallback) + { + legacy->mUpCallback(uuid, legacy->mUserData, status, ext_status); + } + delete legacy; } -// virtual -void LLAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name) -{ } - -// virtual -BOOL LLAssetStorage::hasTempAssetData(const LLUUID& texture_id) const -{ return FALSE; } - -// virtual -std::string LLAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const -{ return std::string(); } - -// virtual -LLUUID LLAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const -{ return LLUUID::null; } - -// virtual -void LLAssetStorage::removeTempAssetData(const LLUUID& asset_id) -{ } - -// virtual -void LLAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id) -{ } - -// virtual -void LLAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const -{ } - -// virtual -void LLAssetStorage::clearTempAssetData() -{ } - // static void LLAssetStorage::reportMetric( const LLUUID& asset_id, const LLAssetType::EType asset_type, const std::string& in_filename, - const LLUUID& agent_id, S32 asset_size, EMetricResult result, - const char *file, const S32 line, const std::string& in_message ) -{ - if( !metric_recipient ) - { - LL_DEBUGS("AssetStorage") << "Couldn't store LLAssetStoreage::reportMetric - no metrics_recipient" << LL_ENDL; - return; - } - - std::string filename(in_filename); - if (filename.empty()) - filename = ll_safe_string(file); - - // Create revised message - new_message = "in_message :: file:line" - std::stringstream new_message; - new_message << in_message << " :: " << filename << ":" << line; - - // Change always_report to true if debugging... do not check it in this way - static bool always_report = false; - const char *metric_name = "LLAssetStorage::Metrics"; - - bool success = result == MR_OKAY; - - if( (!success) || always_report ) - { - LLSD stats; - stats["asset_id"] = asset_id; - stats["asset_type"] = asset_type; - stats["filename"] = filename; - stats["agent_id"] = agent_id; - stats["asset_size"] = (S32)asset_size; - stats["result"] = (S32)result; - - metric_recipient->recordEventDetails( metric_name, new_message.str(), success, stats); - } - else - { - metric_recipient->recordEvent(metric_name, new_message.str(), success); - } + const LLUUID& agent_id, S32 asset_size, EMetricResult result, + const char *file, const S32 line, const std::string& in_message ) +{ + if( !metric_recipient ) + { + LL_DEBUGS("AssetStorage") << "Couldn't store LLAssetStoreage::reportMetric - no metrics_recipient" << LL_ENDL; + return; + } + + std::string filename(in_filename); + if (filename.empty()) + filename = ll_safe_string(file); + + // Create revised message - new_message = "in_message :: file:line" + std::stringstream new_message; + new_message << in_message << " :: " << filename << ":" << line; + + // Change always_report to true if debugging... do not check it in this way + static bool always_report = false; + const char *metric_name = "LLAssetStorage::Metrics"; + + bool success = result == MR_OKAY; + + if( (!success) || always_report ) + { + LLSD stats; + stats["asset_id"] = asset_id; + stats["asset_type"] = asset_type; + stats["filename"] = filename; + stats["agent_id"] = agent_id; + stats["asset_size"] = (S32)asset_size; + stats["result"] = (S32)result; + + metric_recipient->recordEventDetails( metric_name, new_message.str(), success, stats); + } + else + { + metric_recipient->recordEvent(metric_name, new_message.str(), success); + } } // Check if an asset is in the toxic map. If it is, the entry is updated -BOOL LLAssetStorage::isAssetToxic( const LLUUID& uuid ) +BOOL LLAssetStorage::isAssetToxic( const LLUUID& uuid ) { - BOOL is_toxic = FALSE; - - if ( !uuid.isNull() ) - { - toxic_asset_map_t::iterator iter = mToxicAssetMap.find( uuid ); - if ( iter != mToxicAssetMap.end() ) - { // Found toxic asset - (*iter).second = LLFrameTimer::getTotalTime() + TOXIC_ASSET_LIFETIME; - is_toxic = TRUE; - } - } - return is_toxic; + BOOL is_toxic = FALSE; + + if ( !uuid.isNull() ) + { + toxic_asset_map_t::iterator iter = mToxicAssetMap.find( uuid ); + if ( iter != mToxicAssetMap.end() ) + { // Found toxic asset + (*iter).second = LLFrameTimer::getTotalTime() + TOXIC_ASSET_LIFETIME; + is_toxic = TRUE; + } + } + return is_toxic; } // Clean the toxic asset list, remove old entries -void LLAssetStorage::flushOldToxicAssets( BOOL force_it ) -{ - // Scan and look for old entries - U64 now = LLFrameTimer::getTotalTime(); - toxic_asset_map_t::iterator iter = mToxicAssetMap.begin(); - while ( iter != mToxicAssetMap.end() ) - { - if ( force_it - || (*iter).second < now ) - { // Too old - remove it - mToxicAssetMap.erase( iter++ ); - } - else - { - iter++; - } - } +void LLAssetStorage::flushOldToxicAssets( BOOL force_it ) +{ + // Scan and look for old entries + U64 now = LLFrameTimer::getTotalTime(); + toxic_asset_map_t::iterator iter = mToxicAssetMap.begin(); + while ( iter != mToxicAssetMap.end() ) + { + if ( force_it + || (*iter).second < now ) + { // Too old - remove it + mToxicAssetMap.erase( iter++ ); + } + else + { + iter++; + } + } } // Add an item to the toxic asset map -void LLAssetStorage::markAssetToxic( const LLUUID& uuid ) -{ - if ( !uuid.isNull() ) - { - // Set the value to the current time. Creates a new entry if needed - mToxicAssetMap[ uuid ] = LLFrameTimer::getTotalTime() + TOXIC_ASSET_LIFETIME; - } +void LLAssetStorage::markAssetToxic( const LLUUID& uuid ) +{ + if ( !uuid.isNull() ) + { + // Set the value to the current time. Creates a new entry if needed + mToxicAssetMap[ uuid ] = LLFrameTimer::getTotalTime() + TOXIC_ASSET_LIFETIME; + } } diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 0f23754096..853387a595 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -186,18 +186,9 @@ typedef std::map toxic_asset_map_t; // we can use bind and remove the userData parameter. // typedef void (*LLGetAssetCallback)(LLVFS *vfs, const LLUUID &asset_id, - LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status); + LLAssetType::EType asset_type, void *user_data, S32 status, LLExtStat ext_status); -class LLTempAssetStorage -{ -public: - virtual ~LLTempAssetStorage() =0; - virtual void addTempAssetData(const LLUUID& asset_id, - const LLUUID& agent_id, - const std::string& host_name) = 0; -}; - -class LLAssetStorage : public LLTempAssetStorage +class LLAssetStorage { public: // VFS member is public because static child methods need it :( @@ -240,12 +231,11 @@ public: void setUpstream(const LLHost &upstream_host); - virtual BOOL hasLocalAsset(const LLUUID &uuid, LLAssetType::EType type); + BOOL hasLocalAsset(const LLUUID &uuid, LLAssetType::EType type); // public interface methods // note that your callback may get called BEFORE the function returns - - virtual void getAssetData(const LLUUID uuid, LLAssetType::EType atype, LLGetAssetCallback cb, void *user_data, BOOL is_priority = FALSE); + void getAssetData(const LLUUID uuid, LLAssetType::EType atype, LLGetAssetCallback cb, void *user_data, BOOL is_priority = FALSE); /* * TransactionID version @@ -260,25 +250,9 @@ public: bool is_priority = false, bool store_local = false, bool user_waiting= false, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); - - /* - * AssetID version - * Sim needs both store_local and requesting_agent_id. - */ - virtual void storeAssetData( - const LLUUID& asset_id, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file = false, - bool is_priority = false, - bool store_local = false, - const LLUUID& requesting_agent_id = LLUUID::null, - bool user_waiting= false, - F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); + F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT) = 0; - virtual void checkForTimeouts(); + void checkForTimeouts(); void getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id, const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype, @@ -303,17 +277,17 @@ protected: bool findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAssetType::EType type, LLGetAssetCallback callback, void *user_data); - virtual LLSD getPendingDetailsImpl(const request_list_t* requests, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const; + LLSD getPendingDetailsImpl(const request_list_t* requests, + LLAssetType::EType asset_type, + const std::string& detail_prefix) const; - virtual LLSD getPendingRequestImpl(const request_list_t* requests, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const; + LLSD getPendingRequestImpl(const request_list_t* requests, + LLAssetType::EType asset_type, + const LLUUID& asset_id) const; - virtual bool deletePendingRequestImpl(request_list_t* requests, - LLAssetType::EType asset_type, - const LLUUID& asset_id); + bool deletePendingRequestImpl(request_list_t* requests, + LLAssetType::EType asset_type, + const LLUUID& asset_id); public: static const LLAssetRequest* findRequest(const request_list_t* requests, @@ -332,17 +306,17 @@ public: S32 getNumPendingLocalUploads(); S32 getNumPending(ERequestType rt) const; - virtual LLSD getPendingDetails(ERequestType rt, - LLAssetType::EType asset_type, - const std::string& detail_prefix) const; + LLSD getPendingDetails(ERequestType rt, + LLAssetType::EType asset_type, + const std::string& detail_prefix) const; - virtual LLSD getPendingRequest(ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id) const; + LLSD getPendingRequest(ERequestType rt, + LLAssetType::EType asset_type, + const LLUUID& asset_id) const; - virtual bool deletePendingRequest(ERequestType rt, - LLAssetType::EType asset_type, - const LLUUID& asset_id); + bool deletePendingRequest(ERequestType rt, + LLAssetType::EType asset_type, + const LLUUID& asset_id); // download process callbacks @@ -370,22 +344,9 @@ public: static const char* getErrorString( S32 status ); // deprecated file-based methods + // Not overriden void getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32, LLExtStat), void *user_data, BOOL is_priority = FALSE); - /* - * AssetID version. - */ - virtual void storeAssetData( - const std::string& filename, - const LLUUID& asset_id, - LLAssetType::EType type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file = false, - bool is_priority = false, - bool user_waiting = false, - F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT); - /* * TransactionID version */ @@ -398,23 +359,11 @@ public: bool temp_file = false, bool is_priority = false, bool user_waiting = false, - F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT); + F64Seconds timeout = LL_ASSET_STORAGE_TIMEOUT) = 0; static void legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status, LLExtStat ext_status); static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status, LLExtStat ext_status); - // Temp assets are stored on sim nodes, they have agent ID and location data associated with them. - // This is a no-op for non-http asset systems - virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name); - virtual BOOL hasTempAssetData(const LLUUID& texture_id) const; - virtual std::string getTempAssetHostName(const LLUUID& texture_id) const; - virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const; - virtual void removeTempAssetData(const LLUUID& asset_id); - virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id); - // Pass LLUUID::null for all - virtual void dumpTempAssetData(const LLUUID& avatar_id) const; - virtual void clearTempAssetData(); - // add extra methods to handle metadata protected: @@ -424,7 +373,7 @@ protected: virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), void *user_data, BOOL duplicate, - BOOL is_priority); + BOOL is_priority) = 0; private: void _init(LLMessageSystem *msg, diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 2db9c7e67c..b5e68c0617 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -33,6 +33,9 @@ #include "message.h" #include "llagent.h" + +// FIXME asset-http: We are the only customer for gTransferManager - the +// whole class can be yanked once everything is http-ified. #include "lltransfersourceasset.h" #include "lltransfertargetvfile.h" #include "llviewerassetstats.h" @@ -51,38 +54,38 @@ class LLViewerAssetRequest : public LLAssetRequest { public: - LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type) - : LLAssetRequest(uuid, type), - mMetricsStartTime(0) - { - } - - LLViewerAssetRequest & operator=(const LLViewerAssetRequest &); // Not defined - // Default assignment operator valid - - // virtual - ~LLViewerAssetRequest() - { - recordMetrics(); - } + LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type) + : LLAssetRequest(uuid, type), + mMetricsStartTime(0) + { + } + + LLViewerAssetRequest & operator=(const LLViewerAssetRequest &); // Not defined + // Default assignment operator valid + + // virtual + ~LLViewerAssetRequest() + { + recordMetrics(); + } protected: - void recordMetrics() - { - if (mMetricsStartTime.value()) - { - // Okay, it appears this request was used for useful things. Record - // the expected dequeue and duration of request processing. - LLViewerAssetStatsFF::record_dequeue(mType, false, false); - LLViewerAssetStatsFF::record_response(mType, false, false, - (LLViewerAssetStatsFF::get_timestamp() - - mMetricsStartTime)); - mMetricsStartTime = (U32Seconds)0; - } - } - + void recordMetrics() + { + if (mMetricsStartTime.value()) + { + // Okay, it appears this request was used for useful things. Record + // the expected dequeue and duration of request processing. + LLViewerAssetStatsFF::record_dequeue(mType, false, false); + LLViewerAssetStatsFF::record_response(mType, false, false, + (LLViewerAssetStatsFF::get_timestamp() + - mMetricsStartTime)); + mMetricsStartTime = (U32Seconds)0; + } + } + public: - LLViewerAssetStats::duration_t mMetricsStartTime; + LLViewerAssetStats::duration_t mMetricsStartTime; }; ///---------------------------------------------------------------------------- @@ -90,228 +93,228 @@ public: ///---------------------------------------------------------------------------- LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs, - const LLHost &upstream_host) - : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host) + LLVFS *vfs, LLVFS *static_vfs, + const LLHost &upstream_host) + : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host) { } LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, - LLVFS *vfs, LLVFS *static_vfs) - : LLAssetStorage(msg, xfer, vfs, static_vfs) + LLVFS *vfs, LLVFS *static_vfs) + : LLAssetStorage(msg, xfer, vfs, static_vfs) { } // virtual void LLViewerAssetStorage::storeAssetData( - const LLTransactionID& tid, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool store_local, - bool user_waiting, - F64Seconds timeout) + const LLTransactionID& tid, + LLAssetType::EType asset_type, + LLStoreAssetCallback callback, + void* user_data, + bool temp_file, + bool is_priority, + bool store_local, + bool user_waiting, + F64Seconds timeout) { - LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type) - << " ASSET_ID: " << asset_id << LL_ENDL; - - if (mUpstreamHost.isOk()) - { - if (mVFS->getExists(asset_id, asset_type)) - { - // Pack data into this packet if we can fit it. - U8 buffer[MTUBYTES]; - buffer[0] = 0; - - LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ); - S32 asset_size = vfile.getSize(); - - LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); - req->mUpCallback = callback; - req->mUserData = user_data; - - if (asset_size < 1) - { - // This can happen if there's a bug in our code or if the VFS has been corrupted. - LL_WARNS() << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << LL_ENDL; - // LLAssetStorage metric: Zero size VFS - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); - - delete req; - if (callback) - { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_VFS_CORRUPT); - } - return; - } - else - { - // LLAssetStorage metric: Successful Request - S32 size = mVFS->getSize(asset_id, asset_type); - const char *message = "Added to upload queue"; - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message ); - - if(is_priority) - { - mPendingUploads.push_front(req); - } - else - { - mPendingUploads.push_back(req); - } - } - - // Read the data from the VFS if it'll fit in this packet. - if (asset_size + 100 < MTUBYTES) - { - BOOL res = vfile.read(buffer, asset_size); /* Flawfinder: ignore */ - S32 bytes_read = res ? vfile.getLastBytesRead() : 0; - - if( bytes_read == asset_size ) - { - req->mDataSentInFirstPacket = TRUE; - //LL_INFOS() << "LLViewerAssetStorage::createAsset sending data in first packet" << LL_ENDL; - } - else - { - LL_WARNS() << "Probable corruption in VFS file, aborting store asset data" << LL_ENDL; - - // LLAssetStorage metric: VFS corrupt - bogus size - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, asset_size, MR_VFS_CORRUPTION, __FILE__, __LINE__, "VFS corruption" ); - - if (callback) - { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_VFS_CORRUPT); - } - return; - } - } - else - { - // Too big, do an xfer - buffer[0] = 0; - asset_size = 0; - } - mMessageSys->newMessageFast(_PREHASH_AssetUploadRequest); - mMessageSys->nextBlockFast(_PREHASH_AssetBlock); - mMessageSys->addUUIDFast(_PREHASH_TransactionID, tid); - mMessageSys->addS8Fast(_PREHASH_Type, (S8)asset_type); - mMessageSys->addBOOLFast(_PREHASH_Tempfile, temp_file); - mMessageSys->addBOOLFast(_PREHASH_StoreLocal, store_local); - mMessageSys->addBinaryDataFast( _PREHASH_AssetData, buffer, asset_size ); - mMessageSys->sendReliable(mUpstreamHost); - } - else - { - LL_WARNS() << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; - // LLAssetStorage metric: Zero size VFS - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); - if (callback) - { - callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE); - } - } - } - else - { - LL_WARNS() << "Attempt to move asset store request upstream w/o valid upstream provider" << LL_ENDL; - // LLAssetStorage metric: Upstream provider dead - reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_NO_UPSTREAM, __FILE__, __LINE__, "No upstream provider" ); - if (callback) - { - callback(asset_id, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); - } - } + LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type) + << " ASSET_ID: " << asset_id << LL_ENDL; + + if (mUpstreamHost.isOk()) + { + if (mVFS->getExists(asset_id, asset_type)) + { + // Pack data into this packet if we can fit it. + U8 buffer[MTUBYTES]; + buffer[0] = 0; + + LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ); + S32 asset_size = vfile.getSize(); + + LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type); + req->mUpCallback = callback; + req->mUserData = user_data; + + if (asset_size < 1) + { + // This can happen if there's a bug in our code or if the VFS has been corrupted. + LL_WARNS() << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << LL_ENDL; + // LLAssetStorage metric: Zero size VFS + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); + + delete req; + if (callback) + { + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED, LL_EXSTAT_VFS_CORRUPT); + } + return; + } + else + { + // LLAssetStorage metric: Successful Request + S32 size = mVFS->getSize(asset_id, asset_type); + const char *message = "Added to upload queue"; + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, size, MR_OKAY, __FILE__, __LINE__, message ); + + if(is_priority) + { + mPendingUploads.push_front(req); + } + else + { + mPendingUploads.push_back(req); + } + } + + // Read the data from the VFS if it'll fit in this packet. + if (asset_size + 100 < MTUBYTES) + { + BOOL res = vfile.read(buffer, asset_size); /* Flawfinder: ignore */ + S32 bytes_read = res ? vfile.getLastBytesRead() : 0; + + if( bytes_read == asset_size ) + { + req->mDataSentInFirstPacket = TRUE; + //LL_INFOS() << "LLViewerAssetStorage::createAsset sending data in first packet" << LL_ENDL; + } + else + { + LL_WARNS() << "Probable corruption in VFS file, aborting store asset data" << LL_ENDL; + + // LLAssetStorage metric: VFS corrupt - bogus size + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, asset_size, MR_VFS_CORRUPTION, __FILE__, __LINE__, "VFS corruption" ); + + if (callback) + { + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_VFS_CORRUPT); + } + return; + } + } + else + { + // Too big, do an xfer + buffer[0] = 0; + asset_size = 0; + } + mMessageSys->newMessageFast(_PREHASH_AssetUploadRequest); + mMessageSys->nextBlockFast(_PREHASH_AssetBlock); + mMessageSys->addUUIDFast(_PREHASH_TransactionID, tid); + mMessageSys->addS8Fast(_PREHASH_Type, (S8)asset_type); + mMessageSys->addBOOLFast(_PREHASH_Tempfile, temp_file); + mMessageSys->addBOOLFast(_PREHASH_StoreLocal, store_local); + mMessageSys->addBinaryDataFast( _PREHASH_AssetData, buffer, asset_size ); + mMessageSys->sendReliable(mUpstreamHost); + } + else + { + LL_WARNS() << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; + // LLAssetStorage metric: Zero size VFS + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); + if (callback) + { + callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE, LL_EXSTAT_NONEXISTENT_FILE); + } + } + } + else + { + LL_WARNS() << "Attempt to move asset store request upstream w/o valid upstream provider" << LL_ENDL; + // LLAssetStorage metric: Upstream provider dead + reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_NO_UPSTREAM, __FILE__, __LINE__, "No upstream provider" ); + if (callback) + { + callback(asset_id, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + } + } } void LLViewerAssetStorage::storeAssetData( - const std::string& filename, - const LLTransactionID& tid, - LLAssetType::EType asset_type, - LLStoreAssetCallback callback, - void* user_data, - bool temp_file, - bool is_priority, - bool user_waiting, - F64Seconds timeout) + const std::string& filename, + const LLTransactionID& tid, + LLAssetType::EType asset_type, + LLStoreAssetCallback callback, + void* user_data, + bool temp_file, + bool is_priority, + bool user_waiting, + F64Seconds timeout) { - if(filename.empty()) - { - // LLAssetStorage metric: no filename - reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_VFS_CORRUPTION, __FILE__, __LINE__, "Filename missing" ); - LL_ERRS() << "No filename specified" << LL_ENDL; - return; - } - - LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); - LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; - - LL_DEBUGS("AssetStorage") << "ASSET_ID: " << asset_id << LL_ENDL; - - S32 size = 0; - LLFILE* fp = LLFile::fopen(filename, "rb"); - if (fp) - { - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - } - if( size ) - { - LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest; - - legacy->mUpCallback = callback; - legacy->mUserData = user_data; - - LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); - - file.setMaxSize(size); - - const S32 buf_size = 65536; - U8 copy_buf[buf_size]; - while ((size = (S32)fread(copy_buf, 1, buf_size, fp))) - { - file.write(copy_buf, size); - } - fclose(fp); - - // if this upload fails, the caller needs to setup a new tempfile for us - if (temp_file) - { - LLFile::remove(filename); - } - - // LLAssetStorage metric: Success not needed; handled in the overloaded method here: - - LLViewerAssetStorage::storeAssetData( - tid, - asset_type, - legacyStoreDataCallback, - (void**)legacy, - temp_file, - is_priority); - } - else // size == 0 (but previous block changes size) - { - if( fp ) - { - // LLAssetStorage metric: Zero size - reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" ); - } - else - { - // LLAssetStorage metric: Missing File - reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" ); - } - if (callback) - { - callback(asset_id, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE); - } - } + if(filename.empty()) + { + // LLAssetStorage metric: no filename + reportMetric( LLUUID::null, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_VFS_CORRUPTION, __FILE__, __LINE__, "Filename missing" ); + LL_ERRS() << "No filename specified" << LL_ENDL; + return; + } + + LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID()); + LL_DEBUGS("AssetStorage") << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; + + LL_DEBUGS("AssetStorage") << "ASSET_ID: " << asset_id << LL_ENDL; + + S32 size = 0; + LLFILE* fp = LLFile::fopen(filename, "rb"); + if (fp) + { + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + } + if( size ) + { + LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest; + + legacy->mUpCallback = callback; + legacy->mUserData = user_data; + + LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE); + + file.setMaxSize(size); + + const S32 buf_size = 65536; + U8 copy_buf[buf_size]; + while ((size = (S32)fread(copy_buf, 1, buf_size, fp))) + { + file.write(copy_buf, size); + } + fclose(fp); + + // if this upload fails, the caller needs to setup a new tempfile for us + if (temp_file) + { + LLFile::remove(filename); + } + + // LLAssetStorage metric: Success not needed; handled in the overloaded method here: + + LLViewerAssetStorage::storeAssetData( + tid, + asset_type, + legacyStoreDataCallback, + (void**)legacy, + temp_file, + is_priority); + } + else // size == 0 (but previous block changes size) + { + if( fp ) + { + // LLAssetStorage metric: Zero size + reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file was zero length" ); + } + else + { + // LLAssetStorage metric: Missing File + reportMetric( asset_id, asset_type, filename, LLUUID::null, 0, MR_FILE_NONEXIST, __FILE__, __LINE__, "The file didn't exist" ); + } + if (callback) + { + callback(asset_id, user_data, LL_ERR_CANNOT_OPEN_FILE, LL_EXSTAT_BLOCKED_FILE); + } + } } @@ -334,56 +337,56 @@ void LLViewerAssetStorage::storeAssetData( // virtual void LLViewerAssetStorage::_queueDataRequest( - const LLUUID& uuid, - LLAssetType::EType atype, - LLGetAssetCallback callback, - void *user_data, - BOOL duplicate, - BOOL is_priority) + const LLUUID& uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data, + BOOL duplicate, + BOOL is_priority) { - if (mUpstreamHost.isOk()) - { - // stash the callback info so we can find it after we get the response message - LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype); - req->mDownCallback = callback; - req->mUserData = user_data; - req->mIsPriority = is_priority; - if (!duplicate) - { - // Only collect metrics for non-duplicate requests. Others - // are piggy-backing and will artificially lower averages. - req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - - mPendingDownloads.push_back(req); - - if (!duplicate) - { - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsAsset spa; - spa.setAsset(uuid, atype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(uuid, atype); - tpvf.setCallback(downloadCompleteCallback, *req); - - LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); - ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - - LLViewerAssetStatsFF::record_enqueue(atype, false, false); - } - } - else - { - // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; - if (callback) - { - callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); - } - } + if (mUpstreamHost.isOk()) + { + // stash the callback info so we can find it after we get the response message + LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype); + req->mDownCallback = callback; + req->mUserData = user_data; + req->mIsPriority = is_priority; + if (!duplicate) + { + // Only collect metrics for non-duplicate requests. Others + // are piggy-backing and will artificially lower averages. + req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + + mPendingDownloads.push_back(req); + + if (!duplicate) + { + // send request message to our upstream data provider + // Create a new asset transfer. + LLTransferSourceParamsAsset spa; + spa.setAsset(uuid, atype); + + // Set our destination file, and the completion callback. + LLTransferTargetParamsVFile tpvf; + tpvf.setAsset(uuid, atype); + tpvf.setCallback(downloadCompleteCallback, *req); + + LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; + LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); + ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); + + LLViewerAssetStatsFF::record_enqueue(atype, false, false); + } + } + else + { + // uh-oh, we shouldn't have gotten here + LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; + if (callback) + { + callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); + } + } } diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 6baec647e6..4643fccbc1 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -41,7 +41,6 @@ public: LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs); - using LLAssetStorage::storeAssetData; virtual void storeAssetData( const LLTransactionID& tid, LLAssetType::EType atype, -- cgit v1.2.3 From 86449a0ac4cb1432a55c17bfabe83c4c42c096a8 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 14 Feb 2017 09:31:08 -0500 Subject: SL-409 - debug setting to enable/disable use of ViewerAsset cap by asset type. Temporary construction until UDP path goes away. --- indra/llcommon/llassettype.cpp | 93 ++++++++++++++++++++++++++++++++- indra/llcommon/llassettype.h | 5 ++ indra/newview/app_settings/settings.xml | 12 +++++ indra/newview/llstartup.cpp | 10 ++++ indra/newview/llviewercontrol.cpp | 8 +++ 5 files changed, 127 insertions(+), 1 deletion(-) diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 4304db36be..6ecc2ec740 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -31,6 +31,9 @@ #include "llmemory.h" #include "llsingleton.h" +#include +#include + ///---------------------------------------------------------------------------- /// Class LLAssetType ///---------------------------------------------------------------------------- @@ -48,7 +51,8 @@ struct AssetEntry : public LLDictionaryEntry mHumanName(human_name), mCanLink(can_link), mCanFetch(can_fetch), - mCanKnow(can_know) + mCanKnow(can_know), + mFetchWithVACap(false) { llassert(strlen(mTypeName) <= 8); } @@ -58,6 +62,7 @@ struct AssetEntry : public LLDictionaryEntry bool mCanLink; bool mCanFetch; bool mCanKnow; + bool mFetchWithVACap; }; class LLAssetDictionary : public LLSingleton, @@ -251,3 +256,89 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type) } return false; } + +// static +bool LLAssetType::lookupFetchWithVACap(EType asset_type) +{ + LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + const AssetEntry *entry = dict->lookup(asset_type); + if (entry) + { + return entry->mFetchWithVACap; + } + return false; +} + +// FIXME asset-http yank all this after asset-http becomes universal +void LLAssetType::setFetchWithVACapTypeString(const std::string& type_string) +{ + LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + if (type_string=="none") + { + for (LLAssetDictionary::iterator iter = dict->begin(); + iter != dict->end(); + iter++) + { + AssetEntry *entry = iter->second; + entry->mFetchWithVACap = false; + } + } + else if (type_string=="all") + { + for (LLAssetDictionary::iterator iter = dict->begin(); + iter != dict->end(); + iter++) + { + AssetEntry *entry = iter->second; + entry->mFetchWithVACap = true; + } + } + else + { + for (LLAssetDictionary::iterator iter = dict->begin(); + iter != dict->end(); + iter++) + { + AssetEntry *entry = iter->second; + if (entry->mTypeName==type_string) + { + entry->mFetchWithVACap = true; + } + } + } +} + +// FIXME asset-http yank all this after asset-http becomes universal +void LLAssetType::setFetchWithVACapConfigString(const std::string& config_string) +{ + // Clear any currently enabled types + LLAssetType::setFetchWithVACapTypeString("none"); + + // Enable all types specified in the config string. + std::set type_names_for_va_cap; + boost::split(type_names_for_va_cap, config_string, boost::is_any_of(" :,")); + for (std::set::const_iterator it = type_names_for_va_cap.begin(); + it != type_names_for_va_cap.end(); ++it) + { + const std::string& type_string = *it; + LLAssetType::setFetchWithVACapTypeString(type_string); + } + + LLAssetDictionary *dict = LLAssetDictionary::getInstance(); + bool any_found = false; + for (LLAssetDictionary::iterator iter = dict->begin(); + iter != dict->end(); + iter++) + { + AssetEntry *entry = iter->second; + if (entry->mFetchWithVACap) + { + any_found = true; + LL_WARNS() << "Fetch with ViewerAsset cap enabled for " << entry->mTypeName << LL_ENDL; + } + } + if (!any_found) + { + LL_WARNS() << "Fetch with ViewerAsset cap disabled for all types" << LL_ENDL; + } +} diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 3a4b5dad18..e06ebc2a35 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -152,6 +152,11 @@ public: static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer + + static bool lookupFetchWithVACap(EType asset_type); // asset data is fetched via http using ViewerAsset cap. + + static void setFetchWithVACapTypeString(const std::string& type_string); + static void setFetchWithVACapConfigString(const std::string& config_string); static const std::string& badLookup(); // error string when a lookup fails diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9f37c3487e..8b899dc8c8 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15808,6 +15808,18 @@ 0 + ViewerAssetHttpTypes + + Comment + Use the ViewerAsset cap and HTTP pipeline for fetching assets of the listed type names. "none" and "all" are recognized as a special values. + Persist + 1 + Type + String + Value + + + SimulateFBOFailure Comment diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 1bb3d65e05..38fdaea9d8 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -311,6 +311,16 @@ void set_flags_and_update_appearance() // true when all initialization done. bool idle_startup() { + // FIXME asset-http - this configuration stuff is temporary + // construction; once it is always on for certain types + // and we can remove the setting. + static bool va_types_initialized = false; + if (!va_types_initialized) + { + va_types_initialized = true; + LLAssetType::setFetchWithVACapConfigString(gSavedSettings.getString("ViewerAssetHttpTypes")); + } + const F32 PRECACHING_DELAY = gSavedSettings.getF32("PrecachingDelay"); static LLTimer timeout; diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index db71849659..5b51888547 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -127,6 +127,13 @@ static bool handleDebugAvatarJointsChanged(const LLSD& newvalue) return true; } +static bool handleViewerAssetHttpTypesChanged(const LLSD& newvalue) +{ + std::string new_string = newvalue.asString(); + LLAssetType::setFetchWithVACapConfigString(new_string); + return true; +} + static bool handleSetShaderChanged(const LLSD& newvalue) { // changing shader level may invalidate existing cached bump maps, as the shader type determines the format of the bump map it expects - clear and repopulate the bump cache @@ -750,6 +757,7 @@ void settings_setup_listeners() gSavedSettings.getControl("SpellCheckDictionary")->getSignal()->connect(boost::bind(&handleSpellCheckChanged)); gSavedSettings.getControl("LoginLocation")->getSignal()->connect(boost::bind(&handleLoginLocationChanged)); gSavedSettings.getControl("DebugAvatarJoints")->getCommitSignal()->connect(boost::bind(&handleDebugAvatarJointsChanged, _2)); + gSavedSettings.getControl("ViewerAssetHttpTypes")->getCommitSignal()->connect(boost::bind(&handleViewerAssetHttpTypesChanged, _2)); } #if TEST_CACHED_CONTROL -- cgit v1.2.3 From 7d375ed9b7070e354a8072d7ffa137e005691dcd Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 15 Feb 2017 15:34:56 -0500 Subject: SL-409 - WIP on http fetching path for assets --- indra/llmessage/llassetstorage.cpp | 69 ++++++++++--------- indra/llmessage/llassetstorage.h | 4 ++ indra/newview/llviewerassetstorage.cpp | 118 +++++++++++++++++++++++++++++++++ indra/newview/llviewerassetstorage.h | 27 +++++++- indra/newview/llviewerregion.cpp | 1 + 5 files changed, 185 insertions(+), 34 deletions(-) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index b85417c23f..78c7462286 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -544,12 +544,48 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, << "." << LLAssetType::lookup(type) << LL_ENDL; } - // This can be overridden by subclasses _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority); } } +// static +void LLAssetStorage::removeAndCallbackPendingDownloads(S32 result, const LLUUID& file_id, LLAssetType::EType file_type, + const LLUUID& callback_id, LLAssetType::EType callback_type, + LLExtStat ext_status) +{ + // find and callback ALL pending requests for this UUID + // SJB: We process the callbacks in reverse order, I do not know if this is important, + // but I didn't want to mess with it. + request_list_t requests; + for (request_list_t::iterator iter = gAssetStorage->mPendingDownloads.begin(); + iter != gAssetStorage->mPendingDownloads.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* tmp = *curiter; + if ((tmp->getUUID() == file_id) && (tmp->getType()== file_type)) + { + requests.push_front(tmp); + iter = gAssetStorage->mPendingDownloads.erase(curiter); + } + } + for (request_list_t::iterator iter = requests.begin(); + iter != requests.end(); ) + { + request_list_t::iterator curiter = iter++; + LLAssetRequest* tmp = *curiter; + if (tmp->mDownCallback) + { + if (result != LL_ERR_NOERR) + { + add(sFailedDownloadCount, 1); + } + tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result, ext_status); + } + delete tmp; + } +} + void LLAssetStorage::downloadCompleteCallback( S32 result, const LLUUID& file_id, @@ -608,36 +644,7 @@ void LLAssetStorage::downloadCompleteCallback( } } - // find and callback ALL pending requests for this UUID - // SJB: We process the callbacks in reverse order, I do not know if this is important, - // but I didn't want to mess with it. - request_list_t requests; - for (request_list_t::iterator iter = gAssetStorage->mPendingDownloads.begin(); - iter != gAssetStorage->mPendingDownloads.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* tmp = *curiter; - if ((tmp->getUUID() == file_id) && (tmp->getType()== file_type)) - { - requests.push_front(tmp); - iter = gAssetStorage->mPendingDownloads.erase(curiter); - } - } - for (request_list_t::iterator iter = requests.begin(); - iter != requests.end(); ) - { - request_list_t::iterator curiter = iter++; - LLAssetRequest* tmp = *curiter; - if (tmp->mDownCallback) - { - if (result != LL_ERR_NOERR) - { - add(sFailedDownloadCount, 1); - } - tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result, ext_status); - } - delete tmp; - } + removeAndCallbackPendingDownloads(result, file_id, file_type, callback_id, callback_type, ext_status); } void LLAssetStorage::getEstateAsset( diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 853387a595..e1c6028fa0 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -319,6 +319,10 @@ public: const LLUUID& asset_id); + static void removeAndCallbackPendingDownloads(S32 result, const LLUUID& file_id, LLAssetType::EType file_type, + const LLUUID& callback_id, LLAssetType::EType callback_type, + LLExtStat ext_status); + // download process callbacks static void downloadCompleteCallback( S32 result, diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index b5e68c0617..628eb9ec89 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -33,12 +33,16 @@ #include "message.h" #include "llagent.h" +#include "llviewerregion.h" // FIXME asset-http: We are the only customer for gTransferManager - the // whole class can be yanked once everything is http-ified. #include "lltransfersourceasset.h" #include "lltransfertargetvfile.h" #include "llviewerassetstats.h" +#include "llcoros.h" +#include "lleventcoro.h" +#include "llsdutil.h" ///---------------------------------------------------------------------------- /// LLViewerAssetRequest @@ -344,6 +348,26 @@ void LLViewerAssetStorage::_queueDataRequest( BOOL duplicate, BOOL is_priority) { + if (LLAssetType::lookupFetchWithVACap(atype)) + { + queueRequestHttp(uuid, atype, callback, user_data, duplicate, is_priority); + } + else + { + queueRequestUDP(uuid, atype, callback, user_data, duplicate, is_priority); + } +} + +void LLViewerAssetStorage::queueRequestUDP( + const LLUUID& uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data, + BOOL duplicate, + BOOL is_priority) +{ + LL_DEBUGS("ViewerAsset") << "Request asset via UDP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + if (mUpstreamHost.isOk()) { // stash the callback info so we can find it after we get the response message @@ -390,3 +414,97 @@ void LLViewerAssetStorage::_queueDataRequest( } } +void LLViewerAssetStorage::queueRequestHttp( + const LLUUID& uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data, + BOOL duplicate, + BOOL is_priority) +{ + LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + std::string cap_url = gAgent.getRegion()->getCapability("ViewerAsset"); + if (cap_url.empty()) + { + LL_WARNS() << "No ViewerAsset cap found, fetch fails" << LL_ENDL; + // TODO: handle waiting for caps? Other failure mechanism? + return; + } + else + { + LL_DEBUGS("ViewerAsset") << "Will fetch via ViewerAsset cap " << cap_url << LL_ENDL; + + LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype); + req->mDownCallback = callback; + req->mUserData = user_data; + req->mIsPriority = is_priority; + if (!duplicate) + { + // Only collect metrics for non-duplicate requests. Others + // are piggy-backing and will artificially lower averages. + req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + mPendingDownloads.push_back(req); + + // TODO AssetStatsFF stuff from UDP too? + + // This is the same as the current UDP logic - don't re-request a duplicate. + if (!duplicate) + { + LLCoros::instance().launch("LLViewerAssetStorage::assetRequestCoro", + boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, uuid, atype, callback, user_data, duplicate, is_priority)); + } + } +} + +void LLViewerAssetStorage::assetRequestCoro( + const LLUUID& uuid, + LLAssetType::EType atype, + LLGetAssetCallback callback, + void *user_data, + BOOL duplicate, + BOOL is_priority) +{ + std::string url = getAssetURL(uuid,atype); + LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; + + // TODO: what about duplicates? + + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("assetRequestCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + if (!status) + { + // TODO: handle failures + LL_DEBUGS("ViewerAsset") << "request failed, status " << status.toTerseString() << ", now what?" << LL_ENDL; + } + else + { + LL_DEBUGS("ViewerAsset") << "request succeeded" << LL_ENDL; + + LL_DEBUGS("ViewerAsset") << "result: " << ll_pretty_print_sd(httpResults) << LL_ENDL; + + // TODO: Use asset data to create the asset + + // Clean up pending downloads and trigger callbacks + // TODO: what are result_code and ext_status? + S32 result_code = LL_ERR_NOERR; + LLExtStat ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(result_code, uuid, atype, uuid, atype, ext_status); + } +} + +std::string LLViewerAssetStorage::getAssetURL(const LLUUID& uuid, LLAssetType::EType atype) +{ + std::string cap_url = gAgent.getRegion()->getCapability("ViewerAsset"); + std::string type_name = LLAssetType::lookup(atype); + std::string url = cap_url + "/?" + type_name + "_id=" + uuid.asString(); + return url; +} diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 4643fccbc1..474330c7c9 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -28,7 +28,6 @@ #define LLVIEWERASSETSTORAGE_H #include "llassetstorage.h" -//#include "curl/curl.h" class LLVFile; @@ -64,8 +63,6 @@ public: F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT); protected: - using LLAssetStorage::_queueDataRequest; - // virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type, @@ -73,6 +70,30 @@ protected: void *user_data, BOOL duplicate, BOOL is_priority); + + void queueRequestUDP(const LLUUID& uuid, + LLAssetType::EType type, + void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + void *user_data, + BOOL duplicate, + BOOL is_priority); + + void queueRequestHttp(const LLUUID& uuid, + LLAssetType::EType type, + void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + void *user_data, + BOOL duplicate, + BOOL is_priority); + + void assetRequestCoro(const LLUUID& uuid, + LLAssetType::EType type, + void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), + void *user_data, + BOOL duplicate, + BOOL is_priority); + + std::string getAssetURL(const LLUUID& uuid, LLAssetType::EType atype); + }; #endif diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 899ab3a371..0188208cfd 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -2895,6 +2895,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("UpdateScriptAgent"); capabilityNames.append("UpdateScriptTask"); capabilityNames.append("UploadBakedTexture"); + capabilityNames.append("ViewerAsset"); capabilityNames.append("ViewerMetrics"); capabilityNames.append("ViewerStartAuction"); capabilityNames.append("ViewerStats"); -- cgit v1.2.3 From 976b4f91ef3cfd36c0007d5b10d71c92b8bb3c44 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Fri, 17 Feb 2017 09:04:28 -0500 Subject: SL-409 - more WIP on http fetching path for assets, capture results to LLVFile. --- indra/llmessage/llassetstorage.cpp | 26 +++++------------- indra/llmessage/llassetstorage.h | 4 +-- indra/newview/llstartup.cpp | 2 +- indra/newview/llviewerassetstorage.cpp | 49 +++++++++++++++++++++++----------- indra/newview/llviewerassetstorage.h | 6 ++--- 5 files changed, 45 insertions(+), 42 deletions(-) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 78c7462286..7bf886ef26 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -546,13 +546,12 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority); } - } // static -void LLAssetStorage::removeAndCallbackPendingDownloads(S32 result, const LLUUID& file_id, LLAssetType::EType file_type, +void LLAssetStorage::removeAndCallbackPendingDownloads(const LLUUID& file_id, LLAssetType::EType file_type, const LLUUID& callback_id, LLAssetType::EType callback_type, - LLExtStat ext_status) + S32 result_code, LLExtStat ext_status) { // find and callback ALL pending requests for this UUID // SJB: We process the callbacks in reverse order, I do not know if this is important, @@ -576,11 +575,11 @@ void LLAssetStorage::removeAndCallbackPendingDownloads(S32 result, const LLUUID& LLAssetRequest* tmp = *curiter; if (tmp->mDownCallback) { - if (result != LL_ERR_NOERR) + if (result_code!= LL_ERR_NOERR) { add(sFailedDownloadCount, 1); } - tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result, ext_status); + tmp->mDownCallback(gAssetStorage->mVFS, callback_id, callback_type, tmp->mUserData, result_code, ext_status); } delete tmp; } @@ -644,7 +643,7 @@ void LLAssetStorage::downloadCompleteCallback( } } - removeAndCallbackPendingDownloads(result, file_id, file_type, callback_id, callback_type, ext_status); + removeAndCallbackPendingDownloads(file_id, file_type, callback_id, callback_type, ext_status, result); } void LLAssetStorage::getEstateAsset( @@ -806,19 +805,6 @@ void LLAssetStorage::getInvItemAsset( { LL_DEBUGS() << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << LL_ENDL; - // - // Probably will get rid of this early out? - // - //if (asset_id.isNull()) - //{ - // // Special case early out for NULL uuid - // if (callback) - // { - // callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE); - // } - // return; - //} - bool exists = false; U32 size = 0; @@ -879,6 +865,8 @@ void LLAssetStorage::getInvItemAsset( spi.setInvItem(owner_id, task_id, item_id); spi.setAsset(asset_id, atype); + LL_DEBUGS("ViewerAsset") << "requesting inv item id " << item_id << " asset_id " << asset_id << " type " << LLAssetType::lookup(atype) << LL_ENDL; + // Set our destination file, and the completion callback. LLTransferTargetParamsVFile tpvf; tpvf.setAsset(asset_id, atype); diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index e1c6028fa0..2ec8ac31b4 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -319,9 +319,9 @@ public: const LLUUID& asset_id); - static void removeAndCallbackPendingDownloads(S32 result, const LLUUID& file_id, LLAssetType::EType file_type, + static void removeAndCallbackPendingDownloads(const LLUUID& file_id, LLAssetType::EType file_type, const LLUUID& callback_id, LLAssetType::EType callback_type, - LLExtStat ext_status); + S32 result_code, LLExtStat ext_status); // download process callbacks static void downloadCompleteCallback( diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 38fdaea9d8..0b41e35455 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -313,7 +313,7 @@ bool idle_startup() { // FIXME asset-http - this configuration stuff is temporary // construction; once it is always on for certain types - // and we can remove the setting. + // we can remove the setting. static bool va_types_initialized = false; if (!va_types_initialized) { diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 628eb9ec89..3bb2b531dc 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -35,8 +35,6 @@ #include "llagent.h" #include "llviewerregion.h" -// FIXME asset-http: We are the only customer for gTransferManager - the -// whole class can be yanked once everything is http-ified. #include "lltransfersourceasset.h" #include "lltransfertargetvfile.h" #include "llviewerassetstats.h" @@ -427,7 +425,7 @@ void LLViewerAssetStorage::queueRequestHttp( if (cap_url.empty()) { LL_WARNS() << "No ViewerAsset cap found, fetch fails" << LL_ENDL; - // TODO: handle waiting for caps? Other failure mechanism? + // TODO asset-http: handle waiting for caps? Other failure mechanism? return; } else @@ -446,13 +444,13 @@ void LLViewerAssetStorage::queueRequestHttp( } mPendingDownloads.push_back(req); - // TODO AssetStatsFF stuff from UDP too? - // This is the same as the current UDP logic - don't re-request a duplicate. if (!duplicate) { + LLViewerAssetStatsFF::record_enqueue(atype, false, false); + LLCoros::instance().launch("LLViewerAssetStorage::assetRequestCoro", - boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, uuid, atype, callback, user_data, duplicate, is_priority)); + boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, uuid, atype, callback, user_data)); } } } @@ -461,28 +459,24 @@ void LLViewerAssetStorage::assetRequestCoro( const LLUUID& uuid, LLAssetType::EType atype, LLGetAssetCallback callback, - void *user_data, - BOOL duplicate, - BOOL is_priority) + void *user_data) { std::string url = getAssetURL(uuid,atype); LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; - // TODO: what about duplicates? - LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("assetRequestCoro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLCore::HttpOptions::ptr_t httpOpts = LLCore::HttpOptions::ptr_t(new LLCore::HttpOptions); - LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts); + LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) { - // TODO: handle failures + // TODO asset-http: handle failures LL_DEBUGS("ViewerAsset") << "request failed, status " << status.toTerseString() << ", now what?" << LL_ENDL; } else @@ -491,13 +485,36 @@ void LLViewerAssetStorage::assetRequestCoro( LL_DEBUGS("ViewerAsset") << "result: " << ll_pretty_print_sd(httpResults) << LL_ENDL; - // TODO: Use asset data to create the asset + const LLSD::Binary &raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); + + S32 size = raw.size(); + if (size > 0) + { + // This create-then-rename flow is modeled on LLTransferTargetVFile, which is what's used in the UDP case. + LLUUID temp_id; + temp_id.generate(); + LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); + vf.setMaxSize(size); + if (!vf.write(raw.data(),size)) + { + // TODO asset-http: handle error + LL_ERRS() << "Failure in vf.write()" << LL_ENDL; + } + if (!vf.rename(uuid, atype)) + { + LL_ERRS() << "rename failed" << LL_ENDL; + } + } + else + { + // TODO asset-http: handle invalid size case + } // Clean up pending downloads and trigger callbacks - // TODO: what are result_code and ext_status? + // TODO asset-http: what are the result_code and ext_status? S32 result_code = LL_ERR_NOERR; LLExtStat ext_status = LL_EXSTAT_NONE; - removeAndCallbackPendingDownloads(result_code, uuid, atype, uuid, atype, ext_status); + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); } } diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 474330c7c9..d28a8a276f 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -86,11 +86,9 @@ protected: BOOL is_priority); void assetRequestCoro(const LLUUID& uuid, - LLAssetType::EType type, + LLAssetType::EType atype, void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), - void *user_data, - BOOL duplicate, - BOOL is_priority); + void *user_data); std::string getAssetURL(const LLUUID& uuid, LLAssetType::EType atype); -- cgit v1.2.3 From 578e927641477bfbd032adbe7717fda45c0cc0ea Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 27 Feb 2017 14:52:08 -0500 Subject: SL-409 - viewer asset stats updates to distinguish http and udp fetches --- indra/newview/llviewerassetstats.cpp | 191 +++++++++++++----------- indra/newview/llviewerassetstats.h | 16 +- indra/newview/llviewerassetstorage.cpp | 29 ++-- indra/newview/tests/llviewerassetstats_test.cpp | 13 +- 4 files changed, 142 insertions(+), 107 deletions(-) diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 54ac29723f..012da212a9 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -90,90 +90,32 @@ namespace LLViewerAssetStatsFF // - wearables (clothing, bodyparts) which directly affect // user experiences when they log in // - sounds - // - gestures + // - gestures, including animations // - everything else. // - llassert_always(50 == LLViewerAssetType::AT_COUNT); - // Multiple asset definitions are floating around so this requires some - // maintenance and attention. - static const EViewerAssetCategories asset_to_bin_map[LLViewerAssetType::AT_COUNT] = - { - EVACTextureTempHTTPGet, // (0) AT_TEXTURE - EVACSoundUDPGet, // AT_SOUND - EVACOtherGet, // AT_CALLINGCARD - EVACOtherGet, // AT_LANDMARK - EVACOtherGet, // AT_SCRIPT - EVACWearableUDPGet, // AT_CLOTHING - EVACOtherGet, // AT_OBJECT - EVACOtherGet, // AT_NOTECARD - EVACOtherGet, // AT_CATEGORY - EVACOtherGet, // AT_ROOT_CATEGORY - EVACOtherGet, // (10) AT_LSL_TEXT - EVACOtherGet, // AT_LSL_BYTECODE - EVACOtherGet, // AT_TEXTURE_TGA - EVACWearableUDPGet, // AT_BODYPART - EVACOtherGet, // AT_TRASH - EVACOtherGet, // AT_SNAPSHOT_CATEGORY - EVACOtherGet, // AT_LOST_AND_FOUND - EVACSoundUDPGet, // AT_SOUND_WAV - EVACOtherGet, // AT_IMAGE_TGA - EVACOtherGet, // AT_IMAGE_JPEG - EVACGestureUDPGet, // (20) AT_ANIMATION - EVACGestureUDPGet, // AT_GESTURE - EVACOtherGet, // AT_SIMSTATE - EVACOtherGet, // AT_FAVORITE - EVACOtherGet, // AT_LINK - EVACOtherGet, // AT_LINK_FOLDER - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // (30) - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // (40) - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // - EVACOtherGet, // AT_MESH - // (50) - }; - - if (at < 0 || at >= LLViewerAssetType::AT_COUNT) - { - return EVACOtherGet; - } - EViewerAssetCategories ret(asset_to_bin_map[at]); - if (EVACTextureTempHTTPGet == ret) - { - // Indexed with [is_temp][with_http] - static const EViewerAssetCategories texture_bin_map[2][2] = - { - { - EVACTextureNonTempUDPGet, - EVACTextureNonTempHTTPGet, - }, - { - EVACTextureTempUDPGet, - EVACTextureTempHTTPGet, - } - }; - - ret = texture_bin_map[is_temp][with_http]; - } + EViewerAssetCategories ret(EVACOtherGet); + switch (at) + { + case LLAssetType::AT_TEXTURE: + if (is_temp) + ret = with_http ? EVACTextureTempHTTPGet : EVACTextureTempUDPGet; + else + ret = with_http ? EVACTextureNonTempHTTPGet : EVACTextureNonTempUDPGet; + break; + case LLAssetType::AT_SOUND: + case LLAssetType::AT_SOUND_WAV: + ret = with_http ? EVACSoundHTTPGet : EVACSoundUDPGet; + break; + case LLAssetType::AT_CLOTHING: + case LLAssetType::AT_BODYPART: + ret = with_http ? EVACWearableHTTPGet : EVACWearableUDPGet; + break; + case LLAssetType::AT_ANIMATION: + case LLAssetType::AT_GESTURE: + ret = with_http ? EVACGestureHTTPGet : EVACGestureUDPGet; + break; + } return ret; } @@ -185,12 +127,18 @@ namespace LLViewerAssetStatsFF "Number of texture asset http requests enqueued"), sEnqueueAssetRequestsNonTempTextureUDP ("enqueuedassetrequestsnontemptextureudp", "Number of texture asset udp requests enqueued"), + sEnqueuedAssetRequestsWearableHTTP ("enqueuedassetrequestswearablehttp", + "Number of wearable asset http requests enqueued"), sEnqueuedAssetRequestsWearableUdp ("enqueuedassetrequestswearableudp", - "Number of wearable asset requests enqueued"), + "Number of wearable asset udp requests enqueued"), + sEnqueuedAssetRequestsSoundHTTP ("enqueuedassetrequestssoundhttp", + "Number of sound asset http requests enqueued"), sEnqueuedAssetRequestsSoundUdp ("enqueuedassetrequestssoundudp", - "Number of sound asset requests enqueued"), + "Number of sound asset udp requests enqueued"), + sEnqueuedAssetRequestsGestureHTTP ("enqueuedassetrequestsgesturehttp", + "Number of gesture asset http requests enqueued"), sEnqueuedAssetRequestsGestureUdp ("enqueuedassetrequestsgestureudp", - "Number of gesture asset requests enqueued"), + "Number of gesture asset udp requests enqueued"), sEnqueuedAssetRequestsOther ("enqueuedassetrequestsother", "Number of other asset requests enqueued"); @@ -199,8 +147,11 @@ namespace LLViewerAssetStatsFF &sEnqueueAssetRequestsTempTextureUDP, &sEnqueueAssetRequestsNonTempTextureHTTP, &sEnqueueAssetRequestsNonTempTextureUDP, + &sEnqueuedAssetRequestsWearableHTTP, &sEnqueuedAssetRequestsWearableUdp, + &sEnqueuedAssetRequestsSoundHTTP, &sEnqueuedAssetRequestsSoundUdp, + &sEnqueuedAssetRequestsGestureHTTP, &sEnqueuedAssetRequestsGestureUdp, &sEnqueuedAssetRequestsOther }; @@ -213,12 +164,18 @@ namespace LLViewerAssetStatsFF "Number of texture asset http requests dequeued"), sDequeueAssetRequestsNonTempTextureUDP ("dequeuedassetrequestsnontemptextureudp", "Number of texture asset udp requests dequeued"), + sDequeuedAssetRequestsWearableHTTP ("dequeuedassetrequestswearablehttp", + "Number of wearable asset http requests dequeued"), sDequeuedAssetRequestsWearableUdp ("dequeuedassetrequestswearableudp", - "Number of wearable asset requests dequeued"), + "Number of wearable asset udp requests dequeued"), + sDequeuedAssetRequestsSoundHTTP ("dequeuedassetrequestssoundhttp", + "Number of sound asset http requests dequeued"), sDequeuedAssetRequestsSoundUdp ("dequeuedassetrequestssoundudp", - "Number of sound asset requests dequeued"), + "Number of sound asset udp requests dequeued"), + sDequeuedAssetRequestsGestureHTTP ("dequeuedassetrequestsgesturehttp", + "Number of gesture asset http requests dequeued"), sDequeuedAssetRequestsGestureUdp ("dequeuedassetrequestsgestureudp", - "Number of gesture asset requests dequeued"), + "Number of gesture asset udp requests dequeued"), sDequeuedAssetRequestsOther ("dequeuedassetrequestsother", "Number of other asset requests dequeued"); @@ -227,8 +184,11 @@ namespace LLViewerAssetStatsFF &sDequeueAssetRequestsTempTextureUDP, &sDequeueAssetRequestsNonTempTextureHTTP, &sDequeueAssetRequestsNonTempTextureUDP, + &sDequeuedAssetRequestsWearableHTTP, &sDequeuedAssetRequestsWearableUdp, + &sDequeuedAssetRequestsSoundHTTP, &sDequeuedAssetRequestsSoundUdp, + &sDequeuedAssetRequestsGestureHTTP, &sDequeuedAssetRequestsGestureUdp, &sDequeuedAssetRequestsOther }; @@ -241,12 +201,18 @@ namespace LLViewerAssetStatsFF "Time spent responding to texture asset http requests"), sResponseAssetRequestsNonTempTextureUDP ("assetresponsetimesnontemptextureudp", "Time spent responding to texture asset udp requests"), + sResponsedAssetRequestsWearableHTTP ("assetresponsetimeswearablehttp", + "Time spent responding to wearable asset http requests"), sResponsedAssetRequestsWearableUdp ("assetresponsetimeswearableudp", - "Time spent responding to wearable asset requests"), + "Time spent responding to wearable asset udp requests"), + sResponsedAssetRequestsSoundHTTP ("assetresponsetimessounduhttp", + "Time spent responding to sound asset http requests"), sResponsedAssetRequestsSoundUdp ("assetresponsetimessoundudp", - "Time spent responding to sound asset requests"), + "Time spent responding to sound asset udp requests"), + sResponsedAssetRequestsGestureHTTP ("assetresponsetimesgesturehttp", + "Time spent responding to gesture asset http requests"), sResponsedAssetRequestsGestureUdp ("assetresponsetimesgestureudp", - "Time spent responding to gesture asset requests"), + "Time spent responding to gesture asset udp requests"), sResponsedAssetRequestsOther ("assetresponsetimesother", "Time spent responding to other asset requests"); @@ -255,8 +221,11 @@ namespace LLViewerAssetStatsFF &sResponseAssetRequestsTempTextureUDP, &sResponseAssetRequestsNonTempTextureHTTP, &sResponseAssetRequestsNonTempTextureUDP, + &sResponsedAssetRequestsWearableHTTP, &sResponsedAssetRequestsWearableUdp, + &sResponsedAssetRequestsSoundHTTP, &sResponsedAssetRequestsSoundUdp, + &sResponsedAssetRequestsGestureHTTP, &sResponsedAssetRequestsGestureUdp, &sResponsedAssetRequestsOther }; @@ -415,6 +384,19 @@ void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) .resp_mean(rec.getMean(*sResponse[EVACTextureNonTempUDPGet]).value()); } + if (!compact_output + || rec.getSum(*sEnqueued[EVACWearableHTTPGet]) + || rec.getSum(*sDequeued[EVACWearableHTTPGet]) + || rec.getSum(*sResponse[EVACWearableHTTPGet]).value()) + { + r.get_wearable_http .enqueued((S32)rec.getSum(*sEnqueued[EVACWearableHTTPGet])) + .dequeued((S32)rec.getSum(*sDequeued[EVACWearableHTTPGet])) + .resp_count((S32)rec.getSum(*sResponse[EVACWearableHTTPGet]).value()) + .resp_min(rec.getMin(*sResponse[EVACWearableHTTPGet]).value()) + .resp_max(rec.getMax(*sResponse[EVACWearableHTTPGet]).value()) + .resp_mean(rec.getMean(*sResponse[EVACWearableHTTPGet]).value()); + } + if (!compact_output || rec.getSum(*sEnqueued[EVACWearableUDPGet]) || rec.getSum(*sDequeued[EVACWearableUDPGet]) @@ -428,6 +410,19 @@ void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) .resp_mean(rec.getMean(*sResponse[EVACWearableUDPGet]).value()); } + if (!compact_output + || rec.getSum(*sEnqueued[EVACSoundHTTPGet]) + || rec.getSum(*sDequeued[EVACSoundHTTPGet]) + || rec.getSum(*sResponse[EVACSoundHTTPGet]).value()) + { + r.get_sound_http.enqueued((S32)rec.getSum(*sEnqueued[EVACSoundHTTPGet])) + .dequeued((S32)rec.getSum(*sDequeued[EVACSoundHTTPGet])) + .resp_count((S32)rec.getSum(*sResponse[EVACSoundHTTPGet]).value()) + .resp_min(rec.getMin(*sResponse[EVACSoundHTTPGet]).value()) + .resp_max(rec.getMax(*sResponse[EVACSoundHTTPGet]).value()) + .resp_mean(rec.getMean(*sResponse[EVACSoundHTTPGet]).value()); + } + if (!compact_output || rec.getSum(*sEnqueued[EVACSoundUDPGet]) || rec.getSum(*sDequeued[EVACSoundUDPGet]) @@ -441,6 +436,19 @@ void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) .resp_mean(rec.getMean(*sResponse[EVACSoundUDPGet]).value()); } + if (!compact_output + || rec.getSum(*sEnqueued[EVACGestureHTTPGet]) + || rec.getSum(*sDequeued[EVACGestureHTTPGet]) + || rec.getSum(*sResponse[EVACGestureHTTPGet]).value()) + { + r.get_gesture_http .enqueued((S32)rec.getSum(*sEnqueued[EVACGestureHTTPGet])) + .dequeued((S32)rec.getSum(*sDequeued[EVACGestureHTTPGet])) + .resp_count((S32)rec.getSum(*sResponse[EVACGestureHTTPGet]).value()) + .resp_min(rec.getMin(*sResponse[EVACGestureHTTPGet]).value()) + .resp_max(rec.getMax(*sResponse[EVACGestureHTTPGet]).value()) + .resp_mean(rec.getMean(*sResponse[EVACGestureHTTPGet]).value()); + } + if (!compact_output || rec.getSum(*sEnqueued[EVACGestureUDPGet]) || rec.getSum(*sDequeued[EVACGestureUDPGet]) @@ -576,8 +584,11 @@ LLViewerAssetStats::RegionStats::RegionStats() get_texture_temp_udp("get_texture_temp_udp"), get_texture_non_temp_http("get_texture_non_temp_http"), get_texture_non_temp_udp("get_texture_non_temp_udp"), + get_wearable_http("get_wearable_http"), get_wearable_udp("get_wearable_udp"), + get_sound_http("get_sound_http"), get_sound_udp("get_sound_udp"), + get_gesture_http("get_gesture_http"), get_gesture_udp("get_gesture_udp"), get_other("get_other"), fps("fps"), diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h index 9d425c82fc..ddddf7883b 100644 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -104,7 +104,7 @@ public: }; struct FPSStats : public LLInitParam::Block - { + { Mandatory count; Mandatory min, max, @@ -113,13 +113,16 @@ public: }; struct RegionStats : public LLInitParam::Block - { + { Optional get_texture_temp_http, get_texture_temp_udp, get_texture_non_temp_http, get_texture_non_temp_udp, + get_wearable_http, get_wearable_udp, + get_sound_http, get_sound_udp, + get_gesture_http, get_gesture_udp, get_other; Optional fps; @@ -211,9 +214,12 @@ namespace LLViewerAssetStatsFF EVACTextureTempUDPGet, //< Texture GETs - temp/baked, UDP EVACTextureNonTempHTTPGet, //< Texture GETs - perm, HTTP EVACTextureNonTempUDPGet, //< Texture GETs - perm, UDP - EVACWearableUDPGet, //< Wearable GETs - EVACSoundUDPGet, //< Sound GETs - EVACGestureUDPGet, //< Gesture GETs + EVACWearableHTTPGet, //< Wearable GETs HTTP + EVACWearableUDPGet, //< Wearable GETs UDP + EVACSoundHTTPGet, //< Sound GETs HTTP + EVACSoundUDPGet, //< Sound GETs UDP + EVACGestureHTTPGet, //< Gesture GETs HTTP + EVACGestureUDPGet, //< Gesture GETs UDP EVACOtherGet, //< Other GETs EVACCount // Must be last diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 3bb2b531dc..347810e169 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -56,9 +56,10 @@ class LLViewerAssetRequest : public LLAssetRequest { public: - LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type) + LLViewerAssetRequest(const LLUUID &uuid, const LLAssetType::EType type, bool with_http) : LLAssetRequest(uuid, type), - mMetricsStartTime(0) + mMetricsStartTime(0), + mWithHTTP(with_http) { } @@ -78,8 +79,8 @@ protected: { // Okay, it appears this request was used for useful things. Record // the expected dequeue and duration of request processing. - LLViewerAssetStatsFF::record_dequeue(mType, false, false); - LLViewerAssetStatsFF::record_response(mType, false, false, + LLViewerAssetStatsFF::record_dequeue(mType, mWithHTTP, false); + LLViewerAssetStatsFF::record_response(mType, mWithHTTP, false, (LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime)); mMetricsStartTime = (U32Seconds)0; @@ -88,6 +89,7 @@ protected: public: LLViewerAssetStats::duration_t mMetricsStartTime; + bool mWithHTTP; }; ///---------------------------------------------------------------------------- @@ -369,7 +371,8 @@ void LLViewerAssetStorage::queueRequestUDP( if (mUpstreamHost.isOk()) { // stash the callback info so we can find it after we get the response message - LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype); + bool with_http = false; + LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); req->mDownCallback = callback; req->mUserData = user_data; req->mIsPriority = is_priority; @@ -398,7 +401,9 @@ void LLViewerAssetStorage::queueRequestUDP( LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - LLViewerAssetStatsFF::record_enqueue(atype, false, false); + bool with_http = false; + bool is_temp = false; + LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); } } else @@ -432,7 +437,8 @@ void LLViewerAssetStorage::queueRequestHttp( { LL_DEBUGS("ViewerAsset") << "Will fetch via ViewerAsset cap " << cap_url << LL_ENDL; - LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype); + bool with_http = true; + LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); req->mDownCallback = callback; req->mUserData = user_data; req->mIsPriority = is_priority; @@ -447,7 +453,9 @@ void LLViewerAssetStorage::queueRequestHttp( // This is the same as the current UDP logic - don't re-request a duplicate. if (!duplicate) { - LLViewerAssetStatsFF::record_enqueue(atype, false, false); + bool with_http = true; + bool is_temp = false; + LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); LLCoros::instance().launch("LLViewerAssetStorage::assetRequestCoro", boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, uuid, atype, callback, user_data)); @@ -481,9 +489,9 @@ void LLViewerAssetStorage::assetRequestCoro( } else { - LL_DEBUGS("ViewerAsset") << "request succeeded" << LL_ENDL; + LL_DEBUGS("ViewerAsset") << "request succeeded, url " << url << LL_ENDL; - LL_DEBUGS("ViewerAsset") << "result: " << ll_pretty_print_sd(httpResults) << LL_ENDL; + // LL_DEBUGS("ViewerAsset") << "result: " << ll_pretty_print_sd(httpResults) << LL_ENDL; const LLSD::Binary &raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); @@ -508,6 +516,7 @@ void LLViewerAssetStorage::assetRequestCoro( else { // TODO asset-http: handle invalid size case + LL_ERRS() << "bad size" << LL_ENDL; } // Clean up pending downloads and trigger callbacks diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index a08e32cb49..ed3051893e 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -76,8 +76,11 @@ static const char * all_keys[] = "get_texture_temp_udp", "get_texture_non_temp_http", "get_texture_non_temp_udp", + "get_wearable_http", "get_wearable_udp", + "get_sound_http", "get_sound_udp", + "get_gesture_http", "get_gesture_udp" }; @@ -88,8 +91,11 @@ static const char * resp_keys[] = "get_texture_temp_udp", "get_texture_non_temp_http", "get_texture_non_temp_udp", + "get_wearable_http", "get_wearable_udp", + "get_sound_http", "get_sound_udp", + "get_gesture_http", "get_gesture_udp" }; @@ -540,8 +546,11 @@ namespace tut ensure("sd[get_gesture_udp][enqueued] is 0", (0 == sd["get_gesture_udp"]["enqueued"].asInteger())); ensure("sd[get_gesture_udp][dequeued] is 0", (0 == sd["get_gesture_udp"]["dequeued"].asInteger())); - ensure("sd[get_wearable_udp][enqueued] is 4", (4 == sd["get_wearable_udp"]["enqueued"].asInteger())); - ensure("sd[get_wearable_udp][dequeued] is 4", (4 == sd["get_wearable_udp"]["dequeued"].asInteger())); + ensure("sd[get_wearable_http][enqueued] is 2", (2 == sd["get_wearable_http"]["enqueued"].asInteger())); + ensure("sd[get_wearable_http][dequeued] is 2", (2 == sd["get_wearable_http"]["dequeued"].asInteger())); + + ensure("sd[get_wearable_udp][enqueued] is 2", (2 == sd["get_wearable_udp"]["enqueued"].asInteger())); + ensure("sd[get_wearable_udp][dequeued] is 2", (2 == sd["get_wearable_udp"]["dequeued"].asInteger())); ensure("sd[get_other][enqueued] is 4", (4 == sd["get_other"]["enqueued"].asInteger())); ensure("sd[get_other][dequeued] is 0", (0 == sd["get_other"]["dequeued"].asInteger())); -- cgit v1.2.3 From d4d7c39dcd383ceb666c57020aa249f07520bf1b Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 1 Mar 2017 11:19:31 -0500 Subject: SL-409 - fixed some longstanding errors with viewer asset metrics with wrong values being used for sample counts --- indra/newview/llviewerassetstats.cpp | 263 ++++++++++++----------------------- indra/newview/llviewerassetstats.h | 46 +++--- 2 files changed, 114 insertions(+), 195 deletions(-) diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index 012da212a9..e38eff9066 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -115,47 +115,65 @@ namespace LLViewerAssetStatsFF case LLAssetType::AT_GESTURE: ret = with_http ? EVACGestureHTTPGet : EVACGestureUDPGet; break; + default: + ret = EVACOtherGet; + break; } return ret; } - static LLTrace::CountStatHandle<> sEnqueueAssetRequestsTempTextureHTTP ("enqueuedassetrequeststemptexturehttp", - "Number of temporary texture asset http requests enqueued"), - sEnqueueAssetRequestsTempTextureUDP ("enqueuedassetrequeststemptextureudp", - "Number of temporary texture asset udp requests enqueued"), - sEnqueueAssetRequestsNonTempTextureHTTP("enqueuedassetrequestsnontemptexturehttp", - "Number of texture asset http requests enqueued"), - sEnqueueAssetRequestsNonTempTextureUDP ("enqueuedassetrequestsnontemptextureudp", - "Number of texture asset udp requests enqueued"), - sEnqueuedAssetRequestsWearableHTTP ("enqueuedassetrequestswearablehttp", - "Number of wearable asset http requests enqueued"), - sEnqueuedAssetRequestsWearableUdp ("enqueuedassetrequestswearableudp", - "Number of wearable asset udp requests enqueued"), - sEnqueuedAssetRequestsSoundHTTP ("enqueuedassetrequestssoundhttp", - "Number of sound asset http requests enqueued"), - sEnqueuedAssetRequestsSoundUdp ("enqueuedassetrequestssoundudp", - "Number of sound asset udp requests enqueued"), - sEnqueuedAssetRequestsGestureHTTP ("enqueuedassetrequestsgesturehttp", - "Number of gesture asset http requests enqueued"), - sEnqueuedAssetRequestsGestureUdp ("enqueuedassetrequestsgestureudp", - "Number of gesture asset udp requests enqueued"), - sEnqueuedAssetRequestsOther ("enqueuedassetrequestsother", - "Number of other asset requests enqueued"); - - static LLTrace::CountStatHandle<>* sEnqueued[EVACCount] = { - &sEnqueueAssetRequestsTempTextureHTTP, - &sEnqueueAssetRequestsTempTextureUDP, - &sEnqueueAssetRequestsNonTempTextureHTTP, - &sEnqueueAssetRequestsNonTempTextureUDP, - &sEnqueuedAssetRequestsWearableHTTP, - &sEnqueuedAssetRequestsWearableUdp, - &sEnqueuedAssetRequestsSoundHTTP, - &sEnqueuedAssetRequestsSoundUdp, - &sEnqueuedAssetRequestsGestureHTTP, - &sEnqueuedAssetRequestsGestureUdp, - &sEnqueuedAssetRequestsOther - }; - +/* Note that this is very verbose, in a way that's actually somewhat + * risky - when adding or removing a bucket, all these arrays have to + * be updated in parallel (although the risk is somewhat illusory, + * because none of the names actually affect the final XML output, so + * you just have to have the right number of distinct names). Why + * can't we just have an array of stat objects indexed by the bucket + * index? Because CountStatHandle doesn't have a default constructor, + * and is built on a big pile of template code that assumes the name + * parameter in the constructor is useful and needed and mandatory. We + * could replace these stat handles with something more accommodating + * like the LLViewerAssetStats::StatsAccumulator class, but it's hard + * to justify given how rarely this code gets changed. For now, caveat + * developer. */ + + static LLTrace::CountStatHandle<> sEnqueueAssetRequestsTempTextureHTTP ("enqueuedassetrequeststemptexturehttp", + "Number of temporary texture asset http requests enqueued"), + sEnqueueAssetRequestsTempTextureUDP ("enqueuedassetrequeststemptextureudp", + "Number of temporary texture asset udp requests enqueued"), + sEnqueueAssetRequestsNonTempTextureHTTP("enqueuedassetrequestsnontemptexturehttp", + "Number of texture asset http requests enqueued"), + sEnqueueAssetRequestsNonTempTextureUDP ("enqueuedassetrequestsnontemptextureudp", + "Number of texture asset udp requests enqueued"), + sEnqueuedAssetRequestsWearableHTTP ("enqueuedassetrequestswearablehttp", + "Number of wearable asset http requests enqueued"), + sEnqueuedAssetRequestsWearableUdp ("enqueuedassetrequestswearableudp", + "Number of wearable asset udp requests enqueued"), + sEnqueuedAssetRequestsSoundHTTP ("enqueuedassetrequestssoundhttp", + "Number of sound asset http requests enqueued"), + sEnqueuedAssetRequestsSoundUdp ("enqueuedassetrequestssoundudp", + "Number of sosund asset udp requests enqueued"), + sEnqueuedAssetRequestsGestureHTTP ("enqueuedassetrequestsgesturehttp", + "Number of gesture asset http requests enqueued"), + sEnqueuedAssetRequestsGestureUdp ("enqueuedassetrequestsgestureudp", + "Number of gesture asset udp requests enqueued"), + sEnqueuedAssetRequestsOther ("enqueuedassetrequestsother", + "Number of other asset requests enqueued"); + +//static LLTrace::CountStatHandle<> sJunkEnqueued[EVACCount]; + static LLTrace::CountStatHandle<>* sEnqueued[EVACCount] = { + &sEnqueueAssetRequestsTempTextureHTTP, + &sEnqueueAssetRequestsTempTextureUDP, + &sEnqueueAssetRequestsNonTempTextureHTTP, + &sEnqueueAssetRequestsNonTempTextureUDP, + &sEnqueuedAssetRequestsWearableHTTP, + &sEnqueuedAssetRequestsWearableUdp, + &sEnqueuedAssetRequestsSoundHTTP, + &sEnqueuedAssetRequestsSoundUdp, + &sEnqueuedAssetRequestsGestureHTTP, + &sEnqueuedAssetRequestsGestureUdp, + &sEnqueuedAssetRequestsOther + }; + static LLTrace::CountStatHandle<> sDequeueAssetRequestsTempTextureHTTP ("dequeuedassetrequeststemptexturehttp", "Number of temporary texture asset http requests dequeued"), sDequeueAssetRequestsTempTextureUDP ("dequeuedassetrequeststemptextureudp", @@ -322,6 +340,25 @@ void LLViewerAssetStats::setRegion(region_handle_t region_handle) mRegionHandle = region_handle; } +template +void LLViewerAssetStats::getStat(LLTrace::Recording& rec, T& req, LLViewerAssetStatsFF::EViewerAssetCategories cat, bool compact_output) +{ + using namespace LLViewerAssetStatsFF; + + if (!compact_output + || rec.getSampleCount(*sEnqueued[cat]) + || rec.getSampleCount(*sDequeued[cat]) + || rec.getSampleCount(*sResponse[cat])) + { + req .enqueued(rec.getSampleCount(*sEnqueued[cat])) + .dequeued(rec.getSampleCount(*sDequeued[cat])) + .resp_count(rec.getSampleCount(*sResponse[cat])) + .resp_min(rec.getMin(*sResponse[cat]).value()) + .resp_max(rec.getMax(*sResponse[cat]).value()) + .resp_mean(rec.getMean(*sResponse[cat]).value()); + } +} + void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) { using namespace LLViewerAssetStatsFF; @@ -334,147 +371,19 @@ void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) { RegionStats& r = stats.regions.add(); LLTrace::Recording& rec = it->second; - if (!compact_output - || rec.getSum(*sEnqueued[EVACTextureTempHTTPGet]) - || rec.getSum(*sDequeued[EVACTextureTempHTTPGet]) - || rec.getSum(*sResponse[EVACTextureTempHTTPGet]).value()) - { - r.get_texture_temp_http .enqueued((S32)rec.getSum(*sEnqueued[EVACTextureTempHTTPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACTextureTempHTTPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACTextureTempHTTPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACTextureTempHTTPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACTextureTempHTTPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACTextureTempHTTPGet]).value()); - } - if (!compact_output - || rec.getSum(*sEnqueued[EVACTextureTempUDPGet]) - || rec.getSum(*sDequeued[EVACTextureTempUDPGet]) - || rec.getSum(*sResponse[EVACTextureTempUDPGet]).value()) - { - r.get_texture_temp_udp .enqueued((S32)rec.getSum(*sEnqueued[EVACTextureTempUDPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACTextureTempUDPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACTextureTempUDPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACTextureTempUDPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACTextureTempUDPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACTextureTempUDPGet]).value()); - } - if (!compact_output - || rec.getSum(*sEnqueued[EVACTextureNonTempHTTPGet]) - || rec.getSum(*sDequeued[EVACTextureNonTempHTTPGet]) - || rec.getSum(*sResponse[EVACTextureNonTempHTTPGet]).value()) - { - r.get_texture_non_temp_http .enqueued((S32)rec.getSum(*sEnqueued[EVACTextureNonTempHTTPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACTextureNonTempHTTPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACTextureNonTempHTTPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACTextureNonTempHTTPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACTextureNonTempHTTPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACTextureNonTempHTTPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACTextureNonTempUDPGet]) - || rec.getSum(*sDequeued[EVACTextureNonTempUDPGet]) - || rec.getSum(*sResponse[EVACTextureNonTempUDPGet]).value()) - { - r.get_texture_non_temp_udp .enqueued((S32)rec.getSum(*sEnqueued[EVACTextureNonTempUDPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACTextureNonTempUDPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACTextureNonTempUDPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACTextureNonTempUDPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACTextureNonTempUDPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACTextureNonTempUDPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACWearableHTTPGet]) - || rec.getSum(*sDequeued[EVACWearableHTTPGet]) - || rec.getSum(*sResponse[EVACWearableHTTPGet]).value()) - { - r.get_wearable_http .enqueued((S32)rec.getSum(*sEnqueued[EVACWearableHTTPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACWearableHTTPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACWearableHTTPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACWearableHTTPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACWearableHTTPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACWearableHTTPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACWearableUDPGet]) - || rec.getSum(*sDequeued[EVACWearableUDPGet]) - || rec.getSum(*sResponse[EVACWearableUDPGet]).value()) - { - r.get_wearable_udp .enqueued((S32)rec.getSum(*sEnqueued[EVACWearableUDPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACWearableUDPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACWearableUDPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACWearableUDPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACWearableUDPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACWearableUDPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACSoundHTTPGet]) - || rec.getSum(*sDequeued[EVACSoundHTTPGet]) - || rec.getSum(*sResponse[EVACSoundHTTPGet]).value()) - { - r.get_sound_http.enqueued((S32)rec.getSum(*sEnqueued[EVACSoundHTTPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACSoundHTTPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACSoundHTTPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACSoundHTTPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACSoundHTTPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACSoundHTTPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACSoundUDPGet]) - || rec.getSum(*sDequeued[EVACSoundUDPGet]) - || rec.getSum(*sResponse[EVACSoundUDPGet]).value()) - { - r.get_sound_udp .enqueued((S32)rec.getSum(*sEnqueued[EVACSoundUDPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACSoundUDPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACSoundUDPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACSoundUDPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACSoundUDPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACSoundUDPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACGestureHTTPGet]) - || rec.getSum(*sDequeued[EVACGestureHTTPGet]) - || rec.getSum(*sResponse[EVACGestureHTTPGet]).value()) - { - r.get_gesture_http .enqueued((S32)rec.getSum(*sEnqueued[EVACGestureHTTPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACGestureHTTPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACGestureHTTPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACGestureHTTPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACGestureHTTPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACGestureHTTPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACGestureUDPGet]) - || rec.getSum(*sDequeued[EVACGestureUDPGet]) - || rec.getSum(*sResponse[EVACGestureUDPGet]).value()) - { - r.get_gesture_udp .enqueued((S32)rec.getSum(*sEnqueued[EVACGestureUDPGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACGestureUDPGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACGestureUDPGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACGestureUDPGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACGestureUDPGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACGestureUDPGet]).value()); - } - - if (!compact_output - || rec.getSum(*sEnqueued[EVACOtherGet]) - || rec.getSum(*sDequeued[EVACOtherGet]) - || rec.getSum(*sResponse[EVACOtherGet]).value()) - { - r.get_other .enqueued((S32)rec.getSum(*sEnqueued[EVACOtherGet])) - .dequeued((S32)rec.getSum(*sDequeued[EVACOtherGet])) - .resp_count((S32)rec.getSum(*sResponse[EVACOtherGet]).value()) - .resp_min(rec.getMin(*sResponse[EVACOtherGet]).value()) - .resp_max(rec.getMax(*sResponse[EVACOtherGet]).value()) - .resp_mean(rec.getMean(*sResponse[EVACOtherGet]).value()); - } + getStat(rec, r.get_texture_temp_http, EVACTextureTempHTTPGet, compact_output); + getStat(rec, r.get_texture_temp_udp, EVACTextureTempUDPGet, compact_output); + getStat(rec, r.get_texture_non_temp_http, EVACTextureNonTempHTTPGet, compact_output); + getStat(rec, r.get_texture_non_temp_udp, EVACTextureNonTempUDPGet, compact_output); + getStat(rec, r.get_wearable_http, EVACWearableHTTPGet, compact_output); + getStat(rec, r.get_wearable_udp, EVACWearableUDPGet, compact_output); + getStat(rec, r.get_sound_http, EVACSoundHTTPGet, compact_output); + getStat(rec, r.get_sound_udp, EVACSoundUDPGet, compact_output); + getStat(rec, r.get_gesture_http, EVACGestureHTTPGet, compact_output); + getStat(rec, r.get_gesture_udp, EVACGestureUDPGet, compact_output); + getStat(rec, r.get_other, EVACOtherGet, compact_output); + S32 fps = (S32)rec.getLastValue(LLStatViewer::FPS_SAMPLE); if (!compact_output || fps != 0) { diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h index ddddf7883b..88a8b9c61a 100644 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -39,6 +39,26 @@ #include "lltrace.h" #include "llinitparam.h" +namespace LLViewerAssetStatsFF +{ + enum EViewerAssetCategories + { + EVACTextureTempHTTPGet, //< Texture GETs - temp/baked, HTTP + EVACTextureTempUDPGet, //< Texture GETs - temp/baked, UDP + EVACTextureNonTempHTTPGet, //< Texture GETs - perm, HTTP + EVACTextureNonTempUDPGet, //< Texture GETs - perm, UDP + EVACWearableHTTPGet, //< Wearable GETs HTTP + EVACWearableUDPGet, //< Wearable GETs UDP + EVACSoundHTTPGet, //< Sound GETs HTTP + EVACSoundUDPGet, //< Sound GETs UDP + EVACGestureHTTPGet, //< Gesture GETs HTTP + EVACGestureUDPGet, //< Gesture GETs UDP + EVACOtherGet, //< Other GETs + + EVACCount // Must be last + }; +} + /** * @class LLViewerAssetStats * @brief Records performance aspects of asset access operations. @@ -74,6 +94,7 @@ * LLViewerAssetStatsFF is provided for conditional test-and-call * operations. */ + class LLViewerAssetStats : public LLStopWatchControlsMixin { public: @@ -94,8 +115,8 @@ public: struct AssetRequestType : public LLInitParam::Block { Mandatory enqueued, - dequeued, - resp_count; + dequeued, + resp_count; Mandatory resp_min, resp_max, resp_mean; @@ -168,6 +189,11 @@ public: // Retrieve current metrics for all visited regions (NULL region UUID/handle excluded) // Uses AssetStats structure seen above void getStats(AssetStats& stats, bool compact_output); + + // Retrieve a single asset request type (taken from a single region) + template + void getStat(LLTrace::Recording& rec, T& req, LLViewerAssetStatsFF::EViewerAssetCategories cat, bool compact_output); + LLSD asLLSD(bool compact_output); protected: @@ -208,22 +234,6 @@ extern LLViewerAssetStats * gViewerAssetStats; namespace LLViewerAssetStatsFF { - enum EViewerAssetCategories - { - EVACTextureTempHTTPGet, //< Texture GETs - temp/baked, HTTP - EVACTextureTempUDPGet, //< Texture GETs - temp/baked, UDP - EVACTextureNonTempHTTPGet, //< Texture GETs - perm, HTTP - EVACTextureNonTempUDPGet, //< Texture GETs - perm, UDP - EVACWearableHTTPGet, //< Wearable GETs HTTP - EVACWearableUDPGet, //< Wearable GETs UDP - EVACSoundHTTPGet, //< Sound GETs HTTP - EVACSoundUDPGet, //< Sound GETs UDP - EVACGestureHTTPGet, //< Gesture GETs HTTP - EVACGestureUDPGet, //< Gesture GETs UDP - EVACOtherGet, //< Other GETs - - EVACCount // Must be last - }; /** * @brief Allocation and deallocation of globals. -- cgit v1.2.3 From fdfa832f4d4a93629d1ccda11357901e42d7b428 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 1 Mar 2017 18:10:24 -0500 Subject: SL-409 - simple script to summarize viewer metrics logs of asset fetches --- scripts/metrics/viewer_asset_logs.py | 98 ++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 scripts/metrics/viewer_asset_logs.py diff --git a/scripts/metrics/viewer_asset_logs.py b/scripts/metrics/viewer_asset_logs.py new file mode 100644 index 0000000000..47f0658138 --- /dev/null +++ b/scripts/metrics/viewer_asset_logs.py @@ -0,0 +1,98 @@ +#!runpy.sh + +"""\ + +This module contains tools for analyzing viewer asset metrics logs produced by the viewer. + +$LicenseInfo:firstyear=2016&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2016, 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$ +""" + +import argparse +from lxml import etree +from llbase import llsd + +def get_metrics_record(infiles): + for filename in args.infiles: + f = open(filename) + # get an iterable + context = etree.iterparse(f, events=("start", "end")) + + # turn it into an iterator + context = iter(context) + + # get the root element + event, root = context.next() + try: + for event, elem in context: + if event == "end" and elem.tag == "llsd": + xmlstr = etree.tostring(elem, encoding="utf8", method="xml") + sd = llsd.parse_xml(xmlstr) + yield sd + except etree.XMLSyntaxError: + print "Fell off end of document" + + f.close() + +def update_stats(stats,rec): + for region in rec["regions"]: + region_key = (region["grid_x"],region["grid_y"]) + #print "region",region_key + for field, val in region.iteritems(): + if field in ["duration","grid_x","grid_y"]: + continue + if field == "fps": + # handle fps record as special case + pass + else: + #print "field",field + stats.setdefault(field,{}) + type_stats = stats.get(field) + newcount = val["resp_count"] + #print "field",field,"add count",newcount + type_stats["count"] = type_stats.get("count",0) + val["resp_count"] + #print "field",field,"count",type_stats["count"] + if (newcount>0): + type_stats["sum"] = type_stats.get("sum",0) + val["resp_count"] * val["resp_mean"] + type_stats["enqueued"] = type_stats.get("enqueued",0) + val["enqueued"] + type_stats["dequeued"] = type_stats.get("dequeued",0) + val["dequeued"] + + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description="process metric xml files for viewer asset fetching") + parser.add_argument("--verbose", action="store_true",help="verbose flag") + parser.add_argument("infiles", nargs="+", help="name of .xml files to process") + args = parser.parse_args() + + #print "process files:",args.infiles + + stats = {} + for rec in get_metrics_record(args.infiles): + #print "record",rec + + update_stats(stats,rec) + + for key, val in stats.iteritems(): + if val["count"] > 0: + print "key",key,"count",val["count"],"mean",val["sum"]/val["count"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] + -- cgit v1.2.3 From df7c58b6ebba35541224233c1ec0138001682374 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Thu, 2 Mar 2017 13:51:28 -0500 Subject: SL-409 - code simplification for asset metrics, added a couple of new categories, error handling --- indra/newview/app_settings/settings.xml | 2 +- indra/newview/llviewerassetstats.cpp | 210 +++++++++++--------------------- indra/newview/llviewerassetstats.h | 10 +- indra/newview/llviewerassetstorage.cpp | 30 +++-- 4 files changed, 96 insertions(+), 156 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8b899dc8c8..3201c44185 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15817,7 +15817,7 @@ Type String Value - + all SimulateFBOFailure diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index e38eff9066..d47f73ccce 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -80,6 +80,47 @@ * */ +namespace LLTrace +{ +// This little bit of shimmery is to allow the creation of +// default-constructed stat and event handles so we can make arrays of +// the things. + +// The only sensible way to use this function is to immediately make a +// copy of the contents, since it always returns the same pointer. +const char *makeNewAutoName() +{ + static char name[64]; + static S32 auto_namer_number = 0; + snprintf(name,64,"auto_name_%d",auto_namer_number); + auto_namer_number++; + return name; +} + +template +class DCCountStatHandle: + public CountStatHandle +{ +public: + DCCountStatHandle(const char *name = makeNewAutoName(), const char *description=NULL): + CountStatHandle(name,description) + { + } +}; + +template +class DCEventStatHandle: + public EventStatHandle +{ +public: + DCEventStatHandle(const char *name = makeNewAutoName(), const char *description=NULL): + EventStatHandle(name,description) + { + } +}; + +} + namespace LLViewerAssetStatsFF { static EViewerAssetCategories asset_type_to_category(const LLViewerAssetType::EType at, bool with_http, bool is_temp) @@ -94,7 +135,7 @@ namespace LLViewerAssetStatsFF // - everything else. // - EViewerAssetCategories ret(EVACOtherGet); + EViewerAssetCategories ret; switch (at) { case LLAssetType::AT_TEXTURE: @@ -115,138 +156,19 @@ namespace LLViewerAssetStatsFF case LLAssetType::AT_GESTURE: ret = with_http ? EVACGestureHTTPGet : EVACGestureUDPGet; break; + case LLAssetType::AT_LANDMARK: + ret = with_http ? EVACLandmarkHTTPGet : EVACLandmarkUDPGet; + break; default: - ret = EVACOtherGet; + ret = with_http ? EVACOtherHTTPGet : EVACOtherUDPGet; break; } return ret; } -/* Note that this is very verbose, in a way that's actually somewhat - * risky - when adding or removing a bucket, all these arrays have to - * be updated in parallel (although the risk is somewhat illusory, - * because none of the names actually affect the final XML output, so - * you just have to have the right number of distinct names). Why - * can't we just have an array of stat objects indexed by the bucket - * index? Because CountStatHandle doesn't have a default constructor, - * and is built on a big pile of template code that assumes the name - * parameter in the constructor is useful and needed and mandatory. We - * could replace these stat handles with something more accommodating - * like the LLViewerAssetStats::StatsAccumulator class, but it's hard - * to justify given how rarely this code gets changed. For now, caveat - * developer. */ - - static LLTrace::CountStatHandle<> sEnqueueAssetRequestsTempTextureHTTP ("enqueuedassetrequeststemptexturehttp", - "Number of temporary texture asset http requests enqueued"), - sEnqueueAssetRequestsTempTextureUDP ("enqueuedassetrequeststemptextureudp", - "Number of temporary texture asset udp requests enqueued"), - sEnqueueAssetRequestsNonTempTextureHTTP("enqueuedassetrequestsnontemptexturehttp", - "Number of texture asset http requests enqueued"), - sEnqueueAssetRequestsNonTempTextureUDP ("enqueuedassetrequestsnontemptextureudp", - "Number of texture asset udp requests enqueued"), - sEnqueuedAssetRequestsWearableHTTP ("enqueuedassetrequestswearablehttp", - "Number of wearable asset http requests enqueued"), - sEnqueuedAssetRequestsWearableUdp ("enqueuedassetrequestswearableudp", - "Number of wearable asset udp requests enqueued"), - sEnqueuedAssetRequestsSoundHTTP ("enqueuedassetrequestssoundhttp", - "Number of sound asset http requests enqueued"), - sEnqueuedAssetRequestsSoundUdp ("enqueuedassetrequestssoundudp", - "Number of sosund asset udp requests enqueued"), - sEnqueuedAssetRequestsGestureHTTP ("enqueuedassetrequestsgesturehttp", - "Number of gesture asset http requests enqueued"), - sEnqueuedAssetRequestsGestureUdp ("enqueuedassetrequestsgestureudp", - "Number of gesture asset udp requests enqueued"), - sEnqueuedAssetRequestsOther ("enqueuedassetrequestsother", - "Number of other asset requests enqueued"); - -//static LLTrace::CountStatHandle<> sJunkEnqueued[EVACCount]; - static LLTrace::CountStatHandle<>* sEnqueued[EVACCount] = { - &sEnqueueAssetRequestsTempTextureHTTP, - &sEnqueueAssetRequestsTempTextureUDP, - &sEnqueueAssetRequestsNonTempTextureHTTP, - &sEnqueueAssetRequestsNonTempTextureUDP, - &sEnqueuedAssetRequestsWearableHTTP, - &sEnqueuedAssetRequestsWearableUdp, - &sEnqueuedAssetRequestsSoundHTTP, - &sEnqueuedAssetRequestsSoundUdp, - &sEnqueuedAssetRequestsGestureHTTP, - &sEnqueuedAssetRequestsGestureUdp, - &sEnqueuedAssetRequestsOther - }; - - static LLTrace::CountStatHandle<> sDequeueAssetRequestsTempTextureHTTP ("dequeuedassetrequeststemptexturehttp", - "Number of temporary texture asset http requests dequeued"), - sDequeueAssetRequestsTempTextureUDP ("dequeuedassetrequeststemptextureudp", - "Number of temporary texture asset udp requests dequeued"), - sDequeueAssetRequestsNonTempTextureHTTP("dequeuedassetrequestsnontemptexturehttp", - "Number of texture asset http requests dequeued"), - sDequeueAssetRequestsNonTempTextureUDP ("dequeuedassetrequestsnontemptextureudp", - "Number of texture asset udp requests dequeued"), - sDequeuedAssetRequestsWearableHTTP ("dequeuedassetrequestswearablehttp", - "Number of wearable asset http requests dequeued"), - sDequeuedAssetRequestsWearableUdp ("dequeuedassetrequestswearableudp", - "Number of wearable asset udp requests dequeued"), - sDequeuedAssetRequestsSoundHTTP ("dequeuedassetrequestssoundhttp", - "Number of sound asset http requests dequeued"), - sDequeuedAssetRequestsSoundUdp ("dequeuedassetrequestssoundudp", - "Number of sound asset udp requests dequeued"), - sDequeuedAssetRequestsGestureHTTP ("dequeuedassetrequestsgesturehttp", - "Number of gesture asset http requests dequeued"), - sDequeuedAssetRequestsGestureUdp ("dequeuedassetrequestsgestureudp", - "Number of gesture asset udp requests dequeued"), - sDequeuedAssetRequestsOther ("dequeuedassetrequestsother", - "Number of other asset requests dequeued"); - - static LLTrace::CountStatHandle<>* sDequeued[EVACCount] = { - &sDequeueAssetRequestsTempTextureHTTP, - &sDequeueAssetRequestsTempTextureUDP, - &sDequeueAssetRequestsNonTempTextureHTTP, - &sDequeueAssetRequestsNonTempTextureUDP, - &sDequeuedAssetRequestsWearableHTTP, - &sDequeuedAssetRequestsWearableUdp, - &sDequeuedAssetRequestsSoundHTTP, - &sDequeuedAssetRequestsSoundUdp, - &sDequeuedAssetRequestsGestureHTTP, - &sDequeuedAssetRequestsGestureUdp, - &sDequeuedAssetRequestsOther - }; - - static LLTrace::EventStatHandle sResponseAssetRequestsTempTextureHTTP ("assetresponsetimestemptexturehttp", - "Time spent responding to temporary texture asset http requests"), - sResponseAssetRequestsTempTextureUDP ("assetresponsetimestemptextureudp", - "Time spent responding to temporary texture asset udp requests"), - sResponseAssetRequestsNonTempTextureHTTP("assetresponsetimesnontemptexturehttp", - "Time spent responding to texture asset http requests"), - sResponseAssetRequestsNonTempTextureUDP ("assetresponsetimesnontemptextureudp", - "Time spent responding to texture asset udp requests"), - sResponsedAssetRequestsWearableHTTP ("assetresponsetimeswearablehttp", - "Time spent responding to wearable asset http requests"), - sResponsedAssetRequestsWearableUdp ("assetresponsetimeswearableudp", - "Time spent responding to wearable asset udp requests"), - sResponsedAssetRequestsSoundHTTP ("assetresponsetimessounduhttp", - "Time spent responding to sound asset http requests"), - sResponsedAssetRequestsSoundUdp ("assetresponsetimessoundudp", - "Time spent responding to sound asset udp requests"), - sResponsedAssetRequestsGestureHTTP ("assetresponsetimesgesturehttp", - "Time spent responding to gesture asset http requests"), - sResponsedAssetRequestsGestureUdp ("assetresponsetimesgestureudp", - "Time spent responding to gesture asset udp requests"), - sResponsedAssetRequestsOther ("assetresponsetimesother", - "Time spent responding to other asset requests"); - - static LLTrace::EventStatHandle* sResponse[EVACCount] = { - &sResponseAssetRequestsTempTextureHTTP, - &sResponseAssetRequestsTempTextureUDP, - &sResponseAssetRequestsNonTempTextureHTTP, - &sResponseAssetRequestsNonTempTextureUDP, - &sResponsedAssetRequestsWearableHTTP, - &sResponsedAssetRequestsWearableUdp, - &sResponsedAssetRequestsSoundHTTP, - &sResponsedAssetRequestsSoundUdp, - &sResponsedAssetRequestsGestureHTTP, - &sResponsedAssetRequestsGestureUdp, - &sResponsedAssetRequestsOther - }; + static LLTrace::DCCountStatHandle<> sEnqueued[EVACCount]; + static LLTrace::DCCountStatHandle<> sDequeued[EVACCount]; + static LLTrace::DCEventStatHandle sResponse[EVACCount]; } // ------------------------------------------------------ @@ -346,16 +268,16 @@ void LLViewerAssetStats::getStat(LLTrace::Recording& rec, T& req, LLViewerAssetS using namespace LLViewerAssetStatsFF; if (!compact_output - || rec.getSampleCount(*sEnqueued[cat]) - || rec.getSampleCount(*sDequeued[cat]) - || rec.getSampleCount(*sResponse[cat])) + || rec.getSampleCount(sEnqueued[cat]) + || rec.getSampleCount(sDequeued[cat]) + || rec.getSampleCount(sResponse[cat])) { - req .enqueued(rec.getSampleCount(*sEnqueued[cat])) - .dequeued(rec.getSampleCount(*sDequeued[cat])) - .resp_count(rec.getSampleCount(*sResponse[cat])) - .resp_min(rec.getMin(*sResponse[cat]).value()) - .resp_max(rec.getMax(*sResponse[cat]).value()) - .resp_mean(rec.getMean(*sResponse[cat]).value()); + req .enqueued(rec.getSampleCount(sEnqueued[cat])) + .dequeued(rec.getSampleCount(sDequeued[cat])) + .resp_count(rec.getSampleCount(sResponse[cat])) + .resp_min(rec.getMin(sResponse[cat]).value()) + .resp_max(rec.getMax(sResponse[cat]).value()) + .resp_mean(rec.getMean(sResponse[cat]).value()); } } @@ -382,7 +304,10 @@ void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) getStat(rec, r.get_sound_udp, EVACSoundUDPGet, compact_output); getStat(rec, r.get_gesture_http, EVACGestureHTTPGet, compact_output); getStat(rec, r.get_gesture_udp, EVACGestureUDPGet, compact_output); - getStat(rec, r.get_other, EVACOtherGet, compact_output); + getStat(rec, r.get_landmark_http, EVACLandmarkHTTPGet, compact_output); + getStat(rec, r.get_landmark_udp, EVACLandmarkUDPGet, compact_output); + getStat(rec, r.get_other_http, EVACOtherHTTPGet, compact_output); + getStat(rec, r.get_other_udp, EVACOtherUDPGet, compact_output); S32 fps = (S32)rec.getLastValue(LLStatViewer::FPS_SAMPLE); if (!compact_output || fps != 0) @@ -435,21 +360,21 @@ void record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp) { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - add(*sEnqueued[int(eac)], 1); + add(sEnqueued[int(eac)], 1); } void record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp) { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - add(*sDequeued[int(eac)], 1); + add(sDequeued[int(eac)], 1); } void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration) { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - record(*sResponse[int(eac)], F64Microseconds(duration)); + record(sResponse[int(eac)], F64Microseconds(duration)); } void init() @@ -499,7 +424,10 @@ LLViewerAssetStats::RegionStats::RegionStats() get_sound_udp("get_sound_udp"), get_gesture_http("get_gesture_http"), get_gesture_udp("get_gesture_udp"), - get_other("get_other"), + get_landmark_http("get_landmark_http"), + get_landmark_udp("get_landmark_udp"), + get_other_http("get_other_http"), + get_other_udp("get_other_udp"), fps("fps"), grid_x("grid_x"), grid_y("grid_y"), diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h index 88a8b9c61a..a2545c0bad 100644 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -53,7 +53,10 @@ namespace LLViewerAssetStatsFF EVACSoundUDPGet, //< Sound GETs UDP EVACGestureHTTPGet, //< Gesture GETs HTTP EVACGestureUDPGet, //< Gesture GETs UDP - EVACOtherGet, //< Other GETs + EVACLandmarkHTTPGet, //< Landmark GETs HTTP + EVACLandmarkUDPGet, //< Landmark GETs UDP + EVACOtherHTTPGet, //< Other GETs HTTP + EVACOtherUDPGet, //< Other GETs UDP EVACCount // Must be last }; @@ -145,7 +148,10 @@ public: get_sound_udp, get_gesture_http, get_gesture_udp, - get_other; + get_landmark_http, + get_landmark_udp, + get_other_http, + get_other_udp; Optional fps; Optional grid_x, grid_y; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 347810e169..85150bf7fa 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -471,7 +471,7 @@ void LLViewerAssetStorage::assetRequestCoro( { std::string url = getAssetURL(uuid,atype); LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; - + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("assetRequestCoro", httpPolicy)); @@ -480,19 +480,22 @@ void LLViewerAssetStorage::assetRequestCoro( LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts); + S32 result_code = LL_ERR_NOERR; + LLExtStat ext_status = LL_EXSTAT_NONE; + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) { // TODO asset-http: handle failures LL_DEBUGS("ViewerAsset") << "request failed, status " << status.toTerseString() << ", now what?" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; } else { LL_DEBUGS("ViewerAsset") << "request succeeded, url " << url << LL_ENDL; - // LL_DEBUGS("ViewerAsset") << "result: " << ll_pretty_print_sd(httpResults) << LL_ENDL; - const LLSD::Binary &raw = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS_RAW].asBinary(); S32 size = raw.size(); @@ -506,25 +509,28 @@ void LLViewerAssetStorage::assetRequestCoro( if (!vf.write(raw.data(),size)) { // TODO asset-http: handle error - LL_ERRS() << "Failure in vf.write()" << LL_ENDL; + LL_WARNS() << "Failure in vf.write()" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_VFS_CORRUPT; } if (!vf.rename(uuid, atype)) { - LL_ERRS() << "rename failed" << LL_ENDL; + LL_WARNS() << "rename failed" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_VFS_CORRUPT; } } else { // TODO asset-http: handle invalid size case - LL_ERRS() << "bad size" << LL_ENDL; + LL_WARNS() << "bad size" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; } - - // Clean up pending downloads and trigger callbacks - // TODO asset-http: what are the result_code and ext_status? - S32 result_code = LL_ERR_NOERR; - LLExtStat ext_status = LL_EXSTAT_NONE; - removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); } + + // Clean up pending downloads and trigger callbacks + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); } std::string LLViewerAssetStorage::getAssetURL(const LLUUID& uuid, LLAssetType::EType atype) -- cgit v1.2.3 From bcd0453562e032ba1eed3858629bf8a554557543 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Thu, 2 Mar 2017 15:44:54 -0500 Subject: SL-409 - viewer asset stats test build fix --- indra/newview/tests/llviewerassetstats_test.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp index ed3051893e..e2e7f09c3b 100644 --- a/indra/newview/tests/llviewerassetstats_test.cpp +++ b/indra/newview/tests/llviewerassetstats_test.cpp @@ -71,7 +71,8 @@ static const char * all_keys[] = { "duration", "fps", - "get_other", + "get_other_http", + "get_other_udp", "get_texture_temp_http", "get_texture_temp_udp", "get_texture_non_temp_http", @@ -86,7 +87,8 @@ static const char * all_keys[] = static const char * resp_keys[] = { - "get_other", + "get_other_http", + "get_other_udp", "get_texture_temp_http", "get_texture_temp_udp", "get_texture_non_temp_http", @@ -552,8 +554,11 @@ namespace tut ensure("sd[get_wearable_udp][enqueued] is 2", (2 == sd["get_wearable_udp"]["enqueued"].asInteger())); ensure("sd[get_wearable_udp][dequeued] is 2", (2 == sd["get_wearable_udp"]["dequeued"].asInteger())); - ensure("sd[get_other][enqueued] is 4", (4 == sd["get_other"]["enqueued"].asInteger())); - ensure("sd[get_other][dequeued] is 0", (0 == sd["get_other"]["dequeued"].asInteger())); + ensure("sd[get_other_http][enqueued] is 2", (2 == sd["get_other_http"]["enqueued"].asInteger())); + ensure("sd[get_other_http][dequeued] is 0", (0 == sd["get_other_http"]["dequeued"].asInteger())); + + ensure("sd[get_other_udp][enqueued] is 2", (2 == sd["get_other_udp"]["enqueued"].asInteger())); + ensure("sd[get_other_udp][dequeued] is 0", (0 == sd["get_other_udp"]["dequeued"].asInteger())); // Reset and check zeros... // Reset leaves current region in place -- cgit v1.2.3 From f70abb4ad628b19c993a22c7e86d350395555fcf Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Fri, 3 Mar 2017 15:14:09 -0500 Subject: SL-409 - added tracking for bytes fetched to viewer assets metrics (does not currently work for textures) --- indra/llcommon/llerror.cpp | 2 +- indra/llmessage/llassetstorage.cpp | 17 ++++++++++++++++- indra/llmessage/llassetstorage.h | 1 + indra/newview/llviewerassetstats.cpp | 10 +++++++--- indra/newview/llviewerassetstats.h | 9 +++++---- indra/newview/llviewerassetstorage.cpp | 7 +++++-- indra/newview/llviewerassetstorage.h | 5 ++++- scripts/metrics/viewer_asset_logs.py | 8 ++++++-- 8 files changed, 45 insertions(+), 14 deletions(-) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index e6407ecf22..9c49f7eff4 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -572,7 +572,7 @@ namespace LLError mFunctionString += std::string(mFunction) + ":"; for (size_t i = 0; i < mTagCount; i++) { - mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? "" : ","); + mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? " " : ","); } } diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index 7bf886ef26..ab6fc69413 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -195,7 +195,8 @@ LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type mInfoCallback( NULL ), mIsLocal(FALSE), mIsUserWaiting(FALSE), - mTimeout(LL_ASSET_STORAGE_TIMEOUT) + mTimeout(LL_ASSET_STORAGE_TIMEOUT), + mBytesFetched(0) { } @@ -641,6 +642,20 @@ void LLAssetStorage::downloadCompleteCallback( result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; vfile.remove(); } + else + { +#if 1 + for (request_list_t::iterator iter = gAssetStorage->mPendingDownloads.begin(); + iter != gAssetStorage->mPendingDownloads.end(); ++iter ) + { + LLAssetRequest* dlreq = *iter; + if ((dlreq->getUUID() == file_id) && (dlreq->getType()== file_type)) + { + dlreq->mBytesFetched = vfile.getSize(); + } + } +#endif + } } removeAndCallbackPendingDownloads(file_id, file_type, callback_id, callback_type, ext_status, result); diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index 2ec8ac31b4..d6b4fa0c7b 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -138,6 +138,7 @@ public: BOOL mIsUserWaiting; // We don't want to try forever if a user is waiting for a result. F64Seconds mTimeout; // Amount of time before timing out. LLUUID mRequestingAgentID; // Only valid for uploads from an agent + F64 mBytesFetched; virtual LLSD getTerseDetails() const; virtual LLSD getFullDetails() const; diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index d47f73ccce..a9e0ba7b5d 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -168,6 +168,7 @@ namespace LLViewerAssetStatsFF static LLTrace::DCCountStatHandle<> sEnqueued[EVACCount]; static LLTrace::DCCountStatHandle<> sDequeued[EVACCount]; + static LLTrace::DCEventStatHandle<> sBytesFetched[EVACCount]; static LLTrace::DCEventStatHandle sResponse[EVACCount]; } @@ -277,7 +278,8 @@ void LLViewerAssetStats::getStat(LLTrace::Recording& rec, T& req, LLViewerAssetS .resp_count(rec.getSampleCount(sResponse[cat])) .resp_min(rec.getMin(sResponse[cat]).value()) .resp_max(rec.getMax(sResponse[cat]).value()) - .resp_mean(rec.getMean(sResponse[cat]).value()); + .resp_mean(rec.getMean(sResponse[cat]).value()) + .resp_mean_bytes(rec.getMean(sBytesFetched[cat])); } } @@ -370,11 +372,12 @@ void record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp) add(sDequeued[int(eac)], 1); } -void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration) +void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, LLViewerAssetStats::duration_t duration, F64 bytes) { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); record(sResponse[int(eac)], F64Microseconds(duration)); + record(sBytesFetched[int(eac)], bytes); } void init() @@ -403,7 +406,8 @@ LLViewerAssetStats::AssetRequestType::AssetRequestType() resp_count("resp_count"), resp_min("resp_min"), resp_max("resp_max"), - resp_mean("resp_mean") + resp_mean("resp_mean"), + resp_mean_bytes("resp_mean_bytes") {} LLViewerAssetStats::FPSStats::FPSStats() diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h index a2545c0bad..718c284224 100644 --- a/indra/newview/llviewerassetstats.h +++ b/indra/newview/llviewerassetstats.h @@ -118,11 +118,12 @@ public: struct AssetRequestType : public LLInitParam::Block { Mandatory enqueued, - dequeued, - resp_count; + dequeued, + resp_count; Mandatory resp_min, resp_max, - resp_mean; + resp_mean, + resp_mean_bytes; AssetRequestType(); }; @@ -272,7 +273,7 @@ void record_enqueue(LLViewerAssetType::EType at, bool with_http, bool is_temp); void record_dequeue(LLViewerAssetType::EType at, bool with_http, bool is_temp); void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, - LLViewerAssetStats::duration_t duration); + LLViewerAssetStats::duration_t duration, F64 bytes=0); void record_avatar_stats(); diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 85150bf7fa..fa3567620c 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -82,7 +82,8 @@ protected: LLViewerAssetStatsFF::record_dequeue(mType, mWithHTTP, false); LLViewerAssetStatsFF::record_response(mType, mWithHTTP, false, (LLViewerAssetStatsFF::get_timestamp() - - mMetricsStartTime)); + - mMetricsStartTime), + mBytesFetched); mMetricsStartTime = (U32Seconds)0; } } @@ -458,12 +459,13 @@ void LLViewerAssetStorage::queueRequestHttp( LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); LLCoros::instance().launch("LLViewerAssetStorage::assetRequestCoro", - boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, uuid, atype, callback, user_data)); + boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, req, uuid, atype, callback, user_data)); } } } void LLViewerAssetStorage::assetRequestCoro( + LLViewerAssetRequest *req, const LLUUID& uuid, LLAssetType::EType atype, LLGetAssetCallback callback, @@ -506,6 +508,7 @@ void LLViewerAssetStorage::assetRequestCoro( temp_id.generate(); LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); vf.setMaxSize(size); + req->mBytesFetched = size; if (!vf.write(raw.data(),size)) { // TODO asset-http: handle error diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index d28a8a276f..3ca8112601 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -31,6 +31,8 @@ class LLVFile; +class LLViewerAssetRequest; + class LLViewerAssetStorage : public LLAssetStorage { public: @@ -85,7 +87,8 @@ protected: BOOL duplicate, BOOL is_priority); - void assetRequestCoro(const LLUUID& uuid, + void assetRequestCoro(LLViewerAssetRequest *req, + const LLUUID& uuid, LLAssetType::EType atype, void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), void *user_data); diff --git a/scripts/metrics/viewer_asset_logs.py b/scripts/metrics/viewer_asset_logs.py index 47f0658138..8e46ca4707 100644 --- a/scripts/metrics/viewer_asset_logs.py +++ b/scripts/metrics/viewer_asset_logs.py @@ -72,6 +72,7 @@ def update_stats(stats,rec): #print "field",field,"count",type_stats["count"] if (newcount>0): type_stats["sum"] = type_stats.get("sum",0) + val["resp_count"] * val["resp_mean"] + type_stats["sum_bytes"] = type_stats.get("sum_bytes",0) + val["resp_count"] * val.get("resp_mean_bytes",0) type_stats["enqueued"] = type_stats.get("enqueued",0) + val["enqueued"] type_stats["dequeued"] = type_stats.get("dequeued",0) + val["dequeued"] @@ -92,7 +93,10 @@ if __name__ == "__main__": update_stats(stats,rec) - for key, val in stats.iteritems(): + for key in sorted(stats.keys()): + val = stats[key] if val["count"] > 0: - print "key",key,"count",val["count"],"mean",val["sum"]/val["count"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] + print key,"count",val["count"],"mean_time",val["sum"]/val["count"],"mean_bytes",val["sum_bytes"]/val["count"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] + else: + print key,"count",val["count"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] -- cgit v1.2.3 From 09a6daabd2c4281b071f9213906872ea8a744956 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 6 Mar 2017 14:31:31 -0500 Subject: SL-409 - consolidated user of ViewerAsset cap for mesh and texture fetching as well. --- indra/newview/llmeshrepository.cpp | 143 +++++++++++---------------------- indra/newview/llmeshrepository.h | 13 +-- indra/newview/lltexturefetch.cpp | 4 +- indra/newview/llviewerassetstorage.cpp | 2 +- indra/newview/llviewerregion.cpp | 13 ++- indra/newview/llviewerregion.h | 4 +- 6 files changed, 58 insertions(+), 121 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 9dacae2c4e..5cc8ab02da 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -808,10 +808,8 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpLargeOptions(), mHttpHeaders(), mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpLegacyPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), mHttpLargePolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), - mHttpPriority(0), - mGetMeshVersion(2) + mHttpPriority(0) { LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); @@ -828,7 +826,6 @@ LLMeshRepoThread::LLMeshRepoThread() mHttpHeaders = LLCore::HttpHeaders::ptr_t(new LLCore::HttpHeaders); mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_VND_LL_MESH); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH2); - mHttpLegacyPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_MESH1); mHttpLargePolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_LARGE_MESH); } @@ -1103,13 +1100,9 @@ void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } // Mutex: must be holding mMutex when called -void LLMeshRepoThread::setGetMeshCaps(const std::string & get_mesh1, - const std::string & get_mesh2, - int pref_version) +void LLMeshRepoThread::setGetMeshCap(const std::string & mesh_cap) { - mGetMeshCapability = get_mesh1; - mGetMesh2Capability = get_mesh2; - mGetMeshVersion = pref_version; + mGetMeshCapability = mesh_cap; } @@ -1117,29 +1110,14 @@ void LLMeshRepoThread::setGetMeshCaps(const std::string & get_mesh1, // over a GetMesh cap. // // Mutex: acquires mMutex -void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * version) +void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url) { std::string res_url; - int res_version(2); if (gAgent.getRegion()) { LLMutexLock lock(mMutex); - - // Get a consistent pair of (cap string, version). The - // locking could be eliminated here without loss of safety - // by using a set of staging values in setGetMeshCaps(). - - if (! mGetMesh2Capability.empty() && mGetMeshVersion > 1) - { - res_url = mGetMesh2Capability; - res_version = 2; - } - else - { - res_url = mGetMeshCapability; - res_version = 1; - } + res_url = mGetMeshCapability; } if (! res_url.empty()) @@ -1149,19 +1127,15 @@ void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * ver } else { - LL_WARNS_ONCE(LOG_MESH) << "Current region does not have GetMesh capability! Cannot load " + LL_WARNS_ONCE(LOG_MESH) << "Current region does not have ViewerAsset capability! Cannot load " << mesh_id << ".mesh" << LL_ENDL; } *url = res_url; - *version = res_version; } // Issue an HTTP GET request with byte range using the right -// policy class. Large requests go to the large request class. -// If the current region supports GetMesh2, we prefer that for -// smaller requests otherwise we try to use the traditional -// GetMesh capability and connection concurrency. +// policy class. // // @return Valid handle or LLCORE_HTTP_HANDLE_INVALID. // If the latter, actual status is found in @@ -1169,7 +1143,7 @@ void LLMeshRepoThread::constructUrl(LLUUID mesh_id, std::string * url, int * ver // next call to this method. // // Thread: repo -LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int cap_version, +LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, size_t offset, size_t len, const LLCore::HttpHandler::ptr_t &handler) { @@ -1180,16 +1154,14 @@ LLCore::HttpHandle LLMeshRepoThread::getByteRange(const std::string & url, int c if (len < LARGE_MESH_FETCH_THRESHOLD) { - handle = mHttpRequest->requestGetByteRange((2 == cap_version - ? mHttpPolicyClass - : mHttpLegacyPolicyClass), - mHttpPriority, - url, - (disable_range_req ? size_t(0) : offset), - (disable_range_req ? size_t(0) : len), - mHttpOptions, - mHttpHeaders, - handler); + handle = mHttpRequest->requestGetByteRange( mHttpPolicyClass, + mHttpPriority, + url, + (disable_range_req ? size_t(0) : offset), + (disable_range_req ? size_t(0) : len), + mHttpOptions, + mHttpHeaders, + handler); if (LLCORE_HTTP_HANDLE_INVALID != handle) { ++LLMeshRepository::sHTTPRequestCount; @@ -1279,14 +1251,13 @@ bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(2); std::string http_url; - constructUrl(mesh_id, &http_url, &cap_version); + constructUrl(mesh_id, &http_url); if (!http_url.empty()) { LLMeshHandlerBase::ptr_t handler(new LLMeshSkinInfoHandler(mesh_id, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for skin info on mesh " << mID @@ -1372,14 +1343,13 @@ bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(2); std::string http_url; - constructUrl(mesh_id, &http_url, &cap_version); + constructUrl(mesh_id, &http_url); if (!http_url.empty()) { LLMeshHandlerBase::ptr_t handler(new LLMeshDecompositionHandler(mesh_id, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for decomposition mesh " << mID @@ -1464,14 +1434,13 @@ bool LLMeshRepoThread::fetchMeshPhysicsShape(const LLUUID& mesh_id) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(2); std::string http_url; - constructUrl(mesh_id, &http_url, &cap_version); + constructUrl(mesh_id, &http_url); if (!http_url.empty()) { LLMeshHandlerBase::ptr_t handler(new LLMeshPhysicsShapeHandler(mesh_id, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for physics shape on mesh " << mID @@ -1558,9 +1527,8 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) //either cache entry doesn't exist or is corrupt, request header from simulator bool retval = true; - int cap_version(2); std::string http_url; - constructUrl(mesh_params.getSculptID(), &http_url, &cap_version); + constructUrl(mesh_params.getSculptID(), &http_url); if (!http_url.empty()) { @@ -1569,7 +1537,7 @@ bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) //NOTE -- this will break of headers ever exceed 4KB LLMeshHandlerBase::ptr_t handler(new LLMeshHeaderHandler(mesh_params, 0, MESH_HEADER_SIZE)); - LLCore::HttpHandle handle = getByteRange(http_url, cap_version, 0, MESH_HEADER_SIZE, handler); + LLCore::HttpHandle handle = getByteRange(http_url, 0, MESH_HEADER_SIZE, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for mesh header " << mID @@ -1645,14 +1613,13 @@ bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) } //reading from VFS failed for whatever reason, fetch from sim - int cap_version(2); std::string http_url; - constructUrl(mesh_id, &http_url, &cap_version); + constructUrl(mesh_id, &http_url); if (!http_url.empty()) { LLMeshHandlerBase::ptr_t handler(new LLMeshLODHandler(mesh_params, lod, offset, size)); - LLCore::HttpHandle handle = getByteRange(http_url, cap_version, offset, size, handler); + LLCore::HttpHandle handle = getByteRange(http_url, offset, size, handler); if (LLCORE_HTTP_HANDLE_INVALID == handle) { LL_WARNS(LOG_MESH) << "HTTP GET request failed for LOD on mesh " << mID @@ -3292,8 +3259,7 @@ void LLMeshPhysicsShapeHandler::processData(LLCore::BufferArray * /* body */, S3 LLMeshRepository::LLMeshRepository() : mMeshMutex(NULL), mMeshThreadCount(0), - mThread(NULL), - mGetMeshVersion(2) + mThread(NULL) { } @@ -3476,35 +3442,21 @@ void LLMeshRepository::notifyLoadedMeshes() { //called from main thread LL_RECORD_BLOCK_TIME(FTM_MESH_FETCH); - if (1 == mGetMeshVersion) - { - // Legacy GetMesh operation with high connection concurrency - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); - LLMeshRepoThread::sRequestHighWater = llclamp(2 * S32(LLMeshRepoThread::sMaxConcurrentRequests), - REQUEST_HIGH_WATER_MIN, - REQUEST_HIGH_WATER_MAX); - LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, - REQUEST_LOW_WATER_MIN, - REQUEST_LOW_WATER_MAX); - } - else - { - // GetMesh2 operation with keepalives, etc. With pipelining, - // we'll increase this. See llappcorehttp and llcorehttp for - // discussion on connection strategies. - LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); - S32 scale(app_core_http.isPipelined(LLAppCoreHttp::AP_MESH2) - ? (2 * LLAppCoreHttp::PIPELINING_DEPTH) - : 5); - - LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); - LLMeshRepoThread::sRequestHighWater = llclamp(scale * S32(LLMeshRepoThread::sMaxConcurrentRequests), - REQUEST2_HIGH_WATER_MIN, - REQUEST2_HIGH_WATER_MAX); - LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, - REQUEST2_LOW_WATER_MIN, - REQUEST2_LOW_WATER_MAX); - } + // GetMesh2 operation with keepalives, etc. With pipelining, + // we'll increase this. See llappcorehttp and llcorehttp for + // discussion on connection strategies. + LLAppCoreHttp & app_core_http(LLAppViewer::instance()->getAppCoreHttp()); + S32 scale(app_core_http.isPipelined(LLAppCoreHttp::AP_MESH2) + ? (2 * LLAppCoreHttp::PIPELINING_DEPTH) + : 5); + + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("Mesh2MaxConcurrentRequests"); + LLMeshRepoThread::sRequestHighWater = llclamp(scale * S32(LLMeshRepoThread::sMaxConcurrentRequests), + REQUEST2_HIGH_WATER_MIN, + REQUEST2_HIGH_WATER_MAX); + LLMeshRepoThread::sRequestLowWater = llclamp(LLMeshRepoThread::sRequestHighWater / 2, + REQUEST2_LOW_WATER_MIN, + REQUEST2_LOW_WATER_MAX); //clean up completed upload threads for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) @@ -3610,15 +3562,10 @@ void LLMeshRepository::notifyLoadedMeshes() if (gAgent.getRegion()->getName() != region_name && gAgent.getRegion()->capabilitiesReceived()) { region_name = gAgent.getRegion()->getName(); - const bool use_v1(gSavedSettings.getBOOL("MeshUseGetMesh1")); - const std::string mesh1(gAgent.getRegion()->getCapability("GetMesh")); - const std::string mesh2(gAgent.getRegion()->getCapability("GetMesh2")); - mGetMeshVersion = (mesh2.empty() || use_v1) ? 1 : 2; - mThread->setGetMeshCaps(mesh1, mesh2, mGetMeshVersion); + const std::string mesh_cap(gAgent.getRegion()->getViewerAssetUrl()); + mThread->setGetMeshCap(mesh_cap); LL_DEBUGS(LOG_MESH) << "Retrieving caps for region '" << region_name - << "', GetMesh2: " << mesh2 - << ", GetMesh: " << mesh1 - << ", using version: " << mGetMeshVersion + << "', ViewerAsset cap: " << mesh_cap << LL_ENDL; } } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 30f042845a..23af837f6f 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -279,7 +279,6 @@ public: LLCore::HttpOptions::ptr_t mHttpLargeOptions; LLCore::HttpHeaders::ptr_t mHttpHeaders; LLCore::HttpRequest::policy_t mHttpPolicyClass; - LLCore::HttpRequest::policy_t mHttpLegacyPolicyClass; LLCore::HttpRequest::policy_t mHttpLargePolicyClass; LLCore::HttpRequest::priority_t mHttpPriority; @@ -287,8 +286,6 @@ public: http_request_set mHttpRequestSet; // Outstanding HTTP requests std::string mGetMeshCapability; - std::string mGetMesh2Capability; - int mGetMeshVersion; LLMeshRepoThread(); ~LLMeshRepoThread(); @@ -335,12 +332,10 @@ public: // mesh fetch URLs. // // Mutex: must be holding mMutex when called - void setGetMeshCaps(const std::string & get_mesh1, - const std::string & get_mesh2, - int pref_version); + void setGetMeshCap(const std::string & get_mesh); // Mutex: acquires mMutex - void constructUrl(LLUUID mesh_id, std::string * url, int * version); + void constructUrl(LLUUID mesh_id, std::string * url); private: // Issue a GET request to a URL with 'Range' header using @@ -349,7 +344,7 @@ private: // or dispose of handler. // // Threads: Repo thread only - LLCore::HttpHandle getByteRange(const std::string & url, int cap_version, + LLCore::HttpHandle getByteRange(const std::string & url, size_t offset, size_t len, const LLCore::HttpHandler::ptr_t &handler); }; @@ -585,8 +580,6 @@ public: void uploadError(LLSD& args); void updateInventory(inventory_data data); - - int mGetMeshVersion; // Shadows value in LLMeshRepoThread }; extern LLMeshRepository gMeshRepo; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 07b3dc1aa4..7c0d02c9ca 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1351,7 +1351,7 @@ bool LLTextureFetchWorker::doWork(S32 param) if (region) { - std::string http_url = region->getHttpUrl() ; + std::string http_url = region->getViewerAssetUrl(); if (!http_url.empty()) { if (mFTType != FTT_DEFAULT) @@ -4580,7 +4580,7 @@ void LLTextureFetchDebugger::debugHTTP() return; } - mHTTPUrl = region->getHttpUrl(); + mHTTPUrl = region->getViewerAssetUrl(); if (mHTTPUrl.empty()) { LL_INFOS(LOG_TXT) << "Fetch Debugger : Current region URL undefined. Cannot fetch textures through HTTP." << LL_ENDL; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index fa3567620c..41f407b8d9 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -538,7 +538,7 @@ void LLViewerAssetStorage::assetRequestCoro( std::string LLViewerAssetStorage::getAssetURL(const LLUUID& uuid, LLAssetType::EType atype) { - std::string cap_url = gAgent.getRegion()->getCapability("ViewerAsset"); + std::string cap_url = gAgent.getRegion()->getViewerAssetUrl(); std::string type_name = LLAssetType::lookup(atype); std::string url = cap_url + "/?" + type_name + "_id=" + uuid.asString(); return url; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 0188208cfd..eb37613c95 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -521,7 +521,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mColoName("unknown"), mProductSKU("unknown"), mProductName("unknown"), - mHttpUrl(""), + mViewerAssetUrl(""), mCacheLoaded(FALSE), mCacheDirty(FALSE), mReleaseNotesRequested(FALSE), @@ -2843,12 +2843,9 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("IsExperienceAdmin"); capabilityNames.append("IsExperienceContributor"); capabilityNames.append("RegionExperiences"); - capabilityNames.append("GetMesh"); - capabilityNames.append("GetMesh2"); capabilityNames.append("GetMetadata"); capabilityNames.append("GetObjectCost"); capabilityNames.append("GetObjectPhysicsData"); - capabilityNames.append("GetTexture"); capabilityNames.append("GroupAPIv1"); capabilityNames.append("GroupMemberData"); capabilityNames.append("GroupProposalBallot"); @@ -2962,9 +2959,9 @@ void LLViewerRegion::setCapability(const std::string& name, const std::string& u else { mImpl->mCapabilities[name] = url; - if(name == "GetTexture") + if(name == "ViewerAsset") { - mHttpUrl = url ; + mViewerAssetUrl = url; } } } @@ -2975,9 +2972,9 @@ void LLViewerRegion::setCapabilityDebug(const std::string& name, const std::stri if ( ! ( name == "EventQueueGet" || name == "UntrustedSimulatorMessage" || name == "SimulatorFeatures" ) ) { mImpl->mSecondCapabilitiesTracker[name] = url; - if(name == "GetTexture") + if(name == "ViewerAsset") { - mHttpUrl = url ; + mViewerAssetUrl = url; } } } diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index a7bb546d2c..61ce5b454d 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -354,7 +354,7 @@ public: friend std::ostream& operator<<(std::ostream &s, const LLViewerRegion ®ion); /// implements LLCapabilityProvider virtual std::string getDescription() const; - std::string getHttpUrl() const { return mHttpUrl ;} + std::string getViewerAssetUrl() const { return mViewerAssetUrl; } U32 getNumOfVisibleGroups() const; U32 getNumOfActiveCachedObjects() const; @@ -506,7 +506,7 @@ private: std::string mColoName; std::string mProductSKU; std::string mProductName; - std::string mHttpUrl ; + std::string mViewerAssetUrl ; // Maps local ids to cache entries. // Regions can have order 10,000 objects, so assume -- cgit v1.2.3 From 1f44919b3fc994adc27d16fc9dbd476c08d24e3a Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 6 Mar 2017 17:07:44 -0500 Subject: SL-409 - use AP_TEXTURE policy for other viewer asset fetches. Enables pipelining. --- indra/newview/llviewerassetstorage.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 41f407b8d9..153108d8a9 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -33,6 +33,7 @@ #include "message.h" #include "llagent.h" +#include "llappcorehttp.h" #include "llviewerregion.h" #include "lltransfersourceasset.h" @@ -474,7 +475,7 @@ void LLViewerAssetStorage::assetRequestCoro( std::string url = getAssetURL(uuid,atype); LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; - LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCore::HttpRequest::policy_t httpPolicy(LLAppCoreHttp::AP_TEXTURE); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("assetRequestCoro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); -- cgit v1.2.3 From 2cd4418eab69bdd664148c21460327a36ed396ba Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 6 Mar 2017 19:28:17 -0500 Subject: SL-409 - build fix, removed unreferenced vars --- indra/newview/llmeshrepository.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index a0dced3cfd..a28ab499fd 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -343,10 +343,6 @@ LLMeshRepository gMeshRepo; const S32 MESH_HEADER_SIZE = 4096; // Important: assumption is that headers fit in this space -const S32 REQUEST_HIGH_WATER_MIN = 32; // Limits for GetMesh regions -const S32 REQUEST_HIGH_WATER_MAX = 150; // Should remain under 2X throttle -const S32 REQUEST_LOW_WATER_MIN = 16; -const S32 REQUEST_LOW_WATER_MAX = 75; const S32 REQUEST2_HIGH_WATER_MIN = 32; // Limits for GetMesh2 regions const S32 REQUEST2_HIGH_WATER_MAX = 100; -- cgit v1.2.3 From f028290c9e1afd241944ad63e8ce34af755a2233 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 7 Mar 2017 08:06:03 -0500 Subject: SL-409 - added size stats to metrics for textures --- indra/newview/lltexturefetch.cpp | 23 ++++++++++++++++------- scripts/metrics/viewer_asset_logs.py | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 7c0d02c9ca..9469a3c373 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -485,7 +485,7 @@ private: void recordTextureStart(bool is_http); // Threads: Ttf - void recordTextureDone(bool is_http); + void recordTextureDone(bool is_http, F64 byte_count); void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } @@ -1426,6 +1426,13 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (processSimulatorPackets()) { + // Capture some measure of total size for metrics + F64 byte_count = 0; + for (S32 i=mFirstPacket; i<=mLastPacket; i++) + { + byte_count += mPackets[i]->mSize; + } + LL_DEBUGS(LOG_TXT) << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL; mFetcher->removeFromNetworkQueue(this, false); if (mFormattedImage.isNull() || !mFormattedImage->getDataSize()) @@ -1443,7 +1450,8 @@ bool LLTextureFetchWorker::doWork(S32 param) } setState(DECODE_IMAGE); mWriteToCacheState = SHOULD_WRITE; - recordTextureDone(false); + + recordTextureDone(false, byte_count); } else { @@ -2093,7 +2101,7 @@ void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpRe mFetcher->removeFromHTTPQueue(mID, data_size); - recordTextureDone(true); + recordTextureDone(true, data_size); } // -Mw @@ -2493,14 +2501,15 @@ void LLTextureFetchWorker::recordTextureStart(bool is_http) // Threads: Ttf -void LLTextureFetchWorker::recordTextureDone(bool is_http) +void LLTextureFetchWorker::recordTextureDone(bool is_http, F64 byte_count) { if (mMetricsStartTime.value()) { LLViewerAssetStatsFF::record_response(LLViewerAssetType::AT_TEXTURE, - is_http, - LLImageBase::TYPE_AVATAR_BAKE == mType, - LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime, + byte_count); mMetricsStartTime = (U32Seconds)0; } LLViewerAssetStatsFF::record_dequeue(LLViewerAssetType::AT_TEXTURE, diff --git a/scripts/metrics/viewer_asset_logs.py b/scripts/metrics/viewer_asset_logs.py index 8e46ca4707..e48286f696 100644 --- a/scripts/metrics/viewer_asset_logs.py +++ b/scripts/metrics/viewer_asset_logs.py @@ -96,7 +96,7 @@ if __name__ == "__main__": for key in sorted(stats.keys()): val = stats[key] if val["count"] > 0: - print key,"count",val["count"],"mean_time",val["sum"]/val["count"],"mean_bytes",val["sum_bytes"]/val["count"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] + print key,"count",val["count"],"mean_time",val["sum"]/val["count"],"mean_bytes",val["sum_bytes"]/val["count"],"net bytes/sec",val["sum_bytes"]/val["sum"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] else: print key,"count",val["count"],"enqueued",val["enqueued"],"dequeued",val["dequeued"] -- cgit v1.2.3 From e9c786ec6060da836bf6aa49f0e443f41f41a2c0 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Fri, 10 Mar 2017 11:29:48 -0500 Subject: SL-409 - fixes for viewer asset metrics. Copy and/or assignment of Recording objects is failing, changed to create LLSD blob earlier and just copy that. --- indra/newview/llappviewer.cpp | 11 +++----- indra/newview/lltexturefetch.cpp | 52 +++++++++++++++--------------------- indra/newview/lltexturefetch.h | 2 +- indra/newview/llviewerassetstats.cpp | 6 ++--- 4 files changed, 28 insertions(+), 43 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 5eb82a2d0b..b138631f92 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -5912,23 +5912,18 @@ void LLAppViewer::metricsSend(bool enable_reporting) { std::string caps_url = regionp->getCapability("ViewerMetrics"); + LLSD sd = gViewerAssetStats->asLLSD(true); if (gSavedSettings.getBOOL("QAModeMetrics")) { - dump_sequential_xml("metric_asset_stats",gViewerAssetStats->asLLSD(true)); + dump_sequential_xml("metric_asset_stats",sd); } - // Make a copy of the main stats to send into another thread. - // Receiving thread takes ownership. - LLViewerAssetStats * main_stats(new LLViewerAssetStats(*gViewerAssetStats)); - main_stats->stop(); - // Send a report request into 'thread1' to get the rest of the data // and provide some additional parameters while here. LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, gAgentSessionID, gAgentID, - main_stats); - main_stats = 0; // Ownership transferred + sd); } else { diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 9469a3c373..6468991c79 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -824,7 +824,7 @@ public: TFReqSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, - LLViewerAssetStats * main_stats); + LLSD& stats_sd); TFReqSendMetrics & operator=(const TFReqSendMetrics &); // Not defined virtual ~TFReqSendMetrics(); @@ -835,7 +835,7 @@ public: const std::string mCapsURL; const LLUUID mSessionID; const LLUUID mAgentID; - LLViewerAssetStats * mMainStats; + LLSD mStatsSD; private: LLCore::HttpHandler::ptr_t mHandler; @@ -3872,9 +3872,9 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) void LLTextureFetch::commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, - LLViewerAssetStats * main_stats) + LLSD& stats_sd) { - TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, main_stats); + TFReqSendMetrics * req = new TFReqSendMetrics(caps_url, session_id, agent_id, stats_sd); cmdEnqueue(req); } @@ -3983,22 +3983,20 @@ TFReqSetRegion::doWork(LLTextureFetch *) } TFReqSendMetrics::TFReqSendMetrics(const std::string & caps_url, - const LLUUID & session_id, - const LLUUID & agent_id, - LLViewerAssetStats * main_stats): + const LLUUID & session_id, + const LLUUID & agent_id, + LLSD& stats_sd): LLTextureFetch::TFRequest(), mCapsURL(caps_url), mSessionID(session_id), mAgentID(agent_id), - mMainStats(main_stats), + mStatsSD(stats_sd), mHandler(new AssetReportHandler) {} TFReqSendMetrics::~TFReqSendMetrics() { - delete mMainStats; - mMainStats = 0; } @@ -4019,26 +4017,18 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) static volatile bool reporting_started(false); static volatile S32 report_sequence(0); - // We've taken over ownership of the stats copy at this - // point. Get a working reference to it for merging here - // but leave it in 'this'. Destructor will rid us of it. - LLViewerAssetStats & main_stats = *mMainStats; - - LLViewerAssetStats::AssetStats stats; - main_stats.getStats(stats, true); - //LLSD merged_llsd = main_stats.asLLSD(); + // In mStatsSD, we have a copy we own of the LLSD representation + // of the asset stats. Add some additional fields and ship it off. bool initial_report = !reporting_started; - stats.session_id = mSessionID; - stats.agent_id = mAgentID; - stats.message = "ViewerAssetMetrics"; - stats.sequence = static_cast(report_sequence); - stats.initial = initial_report; - stats.break_ = static_cast(LLTextureFetch::svMetricsDataBreak); - - LLSD sd; - LLParamSDParser parser; - parser.writeSD(sd, stats); + mStatsSD["session_id"] = mSessionID; + mStatsSD["agent_id"] = mAgentID; + mStatsSD["message"] = "ViewerAssetMetrics"; + mStatsSD["sequence"] = report_sequence; + mStatsSD["initial"] = initial_report; + mStatsSD["break"] = static_cast(LLTextureFetch::svMetricsDataBreak); + + LL_INFOS(LOG_TXT) << "ViewerAssetMetrics after fields added\n" << ll_pretty_print_sd(mStatsSD) << LL_ENDL; // Update sequence number if (S32_MAX == ++report_sequence) @@ -4049,7 +4039,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // Limit the size of the stats report if necessary. - sd["truncated"] = truncate_viewer_metrics(10, sd); + mStatsSD["truncated"] = truncate_viewer_metrics(10, mStatsSD); if (! mCapsURL.empty()) { @@ -4058,7 +4048,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) fetcher->getMetricsPolicyClass(), report_priority, mCapsURL, - sd, + mStatsSD, LLCore::HttpOptions::ptr_t(), fetcher->getMetricsHeaders(), mHandler); @@ -4072,7 +4062,7 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // In QA mode, Metrics submode, log the result for ease of testing if (fetcher->isQAMode()) { - LL_INFOS(LOG_TXT) << ll_pretty_print_sd(sd) << LL_ENDL; + LL_INFOS(LOG_TXT) << "ViewerAssetMetrics as submitted\n" << ll_pretty_print_sd(mStatsSD) << LL_ENDL; } return true; diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index 072e6a3307..cfa312ccd9 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -160,7 +160,7 @@ public: void commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, - LLViewerAssetStats * main_stats); + LLSD& stats_sd); // Threads: T* void commandDataBreak(); diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp index a9e0ba7b5d..14e05fd440 100644 --- a/indra/newview/llviewerassetstats.cpp +++ b/indra/newview/llviewerassetstats.cpp @@ -323,10 +323,10 @@ void LLViewerAssetStats::getStats(AssetStats& stats, bool compact_output) grid_from_region_handle(it->first, &grid_x, &grid_y); r .grid_x(grid_x) .grid_y(grid_y) - .duration(F64Microseconds(rec.getDuration()).value()); + .duration(F64Seconds(rec.getDuration()).value()); } - stats.duration(mCurRecording ? F64Microseconds(mCurRecording->getDuration()).value() : 0.0); + stats.duration(mCurRecording ? F64Seconds(mCurRecording->getDuration()).value() : 0.0); } LLSD LLViewerAssetStats::asLLSD(bool compact_output) @@ -376,7 +376,7 @@ void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp, { const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp)); - record(sResponse[int(eac)], F64Microseconds(duration)); + record(sResponse[int(eac)], F64Seconds(duration)); record(sBytesFetched[int(eac)], bytes); } -- cgit v1.2.3 From 295c8a437afb29be1ec4b59acad60c35afb6ffc3 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 20 Mar 2017 08:12:37 -0400 Subject: MAINT-7195 possible fix --- indra/newview/llviewerassetstorage.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 153108d8a9..496fbbecff 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -428,6 +428,11 @@ void LLViewerAssetStorage::queueRequestHttp( BOOL is_priority) { LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + if (!gAgent.getRegion()) + { + LL_WARNS() << "No region, fetch fails" << LL_ENDL; + return; + } std::string cap_url = gAgent.getRegion()->getCapability("ViewerAsset"); if (cap_url.empty()) { -- cgit v1.2.3 From 17384ce6c43a9e07031d7deeea4ec9bbee76199c Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 20 Mar 2017 16:04:02 -0400 Subject: MAINT-7195 work (can't repro), removed UDP fetching path, handle more possible timing issues while connecting to region --- indra/llcommon/llassettype.cpp | 90 +----------------- indra/llcommon/llassettype.h | 5 - indra/newview/app_settings/settings.xml | 12 --- indra/newview/llstartup.cpp | 10 -- indra/newview/llviewerassetstorage.cpp | 162 ++++++++++++-------------------- indra/newview/llviewerassetstorage.h | 12 +-- indra/newview/llviewercontrol.cpp | 8 -- 7 files changed, 65 insertions(+), 234 deletions(-) diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 6ecc2ec740..5e8129b9b2 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -51,8 +51,7 @@ struct AssetEntry : public LLDictionaryEntry mHumanName(human_name), mCanLink(can_link), mCanFetch(can_fetch), - mCanKnow(can_know), - mFetchWithVACap(false) + mCanKnow(can_know) { llassert(strlen(mTypeName) <= 8); } @@ -62,7 +61,6 @@ struct AssetEntry : public LLDictionaryEntry bool mCanLink; bool mCanFetch; bool mCanKnow; - bool mFetchWithVACap; }; class LLAssetDictionary : public LLSingleton, @@ -256,89 +254,3 @@ bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type) } return false; } - -// static -bool LLAssetType::lookupFetchWithVACap(EType asset_type) -{ - LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - const AssetEntry *entry = dict->lookup(asset_type); - if (entry) - { - return entry->mFetchWithVACap; - } - return false; -} - -// FIXME asset-http yank all this after asset-http becomes universal -void LLAssetType::setFetchWithVACapTypeString(const std::string& type_string) -{ - LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - if (type_string=="none") - { - for (LLAssetDictionary::iterator iter = dict->begin(); - iter != dict->end(); - iter++) - { - AssetEntry *entry = iter->second; - entry->mFetchWithVACap = false; - } - } - else if (type_string=="all") - { - for (LLAssetDictionary::iterator iter = dict->begin(); - iter != dict->end(); - iter++) - { - AssetEntry *entry = iter->second; - entry->mFetchWithVACap = true; - } - } - else - { - for (LLAssetDictionary::iterator iter = dict->begin(); - iter != dict->end(); - iter++) - { - AssetEntry *entry = iter->second; - if (entry->mTypeName==type_string) - { - entry->mFetchWithVACap = true; - } - } - } -} - -// FIXME asset-http yank all this after asset-http becomes universal -void LLAssetType::setFetchWithVACapConfigString(const std::string& config_string) -{ - // Clear any currently enabled types - LLAssetType::setFetchWithVACapTypeString("none"); - - // Enable all types specified in the config string. - std::set type_names_for_va_cap; - boost::split(type_names_for_va_cap, config_string, boost::is_any_of(" :,")); - for (std::set::const_iterator it = type_names_for_va_cap.begin(); - it != type_names_for_va_cap.end(); ++it) - { - const std::string& type_string = *it; - LLAssetType::setFetchWithVACapTypeString(type_string); - } - - LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - bool any_found = false; - for (LLAssetDictionary::iterator iter = dict->begin(); - iter != dict->end(); - iter++) - { - AssetEntry *entry = iter->second; - if (entry->mFetchWithVACap) - { - any_found = true; - LL_WARNS() << "Fetch with ViewerAsset cap enabled for " << entry->mTypeName << LL_ENDL; - } - } - if (!any_found) - { - LL_WARNS() << "Fetch with ViewerAsset cap disabled for all types" << LL_ENDL; - } -} diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index e06ebc2a35..b849be9f16 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -153,11 +153,6 @@ public: static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer - static bool lookupFetchWithVACap(EType asset_type); // asset data is fetched via http using ViewerAsset cap. - - static void setFetchWithVACapTypeString(const std::string& type_string); - static void setFetchWithVACapConfigString(const std::string& config_string); - static const std::string& badLookup(); // error string when a lookup fails protected: diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 9b26053fb5..717deba8c7 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -15808,18 +15808,6 @@ 0 - ViewerAssetHttpTypes - - Comment - Use the ViewerAsset cap and HTTP pipeline for fetching assets of the listed type names. "none" and "all" are recognized as a special values. - Persist - 1 - Type - String - Value - all - - SimulateFBOFailure Comment diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 0ef337b6c4..b762b2ae1c 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -312,16 +312,6 @@ void set_flags_and_update_appearance() // true when all initialization done. bool idle_startup() { - // FIXME asset-http - this configuration stuff is temporary - // construction; once it is always on for certain types - // we can remove the setting. - static bool va_types_initialized = false; - if (!va_types_initialized) - { - va_types_initialized = true; - LLAssetType::setFetchWithVACapConfigString(gSavedSettings.getString("ViewerAssetHttpTypes")); - } - const F32 PRECACHING_DELAY = gSavedSettings.getF32("PrecachingDelay"); static LLTimer timeout; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 496fbbecff..cb60337a7f 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -42,6 +42,7 @@ #include "llcoros.h" #include "lleventcoro.h" #include "llsdutil.h" +#include "llworld.h" ///---------------------------------------------------------------------------- /// LLViewerAssetRequest @@ -350,17 +351,10 @@ void LLViewerAssetStorage::_queueDataRequest( BOOL duplicate, BOOL is_priority) { - if (LLAssetType::lookupFetchWithVACap(atype)) - { - queueRequestHttp(uuid, atype, callback, user_data, duplicate, is_priority); - } - else - { - queueRequestUDP(uuid, atype, callback, user_data, duplicate, is_priority); - } + queueRequestHttp(uuid, atype, callback, user_data, duplicate, is_priority); } -void LLViewerAssetStorage::queueRequestUDP( +void LLViewerAssetStorage::queueRequestHttp( const LLUUID& uuid, LLAssetType::EType atype, LLGetAssetCallback callback, @@ -368,116 +362,84 @@ void LLViewerAssetStorage::queueRequestUDP( BOOL duplicate, BOOL is_priority) { - LL_DEBUGS("ViewerAsset") << "Request asset via UDP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; - if (mUpstreamHost.isOk()) + bool with_http = true; + LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); + req->mDownCallback = callback; + req->mUserData = user_data; + req->mIsPriority = is_priority; + if (!duplicate) { - // stash the callback info so we can find it after we get the response message - bool with_http = false; - LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); - req->mDownCallback = callback; - req->mUserData = user_data; - req->mIsPriority = is_priority; - if (!duplicate) - { - // Only collect metrics for non-duplicate requests. Others - // are piggy-backing and will artificially lower averages. - req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - - mPendingDownloads.push_back(req); - - if (!duplicate) - { - // send request message to our upstream data provider - // Create a new asset transfer. - LLTransferSourceParamsAsset spa; - spa.setAsset(uuid, atype); - - // Set our destination file, and the completion callback. - LLTransferTargetParamsVFile tpvf; - tpvf.setAsset(uuid, atype); - tpvf.setCallback(downloadCompleteCallback, *req); - - LL_DEBUGS("AssetStorage") << "Starting transfer for " << uuid << LL_ENDL; - LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET); - ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f)); - - bool with_http = false; - bool is_temp = false; - LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); - } + // Only collect metrics for non-duplicate requests. Others + // are piggy-backing and will artificially lower averages. + req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); } - else + mPendingDownloads.push_back(req); + + // This is the same as the current UDP logic - don't re-request a duplicate. + if (!duplicate) { - // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; - if (callback) - { - callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE, LL_EXSTAT_NO_UPSTREAM); - } + bool with_http = true; + bool is_temp = false; + LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); + + LLCoros::instance().launch("LLViewerAssetStorage::assetRequestCoro", + boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, req, uuid, atype, callback, user_data)); } } -void LLViewerAssetStorage::queueRequestHttp( +void LLViewerAssetStorage::capsRecvForRegion(const LLUUID& region_id, std::string pumpname) +{ + LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(region_id); + mViewerAssetUrl = regionp->getViewerAssetUrl(); + + LLEventPumps::instance().obtain(pumpname).post(LLSD()); +} + +void LLViewerAssetStorage::assetRequestCoro( + LLViewerAssetRequest *req, const LLUUID& uuid, LLAssetType::EType atype, LLGetAssetCallback callback, - void *user_data, - BOOL duplicate, - BOOL is_priority) + void *user_data) { - LL_DEBUGS("ViewerAsset") << "Request asset via HTTP " << uuid << " type " << LLAssetType::lookup(atype) << LL_ENDL; + S32 result_code = LL_ERR_NOERR; + LLExtStat ext_status = LL_EXSTAT_NONE; + if (!gAgent.getRegion()) { - LL_WARNS() << "No region, fetch fails" << LL_ENDL; + LL_WARNS_ONCE() << "Asset request fails: no region set" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); return; } - std::string cap_url = gAgent.getRegion()->getCapability("ViewerAsset"); - if (cap_url.empty()) + else if (!gAgent.getRegion()->capabilitiesReceived()) { - LL_WARNS() << "No ViewerAsset cap found, fetch fails" << LL_ENDL; - // TODO asset-http: handle waiting for caps? Other failure mechanism? - return; + LLEventStream capsRecv("waitForCaps", true); + + gAgent.getRegion()->setCapabilitiesReceivedCallback( + boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, _1, capsRecv.getName())); + + llcoro::suspendUntilEventOn(capsRecv); } else { - LL_DEBUGS("ViewerAsset") << "Will fetch via ViewerAsset cap " << cap_url << LL_ENDL; - - bool with_http = true; - LLViewerAssetRequest *req = new LLViewerAssetRequest(uuid, atype, with_http); - req->mDownCallback = callback; - req->mUserData = user_data; - req->mIsPriority = is_priority; - if (!duplicate) - { - // Only collect metrics for non-duplicate requests. Others - // are piggy-backing and will artificially lower averages. - req->mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - mPendingDownloads.push_back(req); - - // This is the same as the current UDP logic - don't re-request a duplicate. - if (!duplicate) + if (mViewerAssetUrl.empty()) { - bool with_http = true; - bool is_temp = false; - LLViewerAssetStatsFF::record_enqueue(atype, with_http, is_temp); - - LLCoros::instance().launch("LLViewerAssetStorage::assetRequestCoro", - boost::bind(&LLViewerAssetStorage::assetRequestCoro, this, req, uuid, atype, callback, user_data)); + mViewerAssetUrl = gAgent.getRegion()->getViewerAssetUrl(); } } -} - -void LLViewerAssetStorage::assetRequestCoro( - LLViewerAssetRequest *req, - const LLUUID& uuid, - LLAssetType::EType atype, - LLGetAssetCallback callback, - void *user_data) -{ - std::string url = getAssetURL(uuid,atype); + if (mViewerAssetUrl.empty()) + { + LL_WARNS_ONCE() << "asset request fails: caps received but no viewer asset cap found" << LL_ENDL; + result_code = LL_ERR_ASSET_REQUEST_FAILED; + ext_status = LL_EXSTAT_NONE; + removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); + return; + } + std::string url = getAssetURL(mViewerAssetUrl, uuid,atype); LL_DEBUGS("ViewerAsset") << "request url: " << url << LL_ENDL; LLCore::HttpRequest::policy_t httpPolicy(LLAppCoreHttp::AP_TEXTURE); @@ -488,9 +450,6 @@ void LLViewerAssetStorage::assetRequestCoro( LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts); - S32 result_code = LL_ERR_NOERR; - LLExtStat ext_status = LL_EXSTAT_NONE; - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) @@ -542,9 +501,8 @@ void LLViewerAssetStorage::assetRequestCoro( removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); } -std::string LLViewerAssetStorage::getAssetURL(const LLUUID& uuid, LLAssetType::EType atype) +std::string LLViewerAssetStorage::getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype) { - std::string cap_url = gAgent.getRegion()->getViewerAssetUrl(); std::string type_name = LLAssetType::lookup(atype); std::string url = cap_url + "/?" + type_name + "_id=" + uuid.asString(); return url; diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 3ca8112601..2f3eaa15a7 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -73,13 +73,6 @@ protected: BOOL duplicate, BOOL is_priority); - void queueRequestUDP(const LLUUID& uuid, - LLAssetType::EType type, - void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), - void *user_data, - BOOL duplicate, - BOOL is_priority); - void queueRequestHttp(const LLUUID& uuid, LLAssetType::EType type, void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), @@ -87,14 +80,17 @@ protected: BOOL duplicate, BOOL is_priority); + void capsRecvForRegion(const LLUUID& region_id, std::string pumpname); + void assetRequestCoro(LLViewerAssetRequest *req, const LLUUID& uuid, LLAssetType::EType atype, void (*callback) (LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32, LLExtStat), void *user_data); - std::string getAssetURL(const LLUUID& uuid, LLAssetType::EType atype); + std::string getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype); + std::string mViewerAssetUrl; }; #endif diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index c0a87adca5..7c1921b143 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -127,13 +127,6 @@ static bool handleDebugAvatarJointsChanged(const LLSD& newvalue) return true; } -static bool handleViewerAssetHttpTypesChanged(const LLSD& newvalue) -{ - std::string new_string = newvalue.asString(); - LLAssetType::setFetchWithVACapConfigString(new_string); - return true; -} - static bool handleSetShaderChanged(const LLSD& newvalue) { // changing shader level may invalidate existing cached bump maps, as the shader type determines the format of the bump map it expects - clear and repopulate the bump cache @@ -757,7 +750,6 @@ void settings_setup_listeners() gSavedSettings.getControl("SpellCheckDictionary")->getSignal()->connect(boost::bind(&handleSpellCheckChanged)); gSavedSettings.getControl("LoginLocation")->getSignal()->connect(boost::bind(&handleLoginLocationChanged)); gSavedSettings.getControl("DebugAvatarJoints")->getCommitSignal()->connect(boost::bind(&handleDebugAvatarJointsChanged, _2)); - gSavedSettings.getControl("ViewerAssetHttpTypes")->getCommitSignal()->connect(boost::bind(&handleViewerAssetHttpTypesChanged, _2)); } #if TEST_CACHED_CONTROL -- cgit v1.2.3 From ca74323ee88d44dbc0a8dbe639057e6cbc86b017 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 21 Mar 2017 08:56:30 -0400 Subject: SL-409 - added version field = 2 to asset stats, made viewer-side metrics files exactly match the payload sent to simulator. --- indra/newview/llappviewer.cpp | 12 +----------- indra/newview/lltexturefetch.cpp | 8 ++++++++ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index b138631f92..5503313dde 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -844,12 +844,6 @@ bool LLAppViewer::init() LLMachineID::init(); { - // Viewer metrics initialization - //static LLCachedControl metrics_submode(gSavedSettings, - // "QAModeMetrics", - // false, - // "Enables QA features (logging, faster cycling) for metrics collector"); - if (gSavedSettings.getBOOL("QAModeMetrics")) { app_metrics_qa_mode = true; @@ -5913,11 +5907,7 @@ void LLAppViewer::metricsSend(bool enable_reporting) std::string caps_url = regionp->getCapability("ViewerMetrics"); LLSD sd = gViewerAssetStats->asLLSD(true); - if (gSavedSettings.getBOOL("QAModeMetrics")) - { - dump_sequential_xml("metric_asset_stats",sd); - } - + // Send a report request into 'thread1' to get the rest of the data // and provide some additional parameters while here. LLAppViewer::sTextureFetch->commandSendMetrics(caps_url, diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 6468991c79..887ad157ba 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4020,12 +4020,15 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // In mStatsSD, we have a copy we own of the LLSD representation // of the asset stats. Add some additional fields and ship it off. + static const S32 metrics_data_version = 2; + bool initial_report = !reporting_started; mStatsSD["session_id"] = mSessionID; mStatsSD["agent_id"] = mAgentID; mStatsSD["message"] = "ViewerAssetMetrics"; mStatsSD["sequence"] = report_sequence; mStatsSD["initial"] = initial_report; + mStatsSD["version"] = metrics_data_version; mStatsSD["break"] = static_cast(LLTextureFetch::svMetricsDataBreak); LL_INFOS(LOG_TXT) << "ViewerAssetMetrics after fields added\n" << ll_pretty_print_sd(mStatsSD) << LL_ENDL; @@ -4041,6 +4044,11 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) mStatsSD["truncated"] = truncate_viewer_metrics(10, mStatsSD); + if (gSavedSettings.getBOOL("QAModeMetrics")) + { + dump_sequential_xml("metric_asset_stats",mStatsSD); + } + if (! mCapsURL.empty()) { // Don't care about handle, this is a fire-and-forget operation. -- cgit v1.2.3 From a1b6e9b43861895c3baf993ba666eff7ab65131a Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 29 Mar 2017 20:29:40 -0400 Subject: MAINT-7166 - diagnostics for movement --- indra/llcharacter/llmotioncontroller.cpp | 8 ++++++++ indra/newview/lltexturefetch.cpp | 4 ---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index d8185aa693..379667418a 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -365,6 +365,8 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id ) // initialize the new instance LLMotion::LLMotionInitStatus stat = motion->onInitialize(mCharacter); + // Only use this in one case, declaring here to stop compiler from whining. + LLKeyframeMotion *keymotion = NULL; switch(stat) { case LLMotion::STATUS_FAILURE: @@ -378,6 +380,12 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id ) case LLMotion::STATUS_SUCCESS: // add motion to our list mLoadedMotions.insert(motion); + keymotion = dynamic_cast(motion); + if (keymotion) + { + std::string noname; + keymotion->dumpToFile(noname); + } break; default: LL_ERRS() << "Invalid initialization status" << LL_ENDL; diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 887ad157ba..09d7c2eb2d 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -30,8 +30,6 @@ #include #include -#include "llstl.h" - #include "lltexturefetch.h" #include "lldir.h" @@ -4030,8 +4028,6 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) mStatsSD["initial"] = initial_report; mStatsSD["version"] = metrics_data_version; mStatsSD["break"] = static_cast(LLTextureFetch::svMetricsDataBreak); - - LL_INFOS(LOG_TXT) << "ViewerAssetMetrics after fields added\n" << ll_pretty_print_sd(mStatsSD) << LL_ENDL; // Update sequence number if (S32_MAX == ++report_sequence) -- cgit v1.2.3 From e6c11693c7142ca11f5ce66185e318dcfe53dc75 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 30 Mar 2017 13:28:53 -0700 Subject: SL-321: upgrade VMP package 503815 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 84aed54b0e..c01685ef50 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3026,9 +3026,9 @@ archive hash - 62e8464a3d020075266e3f8dbb4e89be + 200cc138491dc913fcbb7b173bab4f58 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3745/9922/viewer_manager-1.0-darwin64-503733.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3826/10314/viewer_manager-1.0-darwin64-503815.tar.bz2 name darwin64 @@ -3050,9 +3050,9 @@ archive hash - 1e5cd892526b78ba174feb65b0653d4b + f516b8cdb5f67f86250a927b8dc6f67d url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3746/9929/viewer_manager-1.0-windows-503733.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3828/10320/viewer_manager-1.0-windows-503815.tar.bz2 name windows -- cgit v1.2.3 From 8adca0e9f920c7232c5f29df43210a09e4d7da97 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 30 Mar 2017 15:31:39 -0700 Subject: dummy commit to bump build (target for upgrade) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b80277370a..6eddb9aa9e 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,4 @@ To download the current default version, visit [the download page](https://secondlife.com/support/downloads). For even newer versions try [the Alternate Viewers page](https://wiki.secondlife.com/wiki/Linden_Lab_Official:Alternate_Viewers) + -- cgit v1.2.3 From 2310f31e0d954a6ac1eb69e452faca94a9a59075 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Fri, 31 Mar 2017 13:08:22 -0400 Subject: SL-7166 - backed out misplaced change --- indra/llcharacter/llmotioncontroller.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp index 379667418a..d8185aa693 100644 --- a/indra/llcharacter/llmotioncontroller.cpp +++ b/indra/llcharacter/llmotioncontroller.cpp @@ -365,8 +365,6 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id ) // initialize the new instance LLMotion::LLMotionInitStatus stat = motion->onInitialize(mCharacter); - // Only use this in one case, declaring here to stop compiler from whining. - LLKeyframeMotion *keymotion = NULL; switch(stat) { case LLMotion::STATUS_FAILURE: @@ -380,12 +378,6 @@ LLMotion* LLMotionController::createMotion( const LLUUID &id ) case LLMotion::STATUS_SUCCESS: // add motion to our list mLoadedMotions.insert(motion); - keymotion = dynamic_cast(motion); - if (keymotion) - { - std::string noname; - keymotion->dumpToFile(noname); - } break; default: LL_ERRS() << "Invalid initialization status" << LL_ENDL; -- cgit v1.2.3 From 97c43e6397299620bd3018ae3221ec468cce7962 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 31 Mar 2017 12:38:37 -0700 Subject: SL-321: sign *before* nsis grabs the EXEs --- indra/newview/viewer_manifest.py | 45 ++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 770567f13d..81711c717e 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -699,6 +699,29 @@ class WindowsManifest(ViewerManifest): "%%ENGAGEREGISTRY%%":engage_registry, "%%DELETE_FILES%%":self.nsi_file_commands(False)}) + # If we're on a build machine, sign the code using our Authenticode certificate. JC + # note that the enclosing setup exe is signed later, after the makensis makes it. + sign_py = os.path.expandvars("${SIGN}") + if not sign_py or sign_py == "${SIGN}": + sign_py = 'C:\\buildscripts\\code-signing\\sign.py' + else: + sign_py = sign_py.replace('\\', '\\\\\\\\') + python = os.path.expandvars("${PYTHON}") + if not python or python == "${PYTHON}": + python = 'python' + if os.path.exists(sign_py): + #Unlike the viewer binary, the VMP filenames are invariant with respect to version, os, etc. + print "about to run signing of: ", self.dst_path_of("apply_update.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("apply_update.exe").replace('\\', '\\\\\\\\'))) + print "about to run signing of: ", self.dst_path_of("download_update.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("download_update.exe").replace('\\', '\\\\\\\\'))) + print "about to run signing of: ", self.dst_path_of("SL_Launcher.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("SL_Launcher.exe").replace('\\', '\\\\\\\\'))) + print "about to run signing of: ", self.dst_path_of("update_manager.exe").replace('\\', '\\\\\\\\') + self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("update_manager.exe").replace('\\', '\\\\\\\\'))) + else: + print "Skipping code signing of vmp executables,", sign_py, "does not exist" + # We use the Unicode version of NSIS, available from # http://www.scratchpaper.com/ # Check two paths, one for Program Files, and one for Program Files (x86). @@ -722,30 +745,12 @@ class WindowsManifest(ViewerManifest): else: print >> sys.stderr, "Maximum nsis attempts exceeded; giving up" raise - # self.remove(self.dst_path_of(tempfile)) - # If we're on a build machine, sign the code using our Authenticode certificate. JC - sign_py = os.path.expandvars("${SIGN}") - if not sign_py or sign_py == "${SIGN}": - sign_py = 'C:\\buildscripts\\code-signing\\sign.py' - else: - sign_py = sign_py.replace('\\', '\\\\\\\\') - python = os.path.expandvars("${PYTHON}") - if not python or python == "${PYTHON}": - python = 'python' + if os.path.exists(sign_py): print "about to run signing of: ", self.dst_path_of(installer_file).replace('\\', '\\\\\\\\') self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of(installer_file).replace('\\', '\\\\\\\\'))) - #Unlike the viewer binary, the VMP filenames are invariant with respect to version, os, etc. - print "about to run signing of: ", self.dst_path_of("apply_update.exe").replace('\\', '\\\\\\\\') - self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("apply_update.exe").replace('\\', '\\\\\\\\'))) - print "about to run signing of: ", self.dst_path_of("download_update.exe").replace('\\', '\\\\\\\\') - self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("download_update.exe").replace('\\', '\\\\\\\\'))) - print "about to run signing of: ", self.dst_path_of("SL_Launcher.exe").replace('\\', '\\\\\\\\') - self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("SL_Launcher.exe").replace('\\', '\\\\\\\\'))) - print "about to run signing of: ", self.dst_path_of("update_manager.exe").replace('\\', '\\\\\\\\') - self.run_command("%s %s %s" % (python, sign_py, self.dst_path_of("update_manager.exe").replace('\\', '\\\\\\\\'))) else: - print "Skipping code signing,", sign_py, "does not exist" + print "Skipping code signing of setup executable,", sign_py, "does not exist" self.created_path(self.dst_path_of(installer_file)) self.package_file = installer_file -- cgit v1.2.3 From d2db8239c21298f55a468100d0b3b06d2623bf67 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 5 Apr 2017 16:50:47 -0700 Subject: MAINT-6928: update VMP package 503733, add requests package 504010, associated viewer-manifest changes for new files --- autobuild.xml | 54 +++++++++++++++++++++++++++++++++++++--- indra/newview/viewer_manifest.py | 26 +++++++++++++------ 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index c01685ef50..0edecf0934 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2797,6 +2797,52 @@ version 8.35.500898 + requests + + copyright + Kenneth Reitz + description + Python HTTP Library + license + Apache + license_file + LICENSE + name + requests + platforms + + darwin64 + + archive + + hash + 14a3c749f78e8489c7291c35fe997f2e + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4022/11166/requests-1.0-darwin64-504010.tar.bz2 + + name + darwin64 + + linux64 + + archive + + hash + 7b7f5985cad7917451f9482d0727b380 + url + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4021/11161/requests-1.0-linux64-504010.tar.bz2 + + name + linux64 + + + source + https://bitbucket.org/lindenlab/p64_python-requests + source_type + hg + version + 1.0 + slvoice copyright @@ -3026,9 +3072,9 @@ archive hash - 200cc138491dc913fcbb7b173bab4f58 + 62e8464a3d020075266e3f8dbb4e89be url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3826/10314/viewer_manager-1.0-darwin64-503815.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3745/9922/viewer_manager-1.0-darwin64-503733.tar.bz2 name darwin64 @@ -3050,9 +3096,9 @@ archive hash - f516b8cdb5f67f86250a927b8dc6f67d + 1e5cd892526b78ba174feb65b0653d4b url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3828/10320/viewer_manager-1.0-windows-503815.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3746/9929/viewer_manager-1.0-windows-503733.tar.bz2 name windows diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 81711c717e..512a1e870d 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -436,15 +436,15 @@ class WindowsManifest(ViewerManifest): # These need to be installed as a SxS assembly, currently a 'private' assembly. # See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx if self.args['configuration'].lower() == 'debug': - self.path("msvcr120d.dll") - self.path("msvcp120d.dll") - self.path("msvcr100d.dll") - self.path("msvcp100d.dll") + self.path("msvcr120d.dll") + self.path("msvcp120d.dll") + self.path("msvcr100d.dll") + self.path("msvcp100d.dll") else: - self.path("msvcr120.dll") - self.path("msvcp120.dll") - self.path("msvcr100.dll") - self.path("msvcp100.dll") + self.path("msvcr120.dll") + self.path("msvcp120.dll") + self.path("msvcr100.dll") + self.path("msvcp100.dll") # Vivox runtimes self.path("SLVoice.exe") @@ -778,7 +778,9 @@ class DarwinManifest(ViewerManifest): relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") + vmp266dir = os.path.join(vmpdir, "2.6") llbasedir = os.path.join(pkgdir, "llbase") + requestsdir = os.path.join(pkgdir, "requests") if self.prefix(src="", dst="Contents"): # everything goes in Contents self.path("Info.plist", dst="Info.plist") @@ -799,6 +801,14 @@ class DarwinManifest(ViewerManifest): self.path2basename(llbasedir,"*.py") self.path2basename(llbasedir,"_cllsd.so") self.end_prefix() + #requests module needed by llbase/llrest.py + #this is only needed on POSIX, because in Windows we compile it into the EXE + requests_path = os.path.join(self.get_dst_prefix(),'requests') + if not os.path.exists(requests_path): + os.makedirs(requests_path) + if self.prefix(dst="requests"): + self.path2basename(requestsdir,"*") + self.end_prefix() self.end_prefix() # most everything goes in the Resources directory -- cgit v1.2.3 From 9770519a6e7f542cdfb95c996897f274933ad08f Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Thu, 6 Apr 2017 14:06:49 -0700 Subject: SL-321: add Requests lib cmake file --- indra/cmake/Requests.cmake | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 indra/cmake/Requests.cmake diff --git a/indra/cmake/Requests.cmake b/indra/cmake/Requests.cmake new file mode 100644 index 0000000000..b8fcaf4764 --- /dev/null +++ b/indra/cmake/Requests.cmake @@ -0,0 +1,2 @@ +include (Prebuilt) +use_prebuilt_binary(requests) -- cgit v1.2.3 From 5ae67d8dcd05f2b9bb82ddc07a26e492946da420 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 7 Apr 2017 11:51:10 -0700 Subject: MAINT-7282: add requests module and associated build machinery --- autobuild.xml | 10 +++++----- indra/cmake/Requests.cmake | 6 ++++-- indra/newview/CMakeLists.txt | 1 + 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 5ad0b66336..b6a494f515 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2806,7 +2806,7 @@ license Apache license_file - LICENSE + requests.txt name requests platforms @@ -2816,9 +2816,9 @@ archive hash - 14a3c749f78e8489c7291c35fe997f2e + 20a84252b40223d5c08a6c1575b3333b url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4022/11166/requests-1.0-darwin64-504010.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4106/11536/requests-1.0-darwin64-504094.tar.bz2 name darwin64 @@ -2828,9 +2828,9 @@ archive hash - 7b7f5985cad7917451f9482d0727b380 + a92f2235991871c3d601a73cfef9b2af url - http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4021/11161/requests-1.0-linux64-504010.tar.bz2 + http://automated-builds-secondlife-com.s3.amazonaws.com/ct2/4105/11530/requests-1.0-linux64-504094.tar.bz2 name linux64 diff --git a/indra/cmake/Requests.cmake b/indra/cmake/Requests.cmake index b8fcaf4764..84ee070813 100644 --- a/indra/cmake/Requests.cmake +++ b/indra/cmake/Requests.cmake @@ -1,2 +1,4 @@ -include (Prebuilt) -use_prebuilt_binary(requests) +if (DARWIN) + include (Prebuilt) + use_prebuilt_binary(requests) +endif (DARWIN) diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 7a62d8baec..fa53b5a45d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -43,6 +43,7 @@ include(OPENAL) include(OpenGL) include(OpenSSL) include(PNG) +include(Requests) include(TemplateCheck) include(UI) include(UnixInstall) -- cgit v1.2.3 From 127ffc33df7f810ccfc8f23fdd700407cdd3b158 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Fri, 7 Apr 2017 13:48:00 -0700 Subject: SL-321: upgrade VMP package to 504101 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index b6a494f515..3b4a10fc56 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - 62e8464a3d020075266e3f8dbb4e89be + a5fdf96ad417880108ec7bbdb20843ec url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3745/9922/viewer_manager-1.0-darwin64-503733.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4113/11555/viewer_manager-1.0-darwin64-504101.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 1e5cd892526b78ba174feb65b0653d4b + 1a98826c4ac200fd1f2ad05f8e98c4cf url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/3746/9929/viewer_manager-1.0-windows-503733.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4114/11561/viewer_manager-1.0-windows-504101.tar.bz2 name windows -- cgit v1.2.3 From 6a393ada3e1d2cf0543f80dfd522436554d5bb95 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 10 Apr 2017 13:33:55 -0700 Subject: MAINT-6928: Add in Python 2.6.6 modules --- indra/CMakeLists.txt | 3 --- indra/newview/viewer_manifest.py | 12 ++++++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 84c2a14c6e..39f8697e8c 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -75,9 +75,6 @@ elseif (WINDOWS) endif (EXISTS ${VIEWER_DIR}win_setup) # add_dependencies(viewer windows-setup windows-crash-logger) add_dependencies(viewer windows-crash-logger) -elseif (SOLARIS) - add_subdirectory(solaris_crash_logger) - add_dependencies(viewer solaris-crash-logger) endif (LINUX) add_subdirectory(${VIEWER_PREFIX}newview) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 512a1e870d..7c1db31f04 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -790,7 +790,7 @@ class DarwinManifest(ViewerManifest): self.path(os.path.join(relpkgdir, "libhunspell-1.3.0.dylib"), dst="Resources/libhunspell-1.3.0.dylib") if self.prefix(dst="MacOS"): - #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322 and SL-323 + #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322, SL-323 and MAINT-6928 self.path2basename(vmpdir,"SL_Launcher") self.path2basename(vmpdir,"*.py") llbase_path = os.path.join(self.get_dst_prefix(),'llbase') @@ -808,6 +808,14 @@ class DarwinManifest(ViewerManifest): os.makedirs(requests_path) if self.prefix(dst="requests"): self.path2basename(requestsdir,"*") + self.end_prefix() + #these supplemental files are in case the user still has 2.6 as their default python + #https://jira.secondlife.com/browse/MAINT-6928 + p266_path = os.path.join(self.get_dst_prefix(),'2.6') + if not os.path.exists(p266_path): + os.makedirs(p266_path) + if self.prefix(dst="2.6"): + self.path2basename(vmp266dir,"*.py") self.end_prefix() self.end_prefix() @@ -903,7 +911,7 @@ class DarwinManifest(ViewerManifest): 'ca-bundle.crt', 'SLVoice', ): - self.path2basename(relpkgdir, libfile) + self.path2basename(relpkgdir, libfile) # dylibs that vary based on configuration if self.args['configuration'].lower() == 'debug': -- cgit v1.2.3 From 5a1135e364142c173a75ed0002ff336a31745019 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 11 Apr 2017 09:08:48 -0700 Subject: MAINT-6928: upgrade to VMP package 504140, use renamed directory to walk around codesign bug; fix dullahan file copy code --- autobuild.xml | 8 ++++---- indra/newview/viewer_manifest.py | 12 ++++++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 3b4a10fc56..04d3856103 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - a5fdf96ad417880108ec7bbdb20843ec + e389ce7a6732c9c9e71da4b12d5d6aa3 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4113/11555/viewer_manager-1.0-darwin64-504101.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4151/11742/viewer_manager-1.0-darwin64-504140.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 1a98826c4ac200fd1f2ad05f8e98c4cf + 0c28cb0a9fdeecd138c33166c102c70b url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4114/11561/viewer_manager-1.0-windows-504101.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4153/11755/viewer_manager-1.0-windows-504140.tar.bz2 name windows diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 7c1db31f04..4fb40061c2 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -779,6 +779,8 @@ class DarwinManifest(ViewerManifest): debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") vmp266dir = os.path.join(vmpdir, "2.6") + #apparently the codesign tool has a problem with dir names with dots in them + vmp266nodotdir = os.path.join(vmpdir, "python26") llbasedir = os.path.join(pkgdir, "llbase") requestsdir = os.path.join(pkgdir, "requests") @@ -818,6 +820,12 @@ class DarwinManifest(ViewerManifest): self.path2basename(vmp266dir,"*.py") self.end_prefix() self.end_prefix() + if os.path.exists(vmp266dir): + try: + os.rename(vmp266dir, vmp266nodotdir) + except Exception, e: + print "Failed to rename python 2.6 supplemental directory with error %s" % repr(e) + raise # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): @@ -948,8 +956,8 @@ class DarwinManifest(ViewerManifest): # Dullahan helper apps go inside SLPlugin.app if self.prefix(src="", dst="SLPlugin.app/Contents/Frameworks"): - for helperappfile in ('DullahanHelper.app'): - self.path2basename(relpkgdir, helperappfile) + helperappfile = 'DullahanHelper.app' + self.path2basename(relpkgdir, helperappfile) pluginframeworkpath = self.dst_path_of('Chromium Embedded Framework.framework'); # Putting a Frameworks directory under Contents/MacOS -- cgit v1.2.3 From 9311cc89ea36139b454b36349f11162f72924df7 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 11 Apr 2017 13:45:43 -0400 Subject: DRTVWR-434 - trivial code change to trigger TC rebuild, also slight improvement to a log message --- indra/newview/llviewerassetstorage.cpp | 6 ++++-- indra/newview/llvoavatar.cpp | 2 +- indra/test/test.cpp | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index cb60337a7f..d95b104f5c 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -455,7 +455,7 @@ void LLViewerAssetStorage::assetRequestCoro( if (!status) { // TODO asset-http: handle failures - LL_DEBUGS("ViewerAsset") << "request failed, status " << status.toTerseString() << ", now what?" << LL_ENDL; + LL_DEBUGS("ViewerAsset") << "request failed, status " << status.toTerseString() << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_NONE; } @@ -468,7 +468,9 @@ void LLViewerAssetStorage::assetRequestCoro( S32 size = raw.size(); if (size > 0) { - // This create-then-rename flow is modeled on LLTransferTargetVFile, which is what's used in the UDP case. + // This create-then-rename flow is modeled on + // LLTransferTargetVFile, which is what was used in the UDP + // case. LLUUID temp_id; temp_id.generate(); LLVFile vf(gAssetStorage->mVFS, temp_id, atype, LLVFile::WRITE); diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 8888879b8a..adbfb1831f 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -9381,4 +9381,4 @@ BOOL LLVOAvatar::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, } - +x=2; diff --git a/indra/test/test.cpp b/indra/test/test.cpp index e42374d56b..5bc39b74a6 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -684,4 +684,5 @@ int main(int argc, char **argv) return retval; //delete mycallback; + } -- cgit v1.2.3 From 12957977ebebc4a4213362f2de6e3121df4e57b2 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 11 Apr 2017 14:26:30 -0400 Subject: DRTVWR-434 - more TC build fixing --- indra/newview/llvoavatar.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index adbfb1831f..c3450f5389 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -9379,6 +9379,3 @@ BOOL LLVOAvatar::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex type, // non-self avatars don't have wearables return FALSE; } - - -x=2; -- cgit v1.2.3 From e082edf5961ead47b39a317909a1b362fea8785f Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 11 Apr 2017 12:14:21 -0700 Subject: MAINT-6928: os,rename is retarded, use shutil.move instead --- indra/newview/viewer_manifest.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 4fb40061c2..9a084da1be 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -779,8 +779,6 @@ class DarwinManifest(ViewerManifest): debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") vmp266dir = os.path.join(vmpdir, "2.6") - #apparently the codesign tool has a problem with dir names with dots in them - vmp266nodotdir = os.path.join(vmpdir, "python26") llbasedir = os.path.join(pkgdir, "llbase") requestsdir = os.path.join(pkgdir, "requests") @@ -792,6 +790,8 @@ class DarwinManifest(ViewerManifest): self.path(os.path.join(relpkgdir, "libhunspell-1.3.0.dylib"), dst="Resources/libhunspell-1.3.0.dylib") if self.prefix(dst="MacOS"): + #apparently the codesign tool has a problem with dir names with dots in them + vmp266nodotdir = os.path.join(self.get_dst_prefix(), "python26") #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322, SL-323 and MAINT-6928 self.path2basename(vmpdir,"SL_Launcher") self.path2basename(vmpdir,"*.py") @@ -819,10 +819,11 @@ class DarwinManifest(ViewerManifest): if self.prefix(dst="2.6"): self.path2basename(vmp266dir,"*.py") self.end_prefix() - self.end_prefix() - if os.path.exists(vmp266dir): + self.end_prefix() + if os.path.exists(p266_path): try: - os.rename(vmp266dir, vmp266nodotdir) + print "renaming python 2.6 directory %s to %s " % (p266_path, vmp266nodotdir) + shutil.move(p266_path, vmp266nodotdir) except Exception, e: print "Failed to rename python 2.6 supplemental directory with error %s" % repr(e) raise -- cgit v1.2.3 From 7de21de71f16ffe6ae9d7d72133b32041a4dfcac Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 11 Apr 2017 12:57:09 -0700 Subject: MAINT-6928: guard shutil.move against retry --- indra/newview/viewer_manifest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 9a084da1be..d038392ba0 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -823,7 +823,8 @@ class DarwinManifest(ViewerManifest): if os.path.exists(p266_path): try: print "renaming python 2.6 directory %s to %s " % (p266_path, vmp266nodotdir) - shutil.move(p266_path, vmp266nodotdir) + if not os.path.exists(vmp266nodotdir): + shutil.move(p266_path, vmp266nodotdir) except Exception, e: print "Failed to rename python 2.6 supplemental directory with error %s" % repr(e) raise -- cgit v1.2.3 From e26b66cc6d48b6f98a941090f644807f4f14affb Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 11 Apr 2017 16:18:01 -0700 Subject: MAINT-6928: revert previous post-move strategy, rename via self.path instead --- indra/newview/viewer_manifest.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index d038392ba0..f6bbe7365a 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -778,7 +778,7 @@ class DarwinManifest(ViewerManifest): relpkgdir = os.path.join(pkgdir, "lib", "release") debpkgdir = os.path.join(pkgdir, "lib", "debug") vmpdir = os.path.join(pkgdir, "VMP") - vmp266dir = os.path.join(vmpdir, "2.6") + vmp266dir = os.path.join(vmpdir, "python26") llbasedir = os.path.join(pkgdir, "llbase") requestsdir = os.path.join(pkgdir, "requests") @@ -791,7 +791,7 @@ class DarwinManifest(ViewerManifest): if self.prefix(dst="MacOS"): #apparently the codesign tool has a problem with dir names with dots in them - vmp266nodotdir = os.path.join(self.get_dst_prefix(), "python26") + """vmp266nodotdir = os.path.join(self.get_dst_prefix(), "python26")""" #this copies over the python wrapper script, associated utilities and required libraries, see SL-321, SL-322, SL-323 and MAINT-6928 self.path2basename(vmpdir,"SL_Launcher") self.path2basename(vmpdir,"*.py") @@ -813,21 +813,13 @@ class DarwinManifest(ViewerManifest): self.end_prefix() #these supplemental files are in case the user still has 2.6 as their default python #https://jira.secondlife.com/browse/MAINT-6928 - p266_path = os.path.join(self.get_dst_prefix(),'2.6') + p266_path = os.path.join(self.get_dst_prefix(),'python26') if not os.path.exists(p266_path): os.makedirs(p266_path) - if self.prefix(dst="2.6"): + if self.prefix("python26"): self.path2basename(vmp266dir,"*.py") self.end_prefix() - self.end_prefix() - if os.path.exists(p266_path): - try: - print "renaming python 2.6 directory %s to %s " % (p266_path, vmp266nodotdir) - if not os.path.exists(vmp266nodotdir): - shutil.move(p266_path, vmp266nodotdir) - except Exception, e: - print "Failed to rename python 2.6 supplemental directory with error %s" % repr(e) - raise + self.end_prefix() # most everything goes in the Resources directory if self.prefix(src="", dst="Resources"): -- cgit v1.2.3 From dafdeb95c247ceab2766a141070ff8758b1e174c Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Wed, 12 Apr 2017 12:43:02 -0700 Subject: MAINT-6928: upgrade to VMP package 504232 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 04d3856103..f68f7b77c9 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - e389ce7a6732c9c9e71da4b12d5d6aa3 + bc59c4632795d4c896eed4657883a55b url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4151/11742/viewer_manager-1.0-darwin64-504140.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4243/12165/viewer_manager-1.0-darwin64-504232.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 0c28cb0a9fdeecd138c33166c102c70b + 1e8f5b241bb05bbbdd35ba4453d44c7c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4153/11755/viewer_manager-1.0-windows-504140.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4244/12171/viewer_manager-1.0-windows-504232.tar.bz2 name windows -- cgit v1.2.3 From 3bb00a99e3277828d3c126574e4c676712bcf758 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 12 Apr 2017 16:06:23 -0400 Subject: MAINT-7195 - fixed bug in the wait-for-caps logic. Doesn't really matter in practice since the member variable in question still gets set by the cap received callback. --- indra/newview/llviewerassetstorage.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index d95b104f5c..0ca896ce1a 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -424,12 +424,9 @@ void LLViewerAssetStorage::assetRequestCoro( llcoro::suspendUntilEventOn(capsRecv); } - else + if (mViewerAssetUrl.empty()) { - if (mViewerAssetUrl.empty()) - { - mViewerAssetUrl = gAgent.getRegion()->getViewerAssetUrl(); - } + mViewerAssetUrl = gAgent.getRegion()->getViewerAssetUrl(); } if (mViewerAssetUrl.empty()) { @@ -454,7 +451,6 @@ void LLViewerAssetStorage::assetRequestCoro( LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) { - // TODO asset-http: handle failures LL_DEBUGS("ViewerAsset") << "request failed, status " << status.toTerseString() << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_NONE; -- cgit v1.2.3 From eeae193bd16a4f4de375d862580a6594f66f0387 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Sat, 15 Apr 2017 17:12:54 -0700 Subject: MAINT-6928: upgrade to VMP package 504240 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index f68f7b77c9..78a01bf48c 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - bc59c4632795d4c896eed4657883a55b + 3812156950f0f80d178f224d80fcef1c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4243/12165/viewer_manager-1.0-darwin64-504232.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4251/12224/viewer_manager-1.0-darwin64-504240.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 1e8f5b241bb05bbbdd35ba4453d44c7c + 4a82450f13e4ed4c21d35ef73b836c73 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4244/12171/viewer_manager-1.0-windows-504232.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4252/12230/viewer_manager-1.0-windows-504240.tar.bz2 name windows -- cgit v1.2.3 From 778a35d9bcfc254490dcbc118701b5081b22f848 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 17 Apr 2017 11:42:36 -0700 Subject: SL-617: upgrade VMP package to 504287 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 78a01bf48c..42c164e361 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - 3812156950f0f80d178f224d80fcef1c + 077de083ecc816509d08db03d45de2c5 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4251/12224/viewer_manager-1.0-darwin64-504240.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4298/12539/viewer_manager-1.0-darwin64-504287.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 4a82450f13e4ed4c21d35ef73b836c73 + 7a1d8e797afa953869ccedc3c398c7b8 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4252/12230/viewer_manager-1.0-windows-504240.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4299/12545/viewer_manager-1.0-windows-504287.tar.bz2 name windows -- cgit v1.2.3 From 869771f7c9b6ef30d01120b68b3759aeac74fea5 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 17 Apr 2017 13:09:19 -0700 Subject: SL-617: alter viewer manifest to write exe name to summary.json, upgrade VMP package to 504292 to read it --- autobuild.xml | 8 ++++---- indra/newview/viewer_manifest.py | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 42c164e361..f7ee8cd6d8 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - 077de083ecc816509d08db03d45de2c5 + ba331acee4f295a62b785b1cf80f3584 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4298/12539/viewer_manager-1.0-darwin64-504287.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4303/12557/viewer_manager-1.0-darwin64-504292.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 7a1d8e797afa953869ccedc3c398c7b8 + 4319d4db1e765b2773251f3bdf553034 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4299/12545/viewer_manager-1.0-windows-504287.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4304/12563/viewer_manager-1.0-windows-504292.tar.bz2 name windows diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index f6bbe7365a..bac80d8c13 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -206,6 +206,9 @@ class ViewerManifest(LLManifest): summary_dict = {"Type":"viewer","Version":'.'.join(self.args['version']), "Channel":self.channel_with_pkg_suffix(), "Platform":summary_json_platform} + #MAINT-7294: Windows exe names depend on channel name, so write that in also + if summary_json_platform.startswith('win'): + summary_dict.update({'Executable':self.channel_with_pkg_suffix().replace(' ', '')+'.exe'}) with open(os.path.join(os.pardir,'summary.json'), 'w') as summary_handle: json.dump(summary_dict,summary_handle) -- cgit v1.2.3 From 315045dd67547302cab7e96b86910b1c2355c625 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 17 Apr 2017 16:00:50 -0700 Subject: SL-617: upgrade VMP package to 504305 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index f7ee8cd6d8..abb23a3086 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - ba331acee4f295a62b785b1cf80f3584 + dff51c732bfe920d9b64c54e53dfed30 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4303/12557/viewer_manager-1.0-darwin64-504292.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4316/12634/viewer_manager-1.0-darwin64-504305.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 4319d4db1e765b2773251f3bdf553034 + 00f04af17ed799b399b03816d57df05b url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4304/12563/viewer_manager-1.0-windows-504292.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4317/12640/viewer_manager-1.0-windows-504305.tar.bz2 name windows -- cgit v1.2.3 From 91158bf87cc5603a42ada3a29b1e716d08bed4a3 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 17 Apr 2017 17:39:26 -0700 Subject: MAINT-6928: upgrade VMP package to 504317 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index abb23a3086..688cb9e21a 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - dff51c732bfe920d9b64c54e53dfed30 + b89a240a034cd55e72628a00c12148d4 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4316/12634/viewer_manager-1.0-darwin64-504305.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4331/12670/viewer_manager-1.0-darwin64-504317.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 00f04af17ed799b399b03816d57df05b + 501020737bde2fd513dcfed1d9181cd8 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4317/12640/viewer_manager-1.0-windows-504305.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4328/12655/viewer_manager-1.0-windows-504317.tar.bz2 name windows -- cgit v1.2.3 From 305f4d41995a044d109dd9adb2d3c29501eca0f5 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Tue, 18 Apr 2017 11:57:30 -0700 Subject: MAINT-6928: upgrade VMP package to 504332 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 688cb9e21a..5423397302 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - b89a240a034cd55e72628a00c12148d4 + 6787838a6268f3a5f8cd214189e8b29a url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4331/12670/viewer_manager-1.0-darwin64-504317.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4343/12783/viewer_manager-1.0-darwin64-504332.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 501020737bde2fd513dcfed1d9181cd8 + 1a72b2adf17ab25510c5f915437bf0a2 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4328/12655/viewer_manager-1.0-windows-504317.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4344/12789/viewer_manager-1.0-windows-504332.tar.bz2 name windows -- cgit v1.2.3 From 1ea0db76f60e4d90a846390f9480ae2ae2534fbb Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Tue, 18 Apr 2017 17:33:50 -0400 Subject: SL-409 - Added a bit more logging in hopes of locating cause of increased crashes. --- indra/newview/llviewerassetstorage.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 0ca896ce1a..c0fd0774f6 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -392,7 +392,14 @@ void LLViewerAssetStorage::queueRequestHttp( void LLViewerAssetStorage::capsRecvForRegion(const LLUUID& region_id, std::string pumpname) { LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(region_id); - mViewerAssetUrl = regionp->getViewerAssetUrl(); + if (!regionp) + { + LL_WARNS() << "region not found for region_id " << region_id << LL_ENDL; + } + else + { + mViewerAssetUrl = regionp->getViewerAssetUrl(); + } LLEventPumps::instance().obtain(pumpname).post(LLSD()); } @@ -417,14 +424,18 @@ void LLViewerAssetStorage::assetRequestCoro( } else if (!gAgent.getRegion()->capabilitiesReceived()) { + LL_WARNS_ONCE() << "Waiting for capabilities" << LL_ENDL; + LLEventStream capsRecv("waitForCaps", true); gAgent.getRegion()->setCapabilitiesReceivedCallback( boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, _1, capsRecv.getName())); llcoro::suspendUntilEventOn(capsRecv); + LL_WARNS_ONCE() << "capsRecv got event" << LL_ENDL; + LL_WARNS_ONCE() << "region " << gAgent.getRegion() << " mViewerAssetUrl " << mViewerAssetUrl << LL_ENDL; } - if (mViewerAssetUrl.empty()) + if (mViewerAssetUrl.empty() && gAgent.getRegion()) { mViewerAssetUrl = gAgent.getRegion()->getViewerAssetUrl(); } -- cgit v1.2.3 From 8d12ef71a0de00f7bb3ecce61b7fe3a84bda1b88 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 19 Apr 2017 16:01:56 -0400 Subject: SL-409 - Added tags to some log messages --- indra/newview/llviewerassetstorage.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index c0fd0774f6..bb9195fe90 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -147,7 +147,7 @@ void LLViewerAssetStorage::storeAssetData( if (asset_size < 1) { // This can happen if there's a bug in our code or if the VFS has been corrupted. - LL_WARNS() << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << LL_ENDL; + LL_WARNS("AssetStorage") << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << LL_ENDL; // LLAssetStorage metric: Zero size VFS reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); @@ -188,7 +188,7 @@ void LLViewerAssetStorage::storeAssetData( } else { - LL_WARNS() << "Probable corruption in VFS file, aborting store asset data" << LL_ENDL; + LL_WARNS("AssetStorage") << "Probable corruption in VFS file, aborting store asset data" << LL_ENDL; // LLAssetStorage metric: VFS corrupt - bogus size reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, asset_size, MR_VFS_CORRUPTION, __FILE__, __LINE__, "VFS corruption" ); @@ -217,7 +217,7 @@ void LLViewerAssetStorage::storeAssetData( } else { - LL_WARNS() << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; + LL_WARNS("AssetStorage") << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << LL_ENDL; // LLAssetStorage metric: Zero size VFS reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_ZERO_SIZE, __FILE__, __LINE__, "The file didn't exist or was zero length (VFS - can't tell which)" ); if (callback) @@ -228,7 +228,7 @@ void LLViewerAssetStorage::storeAssetData( } else { - LL_WARNS() << "Attempt to move asset store request upstream w/o valid upstream provider" << LL_ENDL; + LL_WARNS("AssetStorage") << "Attempt to move asset store request upstream w/o valid upstream provider" << LL_ENDL; // LLAssetStorage metric: Upstream provider dead reportMetric( asset_id, asset_type, LLStringUtil::null, LLUUID::null, 0, MR_NO_UPSTREAM, __FILE__, __LINE__, "No upstream provider" ); if (callback) @@ -394,7 +394,7 @@ void LLViewerAssetStorage::capsRecvForRegion(const LLUUID& region_id, std::strin LLViewerRegion *regionp = LLWorld::instance().getRegionFromID(region_id); if (!regionp) { - LL_WARNS() << "region not found for region_id " << region_id << LL_ENDL; + LL_WARNS("ViewerAsset") << "region not found for region_id " << region_id << LL_ENDL; } else { @@ -416,7 +416,7 @@ void LLViewerAssetStorage::assetRequestCoro( if (!gAgent.getRegion()) { - LL_WARNS_ONCE() << "Asset request fails: no region set" << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "Asset request fails: no region set" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_NONE; removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); @@ -424,7 +424,7 @@ void LLViewerAssetStorage::assetRequestCoro( } else if (!gAgent.getRegion()->capabilitiesReceived()) { - LL_WARNS_ONCE() << "Waiting for capabilities" << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "Waiting for capabilities" << LL_ENDL; LLEventStream capsRecv("waitForCaps", true); @@ -432,8 +432,8 @@ void LLViewerAssetStorage::assetRequestCoro( boost::bind(&LLViewerAssetStorage::capsRecvForRegion, this, _1, capsRecv.getName())); llcoro::suspendUntilEventOn(capsRecv); - LL_WARNS_ONCE() << "capsRecv got event" << LL_ENDL; - LL_WARNS_ONCE() << "region " << gAgent.getRegion() << " mViewerAssetUrl " << mViewerAssetUrl << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "capsRecv got event" << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "region " << gAgent.getRegion() << " mViewerAssetUrl " << mViewerAssetUrl << LL_ENDL; } if (mViewerAssetUrl.empty() && gAgent.getRegion()) { @@ -441,7 +441,7 @@ void LLViewerAssetStorage::assetRequestCoro( } if (mViewerAssetUrl.empty()) { - LL_WARNS_ONCE() << "asset request fails: caps received but no viewer asset cap found" << LL_ENDL; + LL_WARNS_ONCE("ViewerAsset") << "asset request fails: caps received but no viewer asset cap found" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_NONE; removeAndCallbackPendingDownloads(uuid, atype, uuid, atype, result_code, ext_status); @@ -486,13 +486,13 @@ void LLViewerAssetStorage::assetRequestCoro( if (!vf.write(raw.data(),size)) { // TODO asset-http: handle error - LL_WARNS() << "Failure in vf.write()" << LL_ENDL; + LL_WARNS("ViewerAsset") << "Failure in vf.write()" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_VFS_CORRUPT; } if (!vf.rename(uuid, atype)) { - LL_WARNS() << "rename failed" << LL_ENDL; + LL_WARNS("ViewerAsset") << "rename failed" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_VFS_CORRUPT; } @@ -500,7 +500,7 @@ void LLViewerAssetStorage::assetRequestCoro( else { // TODO asset-http: handle invalid size case - LL_WARNS() << "bad size" << LL_ENDL; + LL_WARNS("ViewerAsset") << "bad size" << LL_ENDL; result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_NONE; } -- cgit v1.2.3 From 6d737c927ccb73f7d0218af14e149969d8cd8338 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 19 Apr 2017 16:29:21 -0400 Subject: SL-409 - added tags to more log messages --- indra/llmessage/llassetstorage.cpp | 102 ++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp index ab6fc69413..596d57c7b7 100644 --- a/indra/llmessage/llassetstorage.cpp +++ b/indra/llmessage/llassetstorage.cpp @@ -371,11 +371,11 @@ void LLAssetStorage::_cleanupRequests(BOOL all, S32 error) || ((RT_DOWNLOAD == rt) && LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime))) { - LL_WARNS() << "Asset " << getRequestName((ERequestType)rt) << " request " - << (all ? "aborted" : "timed out") << " for " - << tmp->getUUID() << "." - << LLAssetType::lookup(tmp->getType()) << LL_ENDL; - + LL_WARNS("AssetStorage") << "Asset " << getRequestName((ERequestType)rt) << " request " + << (all ? "aborted" : "timed out") << " for " + << tmp->getUUID() << "." + << LLAssetType::lookup(tmp->getType()) << LL_ENDL; + timed_out.push_front(tmp); iter = requests->erase(curiter); } @@ -435,8 +435,8 @@ bool LLAssetStorage::findInStaticVFSAndInvokeCallback(const LLUUID& uuid, LLAsse } else { - LL_WARNS() << "Asset vfile " << uuid << ":" << type - << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL; + LL_WARNS("AssetStorage") << "Asset vfile " << uuid << ":" << type + << " found in static cache with bad size " << file.getSize() << ", ignoring" << LL_ENDL; } } return false; @@ -513,7 +513,7 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, { if (exists) { - LL_WARNS() << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; + LL_WARNS("AssetStorage") << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; file.remove(); } @@ -529,8 +529,8 @@ void LLAssetStorage::getAssetData(const LLUUID uuid, if (callback == tmp->mDownCallback && user_data == tmp->mUserData) { // this is a duplicate from the same subsystem - throw it away - LL_WARNS() << "Discarding duplicate request for asset " << uuid - << "." << LLAssetType::lookup(type) << LL_ENDL; + LL_WARNS("AssetStorage") << "Discarding duplicate request for asset " << uuid + << "." << LLAssetType::lookup(type) << LL_ENDL; return; } @@ -600,13 +600,13 @@ void LLAssetStorage::downloadCompleteCallback( LLAssetRequest* req = (LLAssetRequest*)user_data; if(!req) { - LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without" + LL_WARNS("AssetStorage") << "LLAssetStorage::downloadCompleteCallback called without" "a valid request." << LL_ENDL; return; } if (!gAssetStorage) { - LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; + LL_WARNS("AssetStorage") << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; return; } @@ -637,7 +637,7 @@ void LLAssetStorage::downloadCompleteCallback( LLVFile vfile(gAssetStorage->mVFS, callback_id, callback_type); if (vfile.getSize() <= 0) { - LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; + LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset " << callback_id << LL_ENDL; result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; vfile.remove(); @@ -712,7 +712,7 @@ void LLAssetStorage::getEstateAsset( { if (exists) { - LL_WARNS() << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; + LL_WARNS("AssetStorage") << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; file.remove(); } @@ -752,7 +752,7 @@ void LLAssetStorage::getEstateAsset( else { // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; + LL_WARNS("AssetStorage") << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; if (callback) { add(sFailedDownloadCount, 1); @@ -772,13 +772,13 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( LLEstateAssetRequest *req = (LLEstateAssetRequest*)user_data; if(!req) { - LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" + LL_WARNS("AssetStorage") << "LLAssetStorage::downloadEstateAssetCompleteCallback called" " without a valid request." << LL_ENDL; return; } if (!gAssetStorage) { - LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" + LL_WARNS("AssetStorage") << "LLAssetStorage::downloadEstateAssetCompleteCallback called" " without any asset system, aborting!" << LL_ENDL; return; } @@ -791,7 +791,7 @@ void LLAssetStorage::downloadEstateAssetCompleteCallback( LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType()); if (vfile.getSize() <= 0) { - LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; + LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; vfile.remove(); @@ -836,7 +836,7 @@ void LLAssetStorage::getInvItemAsset( size = exists ? file.getSize() : 0; if(exists && size < 1) { - LL_WARNS() << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; + LL_WARNS("AssetStorage") << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << LL_ENDL; file.remove(); } @@ -896,7 +896,7 @@ void LLAssetStorage::getInvItemAsset( else { // uh-oh, we shouldn't have gotten here - LL_WARNS() << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; + LL_WARNS("AssetStorage") << "Attempt to move asset data request upstream w/o valid upstream provider" << LL_ENDL; if (callback) { add(sFailedDownloadCount, 1); @@ -917,13 +917,13 @@ void LLAssetStorage::downloadInvItemCompleteCallback( LLInvItemRequest *req = (LLInvItemRequest*)user_data; if(!req) { - LL_WARNS() << "LLAssetStorage::downloadEstateAssetCompleteCallback called" + LL_WARNS("AssetStorage") << "LLAssetStorage::downloadEstateAssetCompleteCallback called" " without a valid request." << LL_ENDL; return; } if (!gAssetStorage) { - LL_WARNS() << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; + LL_WARNS("AssetStorage") << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << LL_ENDL; return; } @@ -935,7 +935,7 @@ void LLAssetStorage::downloadInvItemCompleteCallback( LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType()); if (vfile.getSize() <= 0) { - LL_WARNS() << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; + LL_WARNS("AssetStorage") << "downloadCompleteCallback has non-existent or zero-size asset!" << LL_ENDL; result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE; vfile.remove(); @@ -962,7 +962,7 @@ void LLAssetStorage::uploadCompleteCallback( { if (!gAssetStorage) { - LL_WARNS() << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << LL_ENDL; + LL_WARNS("AssetStorage") << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << LL_ENDL; return; } LLAssetRequest *req = (LLAssetRequest *)user_data; @@ -970,7 +970,7 @@ void LLAssetStorage::uploadCompleteCallback( if (result) { - LL_WARNS() << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << LL_ENDL; + LL_WARNS("AssetStorage") << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << LL_ENDL; success = FALSE; } @@ -1046,14 +1046,14 @@ LLAssetStorage::request_list_t* LLAssetStorage::getRequestList(LLAssetStorage::E switch (rt) { case RT_DOWNLOAD: - return &mPendingDownloads; + return &mPendingDownloads; case RT_UPLOAD: - return &mPendingUploads; + return &mPendingUploads; case RT_LOCALUPLOAD: - return &mPendingLocalUploads; + return &mPendingLocalUploads; default: - LL_WARNS() << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; - return NULL; + LL_WARNS("AssetStorage") << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; + return NULL; } } @@ -1062,14 +1062,14 @@ const LLAssetStorage::request_list_t* LLAssetStorage::getRequestList(LLAssetStor switch (rt) { case RT_DOWNLOAD: - return &mPendingDownloads; + return &mPendingDownloads; case RT_UPLOAD: - return &mPendingUploads; + return &mPendingUploads; case RT_LOCALUPLOAD: - return &mPendingLocalUploads; + return &mPendingLocalUploads; default: - LL_WARNS() << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; - return NULL; + LL_WARNS("AssetStorage") << "Unable to find request list for request type '" << rt << "'" << LL_ENDL; + return NULL; } } @@ -1079,14 +1079,14 @@ std::string LLAssetStorage::getRequestName(LLAssetStorage::ERequestType rt) switch (rt) { case RT_DOWNLOAD: - return "download"; + return "download"; case RT_UPLOAD: - return "upload"; + return "upload"; case RT_LOCALUPLOAD: - return "localupload"; + return "localupload"; default: - LL_WARNS() << "Unable to find request name for request type '" << rt << "'" << LL_ENDL; - return ""; + LL_WARNS("AssetStorage") << "Unable to find request name for request type '" << rt << "'" << LL_ENDL; + return ""; } } @@ -1277,37 +1277,37 @@ const char* LLAssetStorage::getErrorString(S32 status) switch( status ) { case LL_ERR_NOERR: - return "No error"; + return "No error"; case LL_ERR_ASSET_REQUEST_FAILED: - return "Asset request: failed"; + return "Asset request: failed"; case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE: - return "Asset request: non-existent file"; + return "Asset request: non-existent file"; case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE: - return "Asset request: asset not found in database"; + return "Asset request: asset not found in database"; case LL_ERR_EOF: - return "End of file"; + return "End of file"; case LL_ERR_CANNOT_OPEN_FILE: - return "Cannot open file"; + return "Cannot open file"; case LL_ERR_FILE_NOT_FOUND: - return "File not found"; + return "File not found"; case LL_ERR_TCP_TIMEOUT: - return "File transfer timeout"; + return "File transfer timeout"; case LL_ERR_CIRCUIT_GONE: - return "Circuit gone"; + return "Circuit gone"; case LL_ERR_PRICE_MISMATCH: - return "Viewer and server do not agree on price"; + return "Viewer and server do not agree on price"; default: - return "Unknown status"; + return "Unknown status"; } } -- cgit v1.2.3 From e6f53dedb704c92af45ede2d97c559cb6639358f Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Mon, 24 Apr 2017 11:25:04 -0400 Subject: MAINT-7343 - Added check for shutdown in progress when asset result arrives --- indra/newview/llviewerassetstorage.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index bb9195fe90..6523b89d7f 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -458,6 +458,12 @@ void LLViewerAssetStorage::assetRequestCoro( LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts); + if (LLApp::isQuitting()) + { + // Bail out if result arrives after shutdown has been started. + return; + } + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) -- cgit v1.2.3 From a3acaaa61b2144af1f76dd8a7bc2b48b9ced00d3 Mon Sep 17 00:00:00 2001 From: Glenn Glazer Date: Mon, 24 Apr 2017 16:13:56 -0700 Subject: MAINT-6928: upgrade VMP package to 504558 --- autobuild.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autobuild.xml b/autobuild.xml index 5423397302..378fdf5619 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -3072,9 +3072,9 @@ archive hash - 6787838a6268f3a5f8cd214189e8b29a + 7c1aaebbb4e34512a1489b3c67f520f2 url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4343/12783/viewer_manager-1.0-darwin64-504332.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4569/13796/viewer_manager-1.0-darwin64-504558.tar.bz2 name darwin64 @@ -3096,9 +3096,9 @@ archive hash - 1a72b2adf17ab25510c5f915437bf0a2 + 360e630b43446b6a8dc35384aa4dbd5c url - http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4344/12789/viewer_manager-1.0-windows-504332.tar.bz2 + http://s3-proxy.lindenlab.com/private-builds-secondlife-com/ct2/4571/13808/viewer_manager-1.0-windows-504558.tar.bz2 name windows -- cgit v1.2.3 From b7b8d6e1aedeac1dfdfcc9200024bbcc8e2dacae Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Wed, 26 Apr 2017 12:39:14 -0400 Subject: MAINT-7343 - added periodic logging of state of the asset store. --- indra/llcommon/llassettype.cpp | 3 -- indra/llmessage/llassetstorage.h | 2 ++ indra/newview/app_settings/settings.xml | 11 +++++++ indra/newview/lltexturefetch.cpp | 5 +++- indra/newview/llviewerassetstorage.cpp | 51 +++++++++++++++++++++++++++++++-- indra/newview/llviewerassetstorage.h | 8 ++++++ indra/newview/llviewerdisplay.cpp | 7 +++++ 7 files changed, 81 insertions(+), 6 deletions(-) diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 5e8129b9b2..4304db36be 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -31,9 +31,6 @@ #include "llmemory.h" #include "llsingleton.h" -#include -#include - ///---------------------------------------------------------------------------- /// Class LLAssetType ///---------------------------------------------------------------------------- diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h index d6b4fa0c7b..33b88473b9 100644 --- a/indra/llmessage/llassetstorage.h +++ b/indra/llmessage/llassetstorage.h @@ -253,6 +253,8 @@ public: bool user_waiting= false, F64Seconds timeout=LL_ASSET_STORAGE_TIMEOUT) = 0; + virtual void logAssetStorageInfo() = 0; + void checkForTimeouts(); void getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id, diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 097a9ac7b9..d2d629339d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -14639,6 +14639,17 @@ Value 0 + AssetStorageLogFrequency + + Comment + Seconds between display of AssetStorage info in log (0 for never) + Persist + 1 + Type + F32 + Value + 60.0 + LogWearableAssetSave Comment diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 09d7c2eb2d..7245a70962 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -1428,7 +1428,10 @@ bool LLTextureFetchWorker::doWork(S32 param) F64 byte_count = 0; for (S32 i=mFirstPacket; i<=mLastPacket; i++) { - byte_count += mPackets[i]->mSize; + if (mPackets[i]) + { + byte_count += mPackets[i]->mSize; + } } LL_DEBUGS(LOG_TXT) << mID << ": Loaded from Sim. Bytes: " << mFormattedImage->getDataSize() << LL_ENDL; diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp index 6523b89d7f..9918d226c2 100644 --- a/indra/newview/llviewerassetstorage.cpp +++ b/indra/newview/llviewerassetstorage.cpp @@ -99,17 +99,30 @@ public: /// LLViewerAssetStorage ///---------------------------------------------------------------------------- +// Unused? LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs, const LLHost &upstream_host) - : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host) + : LLAssetStorage(msg, xfer, vfs, static_vfs, upstream_host), + mAssetCoroCount(0), + mCountRequests(0), + mCountStarted(0), + mCountCompleted(0), + mCountSucceeded(0), + mTotalBytesFetched(0) { } LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, LLVFS *static_vfs) - : LLAssetStorage(msg, xfer, vfs, static_vfs) + : LLAssetStorage(msg, xfer, vfs, static_vfs), + mAssetCoroCount(0), + mCountRequests(0), + mCountStarted(0), + mCountCompleted(0), + mCountSucceeded(0), + mTotalBytesFetched(0) { } @@ -351,6 +364,7 @@ void LLViewerAssetStorage::_queueDataRequest( BOOL duplicate, BOOL is_priority) { + mCountRequests++; queueRequestHttp(uuid, atype, callback, user_data, duplicate, is_priority); } @@ -404,6 +418,20 @@ void LLViewerAssetStorage::capsRecvForRegion(const LLUUID& region_id, std::strin LLEventPumps::instance().obtain(pumpname).post(LLSD()); } +struct LLScopedIncrement +{ + LLScopedIncrement(S32& counter): + mCounter(counter) + { + ++mCounter; + } + ~LLScopedIncrement() + { + --mCounter; + } + S32& mCounter; +}; + void LLViewerAssetStorage::assetRequestCoro( LLViewerAssetRequest *req, const LLUUID& uuid, @@ -411,6 +439,9 @@ void LLViewerAssetStorage::assetRequestCoro( LLGetAssetCallback callback, void *user_data) { + LLScopedIncrement coro_count_boost(mAssetCoroCount); + mCountStarted++; + S32 result_code = LL_ERR_NOERR; LLExtStat ext_status = LL_EXSTAT_NONE; @@ -463,6 +494,8 @@ void LLViewerAssetStorage::assetRequestCoro( // Bail out if result arrives after shutdown has been started. return; } + + mCountCompleted++; LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); @@ -481,6 +514,8 @@ void LLViewerAssetStorage::assetRequestCoro( S32 size = raw.size(); if (size > 0) { + mTotalBytesFetched += size; + // This create-then-rename flow is modeled on // LLTransferTargetVFile, which is what was used in the UDP // case. @@ -502,6 +537,7 @@ void LLViewerAssetStorage::assetRequestCoro( result_code = LL_ERR_ASSET_REQUEST_FAILED; ext_status = LL_EXSTAT_VFS_CORRUPT; } + mCountSucceeded++; } else { @@ -522,3 +558,14 @@ std::string LLViewerAssetStorage::getAssetURL(const std::string& cap_url, const std::string url = cap_url + "/?" + type_name + "_id=" + uuid.asString(); return url; } + +void LLViewerAssetStorage::logAssetStorageInfo() +{ + LLMemory::logMemoryInfo(true); + LL_INFOS("AssetStorage") << "Active coros " << mAssetCoroCount << LL_ENDL; + LL_INFOS("AssetStorage") << "mPendingDownloads size " << mPendingDownloads.size() << LL_ENDL; + LL_INFOS("AssetStorage") << "mCountStarted " << mCountStarted << LL_ENDL; + LL_INFOS("AssetStorage") << "mCountCompleted " << mCountCompleted << LL_ENDL; + LL_INFOS("AssetStorage") << "mCountSucceeded " << mCountSucceeded << LL_ENDL; + LL_INFOS("AssetStorage") << "mTotalBytesFetched " << mTotalBytesFetched << LL_ENDL; +} diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h index 2f3eaa15a7..8a58241378 100644 --- a/indra/newview/llviewerassetstorage.h +++ b/indra/newview/llviewerassetstorage.h @@ -90,7 +90,15 @@ protected: std::string getAssetURL(const std::string& cap_url, const LLUUID& uuid, LLAssetType::EType atype); + void logAssetStorageInfo(); + std::string mViewerAssetUrl; + S32 mAssetCoroCount; + S32 mCountRequests; + S32 mCountStarted; + S32 mCountCompleted; + S32 mCountSucceeded; + S64 mTotalBytesFetched; }; #endif diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index afa00e3e6e..bfa9fa03fa 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -106,6 +106,7 @@ const F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f; U32 gRecentFrameCount = 0; // number of 'recent' frames LLFrameTimer gRecentFPSTime; LLFrameTimer gRecentMemoryTime; +LLFrameTimer gAssetStorageLogTime; // Rendering stuff void pre_show_depth_buffer(); @@ -226,6 +227,12 @@ void display_stats() LLMemory::logMemoryInfo(TRUE) ; gRecentMemoryTime.reset(); } + F32 asset_storage_log_freq = gSavedSettings.getF32("AssetStorageLogFrequency"); + if (asset_storage_log_freq > 0.f && gAssetStorageLogTime.getElapsedTimeF32() >= asset_storage_log_freq) + { + gAssetStorageLogTime.reset(); + gAssetStorage->logAssetStorageInfo(); + } } static LLTrace::BlockTimerStatHandle FTM_PICK("Picking"); -- cgit v1.2.3 From 24b4d3c13f15917f8c775d5b4211d213af9309c0 Mon Sep 17 00:00:00 2001 From: "coyot@coyot-sager-PC" Date: Fri, 28 Apr 2017 01:09:14 +0100 Subject: SL-671: make icon point to launcher, not viewer --- indra/newview/installers/windows/installer_template.nsi | 4 ++++ indra/newview/viewer_manifest.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index fe1b35767e..4396a576cc 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -337,6 +337,10 @@ WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" '"$INSTDIR\$ # URL param must be last item passed to viewer, it ignores subsequent params to avoid parameter injection attacks. WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" '"$INSTDIR\$INSTEXE" -url "%1"' +# Only allow Launcher to be the icon +WriteRegStr HKEY_CLASSES_ROOT "Applications" "$INSTEXE" "IsHostApp" +WriteRegStr HKEY_CLASSES_ROOT "Applications" "$VIEWER_EXE" "NoStartPage" + # Write out uninstaller WriteUninstaller "$INSTDIR\uninst.exe" diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index bac80d8c13..78dc830c1b 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -654,6 +654,7 @@ class WindowsManifest(ViewerManifest): 'version' : '.'.join(self.args['version']), 'version_short' : '.'.join(self.args['version'][:-1]), 'version_dashes' : '-'.join(self.args['version']), + 'viewer_exe' : self.channel_with_pkg_suffix().replace(' ', '')+'.exe', 'final_exe' : self.final_exe(), 'flags':'', 'app_name':self.app_name(), @@ -668,6 +669,7 @@ class WindowsManifest(ViewerManifest): !define VERSION "%(version_short)s" !define VERSION_LONG "%(version)s" !define VERSION_DASHES "%(version_dashes)s" + !define VIEWER_EXE "%(viewer_exe)%s" """ % substitution_strings if self.channel_type() == 'release': -- cgit v1.2.3 From b6e1419007e0bc768302d594649a262952e5fce6 Mon Sep 17 00:00:00 2001 From: "coyot@coyot-sager-PC" Date: Fri, 28 Apr 2017 17:05:58 +0100 Subject: SL-671: fix string substitution --- indra/newview/viewer_manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 78dc830c1b..2e3e4d6a07 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -669,7 +669,7 @@ class WindowsManifest(ViewerManifest): !define VERSION "%(version_short)s" !define VERSION_LONG "%(version)s" !define VERSION_DASHES "%(version_dashes)s" - !define VIEWER_EXE "%(viewer_exe)%s" + !define VIEWER_EXE "%(viewer_exe)s%" """ % substitution_strings if self.channel_type() == 'release': -- cgit v1.2.3 From a67d449813a9b6c6198ed15ad15d30535344051e Mon Sep 17 00:00:00 2001 From: "coyot@coyot-sager-PC" Date: Fri, 28 Apr 2017 20:04:32 +0100 Subject: SL-617: pass final_exe from viewer manifest to NSIS as VIEWER_EXE --- indra/newview/installers/windows/installer_template.nsi | 1 + indra/newview/viewer_manifest.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 4396a576cc..682c37971c 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -102,6 +102,7 @@ Page instfiles ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Var INSTPROG Var INSTEXE +Var VIEWER_EXE Var INSTSHORTCUT Var COMMANDLINE # Command line passed to this installer, set in .onInit Var SHORTCUT_LANG_PARAM # "--set InstallLanguage de", Passes language to viewer diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 2e3e4d6a07..b289db9b8f 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -654,7 +654,6 @@ class WindowsManifest(ViewerManifest): 'version' : '.'.join(self.args['version']), 'version_short' : '.'.join(self.args['version'][:-1]), 'version_dashes' : '-'.join(self.args['version']), - 'viewer_exe' : self.channel_with_pkg_suffix().replace(' ', '')+'.exe', 'final_exe' : self.final_exe(), 'flags':'', 'app_name':self.app_name(), @@ -669,7 +668,7 @@ class WindowsManifest(ViewerManifest): !define VERSION "%(version_short)s" !define VERSION_LONG "%(version)s" !define VERSION_DASHES "%(version_dashes)s" - !define VIEWER_EXE "%(viewer_exe)s%" + !define VIEWER_EXE "%(final_exe)s" """ % substitution_strings if self.channel_type() == 'release': -- cgit v1.2.3 From 9dfb5614e640b3350d89500b7d7486e2065ed7e2 Mon Sep 17 00:00:00 2001 From: "Brad Payne (Vir Linden)" Date: Fri, 28 Apr 2017 16:10:24 -0400 Subject: MAINT-7343 - moved asset downloads to use coprocedure pools, which should reduce the size of potential memory spikes --- indra/newview/app_settings/settings.xml | 9 +++++++++ indra/newview/llviewerassetstorage.cpp | 6 ++++-- indra/newview/llviewerassetstorage.h | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d2d629339d..f2ae685432 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -14723,6 +14723,15 @@ Value 1 + PoolSizeAssetStorage + + Comment + Coroutine Pool size for AssetStorage requests + Type + U32 + Value + 12 + -- cgit v1.2.3