Enable I/O redirection and other platform functions on Windows
authorIgor Kulaychuk <i.kulaychuk@samsung.com>
Wed, 22 Aug 2018 20:31:54 +0000 (23:31 +0300)
committerIgor Kulaychuk <i.kulaychuk@samsung.com>
Fri, 24 Aug 2018 15:36:22 +0000 (18:36 +0300)
src/debug/netcoredbg/CMakeLists.txt
src/debug/netcoredbg/platform.cpp
src/debug/netcoredbg/platform.h

index 5eb428a..13a8877 100644 (file)
@@ -64,7 +64,7 @@ endif()
 add_executable(netcoredbg ${netcoredbg_SRC})
 
 if (WIN32)
-    target_link_libraries(netcoredbg corguids)
+    target_link_libraries(netcoredbg corguids wsock32 ws2_32)
 else()
     target_link_libraries(netcoredbg corguids dl pthread)
 endif()
index cb8dbce..3a940a6 100644 (file)
@@ -2,13 +2,25 @@
 // Distributed under the MIT License.
 // See the LICENSE file in the project root for more information.
 
+#ifndef FEATURE_PAL
+// Turn off macro definitions named max and min in <windows.h> header file
+// to avoid compile error for std::max().
+#define NOMINMAX
+#endif
+#ifdef _MSC_VER
+// Disable compiler warning about unsafe std::copy
+#define _SCL_SECURE_NO_WARNINGS
+#endif
+
 #include "platform.h"
 
 #include <cstring>
 #include <set>
 #include <fstream>
 #include <thread>
+#include <algorithm>
 
+#ifdef FEATURE_PAL
 #include <dirent.h>
 #include <sys/stat.h>
 #include <dlfcn.h>
 #include <linux/limits.h>
 #endif
 
+#else
+#include <windows.h>
+#include <winsock2.h>
+#endif
 
 unsigned long OSPageSize()
 {
     static unsigned long pageSize = 0;
+#ifdef FEATURE_PAL
     if (pageSize == 0)
         pageSize = sysconf(_SC_PAGESIZE);
-
+#endif
     return pageSize;
 }
 
@@ -39,6 +56,462 @@ std::string GetFileName(const std::string &path)
     return i == std::string::npos ? path : path.substr(i + 1);
 }
 
