#include "UpdateWorker.h" #include "CachedDownloader.h" #include "HTTPClient.h" #include "JSONModuleDatabase.h" #include "ModuleManager.h" #include #include #include #include #include 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 loadPendingUpdates(const boost::filesystem::path& appDir) { std::vector 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(); } void UpdateWorkerThread::run() { emit progressChanged(UpdateStatus::loadingUpdateDefinitions, QString("Loading update definitions...")); std::vector 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(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")); }