Skip to content

Commit

Permalink
Fix bootstrapper getting stuck on "installing dotnet" (#5565)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyoyuppe authored and enricogior committed Aug 4, 2020
1 parent 2532c24 commit 67d1985
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 19 deletions.
67 changes: 49 additions & 18 deletions src/common/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,28 +774,34 @@ bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wst
return false;
}

std::optional<std::string> exec_and_read_output(const std::wstring_view command, const DWORD timeout)
std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms)
{
SECURITY_ATTRIBUTES saAttr{ sizeof(saAttr) };
saAttr.bInheritHandle = true;
saAttr.bInheritHandle = false;

wil::unique_handle childStdoutRead;
wil::unique_handle childStdoutWrite;
if (!CreatePipe(&childStdoutRead, &childStdoutWrite, &saAttr, 0))
constexpr size_t bufferSize = 4096;
// We must use a named pipe for async I/O
char pipename[MAX_PATH + 1];
if (!GetTempFileNameA(R"(\\.\pipe\)", "tmp", 1, pipename))
{
return std::nullopt;
}

if (!SetHandleInformation(childStdoutRead.get(), HANDLE_FLAG_INHERIT, 0))
wil::unique_handle readPipe{ CreateNamedPipeA(pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, bufferSize, bufferSize, 0, &saAttr) };

saAttr.bInheritHandle = true;
wil::unique_handle writePipe{ CreateFileA(pipename, GENERIC_WRITE, 0, &saAttr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr) };

if (!readPipe || !writePipe)
{
return std::nullopt;
}

PROCESS_INFORMATION piProcInfo{};
STARTUPINFOW siStartInfo{ sizeof(siStartInfo) };

siStartInfo.hStdError = childStdoutWrite.get();
siStartInfo.hStdOutput = childStdoutWrite.get();
siStartInfo.hStdError = writePipe.get();
siStartInfo.hStdOutput = writePipe.get();
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
siStartInfo.wShowWindow = SW_HIDE;

Expand All @@ -813,24 +819,49 @@ std::optional<std::string> exec_and_read_output(const std::wstring_view command,
{
return std::nullopt;
}
// Child process inherited the write end of the pipe, we can close it now
writePipe.reset();

WaitForSingleObject(piProcInfo.hProcess, timeout);

childStdoutWrite.reset();
CloseHandle(piProcInfo.hThread);
auto closeProcessHandles = wil::scope_exit([&] {
CloseHandle(piProcInfo.hThread);
CloseHandle(piProcInfo.hProcess);
});

std::string childOutput;
bool processExited = false;
for (;;)
{
char buffer[4096];
char buffer[bufferSize];
DWORD gotBytes = 0;
if (!ReadFile(childStdoutRead.get(), buffer, sizeof(buffer), &gotBytes, nullptr) || !gotBytes)
wil::unique_handle IOEvent{ CreateEventW(nullptr, true, false, nullptr) };
OVERLAPPED overlapped{ .hEvent = IOEvent.get() };
ReadFile(readPipe.get(), buffer, sizeof(buffer), nullptr, &overlapped);

const std::array<HANDLE, 2> handlesToWait = { overlapped.hEvent, piProcInfo.hProcess };
switch (WaitForMultipleObjects(1 + !processExited, handlesToWait.data(), false, timeout_ms))
{
break;
case WAIT_OBJECT_0 + 1:
if (!processExited)
{
// When the process exits, we can reduce timeout and read the rest of the output w/o possibly big timeout
timeout_ms = 1000;
processExited = true;
closeProcessHandles.reset();
}
[[fallthrough]];
case WAIT_OBJECT_0:
if (GetOverlappedResultEx(readPipe.get(), &overlapped, &gotBytes, timeout_ms, true))
{
childOutput += std::string_view{ buffer, gotBytes };
break;
}
// Timeout
[[fallthrough]];
default:
goto exit;
}
childOutput += std::string_view{ buffer, gotBytes };
}

CloseHandle(piProcInfo.hProcess);
exit:
CancelIo(readPipe.get());
return childOutput;
}
2 changes: 1 addition & 1 deletion src/common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wch
// is added to the .cpp file.
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)

std::optional<std::string> exec_and_read_output(const std::wstring_view command, const DWORD timeout = INFINITE);
std::optional<std::string> exec_and_read_output(const std::wstring_view command, DWORD timeout_ms = 30000);

// Helper class for various COM-related APIs, e.g working with security descriptors
template<typename T>
Expand Down

0 comments on commit 67d1985

Please sign in to comment.