@shitiandmw/node-pty 1.1.0-agent.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/LICENSE +69 -0
  2. package/README.md +170 -0
  3. package/binding.gyp +111 -0
  4. package/deps/.editorconfig +2 -0
  5. package/deps/winpty/.drone.yml +17 -0
  6. package/deps/winpty/.gitattributes +19 -0
  7. package/deps/winpty/LICENSE +21 -0
  8. package/deps/winpty/Makefile +166 -0
  9. package/deps/winpty/README.md +151 -0
  10. package/deps/winpty/RELEASES.md +280 -0
  11. package/deps/winpty/VERSION.txt +1 -0
  12. package/deps/winpty/configure +167 -0
  13. package/deps/winpty/misc/BufferResizeTests.cc +90 -0
  14. package/deps/winpty/misc/ChangeScreenBuffer.cc +53 -0
  15. package/deps/winpty/misc/ClearConsole.cc +72 -0
  16. package/deps/winpty/misc/ConinMode.cc +117 -0
  17. package/deps/winpty/misc/ConinMode.ps1 +116 -0
  18. package/deps/winpty/misc/ConoutMode.cc +113 -0
  19. package/deps/winpty/misc/DebugClient.py +42 -0
  20. package/deps/winpty/misc/DebugServer.py +63 -0
  21. package/deps/winpty/misc/DumpLines.py +5 -0
  22. package/deps/winpty/misc/EnableExtendedFlags.txt +46 -0
  23. package/deps/winpty/misc/Font-Report-June2016/CP437-Consolas.txt +528 -0
  24. package/deps/winpty/misc/Font-Report-June2016/CP437-Lucida.txt +633 -0
  25. package/deps/winpty/misc/Font-Report-June2016/CP932.txt +630 -0
  26. package/deps/winpty/misc/Font-Report-June2016/CP936.txt +630 -0
  27. package/deps/winpty/misc/Font-Report-June2016/CP949.txt +630 -0
  28. package/deps/winpty/misc/Font-Report-June2016/CP950.txt +630 -0
  29. package/deps/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt +16 -0
  30. package/deps/winpty/misc/Font-Report-June2016/Results.txt +4 -0
  31. package/deps/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt +144 -0
  32. package/deps/winpty/misc/FontSurvey.cc +100 -0
  33. package/deps/winpty/misc/FormatChar.h +21 -0
  34. package/deps/winpty/misc/FreezePerfTest.cc +62 -0
  35. package/deps/winpty/misc/GetCh.cc +20 -0
  36. package/deps/winpty/misc/GetConsolePos.cc +41 -0
  37. package/deps/winpty/misc/GetFont.cc +261 -0
  38. package/deps/winpty/misc/IdentifyConsoleWindow.ps1 +51 -0
  39. package/deps/winpty/misc/IsNewConsole.cc +87 -0
  40. package/deps/winpty/misc/MouseInputNotes.txt +90 -0
  41. package/deps/winpty/misc/MoveConsoleWindow.cc +34 -0
  42. package/deps/winpty/misc/Notes.txt +219 -0
  43. package/deps/winpty/misc/OSVersion.cc +27 -0
  44. package/deps/winpty/misc/ScreenBufferFreezeInactive.cc +101 -0
  45. package/deps/winpty/misc/ScreenBufferTest.cc +671 -0
  46. package/deps/winpty/misc/ScreenBufferTest2.cc +151 -0
  47. package/deps/winpty/misc/SelectAllTest.cc +45 -0
  48. package/deps/winpty/misc/SetBufferSize.cc +32 -0
  49. package/deps/winpty/misc/SetCursorPos.cc +10 -0
  50. package/deps/winpty/misc/SetFont.cc +145 -0
  51. package/deps/winpty/misc/SetWindowRect.cc +36 -0
  52. package/deps/winpty/misc/ShowArgv.cc +12 -0
  53. package/deps/winpty/misc/ShowConsoleInput.cc +40 -0
  54. package/deps/winpty/misc/Spew.py +5 -0
  55. package/deps/winpty/misc/TestUtil.cc +172 -0
  56. package/deps/winpty/misc/UnicodeDoubleWidthTest.cc +102 -0
  57. package/deps/winpty/misc/UnicodeWideTest1.cc +246 -0
  58. package/deps/winpty/misc/UnicodeWideTest2.cc +130 -0
  59. package/deps/winpty/misc/UnixEcho.cc +89 -0
  60. package/deps/winpty/misc/Utf16Echo.cc +46 -0
  61. package/deps/winpty/misc/VeryLargeRead.cc +122 -0
  62. package/deps/winpty/misc/VkEscapeTest.cc +56 -0
  63. package/deps/winpty/misc/Win10ResizeWhileFrozen.cc +52 -0
  64. package/deps/winpty/misc/Win10WrapTest1.cc +57 -0
  65. package/deps/winpty/misc/Win10WrapTest2.cc +30 -0
  66. package/deps/winpty/misc/Win32Echo1.cc +26 -0
  67. package/deps/winpty/misc/Win32Echo2.cc +19 -0
  68. package/deps/winpty/misc/Win32Test1.cc +46 -0
  69. package/deps/winpty/misc/Win32Test2.cc +70 -0
  70. package/deps/winpty/misc/Win32Test3.cc +78 -0
  71. package/deps/winpty/misc/Win32Write1.cc +44 -0
  72. package/deps/winpty/misc/WindowsBugCrashReader.cc +27 -0
  73. package/deps/winpty/misc/WriteConsole.cc +106 -0
  74. package/deps/winpty/misc/build32.sh +9 -0
  75. package/deps/winpty/misc/build64.sh +9 -0
  76. package/deps/winpty/misc/color-test.sh +212 -0
  77. package/deps/winpty/misc/font-notes.txt +300 -0
  78. package/deps/winpty/misc/winbug-15048.cc +201 -0
  79. package/deps/winpty/ship/build-pty4j-libpty.bat +36 -0
  80. package/deps/winpty/ship/common_ship.py +53 -0
  81. package/deps/winpty/ship/make_msvc_package.py +165 -0
  82. package/deps/winpty/ship/ship.py +108 -0
  83. package/deps/winpty/src/agent/Agent.cc +613 -0
  84. package/deps/winpty/src/agent/Agent.h +103 -0
  85. package/deps/winpty/src/agent/AgentCreateDesktop.cc +84 -0
  86. package/deps/winpty/src/agent/AgentCreateDesktop.h +28 -0
  87. package/deps/winpty/src/agent/ConsoleFont.cc +632 -0
  88. package/deps/winpty/src/agent/ConsoleFont.h +28 -0
  89. package/deps/winpty/src/agent/ConsoleInput.cc +852 -0
  90. package/deps/winpty/src/agent/ConsoleInput.h +109 -0
  91. package/deps/winpty/src/agent/ConsoleInputReencoding.cc +121 -0
  92. package/deps/winpty/src/agent/ConsoleInputReencoding.h +36 -0
  93. package/deps/winpty/src/agent/ConsoleLine.cc +152 -0
  94. package/deps/winpty/src/agent/ConsoleLine.h +41 -0
  95. package/deps/winpty/src/agent/Coord.h +87 -0
  96. package/deps/winpty/src/agent/DebugShowInput.cc +239 -0
  97. package/deps/winpty/src/agent/DebugShowInput.h +32 -0
  98. package/deps/winpty/src/agent/DefaultInputMap.cc +422 -0
  99. package/deps/winpty/src/agent/DefaultInputMap.h +28 -0
  100. package/deps/winpty/src/agent/DsrSender.h +30 -0
  101. package/deps/winpty/src/agent/EventLoop.cc +99 -0
  102. package/deps/winpty/src/agent/EventLoop.h +47 -0
  103. package/deps/winpty/src/agent/InputMap.cc +246 -0
  104. package/deps/winpty/src/agent/InputMap.h +114 -0
  105. package/deps/winpty/src/agent/LargeConsoleRead.cc +71 -0
  106. package/deps/winpty/src/agent/LargeConsoleRead.h +68 -0
  107. package/deps/winpty/src/agent/NamedPipe.cc +378 -0
  108. package/deps/winpty/src/agent/NamedPipe.h +125 -0
  109. package/deps/winpty/src/agent/Scraper.cc +699 -0
  110. package/deps/winpty/src/agent/Scraper.h +103 -0
  111. package/deps/winpty/src/agent/SimplePool.h +75 -0
  112. package/deps/winpty/src/agent/SmallRect.h +143 -0
  113. package/deps/winpty/src/agent/Terminal.cc +535 -0
  114. package/deps/winpty/src/agent/Terminal.h +69 -0
  115. package/deps/winpty/src/agent/UnicodeEncoding.h +157 -0
  116. package/deps/winpty/src/agent/UnicodeEncodingTest.cc +189 -0
  117. package/deps/winpty/src/agent/Win32Console.cc +107 -0
  118. package/deps/winpty/src/agent/Win32Console.h +67 -0
  119. package/deps/winpty/src/agent/Win32ConsoleBuffer.cc +193 -0
  120. package/deps/winpty/src/agent/Win32ConsoleBuffer.h +99 -0
  121. package/deps/winpty/src/agent/main.cc +114 -0
  122. package/deps/winpty/src/agent/subdir.mk +61 -0
  123. package/deps/winpty/src/configurations.gypi +60 -0
  124. package/deps/winpty/src/debugserver/DebugServer.cc +117 -0
  125. package/deps/winpty/src/debugserver/subdir.mk +41 -0
  126. package/deps/winpty/src/include/winpty.h +242 -0
  127. package/deps/winpty/src/include/winpty_constants.h +131 -0
  128. package/deps/winpty/src/libwinpty/AgentLocation.cc +75 -0
  129. package/deps/winpty/src/libwinpty/AgentLocation.h +28 -0
  130. package/deps/winpty/src/libwinpty/LibWinptyException.h +54 -0
  131. package/deps/winpty/src/libwinpty/WinptyInternal.h +72 -0
  132. package/deps/winpty/src/libwinpty/subdir.mk +46 -0
  133. package/deps/winpty/src/libwinpty/winpty.cc +970 -0
  134. package/deps/winpty/src/shared/AgentMsg.h +38 -0
  135. package/deps/winpty/src/shared/BackgroundDesktop.cc +122 -0
  136. package/deps/winpty/src/shared/BackgroundDesktop.h +73 -0
  137. package/deps/winpty/src/shared/Buffer.cc +103 -0
  138. package/deps/winpty/src/shared/Buffer.h +102 -0
  139. package/deps/winpty/src/shared/DebugClient.cc +187 -0
  140. package/deps/winpty/src/shared/DebugClient.h +38 -0
  141. package/deps/winpty/src/shared/GenRandom.cc +138 -0
  142. package/deps/winpty/src/shared/GenRandom.h +55 -0
  143. package/deps/winpty/src/shared/GetCommitHash.bat +13 -0
  144. package/deps/winpty/src/shared/Mutex.h +54 -0
  145. package/deps/winpty/src/shared/OsModule.h +63 -0
  146. package/deps/winpty/src/shared/OwnedHandle.cc +36 -0
  147. package/deps/winpty/src/shared/OwnedHandle.h +45 -0
  148. package/deps/winpty/src/shared/PrecompiledHeader.h +43 -0
  149. package/deps/winpty/src/shared/StringBuilder.h +227 -0
  150. package/deps/winpty/src/shared/StringBuilderTest.cc +114 -0
  151. package/deps/winpty/src/shared/StringUtil.cc +55 -0
  152. package/deps/winpty/src/shared/StringUtil.h +80 -0
  153. package/deps/winpty/src/shared/TimeMeasurement.h +63 -0
  154. package/deps/winpty/src/shared/UnixCtrlChars.h +45 -0
  155. package/deps/winpty/src/shared/UpdateGenVersion.bat +20 -0
  156. package/deps/winpty/src/shared/WindowsSecurity.cc +460 -0
  157. package/deps/winpty/src/shared/WindowsSecurity.h +104 -0
  158. package/deps/winpty/src/shared/WindowsVersion.cc +252 -0
  159. package/deps/winpty/src/shared/WindowsVersion.h +29 -0
  160. package/deps/winpty/src/shared/WinptyAssert.cc +55 -0
  161. package/deps/winpty/src/shared/WinptyAssert.h +64 -0
  162. package/deps/winpty/src/shared/WinptyException.cc +57 -0
  163. package/deps/winpty/src/shared/WinptyException.h +43 -0
  164. package/deps/winpty/src/shared/WinptyVersion.cc +42 -0
  165. package/deps/winpty/src/shared/WinptyVersion.h +27 -0
  166. package/deps/winpty/src/shared/winpty_snprintf.h +99 -0
  167. package/deps/winpty/src/subdir.mk +5 -0
  168. package/deps/winpty/src/tests/subdir.mk +28 -0
  169. package/deps/winpty/src/tests/trivial_test.cc +158 -0
  170. package/deps/winpty/src/unix-adapter/InputHandler.cc +114 -0
  171. package/deps/winpty/src/unix-adapter/InputHandler.h +56 -0
  172. package/deps/winpty/src/unix-adapter/OutputHandler.cc +80 -0
  173. package/deps/winpty/src/unix-adapter/OutputHandler.h +53 -0
  174. package/deps/winpty/src/unix-adapter/Util.cc +86 -0
  175. package/deps/winpty/src/unix-adapter/Util.h +31 -0
  176. package/deps/winpty/src/unix-adapter/WakeupFd.cc +70 -0
  177. package/deps/winpty/src/unix-adapter/WakeupFd.h +42 -0
  178. package/deps/winpty/src/unix-adapter/main.cc +729 -0
  179. package/deps/winpty/src/unix-adapter/subdir.mk +41 -0
  180. package/deps/winpty/src/winpty.gyp +234 -0
  181. package/deps/winpty/vcbuild.bat +83 -0
  182. package/lib/conpty_console_list_agent.js +16 -0
  183. package/lib/conpty_console_list_agent.js.map +1 -0
  184. package/lib/eventEmitter2.js +47 -0
  185. package/lib/eventEmitter2.js.map +1 -0
  186. package/lib/eventEmitter2.test.js +30 -0
  187. package/lib/eventEmitter2.test.js.map +1 -0
  188. package/lib/index.js +52 -0
  189. package/lib/index.js.map +1 -0
  190. package/lib/interfaces.js +7 -0
  191. package/lib/interfaces.js.map +1 -0
  192. package/lib/shared/conout.js +11 -0
  193. package/lib/shared/conout.js.map +1 -0
  194. package/lib/terminal.js +190 -0
  195. package/lib/terminal.js.map +1 -0
  196. package/lib/terminal.test.js +139 -0
  197. package/lib/terminal.test.js.map +1 -0
  198. package/lib/testUtils.test.js +28 -0
  199. package/lib/testUtils.test.js.map +1 -0
  200. package/lib/types.js +7 -0
  201. package/lib/types.js.map +1 -0
  202. package/lib/unixTerminal.js +346 -0
  203. package/lib/unixTerminal.js.map +1 -0
  204. package/lib/unixTerminal.test.js +351 -0
  205. package/lib/unixTerminal.test.js.map +1 -0
  206. package/lib/utils.js +39 -0
  207. package/lib/utils.js.map +1 -0
  208. package/lib/windowsConoutConnection.js +125 -0
  209. package/lib/windowsConoutConnection.js.map +1 -0
  210. package/lib/windowsPtyAgent.js +320 -0
  211. package/lib/windowsPtyAgent.js.map +1 -0
  212. package/lib/windowsPtyAgent.test.js +90 -0
  213. package/lib/windowsPtyAgent.test.js.map +1 -0
  214. package/lib/windowsTerminal.js +199 -0
  215. package/lib/windowsTerminal.js.map +1 -0
  216. package/lib/windowsTerminal.test.js +219 -0
  217. package/lib/windowsTerminal.test.js.map +1 -0
  218. package/lib/worker/conoutSocketWorker.js +22 -0
  219. package/lib/worker/conoutSocketWorker.js.map +1 -0
  220. package/package.json +68 -0
  221. package/prebuilds/darwin-arm64/pty.node +0 -0
  222. package/prebuilds/darwin-arm64/spawn-helper +0 -0
  223. package/prebuilds/darwin-x64/pty.node +0 -0
  224. package/prebuilds/darwin-x64/spawn-helper +0 -0
  225. package/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  226. package/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  227. package/prebuilds/win32-arm64/conpty.node +0 -0
  228. package/prebuilds/win32-arm64/conpty.pdb +0 -0
  229. package/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  230. package/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  231. package/prebuilds/win32-arm64/pty.node +0 -0
  232. package/prebuilds/win32-arm64/pty.pdb +0 -0
  233. package/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  234. package/prebuilds/win32-arm64/winpty-agent.pdb +0 -0
  235. package/prebuilds/win32-arm64/winpty.dll +0 -0
  236. package/prebuilds/win32-arm64/winpty.pdb +0 -0
  237. package/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  238. package/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  239. package/prebuilds/win32-x64/conpty.node +0 -0
  240. package/prebuilds/win32-x64/conpty.pdb +0 -0
  241. package/prebuilds/win32-x64/conpty_console_list.node +0 -0
  242. package/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  243. package/prebuilds/win32-x64/pty.node +0 -0
  244. package/prebuilds/win32-x64/pty.pdb +0 -0
  245. package/prebuilds/win32-x64/winpty-agent.exe +0 -0
  246. package/prebuilds/win32-x64/winpty-agent.pdb +0 -0
  247. package/prebuilds/win32-x64/winpty.dll +0 -0
  248. package/prebuilds/win32-x64/winpty.pdb +0 -0
  249. package/scripts/gen-compile-commands.js +8 -0
  250. package/scripts/increment-version.js +54 -0
  251. package/scripts/post-install.js +99 -0
  252. package/scripts/prebuild.js +39 -0
  253. package/scripts/sync-prebuild.js +31 -0
  254. package/scripts/verify-darwin-fd-leak.js +63 -0
  255. package/src/conpty_console_list_agent.ts +15 -0
  256. package/src/eventEmitter2.test.ts +30 -0
  257. package/src/eventEmitter2.ts +48 -0
  258. package/src/index.ts +52 -0
  259. package/src/interfaces.ts +130 -0
  260. package/src/native.d.ts +54 -0
  261. package/src/shared/conout.ts +15 -0
  262. package/src/terminal.test.ts +119 -0
  263. package/src/terminal.ts +211 -0
  264. package/src/testUtils.test.ts +23 -0
  265. package/src/tsconfig.json +22 -0
  266. package/src/types.ts +15 -0
  267. package/src/unix/pty.cc +808 -0
  268. package/src/unix/spawn-helper.cc +23 -0
  269. package/src/unixTerminal.test.ts +367 -0
  270. package/src/unixTerminal.ts +388 -0
  271. package/src/utils.ts +29 -0
  272. package/src/win/conpty.cc +583 -0
  273. package/src/win/conpty.h +41 -0
  274. package/src/win/conpty_console_list.cc +44 -0
  275. package/src/win/path_util.cc +95 -0
  276. package/src/win/path_util.h +26 -0
  277. package/src/win/winpty.cc +333 -0
  278. package/src/windowsConoutConnection.ts +82 -0
  279. package/src/windowsPtyAgent.test.ts +94 -0
  280. package/src/windowsPtyAgent.ts +321 -0
  281. package/src/windowsTerminal.test.ts +229 -0
  282. package/src/windowsTerminal.ts +203 -0
  283. package/src/worker/conoutSocketWorker.ts +22 -0
  284. package/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
  285. package/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
  286. package/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
  287. package/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
  288. package/typings/node-pty.d.ts +211 -0
