installed pty
[VSoRC/.git] / node_modules / node-pty / deps / winpty / src / libwinpty / winpty.cc
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 }