1 //===-- ProcessLauncherWindows.cpp ------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Host/windows/ProcessLauncherWindows.h"
10 #include "lldb/Host/HostProcess.h"
11 #include "lldb/Host/ProcessLaunchInfo.h"
12 
13 #include "llvm/ADT/SmallVector.h"
14 #include "llvm/Support/ConvertUTF.h"
15 
16 #include <string>
17 #include <vector>
18 
19 using namespace lldb;
20 using namespace lldb_private;
21 
22 namespace {
23 void CreateEnvironmentBuffer(const Environment &env,
24                              std::vector<char> &buffer) {
25   if (env.size() == 0)
26     return;
27 
28   // Environment buffer is a null terminated list of null terminated strings
29   for (const auto &KV : env) {
30     std::wstring warg;
31     if (llvm::ConvertUTF8toWide(Environment::compose(KV), warg)) {
32       buffer.insert(buffer.end(), (char *)warg.c_str(),
33                     (char *)(warg.c_str() + warg.size() + 1));
34     }
35   }
36   // One null wchar_t (to end the block) is two null bytes
37   buffer.push_back(0);
38   buffer.push_back(0);
39 }
40 }
41 
42 HostProcess
43 ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
44                                       Status &error) {
45   error.Clear();
46 
47   std::string executable;
48   std::string commandLine;
49   std::vector<char> environment;
50   STARTUPINFO startupinfo = {};
51   PROCESS_INFORMATION pi = {};
52 
53   HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
54   HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
55   HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
56 
57   startupinfo.cb = sizeof(startupinfo);
58   startupinfo.dwFlags |= STARTF_USESTDHANDLES;
59   startupinfo.hStdError =
60       stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
61   startupinfo.hStdInput =
62       stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
63   startupinfo.hStdOutput =
64       stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
65 
66   const char *hide_console_var =
67       getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
68   if (hide_console_var &&
69       llvm::StringRef(hide_console_var).equals_lower("true")) {
70     startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
71     startupinfo.wShowWindow = SW_HIDE;
72   }
73 
74   DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
75   if (launch_info.GetFlags().Test(eLaunchFlagDebug))
76     flags |= DEBUG_ONLY_THIS_PROCESS;
77 
78   if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO))
79     flags &= ~CREATE_NEW_CONSOLE;
80 
81   LPVOID env_block = nullptr;
82   ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment);
83   if (!environment.empty())
84     env_block = environment.data();
85 
86   executable = launch_info.GetExecutableFile().GetPath();
87   launch_info.GetArguments().GetQuotedCommandString(commandLine);
88 
89   std::wstring wexecutable, wcommandLine, wworkingDirectory;
90   llvm::ConvertUTF8toWide(executable, wexecutable);
91   llvm::ConvertUTF8toWide(commandLine, wcommandLine);
92   llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(),
93                           wworkingDirectory);
94 
95   wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because
96                                  // CreateProcessW can modify it
97   BOOL result = ::CreateProcessW(
98       wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block,
99       wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
100       &startupinfo, &pi);
101 
102   if (!result) {
103     // Call GetLastError before we make any other system calls.
104     error.SetError(::GetLastError(), eErrorTypeWin32);
105   }
106 
107   if (result) {
108     // Do not call CloseHandle on pi.hProcess, since we want to pass that back
109     // through the HostProcess.
110     ::CloseHandle(pi.hThread);
111   }
112 
113   if (stdin_handle)
114     ::CloseHandle(stdin_handle);
115   if (stdout_handle)
116     ::CloseHandle(stdout_handle);
117   if (stderr_handle)
118     ::CloseHandle(stderr_handle);
119 
120   if (!result)
121     return HostProcess();
122 
123   return HostProcess(pi.hProcess);
124 }
125 
126 HANDLE
127 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
128                                        int fd) {
129   const FileAction *action = launch_info.GetFileActionForFD(fd);
130   if (action == nullptr)
131     return NULL;
132   SECURITY_ATTRIBUTES secattr = {};
133   secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
134   secattr.bInheritHandle = TRUE;
135 
136   llvm::StringRef path = action->GetPath();
137   DWORD access = 0;
138   DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
139   DWORD create = 0;
140   DWORD flags = 0;
141   if (fd == STDIN_FILENO) {
142     access = GENERIC_READ;
143     create = OPEN_EXISTING;
144     flags = FILE_ATTRIBUTE_READONLY;
145   }
146   if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
147     access = GENERIC_WRITE;
148     create = CREATE_ALWAYS;
149     if (fd == STDERR_FILENO)
150       flags = FILE_FLAG_WRITE_THROUGH;
151   }
152 
153   std::wstring wpath;
154   llvm::ConvertUTF8toWide(path, wpath);
155   HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
156                                 flags, NULL);
157   return (result == INVALID_HANDLE_VALUE) ? NULL : result;
158 }
159