From 4eac37cfae911dcbb9dff2fab071ce026a5cd84b Mon Sep 17 00:00:00 2001 From: Peter Sykora Date: Tue, 27 Mar 2018 22:57:56 +0200 Subject: [PATCH] [impl] Delegate update job to child process --- .gitignore | 2 + CCEngine/CCEngine.vcxproj | 2 + CCEngine/CCEngine.vcxproj.filters | 6 + CCEngine/src/CCEngine.cpp | 51 ++++- CCEngine/src/ProcessUtils.cpp | 294 ++++++++++++++++++++++++++ CCEngine/src/ProcessUtils.h | 62 ++++++ CCEngineLoader/CCEngineLoader.cpp | Bin 1470 -> 8140 bytes CCEngineLoader/CCEngineLoader.vcxproj | 25 ++- 8 files changed, 423 insertions(+), 19 deletions(-) create mode 100644 CCEngine/src/ProcessUtils.cpp create mode 100644 CCEngine/src/ProcessUtils.h diff --git a/.gitignore b/.gitignore index bce07a9..54d1f29 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ /Debug/ /libModuleManagerTest/Debug/ /Release/ +/CCEngineLoader/Debug/ +/CCEngineLoader/Release/ diff --git a/CCEngine/CCEngine.vcxproj b/CCEngine/CCEngine.vcxproj index 6b75436..978d624 100644 --- a/CCEngine/CCEngine.vcxproj +++ b/CCEngine/CCEngine.vcxproj @@ -21,9 +21,11 @@ + + diff --git a/CCEngine/CCEngine.vcxproj.filters b/CCEngine/CCEngine.vcxproj.filters index 251b858..68d0bdc 100644 --- a/CCEngine/CCEngine.vcxproj.filters +++ b/CCEngine/CCEngine.vcxproj.filters @@ -17,10 +17,16 @@ src + + src + src + + src + \ No newline at end of file diff --git a/CCEngine/src/CCEngine.cpp b/CCEngine/src/CCEngine.cpp index 3678448..7913f7f 100644 --- a/CCEngine/src/CCEngine.cpp +++ b/CCEngine/src/CCEngine.cpp @@ -1,9 +1,11 @@ +#include "CachedDownloader.h" #include "SystemParamsProvider_win.h" #include "HTTPClient.h" #include "LicenseClient.h" #include "CCServer.h" #include "ModuleManager.h" #include "JSONModuleDatabase.h" +#include "ProcessUtils.h" #include @@ -11,6 +13,11 @@ //------------------------------------------------------------------------------ +namespace +{ + static const std::string updateCacheFolder = ".upd"; +} + int main(int argc, char* argv[]) { auto appDir = boost::filesystem::system_complete(argv[0]).parent_path(); @@ -69,19 +76,41 @@ int main(int argc, char* argv[]) std::cin >> what; if (what == 'y') { - ModuleManager moduleManager(appDir.string(), moduleDatabase, httpClient); - for (const auto& update : moduleUpdates) { - restartRequired = restartRequired || (update.flag & static_cast(ModuleUpdateFlags::restartRequired)); - moduleManager.applyUpdate(update.moduleId, update); - } - } - } + CachedDownloader cachedDownloader(appDir / updateCacheFolder, httpClient); + for (const auto& update : moduleUpdates) + { + cachedDownloader.download(update.updateUri); + } - if (restartRequired) - { - std::cout << "Restart required" << std::endl; - return 0; + std::ofstream os((appDir / updateCacheFolder / ".updates.json").native()); + ::serialize(os, moduleUpdates); + } + + std::ostringstream command; + command << (appDir / "CCEngineLoader.exe") << " " << getCurrentProcessId(); + + auto stdOutPipe = createAnonymousPipe(false, true); + auto updProcess = createPipedProcess(command.str(), nullptr, std::move(stdOutPipe)); + while (1) + { + auto line = updProcess.readLine(); + std::cout << line << std::endl; + if (line == "RestartRequired") + { + // Exit the current application to allow updater to continue + return 0; + } + } + +// ModuleManager moduleManager(appDir.string(), moduleDatabase, httpClient); +// for (const auto& update : moduleUpdates) +// { +// restartRequired = restartRequired || (update.flag & static_cast(ModuleUpdateFlags::restartRequired)); +// moduleManager.applyUpdate(update.moduleId, update); +// } + + } } auto docRoot = appDir / "data"; diff --git a/CCEngine/src/ProcessUtils.cpp b/CCEngine/src/ProcessUtils.cpp new file mode 100644 index 0000000..ab08ce9 --- /dev/null +++ b/CCEngine/src/ProcessUtils.cpp @@ -0,0 +1,294 @@ +#include "ProcessUtils.h" + +#include + +#include +#include + +namespace detail +{ + class AnonymousPipeImpl + { + public: + AnonymousPipeImpl() + { + m_secAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + } + + virtual ~AnonymousPipeImpl() + { + CloseHandle(m_readEnd); + CloseHandle(m_writeEnd); + } + + public: + HANDLE& readEnd() { return m_readEnd; } + HANDLE& writeEnd() { return m_writeEnd; } + SECURITY_ATTRIBUTES& secAttr() { return m_secAttr; } + + public: + size_t write(const char* data, size_t size) + { + DWORD written = 0; + auto success = WriteFile(m_writeEnd, data, size, &written, NULL); + if (!success) + { + throw std::runtime_error("Could not write pipe"); + } + return static_cast(written); + } + + size_t read(char* data, size_t size) + { + DWORD bytesRead = 0; + auto success = ReadFile(m_readEnd, data, size, &bytesRead, NULL); + if (!success) + { + throw std::runtime_error("Could not read pipe"); + } + return static_cast(bytesRead); + } + + private: + SECURITY_ATTRIBUTES m_secAttr = { 0 }; + HANDLE m_readEnd = nullptr; + HANDLE m_writeEnd = nullptr; + }; + + std::unique_ptr createAnonymousPipe(bool readEndInheritable, bool writeEndInheritable) + { + auto pipe = std::make_unique(); + + bool inheritable = readEndInheritable || writeEndInheritable; + pipe->secAttr().bInheritHandle = inheritable; + pipe->secAttr().lpSecurityDescriptor = NULL; + + if (!CreatePipe(&pipe->readEnd(), &pipe->writeEnd(), &pipe->secAttr(), 0)) + { + throw std::runtime_error("Could not create pipe"); + } + + if (inheritable && !readEndInheritable) + { + if (!SetHandleInformation(pipe->readEnd(), HANDLE_FLAG_INHERIT, 0)) + { + throw std::runtime_error("Setting pipe flags failed"); + } + } + + if (inheritable && !writeEndInheritable) + { + if (!SetHandleInformation(pipe->writeEnd(), HANDLE_FLAG_INHERIT, 0)) + { + throw std::runtime_error("Setting pipe flags failed"); + } + } + + return pipe; + } +} + +namespace detail +{ + +class ProcessHandleImpl +{ +public: + explicit ProcessHandleImpl(HANDLE processHandle, HANDLE threadHandle = nullptr) + : m_processHandle(processHandle) + , m_threadHandle(threadHandle) + {} + ProcessHandleImpl(const ProcessHandleImpl&) = delete; + ProcessHandleImpl& operator= (const ProcessHandleImpl&) = delete; + + virtual ~ProcessHandleImpl() + { + CloseHandle(m_processHandle); + CloseHandle(m_threadHandle); + } + +public: + bool waitForExit(const std::chrono::milliseconds& ms) + { + DWORD ret = WaitForSingleObject(m_processHandle, static_cast(ms.count())); + if (ret == WAIT_OBJECT_0) + { + return true; + } + if (ret == WAIT_TIMEOUT) + { + return false; + } + throw std::runtime_error("Wait for exit failed"); + } + + void waitForExit() + { + DWORD ret = WaitForSingleObject(m_processHandle, INFINITE); + if (ret != WAIT_OBJECT_0) + { + throw std::runtime_error("Inifinite wait for exit failed"); + } + } + + bool isRunning() + { + return !waitForExit(std::chrono::milliseconds(0)); + } + + int exitCode() + { + DWORD exitCode; + auto result = GetExitCodeProcess(m_processHandle, &exitCode); + if (!result) + { + throw std::runtime_error("Could not get exit code"); + } + return exitCode; + } +private: + HANDLE m_processHandle = nullptr; + HANDLE m_threadHandle = nullptr; +}; + +std::unique_ptr openProcess(DWORD processId) +{ + auto retVal = OpenProcess(SYNCHRONIZE, false, processId); + if (retVal == nullptr) + { + return nullptr; + } + + return std::make_unique(retVal); +} + +/** Create a child process that uses the previously created pipes for STDIN and STDOUT. */ +std::unique_ptr createPipedProcess(const std::string& commandLine, HANDLE childPipeReadEnd, HANDLE childPipeWriteEnd) +{ + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + PROCESS_INFORMATION piProcInfo = { 0 }; + STARTUPINFO siStartInfo = { 0 }; + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = childPipeWriteEnd; + siStartInfo.hStdOutput = childPipeWriteEnd; + siStartInfo.hStdInput = childPipeReadEnd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + // Create the child process. + + bool success = CreateProcess(NULL, + (LPSTR)commandLine.c_str(), // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + // If an error occurs, exit the application. + if (!success) + { + throw std::runtime_error("Could not create process"); + } + + return std::make_unique(piProcInfo.hProcess, piProcInfo.hThread); +} + +} + +ProcessHandle::ProcessHandle(std::unique_ptr initVal) + : m_impl(std::move(initVal)) +{} + +ProcessHandle::~ProcessHandle() +{ +} + +bool ProcessHandle::waitForExit(const std::chrono::milliseconds& ms) +{ + return m_impl->waitForExit(ms); +} + +void ProcessHandle::waitForExit() +{ + return m_impl->waitForExit(); +} + +int ProcessHandle::exitCode() +{ + return m_impl->exitCode(); +} + +AnonymousPipe::AnonymousPipe(std::unique_ptr initVal) + : m_impl(std::move(initVal)) +{ +} + +AnonymousPipe::~AnonymousPipe() +{ +} + +size_t AnonymousPipe::write(const char* data, size_t size) +{ + return m_impl->write(data, size); +} + +size_t AnonymousPipe::read(char* data, size_t size) +{ + return m_impl->read(data, size); +} + +std::unique_ptr createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd) +{ + return std::make_unique(detail::createAnonymousPipe(inheritableReadEnd, inheritableWriteEnd)); +} + +ProcessHandle openProcess(uint32_t processId) +{ + return { detail::openProcess(processId) }; +} + +PipedProcessHandle createPipedProcess(const std::string & commandLine, std::unique_ptr stdinPipe, std::unique_ptr stdoutPipe) +{ + auto hisWriteEnd = stdoutPipe ? stdoutPipe->_impl().writeEnd() : nullptr; + auto hisReadEnd = stdinPipe ? stdinPipe->_impl().readEnd() : nullptr; + auto processHandle = detail::createPipedProcess(commandLine, hisReadEnd, hisWriteEnd); + + return { + std::make_unique(std::move(processHandle)), + std::move(stdinPipe), + std::move(stdoutPipe)}; +} + +void PipedProcessHandle::writeLine(const std::string& line) +{ + stdinPipe->write(line.data(), line.size()); + std::ostringstream ss; + ss << std::endl; + auto tmpstr = ss.str(); + stdinPipe->write(tmpstr.data(), tmpstr.size()); +} + +std::string PipedProcessHandle::readLine() +{ + std::ostringstream ss; + char buf[1] = { 0 }; + stdoutPipe->read(buf, 1); + while (buf[0] != '\n') + { + if (buf[0] != '\r') { + ss.write(buf, 1); + } + stdoutPipe->read(buf, 1); + } + + return ss.str(); +} + +unsigned long getCurrentProcessId() +{ + return static_cast(GetCurrentProcessId()); +} diff --git a/CCEngine/src/ProcessUtils.h b/CCEngine/src/ProcessUtils.h new file mode 100644 index 0000000..655c148 --- /dev/null +++ b/CCEngine/src/ProcessUtils.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +namespace detail +{ + class AnonymousPipeImpl; + class ProcessHandleImpl; +} + +class AnonymousPipe +{ +public: + AnonymousPipe(std::unique_ptr initVal); + virtual ~AnonymousPipe(); + +public: + size_t write(const char* data, size_t size); + size_t read(char *data, size_t size); + + detail::AnonymousPipeImpl& _impl() { return (*m_impl); }; +private: + std::unique_ptr m_impl; +}; + +std::unique_ptr createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd); + +class ProcessHandle +{ +public: + ProcessHandle(std::unique_ptr initVal); + virtual ~ProcessHandle(); + +public: + bool waitForExit(const std::chrono::milliseconds& ms); + void waitForExit(); + int exitCode(); + +private: + std::unique_ptr m_impl; +}; + +ProcessHandle openProcess(uint32_t processId); + +struct PipedProcessHandle +{ + void writeLine(const std::string& line); + std::string readLine(); + + std::unique_ptr processHandle; + std::unique_ptr stdinPipe; + std::unique_ptr stdoutPipe; +}; + +PipedProcessHandle createPipedProcess( + const std::string& commandLine, + std::unique_ptr stdinPipe, + std::unique_ptr stdoutPipe); + +unsigned long getCurrentProcessId(); \ No newline at end of file diff --git a/CCEngineLoader/CCEngineLoader.cpp b/CCEngineLoader/CCEngineLoader.cpp index b45833da8ac6c5b31480c973b15d96bd639527ee..a7004c2e4e5f0d4ba6ed582d95afff4754876351 100644 GIT binary patch literal 8140 zcmc&(+ioL85bb9q{$Vg;nE>l7%UcMMqD?}Aa#JosEFjCV9Vd(91bY&8Vfl67oGO>Q zd!}dX4U5nynQK>{sycP)_TPW4x>c9C7dW=v*bUt^{tw)~yLDaH$JNBOUC-U&$lMzK zw(#t7_SF>kg}cI&41Z%>Puv?^-(fW66u7>`^(6SNhx@Uc;{Qm-bw&0F-}f+MfivH? z-G}Z2AX}9)uRj)vJ&d{+T$yVDa~GpHe;>GR-ESBpcikC|V@TobioXqM z^&`G>r|!rdx&zG72M%hmC8)dZ0_Q32ne-jP5YwfRKDM6 zu+BOV-Qt+SrmNyHqMgD&d@^u1B7uHYDOLN%67GQU7GD!<0Vy5)pWto)>38#MYVm$# zz5-4%p#ync%tYI&m73*Wihe}ag^u1rwDb+0PDNf1SL7)f`VIcPt=7QudR@O3i>G99 z-jpoHO4;%%g?_F|;?qB5mJITE&|exEJ=|IR5INuUfp<-=Cc=*)p5H@*zKj_JF|q@D zj_{mzRHQ9%K)-D{kICkJ!1veqJ_GZ*xGLN^@G?S2h#IniR-EEE6PY(MKauij4#|x3 zYw(*Hg>o31%rUflIDQXNv=ZqYTfh+Qx=lpi7P8YeWMtr4j6=0Lf^J@bwk&_JBOE%tbBsfM`el@4-9Uu&(N0vhDn5WOcnG1MR)%04+3z{TY4v z5-fZI89hW?A6C(tLR=bWBU~A$RJ+($XV7@ty~em=DPEjH$4z+k3|=^%>3)Pc$iB4R z$HD>Dyi**V`Ir;r_D#sy6NZotCLy$nS?48uKpr39d?Q|Z1KT{2d8sYu{~hmc!IE6= zR%$)LZ&_(CFpdmLjZN!Phl1FvDEQQ)NNJ@V!6|IdyvsaeHJ-JW(a%_p}xeQ0~0lyGxqLbD`svg9l=Z7){wSWB8ctDTHNy#wJ^B z0TD9}Ysoh1Q3niSO|g7Z=h=|Yy?H!n$IDK61WygYx2C!O9ed7av7$Yd_Pn3snfC7w zU<>ov7*V9}n6X*4d+wM21(o(b%tG`VJBPkt)Y`|o$n{2(y?i!~IkM=^qRmz^+BGRl z>g$bm60FPj;@@Uunu`}9DZg(*7W1&au4zuYhhLXLn$Doj-3#jjeO5^A<65mQ|C9Ob zFPXuZJ#zO6&MT;fKVf`JvhKHdS0EQ~ja4A#COGJQX63rx#_YyF< z$8{?vrP)&IPwV#By<3;fnpRhF8L`?SaV^-yGn2fik0cwpev2y%bJ@FflPN5!BUjp7 zk?PtjTmfY8tX6II8tFO*ZAfG!uW^4Phe72DmU7fk+ z9#b3GDOZ_iJ+H|()~8V0Y}{Yay_#$2$(=Q>fbuy;=97>gCey~-xf)UShNCT?$Fh`b z_MJ-baPs za=AD3bIKp7J!P(k3LJOnf&5N$tmC4cZ1S>sulsRXk&I8FGu_Oy%j|r|YxMHIcrJeS zwMX+d^D(249%cTm=ASZ>`W^Im{&;siwoY{x+96~}8NFtPW)GB+`n@hzgcC#}wPa)! za`!8;2dl7lz1;I*A3VZWgHlvpg1g+ajcdPUA69kU#nqaozte=;#C(sdPCAl3cH&!v zJDj}@Ep#25Nxz|Exqjnb3R%o2>!Ld|+*j^7o)^e7d2MgBGn1$pwHZ;_3&!AOeXz`T zZ{1tiyctIv-yVhT7dMZk<>=Msd>Z@FW=Q+f-qQBkm9mRu^~&Ijb^LEFU}$UprI(>y z>Zrt4o6F9&b~=}ew(PG5i*!%h*XC>_vo;#+^yu z-?yXu!V1is5`Iy~TyO8H&%*gOXDIkvChNQRSU5`+hi5?j7}i<-24{O!${CL=%>>_j znHBwrsxJHmm%kKw-2UrWe%K3G$FewX<9JEON&G8ETs5p>m47R51M_RaovL4%`+Vk$ dr|xOw?>;mo2Yl8qGoWfe<_+KbTKA^k{R97^ojU*k delta 526 zcmYjO!Ab&A6uqN0=~xp~CWDx-0l@(oX^{&fB5)Ov1VP(GM-$X0XDZtS{eg6$eLvzC zw2FS9U5gfdL3HlWkX&ZoyZ4@R-aYTV9?UY+Stg*6I@F>rP54wRu%9sbQ>*b?qQXbX z8h>rP_o`GSMQ7Bc2EID>=V0`~RCGfltbH0_-$r$S@Mw(8$NvhoHu3K4W-i&KfSc>3 z?0hHIa`;6r@ps%+6C3#x@PRWw&O^5Rh$+96${7gt>56MQx~+*-?v2A}B5X$8mP z$m0(^cX$Vm+=d6ea-`qEjn%1#N*E*25k{+~R3ERTU4SKku~+KR%*?Q{*M&rs5f|mn`8u=_db}CO;Qz3-ScN#*%G$ diff --git a/CCEngineLoader/CCEngineLoader.vcxproj b/CCEngineLoader/CCEngineLoader.vcxproj index 74f3478..d22b103 100644 --- a/CCEngineLoader/CCEngineLoader.vcxproj +++ b/CCEngineLoader/CCEngineLoader.vcxproj @@ -30,27 +30,27 @@ Application true v141 - Unicode + MultiByte Application false v141 true - Unicode + MultiByte Application true v141 - Unicode + MultiByte Application false v141 true - Unicode + MultiByte @@ -94,12 +94,13 @@ - Use + NotUsing Level3 Disabled true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) Console @@ -108,12 +109,13 @@ - Use + NotUsing Level3 Disabled true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) Console @@ -122,7 +124,7 @@ - Use + NotUsing Level3 MaxSpeed true @@ -130,6 +132,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) Console @@ -140,7 +143,7 @@ - Use + NotUsing Level3 MaxSpeed true @@ -148,6 +151,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + $(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories) Console @@ -169,6 +173,11 @@ Create + + + {51345e59-83e5-4389-93a9-0131b40522b7} + +