Skip to content

Commit

Permalink
Make loader app load updated system image directly
Browse files Browse the repository at this point in the history
b/344069914
b/328421395

Change-Id: I66734aed7b52bac4b60e823df4873c5dccc8c0f2
  • Loading branch information
yuying-y committed Jun 13, 2024
1 parent 515d982 commit 1cef9b3
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 1 deletion.
1 change: 1 addition & 0 deletions starboard/loader_app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ static_library("slot_management") {
"//starboard/elf_loader",
"//starboard/elf_loader:constants",
"//starboard/elf_loader:sabi_string",
"//third_party/jsoncpp",
]

if (sb_is_evergreen_compatible && current_toolchain == starboard_toolchain) {
Expand Down
126 changes: 125 additions & 1 deletion starboard/loader_app/slot_management.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@

#include "starboard/loader_app/slot_management.h"

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

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

#include "starboard/common/log.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,95 @@ 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 ss1(std::string(v1->begin(), v1->end()));
std::stringstream ss2(std::string(v2->begin(), v2->end()));
std::string seg;
while (std::getline(ss1, seg, kEgVersionDeliminator)) {
n1.push_back(std::stoi(seg));
}
while (std::getline(ss2, 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 of the installation at the |installation_index|,
// and stores in |version|.
bool ReadEvergreenVersion(int installation_index,
char* version,
int version_length) {
// Check the manifest file exists
std::vector<char> installation_path(kSbFileMaxPath);
if (ImGetInstallationPath(installation_index, installation_path.data(),
kSbFileMaxPath) == IM_ERROR) {
SB_LOG(WARNING) << "Failed to get installation path.";
return false;
}
std::vector<char> manifest_file_path(kSbFileMaxPath);
snprintf(manifest_file_path.data(), kSbFileMaxPath, "%s%s%s",
installation_path.data(), kSbFileSepString, kManifestFileName);

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;
}

std::ifstream manifest_file(std::string(manifest_file_path.data()));
Json::Reader reader;
Json::Value obj;

if (!reader.parse(manifest_file, obj) || !obj[kVersionKey]) {
SB_LOG(WARNING) << "Failed to parse version from the manifest file at the "
"installation path.";
manifest_file.close();
return false;
}

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

int RevertBack(int current_installation,
const std::string& app_key,
bool mark_bad,
Expand Down Expand Up @@ -148,9 +246,35 @@ 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 (!ReadEvergreenVersion(current_installation, current_version.data(),
kMaxEgVersionLength)) {
SB_LOG(WARNING)
<< "Failed to read the Evergreen version of the current installation";
}
std::vector<char> system_image_version(kMaxEgVersionLength);
if (!ReadEvergreenVersion(0, system_image_version.data(),
kMaxEgVersionLength)) {
SB_LOG(WARNING)
<< "Failed to read the Evergreen version of the system image";
}

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
85 changes: 85 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,11 @@

#include "starboard/loader_app/slot_management.h"

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

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

Expand All @@ -31,6 +34,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 +46,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 +490,69 @@ 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);
ASSERT_FALSE(
ReadEvergreenVersion(-1, current_version.data(), 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> manifest_file_path(kSbFileMaxPath);
snprintf(manifest_file_path.data(), kSbFileMaxPath, "%s%s%s",
installation_path.data(), kSbFileSepString, kManifestFileName);
std::ofstream manifest_file(std::string(manifest_file_path.data()));
writer.write(manifest_file, root);
ASSERT_FALSE(ReadEvergreenVersion(kTestSlotIndex, current_version.data(),
kMaxEgVersionLength));

Json::Value evergreen_version;
evergreen_version[kVersionKey] = kTestEvergreenVersion2;
root.append(evergreen_version);
writer.write(manifest_file, root);
ASSERT_TRUE(ReadEvergreenVersion(kTestSlotIndex, current_version.data(),
kMaxEgVersionLength));
ASSERT_EQ(kTestEvergreenVersion2, current_version.data());

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

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

0 comments on commit 1cef9b3

Please sign in to comment.