Skip to content

Commit

Permalink
Cherry pick PR #3421: Make loader app load updated system image direc…
Browse files Browse the repository at this point in the history
…tly (#3859)

Refer to the original PR: #3421

b/344069914
b/328421395

Co-authored-by: yuying-y <[email protected]>
  • Loading branch information
cobalt-github-releaser-bot and yuying-y committed Jul 23, 2024
1 parent 319ec52 commit 5a088e7
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 1 deletion.
5 changes: 5 additions & 0 deletions starboard/loader_app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,16 @@ static_library("slot_management") {
"//starboard/elf_loader",
"//starboard/elf_loader:constants",
"//starboard/elf_loader:sabi_string",
"//third_party/jsoncpp:jsoncpp",
]

if (sb_is_evergreen_compatible && current_toolchain == starboard_toolchain) {
deps += [ "//third_party/crashpad/crashpad/wrapper" ]
} else {
deps += [ "//third_party/crashpad/crashpad/wrapper:wrapper_stub" ]
}

configs += [ "//third_party/jsoncpp:jsoncpp_config" ]
}
if (sb_is_evergreen) {
target(starboard_level_gtest_target_type, "slot_management_test") {
Expand All @@ -320,7 +323,9 @@ if (sb_is_evergreen) {
"//starboard/elf_loader:sabi_string",
"//testing/gmock",
"//testing/gtest",
"//third_party/jsoncpp:jsoncpp",
]
configs += [ "//third_party/jsoncpp:jsoncpp_config" ]
}
}

Expand Down
147 changes: 146 additions & 1 deletion starboard/loader_app/slot_management.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@

#include "starboard/loader_app/slot_management.h"

#include <stdio.h>
#include <sys/stat.h>

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/common/string.h"
#include "starboard/configuration_constants.h"
Expand All @@ -33,11 +39,16 @@
#include "starboard/string.h"
#include "third_party/crashpad/crashpad/wrapper/annotations.h"
#include "third_party/crashpad/crashpad/wrapper/wrapper.h"
#include "third_party/jsoncpp/source/include/json/reader.h"
#include "third_party/jsoncpp/source/include/json/value.h"

