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