xref: /llvm-project/lldb/source/Host/windows/ProcessLauncherWindows.cpp (revision 190fadcdb245707011e31b69a24bd6bba7c93ab5)
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/HostProcess.h"
11 #include "lldb/Host/windows/ProcessLauncherWindows.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 {
25 void
26 CreateEnvironmentBuffer(const Args &env, std::vector<char> &buffer)
27 {
28     if (env.GetArgumentCount() == 0)
29         return;
30 
31     // Environment buffer is a null terminated list of null terminated strings
32     for (int i = 0; i < env.GetArgumentCount(); ++i)
33     {
34         std::wstring warg;
35         if (llvm::ConvertUTF8toWide(env.GetArgumentAtIndex(i), warg))
36         {
37             buffer.insert(buffer.end(), (char *)warg.c_str(), (char *)(warg.c_str() + warg.size() + 1));
38         }
39     }
40     // One null wchar_t (to end the block) is two null bytes
41     buffer.push_back(0);
42     buffer.push_back(0);
43 }
44 }
45 
46 HostProcess
47 ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error)
48 {
49     error.Clear();
50 
51     std::string executable;
52     std::string commandLine;
53     std::vector<char> environment;
54     STARTUPINFO startupinfo = {0};
55     PROCESS_INFORMATION pi = {0};
56 
57     HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
58     HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
59     HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
60 
61     startupinfo.cb = sizeof(startupinfo);
62     startupinfo.dwFlags |= STARTF_USESTDHANDLES;
63     startupinfo.hStdError  = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
64     startupinfo.hStdInput  = stdin_handle  ? stdin_handle  : ::GetStdHandle(STD_INPUT_HANDLE);
65     startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
66 
67     const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
68     if (hide_console_var && llvm::StringRef(hide_console_var).equals_lower("true"))
69     {
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(), wworkingDirectory);
91 
92     wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because CreateProcessW can modify it
93     BOOL result = ::CreateProcessW(wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block,
94                                    wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), &startupinfo, &pi);
95     if (result)
96     {
97         // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess.
98         ::CloseHandle(pi.hThread);
99     }
100 
101     if (stdin_handle)
102         ::CloseHandle(stdin_handle);
103     if (stdout_handle)
104         ::CloseHandle(stdout_handle);
105     if (stderr_handle)
106         ::CloseHandle(stderr_handle);
107 
108     if (!result)
109         error.SetError(::GetLastError(), eErrorTypeWin32);
110     return HostProcess(pi.hProcess);
111 }
112 
113 HANDLE
114 ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd)
115 {
116     const FileAction *action = launch_info.GetFileActionForFD(fd);
117     if (action == nullptr)
118         return NULL;
119     SECURITY_ATTRIBUTES secattr = {0};
120     secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
121     secattr.bInheritHandle = TRUE;
122 
123     const char *path = action->GetPath();
124     DWORD access = 0;
125     DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
126     DWORD create = 0;
127     DWORD flags = 0;
128     if (fd == STDIN_FILENO)
129     {
130         access = GENERIC_READ;
131         create = OPEN_EXISTING;
132         flags = FILE_ATTRIBUTE_READONLY;
133     }
134     if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
135     {
136         access = GENERIC_WRITE;
137         create = CREATE_ALWAYS;
138         if (fd == STDERR_FILENO)
139             flags = FILE_FLAG_WRITE_THROUGH;
140     }
141 
142     std::wstring wpath;
143     llvm::ConvertUTF8toWide(path, wpath);
144     HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, flags, NULL);
145     return (result == INVALID_HANDLE_VALUE) ? NULL : result;
146 }
147