xref: /llvm-project/lldb/source/Host/windows/ProcessLauncherWindows.cpp (revision b9c1b51e45b845debb76d8658edabca70ca56079)
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 Args &env, std::vector<char> &buffer) {
25   if (env.GetArgumentCount() == 0)
26     return;
27 
28   // Environment buffer is a null terminated list of null terminated strings
29   for (int i = 0; i < env.GetArgumentCount(); ++i) {
30     std::wstring warg;
31     if (llvm::ConvertUTF8toWide(env.GetArgumentAtIndex(i), 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                                       Error &error) {
45   error.Clear();
46 
47   std::string executable;
48   std::string commandLine;
49   std::vector<char> environment;
50   STARTUPINFO startupinfo = {0};
51   PROCESS_INFORMATION pi = {0};
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   auto &env = const_cast<Args &>(launch_info.GetEnvironmentEntries());
79   LPVOID env_block = nullptr;
80   ::CreateEnvironmentBuffer(env, environment);
81   if (!environment.empty())
82     env_block = environment.data();
83 
84   executable = launch_info.GetExecutableFile().GetPath();
85   launch_info.GetArguments().GetQuotedCommandString(commandLine);
86 
87   std::wstring wexecutable, wcommandLine, wworkingDirectory;
88   llvm::ConvertUTF8toWide(executable, wexecutable);
89   llvm::ConvertUTF8toWide(commandLine, wcommandLine);
90   llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(),
91                           wworkingDirectory);
92 
93   wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because
94                                  // CreateProcessW can modify it
95   BOOL result = ::CreateProcessW(
96       wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block,
97       wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(),
98       &startupinfo, &pi);
99   if (result) {
100     // Do not call CloseHandle on pi.hProcess, since we want to pass that back
101     // through the HostProcess.
102     ::CloseHandle(pi.hThread);
103   }
104 
105   if (stdin_handle)
106     ::CloseHandle(stdin_handle);
107   if (stdout_handle)
108     ::CloseHandle(stdout_handle);
109   if (stderr_handle)
110     ::CloseHandle(stderr_handle);
111 
112   if (!result)
113     error.SetError(::GetLastError(), eErrorTypeWin32);
114   return HostProcess(pi.hProcess);
115 }
116 
117 HANDLE
118 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info,
119                                        int fd) {
120   const FileAction *action = launch_info.GetFileActionForFD(fd);
121   if (action == nullptr)
122     return NULL;
123   SECURITY_ATTRIBUTES secattr = {0};
124   secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
125   secattr.bInheritHandle = TRUE;
126 
127   const char *path = action->GetPath();
128   DWORD access = 0;
129   DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
130   DWORD create = 0;
131   DWORD flags = 0;
132   if (fd == STDIN_FILENO) {
133     access = GENERIC_READ;
134     create = OPEN_EXISTING;
135     flags = FILE_ATTRIBUTE_READONLY;
136   }
137   if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
138     access = GENERIC_WRITE;
139     create = CREATE_ALWAYS;
140     if (fd == STDERR_FILENO)
141       flags = FILE_FLAG_WRITE_THROUGH;
142   }
143 
144   std::wstring wpath;
145   llvm::ConvertUTF8toWide(path, wpath);
146   HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create,
147                                 flags, NULL);
148   return (result == INVALID_HANDLE_VALUE) ? NULL : result;
149 }
150