diff options
Diffstat (limited to 'indra')
57 files changed, 3310 insertions, 480 deletions
| diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 8d4969a49e..d01e1869b6 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -22,7 +22,10 @@ set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")  include(Variables)  if (DARWIN) -  cmake_minimum_required(VERSION 2.6.2 FATAL_ERROR) +  # 2.6.4 fixes a Mac bug in get_target_property(... "SLPlugin" LOCATION): +  # before that version it returns "pathname/SLPlugin", whereas the correct +  # answer is "pathname/SLPlugin.app/Contents/MacOS/SLPlugin". +  cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR)  endif (DARWIN)  if (NOT CMAKE_BUILD_TYPE) diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index a114d6e778..db2cdb5ff8 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -38,10 +38,10 @@ if (WINDOWS)    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /MDd /MP"        CACHE STRING "C++ compiler debug options" FORCE)    set(CMAKE_CXX_FLAGS_RELWITHDEBINFO  -      "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP" +      "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Od /Zi /MD /MP /Ob2"        CACHE STRING "C++ compiler release-with-debug options" FORCE)    set(CMAKE_CXX_FLAGS_RELEASE -      "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP" +      "${CMAKE_CXX_FLAGS_RELEASE} ${LL_CXX_FLAGS} /O2 /Zi /MD /MP /Ob2"        CACHE STRING "C++ compiler release options" FORCE)    set(CMAKE_CXX_STANDARD_LIBRARIES "") diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake index 79c3bb7da2..29e2492551 100644 --- a/indra/cmake/LLAddBuildTest.cmake +++ b/indra/cmake/LLAddBuildTest.cmake @@ -1,265 +1,273 @@ -# -*- cmake -*- -include(LLTestCommand) -include(GoogleMock) - -MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources) -  # Given a project name and a list of sourcefiles (with optional properties on each), -  # add targets to build and run the tests specified. -  # ASSUMPTIONS: -  # * this macro is being executed in the project file that is passed in -  # * current working SOURCE dir is that project dir -  # * there is a subfolder tests/ with test code corresponding to the filenames passed in -  # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT) -  # -  # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code -  # -  # WARNING: do NOT modify this code without working with poppy - -  # there is another branch that will conflict heavily with any changes here. -INCLUDE(GoogleMock) - - -  IF(LL_TEST_VERBOSE) -    MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}") -  ENDIF(LL_TEST_VERBOSE) - -  # Start with the header and project-wide setup before making targets -  #project(UNITTEST_PROJECT_${project}) -  # Setup includes, paths, etc -  SET(alltest_SOURCE_FILES -    ${CMAKE_SOURCE_DIR}/test/test.cpp -    ${CMAKE_SOURCE_DIR}/test/lltut.cpp -    ) -  SET(alltest_DEP_TARGETS -    # needed by the test harness itself -    ${APRUTIL_LIBRARIES} -    ${APR_LIBRARIES} -    llcommon -    ) -  IF(NOT "${project}" STREQUAL "llmath") -    # add llmath as a dep unless the tested module *is* llmath! -    LIST(APPEND alltest_DEP_TARGETS -      llmath -      ) -  ENDIF(NOT "${project}" STREQUAL "llmath") -  SET(alltest_INCLUDE_DIRS -    ${LLMATH_INCLUDE_DIRS} -    ${LLCOMMON_INCLUDE_DIRS} -    ${LIBS_OPEN_DIR}/test -    ${GOOGLEMOCK_INCLUDE_DIRS} -    ) -  SET(alltest_LIBRARIES -    ${GOOGLEMOCK_LIBRARIES} -    ${PTHREAD_LIBRARY} -    ${WINDOWS_LIBRARIES} -    ) -  # Headers, for convenience in targets. -  SET(alltest_HEADER_FILES -    ${CMAKE_SOURCE_DIR}/test/test.h -    ) - -  # Use the default flags -  if (LINUX) -    SET(CMAKE_EXE_LINKER_FLAGS "") -  endif (LINUX) - -  # start the source test executable definitions -  SET(${project}_TEST_OUTPUT "") -  FOREACH (source ${sources}) -    STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} ) -    STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} ) -    IF(LL_TEST_VERBOSE) -      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})") -    ENDIF(LL_TEST_VERBOSE) - -    # -    # Per-codefile additional / external source, header, and include dir property extraction -    # -    # Source -    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES) -    IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) -      SET(${name}_test_additional_SOURCE_FILES "") -    ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND) -    SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} ) -    IF(LL_TEST_VERBOSE) -      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}") -    ENDIF(LL_TEST_VERBOSE) -    # Headers -    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES) -    IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) -      SET(${name}_test_additional_HEADER_FILES "") -    ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND) -    SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES}) -    set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) -    LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES}) -    IF(LL_TEST_VERBOSE) -      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}") -    ENDIF(LL_TEST_VERBOSE) -    # Include dirs -    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS) -    IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) -      SET(${name}_test_additional_INCLUDE_DIRS "") -    ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND) -    INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS ) -    IF(LL_TEST_VERBOSE) -      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}") -    ENDIF(LL_TEST_VERBOSE) - - -    # Setup target -    ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES}) -    SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - -    # -    # Per-codefile additional / external project dep and lib dep property extraction -    # -    # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19 -    # Projects -    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS) -    IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) -      SET(${name}_test_additional_PROJECTS "") -    ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND) -    # Libraries -    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES) -    IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) -      SET(${name}_test_additional_LIBRARIES "") -    ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND) -    IF(LL_TEST_VERBOSE) -      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}") -      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}") -    ENDIF(LL_TEST_VERBOSE) -    # Add to project -    TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} ) -     -    # -    # Setup test targets -    # -    GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION) -    SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt) -    SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR}) - -    # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19 -    IF(LL_TEST_VERBOSE) -      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd  = ${TEST_CMD}") -    ENDIF(LL_TEST_VERBOSE) - -    SET_TEST_PATH(LD_LIBRARY_PATH) -    LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD}) -    IF(LL_TEST_VERBOSE) -      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script  = ${TEST_SCRIPT_CMD}") -    ENDIF(LL_TEST_VERBOSE) -    # Add test  -    ADD_CUSTOM_COMMAND( -        OUTPUT ${TEST_OUTPUT} -        COMMAND ${TEST_SCRIPT_CMD} -        DEPENDS PROJECT_${project}_TEST_${name} -        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -        ) -    # Why not add custom target and add POST_BUILD command? -    # Slightly less uncertain behavior -    # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19 -    # > I did not use a post build step as I could not make it notify of a  -    # > failure after the first time you build and fail a test. - daveh 2009-04-20 -    LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT}) -  ENDFOREACH (source) - -  # Add the test runner target per-project -  # (replaces old _test_ok targets all over the place) -  ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT}) -  ADD_DEPENDENCIES(${project} ${project}_tests) -ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS) - -FUNCTION(LL_ADD_INTEGRATION_TEST  -    testname -    additional_source_files -    library_dependencies -# variable args -    ) -  if(TEST_DEBUG) -    message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on") -  endif(TEST_DEBUG) -   -  SET(source_files -    tests/${testname}_test.cpp -    ${CMAKE_SOURCE_DIR}/test/test.cpp -    ${CMAKE_SOURCE_DIR}/test/lltut.cpp -    ${additional_source_files} -    ) - -  SET(libraries -    ${library_dependencies} -    ${GOOGLEMOCK_LIBRARIES} -    ${PTHREAD_LIBRARY} -    ) - -  # Add test executable build target -  if(TEST_DEBUG) -    message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})") -  endif(TEST_DEBUG) -  ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files}) -  SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}") - -  # Add link deps to the executable -  if(TEST_DEBUG) -    message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})") -  endif(TEST_DEBUG) -  TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries}) - -  # Create the test running command -  SET(test_command ${ARGN}) -  GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION) -  LIST(FIND test_command "{}" test_exe_pos) -  IF(test_exe_pos LESS 0) -    # The {} marker means "the full pathname of the test executable." -    # test_exe_pos -1 means we didn't find it -- so append the test executable -    # name to $ARGN, the variable part of the arg list. This is convenient -    # shorthand for both straightforward execution of the test program (empty -    # $ARGN) and for running a "wrapper" program of some kind accepting the -    # pathname of the test program as the last of its args. You need specify -    # {} only if the test program's pathname isn't the last argument in the -    # desired command line. -    LIST(APPEND test_command "${TEST_EXE}") -  ELSE (test_exe_pos LESS 0) -    # Found {} marker at test_exe_pos. Remove the {}... -    LIST(REMOVE_AT test_command test_exe_pos) -    # ...and replace it with the actual name of the test executable. -    LIST(INSERT test_command test_exe_pos "${TEST_EXE}") -  ENDIF (test_exe_pos LESS 0) - -  SET_TEST_PATH(LD_LIBRARY_PATH) -  LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command}) - -  if(TEST_DEBUG) -    message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}") -  endif(TEST_DEBUG) - -  ADD_CUSTOM_COMMAND( -    TARGET INTEGRATION_TEST_${testname} -    POST_BUILD -    COMMAND ${TEST_SCRIPT_CMD} -    ) - -  # Use CTEST? Not sure how to yet... -  # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD}) - -ENDFUNCTION(LL_ADD_INTEGRATION_TEST) - -MACRO(SET_TEST_PATH LISTVAR) -  IF(WINDOWS) -    # We typically build/package only Release variants of third-party -    # libraries, so append the Release staging dir in case the library being -    # sought doesn't have a debug variant. -    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release) -  ELSEIF(DARWIN) -    # We typically build/package only Release variants of third-party -    # libraries, so append the Release staging dir in case the library being -    # sought doesn't have a debug variant. -    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib) -  ELSE(WINDOWS) -    # Linux uses a single staging directory anyway. -    IF (STANDALONE) -      set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib) -    ELSE (STANDALONE) -      set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib) -    ENDIF (STANDALONE) -  ENDIF(WINDOWS) -ENDMACRO(SET_TEST_PATH) +# -*- cmake -*-
 +include(LLTestCommand)
 +include(GoogleMock)
 +
 +MACRO(LL_ADD_PROJECT_UNIT_TESTS project sources)
 +  # Given a project name and a list of sourcefiles (with optional properties on each),
 +  # add targets to build and run the tests specified.
 +  # ASSUMPTIONS:
 +  # * this macro is being executed in the project file that is passed in
 +  # * current working SOURCE dir is that project dir
 +  # * there is a subfolder tests/ with test code corresponding to the filenames passed in
 +  # * properties for each sourcefile passed in indicate what libs to link that file with (MAKE NO ASSUMPTIONS ASIDE FROM TUT)
 +  #
 +  # More info and examples at: https://wiki.secondlife.com/wiki/How_to_add_unit_tests_to_indra_code
 +  #
 +  # WARNING: do NOT modify this code without working with poppy -
 +  # there is another branch that will conflict heavily with any changes here.
 +INCLUDE(GoogleMock)
 +
 +
 +  IF(LL_TEST_VERBOSE)
 +    MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} sources: ${sources}")
 +  ENDIF(LL_TEST_VERBOSE)
 +
 +  # Start with the header and project-wide setup before making targets
 +  #project(UNITTEST_PROJECT_${project})
 +  # Setup includes, paths, etc
 +  SET(alltest_SOURCE_FILES
 +    ${CMAKE_SOURCE_DIR}/test/test.cpp
 +    ${CMAKE_SOURCE_DIR}/test/lltut.cpp
 +    )
 +  SET(alltest_DEP_TARGETS
 +    # needed by the test harness itself
 +    ${APRUTIL_LIBRARIES}
 +    ${APR_LIBRARIES}
 +    llcommon
 +    )
 +  IF(NOT "${project}" STREQUAL "llmath")
 +    # add llmath as a dep unless the tested module *is* llmath!
 +    LIST(APPEND alltest_DEP_TARGETS
 +      llmath
 +      )
 +  ENDIF(NOT "${project}" STREQUAL "llmath")
 +  SET(alltest_INCLUDE_DIRS
 +    ${LLMATH_INCLUDE_DIRS}
 +    ${LLCOMMON_INCLUDE_DIRS}
 +    ${LIBS_OPEN_DIR}/test
 +    ${GOOGLEMOCK_INCLUDE_DIRS}
 +    )
 +  SET(alltest_LIBRARIES
 +    ${GOOGLEMOCK_LIBRARIES}
 +    ${PTHREAD_LIBRARY}
 +    ${WINDOWS_LIBRARIES}
 +    )
 +  # Headers, for convenience in targets.
 +  SET(alltest_HEADER_FILES
 +    ${CMAKE_SOURCE_DIR}/test/test.h
 +    )
 +
 +  # Use the default flags
 +  if (LINUX)
 +    SET(CMAKE_EXE_LINKER_FLAGS "")
 +  endif (LINUX)
 +
 +  # start the source test executable definitions
 +  SET(${project}_TEST_OUTPUT "")
 +  FOREACH (source ${sources})
 +    STRING( REGEX REPLACE "(.*)\\.[^.]+$" "\\1" name ${source} )
 +    STRING( REGEX REPLACE ".*\\.([^.]+)$" "\\1" extension ${source} )
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS UNITTEST_PROJECT_${project} individual source: ${source} (${name}.${extension})")
 +    ENDIF(LL_TEST_VERBOSE)
 +
 +    #
 +    # Per-codefile additional / external source, header, and include dir property extraction
 +    #
 +    # Source
 +    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_SOURCE_FILES ${source} LL_TEST_ADDITIONAL_SOURCE_FILES)
 +    IF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
 +      SET(${name}_test_additional_SOURCE_FILES "")
 +    ENDIF(${name}_test_additional_SOURCE_FILES MATCHES NOTFOUND)
 +    SET(${name}_test_SOURCE_FILES ${source} tests/${name}_test.${extension} ${alltest_SOURCE_FILES} ${${name}_test_additional_SOURCE_FILES} )
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_SOURCE_FILES ${${name}_test_SOURCE_FILES}")
 +    ENDIF(LL_TEST_VERBOSE)
 +    # Headers
 +    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_HEADER_FILES ${source} LL_TEST_ADDITIONAL_HEADER_FILES)
 +    IF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
 +      SET(${name}_test_additional_HEADER_FILES "")
 +    ENDIF(${name}_test_additional_HEADER_FILES MATCHES NOTFOUND)
 +    SET(${name}_test_HEADER_FILES ${name}.h ${${name}_test_additional_HEADER_FILES})
 +    set_source_files_properties(${${name}_test_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE)
 +    LIST(APPEND ${name}_test_SOURCE_FILES ${${name}_test_HEADER_FILES})
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_HEADER_FILES ${${name}_test_HEADER_FILES}")
 +    ENDIF(LL_TEST_VERBOSE)
 +    # Include dirs
 +    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_INCLUDE_DIRS ${source} LL_TEST_ADDITIONAL_INCLUDE_DIRS)
 +    IF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
 +      SET(${name}_test_additional_INCLUDE_DIRS "")
 +    ENDIF(${name}_test_additional_INCLUDE_DIRS MATCHES NOTFOUND)
 +    INCLUDE_DIRECTORIES(${alltest_INCLUDE_DIRS} ${name}_test_additional_INCLUDE_DIRS )
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_INCLUDE_DIRS ${${name}_test_additional_INCLUDE_DIRS}")
 +    ENDIF(LL_TEST_VERBOSE)
 +
 +
 +    # Setup target
 +    ADD_EXECUTABLE(PROJECT_${project}_TEST_${name} ${${name}_test_SOURCE_FILES})
 +    SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
 +
 +    #
 +    # Per-codefile additional / external project dep and lib dep property extraction
 +    #
 +    # WARNING: it's REALLY IMPORTANT to not mix these. I guarantee it will not work in the future. + poppy 2009-04-19
 +    # Projects
 +    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_PROJECTS ${source} LL_TEST_ADDITIONAL_PROJECTS)
 +    IF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
 +      SET(${name}_test_additional_PROJECTS "")
 +    ENDIF(${name}_test_additional_PROJECTS MATCHES NOTFOUND)
 +    # Libraries
 +    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_LIBRARIES ${source} LL_TEST_ADDITIONAL_LIBRARIES)
 +    IF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
 +      SET(${name}_test_additional_LIBRARIES "")
 +    ENDIF(${name}_test_additional_LIBRARIES MATCHES NOTFOUND)
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_PROJECTS ${${name}_test_additional_PROJECTS}")
 +      MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_LIBRARIES ${${name}_test_additional_LIBRARIES}")
 +    ENDIF(LL_TEST_VERBOSE)
 +    # Add to project
 +    TARGET_LINK_LIBRARIES(PROJECT_${project}_TEST_${name} ${alltest_LIBRARIES} ${alltest_DEP_TARGETS} ${${name}_test_additional_PROJECTS} ${${name}_test_additional_LIBRARIES} )
 +    # Compile-time Definitions
 +    GET_SOURCE_FILE_PROPERTY(${name}_test_additional_CFLAGS ${source} LL_TEST_ADDITIONAL_CFLAGS)
 +     IF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND)
 +       SET_TARGET_PROPERTIES(PROJECT_${project}_TEST_${name} PROPERTIES COMPILE_FLAGS ${${name}_test_additional_CFLAGS} )
 +       IF(LL_TEST_VERBOSE)
 +         MESSAGE("LL_ADD_PROJECT_UNIT_TESTS ${name}_test_additional_CFLAGS ${${name}_test_additional_CFLAGS}")
 +       ENDIF(LL_TEST_VERBOSE)
 +     ENDIF(NOT ${name}_test_additional_CFLAGS MATCHES NOTFOUND)
 +     
 +    #
 +    # Setup test targets
 +    #
 +    GET_TARGET_PROPERTY(TEST_EXE PROJECT_${project}_TEST_${name} LOCATION)
 +    SET(TEST_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/PROJECT_${project}_TEST_${name}_ok.txt)
 +    SET(TEST_CMD ${TEST_EXE} --touch=${TEST_OUTPUT} --sourcedir=${CMAKE_CURRENT_SOURCE_DIR})
 +
 +    # daveh - what configuration does this use? Debug? it's cmake-time, not build time. + poppy 2009-04-19
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_cmd  = ${TEST_CMD}")
 +    ENDIF(LL_TEST_VERBOSE)
 +
 +    SET_TEST_PATH(LD_LIBRARY_PATH)
 +    LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${TEST_CMD})
 +    IF(LL_TEST_VERBOSE)
 +      MESSAGE(STATUS "LL_ADD_PROJECT_UNIT_TESTS ${name} test_script  = ${TEST_SCRIPT_CMD}")
 +    ENDIF(LL_TEST_VERBOSE)
 +    # Add test 
 +    ADD_CUSTOM_COMMAND(
 +        OUTPUT ${TEST_OUTPUT}
 +        COMMAND ${TEST_SCRIPT_CMD}
 +        DEPENDS PROJECT_${project}_TEST_${name}
 +        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
 +        )
 +    # Why not add custom target and add POST_BUILD command?
 +    # Slightly less uncertain behavior
 +    # (OUTPUT commands run non-deterministically AFAIK) + poppy 2009-04-19
 +    # > I did not use a post build step as I could not make it notify of a 
 +    # > failure after the first time you build and fail a test. - daveh 2009-04-20
 +    LIST(APPEND ${project}_TEST_OUTPUT ${TEST_OUTPUT})
 +  ENDFOREACH (source)
 +
 +  # Add the test runner target per-project
 +  # (replaces old _test_ok targets all over the place)
 +  ADD_CUSTOM_TARGET(${project}_tests ALL DEPENDS ${${project}_TEST_OUTPUT})
 +  ADD_DEPENDENCIES(${project} ${project}_tests)
 +ENDMACRO(LL_ADD_PROJECT_UNIT_TESTS)
 +
 +FUNCTION(LL_ADD_INTEGRATION_TEST 
 +    testname
 +    additional_source_files
 +    library_dependencies
 +# variable args
 +    )
 +  if(TEST_DEBUG)
 +    message(STATUS "Adding INTEGRATION_TEST_${testname} - debug output is on")
 +  endif(TEST_DEBUG)
 +  
 +  SET(source_files
 +    tests/${testname}_test.cpp
 +    ${CMAKE_SOURCE_DIR}/test/test.cpp
 +    ${CMAKE_SOURCE_DIR}/test/lltut.cpp
 +    ${additional_source_files}
 +    )
 +
 +  SET(libraries
 +    ${library_dependencies}
 +    ${GOOGLEMOCK_LIBRARIES}
 +    ${PTHREAD_LIBRARY}
 +    )
 +
 +  # Add test executable build target
 +  if(TEST_DEBUG)
 +    message(STATUS "ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})")
 +  endif(TEST_DEBUG)
 +  ADD_EXECUTABLE(INTEGRATION_TEST_${testname} ${source_files})
 +  SET_TARGET_PROPERTIES(INTEGRATION_TEST_${testname} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}")
 +
 +  # Add link deps to the executable
 +  if(TEST_DEBUG)
 +    message(STATUS "TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})")
 +  endif(TEST_DEBUG)
 +  TARGET_LINK_LIBRARIES(INTEGRATION_TEST_${testname} ${libraries})
 +
 +  # Create the test running command
 +  SET(test_command ${ARGN})
 +  GET_TARGET_PROPERTY(TEST_EXE INTEGRATION_TEST_${testname} LOCATION)
 +  LIST(FIND test_command "{}" test_exe_pos)
 +  IF(test_exe_pos LESS 0)
 +    # The {} marker means "the full pathname of the test executable."
 +    # test_exe_pos -1 means we didn't find it -- so append the test executable
 +    # name to $ARGN, the variable part of the arg list. This is convenient
 +    # shorthand for both straightforward execution of the test program (empty
 +    # $ARGN) and for running a "wrapper" program of some kind accepting the
 +    # pathname of the test program as the last of its args. You need specify
 +    # {} only if the test program's pathname isn't the last argument in the
 +    # desired command line.
 +    LIST(APPEND test_command "${TEST_EXE}")
 +  ELSE (test_exe_pos LESS 0)
 +    # Found {} marker at test_exe_pos. Remove the {}...
 +    LIST(REMOVE_AT test_command test_exe_pos)
 +    # ...and replace it with the actual name of the test executable.
 +    LIST(INSERT test_command test_exe_pos "${TEST_EXE}")
 +  ENDIF (test_exe_pos LESS 0)
 +
 +  SET_TEST_PATH(LD_LIBRARY_PATH)
 +  LL_TEST_COMMAND(TEST_SCRIPT_CMD "${LD_LIBRARY_PATH}" ${test_command})
 +
 +  if(TEST_DEBUG)
 +    message(STATUS "TEST_SCRIPT_CMD: ${TEST_SCRIPT_CMD}")
 +  endif(TEST_DEBUG)
 +
 +  ADD_CUSTOM_COMMAND(
 +    TARGET INTEGRATION_TEST_${testname}
 +    POST_BUILD
 +    COMMAND ${TEST_SCRIPT_CMD}
 +    )
 +
 +  # Use CTEST? Not sure how to yet...
 +  # ADD_TEST(INTEGRATION_TEST_RUNNER_${testname} ${TEST_SCRIPT_CMD})
 +
 +ENDFUNCTION(LL_ADD_INTEGRATION_TEST)
 +
 +MACRO(SET_TEST_PATH LISTVAR)
 +  IF(WINDOWS)
 +    # We typically build/package only Release variants of third-party
 +    # libraries, so append the Release staging dir in case the library being
 +    # sought doesn't have a debug variant.
 +    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR} ${SHARED_LIB_STAGING_DIR}/Release)
 +  ELSEIF(DARWIN)
 +    # We typically build/package only Release variants of third-party
 +    # libraries, so append the Release staging dir in case the library being
 +    # sought doesn't have a debug variant.
 +    set(${LISTVAR} ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/Resources ${SHARED_LIB_STAGING_DIR}/Release/Resources /usr/lib)
 +  ELSE(WINDOWS)
 +    # Linux uses a single staging directory anyway.
 +    IF (STANDALONE)
 +      set(${LISTVAR} ${CMAKE_BINARY_DIR}/llcommon /usr/lib /usr/local/lib)
 +    ELSE (STANDALONE)
 +      set(${LISTVAR} ${SHARED_LIB_STAGING_DIR} /usr/lib)
 +    ENDIF (STANDALONE)
 +  ENDIF(WINDOWS)
 +ENDMACRO(SET_TEST_PATH)
 diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp index 23c34e52e7..d909516bf8 100644 --- a/indra/linux_updater/linux_updater.cpp +++ b/indra/linux_updater/linux_updater.cpp @@ -49,6 +49,7 @@ const guint ROTATE_IMAGE_TIMEOUT = 8000;  typedef struct _updater_app_state {  	std::string app_name;  	std::string url; +	std::string file;  	std::string image_dir;  	std::string dest_dir;  	std::string strings_dirs; @@ -113,7 +114,7 @@ BOOL install_package(std::string package_file, std::string destination);  BOOL spawn_viewer(UpdaterAppState *app_state);  extern "C" { -	void on_window_closed(GtkWidget *sender, gpointer state); +	void on_window_closed(GtkWidget *sender, GdkEvent *event, gpointer state);  	gpointer worker_thread_cb(gpointer *data);  	int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow);  	gboolean rotate_image_cb(gpointer data); @@ -220,7 +221,7 @@ std::string next_image_filename(std::string& image_path)  	return image_path + "/" + image_filename;  } -void on_window_closed(GtkWidget *sender, gpointer data) +void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data)  {  	UpdaterAppState *app_state; @@ -266,85 +267,95 @@ gpointer worker_thread_cb(gpointer data)  	CURLcode result;  	FILE *package_file;  	GError *error = NULL; -	char *tmp_filename = NULL;  	int fd;  	//g_return_val_if_fail (data != NULL, NULL);  	app_state = (UpdaterAppState *) data;  	try { -		// create temporary file to store the package. -		fd = g_file_open_tmp -			("secondlife-update-XXXXXX", &tmp_filename, &error); -		if (error != NULL) -		{ -			llerrs << "Unable to create temporary file: " -			       << error->message -			       << llendl; - -			g_error_free(error); -			throw 0; -		} -		package_file = fdopen(fd, "wb"); -		if (package_file == NULL) +		if(!app_state->url.empty())  		{ -			llerrs << "Failed to create temporary file: " -			       << tmp_filename -			       << llendl; +			char* tmp_local_filename = NULL; +			// create temporary file to store the package. +			fd = g_file_open_tmp +				("secondlife-update-XXXXXX", &tmp_local_filename, &error); +			if (error != NULL) +			{ +				llerrs << "Unable to create temporary file: " +					   << error->message +					   << llendl; -			gdk_threads_enter(); -			display_error(app_state->window, -				      LLTrans::getString("UpdaterFailDownloadTitle"), -				      LLTrans::getString("UpdaterFailUpdateDescriptive")); -			gdk_threads_leave(); -			throw 0; -		} +				g_error_free(error); +				throw 0; +			} +			 +			if(tmp_local_filename != NULL) +			{ +				app_state->file = tmp_local_filename; +				g_free(tmp_local_filename); +			} -		// initialize curl and start downloading the package -		llinfos << "Downloading package: " << app_state->url << llendl; +			package_file = fdopen(fd, "wb"); +			if (package_file == NULL) +			{ +				llerrs << "Failed to create temporary file: " +					   << app_state->file.c_str() +					   << llendl; + +				gdk_threads_enter(); +				display_error(app_state->window, +							  LLTrans::getString("UpdaterFailDownloadTitle"), +							  LLTrans::getString("UpdaterFailUpdateDescriptive")); +				gdk_threads_leave(); +				throw 0; +			} -		curl = curl_easy_init(); -		if (curl == NULL) -		{ -			llerrs << "Failed to initialize libcurl" << llendl; +			// initialize curl and start downloading the package +			llinfos << "Downloading package: " << app_state->url << llendl; -			gdk_threads_enter(); -			display_error(app_state->window, -				      LLTrans::getString("UpdaterFailDownloadTitle"), -				      LLTrans::getString("UpdaterFailUpdateDescriptive")); -			gdk_threads_leave(); -			throw 0; -		} +			curl = curl_easy_init(); +			if (curl == NULL) +			{ +				llerrs << "Failed to initialize libcurl" << llendl; + +				gdk_threads_enter(); +				display_error(app_state->window, +							  LLTrans::getString("UpdaterFailDownloadTitle"), +							  LLTrans::getString("UpdaterFailUpdateDescriptive")); +				gdk_threads_leave(); +				throw 0; +			} -		curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str()); -		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE); -		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE); -		curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file); -		curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); -		curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,  -				 &download_progress_cb); -		curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state); +			curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str()); +			curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE); +			curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE); +			curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file); +			curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); +			curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,  +							 &download_progress_cb); +			curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state); -		result = curl_easy_perform(curl); -		fclose(package_file); -		curl_easy_cleanup(curl); +			result = curl_easy_perform(curl); +			fclose(package_file); +			curl_easy_cleanup(curl); -		if (result) -		{ -			llerrs << "Failed to download update: "  -			       << app_state->url  -			       << llendl; +			if (result) +			{ +				llerrs << "Failed to download update: "  +					   << app_state->url  +					   << llendl; -			gdk_threads_enter(); -			display_error(app_state->window, -				      LLTrans::getString("UpdaterFailDownloadTitle"), -				      LLTrans::getString("UpdaterFailUpdateDescriptive")); -			gdk_threads_leave(); +				gdk_threads_enter(); +				display_error(app_state->window, +							  LLTrans::getString("UpdaterFailDownloadTitle"), +							  LLTrans::getString("UpdaterFailUpdateDescriptive")); +				gdk_threads_leave(); -			throw 0; +				throw 0; +			}  		} - +		  		// now pulse the progres bar back and forth while the package is  		// being unpacked  		gdk_threads_enter(); @@ -357,7 +368,7 @@ gpointer worker_thread_cb(gpointer data)  		// *TODO: if the destination is not writable, terminate this  		// thread and show file chooser? -		if (!install_package(tmp_filename, app_state->dest_dir)) +		if (!install_package(app_state->file.c_str(), app_state->dest_dir))  		{  			llwarns << "Failed to install package to destination: "  				<< app_state->dest_dir @@ -392,15 +403,6 @@ gpointer worker_thread_cb(gpointer data)  		app_state->failure = TRUE;  	} -	// FIXME: delete package file also if delete-event is raised on window -	if (tmp_filename != NULL) -	{ -		if (gDirUtilp->fileExists(tmp_filename)) -		{ -			LLFile::remove(tmp_filename); -		} -	} -  	gdk_threads_enter();  	updater_app_quit(app_state);  	gdk_threads_leave(); @@ -712,7 +714,7 @@ BOOL spawn_viewer(UpdaterAppState *app_state)  void show_usage_and_exit()  { -	std::cout << "Usage: linux-updater --url URL --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE" +	std::cout << "Usage: linux-updater <--url URL | --file FILE> --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"  		  << "[--image-dir PATH]"  		  << std::endl;  	exit(1); @@ -728,6 +730,10 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)  		{  			app_state->url = argv[i];  		} +		else if ((!strcmp(argv[i], "--file")) && (++i < argc)) +		{ +			app_state->file = argv[i]; +		}  		else if ((!strcmp(argv[i], "--name")) && (++i < argc))  		{  			app_state->app_name = argv[i]; @@ -756,7 +762,7 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)  	}  	if (app_state->app_name.empty()  -	    || app_state->url.empty()  +	    || (app_state->url.empty() && app_state->file.empty())    	    || app_state->dest_dir.empty())  	{  		show_usage_and_exit(); @@ -771,10 +777,10 @@ void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)  int main(int argc, char **argv)  { -	UpdaterAppState app_state; +	UpdaterAppState* app_state = new UpdaterAppState;  	GThread *worker_thread; -	parse_args_and_init(argc, argv, &app_state); +	parse_args_and_init(argc, argv, app_state);  	// Initialize logger, and rename old log file  	gDirUtilp->initAppDirs("SecondLife"); @@ -797,17 +803,29 @@ int main(int argc, char **argv)  	gtk_init(&argc, &argv);  	// create UI -	updater_app_ui_init(&app_state); +	updater_app_ui_init(app_state);  	//llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl;  	// create download thread  	worker_thread = g_thread_create -		(GThreadFunc(worker_thread_cb), &app_state, FALSE, NULL); +		(GThreadFunc(worker_thread_cb), app_state, FALSE, NULL);  	gdk_threads_enter();  	gtk_main();  	gdk_threads_leave(); -	return (app_state.failure == FALSE) ? 0 : 1; +	// Delete the file only if created from url download. +	if(!app_state->url.empty() && !app_state->file.empty()) +	{ +		if (gDirUtilp->fileExists(app_state->file)) +		{ +			LLFile::remove(app_state->file); +		} +	} + +	bool success = !app_state->failure; +	delete app_state; +	return success ? 0 : 1;  } + diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 478f2fedbd..9342a22d46 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -93,6 +93,7 @@ set(llcommon_SOURCE_FILES      llstringtable.cpp      llsys.cpp      llthread.cpp +    llthreadsafequeue.cpp      lltimer.cpp      lluri.cpp      lluuid.cpp @@ -225,6 +226,7 @@ set(llcommon_HEADER_FILES      llstringtable.h      llsys.h      llthread.h +    llthreadsafequeue.h      lltimer.h      lltreeiterators.h      lluri.h diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 289ce0bc2c..8f02391e75 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -318,7 +318,12 @@ void llofstream::close()  	if(is_open())  	{  		if (_Filebuffer->close() == 0) +		{  			_Myios::setstate(ios_base::failbit);	/*Flawfinder: ignore*/ +		} +		delete _Filebuffer; +		_Filebuffer = NULL; +		_ShouldClose = false;  	}  } diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index d7b7c3699c..148aaf8aed 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -147,16 +147,20 @@ void LLThread::shutdown()  		{  			// This thread just wouldn't stop, even though we gave it time  			llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; +			// Put a stake in its heart. +			apr_thread_exit(mAPRThreadp, -1);  			return;  		}  		mAPRThreadp = NULL;  	}  	delete mRunCondition; +	mRunCondition = 0; -	if (mIsLocalPool) +	if (mIsLocalPool && mAPRPoolp)  	{  		apr_pool_destroy(mAPRPoolp); +		mAPRPoolp = 0;  	}  } diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp new file mode 100644 index 0000000000..8a73e632a9 --- /dev/null +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -0,0 +1,109 @@ +/**  + * @file llthread.cpp + * + * $LicenseInfo:firstyear=2004&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 <apr_pools.h> +#include <apr_queue.h> +#include "llthreadsafequeue.h" + + + +// LLThreadSafeQueueImplementation +//----------------------------------------------------------------------------- + + +LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity): +	mOwnsPool(pool == 0), +	mPool(pool), +	mQueue(0) +{ +	if(mOwnsPool) { +		apr_status_t status = apr_pool_create(&mPool, 0); +		if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool"); +	} else { +		; // No op. +	} +	 +	apr_status_t status = apr_queue_create(&mQueue, capacity, mPool); +	if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue"); +} + + +LLThreadSafeQueueImplementation::~LLThreadSafeQueueImplementation() +{ +	if(mQueue != 0) { +		if(apr_queue_size(mQueue) != 0) llwarns <<  +			"terminating queue which still contains " << apr_queue_size(mQueue) << +			" elements;" << "memory will be leaked" << LL_ENDL; +		apr_queue_term(mQueue); +	} +	if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool); +} + + +void LLThreadSafeQueueImplementation::pushFront(void * element) +{ +	apr_status_t status = apr_queue_push(mQueue, element); +	 +	if(status == APR_EINTR) { +		throw LLThreadSafeQueueInterrupt(); +	} else if(status != APR_SUCCESS) { +		throw LLThreadSafeQueueError("push failed"); +	} else { +		; // Success. +	} +} + + +bool LLThreadSafeQueueImplementation::tryPushFront(void * element){ +	return apr_queue_trypush(mQueue, element) == APR_SUCCESS; +} + + +void * LLThreadSafeQueueImplementation::popBack(void) +{ +	void * element; +	apr_status_t status = apr_queue_pop(mQueue, &element); + +	if(status == APR_EINTR) { +		throw LLThreadSafeQueueInterrupt(); +	} else if(status != APR_SUCCESS) { +		throw LLThreadSafeQueueError("pop failed"); +	} else { +		return element; +	} +} + + +bool LLThreadSafeQueueImplementation::tryPopBack(void *& element) +{ +	return apr_queue_trypop(mQueue, &element) == APR_SUCCESS; +} + + +size_t LLThreadSafeQueueImplementation::size() +{ +	return apr_queue_size(mQueue); +} diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h new file mode 100644 index 0000000000..58cac38769 --- /dev/null +++ b/indra/llcommon/llthreadsafequeue.h @@ -0,0 +1,205 @@ +/**  + * @file llthreadsafequeue.h + * @brief Base classes for thread, mutex and condition handling. + * + * $LicenseInfo:firstyear=2004&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_LLTHREADSAFEQUEUE_H +#define LL_LLTHREADSAFEQUEUE_H + + +#include <string> +#include <stdexcept> + + +struct apr_pool_t; // From apr_pools.h +class LLThreadSafeQueueImplementation; // See below. + + +// +// A general queue exception. +// +class LL_COMMON_API LLThreadSafeQueueError: +public std::runtime_error +{ +public: +	LLThreadSafeQueueError(std::string const & message): +	std::runtime_error(message) +	{ +		; // No op. +	} +}; + + +// +// An exception raised when blocking operations are interrupted. +// +class LL_COMMON_API LLThreadSafeQueueInterrupt: +	public LLThreadSafeQueueError +{ +public: +	LLThreadSafeQueueInterrupt(void): +		LLThreadSafeQueueError("queue operation interrupted") +	{ +		; // No op. +	} +}; + + +struct apr_queue_t; // From apr_queue.h + + +// +// Implementation details.  +// +class LL_COMMON_API LLThreadSafeQueueImplementation +{ +public: +	LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity); +	~LLThreadSafeQueueImplementation(); +	void pushFront(void * element); +	bool tryPushFront(void * element); +	void * popBack(void); +	bool tryPopBack(void *& element); +	size_t size(); +	 +private: +	bool mOwnsPool; +	apr_pool_t * mPool; +	apr_queue_t * mQueue; +}; + + +// +// Implements a thread safe FIFO. +// +template<typename ElementT> +class LLThreadSafeQueue +{ +public: +	typedef ElementT value_type; +	 +	// If the pool is set to NULL one will be allocated and managed by this +	// queue. +	LLThreadSafeQueue(apr_pool_t * pool = 0, unsigned int capacity = 1024); +	 +	// Add an element to the front of queue (will block if the queue has +	// reached capacity). +	// +	// This call will raise an interrupt error if the queue is deleted while +	// the caller is blocked. +	void pushFront(ElementT const & element); +	 +	// Try to add an element to the front ofqueue without blocking. Returns +	// true only if the element was actually added. +	bool tryPushFront(ElementT const & element); +	 +	// Pop the element at the end of the queue (will block if the queue is +	// empty). +	// +	// This call will raise an interrupt error if the queue is deleted while +	// the caller is blocked. +	ElementT popBack(void); +	 +	// Pop an element from the end of the queue if there is one available. +	// Returns true only if an element was popped. +	bool tryPopBack(ElementT & element); +	 +	// Returns the size of the queue. +	size_t size(); + +private: +	LLThreadSafeQueueImplementation mImplementation; +}; + + + +// LLThreadSafeQueue +//----------------------------------------------------------------------------- + + +template<typename ElementT> +LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity): +	mImplementation(pool, capacity) +{ +	; // No op. +} + + +template<typename ElementT> +void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element) +{ +	ElementT * elementCopy = new ElementT(element); +	try { +		mImplementation.pushFront(elementCopy); +	} catch (LLThreadSafeQueueInterrupt) { +		delete elementCopy; +		throw; +	} +} + + +template<typename ElementT> +bool LLThreadSafeQueue<ElementT>::tryPushFront(ElementT const & element) +{ +	ElementT * elementCopy = new ElementT(element); +	bool result = mImplementation.tryPushFront(elementCopy); +	if(!result) delete elementCopy; +	return result; +} + + +template<typename ElementT> +ElementT LLThreadSafeQueue<ElementT>::popBack(void) +{ +	ElementT * element = reinterpret_cast<ElementT *> (mImplementation.popBack()); +	ElementT result(*element); +	delete element; +	return result; +} + + +template<typename ElementT> +bool LLThreadSafeQueue<ElementT>::tryPopBack(ElementT & element) +{ +	void * storedElement; +	bool result = mImplementation.tryPopBack(storedElement); +	if(result) { +		ElementT * elementPtr = reinterpret_cast<ElementT *>(storedElement);  +		element = *elementPtr; +		delete elementPtr; +	} else { +		; // No op. +	} +	return result; +} + + +template<typename ElementT> +size_t LLThreadSafeQueue<ElementT>::size(void) +{ +	return mImplementation.size(); +} + + +#endif diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index ff9af21e54..19c42bf61a 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1620,7 +1620,10 @@ void LLUI::initClass(const settings_map_t& settings,  void LLUI::cleanupClass()  { -	sImageProvider->cleanUp(); +	if(sImageProvider) +	{ +		sImageProvider->cleanUp(); +	}  }  void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup,  const clear_popups_t& clear_popups) diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index f9a39826f5..27c694dde9 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -1107,7 +1107,7 @@ bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::strin  		return sd.asBoolean();  	else  	{ -		CONTROL_ERRS << "Invalid BOOL value" << llendl; +		CONTROL_ERRS << "Invalid BOOL value for " << control_name << ": " << sd << llendl;  		return FALSE;  	}  } @@ -1119,7 +1119,7 @@ S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string&  		return sd.asInteger();  	else  	{ -		CONTROL_ERRS << "Invalid S32 value" << llendl; +		CONTROL_ERRS << "Invalid S32 value for " << control_name << ": " << sd << llendl;  		return 0;  	}  } @@ -1131,7 +1131,7 @@ U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string&  		return sd.asInteger();  	else  	{ -		CONTROL_ERRS << "Invalid U32 value" << llendl; +		CONTROL_ERRS << "Invalid U32 value for " << control_name << ": " << sd << llendl;  		return 0;  	}  } @@ -1143,7 +1143,7 @@ F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string&  		return (F32) sd.asReal();  	else  	{ -		CONTROL_ERRS << "Invalid F32 value" << llendl; +		CONTROL_ERRS << "Invalid F32 value for " << control_name << ": " << sd << llendl;  		return 0.0f;  	}  } @@ -1155,7 +1155,7 @@ std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, co  		return sd.asString();  	else  	{ -		CONTROL_ERRS << "Invalid string value" << llendl; +		CONTROL_ERRS << "Invalid string value for " << control_name << ": " << sd << llendl;  		return LLStringUtil::null;  	}  } @@ -1173,7 +1173,7 @@ LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const  		return (LLVector3)sd;  	else  	{ -		CONTROL_ERRS << "Invalid LLVector3 value" << llendl; +		CONTROL_ERRS << "Invalid LLVector3 value for " << control_name << ": " << sd << llendl;  		return LLVector3::zero;  	}  } @@ -1185,7 +1185,7 @@ LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, cons  		return (LLVector3d)sd;  	else  	{ -		CONTROL_ERRS << "Invalid LLVector3d value" << llendl; +		CONTROL_ERRS << "Invalid LLVector3d value for " << control_name << ": " << sd << llendl;  		return LLVector3d::zero;  	}  } @@ -1197,7 +1197,7 @@ LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::s  		return LLRect(sd);  	else  	{ -		CONTROL_ERRS << "Invalid rect value" << llendl; +		CONTROL_ERRS << "Invalid rect value for " << control_name << ": " << sd << llendl;  		return LLRect::null;  	}  } @@ -1211,19 +1211,19 @@ LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const st  		LLColor4 color(sd);  		if (color.mV[VRED] < 0.f || color.mV[VRED] > 1.f)  		{ -			llwarns << "Color " << control_name << " value out of range " << llendl; +			llwarns << "Color " << control_name << " red value out of range: " << color << llendl;  		}  		else if (color.mV[VGREEN] < 0.f || color.mV[VGREEN] > 1.f)  		{ -			llwarns << "Color " << control_name << " value out of range " << llendl; +			llwarns << "Color " << control_name << " green value out of range: " << color << llendl;  		}  		else if (color.mV[VBLUE] < 0.f || color.mV[VBLUE] > 1.f)  		{ -			llwarns << "Color " << control_name << " value out of range " << llendl; +			llwarns << "Color " << control_name << " blue value out of range: " << color << llendl;  		}  		else if (color.mV[VALPHA] < 0.f || color.mV[VALPHA] > 1.f)  		{ -			llwarns << "Color " << control_name << " value out of range " << llendl; +			llwarns << "Color " << control_name << " alpha value out of range: " << color << llendl;  		}  		return LLColor4(sd); @@ -1242,7 +1242,7 @@ LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const st  		return sd;  	else  	{ -		CONTROL_ERRS << "Invalid LLColor3 value" << llendl; +		CONTROL_ERRS << "Invalid LLColor3 value for " << control_name << ": " << sd << llendl;  		return LLColor3::white;  	}  } diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp index 23980ffac2..5d19e8a889 100644 --- a/indra/mac_updater/mac_updater.cpp +++ b/indra/mac_updater/mac_updater.cpp @@ -26,6 +26,9 @@  #include "linden_common.h" +#include <boost/format.hpp> + +#include <libgen.h>  #include <sys/types.h>  #include <sys/stat.h>  #include <unistd.h> @@ -62,6 +65,8 @@ Boolean gCancelled = false;  const char *gUpdateURL;  const char *gProductName;  const char *gBundleID; +const char *gDmgFile; +const char *gMarkerPath;  void *updatethreadproc(void*); @@ -334,6 +339,14 @@ int parse_args(int argc, char **argv)  		{  			gBundleID = argv[j];  		} +		else if ((!strcmp(argv[j], "-dmg")) && (++j < argc))  +		{ +			gDmgFile = argv[j]; +		} +		else if ((!strcmp(argv[j], "-marker")) && (++j < argc))  +		{ +			gMarkerPath = argv[j];; +		}  	}  	return 0; @@ -361,10 +374,12 @@ int main(int argc, char **argv)  	gUpdateURL  = NULL;  	gProductName = NULL;  	gBundleID = NULL; +	gDmgFile = NULL; +	gMarkerPath = NULL;  	parse_args(argc, argv); -	if (!gUpdateURL) +	if ((gUpdateURL == NULL) && (gDmgFile == NULL))  	{ -		llinfos << "Usage: mac_updater -url <url> [-name <product_name>] [-program <program_name>]" << llendl; +		llinfos << "Usage: mac_updater -url <url> | -dmg <dmg file> [-name <product_name>] [-program <program_name>]" << llendl;  		exit(1);  	}  	else @@ -488,11 +503,18 @@ int main(int argc, char **argv)  					NULL,  					&retval_mac);  		} - +		 +		if(gMarkerPath != 0) +		{ +			// Create a install fail marker that can be used by the viewer to +			// detect install problems. +			std::ofstream stream(gMarkerPath); +			if(stream) stream << -1; +		} +		exit(-1); +	} else { +		exit(0);  	} -	 -	// Don't dispose of things, just exit.  This keeps the update thread from potentially getting hosed. -	exit(0);  	if(gWindow != NULL)  	{ @@ -700,17 +722,26 @@ static OSErr findAppBundleOnDiskImage(FSRef *parent, FSRef *app)  						// Looks promising.  Check to see if it has the right bundle identifier.  						if(isFSRefViewerBundle(&ref))  						{ +							llinfos << name << " is the one" << llendl;  							// This is the one.  Return it.  							*app = ref;  							found = true; +							break; +						} else { +							llinfos << name << " is not the bundle we are looking for; move along" << llendl;  						} +  					}  				}  			}  		} -		while(!err && !found); +		while(!err); +		 +		llinfos << "closing the iterator" << llendl;  		FSCloseIterator(iterator); +		 +		llinfos << "closed" << llendl;  	}  	if(!err && !found) @@ -921,6 +952,22 @@ void *updatethreadproc(void*)  #endif // 0 *HACK for DEV-11935 +		// Skip downloading the file if the dmg was passed on the command line. +		std::string dmgName; +		if(gDmgFile != NULL) { +			dmgName = basename((char *)gDmgFile); +			char * dmgDir = dirname((char *)gDmgFile); +			strncpy(tempDir, dmgDir, sizeof(tempDir)); +			err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); +			if(err != noErr) throw 0; +			chdir(tempDir); +			goto begin_install; +		} else { +			// Continue on to download file. +			dmgName = "SecondLife.dmg"; +		} + +		  		strncat(temp, "/SecondLifeUpdate_XXXXXX", (sizeof(temp) - strlen(temp)) - 1);  		if(mkdtemp(temp) == NULL)  		{ @@ -979,14 +1026,17 @@ void *updatethreadproc(void*)  			fclose(downloadFile);  			downloadFile = NULL;  		} -		 + +	begin_install:  		sendProgress(0, 0, CFSTR("Mounting image..."));  		LLFile::mkdir("mnt", 0700);  		// NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,  		//		but if our cleanup fails, this makes it much harder for the user to unmount the image.  		std::string mountOutput; -		FILE* mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");		/* Flawfinder: ignore */ +		boost::format cmdFormat("hdiutil attach %s -mountpoint mnt"); +		cmdFormat % dmgName; +		FILE* mounter = popen(cmdFormat.str().c_str(), "r");		/* Flawfinder: ignore */  		if(mounter == NULL)  		{ @@ -1052,12 +1102,19 @@ void *updatethreadproc(void*)  			throw 0;  		} +		sendProgress(0, 0, CFSTR("Searching for the app bundle..."));  		err = findAppBundleOnDiskImage(&mountRef, &sourceRef);  		if(err != noErr)  		{  			llinfos << "Couldn't find application bundle on mounted disk image." << llendl;  			throw 0;  		} +		else +		{ +			llinfos << "found the bundle." << llendl; +		} + +		sendProgress(0, 0, CFSTR("Preparing to copy files..."));  		FSRef asideRef;  		char aside[MAX_PATH];		/* Flawfinder: ignore */ @@ -1077,7 +1134,11 @@ void *updatethreadproc(void*)  			// Move aside old version (into work directory)  			err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);  			if(err != noErr) +			{ +				llwarns << "failed to move aside old version (error code " <<  +					err << ")" << llendl;  				throw 0; +			}  			// Grab the path for later use.  			err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside)); @@ -1175,6 +1236,10 @@ void *updatethreadproc(void*)  		llinfos << "Moving work directory to the trash." << llendl;  		err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL); +		if(err != noErr) { +			llwarns << "failed to move files to trash, (error code " << +				err << ")" << llendl; +		}  //		snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);  //		printf("%s\n", temp); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 3a4b9be0d7..5d6853a526 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -64,6 +64,7 @@ include_directories(      ${LSCRIPT_INCLUDE_DIRS}      ${LSCRIPT_INCLUDE_DIRS}/lscript_compile      ${LLLOGIN_INCLUDE_DIRS} +    ${UPDATER_INCLUDE_DIRS}      )  set(viewer_SOURCE_FILES @@ -284,6 +285,7 @@ set(viewer_SOURCE_FILES      llloginhandler.cpp      lllogininstance.cpp      llmachineid.cpp +    llmainlooprepeater.cpp      llmanip.cpp      llmaniprotate.cpp      llmanipscale.cpp @@ -817,6 +819,7 @@ set(viewer_HEADER_FILES      llloginhandler.h      lllogininstance.h      llmachineid.h +    llmainlooprepeater.h      llmanip.h      llmaniprotate.h      llmanipscale.h @@ -1644,7 +1647,14 @@ if (WINDOWS)      endif (PACKAGE)  endif (WINDOWS) +# *NOTE - this list is very sensitive to ordering, test carefully on all +# platforms if you change the releative order of the entries here. +# In particular, cmake 2.6.4 (when buidling with linux/makefile generators) +# appears to sometimes de-duplicate redundantly listed dependencies improperly. +# To work around this, higher level modules should be listed before the modules +# that they depend upon. -brad  target_link_libraries(${VIEWER_BINARY_NAME} +    ${UPDATER_LIBRARIES}      ${LLAUDIO_LIBRARIES}      ${LLCHARACTER_LIBRARIES}      ${LLIMAGE_LIBRARIES} @@ -1831,10 +1841,18 @@ if (PACKAGE)      set(VIEWER_COPY_MANIFEST copy_l_viewer_manifest)    endif (LINUX) +  if(CMAKE_CFG_INTDIR STREQUAL ".") +      set(LLBUILD_CONFIG ${CMAKE_BUILD_TYPE}) +  else(CMAKE_CFG_INTDIR STREQUAL ".") +      # set LLBUILD_CONFIG to be a shell variable evaluated at build time +      # reflecting the configuration we are currently building. +      set(LLBUILD_CONFIG ${CMAKE_CFG_INTDIR}) +  endif(CMAKE_CFG_INTDIR STREQUAL ".")    add_custom_command(OUTPUT "${VIEWER_SYMBOL_FILE}"      COMMAND "${PYTHON_EXECUTABLE}"      ARGS        "${CMAKE_CURRENT_SOURCE_DIR}/generate_breakpad_symbols.py" +      "${LLBUILD_CONFIG}"        "${VIEWER_DIST_DIR}"        "${VIEWER_EXE_GLOBS}"        "${VIEWER_LIB_GLOB}" @@ -1843,7 +1861,7 @@ if (PACKAGE)      DEPENDS generate_breakpad_symbols.py      VERBATIM    ) -  add_custom_target(generate_breakpad_symbols ALL DEPENDS "${VIEWER_SYMBOL_FILE}") +  add_custom_target(generate_breakpad_symbols DEPENDS "${VIEWER_SYMBOL_FILE}")    add_dependencies(generate_breakpad_symbols "${VIEWER_BINARY_NAME}" "${VIEWER_COPY_MANIFEST}")    add_dependencies(package generate_breakpad_symbols)  endif (PACKAGE) @@ -1858,6 +1876,7 @@ if (LL_TESTS)      llmediadataclient.cpp      lllogininstance.cpp      llviewerhelputil.cpp +    llversioninfo.cpp    )    ################################################## diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index 0562cf5480..1b8393330d 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -363,8 +363,7 @@      <map>        <key>count</key>        <integer>1</integer> -      <key>map-to</key> -      <string>VersionChannelName</string> +      <!-- Special case. Not mapped to a setting. -->      </map>      <key>loginpage</key> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index aad2fe3276..75bf781fec 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -11079,7 +11079,62 @@        <key>Value</key>        <integer>15</integer>      </map> -	<key>UploadBakedTexOld</key> +    <key>UpdaterServiceActive</key> +    <map> +      <key>Comment</key> +      <string>Enable or disable the updater service.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>1</integer> +    </map> +    <key>UpdaterServiceCheckPeriod</key> +    <map> +      <key>Comment</key> +      <string>Default period between update checking.</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>U32</string> +      <key>Value</key> +      <integer>3600</integer> +    </map> +    <key>UpdaterServiceURL</key> +    <map> +      <key>Comment</key> +      <string>Default location for the updater service.</string> +      <key>Persist</key> +      <integer>0</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>http://update.secondlife.com</string> +    </map> +    <key>UpdaterServicePath</key> +    <map> +      <key>Comment</key> +      <string>Path on the update server host.</string> +      <key>Persist</key> +      <integer>0</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>update</string> +    </map> +    <key>UpdaterServiceProtocolVersion</key> +    <map> +      <key>Comment</key> +      <string>The update protocol version to use.</string> +      <key>Persist</key> +      <integer>0</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>v1.0</string> +    </map> +    <key>UploadBakedTexOld</key>      <map>        <key>Comment</key>        <string>Forces the baked texture pipeline to upload using the old method.</string> @@ -11432,17 +11487,6 @@        <key>Value</key>        <integer>0</integer>      </map> -    <key>VersionChannelName</key> -    <map> -      <key>Comment</key> -      <string>Versioning Channel Name.</string> -      <key>Persist</key> -      <integer>0</integer> -      <key>Type</key> -      <string>String</string> -      <key>Value</key> -      <string>Second Life Release</string> -    </map>      <key>VertexShaderEnable</key>      <map>        <key>Comment</key> diff --git a/indra/newview/generate_breakpad_symbols.py b/indra/newview/generate_breakpad_symbols.py index 8f2dfd2348..4fd04d780e 100644 --- a/indra/newview/generate_breakpad_symbols.py +++ b/indra/newview/generate_breakpad_symbols.py @@ -31,6 +31,7 @@ import fnmatch  import itertools  import operator  import os +import re  import sys  import shlex  import subprocess @@ -45,8 +46,12 @@ class MissingModuleError(Exception):          Exception.__init__(self, "Failed to find required modules: %r" % modules)          self.modules = modules -def main(viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file): -    print "generate_breakpad_symbols run with args: %s" % str((viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file)) +def main(configuration, viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file): +    print "generate_breakpad_symbols run with args: %s" % str((configuration, viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_file)) + +    if not re.match("release", configuration, re.IGNORECASE): +        print "skipping breakpad symbol generation for non-release build." +        return 0      # split up list of viewer_exes      # "'Second Life' SLPlugin" becomes ['Second Life', 'SLPlugin'] @@ -122,7 +127,7 @@ def main(viewer_dir, viewer_exes, libs_suffix, dump_syms_tool, viewer_symbol_fil      return 0  if __name__ == "__main__": -    if len(sys.argv) != 6: +    if len(sys.argv) != 7:          usage()          sys.exit(1)      sys.exit(main(*sys.argv[1:])) diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index d5712f80cf..4e8ed807ee 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -85,6 +85,8 @@ AutoCloseWindow true					; after all files install, close window  InstallDir "$PROGRAMFILES\${INSTNAME}"  InstallDirRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" ""  DirText $(DirectoryChooseTitle) $(DirectoryChooseSetup) +Page directory dirPre +Page instfiles  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  ;;; Variables @@ -95,6 +97,8 @@ Var INSTFLAGS  Var INSTSHORTCUT  Var COMMANDLINE         ; command line passed to this installer, set in .onInit  Var SHORTCUT_LANG_PARAM ; "--set InstallLanguage de", passes language to viewer +Var SKIP_DIALOGS        ; set from command line in  .onInit. autoinstall  +                        ; GUI and the defaults.  ;;; Function definitions should go before file includes, because calls to  ;;; DLLs like LangDLL trigger an implicit file include, so if that call is at @@ -110,6 +114,9 @@ Var SHORTCUT_LANG_PARAM ; "--set InstallLanguage de", passes language to viewer  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  Function .onInstSuccess      Push $R0	# Option value, unused + +    StrCmp $SKIP_DIALOGS "true" label_launch  +      ${GetOptions} $COMMANDLINE "/AUTOSTART" $R0      # If parameter was there (no error) just launch      # Otherwise ask @@ -128,6 +135,13 @@ label_no_launch:  	Pop $R0  FunctionEnd +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; Pre-directory page callback +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Function dirPre +    StrCmp $SKIP_DIALOGS "true" 0 +2 +	Abort +FunctionEnd      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  ; Make sure we're not on Windows 98 / ME @@ -145,7 +159,8 @@ Function CheckWindowsVersion  	StrCmp $R0 "NT" win_ver_bad  	Return  win_ver_bad: -	MessageBox MB_YESNO $(CheckWindowsVersionMB) IDNO win_ver_abort +	StrCmp $SKIP_DIALOGS "true" +2 ; If skip_dialogs is set just install +            MessageBox MB_YESNO $(CheckWindowsVersionMB) IDNO win_ver_abort  	Return  win_ver_abort:  	Quit @@ -184,13 +199,13 @@ FunctionEnd  ; If it has, allow user to bail out of install process.  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  Function CheckIfAlreadyCurrent -  Push $0 -	ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" -    StrCmp $0 ${VERSION_LONG} 0 DONE -	MessageBox MB_OKCANCEL $(CheckIfCurrentMB) /SD IDOK IDOK DONE +    Push $0 +    ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\$INSTPROG" "Version" +    StrCmp $0 ${VERSION_LONG} 0 continue_install +    StrCmp $SKIP_DIALOGS "true" continue_install +    MessageBox MB_OKCANCEL $(CheckIfCurrentMB) /SD IDOK IDOK continue_install      Quit - -  DONE: +continue_install:      Pop $0      Return  FunctionEnd @@ -203,7 +218,9 @@ Function CloseSecondLife    Push $0    FindWindow $0 "Second Life" ""    IntCmp $0 0 DONE -  MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL +   +  StrCmp $SKIP_DIALOGS "true" CLOSE +    MessageBox MB_OKCANCEL $(CloseSecondLifeInstMB) IDOK CLOSE IDCANCEL CANCEL_INSTALL    CANCEL_INSTALL:      Quit @@ -659,23 +676,29 @@ FunctionEnd  Function .onInit      Push $0      ${GetParameters} $COMMANDLINE              ; get our command line + +    ${GetOptions} $COMMANDLINE "/SKIP_DIALOGS" $0    +    IfErrors +2 0 ; If error jump past setting SKIP_DIALOGS +        StrCpy $SKIP_DIALOGS "true" +      ${GetOptions} $COMMANDLINE "/LANGID=" $0   ; /LANGID=1033 implies US English      ; If no language (error), then proceed -    IfErrors lbl_check_silent +    IfErrors lbl_configure_default_lang      ; No error means we got a language, so use it      StrCpy $LANGUAGE $0      Goto lbl_return -lbl_check_silent: -    ; For silent installs, no language prompt, use default -    IfSilent lbl_return -     -	; If we currently have a version of SL installed, default to the language of that install +lbl_configure_default_lang: +    ; If we currently have a version of SL installed, default to the language of that install      ; Otherwise don't change $LANGUAGE and it will default to the OS UI language. -	ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage" -    IfErrors lbl_build_menu +    ReadRegStr $0 HKEY_LOCAL_MACHINE "SOFTWARE\Linden Research, Inc.\${INSTNAME}" "InstallerLanguage" +    IfErrors +2 0 ; If error skip the copy instruction   	StrCpy $LANGUAGE $0 +    ; For silent installs, no language prompt, use default +    IfSilent lbl_return +    StrCmp $SKIP_DIALOGS "true" lbl_return +    lbl_build_menu:  	Push ""      # Use separate file so labels can be UTF-16 but we can still merge changes diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f66663891d..b6f52e3e15 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -83,6 +83,7 @@  #include "llweb.h"  #include "llsecondlifeurls.h" +#include "llupdaterservice.h"  // Linden library includes  #include "llavatarnamecache.h" @@ -196,6 +197,8 @@  #include "llsecapi.h"  #include "llmachineid.h" +#include "llmainlooprepeater.h" +  // *FIX: These extern globals should be cleaned up.  // The globals either represent state/config/resource-storage of either   // this app, or another 'component' of the viewer. App globals should be  @@ -576,7 +579,8 @@ LLAppViewer::LLAppViewer() :  	mAgentRegionLastAlive(false),  	mRandomizeFramerate(LLCachedControl<bool>(gSavedSettings,"Randomize Framerate", FALSE)),  	mPeriodicSlowFrame(LLCachedControl<bool>(gSavedSettings,"Periodic Slow Frame", FALSE)), -	mFastTimerLogThread(NULL) +	mFastTimerLogThread(NULL), +	mUpdater(new LLUpdaterService())  {  	if(NULL != sInstance)  	{ @@ -652,10 +656,13 @@ bool LLAppViewer::init()      initThreads();      writeSystemInfo(); -	// Build a string representing the current version number. -    gCurrentVersion = llformat("%s %s",  -							   gSavedSettings.getString("VersionChannelName").c_str(), -							   LLVersionInfo::getVersion().c_str()); +	// Initialize updater service (now that we have an io pump) +	initUpdater(); +	if(isQuitting()) +	{ +		// Early out here because updater set the quitting flag. +		return true; +	}  	//////////////////////////////////////////////////////////////////////////////  	////////////////////////////////////////////////////////////////////////////// @@ -799,6 +806,9 @@ bool LLAppViewer::init()  		return 1;  	} +	// Initialize the repeater service. +	LLMainLoopRepeater::instance().start(); +	  	//  	// Initialize the window  	// @@ -895,7 +905,8 @@ bool LLAppViewer::init()  	gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString();  	// Save the current version to the prefs file -	gSavedSettings.setString("LastRunVersion", gCurrentVersion); +	gSavedSettings.setString("LastRunVersion",  +							 LLVersionInfo::getVersionAndChannel());  	gSimLastTime = gRenderStartTime.getElapsedTimeF32();  	gSimFrames = (F32)gFrameCount; @@ -970,7 +981,7 @@ bool LLAppViewer::mainLoop()  	gServicePump = new LLPumpIO(gAPRPoolp);  	LLHTTPClient::setPump(*gServicePump);  	LLCurl::setCAFile(gDirUtilp->getCAFile()); - +	  	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.  	LLVoiceChannel::initClass(); @@ -1353,11 +1364,14 @@ bool LLAppViewer::cleanup()  	llinfos << "Cleaning Up" << llendflush;  	// Must clean up texture references before viewer window is destroyed. -	LLHUDManager::getInstance()->updateEffects(); -	LLHUDObject::updateAll(); -	LLHUDManager::getInstance()->cleanupEffects(); -	LLHUDObject::cleanupHUDObjects(); -	llinfos << "HUD Objects cleaned up" << llendflush; +	if(LLHUDManager::instanceExists()) +	{ +		LLHUDManager::getInstance()->updateEffects(); +		LLHUDObject::updateAll(); +		LLHUDManager::getInstance()->cleanupEffects(); +		LLHUDObject::cleanupHUDObjects(); +		llinfos << "HUD Objects cleaned up" << llendflush; +	}  	LLKeyframeDataCache::clear(); @@ -1369,8 +1383,10 @@ bool LLAppViewer::cleanup()  	// Note: this is where gWorldMap used to be deleted.  	// Note: this is where gHUDManager used to be deleted. -	LLHUDManager::getInstance()->shutdownClass(); -	 +	if(LLHUDManager::instanceExists()) +	{ +		LLHUDManager::getInstance()->shutdownClass(); +	}  	delete gAssetStorage;  	gAssetStorage = NULL; @@ -1666,7 +1682,10 @@ bool LLAppViewer::cleanup()  #ifndef LL_RELEASE_FOR_DOWNLOAD  	llinfos << "Auditing VFS" << llendl; -	gVFS->audit(); +	if(gVFS) +	{ +		gVFS->audit(); +	}  #endif  	llinfos << "Misc Cleanup" << llendflush; @@ -1707,6 +1726,8 @@ bool LLAppViewer::cleanup()  		llinfos << "File launched." << llendflush;  	} +	LLMainLoopRepeater::instance().stop(); +  	ll_close_fail_log();      llinfos << "Goodbye!" << llendflush; @@ -1953,8 +1974,6 @@ bool LLAppViewer::initConfiguration()  	gSavedSettings.setString("ClientSettingsFile",           gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Global"))); -	gSavedSettings.setString("VersionChannelName", LLVersionInfo::getChannel()); -  #ifndef	LL_RELEASE_FOR_DOWNLOAD  	// provide developer build only overrides for these control variables that are not  	// persisted to settings.xml @@ -2095,6 +2114,11 @@ bool LLAppViewer::initConfiguration()          }      } +    if(clp.hasOption("channel")) +    { +		LLVersionInfo::resetChannel(clp.getOption("channel")[0]); +	} +	  	// If we have specified crash on startup, set the global so we'll trigger the crash at the right time  	if(clp.hasOption("crashonstartup")) @@ -2364,6 +2388,58 @@ bool LLAppViewer::initConfiguration()  	return true; // Config was successful.  } +namespace { +    // *TODO - decide if there's a better place for this function. +    // do we need a file llupdaterui.cpp or something? -brad +    bool notify_update(LLSD const & evt) +    { +		switch (evt["type"].asInteger()) +		{ +			case LLUpdaterService::DOWNLOAD_COMPLETE: +				LLNotificationsUtil::add("DownloadBackground"); +				break; +			case LLUpdaterService::INSTALL_ERROR: +				LLNotificationsUtil::add("FailedUpdateInstall"); +				break; +			default: +				llinfos << "unhandled update event " << evt << llendl; +				break; +		} + +		// let others also handle this event by default +        return false; +    } +}; + +void LLAppViewer::initUpdater() +{ +	// Initialize the updater service. +	// Generate URL to the udpater service +	// Get Channel +	// Get Version +	std::string url = gSavedSettings.getString("UpdaterServiceURL"); +	std::string channel = LLVersionInfo::getChannel(); +	std::string version = LLVersionInfo::getVersion(); +	std::string protocol_version = gSavedSettings.getString("UpdaterServiceProtocolVersion"); +	std::string service_path = gSavedSettings.getString("UpdaterServicePath"); +	U32 check_period = gSavedSettings.getU32("UpdaterServiceCheckPeriod"); + +	mUpdater->setAppExitCallback(boost::bind(&LLAppViewer::forceQuit, this)); +	mUpdater->initialize(protocol_version,  +						 url,  +						 service_path,  +						 channel,  +						 version); + 	mUpdater->setCheckPeriod(check_period); +	if(gSavedSettings.getBOOL("UpdaterServiceActive")) +	{ +		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); +}  void LLAppViewer::checkForCrash(void)  { @@ -2524,15 +2600,18 @@ void LLAppViewer::cleanupSavedSettings()  	// save window position if not maximized  	// as we don't track it in callbacks -	BOOL maximized = gViewerWindow->mWindow->getMaximized(); -	if (!maximized) +	if(NULL != gViewerWindow)  	{ -		LLCoordScreen window_pos; - -		if (gViewerWindow->mWindow->getPosition(&window_pos)) +		BOOL maximized = gViewerWindow->mWindow->getMaximized(); +		if (!maximized)  		{ -			gSavedSettings.setS32("WindowX", window_pos.mX); -			gSavedSettings.setS32("WindowY", window_pos.mY); +			LLCoordScreen window_pos; + +			if (gViewerWindow->mWindow->getPosition(&window_pos)) +			{ +				gSavedSettings.setS32("WindowX", window_pos.mX); +				gSavedSettings.setS32("WindowY", window_pos.mY); +			}  		}  	} @@ -2555,7 +2634,7 @@ void LLAppViewer::writeSystemInfo()  {  	gDebugInfo["SLLog"] = LLError::logFileName(); -	gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName"); +	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();  	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();  	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor();  	gDebugInfo["ClientInfo"]["PatchVersion"] = LLVersionInfo::getPatch(); @@ -2658,7 +2737,7 @@ void LLAppViewer::handleViewerCrash()  	//We already do this in writeSystemInfo(), but we do it again here to make /sure/ we have a version  	//to check against no matter what -	gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName"); +	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();  	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();  	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); @@ -4271,7 +4350,10 @@ void LLAppViewer::disconnectViewer()  	// This is where we used to call gObjectList.destroy() and then delete gWorldp.  	// Now we just ask the LLWorld singleton to cleanly shut down. -	LLWorld::getInstance()->destroyClass(); +	if(LLWorld::instanceExists()) +	{ +		LLWorld::getInstance()->destroyClass(); +	}  	// call all self-registered classes  	LLDestroyClassList::instance().fireCallbacks(); @@ -4385,7 +4467,7 @@ void LLAppViewer::handleLoginComplete()  	initMainloopTimeout("Mainloop Init");  	// Store some data to DebugInfo in case of a freeze. -	gDebugInfo["ClientInfo"]["Name"] = gSavedSettings.getString("VersionChannelName"); +	gDebugInfo["ClientInfo"]["Name"] = LLVersionInfo::getChannel();  	gDebugInfo["ClientInfo"]["MajorVersion"] = LLVersionInfo::getMajor();  	gDebugInfo["ClientInfo"]["MinorVersion"] = LLVersionInfo::getMinor(); @@ -4491,7 +4573,7 @@ void LLAppViewer::launchUpdater()  	// *TODO change userserver to be grid on both viewer and sim, since  	// userserver no longer exists.  	query_map["userserver"] = LLGridManager::getInstance()->getGridLabel(); -	query_map["channel"] = gSavedSettings.getString("VersionChannelName"); +	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); diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index a14ab4362f..aa4256a2bd 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -39,7 +39,7 @@ class LLTextureCache;  class LLImageDecodeThread;  class LLTextureFetch;  class LLWatchdogTimeout; -class LLCommandLineParser; +class LLUpdaterService;  struct apr_dso_handle_t; @@ -186,7 +186,7 @@ private:  	bool initThreads(); // Initialize viewer threads, return false on failure.  	bool initConfiguration(); // Initialize settings from the command line/config file. - +	void initUpdater(); // Initialize the updater service.  	bool initCache(); // Initialize local client cache. @@ -264,7 +264,14 @@ private:  	U32 mAvailPhysicalMemInKB ;  	U32 mAvailVirtualMemInKB ; +	 +	boost::scoped_ptr<LLUpdaterService> mUpdater; + +	//--------------------------------------------- +	//*NOTE: Mani - legacy updater stuff +	// Still useable?  public: +  	//some information for updater  	typedef struct  	{ @@ -274,6 +281,7 @@ public:  	static LLUpdaterInfo *sUpdaterInfo ;  	void launchUpdater(); +	//---------------------------------------------  };  // consts from viewer.h diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 8f385160e9..885d553524 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -1092,9 +1092,11 @@ LLChicletPanel::LLChicletPanel(const Params&p)  LLChicletPanel::~LLChicletPanel()  { -	LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton); -	LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton); - +	if(LLTransientFloaterMgr::instanceExists()) +	{ +		LLTransientFloaterMgr::getInstance()->removeControlView(mLeftScrollButton); +		LLTransientFloaterMgr::getInstance()->removeControlView(mRightScrollButton); +	}  }  void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){ diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 2b92b228b3..b4a1457f47 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -166,7 +166,7 @@ void LLCurrencyUIManager::Impl::updateCurrencyInfo()  		gAgent.getSecureSessionID().asString());  	keywordArgs.appendString("language", LLUI::getLanguage());  	keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy); -	keywordArgs.appendString("viewerChannel", gSavedSettings.getString("VersionChannelName")); +	keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel());  	keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor());  	keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor());  	keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); @@ -241,7 +241,7 @@ void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)  	{  		keywordArgs.appendString("password", password);  	} -	keywordArgs.appendString("viewerChannel", gSavedSettings.getString("VersionChannelName")); +	keywordArgs.appendString("viewerChannel", LLVersionInfo::getChannel());  	keywordArgs.appendInt("viewerMajorVersion", LLVersionInfo::getMajor());  	keywordArgs.appendInt("viewerMinorVersion", LLVersionInfo::getMinor());  	keywordArgs.appendInt("viewerPatchVersion", LLVersionInfo::getPatch()); diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 135137069c..8ae3ccbae3 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -213,7 +213,7 @@ LLSD LLFloaterAbout::getInfo()  	info["VIEWER_VERSION_STR"] = LLVersionInfo::getVersion();  	info["BUILD_DATE"] = __DATE__;  	info["BUILD_TIME"] = __TIME__; -	info["CHANNEL"] = gSavedSettings.getString("VersionChannelName"); +	info["CHANNEL"] = LLVersionInfo::getChannel();  	info["VIEWER_RELEASE_NOTES_URL"] = get_viewer_release_notes_url(); @@ -291,7 +291,7 @@ static std::string get_viewer_release_notes_url()  	std::string url = LLTrans::getString("RELEASE_NOTES_BASE_URL");  	if (! LLStringUtil::endsWith(url, "/"))  		url += "/"; -	url += gSavedSettings.getString("VersionChannelName") + "/"; +	url += LLVersionInfo::getChannel() + "/";  	url += LLVersionInfo::getShortVersion();  	return LLWeb::escapeURL(url);  } diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 029e700c4c..fe84aca147 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -42,6 +42,7 @@  // newview  #include "llviewernetwork.h"  #include "llviewercontrol.h" +#include "llversioninfo.h"  #include "llslurl.h"  #include "llstartup.h"  #include "llfloaterreg.h" @@ -181,8 +182,8 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia  	request_params["read_critical"] = false; // handleTOSResponse  	request_params["last_exec_event"] = mLastExecEvent;  	request_params["mac"] = hashed_unique_id_string; -	request_params["version"] = gCurrentVersion; // Includes channel name -	request_params["channel"] = gSavedSettings.getString("VersionChannelName"); +	request_params["version"] = LLVersionInfo::getVersionAndChannel(); // Includes channel name +	request_params["channel"] = LLVersionInfo::getChannel();  	request_params["id0"] = mSerialNumber;  	mRequestData.clear(); diff --git a/indra/newview/llmainlooprepeater.cpp b/indra/newview/llmainlooprepeater.cpp new file mode 100644 index 0000000000..5c020e6d98 --- /dev/null +++ b/indra/newview/llmainlooprepeater.cpp @@ -0,0 +1,88 @@ +/**  + * @file llmachineid.cpp + * @brief retrieves unique machine ids + * + * $LicenseInfo:firstyear=2009&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 "llviewerprecompiledheaders.h" +#include "llapr.h" +#include "llevents.h" +#include "llmainlooprepeater.h" + + + +// LLMainLoopRepeater +//----------------------------------------------------------------------------- + + +LLMainLoopRepeater::LLMainLoopRepeater(void): +	mQueue(0) +{ +	; // No op. +} + + +void LLMainLoopRepeater::start(void) +{ +	if(mQueue != 0) return; + +	mQueue = new LLThreadSafeQueue<LLSD>(gAPRPoolp, 1024); +	mMainLoopConnection = LLEventPumps::instance(). +		obtain("mainloop").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMainLoop, this, _1)); +	mRepeaterConnection = LLEventPumps::instance(). +		obtain("mainlooprepeater").listen(LLEventPump::inventName(), boost::bind(&LLMainLoopRepeater::onMessage, this, _1)); +} + + +void LLMainLoopRepeater::stop(void) +{ +	mMainLoopConnection.release(); +	mRepeaterConnection.release(); + +	delete mQueue; +	mQueue = 0; +} + + +bool LLMainLoopRepeater::onMainLoop(LLSD const &) +{ +	LLSD message; +	while(mQueue->tryPopBack(message)) { +		std::string pump = message["pump"].asString(); +		if(pump.length() == 0 ) continue; // No pump. +		LLEventPumps::instance().obtain(pump).post(message["payload"]); +	} +	return false; +} + + +bool LLMainLoopRepeater::onMessage(LLSD const & event) +{ +	try { +		mQueue->pushFront(event); +	} catch(LLThreadSafeQueueError & e) { +		llwarns << "could not repeat message (" << e.what() << ")" <<  +			event.asString() << LL_ENDL; +	} +	return false; +} diff --git a/indra/newview/llmainlooprepeater.h b/indra/newview/llmainlooprepeater.h new file mode 100644 index 0000000000..f84c0ca94c --- /dev/null +++ b/indra/newview/llmainlooprepeater.h @@ -0,0 +1,65 @@ +/**  + * @file llmainlooprepeater.h + * @brief a service for repeating messages on the main loop. + * + * $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_LLMAINLOOPREPEATER_H +#define LL_LLMAINLOOPREPEATER_H + + +#include "llsd.h" +#include "llthreadsafequeue.h" + + +// +// A service which creates the pump 'mainlooprepeater' to which any thread can +// post a message that will be re-posted on the main loop. +// +// The posted message should contain two map elements: pump and payload.  The +// pump value is a string naming the pump to which the message should be +// re-posted.  The payload value is what will be posted to the designated pump. +// +class LLMainLoopRepeater: +	public LLSingleton<LLMainLoopRepeater> +{ +public: +	LLMainLoopRepeater(void); +	 +	// Start the repeater service. +	void start(void); +	 +	// Stop the repeater service. +	void stop(void); +	 +private: +	LLTempBoundListener mMainLoopConnection; +	LLTempBoundListener mRepeaterConnection; +	LLThreadSafeQueue<LLSD> * mQueue; +	 +	bool onMainLoop(LLSD const &); +	bool onMessage(LLSD const & event); +}; + + +#endif diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index 6658e1d7e8..d38bb5aa4a 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -552,7 +552,7 @@ LLPanelStandStopFlying::LLPanelStandStopFlying() :  }  // static -inline LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance() +LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance()  {  	static LLPanelStandStopFlying* panel = getStandStopFlyingPanel();  	return panel; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 467aefc60f..cf567fb208 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -230,7 +230,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,  	getChild<LLPanel>("login")->setDefaultBtn("connect_btn"); -	std::string channel = gSavedSettings.getString("VersionChannelName"); +	std::string channel = LLVersionInfo::getChannel();  	std::string version = llformat("%s (%d)",  								   LLVersionInfo::getShortVersion().c_str(),  								   LLVersionInfo::getBuild()); @@ -817,7 +817,7 @@ void LLPanelLogin::loadLoginPage()  								   LLVersionInfo::getShortVersion().c_str(),  								   LLVersionInfo::getBuild()); -	char* curl_channel = curl_escape(gSavedSettings.getString("VersionChannelName").c_str(), 0); +	char* curl_channel = curl_escape(LLVersionInfo::getChannel().c_str(), 0);  	char* curl_version = curl_escape(version.c_str(), 0);  	oStr << "&channel=" << curl_channel; diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index 3dce66f394..c76ecae4a2 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -134,8 +134,11 @@ LLSpeakButton::LLSpeakButton(const Params& p)  LLSpeakButton::~LLSpeakButton()  { -	LLTransientFloaterMgr::getInstance()->removeControlView(mSpeakBtn); -	LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn); +	if(LLTransientFloaterMgr::instanceExists()) +	{ +		LLTransientFloaterMgr::getInstance()->removeControlView(mSpeakBtn); +		LLTransientFloaterMgr::getInstance()->removeControlView(mShowBtn); +	}  }  void LLSpeakButton::setSpeakToolTip(const std::string& msg) diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index ede1d6bebe..9b38bf22ff 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -308,7 +308,10 @@ void LLSpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker  void LLSpeakingIndicatorManager::unregisterSpeakingIndicator(const LLUUID& speaker_id, const LLSpeakingIndicator* const speaking_indicator)  { -	SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator); +	if(SpeakingIndicatorManager::instanceExists()) +	{ +		SpeakingIndicatorManager::instance().unregisterSpeakingIndicator(speaker_id, speaking_indicator); +	}  }  // EOF diff --git a/indra/newview/lltransientfloatermgr.cpp b/indra/newview/lltransientfloatermgr.cpp index 78dd602f39..6deab96b45 100644 --- a/indra/newview/lltransientfloatermgr.cpp +++ b/indra/newview/lltransientfloatermgr.cpp @@ -36,8 +36,11 @@  LLTransientFloaterMgr::LLTransientFloaterMgr()  { -	gViewerWindow->getRootView()->addMouseDownCallback(boost::bind( +	if(gViewerWindow) +	{ +		gViewerWindow->getRootView()->addMouseDownCallback(boost::bind(  			&LLTransientFloaterMgr::leftMouseClickCallback, this, _1, _2, _3)); +	}  	mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(GLOBAL, std::set<LLView*>()));  	mGroupControls.insert(std::pair<ETransientGroup, std::set<LLView*> >(DOCKED, std::set<LLView*>())); diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 050e34ade9..8ccfdb071b 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -36,7 +36,7 @@  #include "llbufferstream.h"  #include "llui.h" -#include "llversionviewer.h" +#include "llversioninfo.h"  #include "llviewercontrol.h"  #include "jsoncpp/reader.h" @@ -64,11 +64,11 @@ void LLTranslate::translateMessage(LLHTTPClient::ResponderPtr &result, const std  	getTranslateUrl(url, from_lang, to_lang, mesg);      std::string user_agent = llformat("%s %d.%d.%d (%d)", -		LL_CHANNEL, -		LL_VERSION_MAJOR, -		LL_VERSION_MINOR, -		LL_VERSION_PATCH, -		LL_VERSION_BUILD ); +		LLVersionInfo::getChannel().c_str(), +		LLVersionInfo::getMajor(), +		LLVersionInfo::getMinor(), +		LLVersionInfo::getPatch(), +		LLVersionInfo::getBuild());  	if (!m_Header.size())  	{ diff --git a/indra/newview/llversioninfo.cpp b/indra/newview/llversioninfo.cpp index 733d05834a..53994c68f2 100644 --- a/indra/newview/llversioninfo.cpp +++ b/indra/newview/llversioninfo.cpp @@ -95,9 +95,42 @@ const std::string &LLVersionInfo::getShortVersion()  	return version;  } +namespace +{ +	/// Storage of the channel name the viewer is using. +	//  The channel name is set by hardcoded constant,  +	//  or by calling LLVersionInfo::resetChannel() +	std::string sWorkingChannelName(LL_CHANNEL); + +	// Storage for the "version and channel" string. +	// This will get reset too. +	std::string sVersionChannel(""); +} + +//static +const std::string &LLVersionInfo::getVersionAndChannel() +{ +	if (sVersionChannel.empty()) +	{ +		// cache the version string +		std::ostringstream stream; +		stream << LLVersionInfo::getVersion()  +			   << " " +			   << LLVersionInfo::getChannel(); +		sVersionChannel = stream.str(); +	} + +	return sVersionChannel; +} +  //static  const std::string &LLVersionInfo::getChannel()  { -	static std::string name(LL_CHANNEL); -	return name; +	return sWorkingChannelName; +} + +void LLVersionInfo::resetChannel(const std::string& channel) +{ +	sWorkingChannelName = channel; +	sVersionChannel.clear(); // Reset version and channel string til next use.  } diff --git a/indra/newview/llversioninfo.h b/indra/newview/llversioninfo.h index e468b6ae4e..36defbcd68 100644 --- a/indra/newview/llversioninfo.h +++ b/indra/newview/llversioninfo.h @@ -58,8 +58,15 @@ public:  	/// return the viewer version as a string like "2.0.0"  	static const std::string &getShortVersion(); +	/// return the viewer version and channel as a string +	/// like "2.0.0.200030 Second Life Release" +	static const std::string &getVersionAndChannel(); +  	/// return the channel name, e.g. "Second Life"  	static const std::string &getChannel(); +	 +	/// reset the channel name used by the viewer. +	static void resetChannel(const std::string& channel);  };  #endif diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 117e49d67f..622d09c600 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -70,6 +70,7 @@  #include "llpaneloutfitsinventory.h"  #include "llpanellogin.h"  #include "llpaneltopinfobar.h" +#include "llupdaterservice.h"  #ifdef TOGGLE_HACKED_GODLIKE_VIEWER  BOOL 				gHackGodmode = FALSE; @@ -82,7 +83,6 @@ LLControlGroup gCrashSettings("CrashSettings");	// saved at end of session  LLControlGroup gWarningSettings("Warnings"); // persists ignored dialogs/warnings  std::string gLastRunVersion; -std::string gCurrentVersion;  extern BOOL gResizeScreenTexture;  extern BOOL gDebugGL; @@ -502,6 +502,18 @@ bool toggle_show_object_render_cost(const LLSD& newvalue)  	return true;  } +void toggle_updater_service_active(LLControlVariable* control, const LLSD& new_value) +{ +    if(new_value.asBoolean()) +    { +        LLUpdaterService().startChecking(); +    } +    else +    { +        LLUpdaterService().stopChecking(); +    } +} +  ////////////////////////////////////////////////////////////////////////////  void settings_setup_listeners() @@ -649,6 +661,7 @@ void settings_setup_listeners()  	gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_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("UpdaterServiceActive")->getSignal()->connect(&toggle_updater_service_active);  	gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));  	gSavedSettings.getControl("RenderTransparentWater")->getSignal()->connect(boost::bind(&handleRenderTransparentWaterChanged, _2));  } diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h index 22b48f8906..d7191f5c8d 100644 --- a/indra/newview/llviewercontrol.h +++ b/indra/newview/llviewercontrol.h @@ -57,7 +57,5 @@ extern LLControlGroup gCrashSettings;  // Set after settings loaded  extern std::string gLastRunVersion; -extern std::string gCurrentVersion; -  #endif // LL_LLVIEWERCONTROL_H diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 31cf0acdd7..fae4eb3c05 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -492,7 +492,7 @@ std::string LLViewerMedia::getCurrentUserAgent()  	// Just in case we need to check browser differences in A/B test  	// builds. -	std::string channel = gSavedSettings.getString("VersionChannelName"); +	std::string channel = LLVersionInfo::getChannel();  	// append our magic version number string to the browser user agent id  	// See the HTTP 1.0 and 1.1 specifications for allowed formats: diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 46c78e2bb4..402f00c5e7 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -48,6 +48,7 @@  #include "llagent.h"  #include "llagentcamera.h"  #include "llviewercontrol.h" +#include "llversioninfo.h"  #include "llfloatertools.h"  #include "lldebugview.h"  #include "llfasttimerview.h" @@ -749,7 +750,7 @@ void send_stats()  	// send fps only for time app spends in foreground  	agent["fps"] = (F32)gForegroundFrameCount / gForegroundTime.getElapsedTimeF32(); -	agent["version"] = gCurrentVersion; +	agent["version"] = LLVersionInfo::getVersionAndChannel();  	std::string language = LLUI::getLanguage();  	agent["language"] = language; diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 1247e4724e..9536bf2cf7 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2871,6 +2871,25 @@ Download to your Applications folder?    <notification     icon="alertmodal.tga" +   name="FailedUpdateInstall" +   type="alertmodal"> +An error occurred installing the viewer update. +Please download and install the latest viewer from +http://secondlife.com/download. +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> +  <notification +   icon="notifytip.tga" +   name="DownloadBackground" +   type="notifytip"> +An updated version of [APP_NAME] has been downloaded. +It will be applied the next time you restart [APP_NAME] +  </notification> + +  <notification +   icon="alertmodal.tga"     name="DeedObjectToGroup"     type="alertmodal">  Deeding this object will cause the group to: diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 14aa38c5d3..584bd1ea9d 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -341,4 +341,20 @@     name="web_proxy_port"     top_delta="0"     width="145" /> + +  <check_box +    top_delta="2" +    enabled="true" +    follows="left|top" +    height="18" +    initial_value="true" +    control_name="UpdaterServiceActive" +    label="Automatically download and install [APP_NAME] updates" +    left="30" +    mouse_opaque="true" +    name="updater_service_active" +    radio_style="false" +    width="400" +    top_pad="10"/> +  </panel> diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index db50b89620..b902c7ab09 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -48,6 +48,9 @@ const std::string VIEWERLOGIN_GRIDLABEL("viewerlogin_grid");  const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno"); +const std::string VIEWERLOGIN_CHANNEL("invalid_channel"); +const std::string VIEWERLOGIN_VERSION_CHANNEL("invalid_version"); +  // Link seams.  //----------------------------------------------------------------------------- @@ -160,7 +163,6 @@ std::string LLGridManager::getAppSLURLBase(const std::string& grid_name)  //-----------------------------------------------------------------------------  #include "../llviewercontrol.h"  LLControlGroup gSavedSettings("Global"); -std::string gCurrentVersion = "invalid_version";  LLControlGroup::LLControlGroup(const std::string& name) :  	LLInstanceTracker<LLControlGroup, std::string>(name){} @@ -177,6 +179,10 @@ BOOL LLControlGroup::declareString(const std::string& name, const std::string &i  #include "lluicolortable.h"  void LLUIColorTable::saveUserSettings(void)const {} +//----------------------------------------------------------------------------- +#include "../llversioninfo.h" +const std::string &LLVersionInfo::getVersionAndChannel() { return VIEWERLOGIN_VERSION_CHANNEL; } +const std::string &LLVersionInfo::getChannel() { return VIEWERLOGIN_CHANNEL; }  //-----------------------------------------------------------------------------  #include "llnotifications.h" @@ -290,7 +296,6 @@ namespace tut  			gSavedSettings.declareBOOL("UseDebugMenus", FALSE, "", FALSE);  			gSavedSettings.declareBOOL("ForceMandatoryUpdate", FALSE, "", FALSE);  			gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", FALSE); -			gSavedSettings.declareString("VersionChannelName", "test_version_string", "", FALSE);  			gSavedSettings.declareString("NextLoginLocation", "", "", FALSE);  			gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", FALSE); diff --git a/indra/newview/tests/llversioninfo_test.cpp b/indra/newview/tests/llversioninfo_test.cpp new file mode 100644 index 0000000000..8855a24ead --- /dev/null +++ b/indra/newview/tests/llversioninfo_test.cpp @@ -0,0 +1,114 @@ +/**  + * @file llversioninfo_test.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 "../test/lltut.h" + +#include "../llversioninfo.h" +#include "llversionviewer.h" + +namespace tut +{ +    struct versioninfo +    { +		versioninfo() +			: mResetChannel("Reset Channel") +		{ +			std::ostringstream stream; +			stream << LL_VERSION_MAJOR << "." +				   << LL_VERSION_MINOR << "." +				   << LL_VERSION_PATCH << "." +				   << LL_VERSION_BUILD; +			mVersion = stream.str(); +			stream.str(""); + +			stream << LL_VERSION_MAJOR << "." +				   << LL_VERSION_MINOR << "." +				   << LL_VERSION_PATCH; +			mShortVersion = stream.str(); +			stream.str(""); + +			stream << mVersion +				   << " " +				   << LL_CHANNEL; +			mVersionAndChannel = stream.str(); +			stream.str(""); + +			stream << mVersion +				   << " " +				   << mResetChannel; +			mResetVersionAndChannel = stream.str(); +		} +		std::string mResetChannel; +		std::string mVersion; +		std::string mShortVersion; +		std::string mVersionAndChannel; +		std::string mResetVersionAndChannel; +    }; +     +	typedef test_group<versioninfo> versioninfo_t; +	typedef versioninfo_t::object versioninfo_object_t; +	tut::versioninfo_t tut_versioninfo("LLVersionInfo"); + +	template<> template<> +	void versioninfo_object_t::test<1>() +	{ +		ensure_equals("Major version",  +					  LLVersionInfo::getMajor(),  +					  LL_VERSION_MAJOR); +		ensure_equals("Minor version",  +					  LLVersionInfo::getMinor(),  +					  LL_VERSION_MINOR); +		ensure_equals("Patch version",  +					  LLVersionInfo::getPatch(),  +					  LL_VERSION_PATCH); +		ensure_equals("Build version",  +					  LLVersionInfo::getBuild(),  +					  LL_VERSION_BUILD); +		ensure_equals("Channel version",  +					  LLVersionInfo::getChannel(),  +					  LL_CHANNEL); + +		ensure_equals("Version String",  +					  LLVersionInfo::getVersion(),  +					  mVersion); +		ensure_equals("Short Version String",  +					  LLVersionInfo::getShortVersion(),  +					  mShortVersion); +		ensure_equals("Version and channel String",  +					  LLVersionInfo::getVersionAndChannel(),  +					  mVersionAndChannel); + +		LLVersionInfo::resetChannel(mResetChannel); +		ensure_equals("Reset channel version",  +					  LLVersionInfo::getChannel(),  +					  mResetChannel); + +		ensure_equals("Reset Version and channel String",  +					  LLVersionInfo::getVersionAndChannel(),  +					  mResetVersionAndChannel); +	} +} diff --git a/indra/newview/tests/llviewerhelputil_test.cpp b/indra/newview/tests/llviewerhelputil_test.cpp index a0f1d1c3c3..b425b50c8b 100644 --- a/indra/newview/tests/llviewerhelputil_test.cpp +++ b/indra/newview/tests/llviewerhelputil_test.cpp @@ -72,16 +72,13 @@ static void substitute_string(std::string &input, const std::string &search, con  	}  } -class LLAgent -{ -public: -	LLAgent() {} -	~LLAgent() {} -#ifdef __GNUC__ -	__attribute__ ((noinline)) -#endif -	bool isGodlike() const { return FALSE; } -}; +#include "../llagent.h" +LLAgent::LLAgent() : mAgentAccess(gSavedSettings) { } +LLAgent::~LLAgent() { } +bool LLAgent::isGodlike() const { return FALSE; } +LLAgentAccess::LLAgentAccess(LLControlGroup& settings) : mSavedSettings(settings) { } +LLUIColor::LLUIColor() {} +  LLAgent gAgent;  std::string LLWeb::expandURLSubstitutions(const std::string &url, diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 6861f02bfb..4e5d6271df 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -247,10 +247,13 @@ class WindowsManifest(ViewerManifest):          self.disable_manifest_check() +        self.path("../viewer_components/updater/scripts/windows/update_install.bat") +          # Get shared libs from the shared libs staging directory          if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']),                         dst=""): +              self.enable_crt_manifest_check()              # Get kdu dll, continue if missing. @@ -572,6 +575,8 @@ class DarwinManifest(ViewerManifest):              # copy additional libs in <bundle>/Contents/MacOS/              self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib") +            self.path("../viewer_components/updater/scripts/darwin/update_install", "MacOS/update_install") +              # most everything goes in the Resources directory              if self.prefix(src="", dst="Resources"):                  super(DarwinManifest, self).construct() @@ -743,6 +748,11 @@ class DarwinManifest(ViewerManifest):              devfile = re.search("/dev/disk([0-9]+)[^s]", hdi_output).group(0).strip()              volpath = re.search('HFS\s+(.+)', hdi_output).group(1).strip() +            if devfile != '/dev/disk1': +                # adding more debugging info based upon nat's hunches to the +                # logs to help track down 'SetFile -a V' failures -brad +                print "WARNING: 'SetFile -a V' command below is probably gonna fail" +              # Copy everything in to the mounted .dmg              if self.default_channel() and not self.default_grid(): @@ -842,6 +852,8 @@ class LinuxManifest(ViewerManifest):              # recurse              self.end_prefix("res-sdl") +        self.path("../viewer_components/updater/scripts/linux/update_install", "bin/update_install") +          # plugins          if self.prefix(src="", dst="bin/llplugin"):              self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") @@ -870,7 +882,7 @@ class LinuxManifest(ViewerManifest):          if self.args['buildtype'].lower() == 'release' and self.is_packaging_viewer():              print "* Going strip-crazy on the packaged binaries, since this is a RELEASE build" -            self.run_command("find %(d)r/bin %(d)r/lib -type f | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure +            self.run_command("find %(d)r/bin %(d)r/lib -type f \\! -name update_install | xargs --no-run-if-empty strip -S" % {'d': self.get_dst_prefix()} ) # makes some small assumptions about our packaged dir structure          # Fix access permissions          self.run_command(""" @@ -897,6 +909,9 @@ class LinuxManifest(ViewerManifest):                          'dir': self.get_build_prefix(),                          'inst_name': installer_name,                          'inst_path':self.build_path_of(installer_name)}) +            else: +                print "Skipping %s.tar.bz2 for non-Release build (%s)" % \ +                      (installer_name, self.args['buildtype'])          finally:              self.run_command("mv %(inst)s %(dst)s" % {                  'dst': self.get_dst_prefix(), diff --git a/indra/viewer_components/CMakeLists.txt b/indra/viewer_components/CMakeLists.txt index 0993b64b14..74c9b4568d 100644 --- a/indra/viewer_components/CMakeLists.txt +++ b/indra/viewer_components/CMakeLists.txt @@ -1,4 +1,4 @@  # -*- cmake -*-  add_subdirectory(login) - +add_subdirectory(updater) diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt new file mode 100644 index 0000000000..0e288bb496 --- /dev/null +++ b/indra/viewer_components/updater/CMakeLists.txt @@ -0,0 +1,82 @@ +# -*- cmake -*- + +project(updater_service) + +include(00-Common) +if(LL_TESTS) +  include(LLAddBuildTest) +endif(LL_TESTS) +include(CMakeCopyIfDifferent) +include(CURL) +include(LLCommon) +include(LLMessage) +include(LLPlugin) +include(LLVFS) + +include_directories( +    ${LLCOMMON_INCLUDE_DIRS} +    ${LLMESSAGE_INCLUDE_DIRS} +    ${LLPLUGIN_INCLUDE_DIRS} +	${LLVFS_INCLUDE_DIRS} +	${CURL_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) + +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} +    ${LLPLUGIN_LIBRARIES} +	${LLVFS_LIBRARIES} +    ) + +if(LL_TESTS) +  SET(llupdater_service_TEST_SOURCE_FILES +      llupdaterservice.cpp +      ) + +# *NOTE:Mani - I was trying to use the preprocessor seam to mock out +#              llifstream (and other) llcommon classes. I didn't work +#              because of the windows declspec(dllimport)attribute. +#set_source_files_properties( +#    llupdaterservice.cpp +#    PROPERTIES +#      LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream" +#    ) + +  LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}") +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 new file mode 100644 index 0000000000..c6aa9b0f11 --- /dev/null +++ b/indra/viewer_components/updater/llupdatechecker.cpp @@ -0,0 +1,194 @@ +/**  + * @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 <stdexcept> +#include <boost/format.hpp> +#include "llhttpclient.h" +#include "llsd.h" +#include "llupdatechecker.h" +#include "lluri.h" + + +#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. +	} +}; + + +class LLUpdateChecker::Implementation: +	public LLHTTPClient::Responder +{ +public: +	Implementation(Client & client); +	~Implementation(); +	void check(std::string const & protocolVersion, std::string const & hostUrl,  +			   std::string const & servicePath, std::string channel, std::string version); +	 +	// Responder: +	virtual void completed(U32 status, +						   const std::string & reason, +						   const LLSD& content); +	virtual void error(U32 status, const std::string & reason); +	 +private:	 +	static const char * sProtocolVersion; +	 +	Client & mClient; +	LLHTTPClient mHttpClient; +	bool mInProgress; +	std::string mVersion; +	 +	std::string buildUrl(std::string const & protocolVersion, std::string const & hostUrl,  +						 std::string const & servicePath, std::string channel, std::string version); + +	LOG_CLASS(LLUpdateChecker::Implementation); +}; + + + +// LLUpdateChecker +//----------------------------------------------------------------------------- + + +LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client): +	mImplementation(new LLUpdateChecker::Implementation(client)) +{ +	; // No op. +} + + +void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl,  +							std::string const & servicePath, std::string channel, std::string version) +{ +	mImplementation->check(protocolVersion, hostUrl, servicePath, channel, version); +} + + + +// LLUpdateChecker::Implementation +//----------------------------------------------------------------------------- + + +const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.0"; + + +LLUpdateChecker::Implementation::Implementation(LLUpdateChecker::Client & client): +	mClient(client), +	mInProgress(false) +{ +	; // No op. +} + + +LLUpdateChecker::Implementation::~Implementation() +{ +	; // No op. +} + + +void LLUpdateChecker::Implementation::check(std::string const & protocolVersion, std::string const & hostUrl,  +											std::string const & servicePath, std::string channel, std::string version) +{ +	llassert(!mInProgress); +	 +	if(protocolVersion != sProtocolVersion) throw CheckError("unsupported protocol"); +		 +	mInProgress = true; +	mVersion = version; +	std::string checkUrl = buildUrl(protocolVersion, hostUrl, servicePath, channel, version); +	LL_INFOS("UpdateCheck") << "checking for updates at " << checkUrl << llendl; +	 +	// The HTTP client will wrap a raw pointer in a boost::intrusive_ptr causing the +	// passed object to be silently and automatically deleted.  We pass a self- +	// referential intrusive pointer to which we add a reference to keep the +	// client from deleting the update checker implementation instance. +	LLHTTPClient::ResponderPtr temporaryPtr(this); +	boost::intrusive_ptr_add_ref(temporaryPtr.get()); +	mHttpClient.get(checkUrl, temporaryPtr); +} + +void LLUpdateChecker::Implementation::completed(U32 status, +							  const std::string & reason, +							  const LLSD & content) +{ +	mInProgress = false;	 +	 +	if(status != 200) { +		LL_WARNS("UpdateCheck") << "html error " << status << " (" << reason << ")" << llendl; +		mClient.error(reason); +	} else if(!content.asBoolean()) { +		LL_INFOS("UpdateCheck") << "up to date" << llendl; +		mClient.upToDate(); +	} else if(content["required"].asBoolean()) { +		LL_INFOS("UpdateCheck") << "version invalid" << llendl; +		LLURI uri(content["url"].asString()); +		mClient.requiredUpdate(content["version"].asString(), uri, content["hash"].asString()); +	} else { +		LL_INFOS("UpdateCheck") << "newer version " << content["version"].asString() << " available" << llendl; +		LLURI uri(content["url"].asString()); +		mClient.optionalUpdate(content["version"].asString(), uri, content["hash"].asString()); +	} +} + + +void LLUpdateChecker::Implementation::error(U32 status, const std::string & reason) +{ +	mInProgress = false; +	LL_WARNS("UpdateCheck") << "update check failed; " << reason << llendl; +	mClient.error(reason); +} + + +std::string LLUpdateChecker::Implementation::buildUrl(std::string const & protocolVersion, std::string const & hostUrl,  +													  std::string const & servicePath, std::string channel, std::string version) +{	 +#ifdef LL_WINDOWS +	static const char * platform = "win"; +#elif LL_DARWIN +	static const char * platform = "mac"; +#else +	static const char * platform = "lnx"; +#endif +	 +	LLSD path; +	path.append(servicePath); +	path.append(protocolVersion); +	path.append(channel); +	path.append(version); +	path.append(platform); +	return LLURI::buildHTTP(hostUrl, path).asString(); +} diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h new file mode 100644 index 0000000000..cea1f13647 --- /dev/null +++ b/indra/viewer_components/updater/llupdatechecker.h @@ -0,0 +1,82 @@ +/**  + * @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 <boost/shared_ptr.hpp> + + +// +// Implements asynchronous checking for updates. +// +class LLUpdateChecker { +public: +	class Client; +	class Implementation; +	 +	// 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 check(std::string const & protocolVersion, std::string const & hostUrl,  +			   std::string const & servicePath, std::string channel, std::string version); +	 +private: +	boost::shared_ptr<Implementation> mImplementation; +}; + + +class LLURI; // From lluri.h + + +// +// The client interface implemented by a requestor checking for an update. +// +class LLUpdateChecker::Client +{ +public: +	// An error occurred while checking for an update. +	virtual void error(std::string const & message) = 0; +	 +	// A newer version is available, but the current version may still be used. +	virtual void optionalUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash) = 0; +	 +	// A newer version is available, and the current version is no longer valid.  +	virtual void requiredUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash) = 0; +	 +	// The checked version is up to date; no newer version exists. +	virtual void upToDate(void) = 0; +}; + + +#endif diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp new file mode 100644 index 0000000000..c17a50e242 --- /dev/null +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -0,0 +1,428 @@ +/**  + * @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 <stdexcept> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <curl/curl.h> +#include "lldir.h" +#include "llfile.h" +#include "llmd5.h" +#include "llsd.h" +#include "llsdserialize.h" +#include "llthread.h" +#include "llupdatedownloader.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); +	bool isDownloading(void); +	size_t onHeader(void * header, size_t size); +	size_t onBody(void * header, size_t size); +	void resume(void); +	 +private: +	bool mCancelled; +	LLUpdateDownloader::Client & mClient; +	CURL * mCurl; +	LLSD mDownloadData; +	llofstream mDownloadStream; +	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(void); + +	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) +{ +	mImplementation->download(uri, hash); +} + + +bool LLUpdateDownloader::isDownloading(void) +{ +	return mImplementation->isDownloading(); +} + + +void LLUpdateDownloader::resume(void) +{ +	mImplementation->resume(); +} + + + +// 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<LLUpdateDownloader::Implementation *>(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<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes); +	} +} + + +LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client): +	LLThread("LLUpdateDownloader"), +	mCancelled(false), +	mClient(client), +	mCurl(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. +	} +	if(mCurl) curl_easy_cleanup(mCurl); +} + + +void LLUpdateDownloader::Implementation::cancel(void) +{ +	mCancelled = true; +} +	 + +void LLUpdateDownloader::Implementation::download(LLURI const & uri, std::string const & hash) +{ +	if(isDownloading()) mClient.downloadError("download in progress"); + +	mDownloadRecordPath = downloadMarkerPath(); +	mDownloadData = LLSD(); +	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); +	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(!validateDownload()) { +				LLFile::remove(filePath); +				download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString()); +			} else { +				mClient.downloadComplete(mDownloadData); +			} +		} else { +			download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString()); +		} +	} catch(DownloadError & e) { +		mClient.downloadError(e.what()); +	} +} + + +size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size) +{ +	char const * headerPtr = reinterpret_cast<const char *> (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<size_t>(contentLength); +			LL_INFOS("UpdateDownload") << "download size is " << size << LL_ENDL; +			 +			mDownloadData["size"] = LLSD(LLSD::Integer(size)); +			llofstream odataStream(mDownloadRecordPath); +			LLSDSerialize::toPrettyXML(mDownloadData, odataStream); +		} catch (std::exception const & e) { +			LL_WARNS("UpdateDownload") << "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(reinterpret_cast<const char *>(buffer), size); +	if(mDownloadStream.bad()) { +		return 0; +	} else { +		return size; +	} +} + + +void LLUpdateDownloader::Implementation::run(void) +{ +	CURLcode code = curl_easy_perform(mCurl); +	mDownloadStream.close(); +	if(code == CURLE_OK) { +		LLFile::remove(mDownloadRecordPath); +		if(validateDownload()) { +			LL_INFOS("UpdateDownload") << "download successful" << LL_ENDL; +			mClient.downloadComplete(mDownloadData); +		} else { +			LL_INFOS("UpdateDownload") << "download failed hash check" << LL_ENDL; +			std::string filePath = mDownloadData["path"].asString(); +			if(filePath.size() != 0) LLFile::remove(filePath); +			mClient.downloadError("failed hash check"); +		} +	} else if(mCancelled && (code == CURLE_WRITE_ERROR)) { +		LL_INFOS("UpdateDownload") << "download canceled by user" << LL_ENDL; +		// Do not call back client. +	} else { +		LL_WARNS("UpdateDownload") << "download failed with error '" <<  +			curl_easy_strerror(code) << "'" << LL_ENDL; +		LLFile::remove(mDownloadRecordPath); +		if(mDownloadData.has("path")) LLFile::remove(mDownloadData["path"].asString()); +		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 == 0) { +		mCurl = curl_easy_init(); +	} else { +		curl_easy_reset(mCurl); +	} +	 +	if(mCurl == 0) throw DownloadError("failed to initialize curl"); +	 +	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, true)); +	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, true)); +	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &write_function)); +	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this)); +	if(processHeader) { +	   throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, &header_function)); +	   throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, this)); +	} +	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true)); +	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str())); +} + + +void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte) +{ +	LL_INFOS("UpdateDownload") << "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, CURLOPT_HTTPHEADER, mHeaderList)); +	 +	mDownloadStream.open(mDownloadData["path"].asString(), +						 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("UpdateDownload") << "downloading " << filePath +		<< " from " << uri.asString() << LL_ENDL; +	LL_INFOS("UpdateDownload") << "hash of file is " << hash << LL_ENDL; +		 +	llofstream dataStream(mDownloadRecordPath); +	LLSDSerialize::toPrettyXML(mDownloadData, dataStream); +	 +	mDownloadStream.open(filePath, 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::validateDownload(void) +{ +	std::string filePath = mDownloadData["path"].asString(); +	llifstream fileStream(filePath, std::ios_base::in | std::ios_base::binary); +	if(!fileStream) return false; + +	std::string hash = mDownloadData["hash"].asString(); +	if(hash.size() != 0) { +		LL_INFOS("UpdateDownload") << "checking hash..." << LL_ENDL; +		char digest[33]; +		LLMD5(fileStream).hex_digest(digest); +		if(hash != digest) { +			LL_WARNS("UpdateDownload") << "download hash mismatch; expeted " << hash << +				" but download is " << digest << LL_ENDL; +		} +		return hash == digest; +	} else { +		return true; // No hash check provided. +	} +} diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h new file mode 100644 index 0000000000..1b3d7480fd --- /dev/null +++ b/indra/viewer_components/updater/llupdatedownloader.h @@ -0,0 +1,87 @@ +/**  + * @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 <string> +#include <boost/shared_ptr.hpp> +#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); +	 +	// Returns true if a download is in progress. +	bool isDownloading(void); +	 +	// Resume a partial download. +	void resume(void); +	 +private: +	boost::shared_ptr<Implementation> 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 +	// 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 new file mode 100644 index 0000000000..6e69bcf28b --- /dev/null +++ b/indra/viewer_components/updater/llupdateinstaller.cpp @@ -0,0 +1,90 @@ +/**  + * @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 <apr_file_io.h> +#include "llapr.h" +#include "llprocesslauncher.h" +#include "llupdateinstaller.h" +#include "lldir.h" + + +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, 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"); +	} +	 +	llinfos << "UpdateInstaller: installing " << updatePath << " using " << +		actualScriptPath << LL_ENDL; +	 +	LLProcessLauncher launcher; +	launcher.setExecutable(actualScriptPath); +	launcher.addArgument(updatePath); +	launcher.addArgument(ll_install_failed_marker_path().c_str()); +	int result = launcher.launch(); +	launcher.orphan(); +	 +	return result; +} + + +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 new file mode 100644 index 0000000000..6ce08ce6fa --- /dev/null +++ b/indra/viewer_components/updater/llupdateinstaller.h @@ -0,0 +1,57 @@ +/**  + * @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 <string> + + +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. +					   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 new file mode 100644 index 0000000000..cc60eaead2 --- /dev/null +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -0,0 +1,519 @@ +/**  + * @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 "llupdatedownloader.h" +#include "llevents.h" +#include "lltimer.h" +#include "llupdaterservice.h" +#include "llupdatechecker.h" +#include "llupdateinstaller.h" +#include "llversionviewer.h" + +#include <boost/scoped_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "lldir.h" +#include "llsdserialize.h" +#include "llfile.h" + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + + +namespace  +{ +	boost::weak_ptr<LLUpdaterServiceImpl> 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"; +#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 +		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 mUrl; +	std::string mPath; +	std::string mChannel; +	std::string mVersion; +	 +	unsigned int mCheckPeriod; +	bool mIsChecking; +	bool mIsDownloading; +	 +	LLUpdateChecker mUpdateChecker; +	LLUpdateDownloader mUpdateDownloader; +	LLTimer mTimer; + +	LLUpdaterService::app_exit_callback_t mAppExitCallback; +	 +	LOG_CLASS(LLUpdaterServiceImpl); +	 +public: +	LLUpdaterServiceImpl(); +	virtual ~LLUpdaterServiceImpl(); + +	void initialize(const std::string& protocol_version, +				   const std::string& url,  +				   const std::string& path, +				   const std::string& channel, +				   const std::string& version); +	 +	void setCheckPeriod(unsigned int seconds); + +	void startChecking(bool install_if_ready); +	void stopChecking(); +	bool isChecking(); +	 +	void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;} + +	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); +	virtual void optionalUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash); +	virtual void requiredUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash); +	virtual void upToDate(void); +	 +	// LLUpdateDownloader::Client +	void downloadComplete(LLSD const & data); +	void downloadError(std::string const & message); + +	bool onMainLoop(LLSD const & event); + +private: +	void restartTimer(unsigned int seconds); +	void stopTimer(); +}; + +const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl"; + +LLUpdaterServiceImpl::LLUpdaterServiceImpl() : +	mIsChecking(false), +	mIsDownloading(false), +	mCheckPeriod(0), +	mUpdateChecker(*this), +	mUpdateDownloader(*this) +{ +} + +LLUpdaterServiceImpl::~LLUpdaterServiceImpl() +{ +	LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL; +	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); +} + +void LLUpdaterServiceImpl::initialize(const std::string& protocol_version, +									  const std::string& url,  +									  const std::string& path, +									  const std::string& channel, +									  const std::string& version) +{ +	if(mIsChecking || mIsDownloading) +	{ +		throw LLUpdaterService::UsageError("LLUpdaterService::initialize call " +										   "while updater is running."); +	} +		 +	mProtocolVersion = protocol_version; +	mUrl = url; +	mPath = path; +	mChannel = channel; +	mVersion = version; +} + +void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds) +{ +	mCheckPeriod = seconds; +} + +void LLUpdaterServiceImpl::startChecking(bool install_if_ready) +{ +	if(mUrl.empty() || 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) +		{ +			// Checking can only occur during the mainloop. +			// reset the timer to 0 so that the next mainloop event  +			// triggers a check; +			restartTimer(0);  +		} +	} +} + +void LLUpdaterServiceImpl::stopChecking() +{ +	if(mIsChecking) +	{ +		mIsChecking = false; +		stopTimer(); +	} + +    if(mIsDownloading) +    { +        mUpdateDownloader.cancel(); +		mIsDownloading = false; +    } +} + +bool LLUpdaterServiceImpl::isChecking() +{ +	return mIsChecking; +} + +bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller) +{ +	bool foundInstall = false; // return true if install is found. + +	llifstream update_marker(update_marker_path(),  +							 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. +		LLSD path = update_info.get("path"); +		if(update_info["current_version"].asString() != ll_get_version()) +		{ +			// This viewer is not the same version as the one that downloaded +			// the update.  Do not install this update. +			if(!path.asString().empty()) +			{ +				llinfos << "ignoring update dowloaded by different client version" << llendl; +				LLFile::remove(path.asString()); +				LLFile::remove(update_marker_path()); +			} +			else +			{ +				; // Nothing to clean up. +			} +			 +			foundInstall = false; +		}  +		else if(path.isDefined() && !path.asString().empty()) +		{ +			if(launchInstaller) +			{ +				LLFile::remove(update_marker_path()); + +				int result = ll_install_update(install_script_path(), +											   update_info["path"].asString(), +											   install_script_mode());	 +				 +				if((result == 0) && mAppExitCallback) +				{ +					mAppExitCallback(); +				} else if(result != 0) { +					llwarns << "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,  +								 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(); +			if(download_info["current_version"].asString() == ll_get_version()) +			{ +				mIsDownloading = true; +				mUpdateDownloader.resume(); +				result = true; +			} +			else  +			{ +				// The viewer that started this download is not the same as this viewer; ignore. +				llinfos << "ignoring partial download from different viewer version" << llendl; +				std::string path = download_info["path"].asString(); +				if(!path.empty()) LLFile::remove(path); +				LLFile::remove(download_marker_path); +			} +		}  +	} +	return result; +} + +void LLUpdaterServiceImpl::error(std::string const & message) +{ +	if(mIsChecking) +	{ +		restartTimer(mCheckPeriod); +	} +} + +void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion, +										  LLURI const & uri, +										  std::string const & hash) +{ +	stopTimer(); +	mIsDownloading = true; +	mUpdateDownloader.download(uri, hash); +} + +void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion, +										  LLURI const & uri, +										  std::string const & hash) +{ +	stopTimer(); +	mIsDownloading = true; +	mUpdateDownloader.download(uri, hash); +} + +void LLUpdaterServiceImpl::upToDate(void) +{ +	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()); +	LLSDSerialize::toPrettyXML(data, update_marker); +	 +	LLSD event; +	event["pump"] = LLUpdaterService::pumpName(); +	LLSD payload; +	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE); +	event["payload"] = payload; +	LLEventPumps::instance().obtain("mainlooprepeater").post(event); +} + +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); +} + +void LLUpdaterServiceImpl::restartTimer(unsigned int seconds) +{ +	LL_INFOS("UpdaterService") << "will check for update again in " <<  +	seconds << " seconds" << LL_ENDL;  +	mTimer.start(); +	mTimer.setTimerExpirySec(seconds); +	LLEventPumps::instance().obtain("mainloop").listen( +		sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1)); +} + +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())) +		{ +			// TODO: notify the user. +			llinfos << "found marker " << ll_install_failed_marker_path() << llendl; +			llinfos << "last install attempt failed" << llendl; +			LLFile::remove(ll_install_failed_marker_path()); +			 +			LLSD event; +			event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR); +			LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event); +		} +		else +		{ +			mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion); +		} +	}  +	else  +	{ +		// Keep on waiting... +	} +	 +	return false; +} + + +//----------------------------------------------------------------------- +// Facade interface + +std::string const & LLUpdaterService::pumpName(void) +{ +	static std::string name("updater_service"); +	return name; +} + +LLUpdaterService::LLUpdaterService() +{ +	if(gUpdater.expired()) +	{ +		mImpl =  +			boost::shared_ptr<LLUpdaterServiceImpl>(new LLUpdaterServiceImpl()); +		gUpdater = mImpl; +	} +	else +	{ +		mImpl = gUpdater.lock(); +	} +} + +LLUpdaterService::~LLUpdaterService() +{ +} + +void LLUpdaterService::initialize(const std::string& protocol_version, +								 const std::string& url,  +								 const std::string& path, +								 const std::string& channel, +								 const std::string& version) +{ +	mImpl->initialize(protocol_version, url, path, channel, version); +} + +void LLUpdaterService::setCheckPeriod(unsigned int seconds) +{ +	mImpl->setCheckPeriod(seconds); +} +	 +void LLUpdaterService::startChecking(bool install_if_ready) +{ +	mImpl->startChecking(install_if_ready); +} + +void LLUpdaterService::stopChecking() +{ +	mImpl->stopChecking(); +} + +bool LLUpdaterService::isChecking() +{ +	return mImpl->isChecking(); +} + +void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) +{ +	return mImpl->setAppExitCallback(aecb); +} + + +std::string const & ll_get_version(void) { +	static std::string version(""); +	 +	if (version.empty()) { +		std::ostringstream stream; +		stream << LL_VERSION_MAJOR << "." +		<< LL_VERSION_MINOR << "." +		<< LL_VERSION_PATCH << "." +		<< LL_VERSION_BUILD; +		version = stream.str(); +	} +	 +	return version; +} + diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h new file mode 100644 index 0000000000..752a6f834b --- /dev/null +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -0,0 +1,85 @@ +/**  + * @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 <boost/shared_ptr.hpp> +#include <boost/function.hpp> + +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); +	 +	// Type codes for events posted by this service.  Stored the event's 'type' element. +	enum eUpdateEvent { +		INVALID, +		DOWNLOAD_COMPLETE, +		DOWNLOAD_ERROR, +		INSTALL_ERROR +	}; + +	LLUpdaterService(); +	~LLUpdaterService(); + +	void initialize(const std::string& protocol_version, +				    const std::string& url,  +				    const std::string& path, +				    const std::string& channel, +				    const std::string& version); + +	void setCheckPeriod(unsigned int seconds); +	 +	void startChecking(bool install_if_ready = false); +	void stopChecking(); +	bool isChecking(); + +	typedef boost::function<void (void)> app_exit_callback_t; +	template <typename F> +	void setAppExitCallback(F const &callable)  +	{  +		app_exit_callback_t aecb = callable; +		setImplAppExitCallback(aecb); +	} + +private: +	boost::shared_ptr<LLUpdaterServiceImpl> 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/update_install b/indra/viewer_components/updater/scripts/darwin/update_install new file mode 100644 index 0000000000..b174b3570a --- /dev/null +++ b/indra/viewer_components/updater/scripts/darwin/update_install @@ -0,0 +1,9 @@ +#! /bin/bash + +# +# The first argument contains the path to the installer app.  The second a path +# to a marker file which should be created if the installer fails.q +# + +open ../Resources/mac-updater.app --args -dmg "$1" -name "Second Life Viewer 2" -marker "$2" +exit 0 diff --git a/indra/viewer_components/updater/scripts/linux/update_install b/indra/viewer_components/updater/scripts/linux/update_install new file mode 100644 index 0000000000..fef5ef7d09 --- /dev/null +++ b/indra/viewer_components/updater/scripts/linux/update_install @@ -0,0 +1,10 @@ +#! /bin/bash +INSTALL_DIR=$(cd "$(dirname $0)/.." ; pwd) +export LD_LIBRARY_PATH=$INSTALL_DIR/lib +bin/linux-updater.bin --file "$1" --dest "$INSTALL_DIR" --name "Second Life Viewer 2" --stringsdir "$INSTALL_DIR/skins/default/xui/en" --stringsfile "strings.xml" + +if [ $? -ne 0 ] +   then touch $2 +fi + +rm -f $1 diff --git a/indra/viewer_components/updater/scripts/windows/update_install.bat b/indra/viewer_components/updater/scripts/windows/update_install.bat new file mode 100644 index 0000000000..42e148a707 --- /dev/null +++ b/indra/viewer_components/updater/scripts/windows/update_install.bat @@ -0,0 +1,3 @@ +start /WAIT %1 /SKIP_DIALOGS
 +IF ERRORLEVEL 1 ECHO %ERRORLEVEL% > %2
 +DEL %1
 diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp new file mode 100644 index 0000000000..04ed4e6364 --- /dev/null +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -0,0 +1,199 @@ +/**
 + * @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::check(std::string const & protocolVersion, std::string const & hostUrl, 
 +								  std::string const & servicePath, std::string channel, std::string version)
 +{}
 +LLUpdateDownloader::LLUpdateDownloader(Client & ) {}
 +void LLUpdateDownloader::download(LLURI const & , std::string const &){}
 +
 +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;
 +	}
 +
 +	BOOL getNextFileInDir(const std::string &dirname, 
 +						  const std::string &mask, 
 +						  std::string &fname) 
 +	{
 +		return false;
 +	}
 +	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){}
 +bool LLDir::setCacheDir(const std::string &path){ return true; }
 +void LLDir::dumpCurrentDirectories() {}
 +
 +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) {}
 +
 +int ll_install_update(std::string const &, std::string const &, 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<char,std::char_traits< char > >(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_data> 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
 +		{
 +			updater.initialize("1.0",test_url, "update" ,test_channel, test_version);
 +			updater.startChecking();
 +			updater.initialize("1.0", "other_url", "update", test_channel, test_version);
 +		}
 +		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;
 +		updater.initialize("1.0", test_url, "update", test_channel, test_version);
 +		updater.startChecking();
 +		ensure(updater.isChecking());
 +		updater.stopChecking();
 +		ensure(!updater.isChecking());
 +	}
 +}
 | 
