xref: /llvm-project/lldb/source/Host/windows/ProcessLauncherWindows.cpp (revision d75a8fff7f6f9a59f2241dfb01a598d4fb1c12eb)
1 //===-- ProcessLauncherWindows.cpp ------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Host/windows/ProcessLauncherWindows.h"
11 #include "lldb/Host/HostProcess.h"
12 #include "lldb/Target/ProcessLaunchInfo.h"
13 
14 #include "llvm/ADT/SmallVector.h"
15 #include "llvm/Support/ConvertUTF.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 
43 HostProcess
44 ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info,
45                                       Status &error) {
46   error.Clear();
47 
48   std::string executable;
49   std::string commandLine;
50   std::vector<char> environment;
51   STARTUPINFO startupinfo = {};
52   PROCESS_INFORMATION pi = {};
53 
54   HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
55   HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
56   HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
57 
58   startupinfo.cb = sizeof(startupinfo);
59   startupinfo.dwFlags |= STARTF_USESTDHANDLES;
60   startupinfo.hStdError =
61       stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
62   startupinfo.hStdInput =
63       stdin_handle ? stdin_handle : ::GetStdHandle(STD_INPUT_HANDLE);
64   startupinfo.hStdOutput =
65       stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
66 
67   const char *hide_console_var =
68       getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
69   if (hide_console_var &&
70       llvm::StringRef(hide_console_var).equals_lower("true")) {
71     startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
72     startupinfo.wShowWindow = SW_HIDE;
73   }
74 
75   DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
76   if (launch_info.GetFlags().Test(eLaunchFlagDebug))
77     flags |= DEBUG_ONLY_THIS_PROCESS;
78 
79   if (launch_info.GetFlags().Test(eLaunchFlagDisableSTDIO))
80     flags &= ~CREATE_NEW_CONSOLE;
81 
82   LPVOID env_block = nullptr;
83   ::CreateEnvironmentBuffer(launch_info.GetEnvironment(), environment);
84   if (!environment.empty())
85     env_block = environment.data();
86 
87   executable = launch_info.GetExecutableFile().GetPath();
88   launch_info.GetArguments().GetQuotedCommandString(commandLine);
89 
90   std::wstring wexecutable, wcommandLine, wworkingDirectory;
91   llvm::ConvertUTF8toWide(executable, wexecutable);
92   llvm::ConvertUTF8toWide(commandLine, wcommandLine);
93   llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(),
94                           wworkingDirectory);
95 
96   wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because
97                                  // CreateProcessW can modify it
98   BOOL result = ::CreateProcessW(
99       wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block,
100       wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
101       &startupinfo, &pi);
102 
103   if (!result) {
104     // Call GetLastError before we make any other system calls.
105     error.SetError(::GetLastError(), eErrorTypeWin32);
106   }
107 
108   if (result) {
109     // Do not call CloseHandle on pi.hProcess, since we want to pass that back
110     // through the HostProcess.
111     ::CloseHandle(pi.hThread);
112   }
113 
114   if (stdin_handle)
115     ::CloseHandle(stdin_handle);
116   if (stdout_handle)
117     ::CloseHandle(stdout_handle);
118   if (stderr_handle)
119     ::CloseHandle(stderr_handle);
120 
121   if (!result)
122     return HostProcess();
123 
124   return HostProcess(pi.hProcess);
125 }
126 
127 HANDLE
128 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
129                                        int fd) {
130   const FileAction *action = launch_info.GetFileActionForFD(fd);
131   if (action == nullptr)
132     return NULL;
133   SECURITY_ATTRIBUTES secattr = {};
134   secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
135   secattr.bInheritHandle = TRUE;
136 
137   llvm::StringRef path = action->GetPath();
138   DWORD access = 0;
139   DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
140   DWORD create = 0;
141   DWORD flags = 0;
142   if (fd == STDIN_FILENO) {
143     access = GENERIC_READ;
144     create = OPEN_EXISTING;
145     flags = FILE_ATTRIBUTE_READONLY;
146   }
147   if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
148     access = GENERIC_WRITE;
149     create = CREATE_ALWAYS;
150     if (fd == STDERR_FILENO)
151       flags = FILE_FLAG_WRITE_THROUGH;
152   }
153 
154   std::wstring wpath;
155   llvm::ConvertUTF8toWide(path, wpath);
156   HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
157                                 flags, NULL);
158   return (result == INVALID_HANDLE_VALUE) ? NULL : result;
159 }
160