| // Copyright 2020 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/loader_app/slot_management.h" |
| |
| #include <vector> |
| |
| #include "starboard/common/log.h" |
| #include "starboard/common/string.h" |
| #include "starboard/configuration_constants.h" |
| #include "starboard/elf_loader/elf_loader_constants.h" |
| #include "starboard/elf_loader/sabi_string.h" |
| #include "starboard/event.h" |
| #include "starboard/file.h" |
| #include "starboard/loader_app/app_key_files.h" |
| #include "starboard/loader_app/drain_file.h" |
| #include "starboard/loader_app/installation_manager.h" |
| #include "starboard/memory.h" |
| #include "starboard/string.h" |
| #include "third_party/crashpad/wrapper/wrapper.h" |
| |
| namespace starboard { |
| namespace loader_app { |
| namespace { |
| |
| // The max number of installations slots. |
| const int kMaxNumInstallations = 3; |
| |
| // Relative path for the Cobalt so file. |
| const char kCobaltLibraryPath[] = "lib"; |
| |
| // Filename for the Cobalt binary. |
| const char kCobaltLibraryName[] = "libcobalt.so"; |
| |
| // Filename for the compressed Cobalt binary. |
| const char kCompressedCobaltLibraryName[] = "libcobalt.lz4"; |
| |
| // Relative path for the content directory of |
| // the Cobalt installation. |
| const char kCobaltContentPath[] = "content"; |
| |
| } // namespace |
| |
| int RevertBack(int current_installation, |
| const std::string& app_key, |
| bool mark_bad) { |
| SB_LOG(INFO) << "RevertBack current_installation=" << current_installation; |
| SB_DCHECK(current_installation != 0); |
| if (mark_bad) { |
| std::vector<char> installation_path(kSbFileMaxPath); |
| if (ImGetInstallationPath(current_installation, installation_path.data(), |
| kSbFileMaxPath) != IM_ERROR) { |
| std::string bad_app_key_file_path = |
| starboard::loader_app::GetBadAppKeyFilePath(installation_path.data(), |
| app_key); |
| if (bad_app_key_file_path.empty()) { |
| SB_LOG(WARNING) << "Failed to get bad app key file path for path=" |
| << installation_path.data() |
| << " and app_key=" << app_key; |
| } else { |
| if (!starboard::loader_app::CreateAppKeyFile(bad_app_key_file_path)) { |
| SB_LOG(WARNING) << "Failed to create bad app key file: " |
| << bad_app_key_file_path; |
| } |
| } |
| } else { |
| SB_LOG(WARNING) << "Failed to get installation path for index: " |
| << current_installation; |
| } |
| } |
| current_installation = ImRevertToSuccessfulInstallation(); |
| return current_installation; |
| } |
| |
| bool CheckBadFileExists(const char* installation_path, const char* app_key) { |
| std::string bad_app_key_file_path = |
| starboard::loader_app::GetBadAppKeyFilePath(installation_path, app_key); |
| SB_DCHECK(!bad_app_key_file_path.empty()); |
| SB_LOG(INFO) << "bad_app_key_file_path: " << bad_app_key_file_path; |
| SB_LOG(INFO) << "bad_app_key_file_path SbFileExists: " |
| << SbFileExists(bad_app_key_file_path.c_str()); |
| return !bad_app_key_file_path.empty() && |
| SbFileExists(bad_app_key_file_path.c_str()); |
| } |
| |
| bool AdoptInstallation(int current_installation, |
| const char* installation_path, |
| const char* app_key) { |
| SB_LOG(INFO) << "AdoptInstallation: current_installation=" |
| << current_installation |
| << " installation_path=" << installation_path |
| << " app_key=" << app_key; |
| // Check that a good file exists from at least one app before adopting. |
| if (!AnyGoodAppKeyFile(installation_path)) { |
| SB_LOG(ERROR) << "No good files present"; |
| return false; |
| } |
| std::string good_app_key_file_path = |
| starboard::loader_app::GetGoodAppKeyFilePath(installation_path, app_key); |
| if (good_app_key_file_path.empty()) { |
| SB_LOG(WARNING) << "Failed to get good app key file path for app_key=" |
| << app_key; |
| return false; |
| } |
| |
| if (!SbFileExists(good_app_key_file_path.c_str())) { |
| if (!starboard::loader_app::CreateAppKeyFile(good_app_key_file_path)) { |
| SB_LOG(WARNING) << "Failed to create good app key file"; |
| return false; |
| } |
| if (ImResetInstallation(current_installation) == IM_ERROR) { |
| return false; |
| } |
| if (ImRollForward(current_installation) == IM_ERROR) { |
| SB_LOG(WARNING) << "Failed to roll forward"; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void* LoadSlotManagedLibrary(const std::string& app_key, |
| const std::string& alternative_content_path, |
| LibraryLoader* library_loader, |
| bool use_compression, |
| bool use_memory_mapped_file) { |
| if (use_compression && use_memory_mapped_file) { |
| SB_LOG(ERROR) << "Using both compression and mmap files is not supported"; |
| return NULL; |
| } |
| // Initialize the Installation Manager. |
| SB_CHECK(ImInitialize(kMaxNumInstallations, app_key.c_str()) == IM_SUCCESS) |
| << "Abort. Failed to initialize Installation Manager"; |
| |
| // Roll forward if needed. |
| if (ImRollForwardIfNeeded() == IM_ERROR) { |
| SB_LOG(WARNING) << "Failed to roll forward"; |
| } |
| |
| // TODO: Try to simplify the loop. |
| // Loop by priority. |
| int current_installation = ImGetCurrentInstallationIndex(); |
| while (current_installation != IM_ERROR) { |
| // if not successful and num_tries_left > 0 decrement and try to |
| // load the library. |
| if (ImGetInstallationStatus(current_installation) != |
| IM_INSTALLATION_STATUS_SUCCESS) { |
| int num_tries_left = ImGetInstallationNumTriesLeft(current_installation); |
| if (num_tries_left == IM_ERROR || num_tries_left <= 0 || |
| ImDecrementInstallationNumTries(current_installation) == IM_ERROR) { |
| SB_LOG(INFO) << "Out of retries"; |
| // If no more tries are left or if we have hard failure, |
| // discard the image and auto rollback, but only if |
| // the current image is not the system image. |
| if (current_installation != 0) { |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| } |
| } |
| } |
| |
| SB_LOG(INFO) << "Try to load the Cobalt binary"; |
| SB_LOG(INFO) << "current_installation=" << current_installation; |
| |
| // Try to load the image. Failures here discard the image. |
| std::vector<char> installation_path(kSbFileMaxPath); |
| if (ImGetInstallationPath(current_installation, installation_path.data(), |
| kSbFileMaxPath) == IM_ERROR) { |
| SB_LOG(ERROR) << "Failed to find library file"; |
| |
| // Hard failure. Discard the image and auto rollback, but only if |
| // the current image is not the system image. |
| if (current_installation != 0) { |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| continue; |
| } else { |
| // The system image at index 0 failed. |
| return NULL; |
| } |
| } |
| |
| SB_DLOG(INFO) << "installation_path=" << installation_path.data(); |
| |
| if (current_installation != 0) { |
| // Cleanup all expired files from all apps. |
| DrainFileClearExpired(installation_path.data()); |
| |
| // Cleanup all drain files from the current app. |
| DrainFileClearForApp(installation_path.data(), app_key.c_str()); |
| |
| // Check for bad file. |
| if (CheckBadFileExists(installation_path.data(), app_key.c_str())) { |
| SB_LOG(INFO) << "Bad app key file"; |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| continue; |
| } |
| // If the current installation is in use by an updater roll back. |
| if (DrainFileIsAnotherAppDraining(installation_path.data(), |
| app_key.c_str())) { |
| SB_LOG(INFO) << "Active slot draining"; |
| current_installation = |
| RevertBack(current_installation, app_key, false /* mark_bad */); |
| continue; |
| } |
| // Adopt installation performed from different app. |
| if (!AdoptInstallation(current_installation, installation_path.data(), |
| app_key.c_str())) { |
| SB_LOG(INFO) << "Unable to adopt installation"; |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| continue; |
| } |
| } |
| |
| // installation_n/lib/libcobalt.so |
| std::vector<char> lib_path(kSbFileMaxPath); |
| std::string library_name; |
| if (use_compression) { |
| library_name = kCompressedCobaltLibraryName; |
| } else { |
| library_name = kCobaltLibraryName; |
| } |
| SbStringFormatF(lib_path.data(), kSbFileMaxPath, "%s%s%s%s%s", |
| installation_path.data(), kSbFileSepString, |
| kCobaltLibraryPath, kSbFileSepString, library_name.c_str()); |
| |
| SB_LOG(INFO) << "lib_path=" << lib_path.data(); |
| |
| std::string content; |
| if (alternative_content_path.empty()) { |
| // installation_n/content |
| std::vector<char> content_path(kSbFileMaxPath); |
| SbStringFormatF(content_path.data(), kSbFileMaxPath, "%s%s%s", |
| installation_path.data(), kSbFileSepString, |
| kCobaltContentPath); |
| content = content_path.data(); |
| } else { |
| content = alternative_content_path.c_str(); |
| } |
| |
| SB_LOG(INFO) << "content=" << content; |
| |
| if (!library_loader->Load(lib_path.data(), content.c_str(), use_compression, |
| use_memory_mapped_file)) { |
| SB_LOG(WARNING) << "Failed to load Cobalt!"; |
| |
| // Hard failure. Discard the image and auto rollback, but only if |
| // the current image is not the system image. |
| if (current_installation != 0) { |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| continue; |
| } else { |
| // The system image at index 0 failed. |
| return NULL; |
| } |
| } |
| |
| EvergreenInfo evergreen_info; |
| GetEvergreenInfo(&evergreen_info); |
| if (!third_party::crashpad::wrapper::AddEvergreenInfoToCrashpad( |
| evergreen_info)) { |
| SB_LOG(ERROR) |
| << "Could not send Cobalt library information into Crashapd."; |
| } else { |
| SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad."; |
| } |
| |
| auto get_evergreen_sabi_string_func = reinterpret_cast<const char* (*)()>( |
| library_loader->Resolve("GetEvergreenSabiString")); |
| |
| if (!CheckSabi(get_evergreen_sabi_string_func)) { |
| SB_LOG(ERROR) << "CheckSabi failed"; |
| // Hard failure. Discard the image and auto rollback, but only if |
| // the current image is not the system image. |
| if (current_installation != 0) { |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| continue; |
| } else { |
| // The system image at index 0 failed. |
| return NULL; |
| } |
| } |
| |
| auto get_user_agent_func = reinterpret_cast<const char* (*)()>( |
| library_loader->Resolve("GetCobaltUserAgentString")); |
| if (!get_user_agent_func) { |
| SB_LOG(ERROR) << "Failed to get user agent string"; |
| } else { |
| CrashpadAnnotations cobalt_version_info; |
| memset(&cobalt_version_info, 0, sizeof(CrashpadAnnotations)); |
| starboard::strlcpy(cobalt_version_info.user_agent_string, |
| get_user_agent_func(), USER_AGENT_STRING_MAX_SIZE); |
| third_party::crashpad::wrapper::AddAnnotationsToCrashpad( |
| cobalt_version_info); |
| SB_DLOG(INFO) << "Added user agent string to Crashpad."; |
| } |
| |
| SB_DLOG(INFO) << "Successfully loaded Cobalt!\n"; |
| void* p = library_loader->Resolve("SbEventHandle"); |
| if (p != NULL) { |
| SB_DLOG(INFO) << "Symbol Lookup succeeded address: " << p; |
| return p; |
| } else { |
| SB_LOG(ERROR) << "Symbol Lookup failed\n"; |
| |
| // Hard failure. Discard the image and auto rollback, but only if |
| // the current image is not the system image. |
| if (current_installation != 0) { |
| current_installation = |
| RevertBack(current_installation, app_key, true /* mark_bad */); |
| continue; |
| } else { |
| // The system image at index 0 failed. |
| return NULL; |
| } |
| } |
| } |
| return NULL; |
| } |
| |
| } // namespace loader_app |
| } // namespace starboard |