[impl] Delegate update job to child process

This commit is contained in:
Peter Sykora 2018-03-27 22:57:56 +02:00
parent fe7c1bb368
commit 4eac37cfae
8 changed files with 423 additions and 19 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@
/Debug/
/libModuleManagerTest/Debug/
/Release/
/CCEngineLoader/Debug/
/CCEngineLoader/Release/

View File

@ -21,9 +21,11 @@
<ItemGroup>
<ClCompile Include="src\CCEngine.cpp" />
<ClCompile Include="src\CCServer.cpp" />
<ClCompile Include="src\ProcessUtils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\CCServer.h" />
<ClInclude Include="src\ProcessUtils.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\libLicenseClient\libLicenseClient.vcxproj">

View File

@ -17,10 +17,16 @@
<ClCompile Include="src\CCServer.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\ProcessUtils.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\CCServer.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="src\ProcessUtils.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -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 <boost/filesystem.hpp>
@ -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<uint32_t>(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<uint32_t>(ModuleUpdateFlags::restartRequired));
// moduleManager.applyUpdate(update.moduleId, update);
// }
}
}
auto docRoot = appDir / "data";

View File

@ -0,0 +1,294 @@
#include "ProcessUtils.h"
#include <windows.h>
#include <string>
#include <sstream>
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<size_t>(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<size_t>(bytesRead);
}
private:
SECURITY_ATTRIBUTES m_secAttr = { 0 };
HANDLE m_readEnd = nullptr;
HANDLE m_writeEnd = nullptr;
};
std::unique_ptr<AnonymousPipeImpl> createAnonymousPipe(bool readEndInheritable, bool writeEndInheritable)
{
auto pipe = std::make_unique<AnonymousPipeImpl>();
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<DWORD>(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<ProcessHandleImpl> openProcess(DWORD processId)
{
auto retVal = OpenProcess(SYNCHRONIZE, false, processId);
if (retVal == nullptr)
{
return nullptr;
}
return std::make_unique<ProcessHandleImpl>(retVal);
}
/** Create a child process that uses the previously created pipes for STDIN and STDOUT. */
std::unique_ptr<ProcessHandleImpl> 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<ProcessHandleImpl>(piProcInfo.hProcess, piProcInfo.hThread);
}
}
ProcessHandle::ProcessHandle(std::unique_ptr<detail::ProcessHandleImpl> 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<detail::AnonymousPipeImpl> 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<AnonymousPipe> createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd)
{
return std::make_unique<AnonymousPipe>(detail::createAnonymousPipe(inheritableReadEnd, inheritableWriteEnd));
}
ProcessHandle openProcess(uint32_t processId)
{
return { detail::openProcess(processId) };
}
PipedProcessHandle createPipedProcess(const std::string & commandLine, std::unique_ptr<AnonymousPipe> stdinPipe, std::unique_ptr<AnonymousPipe> 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<ProcessHandle>(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<unsigned long>(GetCurrentProcessId());
}

View File

@ -0,0 +1,62 @@
#pragma once
#include <chrono>
#include <memory>
#include <string>
namespace detail
{
class AnonymousPipeImpl;
class ProcessHandleImpl;
}
class AnonymousPipe
{
public:
AnonymousPipe(std::unique_ptr<detail::AnonymousPipeImpl> 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<detail::AnonymousPipeImpl> m_impl;
};
std::unique_ptr<AnonymousPipe> createAnonymousPipe(bool inheritableReadEnd, bool inheritableWriteEnd);
class ProcessHandle
{
public:
ProcessHandle(std::unique_ptr<detail::ProcessHandleImpl> initVal);
virtual ~ProcessHandle();
public:
bool waitForExit(const std::chrono::milliseconds& ms);
void waitForExit();
int exitCode();
private:
std::unique_ptr<detail::ProcessHandleImpl> m_impl;
};
ProcessHandle openProcess(uint32_t processId);
struct PipedProcessHandle
{
void writeLine(const std::string& line);
std::string readLine();
std::unique_ptr<ProcessHandle> processHandle;
std::unique_ptr<AnonymousPipe> stdinPipe;
std::unique_ptr<AnonymousPipe> stdoutPipe;
};
PipedProcessHandle createPipedProcess(
const std::string& commandLine,
std::unique_ptr<AnonymousPipe> stdinPipe,
std::unique_ptr<AnonymousPipe> stdoutPipe);
unsigned long getCurrentProcessId();

Binary file not shown.

View File

@ -30,27 +30,27 @@
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@ -94,12 +94,13 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -108,12 +109,13 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -122,7 +124,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
@ -130,6 +132,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -140,7 +143,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
@ -148,6 +151,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\libLicenseClient\api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -169,6 +173,11 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\libLicenseClient\libLicenseClient.vcxproj">
<Project>{51345e59-83e5-4389-93a9-0131b40522b7}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>