namespace starboard {
namespace loader_app {
namespace {

// The max length of Evergreen version string.
const int kMaxEgVersionLength = 20;

// The max number of installations slots.
const int kMaxNumInstallations = 3;

Expand All @@ -54,8 +65,113 @@ const char kCompressedCobaltLibraryName[] = "libcobalt.lz4";
// the Cobalt installation.
const char kCobaltContentPath[] = "content";

// Filename for the manifest file which contains the Evergreen version.
const char kManifestFileName[] = "manifest.json";

// Deliminator of the Evergreen version string segments.
const char kEgVersionDeliminator = '.';

// Evergreen version key in the manifest file.
const char kVersionKey[] = "version";

} // namespace

// Compares the Evergreen versions v1 and v2. Returns 1 if v1 is newer than v2;
// returns -1 if v1 is older than v2; returns 0 if v1 is the same as v2, or if
// either of them is invalid.
int CompareEvergreenVersion(std::vector<char>* v1, std::vector<char>* v2) {
if ((*v1)[0] == '\0' || (*v2)[0] == '\0') {
return 0;
}

// Split the version strings into segments of numbers
std::vector<int> n1, n2;
std::stringstream s1(std::string(v1->begin(), v1->end()));
std::stringstream s2(std::string(v2->begin(), v2->end()));
std::string seg;
while (std::getline(s1, seg, kEgVersionDeliminator)) {
n1.push_back(std::stoi(seg));
}
while (std::getline(s2, seg, kEgVersionDeliminator)) {
n2.push_back(std::stoi(seg));
}

// Compare each segment
int size = std::min(n1.size(), n2.size());
for (int i = 0; i < size; i++) {
if (n1[i] > n2[i]) {
return 1;
} else if (n1[i] < n2[i]) {
return -1;
}
}

// If all segments are equal, compare the lengths
if (n1.size() > n2.size()) {
return 1;
} else if (n1.size() < n2.size()) {
return -1;
}
return 0;
}

// Reads the Evergreen version from the manifest file at the
// |manifest_file_path|, and stores in |version|.
bool ReadEvergreenVersion(std::vector<char>* manifest_file_path,
char* version,
int version_length) {
// Check the manifest file exists
struct stat info;
if (stat(manifest_file_path->data(), &info) != 0) {
SB_LOG(WARNING)
<< "Failed to open the manifest file at the installation path.";
return false;
}

ScopedFile manifest_file(manifest_file_path->data(), O_RDONLY,
S_IRWXU | S_IRGRP);
int64_t file_size = manifest_file.GetSize();
std::vector<char> file_data(file_size);
int read_size = manifest_file.ReadAll(file_data.data(), file_size);
if (read_size < 0) {
SB_LOG(WARNING) << "Error while reading from the manifest file.";
return false;
}

Json::Reader reader;
Json::Value obj;
if (!reader.parse(std::string(file_data.data()), obj) || !obj[kVersionKey]) {
SB_LOG(WARNING) << "Failed to parse version from the manifest file at the "
"installation path.";
return false;
}

snprintf(version, version_length, "%s", obj[kVersionKey].asString().c_str());
return true;
}

bool GetEvergreenVersionByIndex(int installation_index,
char* version,
int version_length) {
std::vector<char> installation_path(kSbFileMaxPath);
if (ImGetInstallationPath(installation_index, installation_path.data(),
kSbFileMaxPath) == IM_ERROR) {
SB_LOG(ERROR) << "Failed to get installation path of installation index "
<< installation_index;
return false;
}
std::vector<char> manifest_file_path(kSbFileMaxPath);
snprintf(manifest_file_path.data(), kSbFileMaxPath, "%s%s%s",
installation_path.data(), kSbFileSepString, kManifestFileName);
if (!ReadEvergreenVersion(&manifest_file_path, version, version_length)) {
SB_LOG(WARNING)
<< "Failed to read the Evergreen version of installation index "
<< installation_index;
return false;
}
return true;
}

int RevertBack(int current_installation,
const std::string& app_key,
bool mark_bad,
Expand Down Expand Up @@ -148,9 +264,38 @@ void* LoadSlotManagedLibrary(const std::string& app_key,
SB_LOG(WARNING) << "Failed to roll forward";
}

int current_installation = ImGetCurrentInstallationIndex();

// Check the system image. If it's newer than the current slot, update to
// system image immediately.
if (current_installation != 0) {
std::vector<char> current_version(kMaxEgVersionLength);
if (!GetEvergreenVersionByIndex(current_installation,
current_version.data(),
kMaxEgVersionLength)) {
SB_LOG(WARNING)
<< "Failed to get the Evergreen version of installation index "
<< current_installation;
}

std::vector<char> system_image_version(kMaxEgVersionLength);
if (!GetEvergreenVersionByIndex(0, system_image_version.data(),
kMaxEgVersionLength)) {
SB_LOG(WARNING)
<< "Failed to get the Evergreen version of installation index " << 0;
}

if (CompareEvergreenVersion(&system_image_version, &current_version) > 0) {
if (ImRollForward(0) != IM_ERROR) {
current_installation = 0;
} else {
SB_LOG(WARNING) << "Failed to roll forward to system image";
}
}
}

// 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.
Expand Down
96 changes: 96 additions & 0 deletions starboard/loader_app/slot_management_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

#include "starboard/loader_app/slot_management.h"

#include <stdio.h>
#include <sys/stat.h>

#include <iostream>
#include <string>
#include <vector>

Expand All @@ -31,6 +33,9 @@
#include "starboard/loader_app/installation_manager.h"
#include "starboard/loader_app/installation_store.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/jsoncpp/source/include/json/reader.h"
#include "third_party/jsoncpp/source/include/json/value.h"
#include "third_party/jsoncpp/source/include/json/writer.h"

#if SB_IS(EVERGREEN_COMPATIBLE)

Expand All @@ -40,6 +45,22 @@ namespace {

const char kTestAppKey[] = "1234";
const char kTestApp2Key[] = "ABCD";
const char kTestEvergreenVersion1[] = "1.2";
const char kTestEvergreenVersion2[] = "1.2.1";
const char kTestEvergreenVersion3[] = "1.2.3";
const char kTestEvergreenVersion4[] = "2.2.3";
const kTestSlotIndex = 0;
// The max length of Evergreen version string.
const int kMaxEgVersionLength = 20;

// Filename for the manifest file which contains the Evergreen version.
const char kManifestFileName[] = "manifest.json";

// Deliminator of the Evergreen version string segments.
const char kEgVersionDeliminator = '.';

// Evergreen version key in the manifest file.
const char kVersionKey[] = "version";

void SbEventFake(const SbEvent*) {}

Expand Down Expand Up @@ -468,6 +489,81 @@ TEST_P(SlotManagementTest, BadSabi) {
SbFileDeleteRecursive(good_path.c_str(), false);
}

TEST_P(SlotManagementTest, CompareEvergreenVersion) {
if (!storage_path_implemented_) {
return;
}
std::vector<char> v1(kTestEvergreenVersion1,
kTestEvergreenVersion1 + strlen(kTestEvergreenVersion1));
std::vector<char> v2(kTestEvergreenVersion2,
kTestEvergreenVersion2 + strlen(kTestEvergreenVersion2));
std::vector<char> v3(kMaxEgVersionLength);
ASSERT_EQ(0, CompareEvergreenVersion(&v1, &v3));
ASSERT_EQ(0, CompareEvergreenVersion(&v1, &v1));
ASSERT_EQ(-1, CompareEvergreenVersion(&v1, &v2));
v3.assign(kTestEvergreenVersion3,
kTestEvergreenVersion3 + strlen(kTestEvergreenVersion3));
ASSERT_EQ(1, CompareEvergreenVersion(&v3, &v2))
std::vector<char> v4(kTestEvergreenVersion4,
kTestEvergreenVersion4 + strlen(kTestEvergreenVersion4));
ASSERT_EQ(1, CompareEvergreenVersion(&v4, &v3));
}

TEST_P(SlotManagementTest, ReadEvergreenVersion) {
if (!storage_path_implemented_) {
return;
}
ImInitialize(3, kTestAppKey);
ImReset();

std::vector<char> current_version(kMaxEgVersionLength);
Json::Value root;
Json::Value manifest_version;
manifest_version["manifest_version"] = 2;
root.append(manifest_version);
Json::StyledStreamWriter writer;

std::vector<char> installation_path(kSbFileMaxPath);
if (ImGetInstallationPath(kTestSlotIndex, installation_path.data(),
kSbFileMaxPath) == IM_ERROR) {
SB_LOG(WARNING) << "Failed to get installation path.";
return false;
}
std::vector<char> test_dir_path(kSbFileMaxPath);
snprintf(test_dir_path.data(), kSbFileMaxPath, "%s%s%s",
installation_path.data(), kSbFileSepString, "test_dir", );
std::vector<char> manifest_file_path(kSbFileMaxPath);
snprintf(manifest_file_path.data(), kSbFileMaxPath, "%s%s%s",
test_dir_path.data(), kSbFileSepString, kManifestFileName);

ScopedFile manifest_file(manifest_file_path.data(), O_RDWR | O_CREAT,
S_IRWXU | S_IRWXG);
std::stringstream manifest_file_s1();
writer.write(manifest_file_s1, root);
std::string manifest_file_str1 = manifest_file_s1.str();
manifest_file.WriteAll(manifest_file_str1.c_str(),
manifest_file_str1.length());

ASSERT_FALSE(ReadEvergreenVersion(&manifest_file_path, current_version.data(),
kMaxEgVersionLength));

Json::Value evergreen_version;
evergreen_version[kVersionKey] = kTestEvergreenVersion2;
root.append(evergreen_version);
std::stringstream manifest_file_s2();
writer.write(manifest_file_s2, root);
std::string manifest_file_str2 = manifest_file_s2.str();
manifest_file.WriteAll(manifest_file_str2.c_str(),
manifest_file_str2.length());

ASSERT_TRUE(ReadEvergreenVersion(&manifest_file_path, current_version.data(),
kMaxEgVersionLength));
ASSERT_EQ(kTestEvergreenVersion2, current_version.data());

ImUninitialize();
SbFileDeleteRecursive(test_dir_path.data(), false);
}

INSTANTIATE_TEST_CASE_P(SlotManagementTests,
SlotManagementTest,
::testing::Bool());
Expand Down

0 comments on commit 5a088e7

Please sign in to comment.