+// From https://stackoverflow.com/questions/13541313/handle-socket-descriptors-like-file-descriptor-fstream-c-linux
+#ifdef WIN32
+typedef HANDLE fd_t;
+#define FD_INVALID_VALUE INVALID_HANDLE_VALUE
+#else
+typedef int fd_t;
+#define FD_INVALID_VALUE (-1)
+#endif
+
+class fdbuf : public std::streambuf
+{
+private:
+    enum { bufsize = 1024 };
+    char outbuf_[bufsize];
+    char inbuf_[bufsize + 16 - sizeof(int)];
+    fd_t  fd_;
+public:
+    typedef std::streambuf::traits_type traits_type;
+
+    fdbuf(fd_t fd);
+    virtual ~fdbuf();
+    void open(fd_t fd);
+    void close();
+
+protected:
+    int overflow(int c) override;
+    int underflow() override;
+    int sync() override;
+
+private:
+    int fdsync();
+
+    std::streamsize fdread(void *buf, size_t count);
+    std::streamsize fdwrite(const void *buf, size_t count);
+    void fdclose();
+};
+
+fdbuf::fdbuf(fd_t fd)
+  : fd_(FD_INVALID_VALUE) {
+    this->open(fd);
+}
+
+fdbuf::~fdbuf() {
+    if (this->fd_ != FD_INVALID_VALUE) {
+        this->fdsync();
+        this->fdclose();
+    }
+}
+
+std::streamsize fdbuf::fdread(void *buf, size_t count)
+{
+#ifdef WIN32
+    DWORD dwRead = 0;
+    BOOL bSuccess = ReadFile(this->fd_, buf, (DWORD)count, &dwRead, NULL);
+
+    if (!bSuccess)
+        dwRead = 0;
+
+    return dwRead;
+#else
+    return ::read(this->fd_, buf, count);
+#endif
+}
+
+std::streamsize fdbuf::fdwrite(const void *buf, size_t count)
+{
+#ifdef WIN32
+    DWORD dwWritten = 0;
+    BOOL bSuccess = WriteFile(this->fd_, buf, (DWORD)count, &dwWritten, NULL);
+
+    if (!bSuccess)
+        dwWritten = 0;
+
+    return dwWritten;
+#else
+    return ::write(this->fd_, buf, count);
+#endif
+}
+
+void fdbuf::fdclose()
+{
+#ifdef WIN32
+    CloseHandle(this->fd_);
+#else
+    ::close(this->fd_);
+#endif
+}
+
+void fdbuf::open(fd_t fd) {
+    this->close();
+    this->fd_ = fd;
+    this->setg(this->inbuf_, this->inbuf_, this->inbuf_);
+    this->setp(this->outbuf_, this->outbuf_ + bufsize - 1);
+}
+
+void fdbuf::close() {
+    if (!(this->fd_ < 0)) {
+        this->sync();
+        this->fdclose();
+    }
+}
+
+int fdbuf::overflow(int c) {
+    if (!traits_type::eq_int_type(c, traits_type::eof())) {
+        *this->pptr() = traits_type::to_char_type(c);
+        this->pbump(1);
+    }
+    return this->sync() == -1
+        ? traits_type::eof()
+        : traits_type::not_eof(c);
+}
+
+int fdbuf::sync() {
+    return fdsync();
+}
+
+int fdbuf::fdsync() {
+    if (this->pbase() != this->pptr()) {
+        std::streamsize size(this->pptr() - this->pbase());
+        std::streamsize done(fdwrite(this->outbuf_, size));
+        // The code below assumes that it is success if the stream made
+        // some progress. Depending on the needs it may be more
+        // reasonable to consider it a success only if it managed to
+        // write the entire buffer and, e.g., loop a couple of times
+        // to try achieving this success.
+        if (0 < done) {
+            std::copy(this->pbase() + done, this->pptr(), this->pbase());
+            this->setp(this->pbase(), this->epptr());
+            this->pbump((int)(size - done));
+        }
+    }
+    return this->pptr() != this->epptr()? 0: -1;
+}
+
+int fdbuf::underflow()
+{
+    if (this->gptr() == this->egptr()) {
+        std::streamsize pback(std::min(this->gptr() - this->eback(),
+                                       std::ptrdiff_t(16 - sizeof(int))));
+        std::copy(this->egptr() - pback, this->egptr(), this->eback());
+        int done((int)fdread(this->eback() + pback, bufsize));
+        this->setg(this->eback(),
+                   this->eback() + pback,
+                   this->eback() + pback + std::max(0, done));
+    }
+    return this->gptr() == this->egptr()
+        ? traits_type::eof()
+        : traits_type::to_int_type(*this->gptr());
+}
+
+#ifdef FEATURE_PAL
+struct IORedirectServerHandles
+{
+    int m_sockFd;
+    int m_realStdInFd;
+    int m_realStdOutFd;
+    int m_realStdErrFd;
+    int m_appStdIn;
+
+    IORedirectServerHandles() :
+        m_sockFd(-1),
+        m_realStdInFd(STDIN_FILENO),
+        m_realStdOutFd(STDOUT_FILENO),
+        m_realStdErrFd(STDERR_FILENO),
+        m_appStdIn(-1)
+    {
+    }
+
+    ~IORedirectServerHandles()
+    {
+        if (m_sockFd != -1)
+            ::close(m_sockFd);
+    }
+
+    bool IsConnected() const { return m_sockFd != -1; }
+
+    void RedirectOutput(
+        std::function<void(std::string)> onStdOut,
+        std::function<void(std::string)> onStdErr);
+
+    int WaitForConnection(uint16_t port);
+};
+#else
+struct IORedirectServerHandles
+{
+    SOCKET m_sockFd;
+    HANDLE m_realStdInFd;
+    HANDLE m_realStdOutFd;
+    HANDLE m_realStdErrFd;
+    HANDLE m_appStdIn;
+
+    IORedirectServerHandles() :
+        m_sockFd(INVALID_SOCKET),
+        m_realStdInFd(GetStdHandle(STD_INPUT_HANDLE)),
+        m_realStdOutFd(GetStdHandle(STD_OUTPUT_HANDLE)),
+        m_realStdErrFd(GetStdHandle(STD_ERROR_HANDLE)),
+        m_appStdIn(INVALID_HANDLE_VALUE)
+    {
+    }
+
+    ~IORedirectServerHandles()
+    {
+        if (m_sockFd != INVALID_SOCKET)
+        {
+            ::closesocket(m_sockFd);
+        }
+        WSACleanup();
+    }
+
+    bool IsConnected() const { return m_sockFd != INVALID_SOCKET; }
+
+    void RedirectOutput(
+        std::function<void(std::string)> onStdOut,
+        std::function<void(std::string)> onStdErr);
+
+    SOCKET WaitForConnection(uint16_t port);
+};
+#endif
+
+#ifndef FEATURE_PAL
+
+void AddFilesFromDirectoryToTpaList(const std::string &directory, std::string& tpaList)
+{
+    const char * const tpaExtensions[] = {
+        "*.ni.dll",      // Probe for .ni.dll first so that it's preferred if ni and il coexist in the same dir
+        "*.dll",
+        "*.ni.exe",
+        "*.exe",
+    };
+
+    std::set<std::string> addedAssemblies;
+
+    // Walk the directory for each extension separately so that we first get files with .ni.dll extension,
+    // then files with .dll extension, etc.
+    for (int extIndex = 0; extIndex < sizeof(tpaExtensions) / sizeof(tpaExtensions[0]); extIndex++)
+    {
+        const char* ext = tpaExtensions[extIndex];
+        size_t extLength = strlen(ext);
+
+        std::string assemblyPath(directory);
+        assemblyPath.append("\\");
+        assemblyPath.append(tpaExtensions[extIndex]);
+
+        WIN32_FIND_DATAA data;
+        HANDLE findHandle = FindFirstFileA(assemblyPath.c_str(), &data);
+
+        if (findHandle != INVALID_HANDLE_VALUE)
+        {
+            do
+            {
+                if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+                {
+
+                    std::string filename(data.cFileName);
+                    size_t extPos = filename.length() - extLength;
+                    std::string filenameWithoutExt(filename.substr(0, extPos));
+
+                    // Make sure if we have an assembly with multiple extensions present,
+                    // we insert only one version of it.
+                    if (addedAssemblies.find(filenameWithoutExt) == addedAssemblies.end())
+                    {
+                        addedAssemblies.insert(filenameWithoutExt);
+
+                        tpaList.append(directory);
+                        tpaList.append("\\");
+                        tpaList.append(filename);
+                        tpaList.append(";");
+                    }
+                }
+            }
+            while (0 != FindNextFileA(findHandle, &data));
+
+            FindClose(findHandle);
+        }
+    }
+}
+
+std::string GetExeAbsPath()
+{
+    char hostPath[MAX_LONGPATH + 1];
+    if (::GetModuleFileNameA(NULL, hostPath, MAX_LONGPATH) == 0)
+    {
+        return false;
+    }
+
+    return std::string(hostPath);
+}
+
+bool SetWorkDir(const std::string &path)
+{
+    return SetCurrentDirectoryA(path.c_str());
+}
+
+void USleep(uint32_t duration)
+{
+    HANDLE timer;
+    LARGE_INTEGER ft;
+
+    ft.QuadPart = -(10*(int32_t)duration); // Convert to 100 nanosecond interval, negative value indicates relative time
+
+    timer = CreateWaitableTimer(NULL, TRUE, NULL);
+    SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
+    WaitForSingleObject(timer, INFINITE);
+    CloseHandle(timer);
+}
+
+void *DLOpen(const std::string &path)
+{
+    return LoadLibraryA(path.c_str());
+}
+
+void *DLSym(void *handle, const std::string &name)
+{
+    return GetProcAddress((HMODULE)handle, name.c_str());
+}
+
+void UnsetCoreCLREnv()
+{
+    _putenv("CORECLR_ENABLE_PROFILING=");
+}
+
+SOCKET IORedirectServerHandles::WaitForConnection(uint16_t port)
+{
+    WSADATA wsa;
+    SOCKET newsockfd;
+    int clilen;
+    struct sockaddr_in serv_addr, cli_addr;
+
+    if (port == 0)
+        return INVALID_SOCKET;
+
+    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
+    {
+        return INVALID_SOCKET;
+    }
+
+    m_sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
+    if (m_sockFd == INVALID_SOCKET)
+    {
+        WSACleanup();
+        return INVALID_SOCKET;
+    }
+
+    BOOL enable = 1;
+    if (setsockopt(m_sockFd, SOL_SOCKET, SO_REUSEADDR, (const char *)&enable, sizeof(BOOL)) == SOCKET_ERROR)
+    {
+        ::closesocket(m_sockFd);
+        WSACleanup();
+        m_sockFd = INVALID_SOCKET;
+        return INVALID_SOCKET;
+    }
+    memset(&serv_addr, 0, sizeof(serv_addr));
+
+    serv_addr.sin_family = AF_INET;
+    serv_addr.sin_addr.s_addr = INADDR_ANY;
+    serv_addr.sin_port = htons(port);
+
+    if (::bind(m_sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
+    {
+        ::closesocket(m_sockFd);
+        WSACleanup();
+        m_sockFd = INVALID_SOCKET;
+        return INVALID_SOCKET;
+    }
+
+    ::listen(m_sockFd, 5);
+
+    CloseHandle(m_realStdInFd);
+    CloseHandle(m_realStdOutFd);
+    CloseHandle(m_realStdErrFd);
+    m_realStdInFd = INVALID_HANDLE_VALUE;
+    m_realStdOutFd = INVALID_HANDLE_VALUE;
+    m_realStdErrFd = INVALID_HANDLE_VALUE;
+
+    clilen = sizeof(cli_addr);
+    newsockfd = ::accept(m_sockFd, (struct sockaddr *) &cli_addr, &clilen);
+    if (newsockfd == INVALID_SOCKET)
+    {
+        ::closesocket(m_sockFd);
+        WSACleanup();
+        m_sockFd = INVALID_SOCKET;
+        return INVALID_SOCKET;
+    }
+
+    return newsockfd;
+}
+
+#define BUFSIZE 4096
+
+static std::function<void()> GetFdReadFunction(HANDLE h, std::function<void(std::string)> cb)
+{
+    return [h, cb]() {
+        char buffer[BUFSIZE];
+
+        while (true)
+        {
+            DWORD dwRead = 0;
+            BOOL bSuccess = ReadFile(h, buffer, BUFSIZE, &dwRead, NULL);
+
+            if (!bSuccess || dwRead == 0)
+            {
+                break;
+            }
+            cb(std::string(buffer, dwRead));
+        }
+    };
+}
+
+void IORedirectServerHandles::RedirectOutput(
+    std::function<void(std::string)> onStdOut,
+    std::function<void(std::string)> onStdErr)
+{
+    SECURITY_ATTRIBUTES saAttr;
+
+    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+    saAttr.bInheritHandle = TRUE;
+    saAttr.lpSecurityDescriptor = NULL;
+
+    HANDLE newStdOutRd;
+    HANDLE newStdOutWr;
+
+    if (!CreatePipe(&newStdOutRd, &newStdOutWr, &saAttr, 0))
+        return;
+    if (!SetHandleInformation(newStdOutRd, HANDLE_FLAG_INHERIT, 0))
+        return;
+    if (!SetStdHandle(STD_OUTPUT_HANDLE, newStdOutWr))
+        return;
+
+    HANDLE newStdErrRd;
+    HANDLE newStdErrWr;
+
+    if (!CreatePipe(&newStdErrRd, &newStdErrWr, &saAttr, 0))
+        return;
+    if (!SetHandleInformation(newStdErrRd, HANDLE_FLAG_INHERIT, 0))
+        return;
+    if (!SetStdHandle(STD_ERROR_HANDLE, newStdErrWr))
+        return;
+
+    HANDLE newStdInRd;
+    HANDLE newStdInWr;
+
+    if (!CreatePipe(&newStdInRd, &newStdInWr, &saAttr, 0))
+        return;
+    if (!SetHandleInformation(newStdInWr, HANDLE_FLAG_INHERIT, 0))
+        return;
+    if (!SetStdHandle(STD_INPUT_HANDLE, newStdInRd))
+        return;
+
+    m_appStdIn = newStdInWr;
+
+    std::thread(GetFdReadFunction(newStdOutRd, onStdOut)).detach();
+    std::thread(GetFdReadFunction(newStdErrRd, onStdErr)).detach();
+}
+
+#else
+
 void AddFilesFromDirectoryToTpaList(const std::string &directory, std::string &tpaList)
 {
     const char * const tpaExtensions[] = {
@@ -192,160 +665,24 @@ void UnsetCoreCLREnv()
     unsetenv("CORECLR_ENABLE_PROFILING");
 }
 
-// From https://stackoverflow.com/questions/13541313/handle-socket-descriptors-like-file-descriptor-fstream-c-linux
-class fdbuf : public std::streambuf
-{
-private:
-    enum { bufsize = 1024 };
-    char outbuf_[bufsize];
-    char inbuf_[bufsize + 16 - sizeof(int)];
-    int  fd_;
-public:
-    typedef std::streambuf::traits_type traits_type;
-
-    fdbuf(int fd);
-    virtual ~fdbuf();
-    void open(int fd);
-    void close();
-
-protected:
-    int overflow(int c) override;
-    int underflow() override;
-    int sync() override;
-
-private:
-    int fdsync();
-};
-
-fdbuf::fdbuf(int fd)
-  : fd_(-1) {
-    this->open(fd);
-}
-
-fdbuf::~fdbuf() {
-    if (!(this->fd_ < 0)) {
-        this->fdsync();
-        ::close(this->fd_);
-    }
-}
-
-void fdbuf::open(int fd) {
-    this->close();
-    this->fd_ = fd;
-    this->setg(this->inbuf_, this->inbuf_, this->inbuf_);
-    this->setp(this->outbuf_, this->outbuf_ + bufsize - 1);
-}
-
-void fdbuf::close() {
-    if (!(this->fd_ < 0)) {
-        this->sync();
-        ::close(this->fd_);
-    }
-}
-
-int fdbuf::overflow(int c) {
-    if (!traits_type::eq_int_type(c, traits_type::eof())) {
-        *this->pptr() = traits_type::to_char_type(c);
-        this->pbump(1);
-    }
-    return this->sync() == -1
-        ? traits_type::eof()
-        : traits_type::not_eof(c);
-}
-
-int fdbuf::sync() {
-    return fdsync();
-}
-
-int fdbuf::fdsync() {
-    if (this->pbase() != this->pptr()) {
-        std::streamsize size(this->pptr() - this->pbase());
-        std::streamsize done(::write(this->fd_, this->outbuf_, size));
-        // The code below assumes that it is success if the stream made
-        // some progress. Depending on the needs it may be more
-        // reasonable to consider it a success only if it managed to
-        // write the entire buffer and, e.g., loop a couple of times
-        // to try achieving this success.
-        if (0 < done) {
-            std::copy(this->pbase() + done, this->pptr(), this->pbase());
-            this->setp(this->pbase(), this->epptr());
-            this->pbump(size - done);
-        }
-    }
-    return this->pptr() != this->epptr()? 0: -1;
-}
-
-int fdbuf::underflow()
-{
-    if (this->gptr() == this->egptr()) {
-        std::streamsize pback(std::min(this->gptr() - this->eback(),
-                                       std::ptrdiff_t(16 - sizeof(int))));
-        std::copy(this->egptr() - pback, this->egptr(), this->eback());
-        int done(::read(this->fd_, this->eback() + pback, bufsize));
-        this->setg(this->eback(),
-                   this->eback() + pback,
-                   this->eback() + pback + std::max(0, done));
-    }
-    return this->gptr() == this->egptr()
-        ? traits_type::eof()
-        : traits_type::to_int_type(*this->gptr());
-}
-
-IORedirectServer::IORedirectServer(
-    uint16_t port,
-    std::function<void(std::string)> onStdOut,
-    std::function<void(std::string)> onStdErr) :
-    m_in(nullptr),
-    m_out(nullptr),
-    m_sockfd(-1),
-    m_realStdInFd(STDIN_FILENO),
-    m_realStdOutFd(STDOUT_FILENO),
-    m_realStdErrFd(STDERR_FILENO),
-    m_appStdIn(-1)
-{
-    RedirectOutput(onStdOut, onStdErr);
-    int fd = WaitForConnection(port);
-
-    if (fd != -1)
-    {
-        m_in = new fdbuf(fd);
-        m_out = new fdbuf(fd);
-    }
-    else
-    {
-        m_in = new fdbuf(m_realStdInFd);
-        m_out = new fdbuf(m_realStdOutFd);
-    }
-    m_err = new fdbuf(m_realStdErrFd);
-
-    m_prevIn = std::cin.rdbuf();
-    m_prevOut = std::cout.rdbuf();
-    m_prevErr = std::cerr.rdbuf();
-
-    std::cin.rdbuf(m_in);
-    std::cout.rdbuf(m_out);
-    std::cerr.rdbuf(m_err);
-}
-
-int IORedirectServer::WaitForConnection(uint16_t port)
+int IORedirectServerHandles::WaitForConnection(uint16_t port)
 {
     int newsockfd;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;
-    int n;
 
     if (port == 0)
         return -1;
 
-    m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
-    if (m_sockfd < 0)
+    m_sockFd = ::socket(AF_INET, SOCK_STREAM, 0);
+    if (m_sockFd < 0)
         return -1;
 
     int enable = 1;
-    if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
+    if (setsockopt(m_sockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
     {
-        ::close(m_sockfd);
-        m_sockfd = -1;
+        ::close(m_sockFd);
+        m_sockFd = -1;
         return -1;
     }
     memset(&serv_addr, 0, sizeof(serv_addr));
@@ -354,14 +691,14 @@ int IORedirectServer::WaitForConnection(uint16_t port)
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(port);
 
-    if (::bind(m_sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+    if (::bind(m_sockFd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
     {
-        ::close(m_sockfd);
-        m_sockfd = -1;
+        ::close(m_sockFd);
+        m_sockFd = -1;
         return -1;
     }
 
-    ::listen(m_sockfd, 5);
+    ::listen(m_sockFd, 5);
 
     // On Tizen, launch_app won't terminate until stdin, stdout and stderr are closed.
     // But Visual Studio initiates the connection only after launch_app termination,
@@ -374,28 +711,17 @@ int IORedirectServer::WaitForConnection(uint16_t port)
     m_realStdErrFd = -1;
 
     clilen = sizeof(cli_addr);
-    newsockfd = ::accept(m_sockfd, (struct sockaddr *) &cli_addr, &clilen);
+    newsockfd = ::accept(m_sockFd, (struct sockaddr *) &cli_addr, &clilen);
     if (newsockfd < 0)
     {
-        ::close(m_sockfd);
-        m_sockfd = -1;
+        ::close(m_sockFd);
+        m_sockFd = -1;
         return -1;
     }
 
     return newsockfd;
 }
 
-IORedirectServer::~IORedirectServer()
-{
-    std::cin.rdbuf(m_prevIn);
-    std::cout.rdbuf(m_prevOut);
-    std::cout.rdbuf(m_prevErr);
-    delete m_in;
-    delete m_out;
-    delete m_err;
-    ::close(m_sockfd);
-}
-
 static std::function<void()> GetFdReadFunction(int fd, std::function<void(std::string)> cb)
 {
     return [fd, cb]() {
@@ -416,8 +742,9 @@ static std::function<void()> GetFdReadFunction(int fd, std::function<void(std::s
     };
 }
 
-void IORedirectServer::RedirectOutput(std::function<void(std::string)> onStdOut,
-                                      std::function<void(std::string)> onStdErr)
+void IORedirectServerHandles::RedirectOutput(
+    std::function<void(std::string)> onStdOut,
+    std::function<void(std::string)> onStdErr)
 {
     // TODO: fcntl(fd, F_SETFD, FD_CLOEXEC);
     m_realStdInFd = dup(STDIN_FILENO);
@@ -445,3 +772,62 @@ void IORedirectServer::RedirectOutput(std::function<void(std::string)> onStdOut,
     std::thread(GetFdReadFunction(outPipe[0], onStdOut)).detach();
     std::thread(GetFdReadFunction(errPipe[0], onStdErr)).detach();
 }
+
+#endif
+
+IORedirectServer::operator bool() const
+{
+    return m_handles->IsConnected();
+}
+
+IORedirectServer::IORedirectServer(
+    uint16_t port,
+    std::function<void(std::string)> onStdOut,
+    std::function<void(std::string)> onStdErr) :
+    m_in(nullptr),
+    m_out(nullptr),
+    m_handles(new IORedirectServerHandles())
+{
+    m_handles->RedirectOutput(onStdOut, onStdErr);
+
+#ifdef WIN32
+    SOCKET s = m_handles->WaitForConnection(port);
+    if (s != INVALID_SOCKET)
+    {
+        m_in = new fdbuf((HANDLE)s);
+        m_out = new fdbuf((HANDLE)s);
+    }
+#else
+    int fd = m_handles->WaitForConnection(port);
+    if (fd != -1)
+    {
+        m_in = new fdbuf(fd);
+        m_out = new fdbuf(fd);
+    }
+#endif
+    else
+    {
+        m_in = new fdbuf(m_handles->m_realStdInFd);
+        m_out = new fdbuf(m_handles->m_realStdOutFd);
+    }
+    m_err = new fdbuf(m_handles->m_realStdErrFd);
+
+    m_prevIn = std::cin.rdbuf();
+    m_prevOut = std::cout.rdbuf();
+    m_prevErr = std::cerr.rdbuf();
+
+    std::cin.rdbuf(m_in);
+    std::cout.rdbuf(m_out);
+    std::cerr.rdbuf(m_err);
+}
+
+IORedirectServer::~IORedirectServer()
+{
+    std::cin.rdbuf(m_prevIn);
+    std::cout.rdbuf(m_prevOut);
+    std::cout.rdbuf(m_prevErr);
+    delete m_in;
+    delete m_out;
+    delete m_err;
+    delete m_handles;
+}
index b77fde5..b9eabcf 100644 (file)
@@ -7,6 +7,8 @@
 #include <iostream>
 #include <functional>
 
+#include <palclr.h>
+
 unsigned long OSPageSize();
 void AddFilesFromDirectoryToTpaList(const std::string &directory, std::string &tpaList);
 std::string GetExeAbsPath();
@@ -17,6 +19,8 @@ void *DLOpen(const std::string &path);
 void *DLSym(void *handle, const std::string &name);
 void UnsetCoreCLREnv();
 
+struct IORedirectServerHandles;
+
 class IORedirectServer
 {
     std::streambuf *m_in;
@@ -25,21 +29,13 @@ class IORedirectServer
     std::streambuf *m_prevIn;
     std::streambuf *m_prevOut;
     std::streambuf *m_prevErr;
-    int m_sockfd;
-    int m_realStdInFd;
-    int m_realStdOutFd;
-    int m_realStdErrFd;
-    int m_appStdIn;
+    IORedirectServerHandles *m_handles;
 
-    void RedirectOutput(
-        std::function<void(std::string)> onStdOut,
-        std::function<void(std::string)> onStdErr);
-    int WaitForConnection(uint16_t port);
 public:
     IORedirectServer(
         uint16_t port,
         std::function<void(std::string)> onStdOut,
         std::function<void(std::string)> onStdErr);
     ~IORedirectServer();
-    operator bool() const { return m_sockfd != -1; }
+    operator bool() const;
 };