@@ -0,0 +1,970 @@
1
+ // Copyright (c) 2011-2016 Ryan Prichard
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to
5
+ // deal in the Software without restriction, including without limitation the
6
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ // sell copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in
11
+ // all copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
+ // IN THE SOFTWARE.
20
+
21
+ #include <windows.h>
22
+
23
+ #include <stdint.h>
24
+ #include <stdio.h>
25
+ #include <string.h>
26
+
27
+ #include <limits>
28
+ #include <string>
29
+ #include <vector>
30
+
31
+ #include "../include/winpty.h"
32
+
33
+ #include "../shared/AgentMsg.h"
34
+ #include "../shared/BackgroundDesktop.h"
35
+ #include "../shared/Buffer.h"
36
+ #include "../shared/DebugClient.h"
37
+ #include "../shared/GenRandom.h"
38
+ #include "../shared/OwnedHandle.h"
39
+ #include "../shared/StringBuilder.h"
40
+ #include "../shared/StringUtil.h"
41
+ #include "../shared/WindowsSecurity.h"
42
+ #include "../shared/WindowsVersion.h"
43
+ #include "../shared/WinptyAssert.h"
44
+ #include "../shared/WinptyException.h"
45
+ #include "../shared/WinptyVersion.h"
46
+
47
+ #include "AgentLocation.h"
48
+ #include "LibWinptyException.h"
49
+ #include "WinptyInternal.h"
50
+
51
+
52
+
53
+ /*****************************************************************************
54
+ * Error handling -- translate C++ exceptions to an optional error object
55
+ * output and log the result. */
56
+
57
+ static const winpty_error_s kOutOfMemory = {
58
+ WINPTY_ERROR_OUT_OF_MEMORY,
59
+ L"Out of memory",
60
+ nullptr
61
+ };
62
+
63
+ static const winpty_error_s kBadRpcPacket = {
64
+ WINPTY_ERROR_UNSPECIFIED,
65
+ L"Bad RPC packet",
66
+ nullptr
67
+ };
68
+
69
+ static const winpty_error_s kUncaughtException = {
70
+ WINPTY_ERROR_UNSPECIFIED,
71
+ L"Uncaught C++ exception",
72
+ nullptr
73
+ };
74
+
75
+ /* Gets the error code from the error object. */
76
+ WINPTY_API winpty_result_t winpty_error_code(winpty_error_ptr_t err) {
77
+ return err != nullptr ? err->code : WINPTY_ERROR_SUCCESS;
78
+ }
79
+
80
+ /* Returns a textual representation of the error. The string is freed when
81
+ * the error is freed. */
82
+ WINPTY_API LPCWSTR winpty_error_msg(winpty_error_ptr_t err) {
83
+ if (err != nullptr) {
84
+ if (err->msgStatic != nullptr) {
85
+ return err->msgStatic;
86
+ } else {
87
+ ASSERT(err->msgDynamic != nullptr);
88
+ std::wstring *msgPtr = err->msgDynamic->get();
89
+ ASSERT(msgPtr != nullptr);
90
+ return msgPtr->c_str();
91
+ }
92
+ } else {
93
+ return L"Success";
94
+ }
95
+ }
96
+
97
+ /* Free the error object. Every error returned from the winpty API must be
98
+ * freed. */
99
+ WINPTY_API void winpty_error_free(winpty_error_ptr_t err) {
100
+ if (err != nullptr && err->msgDynamic != nullptr) {
101
+ delete err->msgDynamic;
102
+ delete err;
103
+ }
104
+ }
105
+
106
+ static void translateException(winpty_error_ptr_t *&err) {
107
+ winpty_error_ptr_t ret = nullptr;
108
+ try {
109
+ try {
110
+ throw;
111
+ } catch (const ReadBuffer::DecodeError&) {
112
+ ret = const_cast<winpty_error_ptr_t>(&kBadRpcPacket);
113
+ } catch (const LibWinptyException &e) {
114
+ std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
115
+ obj->code = e.code();
116
+ obj->msgStatic = nullptr;
117
+ obj->msgDynamic =
118
+ new std::shared_ptr<std::wstring>(e.whatSharedStr());
119
+ ret = obj.release();
120
+ } catch (const WinptyException &e) {
121
+ std::unique_ptr<winpty_error_t> obj(new winpty_error_t);
122
+ std::shared_ptr<std::wstring> msg(new std::wstring(e.what()));
123
+ obj->code = WINPTY_ERROR_UNSPECIFIED;
124
+ obj->msgStatic = nullptr;
125
+ obj->msgDynamic = new std::shared_ptr<std::wstring>(msg);
126
+ ret = obj.release();
127
+ }
128
+ } catch (const std::bad_alloc&) {
129
+ ret = const_cast<winpty_error_ptr_t>(&kOutOfMemory);
130
+ } catch (...) {
131
+ ret = const_cast<winpty_error_ptr_t>(&kUncaughtException);
132
+ }
133
+ trace("libwinpty error: code=%u msg='%s'",
134
+ static_cast<unsigned>(ret->code),
135
+ utf8FromWide(winpty_error_msg(ret)).c_str());
136
+ if (err != nullptr) {
137
+ *err = ret;
138
+ } else {
139
+ winpty_error_free(ret);
140
+ }
141
+ }
142
+
143
+ #define API_TRY \
144
+ if (err != nullptr) { *err = nullptr; } \
145
+ try
146
+
147
+ #define API_CATCH(ret) \
148
+ catch (...) { translateException(err); return (ret); }
149
+
150
+
151
+
152
+ /*****************************************************************************
153
+ * Configuration of a new agent. */
154
+
155
+ WINPTY_API winpty_config_t *
156
+ winpty_config_new(UINT64 flags, winpty_error_ptr_t *err /*OPTIONAL*/) {
157
+ API_TRY {
158
+ ASSERT((flags & WINPTY_FLAG_MASK) == flags);
159
+ std::unique_ptr<winpty_config_t> ret(new winpty_config_t);
160
+ ret->flags = flags;
161
+ return ret.release();
162
+ } API_CATCH(nullptr)
163
+ }
164
+
165
+ WINPTY_API void winpty_config_free(winpty_config_t *cfg) {
166
+ delete cfg;
167
+ }
168
+
169
+ WINPTY_API void
170
+ winpty_config_set_initial_size(winpty_config_t *cfg, int cols, int rows) {
171
+ ASSERT(cfg != nullptr && cols > 0 && rows > 0);
172
+ cfg->cols = cols;
173
+ cfg->rows = rows;
174
+ }
175
+
176
+ WINPTY_API void
177
+ winpty_config_set_mouse_mode(winpty_config_t *cfg, int mouseMode) {
178
+ ASSERT(cfg != nullptr &&
179
+ mouseMode >= WINPTY_MOUSE_MODE_NONE &&
180
+ mouseMode <= WINPTY_MOUSE_MODE_FORCE);
181
+ cfg->mouseMode = mouseMode;
182
+ }
183
+
184
+ WINPTY_API void
185
+ winpty_config_set_agent_timeout(winpty_config_t *cfg, DWORD timeoutMs) {
186
+ ASSERT(cfg != nullptr && timeoutMs > 0);
187
+ cfg->timeoutMs = timeoutMs;
188
+ }
189
+
190
+
191
+
192
+ /*****************************************************************************
193
+ * Agent I/O. */
194
+
195
+ namespace {
196
+
197
+ // Once an I/O operation fails with ERROR_IO_PENDING, the caller *must* wait
198
+ // for it to complete, even after calling CancelIo on it! See
199
+ // https://blogs.msdn.microsoft.com/oldnewthing/20110202-00/?p=11613. This
200
+ // class enforces that requirement.
201
+ class PendingIo {
202
+ HANDLE m_file;
203
+ OVERLAPPED &m_over;
204
+ bool m_finished;
205
+ public:
206
+ // The file handle and OVERLAPPED object must live as long as the PendingIo
207
+ // object.
208
+ PendingIo(HANDLE file, OVERLAPPED &over) :
209
+ m_file(file), m_over(over), m_finished(false) {}
210
+ ~PendingIo() {
211
+ if (!m_finished) {
212
+ // We're not usually that interested in CancelIo's return value.
213
+ // In any case, we must not throw an exception in this dtor.
214
+ CancelIo(m_file);
215
+ waitForCompletion();
216
+ }
217
+ }
218
+ std::tuple<BOOL, DWORD> waitForCompletion(DWORD &actual) WINPTY_NOEXCEPT {
219
+ m_finished = true;
220
+ const BOOL success =
221
+ GetOverlappedResult(m_file, &m_over, &actual, TRUE);
222
+ return std::make_tuple(success, GetLastError());
223
+ }
224
+ std::tuple<BOOL, DWORD> waitForCompletion() WINPTY_NOEXCEPT {
225
+ DWORD actual = 0;
226
+ return waitForCompletion(actual);
227
+ }
228
+ };
229
+
230
+ } // anonymous namespace
231
+
232
+ static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
233
+ DWORD &lastError, DWORD &actual) {
234
+ if (!success && lastError == ERROR_IO_PENDING) {
235
+ PendingIo io(wp.controlPipe.get(), over);
236
+ const HANDLE waitHandles[2] = { wp.ioEvent.get(),
237
+ wp.agentProcess.get() };
238
+ DWORD waitRet = WaitForMultipleObjects(
239
+ 2, waitHandles, FALSE, wp.agentTimeoutMs);
240
+ if (waitRet != WAIT_OBJECT_0) {
241
+ // The I/O is still pending. Cancel it, close the I/O event, and
242
+ // throw an exception.
243
+ if (waitRet == WAIT_OBJECT_0 + 1) {
244
+ throw LibWinptyException(WINPTY_ERROR_AGENT_DIED, L"agent died");
245
+ } else if (waitRet == WAIT_TIMEOUT) {
246
+ throw LibWinptyException(WINPTY_ERROR_AGENT_TIMEOUT,
247
+ L"agent timed out");
248
+ } else if (waitRet == WAIT_FAILED) {
249
+ throwWindowsError(L"WaitForMultipleObjects failed");
250
+ } else {
251
+ ASSERT(false &&
252
+ "unexpected WaitForMultipleObjects return value");
253
+ }
254
+ }
255
+ std::tie(success, lastError) = io.waitForCompletion(actual);
256
+ }
257
+ }
258
+
259
+ static void handlePendingIo(winpty_t &wp, OVERLAPPED &over, BOOL &success,
260
+ DWORD &lastError) {
261
+ DWORD actual = 0;
262
+ handlePendingIo(wp, over, success, lastError, actual);
263
+ }
264
+
265
+ static void handleReadWriteErrors(winpty_t &wp, BOOL success, DWORD lastError,
266
+ const wchar_t *genericErrMsg) {
267
+ if (!success) {
268
+ // If the pipe connection is broken after it's been connected, then
269
+ // later I/O operations fail with ERROR_BROKEN_PIPE (reads) or
270
+ // ERROR_NO_DATA (writes). With Wine, they may also fail with
271
+ // ERROR_PIPE_NOT_CONNECTED. See this gist[1].
272
+ //
273
+ // [1] https://gist.github.com/rprichard/8dd8ca134b39534b7da2733994aa07ba
274
+ if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_NO_DATA ||
275
+ lastError == ERROR_PIPE_NOT_CONNECTED) {
276
+ throw LibWinptyException(WINPTY_ERROR_LOST_CONNECTION,
277
+ L"lost connection to agent");
278
+ } else {
279
+ throwWindowsError(genericErrMsg, lastError);
280
+ }
281
+ }
282
+ }
283
+
284
+ // Calls ConnectNamedPipe to wait until the agent connects to the control pipe.
285
+ static void
286
+ connectControlPipe(winpty_t &wp) {
287
+ OVERLAPPED over = {};
288
+ over.hEvent = wp.ioEvent.get();
289
+ BOOL success = ConnectNamedPipe(wp.controlPipe.get(), &over);
290
+ DWORD lastError = GetLastError();
291
+ handlePendingIo(wp, over, success, lastError);
292
+ if (!success && lastError == ERROR_PIPE_CONNECTED) {
293
+ success = TRUE;
294
+ }
295
+ if (!success) {
296
+ throwWindowsError(L"ConnectNamedPipe failed", lastError);
297
+ }
298
+ }
299
+
300
+ static void writeData(winpty_t &wp, const void *data, size_t amount) {
301
+ // Perform a single pipe write.
302
+ DWORD actual = 0;
303
+ OVERLAPPED over = {};
304
+ over.hEvent = wp.ioEvent.get();
305
+ BOOL success = WriteFile(wp.controlPipe.get(), data, amount,
306
+ &actual, &over);
307
+ DWORD lastError = GetLastError();
308
+ if (!success) {
309
+ handlePendingIo(wp, over, success, lastError, actual);
310
+ handleReadWriteErrors(wp, success, lastError, L"WriteFile failed");
311
+ ASSERT(success);
312
+ }
313
+ // TODO: Can a partial write actually happen somehow?
314
+ ASSERT(actual == amount && "WriteFile wrote fewer bytes than requested");
315
+ }
316
+
317
+ static inline WriteBuffer newPacket() {
318
+ WriteBuffer packet;
319
+ packet.putRawValue<uint64_t>(0); // Reserve space for size.
320
+ return packet;
321
+ }
322
+
323
+ static void writePacket(winpty_t &wp, WriteBuffer &packet) {
324
+ const auto &buf = packet.buf();
325
+ packet.replaceRawValue<uint64_t>(0, buf.size());
326
+ writeData(wp, buf.data(), buf.size());
327
+ }
328
+
329
+ static size_t readData(winpty_t &wp, void *data, size_t amount) {
330
+ DWORD actual = 0;
331
+ OVERLAPPED over = {};
332
+ over.hEvent = wp.ioEvent.get();
333
+ BOOL success = ReadFile(wp.controlPipe.get(), data, amount,
334
+ &actual, &over);
335
+ DWORD lastError = GetLastError();
336
+ if (!success) {
337
+ handlePendingIo(wp, over, success, lastError, actual);
338
+ handleReadWriteErrors(wp, success, lastError, L"ReadFile failed");
339
+ }
340
+ return actual;
341
+ }
342
+
343
+ static void readAll(winpty_t &wp, void *data, size_t amount) {
344
+ while (amount > 0) {
345
+ const size_t chunk = readData(wp, data, amount);
346
+ ASSERT(chunk <= amount && "readData result is larger than amount");
347
+ data = reinterpret_cast<char*>(data) + chunk;
348
+ amount -= chunk;
349
+ }
350
+ }
351
+
352
+ static uint64_t readUInt64(winpty_t &wp) {
353
+ uint64_t ret = 0;
354
+ readAll(wp, &ret, sizeof(ret));
355
+ return ret;
356
+ }
357
+
358
+ // Returns a reply packet's payload.
359
+ static ReadBuffer readPacket(winpty_t &wp) {
360
+ const uint64_t packetSize = readUInt64(wp);
361
+ if (packetSize < sizeof(packetSize) || packetSize > SIZE_MAX) {
362
+ throwWinptyException(L"Agent RPC error: invalid packet size");
363
+ }
364
+ const size_t payloadSize = packetSize - sizeof(packetSize);
365
+ std::vector<char> bytes(payloadSize);
366
+ readAll(wp, bytes.data(), bytes.size());
367
+ return ReadBuffer(std::move(bytes));
368
+ }
369
+
370
+ static OwnedHandle createControlPipe(const std::wstring &name) {
371
+ const auto sd = createPipeSecurityDescriptorOwnerFullControl();
372
+ if (!sd) {
373
+ throwWinptyException(
374
+ L"could not create the control pipe's SECURITY_DESCRIPTOR");
375
+ }
376
+ SECURITY_ATTRIBUTES sa = {};
377
+ sa.nLength = sizeof(sa);
378
+ sa.lpSecurityDescriptor = sd.get();
379
+ HANDLE ret = CreateNamedPipeW(name.c_str(),
380
+ /*dwOpenMode=*/
381
+ PIPE_ACCESS_DUPLEX |
382
+ FILE_FLAG_FIRST_PIPE_INSTANCE |
383
+ FILE_FLAG_OVERLAPPED,
384
+ /*dwPipeMode=*/rejectRemoteClientsPipeFlag(),
385
+ /*nMaxInstances=*/1,
386
+ /*nOutBufferSize=*/8192,
387
+ /*nInBufferSize=*/256,
388
+ /*nDefaultTimeOut=*/30000,
389
+ &sa);
390
+ if (ret == INVALID_HANDLE_VALUE) {
391
+ throwWindowsError(L"CreateNamedPipeW failed");
392
+ }
393
+ return OwnedHandle(ret);
394
+ }
395
+
396
+
397
+
398
+ /*****************************************************************************
399
+ * Start the agent. */
400
+
401
+ static OwnedHandle createEvent() {
402
+ // manual reset, initially unset
403
+ HANDLE h = CreateEventW(nullptr, TRUE, FALSE, nullptr);
404
+ if (h == nullptr) {
405
+ throwWindowsError(L"CreateEventW failed");
406
+ }
407
+ return OwnedHandle(h);
408
+ }
409
+
410
+ // For debugging purposes, provide a way to keep the console on the main window
411
+ // station, visible.
412
+ static bool shouldShowConsoleWindow() {
413
+ char buf[32];
414
+ return GetEnvironmentVariableA("WINPTY_SHOW_CONSOLE", buf, sizeof(buf)) > 0;
415
+ }
416
+
417
+ static bool shouldCreateBackgroundDesktop(bool &createUsingAgent) {
418
+ // Prior to Windows 7, winpty's repeated selection-deselection loop
419
+ // prevented the user from interacting with their *visible* console
420
+ // windows, unless we placed the console onto a background desktop.
421
+ // The SetProcessWindowStation call interferes with the clipboard and
422
+ // isn't thread-safe, though[1]. The call should perhaps occur in a
423
+ // special agent subprocess. Spawning a process in a background desktop
424
+ // also breaks ConEmu, but marking the process SW_HIDE seems to correct
425
+ // that[2].
426
+ //
427
+ // Windows 7 moved a lot of console handling out of csrss.exe and into
428
+ // a per-console conhost.exe process, which may explain why it isn't
429
+ // affected.
430
+ //
431
+ // This is a somewhat risky change, so there are low-level flags to
432
+ // assist in debugging if there are issues.
433
+ //
434
+ // [1] https://github.com/rprichard/winpty/issues/58
435
+ // [2] https://github.com/rprichard/winpty/issues/70
436
+ bool ret = !shouldShowConsoleWindow() && !isAtLeastWindows7();
437
+ const bool force = hasDebugFlag("force_desktop");
438
+ const bool force_spawn = hasDebugFlag("force_desktop_spawn");
439
+ const bool force_curproc = hasDebugFlag("force_desktop_curproc");
440
+ const bool suppress = hasDebugFlag("no_desktop");
441
+ if (force + force_spawn + force_curproc + suppress > 1) {
442
+ trace("error: Only one of force_desktop, force_desktop_spawn, "
443
+ "force_desktop_curproc, and no_desktop may be set");
444
+ } else if (force) {
445
+ ret = true;
446
+ } else if (force_spawn) {
447
+ ret = true;
448
+ createUsingAgent = true;
449
+ } else if (force_curproc) {
450
+ ret = true;
451
+ createUsingAgent = false;
452
+ } else if (suppress) {
453
+ ret = false;
454
+ }
455
+ return ret;
456
+ }
457
+
458
+ static bool shouldSpecifyHideFlag() {
459
+ const bool force = hasDebugFlag("force_sw_hide");
460
+ const bool suppress = hasDebugFlag("no_sw_hide");
461
+ bool ret = !shouldShowConsoleWindow();
462
+ if (force && suppress) {
463
+ trace("error: Both the force_sw_hide and no_sw_hide flags are set");
464
+ } else if (force) {
465
+ ret = true;
466
+ } else if (suppress) {
467
+ ret = false;
468
+ }
469
+ return ret;
470
+ }
471
+
472
+ static OwnedHandle startAgentProcess(
473
+ const std::wstring &desktop,
474
+ const std::wstring &controlPipeName,
475
+ const std::wstring &params,
476
+ DWORD creationFlags,
477
+ DWORD &agentPid) {
478
+ const std::wstring exePath = findAgentProgram();
479
+ const std::wstring cmdline =
480
+ (WStringBuilder(256)
481
+ << L"\"" << exePath << L"\" "
482
+ << controlPipeName << L' '
483
+ << params).str_moved();
484
+
485
+ auto cmdlineV = vectorWithNulFromString(cmdline);
486
+ auto desktopV = vectorWithNulFromString(desktop);
487
+
488
+ // Start the agent.
489
+ STARTUPINFOW sui = {};
490
+ sui.cb = sizeof(sui);
491
+ sui.lpDesktop = desktop.empty() ? nullptr : desktopV.data();
492
+
493
+ if (shouldSpecifyHideFlag()) {
494
+ sui.dwFlags |= STARTF_USESHOWWINDOW;
495
+ sui.wShowWindow = SW_HIDE;
496
+ }
497
+ PROCESS_INFORMATION pi = {};
498
+ const BOOL success =
499
+ CreateProcessW(exePath.c_str(),
500
+ cmdlineV.data(),
501
+ nullptr, nullptr,
502
+ /*bInheritHandles=*/FALSE,
503
+ /*dwCreationFlags=*/creationFlags,
504
+ nullptr, nullptr,
505
+ &sui, &pi);
506
+ if (!success) {
507
+ const DWORD lastError = GetLastError();
508
+ const auto errStr =
509
+ (WStringBuilder(256)
510
+ << L"winpty-agent CreateProcess failed: cmdline='" << cmdline
511
+ << L"' err=0x" << whexOfInt(lastError)).str_moved();
512
+ throw LibWinptyException(
513
+ WINPTY_ERROR_AGENT_CREATION_FAILED, errStr.c_str());
514
+ }
515
+ CloseHandle(pi.hThread);
516
+ TRACE("Created agent successfully, pid=%u, cmdline=%s",
517
+ static_cast<unsigned int>(pi.dwProcessId),
518
+ utf8FromWide(cmdline).c_str());
519
+ agentPid = pi.dwProcessId;
520
+ return OwnedHandle(pi.hProcess);
521
+ }
522
+
523
+ static void verifyPipeClientPid(HANDLE serverPipe, DWORD agentPid) {
524
+ const auto client = getNamedPipeClientProcessId(serverPipe);
525
+ const auto success = std::get<0>(client);
526
+ const auto lastError = std::get<2>(client);
527
+ if (success == GetNamedPipeClientProcessId_Result::Success) {
528
+ const auto clientPid = std::get<1>(client);
529
+ if (clientPid != agentPid) {
530
+ WStringBuilder errMsg;
531
+ errMsg << L"Security check failed: pipe client pid (" << clientPid
532
+ << L") does not match agent pid (" << agentPid << L")";
533
+ throwWinptyException(errMsg.c_str());
534
+ }
535
+ } else if (success == GetNamedPipeClientProcessId_Result::UnsupportedOs) {
536
+ trace("Pipe client PID security check skipped: "
537
+ "GetNamedPipeClientProcessId unsupported on this OS version");
538
+ } else {
539
+ throwWindowsError(L"GetNamedPipeClientProcessId failed", lastError);
540
+ }
541
+ }
542
+
543
+ static std::unique_ptr<winpty_t>
544
+ createAgentSession(const winpty_config_t *cfg,
545
+ const std::wstring &desktop,
546
+ const std::wstring &params,
547
+ DWORD creationFlags) {
548
+ std::unique_ptr<winpty_t> wp(new winpty_t);
549
+ wp->agentTimeoutMs = cfg->timeoutMs;
550
+ wp->ioEvent = createEvent();
551
+
552
+ // Create control server pipe.
553
+ const auto pipeName =
554
+ L"\\\\.\\pipe\\winpty-control-" + GenRandom().uniqueName();
555
+ wp->controlPipe = createControlPipe(pipeName);
556
+
557
+ DWORD agentPid = 0;
558
+ wp->agentProcess = startAgentProcess(
559
+ desktop, pipeName, params, creationFlags, agentPid);
560
+ connectControlPipe(*wp.get());
561
+ verifyPipeClientPid(wp->controlPipe.get(), agentPid);
562
+
563
+ return std::move(wp);
564
+ }
565
+
566
+ namespace {
567
+
568
+ class AgentDesktop {
569
+ public:
570
+ virtual std::wstring name() = 0;
571
+ virtual ~AgentDesktop() {}
572
+ };
573
+
574
+ class AgentDesktopDirect : public AgentDesktop {
575
+ public:
576
+ AgentDesktopDirect(BackgroundDesktop &&desktop) :
577
+ m_desktop(std::move(desktop))
578
+ {
579
+ }
580
+ std::wstring name() override { return m_desktop.desktopName(); }
581
+ private:
582
+ BackgroundDesktop m_desktop;
583
+ };
584
+
585
+ class AgentDesktopIndirect : public AgentDesktop {
586
+ public:
587
+ AgentDesktopIndirect(std::unique_ptr<winpty_t> &&wp,
588
+ std::wstring &&desktopName) :
589
+ m_wp(std::move(wp)),
590
+ m_desktopName(std::move(desktopName))
591
+ {
592
+ }
593
+ std::wstring name() override { return m_desktopName; }
594
+ private:
595
+ std::unique_ptr<winpty_t> m_wp;
596
+ std::wstring m_desktopName;
597
+ };
598
+
599
+ } // anonymous namespace
600
+
601
+ std::unique_ptr<AgentDesktop>
602
+ setupBackgroundDesktop(const winpty_config_t *cfg) {
603
+ bool useDesktopAgent =
604
+ !(cfg->flags & WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION);
605
+ const bool useDesktop = shouldCreateBackgroundDesktop(useDesktopAgent);
606
+
607
+ if (!useDesktop) {
608
+ return std::unique_ptr<AgentDesktop>();
609
+ }
610
+
611
+ if (useDesktopAgent) {
612
+ auto wp = createAgentSession(
613
+ cfg, std::wstring(), L"--create-desktop", DETACHED_PROCESS);
614
+
615
+ // Read the desktop name.
616
+ auto packet = readPacket(*wp.get());
617
+ auto desktopName = packet.getWString();
618
+ packet.assertEof();
619
+
620
+ if (desktopName.empty()) {
621
+ return std::unique_ptr<AgentDesktop>();
622
+ } else {
623
+ return std::unique_ptr<AgentDesktop>(
624
+ new AgentDesktopIndirect(std::move(wp),
625
+ std::move(desktopName)));
626
+ }
627
+ } else {
628
+ try {
629
+ BackgroundDesktop desktop;
630
+ return std::unique_ptr<AgentDesktop>(new AgentDesktopDirect(
631
+ std::move(desktop)));
632
+ } catch (const WinptyException &e) {
633
+ trace("Error: failed to create background desktop, "
634
+ "using original desktop instead: %s",
635
+ utf8FromWide(e.what()).c_str());
636
+ return std::unique_ptr<AgentDesktop>();
637
+ }
638
+ }
639
+ }
640
+
641
+ WINPTY_API winpty_t *
642
+ winpty_open(const winpty_config_t *cfg,
643
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
644
+ API_TRY {
645
+ ASSERT(cfg != nullptr);
646
+ dumpWindowsVersion();
647
+ dumpVersionToTrace();
648
+
649
+ // Setup a background desktop for the agent.
650
+ auto desktop = setupBackgroundDesktop(cfg);
651
+ const auto desktopName = desktop ? desktop->name() : std::wstring();
652
+
653
+ // Start the primary agent session.
654
+ const auto params =
655
+ (WStringBuilder(128)
656
+ << cfg->flags << L' '
657
+ << cfg->mouseMode << L' '
658
+ << cfg->cols << L' '
659
+ << cfg->rows).str_moved();
660
+ auto wp = createAgentSession(cfg, desktopName, params,
661
+ CREATE_NEW_CONSOLE);
662
+
663
+ // Close handles to the background desktop and restore the original
664
+ // window station. This must wait until we know the agent is running
665
+ // -- if we close these handles too soon, then the desktop and
666
+ // windowstation will be destroyed before the agent can connect with
667
+ // them.
668
+ //
669
+ // If we used a separate agent process to create the desktop, we
670
+ // disconnect from that process here, allowing it to exit.
671
+ desktop.reset();
672
+
673
+ // If we ran the agent process on a background desktop, then when we
674
+ // spawn a child process from the agent, it will need to be explicitly
675
+ // placed back onto the original desktop.
676
+ if (!desktopName.empty()) {
677
+ wp->spawnDesktopName = getCurrentDesktopName();
678
+ }
679
+
680
+ // Get the CONIN/CONOUT pipe names.
681
+ auto packet = readPacket(*wp.get());
682
+ wp->coninPipeName = packet.getWString();
683
+ wp->conoutPipeName = packet.getWString();
684
+ if (cfg->flags & WINPTY_FLAG_CONERR) {
685
+ wp->conerrPipeName = packet.getWString();
686
+ }
687
+ packet.assertEof();
688
+
689
+ return wp.release();
690
+ } API_CATCH(nullptr)
691
+ }
692
+
693
+ WINPTY_API HANDLE winpty_agent_process(winpty_t *wp) {
694
+ ASSERT(wp != nullptr);
695
+ return wp->agentProcess.get();
696
+ }
697
+
698
+
699
+
700
+ /*****************************************************************************
701
+ * I/O pipes. */
702
+
703
+ static const wchar_t *cstrFromWStringOrNull(const std::wstring &str) {
704
+ try {
705
+ return str.c_str();
706
+ } catch (const std::bad_alloc&) {
707
+ return nullptr;
708
+ }
709
+ }
710
+
711
+ WINPTY_API LPCWSTR winpty_conin_name(winpty_t *wp) {
712
+ ASSERT(wp != nullptr);
713
+ return cstrFromWStringOrNull(wp->coninPipeName);
714
+ }
715
+
716
+ WINPTY_API LPCWSTR winpty_conout_name(winpty_t *wp) {
717
+ ASSERT(wp != nullptr);
718
+ return cstrFromWStringOrNull(wp->conoutPipeName);
719
+ }
720
+
721
+ WINPTY_API LPCWSTR winpty_conerr_name(winpty_t *wp) {
722
+ ASSERT(wp != nullptr);
723
+ if (wp->conerrPipeName.empty()) {
724
+ return nullptr;
725
+ } else {
726
+ return cstrFromWStringOrNull(wp->conerrPipeName);
727
+ }
728
+ }
729
+
730
+
731
+
732
+ /*****************************************************************************
733
+ * winpty agent RPC calls. */
734
+
735
+ namespace {
736
+
737
+ // Close the control pipe if something goes wrong with the pipe communication,
738
+ // which could leave the control pipe in an inconsistent state.
739
+ class RpcOperation {
740
+ public:
741
+ RpcOperation(winpty_t &wp) : m_wp(wp) {
742
+ if (m_wp.controlPipe.get() == nullptr) {
743
+ throwWinptyException(L"Agent shutdown due to RPC failure");
744
+ }
745
+ }
746
+ ~RpcOperation() {
747
+ if (!m_success) {
748
+ trace("~RpcOperation: Closing control pipe");
749
+ m_wp.controlPipe.dispose(true);
750
+ }
751
+ }
752
+ void success() { m_success = true; }
753
+ private:
754
+ winpty_t &m_wp;
755
+ bool m_success = false;
756
+ };
757
+
758
+ } // anonymous namespace
759
+
760
+
761
+
762
+ /*****************************************************************************
763
+ * winpty agent RPC call: process creation. */
764
+
765
+ // Return a std::wstring containing every character of the environment block.
766
+ // Typically, the block is non-empty, so the std::wstring returned ends with
767
+ // two NUL terminators. (These two terminators are counted in size(), so
768
+ // calling c_str() produces a triply-terminated string.)
769
+ static std::wstring wstringFromEnvBlock(const wchar_t *env) {
770
+ std::wstring envStr;
771
+ if (env != NULL) {
772
+ const wchar_t *p = env;
773
+ while (*p != L'\0') {
774
+ p += wcslen(p) + 1;
775
+ }
776
+ p++;
777
+ envStr.assign(env, p);
778
+
779
+ // Assuming the environment was non-empty, envStr now ends with two NUL
780
+ // terminators.
781
+ //
782
+ // If the environment were empty, though, then envStr would only be
783
+ // singly terminated, but the MSDN documentation thinks an env block is
784
+ // always doubly-terminated, so add an extra NUL just in case it
785
+ // matters.
786
+ const auto envStrSz = envStr.size();
787
+ if (envStrSz == 1) {
788
+ ASSERT(envStr[0] == L'\0');
789
+ envStr.push_back(L'\0');
790
+ } else {
791
+ ASSERT(envStrSz >= 3);
792
+ ASSERT(envStr[envStrSz - 3] != L'\0');
793
+ ASSERT(envStr[envStrSz - 2] == L'\0');
794
+ ASSERT(envStr[envStrSz - 1] == L'\0');
795
+ }
796
+ }
797
+ return envStr;
798
+ }
799
+
800
+ WINPTY_API winpty_spawn_config_t *
801
+ winpty_spawn_config_new(UINT64 winptyFlags,
802
+ LPCWSTR appname /*OPTIONAL*/,
803
+ LPCWSTR cmdline /*OPTIONAL*/,
804
+ LPCWSTR cwd /*OPTIONAL*/,
805
+ LPCWSTR env /*OPTIONAL*/,
806
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
807
+ API_TRY {
808
+ ASSERT((winptyFlags & WINPTY_SPAWN_FLAG_MASK) == winptyFlags);
809
+ std::unique_ptr<winpty_spawn_config_t> cfg(new winpty_spawn_config_t);
810
+ cfg->winptyFlags = winptyFlags;
811
+ if (appname != nullptr) { cfg->appname = appname; }
812
+ if (cmdline != nullptr) { cfg->cmdline = cmdline; }
813
+ if (cwd != nullptr) { cfg->cwd = cwd; }
814
+ if (env != nullptr) { cfg->env = wstringFromEnvBlock(env); }
815
+ return cfg.release();
816
+ } API_CATCH(nullptr)
817
+ }
818
+
819
+ WINPTY_API void winpty_spawn_config_free(winpty_spawn_config_t *cfg) {
820
+ delete cfg;
821
+ }
822
+
823
+ // It's safe to truncate a handle from 64-bits to 32-bits, or to sign-extend it
824
+ // back to 64-bits. See the MSDN article, "Interprocess Communication Between
825
+ // 32-bit and 64-bit Applications".
826
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203.aspx
827
+ static inline HANDLE handleFromInt64(int64_t i) {
828
+ return reinterpret_cast<HANDLE>(static_cast<intptr_t>(i));
829
+ }
830
+
831
+ // Given a process and a handle in that process, duplicate the handle into the
832
+ // current process and close it in the originating process.
833
+ static inline OwnedHandle stealHandle(HANDLE process, HANDLE handle) {
834
+ HANDLE result = nullptr;
835
+ if (!DuplicateHandle(process, handle,
836
+ GetCurrentProcess(),
837
+ &result, 0, FALSE,
838
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
839
+ throwWindowsError(L"DuplicateHandle of process handle");
840
+ }
841
+ return OwnedHandle(result);
842
+ }
843
+
844
+ WINPTY_API BOOL
845
+ winpty_spawn(winpty_t *wp,
846
+ const winpty_spawn_config_t *cfg,
847
+ HANDLE *process_handle /*OPTIONAL*/,
848
+ HANDLE *thread_handle /*OPTIONAL*/,
849
+ DWORD *create_process_error /*OPTIONAL*/,
850
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
851
+ API_TRY {
852
+ ASSERT(wp != nullptr && cfg != nullptr);
853
+
854
+ if (process_handle != nullptr) { *process_handle = nullptr; }
855
+ if (thread_handle != nullptr) { *thread_handle = nullptr; }
856
+ if (create_process_error != nullptr) { *create_process_error = 0; }
857
+
858
+ LockGuard<Mutex> lock(wp->mutex);
859
+ RpcOperation rpc(*wp);
860
+
861
+ // Send spawn request.
862
+ auto packet = newPacket();
863
+ packet.putInt32(AgentMsg::StartProcess);
864
+ packet.putInt64(cfg->winptyFlags);
865
+ packet.putInt32(process_handle != nullptr);
866
+ packet.putInt32(thread_handle != nullptr);
867
+ packet.putWString(cfg->appname);
868
+ packet.putWString(cfg->cmdline);
869
+ packet.putWString(cfg->cwd);
870
+ packet.putWString(cfg->env);
871
+ packet.putWString(wp->spawnDesktopName);
872
+ writePacket(*wp, packet);
873
+
874
+ // Receive reply.
875
+ auto reply = readPacket(*wp);
876
+ const auto result = static_cast<StartProcessResult>(reply.getInt32());
877
+ if (result == StartProcessResult::CreateProcessFailed) {
878
+ const DWORD lastError = reply.getInt32();
879
+ reply.assertEof();
880
+ if (create_process_error != nullptr) {
881
+ *create_process_error = lastError;
882
+ }
883
+ rpc.success();
884
+ throw LibWinptyException(WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED,
885
+ L"CreateProcess failed");
886
+ } else if (result == StartProcessResult::ProcessCreated) {
887
+ const HANDLE remoteProcess = handleFromInt64(reply.getInt64());
888
+ const HANDLE remoteThread = handleFromInt64(reply.getInt64());
889
+ reply.assertEof();
890
+ OwnedHandle localProcess;
891
+ OwnedHandle localThread;
892
+ if (remoteProcess != nullptr) {
893
+ localProcess =
894
+ stealHandle(wp->agentProcess.get(), remoteProcess);
895
+ }
896
+ if (remoteThread != nullptr) {
897
+ localThread =
898
+ stealHandle(wp->agentProcess.get(), remoteThread);
899
+ }
900
+ if (process_handle != nullptr) {
901
+ *process_handle = localProcess.release();
902
+ }
903
+ if (thread_handle != nullptr) {
904
+ *thread_handle = localThread.release();
905
+ }
906
+ rpc.success();
907
+ } else {
908
+ throwWinptyException(
909
+ L"Agent RPC error: invalid StartProcessResult");
910
+ }
911
+ return TRUE;
912
+ } API_CATCH(FALSE)
913
+ }
914
+
915
+
916
+
917
+ /*****************************************************************************
918
+ * winpty agent RPC calls: everything else */
919
+
920
+ WINPTY_API BOOL
921
+ winpty_set_size(winpty_t *wp, int cols, int rows,
922
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
923
+ API_TRY {
924
+ ASSERT(wp != nullptr && cols > 0 && rows > 0);
925
+ LockGuard<Mutex> lock(wp->mutex);
926
+ RpcOperation rpc(*wp);
927
+ auto packet = newPacket();
928
+ packet.putInt32(AgentMsg::SetSize);
929
+ packet.putInt32(cols);
930
+ packet.putInt32(rows);
931
+ writePacket(*wp, packet);
932
+ readPacket(*wp).assertEof();
933
+ rpc.success();
934
+ return TRUE;
935
+ } API_CATCH(FALSE)
936
+ }
937
+
938
+ WINPTY_API int
939
+ winpty_get_console_process_list(winpty_t *wp, int *processList, const int processCount,
940
+ winpty_error_ptr_t *err /*OPTIONAL*/) {
941
+ API_TRY {
942
+ ASSERT(wp != nullptr);
943
+ ASSERT(processList != nullptr);
944
+ LockGuard<Mutex> lock(wp->mutex);
945
+ RpcOperation rpc(*wp);
946
+ auto packet = newPacket();
947
+ packet.putInt32(AgentMsg::GetConsoleProcessList);
948
+ writePacket(*wp, packet);
949
+ auto reply = readPacket(*wp);
950
+
951
+ auto actualProcessCount = reply.getInt32();
952
+
953
+ if (actualProcessCount <= processCount) {
954
+ for (auto i = 0; i < actualProcessCount; i++) {
955
+ processList[i] = reply.getInt32();
956
+ }
957
+ }
958
+
959
+ reply.assertEof();
960
+ rpc.success();
961
+ return actualProcessCount;
962
+ } API_CATCH(0)
963
+ }
964
+
965
+ WINPTY_API void winpty_free(winpty_t *wp) {
966
+ // At least in principle, CloseHandle can fail, so this deletion can
967
+ // fail. It won't throw an exception, but maybe there's an error that
968
+ // should be propagated?
969
+ delete wp;
970
+ }