diff options
Diffstat (limited to 'indra/viewer_components/manager/SL_Launcher')
-rwxr-xr-x | indra/viewer_components/manager/SL_Launcher | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/indra/viewer_components/manager/SL_Launcher b/indra/viewer_components/manager/SL_Launcher new file mode 100755 index 0000000000..0403e01cec --- /dev/null +++ b/indra/viewer_components/manager/SL_Launcher @@ -0,0 +1,194 @@ +#!/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 +from llbase import llsd +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 + 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 == '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 --<param> {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) = <some generator> 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 + 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: + 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) + +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) |