IPCWindows.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. //
  2. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #ifdef ATOMIC_PLATFORM_WINDOWS
  23. #include <Windows.h>
  24. #include <Sddl.h>
  25. #include <AccCtrl.h>
  26. #include <Aclapi.h>
  27. #include <string>
  28. #include "../Core/Timer.h"
  29. #include "../IO/Log.h"
  30. #include "IPCWindows.h"
  31. #include "IPC.h"
  32. typedef std::wstring IPCWString;
  33. namespace Atomic
  34. {
  35. static const wchar_t kPipePrefix[] = L"\\\\.\\pipe\\";
  36. static const int kPipeBufferSz = 4 * 1024;
  37. static LONG g_pipe_seq = 0;
  38. HANDLE PipePair::OpenPipeServer(const wchar_t* name, bool read)
  39. {
  40. IPCWString pipename(kPipePrefix);
  41. pipename.append(name);
  42. DWORD openMode = read ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
  43. return ::CreateNamedPipeW(pipename.c_str(), openMode,
  44. PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
  45. 1, kPipeBufferSz, kPipeBufferSz, 200, NULL);
  46. }
  47. HANDLE PipePair::OpenPipeClient(const wchar_t* name, bool read)
  48. {
  49. IPCWString pipename(kPipePrefix);
  50. pipename.append(name);
  51. SECURITY_ATTRIBUTES sa;
  52. sa.bInheritHandle = TRUE;
  53. sa.lpSecurityDescriptor = NULL;
  54. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  55. DWORD accessMode = read ? GENERIC_READ : GENERIC_WRITE;
  56. for (;;) {
  57. HANDLE pipe = ::CreateFileW(pipename.c_str(), accessMode, 0, &sa,
  58. OPEN_EXISTING, 0, NULL);
  59. if (INVALID_HANDLE_VALUE == pipe) {
  60. if (ERROR_PIPE_BUSY != ::GetLastError()) {
  61. return pipe;
  62. }
  63. // wait and retry.
  64. ::Sleep(25);
  65. }
  66. else {
  67. // success.
  68. return pipe;
  69. }
  70. }
  71. }
  72. PipePair::PipePair() :
  73. srvRead_(INVALID_IPCHANDLE_VALUE),
  74. srvWrite_(INVALID_IPCHANDLE_VALUE),
  75. clnRead_(INVALID_IPCHANDLE_VALUE),
  76. clnWrite_(INVALID_IPCHANDLE_VALUE)
  77. {
  78. // Come up with a reasonable unique name.
  79. const wchar_t kPipePattern[] = L"ko.%x.%x.%x";
  80. wchar_t serverReadName[8 * 3 + sizeof(kPipePattern)];
  81. ::wsprintfW(serverReadName, kPipePattern, ::GetCurrentProcessId(), ::GetTickCount(),
  82. ::InterlockedIncrement(&g_pipe_seq));
  83. wchar_t serverWriteName[8 * 3 + sizeof(kPipePattern)];
  84. ::wsprintfW(serverWriteName, kPipePattern, ::GetCurrentProcessId(), ::GetTickCount() + 1,
  85. ::InterlockedIncrement(&g_pipe_seq));
  86. srvRead_ = OpenPipeServer(serverReadName, true);
  87. srvWrite_ = OpenPipeServer(serverWriteName, false);
  88. // Don't allow client impersonation.
  89. clnRead_ = OpenPipeClient(serverWriteName, true);
  90. clnWrite_ = OpenPipeClient(serverReadName, false);
  91. /*
  92. if (INVALID_HANDLE_VALUE == client)
  93. {
  94. ::CloseHandle(server);
  95. return;
  96. }
  97. */
  98. if (!::ConnectNamedPipe(srvRead_, NULL))
  99. {
  100. if (ERROR_PIPE_CONNECTED != ::GetLastError())
  101. {
  102. // ::CloseHandle(server);
  103. //::CloseHandle(client);
  104. return;
  105. }
  106. }
  107. if (!::ConnectNamedPipe(srvWrite_, NULL))
  108. {
  109. if (ERROR_PIPE_CONNECTED != ::GetLastError())
  110. {
  111. // ::CloseHandle(server);
  112. //::CloseHandle(client);
  113. return;
  114. }
  115. }
  116. }
  117. PipeWin::PipeWin() : pipeRead_(INVALID_IPCHANDLE_VALUE), pipeWrite_(INVALID_IPCHANDLE_VALUE), readerThread_(this)
  118. {
  119. }
  120. PipeWin::~PipeWin()
  121. {
  122. readerThread_.Kill();
  123. if (pipeRead_ != INVALID_HANDLE_VALUE)
  124. {
  125. ::DisconnectNamedPipe(pipeRead_); // $$$ disconect is valid on the server side.
  126. ::CloseHandle(pipeRead_);
  127. }
  128. if (pipeWrite_ != INVALID_HANDLE_VALUE)
  129. {
  130. ::DisconnectNamedPipe(pipeWrite_); // $$$ disconect is valid on the server side.
  131. ::CloseHandle(pipeWrite_);
  132. }
  133. }
  134. bool PipeWin::OpenClient(IPCHandle pipeRead, IPCHandle pipeWrite)
  135. {
  136. pipeRead_ = pipeRead;
  137. pipeWrite_ = pipeWrite;
  138. readerThread_.Run();
  139. return true;
  140. }
  141. bool PipeWin::OpenServer(IPCHandle pipeRead, IPCHandle pipeWrite)
  142. {
  143. pipeRead_ = pipeRead;
  144. pipeWrite_ = pipeWrite;
  145. readerThread_.Run();
  146. return true;
  147. }
  148. bool PipeWin::Write(const void* buf, size_t sz)
  149. {
  150. DWORD written = 0;
  151. if (TRUE == ::WriteFile(pipeWrite_, buf, (DWORD) sz, &written, NULL))
  152. return true;
  153. return false;
  154. }
  155. void PipeWin::ReaderThread::Kill()
  156. {
  157. if (handle_)
  158. {
  159. BOOL result = TerminateThread((HANDLE)handle_, 0);
  160. result = CloseHandle((HANDLE)handle_);
  161. handle_ = 0;
  162. }
  163. }
  164. void PipeWin::ReaderThread::ThreadFunction()
  165. {
  166. while(shouldRun_)
  167. {
  168. if (readSize_)
  169. continue;
  170. DWORD bytesRead = 0;
  171. if (TRUE == ::ReadFile(pipeWin_->pipeRead_, &buf_[0], 4096, &bytesRead, NULL))
  172. {
  173. readSize_ = (unsigned) bytesRead;
  174. }
  175. Time::Sleep(100);
  176. }
  177. }
  178. bool PipeWin::Read(void* buf, size_t* sz)
  179. {
  180. *sz = 0;
  181. if (readerThread_.readSize_)
  182. {
  183. memcpy(buf, &readerThread_.buf_[0], readerThread_.readSize_);
  184. *sz = readerThread_.readSize_;
  185. readerThread_.readSize_ = 0;
  186. }
  187. return true;
  188. }
  189. char* PipeTransport::Receive(size_t* size)
  190. {
  191. if (buf_.Size() < kBufferSz)
  192. {
  193. buf_.Resize(kBufferSz);
  194. }
  195. *size = kBufferSz;
  196. if (!Read(&buf_[0], size))
  197. {
  198. return NULL;
  199. }
  200. return &buf_[0];
  201. }
  202. IPCProcess::IPCProcess(Context* context, IPCHandle clientRead, IPCHandle clientWrite, IPCHandle pid) : Object(context),
  203. pid_(pid),
  204. clientRead_(clientRead),
  205. clientWrite_(clientWrite)
  206. {
  207. }
  208. IPCProcess::~IPCProcess()
  209. {
  210. }
  211. bool IPCProcess::Terminate()
  212. {
  213. if (TerminateProcess(pid_, 0))
  214. {
  215. WaitForSingleObject(pid_, 1000);
  216. return true;
  217. }
  218. return false;
  219. }
  220. bool IPCProcess::IsRunning()
  221. {
  222. DWORD exitCode;
  223. if (!GetExitCodeProcess(pid_, &exitCode))
  224. return false;
  225. return exitCode == STILL_ACTIVE;
  226. }
  227. bool IPCProcess::Launch(const String& command, const Vector<String>& args, const String& initialDirectory)
  228. {
  229. STARTUPINFOW si = { sizeof(si) };
  230. PROCESS_INFORMATION pi = { 0 };
  231. // CreateProcess wants a single string
  232. String sargs;
  233. sargs.Join(args, " ");
  234. // convert to wide
  235. WString wcommand(command);
  236. // prepend the command and convert to wide
  237. WString wargs("\"" + command + "\" " + sargs);
  238. // The child process inherits the pipe handle.
  239. if (!::CreateProcessW(wcommand.CString(), (LPWSTR) wargs.CString(), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
  240. return false;
  241. }
  242. IPC* ipc = GetSubsystem<IPC>();
  243. IPCHandle jobHandle = ipc->GetJobHandle();
  244. if (jobHandle)
  245. {
  246. if (0 == AssignProcessToJobObject(jobHandle, pi.hProcess))
  247. {
  248. LOGERROR("IPCProcess::Launch - unable to assign job");
  249. }
  250. }
  251. pid_ = pi.hProcess;
  252. ::CloseHandle(pi.hThread);
  253. return true;
  254. }
  255. }
  256. #endif