Browse Source

Windows IPC work in progress

JoshEngebretson 10 years ago
parent
commit
92fccc0854

+ 2 - 2
Source/Atomic/IPC/IPC.cpp

@@ -35,9 +35,9 @@ IPC::~IPC()
 
 bool IPC::InitWorker(IPCHandle fd1, IPCHandle fd2)
 {
-    // close server fd
-
+    
 #ifndef ATOMIC_PLATFORM_WINDOWS
+    // close server fd
     close(fd1);
 #endif
 

+ 19 - 7
Source/Atomic/IPC/IPCBroker.cpp

@@ -66,19 +66,31 @@ bool IPCBroker::SpawnWorker(const String& command, const Vector<String>& args, c
 
     otherProcess_ = new IPCProcess(context_, pp_.fd1(), pp_.fd2());
 
-    transport_.OpenServer(otherProcess_->fd1());
+    transport_.OpenServer(pp_.fd1());
 
     // copy args
     for (unsigned i = 0; i < args.Size(); i++)
         pargs.Push(args[i]);
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
-    // fd2 with be a HANDLE on windows
-    /*
-        wchar_t pipe_num[10];
-        _i64tow_s(reinterpret_cast<__int64>(pp.fd2()), pipe_num, sizeof(pipe_num)/sizeof(pipe_num[0]), 10);
-        writable_cmdline += kCmdLinePipeEq + std::wstring(pipe_num);
-    */
+
+    wchar_t pipe_num[10];
+    _i64tow_s(reinterpret_cast<__int64>(pp_.fd2()), pipe_num, sizeof(pipe_num)/sizeof(pipe_num[0]), 10);
+    
+    String cpipe;
+    cpipe.SetUTF8FromWChar(pipe_num);
+
+    String ipc_client = "--ipc-client=" + cpipe;
+    pargs.Push(ipc_client);
+
+    _i64tow_s(reinterpret_cast<__int64>(pp_.fd1()), pipe_num, sizeof(pipe_num)/sizeof(pipe_num[0]), 10);
+
+    String spipe;
+    spipe.SetUTF8FromWChar(pipe_num);
+
+    String ipc_server = "--ipc-server=" + spipe;
+    pargs.Push(ipc_server);
+
 #else
     pargs.Push(ToString("--ipc-server=%i", pp_.fd1()));
     pargs.Push(ToString("--ipc-client=%i", pp_.fd2()));

+ 1 - 0
Source/Atomic/IPC/IPCTypes.h

@@ -17,6 +17,7 @@ typedef void* IPCHandle;
 #else
 
 typedef int IPCHandle;
+#define INVALID_IPCHANDLE_VALUE -1
 
 #endif
 

+ 212 - 48
Source/Atomic/IPC/IPCWindows.cpp

@@ -1,79 +1,243 @@
 #ifdef ATOMIC_PLATFORM_WINDOWS
 
+#include <Windows.h>
+#include <Sddl.h>
+#include <AccCtrl.h>
+#include <Aclapi.h>
+#include <string>
+
 #include "IPCWindows.h"
 
+typedef std::wstring IPCWString;
+
 namespace Atomic
 {
 
-PipePair::PipePair(bool inherit_fd2)
-{
-    srv_ = INVALID_IPCHANDLE_VALUE;
-    cln_ = INVALID_IPCHANDLE_VALUE;
+    static const wchar_t kPipePrefix[] = L"\\\\.\\pipe\\";
+    static const int kPipeBufferSz = 4 * 1024;
+    static LONG g_pipe_seq = 0;
 
-}
+    bool checkIntegritySupport()
+    {
+        OSVERSIONINFO osvi;
 
-PipeWin::PipeWin() : pipe_(INVALID_IPCHANDLE_VALUE) {
-}
+        ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+        osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+        GetVersionEx(&osvi);
 
-PipeWin::~PipeWin()
-{
+        return osvi.dwMajorVersion > 5;
+    }
 
-}
+    HANDLE PipePair::OpenPipeServer(const wchar_t* name, bool low_integrity) {
+        SECURITY_ATTRIBUTES sa = { 0 };
+        SECURITY_ATTRIBUTES *psa = 0;
+
+        static const bool is_integrity_supported = false;// checkIntegritySupport();
+
+        if (is_integrity_supported && low_integrity) {
+            psa = &sa;
+            sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+            sa.bInheritHandle = TRUE;
+            if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
+                TEXT("S:(ML;;NWNR;;;LW)"), SDDL_REVISION_1, &sa.lpSecurityDescriptor, NULL))
+                return INVALID_HANDLE_VALUE;
+        }
+
+        IPCWString pipename(kPipePrefix);
+        pipename.append(name);
+        return ::CreateNamedPipeW(pipename.c_str(), PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
+            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+            1, kPipeBufferSz, kPipeBufferSz, 200, psa);
+    }
 
-bool PipeWin::OpenClient(IPCHandle pipe) {
-    return true;
-}
+    HANDLE PipePair::OpenPipeClient(const wchar_t* name, bool inherit, bool impersonate) {
+        IPCWString pipename(kPipePrefix);
+        pipename.append(name);
+
+        SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, inherit ? TRUE : FALSE };
+        for (;;) {
+            DWORD attributes = impersonate ? 0 : SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION;
+            HANDLE pipe = ::CreateFileW(pipename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,
+                OPEN_EXISTING, attributes, NULL);
+
+            if (INVALID_HANDLE_VALUE == pipe) {
+                if (ERROR_PIPE_BUSY != ::GetLastError()) {
+                    return pipe;
+                }
+                // wait and retry.
+                ::Sleep(25);
+            }
+            else {
+                // success.
+                return pipe;
+            }
+        }
+    }
 
-bool PipeWin::OpenServer(IPCHandle pipe, bool connect) {
-    return true;
-}
+    PipePair::PipePair(bool inherit_fd2) :
+        srv_(INVALID_IPCHANDLE_VALUE),
+        cln_(INVALID_IPCHANDLE_VALUE)
+    {
+        // Come up with a reasonable unique name.
+        const wchar_t kPipePattern[] = L"ko.%x.%x.%x";
+        
+        wchar_t name[8 * 3 + sizeof(kPipePattern)];
+        
+        ::wsprintfW(name, kPipePattern, ::GetCurrentProcessId(), ::GetTickCount(),
+            ::InterlockedIncrement(&g_pipe_seq));
+        
+        HANDLE server = OpenPipeServer(name);
+        
+        if (INVALID_HANDLE_VALUE == server) 
+        {
+            return;
+        }
+        
+        // Don't allow client impersonation.
+        HANDLE client = OpenPipeClient(name, inherit_fd2, false);
+
+        if (INVALID_HANDLE_VALUE == client) 
+        {
+            ::CloseHandle(server);
+            return;
+        }
+        if (!::ConnectNamedPipe(server, NULL)) 
+        {
+            if (ERROR_PIPE_CONNECTED != ::GetLastError()) 
+            {
+                ::CloseHandle(server);
+                ::CloseHandle(client);
+                return;
+            }
+        }
+
+        srv_ = server;
+        cln_ = client;
 
+    }
 
-bool PipeWin::Write(const void* buf, size_t sz)
-{
-    return true;
-}
+    PipeWin::PipeWin() : pipe_(INVALID_IPCHANDLE_VALUE)
+    {
 
-bool PipeWin::Read(void* buf, size_t* sz) {
-    return true;
-}
+    }
+
+    PipeWin::~PipeWin()
+    {
+        if (pipe_ != INVALID_HANDLE_VALUE) 
+        {
+            ::DisconnectNamedPipe(pipe_);  // $$$ disconect is valid on the server side.
+            ::CloseHandle(pipe_);
+        }
+    }
 
