132 lines
4.5 KiB
C++
132 lines
4.5 KiB
C++
#include "UpdateWorker.h"
|
|
|
|
#include "CachedDownloader.h"
|
|
#include "HTTPClient.h"
|
|
#include "JSONModuleDatabase.h"
|
|
#include "ModuleManager.h"
|
|
|
|
#include <windows.h>
|
|
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/property_tree/ptree.hpp>
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
#include <fstream>
|
|
|
|
|
|
namespace pt = boost::property_tree;
|
|
namespace fs = boost::filesystem;
|
|
|
|
|
|
namespace
|
|
{
|
|
static const std::string moduleDatabaseFolder = ".inst";
|
|
static const std::string updateCacheFolder = ".upd";
|
|
static const std::string updateTrashFolder = ".upd/.remove";
|
|
|
|
void waitUntilProcessTerminates(unsigned long processID)
|
|
{
|
|
HANDLE hProcess = OpenProcess(SYNCHRONIZE, true, processID);
|
|
|
|
if (NULL == hProcess)
|
|
{
|
|
WaitForSingleObject(hProcess, INFINITE);
|
|
}
|
|
}
|
|
|
|
void runApp(const std::string& cmdLine, bool waitForProcessToExit)
|
|
{
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
// Start the child process.
|
|
if (!CreateProcess(NULL, // No module name (use command line)
|
|
(LPSTR)cmdLine.c_str(), // Command line
|
|
NULL, // Process handle not inheritable
|
|
NULL, // Thread handle not inheritable
|
|
FALSE, // Set handle inheritance to FALSE
|
|
DETACHED_PROCESS, // No creation flags
|
|
NULL, // Use parent's environment block
|
|
NULL, // Use parent's starting directory
|
|
&si, // Pointer to STARTUPINFO structure
|
|
&pi) // Pointer to PROCESS_INFORMATION structure
|
|
)
|
|
{
|
|
printf("CreateProcess failed (%d).\n", GetLastError());
|
|
return;
|
|
}
|
|
|
|
if (waitForProcessToExit)
|
|
{
|
|
// Wait until child process exits.
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
}
|
|
|
|
// Close process and thread handles.
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
|
|
std::vector<ModuleUpdate> loadPendingUpdates(const boost::filesystem::path& appDir)
|
|
{
|
|
std::vector<ModuleUpdate> result;
|
|
|
|
boost::filesystem::path path(appDir / updateCacheFolder / ".updates.json");
|
|
if (boost::filesystem::is_regular_file(path))
|
|
{
|
|
pt::ptree root;
|
|
pt::read_json(path.string(), root);
|
|
|
|
deserialize(root, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
UpdateWorkerThread::UpdateWorkerThread(const boost::filesystem::path& appDir, unsigned parentProcessId)
|
|
: m_appDir(appDir)
|
|
, m_parentProcessId(parentProcessId)
|
|
{
|
|
qRegisterMetaType<UpdateStatus>();
|
|
}
|
|
|
|
void UpdateWorkerThread::run()
|
|
{
|
|
emit progressChanged(UpdateStatus::loadingUpdateDefinitions, QString("Loading update definitions..."));
|
|
std::vector<ModuleUpdate> moduleUpdates = loadPendingUpdates(m_appDir);
|
|
|
|
HTTPClient httpClient;
|
|
CachedDownloader cachedDownloader(m_appDir / updateCacheFolder, httpClient);
|
|
JSONModuleDatabase moduleDatabase((m_appDir / moduleDatabaseFolder).string());
|
|
ModuleManager moduleManager(m_appDir.string(), moduleDatabase, cachedDownloader, (m_appDir / updateTrashFolder).string());
|
|
bool isRestartRequired = false;
|
|
for (const auto& update : moduleUpdates)
|
|
{
|
|
bool restartRequired = update.flag & static_cast<uint32_t>(ModuleUpdateFlags::restartRequired);
|
|
isRestartRequired = isRestartRequired || restartRequired;
|
|
if (restartRequired && m_parentProcessId)
|
|
{
|
|
std::cout << "RestartRequired" << std::endl;
|
|
emit progressChanged(UpdateStatus::waitingForAppToExit, QString("Waiting for application to exit"));
|
|
waitUntilProcessTerminates(m_parentProcessId);
|
|
m_parentProcessId = 0;
|
|
}
|
|
emit progressChanged(UpdateStatus::installingUpdates, QString("Installing update %1...").arg(update.moduleId.c_str()));
|
|
moduleManager.applyUpdate(update.moduleId, update);
|
|
std::cout << "UpdateApplied " << update.moduleId << " " << update.version << std::endl;
|
|
}
|
|
|
|
std::cout << "Done" << std::endl;
|
|
if (isRestartRequired)
|
|
{
|
|
emit progressChanged(UpdateStatus::restartingApp, QString("Restarting application"));
|
|
runApp((m_appDir / "CCEngine.exe").string() + " --updated", false);
|
|
}
|
|
|
|
emit progressChanged(UpdateStatus::done, QString("Updated successfully"));
|
|
}
|