updater: flush log after each write
This commit is contained in:
parent
804b94ee9e
commit
de0bc06856
|
@ -140,8 +140,7 @@ void UI::Init()
|
|||
}
|
||||
|
||||
bool Platform::VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
||||
const std::string& install_base_path, const std::string& temp_dir,
|
||||
FILE* log_fp)
|
||||
const std::string& install_base_path, const std::string& temp_dir)
|
||||
{
|
||||
const auto op_it = std::find_if(to_update.cbegin(), to_update.cend(), [&](const auto& op) {
|
||||
return op.filename == "Dolphin.app/Contents/Info.plist";
|
||||
|
@ -155,7 +154,7 @@ bool Platform::VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
NSData* data = [NSData dataWithContentsOfFile:[NSString stringWithCString:plist_path.c_str()]];
|
||||
if (!data)
|
||||
{
|
||||
fprintf(log_fp, "Failed to read %s, skipping platform version check.\n", plist_path.c_str());
|
||||
LogToFile("Failed to read %s, skipping platform version check.\n", plist_path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -167,13 +166,13 @@ bool Platform::VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
error:&error];
|
||||
if (error)
|
||||
{
|
||||
fprintf(log_fp, "Failed to parse %s, skipping platform version check.\n", plist_path.c_str());
|
||||
LogToFile("Failed to parse %s, skipping platform version check.\n", plist_path.c_str());
|
||||
return true;
|
||||
}
|
||||
NSString* min_version_str = info_dict[@"LSMinimumSystemVersion"];
|
||||
if (!min_version_str)
|
||||
{
|
||||
fprintf(log_fp, "LSMinimumSystemVersion key missing, skipping platform version check.\n");
|
||||
LogToFile("LSMinimumSystemVersion key missing, skipping platform version check.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -181,9 +180,8 @@ bool Platform::VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
NSOperatingSystemVersion next_version{
|
||||
[components[0] integerValue], [components[1] integerValue], [components[2] integerValue]};
|
||||
|
||||
fprintf(log_fp, "Platform version check: next_version=%ld.%ld.%ld\n",
|
||||
(long)next_version.majorVersion, (long)next_version.minorVersion,
|
||||
(long)next_version.patchVersion);
|
||||
LogToFile("Platform version check: next_version=%ld.%ld.%ld\n", (long)next_version.majorVersion,
|
||||
(long)next_version.minorVersion, (long)next_version.patchVersion);
|
||||
|
||||
if (![[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:next_version])
|
||||
{
|
||||
|
|
|
@ -15,5 +15,5 @@
|
|||
namespace Platform
|
||||
{
|
||||
bool VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
||||
const std::string& install_base_path, const std::string& temp_dir, FILE* log_fp);
|
||||
const std::string& install_base_path, const std::string& temp_dir);
|
||||
} // namespace Platform
|
||||
|
|
|
@ -34,14 +34,25 @@
|
|||
|
||||
// Refer to docs/autoupdate_overview.md for a detailed overview of the autoupdate process
|
||||
|
||||
// Where to log updater output.
|
||||
static FILE* log_fp = stderr;
|
||||
|
||||
// Public key used to verify update manifests.
|
||||
const std::array<u8, 32> UPDATE_PUB_KEY = {
|
||||
0x2a, 0xb3, 0xd1, 0xdc, 0x6e, 0xf5, 0x07, 0xf6, 0xa0, 0x6c, 0x7c, 0x54, 0xdf, 0x54, 0xf4, 0x42,
|
||||
0x80, 0xa6, 0x28, 0x8b, 0x6d, 0x70, 0x14, 0xb5, 0x4c, 0x34, 0x95, 0x20, 0x4d, 0xd4, 0xd3, 0x5d};
|
||||
|
||||
// Where to log updater output.
|
||||
static FILE* log_fp = stderr;
|
||||
|
||||
void LogToFile(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
||||
vfprintf(log_fp, fmt, args);
|
||||
fflush(log_fp);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
bool ProgressCallback(double total, double now, double, double)
|
||||
{
|
||||
UI::SetCurrentProgress(static_cast<int>(now), static_cast<int>(total));
|
||||
|
@ -120,7 +131,7 @@ std::optional<std::string> GzipInflate(const std::string& data)
|
|||
|
||||
if (ret != Z_STREAM_END)
|
||||
{
|
||||
fprintf(log_fp, "Could not read the data as gzip: error %d.\n", ret);
|
||||
LogToFile("Could not read the data as gzip: error %d.\n", ret);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -148,7 +159,7 @@ bool VerifySignature(const std::string& data, const std::string& b64_signature)
|
|||
b64_signature.size()) ||
|
||||
sig_size != sizeof(signature))
|
||||
{
|
||||
fprintf(log_fp, "Invalid base64: %s\n", b64_signature.c_str());
|
||||
LogToFile("Invalid base64: %s\n", b64_signature.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -166,22 +177,22 @@ void TodoList::Log() const
|
|||
{
|
||||
if (to_update.size())
|
||||
{
|
||||
fprintf(log_fp, "Updating:\n");
|
||||
LogToFile("Updating:\n");
|
||||
for (const auto& op : to_update)
|
||||
{
|
||||
std::string old_desc =
|
||||
op.old_hash ? HexEncode(op.old_hash->data(), op.old_hash->size()) : "(new)";
|
||||
fprintf(log_fp, " - %s: %s -> %s\n", op.filename.c_str(), old_desc.c_str(),
|
||||
HexEncode(op.new_hash.data(), op.new_hash.size()).c_str());
|
||||
LogToFile(" - %s: %s -> %s\n", op.filename.c_str(), old_desc.c_str(),
|
||||
HexEncode(op.new_hash.data(), op.new_hash.size()).c_str());
|
||||
}
|
||||
}
|
||||
if (to_delete.size())
|
||||
{
|
||||
fprintf(log_fp, "Deleting:\n");
|
||||
LogToFile("Deleting:\n");
|
||||
for (const auto& op : to_delete)
|
||||
{
|
||||
fprintf(log_fp, " - %s (%s)\n", op.filename.c_str(),
|
||||
HexEncode(op.old_hash.data(), op.old_hash.size()).c_str());
|
||||
LogToFile(" - %s (%s)\n", op.filename.c_str(),
|
||||
HexEncode(op.old_hash.data(), op.old_hash.size()).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +226,7 @@ bool DownloadContent(const std::vector<TodoList::DownloadOp>& to_download,
|
|||
content_store_path.insert(2, "/");
|
||||
|
||||
std::string url = content_base_url + content_store_path;
|
||||
fprintf(log_fp, "Downloading %s ...\n", url.c_str());
|
||||
LogToFile("Downloading %s ...\n", url.c_str());
|
||||
|
||||
auto resp = req.Get(url);
|
||||
if (!resp)
|
||||
|
@ -234,14 +245,14 @@ bool DownloadContent(const std::vector<TodoList::DownloadOp>& to_download,
|
|||
Manifest::Hash contents_hash = ComputeHash(decompressed);
|
||||
if (contents_hash != download.hash)
|
||||
{
|
||||
fprintf(log_fp, "Wrong hash on downloaded content %s.\n", url.c_str());
|
||||
LogToFile("Wrong hash on downloaded content %s.\n", url.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string out = temp_path + DIR_SEP + hash_filename;
|
||||
if (!File::WriteStringToFile(out, decompressed))
|
||||
{
|
||||
fprintf(log_fp, "Could not write cache file %s.\n", out.c_str());
|
||||
LogToFile("Could not write cache file %s.\n", out.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +263,7 @@ bool PlatformVersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
const std::string& install_base_path, const std::string& temp_dir)
|
||||
{
|
||||
UI::SetDescription("Checking platform...");
|
||||
return Platform::VersionCheck(to_update, install_base_path, temp_dir, log_fp);
|
||||
return Platform::VersionCheck(to_update, install_base_path, temp_dir);
|
||||
}
|
||||
|
||||
TodoList ComputeActionsToDo(Manifest this_manifest, Manifest next_manifest)
|
||||
|
@ -310,10 +321,10 @@ void CleanUpTempDir(const std::string& temp_dir, const TodoList& todo)
|
|||
bool BackupFile(const std::string& path)
|
||||
{
|
||||
std::string backup_path = path + ".bak";
|
||||
fprintf(log_fp, "Backing up existing %s to .bak.\n", path.c_str());
|
||||
LogToFile("Backing up existing %s to .bak.\n", path.c_str());
|
||||
if (!File::Rename(path, backup_path))
|
||||
{
|
||||
fprintf(log_fp, "Cound not rename %s to %s for backup.\n", path.c_str(), backup_path.c_str());
|
||||
LogToFile("Cound not rename %s to %s for backup.\n", path.c_str(), backup_path.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -328,7 +339,7 @@ bool DeleteObsoleteFiles(const std::vector<TodoList::DeleteOp>& to_delete,
|
|||
|
||||
if (!File::Exists(path))
|
||||
{
|
||||
fprintf(log_fp, "File %s is already missing.\n", op.filename.c_str());
|
||||
LogToFile("File %s is already missing.\n", op.filename.c_str());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
@ -336,7 +347,7 @@ bool DeleteObsoleteFiles(const std::vector<TodoList::DeleteOp>& to_delete,
|
|||
std::string contents;
|
||||
if (!File::ReadFileToString(path, contents))
|
||||
{
|
||||
fprintf(log_fp, "Could not read file planned for deletion: %s.\n", op.filename.c_str());
|
||||
LogToFile("Could not read file planned for deletion: %s.\n", op.filename.c_str());
|
||||
return false;
|
||||
}
|
||||
Manifest::Hash contents_hash = ComputeHash(contents);
|
||||
|
@ -365,7 +376,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
std::string path = install_base_path + DIR_SEP + op.filename;
|
||||
if (!File::CreateFullPath(path))
|
||||
{
|
||||
fprintf(log_fp, "Could not create directory structure for %s.\n", op.filename.c_str());
|
||||
LogToFile("Could not create directory structure for %s.\n", op.filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -385,7 +396,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
|
||||
if (S_ISLNK(file_stats.st_mode))
|
||||
{
|
||||
fprintf(log_fp, "%s is symlink, skipping\n", path.c_str());
|
||||
LogToFile("%s is symlink, skipping\n", path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -408,13 +419,13 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
std::string contents;
|
||||
if (!File::ReadFileToString(path, contents))
|
||||
{
|
||||
fprintf(log_fp, "Could not read existing file %s.\n", op.filename.c_str());
|
||||
LogToFile("Could not read existing file %s.\n", op.filename.c_str());
|
||||
return false;
|
||||
}
|
||||
Manifest::Hash contents_hash = ComputeHash(contents);
|
||||
if (contents_hash == op.new_hash)
|
||||
{
|
||||
fprintf(log_fp, "File %s was already up to date. Partial update?\n", op.filename.c_str());
|
||||
LogToFile("File %s was already up to date. Partial update?\n", op.filename.c_str());
|
||||
continue;
|
||||
}
|
||||
else if (!op.old_hash || contents_hash != *op.old_hash || is_self)
|
||||
|
@ -426,8 +437,8 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
|
||||
// Now we can safely move the new contents to the location.
|
||||
std::string content_filename = HexEncode(op.new_hash.data(), op.new_hash.size());
|
||||
fprintf(log_fp, "Updating file %s from content %s...\n", op.filename.c_str(),
|
||||
content_filename.c_str());
|
||||
LogToFile("Updating file %s from content %s...\n", op.filename.c_str(),
|
||||
content_filename.c_str());
|
||||
#ifdef __APPLE__
|
||||
// macOS caches the code signature of Mach-O executables when they're first loaded.
|
||||
// Unfortunately, there is a quirk in the kernel with how it handles the cache: if the file is
|
||||
|
@ -440,8 +451,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
const std::string temporary_file = temp_path + DIR_SEP + "temporary_file";
|
||||
if (!File::CopyRegularFile(temp_path + DIR_SEP + content_filename, temporary_file))
|
||||
{
|
||||
fprintf(log_fp, "Could not copy %s to %s.\n", content_filename.c_str(),
|
||||
temporary_file.c_str());
|
||||
LogToFile("Could not copy %s to %s.\n", content_filename.c_str(), temporary_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -450,7 +460,7 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
if (!File::CopyRegularFile(temp_path + DIR_SEP + content_filename, path))
|
||||
#endif
|
||||
{
|
||||
fprintf(log_fp, "Could not update file %s.\n", op.filename.c_str());
|
||||
LogToFile("Could not update file %s.\n", op.filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -465,32 +475,32 @@ bool UpdateFiles(const std::vector<TodoList::UpdateOp>& to_update,
|
|||
bool PerformUpdate(const TodoList& todo, const std::string& install_base_path,
|
||||
const std::string& content_base_url, const std::string& temp_path)
|
||||
{
|
||||
fprintf(log_fp, "Starting download step...\n");
|
||||
LogToFile("Starting download step...\n");
|
||||
if (!DownloadContent(todo.to_download, content_base_url, temp_path))
|
||||
return false;
|
||||
fprintf(log_fp, "Download step completed.\n");
|
||||
LogToFile("Download step completed.\n");
|
||||
|
||||
fprintf(log_fp, "Starting platform version check step...\n");
|
||||
LogToFile("Starting platform version check step...\n");
|
||||
if (!PlatformVersionCheck(todo.to_update, install_base_path, temp_path))
|
||||
return false;
|
||||
fprintf(log_fp, "Platform version check step completed.\n");
|
||||
LogToFile("Platform version check step completed.\n");
|
||||
|
||||
fprintf(log_fp, "Starting update step...\n");
|
||||
LogToFile("Starting update step...\n");
|
||||
if (!UpdateFiles(todo.to_update, install_base_path, temp_path))
|
||||
return false;
|
||||
fprintf(log_fp, "Update step completed.\n");
|
||||
LogToFile("Update step completed.\n");
|
||||
|
||||
fprintf(log_fp, "Starting deletion step...\n");
|
||||
LogToFile("Starting deletion step...\n");
|
||||
if (!DeleteObsoleteFiles(todo.to_delete, install_base_path))
|
||||
return false;
|
||||
fprintf(log_fp, "Deletion step completed.\n");
|
||||
LogToFile("Deletion step completed.\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FatalError(const std::string& message)
|
||||
{
|
||||
fprintf(log_fp, "%s\n", message.c_str());
|
||||
LogToFile("%s\n", message.c_str());
|
||||
|
||||
UI::SetVisible(true);
|
||||
UI::Error(message);
|
||||
|
@ -506,13 +516,13 @@ std::optional<Manifest> ParseManifest(const std::string& manifest)
|
|||
size_t filename_end_pos = manifest.find('\t', pos);
|
||||
if (filename_end_pos == std::string::npos)
|
||||
{
|
||||
fprintf(log_fp, "Manifest entry %zu: could not find filename end.\n", parsed.entries.size());
|
||||
LogToFile("Manifest entry %zu: could not find filename end.\n", parsed.entries.size());
|
||||
return {};
|
||||
}
|
||||
size_t hash_end_pos = manifest.find('\n', filename_end_pos);
|
||||
if (hash_end_pos == std::string::npos)
|
||||
{
|
||||
fprintf(log_fp, "Manifest entry %zu: could not find hash end.\n", parsed.entries.size());
|
||||
LogToFile("Manifest entry %zu: could not find hash end.\n", parsed.entries.size());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -520,16 +530,14 @@ std::optional<Manifest> ParseManifest(const std::string& manifest)
|
|||
std::string hash = manifest.substr(filename_end_pos + 1, hash_end_pos - filename_end_pos - 1);
|
||||
if (hash.size() != 32)
|
||||
{
|
||||
fprintf(log_fp, "Manifest entry %zu: invalid hash: \"%s\".\n", parsed.entries.size(),
|
||||
hash.c_str());
|
||||
LogToFile("Manifest entry %zu: invalid hash: \"%s\".\n", parsed.entries.size(), hash.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
Manifest::Hash decoded_hash;
|
||||
if (!HexDecode(hash, decoded_hash.data(), decoded_hash.size()))
|
||||
{
|
||||
fprintf(log_fp, "Manifest entry %zu: invalid hash: \"%s\".\n", parsed.entries.size(),
|
||||
hash.c_str());
|
||||
LogToFile("Manifest entry %zu: invalid hash: \"%s\".\n", parsed.entries.size(), hash.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -548,7 +556,7 @@ std::optional<Manifest> FetchAndParseManifest(const std::string& url)
|
|||
Common::HttpRequest::Response resp = http.Get(url);
|
||||
if (!resp)
|
||||
{
|
||||
fprintf(log_fp, "Manifest download failed.\n");
|
||||
LogToFile("Manifest download failed.\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -562,7 +570,7 @@ std::optional<Manifest> FetchAndParseManifest(const std::string& url)
|
|||
size_t boundary = decompressed.rfind("\n\n");
|
||||
if (boundary == std::string::npos)
|
||||
{
|
||||
fprintf(log_fp, "No signature was found in manifest.\n");
|
||||
LogToFile("No signature was found in manifest.\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -581,7 +589,7 @@ std::optional<Manifest> FetchAndParseManifest(const std::string& url)
|
|||
}
|
||||
if (!found_valid_signature)
|
||||
{
|
||||
fprintf(log_fp, "Could not verify signature of the manifest.\n");
|
||||
LogToFile("Could not verify signature of the manifest.\n");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -692,9 +700,9 @@ bool RunUpdater(std::vector<std::string> args)
|
|||
atexit(FlushLog);
|
||||
}
|
||||
|
||||
fprintf(log_fp, "Updating from: %s\n", opts.this_manifest_url.c_str());
|
||||
fprintf(log_fp, "Updating to: %s\n", opts.next_manifest_url.c_str());
|
||||
fprintf(log_fp, "Install path: %s\n", opts.install_base_path.c_str());
|
||||
LogToFile("Updating from: %s\n", opts.this_manifest_url.c_str());
|
||||
LogToFile("Updating to: %s\n", opts.next_manifest_url.c_str());
|
||||
LogToFile("Install path: %s\n", opts.install_base_path.c_str());
|
||||
|
||||
if (!File::IsDirectory(opts.install_base_path))
|
||||
{
|
||||
|
@ -704,13 +712,13 @@ bool RunUpdater(std::vector<std::string> args)
|
|||
|
||||
if (opts.parent_pid)
|
||||
{
|
||||
fprintf(log_fp, "Waiting for parent PID %d to complete...\n", *opts.parent_pid);
|
||||
LogToFile("Waiting for parent PID %d to complete...\n", *opts.parent_pid);
|
||||
|
||||
auto pid = opts.parent_pid.value();
|
||||
|
||||
UI::WaitForPID(static_cast<u32>(pid));
|
||||
|
||||
fprintf(log_fp, "Completed! Proceeding with update.\n");
|
||||
LogToFile("Completed! Proceeding with update.\n");
|
||||
}
|
||||
|
||||
UI::SetVisible(true);
|
||||
|
|
|
@ -49,6 +49,7 @@ struct TodoList
|
|||
void Log() const;
|
||||
};
|
||||
|
||||
void LogToFile(const char* fmt, ...);
|
||||
std::string HexEncode(const u8* buffer, size_t size);
|
||||
Manifest::Hash ComputeHash(const std::string& contents);
|
||||
bool RunUpdater(std::vector<std::string> args);
|
||||
|
|
|
@ -241,7 +241,7 @@ static VersionCheckResult OSVersionCheck(const BuildInfo& build_info)
|
|||
|
||||
std::optional<BuildInfos> InitBuildInfos(const std::vector<TodoList::UpdateOp>& to_update,
|
||||
const std::string& install_base_path,
|
||||
const std::string& temp_dir, FILE* log_fp)
|
||||
const std::string& temp_dir)
|
||||
{
|
||||
const auto op_it = std::find_if(to_update.cbegin(), to_update.cend(),
|
||||
[&](const auto& op) { return op.filename == "build_info.txt"; });
|
||||
|
@ -255,7 +255,7 @@ std::optional<BuildInfos> InitBuildInfos(const std::vector<TodoList::UpdateOp>&
|
|||
if (!File::ReadFileToString(build_info_path, build_info_content) ||
|
||||
op.new_hash != ComputeHash(build_info_content))
|
||||
{
|
||||
fprintf(log_fp, "Failed to read %s\n.", build_info_path.c_str());
|
||||
LogToFile("Failed to read %s\n.", build_info_path.c_str());
|
||||
return {};
|
||||
}
|
||||
BuildInfos build_infos;
|
||||
|
@ -266,7 +266,7 @@ std::optional<BuildInfos> InitBuildInfos(const std::vector<TodoList::UpdateOp>&
|
|||
if (File::ReadFileToString(build_info_path, build_info_content))
|
||||
{
|
||||
if (op.old_hash != ComputeHash(build_info_content))
|
||||
fprintf(log_fp, "Using modified existing BuildInfo %s.\n", build_info_path.c_str());
|
||||
LogToFile("Using modified existing BuildInfo %s.\n", build_info_path.c_str());
|
||||
build_infos.current = Platform::BuildInfo(build_info_content);
|
||||
}
|
||||
return build_infos;
|
||||
|
@ -305,9 +305,9 @@ bool CheckBuildInfo(const BuildInfos& build_infos)
|
|||
}
|
||||
|
||||
bool VersionCheck(const std::vector<TodoList::UpdateOp>& to_update,
|
||||
const std::string& install_base_path, const std::string& temp_dir, FILE* log_fp)
|
||||
const std::string& install_base_path, const std::string& temp_dir)
|
||||
{
|
||||
auto build_infos = InitBuildInfos(to_update, install_base_path, temp_dir, log_fp);
|
||||
auto build_infos = InitBuildInfos(to_update, install_base_path, temp_dir);
|
||||
// If there's no build info, it means the check should be skipped.
|
||||
if (!build_infos.has_value())
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue