Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make loader app load updated system image directly #3421

Merged
merged 1 commit into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
yuying-y marked this conversation as resolved.
Show resolved Hide resolved

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) {
yuying-y marked this conversation as resolved.
Show resolved Hide resolved
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) {
yuying-y marked this conversation as resolved.
Show resolved Hide resolved
if (!storage_path_implemented_) {
return;
}
std::vector<char> v1(kTestEvergreenVersion1,
kTestEvergreenVersion1 + strlen(kTestEvergreenVersion1));
std::vector<char> v2(kTestEvergreenVersion2,
yuying-y marked this conversation as resolved.
Show resolved Hide resolved
kTestEvergreenVersion2 + strlen(kTestEvergreenVersion2));
std::vector<char> v3(kMaxEgVersionLength);
ASSERT_EQ(0, CompareEvergreenVersion(&v1, &v3));
yuying-y marked this conversation as resolved.
Show resolved Hide resolved
ASSERT_EQ(0, CompareEvergreenVersion(&v1, &v1));
ASSERT_EQ(-1, CompareEvergreenVersion(&v1, &v2));
v3.assign(kTestEvergreenVersion3,
kTestEvergreenVersion3 + strlen(kTestEvergreenVersion3));
ASSERT_EQ(1, CompareEvergreenVersion(&v3, &v2))
yuying-y marked this conversation as resolved.
Show resolved Hide resolved
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
Loading