+    bool PipeWin::OpenClient(IPCHandle pipe)
+    {
+        pipe_ = pipe;
+        return true;
 
-char* PipeTransport::Receive(size_t* size) {
-    if (buf_.Size() < kBufferSz) {
-        buf_.Resize(kBufferSz);
     }
 
-    *size = kBufferSz;
-    if (!Read(&buf_[0], size)) {
-        return NULL;
+    bool PipeWin::OpenServer(IPCHandle pipe, bool connect)
+    {
+        pipe_ = pipe;
+
+        if (connect) 
+        {
+            if (!::ConnectNamedPipe(pipe, NULL)) 
+            {
+                if (ERROR_PIPE_CONNECTED != ::GetLastError()) 
+                {
+                    return false;
+                }
+            }
+        }
+
+        return true;
     }
-    return &buf_[0];
-}
 
 
-IPCProcess::IPCProcess(Context* context, IPCHandle fd1, IPCHandle fd2, IPCHandle pid) : Object(context),
-    pid_(pid),
-    fd1_(fd1),
-    fd2_(fd2)
-{
-}
+    bool PipeWin::Write(const void* buf, size_t sz)
+    {
+        DWORD written = 0;
+        if (TRUE == ::WriteFile(pipe_, buf, sz, &written, NULL))
+            return true;
 
-IPCProcess::~IPCProcess()
-{
+        return false;
+    }
 
-}
+    bool PipeWin::Read(void* buf, size_t* sz)
+    {
+        if (TRUE == ::ReadFile(pipe_, buf, *sz, reinterpret_cast<DWORD*>(sz), NULL))
+        {
+            return true;
+        }
 
-bool IPCProcess::IsRunning()
-{
-    return false;
+        return false;
+    }
 
-}
+    char* PipeTransport::Receive(size_t* size)
+    {
+        if (buf_.Size() < kBufferSz) 
+        {
+            buf_.Resize(kBufferSz);
+        }
 
-bool IPCProcess::Launch(const String& command, const Vector<String>& args, const String& initialDirectory)
-{
-    return false;
-}
+        *size = kBufferSz;
+
+        if (!Read(&buf_[0], size)) 
+        {
+            return NULL;
+        }
+        return &buf_[0];
+    }
+
+
+    IPCProcess::IPCProcess(Context* context, IPCHandle fd1, IPCHandle fd2, IPCHandle pid) : Object(context),
+        pid_(pid),
+        fd1_(fd1),
+        fd2_(fd2)
+    {
+    }
+
+    IPCProcess::~IPCProcess()
+    {
+
+    }
+
+    bool IPCProcess::IsRunning()
+    {
+        return true;
+
+    }
+
+    bool IPCProcess::Launch(const String& command, const Vector<String>& args, const String& initialDirectory)
+    {
+        STARTUPINFOW si = { sizeof(si) };
+        PROCESS_INFORMATION pi = { 0 };
+
+        // CreateProcess wants a single string
+        String sargs;
+        sargs.Join(args, " ");
+
+        // convert to wide
+        WString wcommand(command);
+
+        // prepend the command and convert to wide
+        WString wargs("\"" + command + "\" " + sargs);
+
+        // The child process inherits the pipe handle.
+        if (!::CreateProcessW(wcommand.CString(), (LPWSTR) wargs.CString(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
+            return false;
+        }
+
+        pid_ = pi.hProcess;
+        ::CloseHandle(pi.hThread);
+        
+        // The client side of the pipe has been already duplicated into the worker process.
+        ::CloseHandle(fd2_);
+
+        return true;
+    }
 
 }
 

+ 1 - 1
Source/Atomic/IPC/IPCWindows.h

@@ -12,7 +12,7 @@ namespace Atomic
 class PipePair {
 
 public:
-    PipePair(bool inherit_fd2 = false);
+    PipePair(bool inherit_fd2 = true);
     IPCHandle fd1() const { return srv_; }
     IPCHandle fd2() const { return cln_; }
 

+ 4 - 2
Source/Atomic/IPC/IPCWorker.cpp

@@ -19,7 +19,9 @@ IPCWorker::IPCWorker(IPCHandle fd, Context* context) : IPCChannel(context),
     fd_(fd)
 {
 
-#ifndef ATOMIC_PLATFORM_WINDOWS
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    otherProcess_ = new IPCProcess(context_, INVALID_IPCHANDLE_VALUE, fd_, INVALID_IPCHANDLE_VALUE);
+#else
     otherProcess_ = new IPCProcess(context_, -1, fd, getppid());
 #endif
 
@@ -64,7 +66,7 @@ void IPCWorker::ThreadFunction()
 
         if (!Receive())
         {
-            break;
+           break;
         }
     }
 

+ 35 - 5
Source/AtomicEditor/Source/Player/AEPlayerApplication.cpp

@@ -58,8 +58,8 @@ static Javascript* javascript = NULL;
 AEPlayerApplication::AEPlayerApplication(Context* context) :
     Application(context)
 {
-    fd_[0] = -1;
-    fd_[1] = -1;
+    fd_[0] = INVALID_IPCHANDLE_VALUE;
+    fd_[1] = INVALID_IPCHANDLE_VALUE;
 
 #ifdef ATOMIC_3D
     RegisterEnvironmentLibrary(context_);
@@ -113,14 +113,30 @@ void AEPlayerApplication::Setup()
                 LOGINFOF("Starting IPCWorker %s", argument.CString());
 
                 Vector<String> ipc = argument.Split(argument.CString(), '=');
+
                 if (ipc.Size() == 2)
                 {
-                    int fd = ToInt(ipc[1].CString());
                     if (argument.StartsWith("--ipc-server="))
+                    {
+#ifdef ATOMIC_PLATFORM_WINDOWS
+                        WString wipc(ipc[1]);
+                        HANDLE pipe = reinterpret_cast<HANDLE>(_wtoi64(wipc.CString()));
+                        fd_[0] = pipe;
+#else
+                        int fd = ToInt(ipc[1].CString());
                         fd_[0] = fd;
+#endif
+                    }
                     else
                     {
+#ifdef ATOMIC_PLATFORM_WINDOWS
+                        WString wipc(ipc[1]);
+                        HANDLE pipe = reinterpret_cast<HANDLE>(_wtoi64(wipc.CString()));
+                        fd_[1] = pipe;
+#else
+                        int fd = ToInt(ipc[1].CString());
                         fd_[1] = fd;
+#endif
                     }
 
                 }
@@ -130,7 +146,6 @@ void AEPlayerApplication::Setup()
             {
                 engineParameters_["ResourcePrefixPath"] = "";
 
-
                 // This works for a local dev build, --editor-resource-paths command below is for
                 // launching from AtomicEditor (IPC)
 
@@ -169,12 +184,27 @@ void AEPlayerApplication::Start()
 
     SubscribeToEvent(E_IPCHELLOFROMBROKER, HANDLER(AEPlayerApplication, HandleHelloFromBroker));
 
-    if (fd_[0] != -1 && fd_[1] != -1)
+#ifdef ATOMIC_PLATFORM_WINDOWS
+    if (fd_[0] != INVALID_IPCHANDLE_VALUE)
+    {
+        //::CloseHandle(fd_[0]);
+        fd_[0] = INVALID_IPCHANDLE_VALUE;
+    }
+
+    if (fd_[1] != INVALID_IPCHANDLE_VALUE)
     {
         IPC* ipc = new IPC(context_);
         context_->RegisterSubsystem(ipc);
         //ipc->InitWorker(fd_[0], fd_[1]);
     }
+#else
+    if (fd_[0] != INVALID_IPCHANDLE_VALUE && fd_[1] != INVALID_IPCHANDLE_VALUE)
+    {
+        IPC* ipc = new IPC(context_);
+        context_->RegisterSubsystem(ipc);
+        ipc->InitWorker(fd_[0], fd_[1]);
+    }
+#endif
 
     // Instantiate and register the Javascript subsystem
     javascript = new Javascript(context_);

+ 2 - 2
Source/AtomicEditor/Source/Player/AEPlayerApplication.h

@@ -22,6 +22,7 @@
 
 #pragma once
 
+#include <Atomic/IPC/IPCTypes.h>
 #include <Atomic/Engine/Application.h>
 
 using namespace Atomic;
@@ -54,8 +55,7 @@ private:
 
     void HandleHelloFromBroker(StringHash eventType, VariantMap& eventData);
 
-    int fd_[2];
-
+    IPCHandle fd_[2];
 };
 
 }

+ 10 - 1
Source/ToolCore/ToolEnvironment.cpp

@@ -113,7 +113,16 @@ void ToolEnvironment::SetRootBuildDir(const String& buildDir, bool setBinaryPath
 
     if (setBinaryPaths)
     {
-#ifdef ATOMIC_PLATFORM_OSX
+#ifdef ATOMIC_PLATFORM_WINDOWS
+
+#ifdef _DEBUG
+        playerBinary_ = rootBuildDir_ + "Source/AtomicPlayer/Debug/AtomicPlayer.exe";
+        editorBinary_ = rootBuildDir_ + "Source/AtomicEditor/Debug/AtomicEditor.exe";
+#else
+        playerBinary_ = rootBuildDir_ + "Source/AtomicPlayer/Release/AtomicPlayer.exe";
+        editorBinary_ = rootBuildDir_ + "Source/AtomicEditor/Release/AtomicEditor.exe";
+#endif
+#elif ATOMIC_PLATFORM_OSX
         playerBinary_ = rootBuildDir_ + "Source/AtomicPlayer/AtomicPlayer.app/Contents/MacOS/AtomicPlayer";
         editorBinary_ = rootBuildDir_ + "Source/AtomicEditor/AtomicEditor.app/Contents/MacOS/AtomicEditor";
 #endif