1172d37d3SZachary Turner //===-- ProcessLauncherWindows.cpp ------------------------------*- C++ -*-===//
2172d37d3SZachary Turner //
3172d37d3SZachary Turner //                     The LLVM Compiler Infrastructure
4172d37d3SZachary Turner //
5172d37d3SZachary Turner // This file is distributed under the University of Illinois Open Source
6172d37d3SZachary Turner // License. See LICENSE.TXT for details.
7172d37d3SZachary Turner //
8172d37d3SZachary Turner //===----------------------------------------------------------------------===//
9172d37d3SZachary Turner 
10172d37d3SZachary Turner #include "lldb/Host/HostProcess.h"
11172d37d3SZachary Turner #include "lldb/Host/windows/ProcessLauncherWindows.h"
12172d37d3SZachary Turner #include "lldb/Target/ProcessLaunchInfo.h"
13172d37d3SZachary Turner 
14*190fadcdSZachary Turner #include "llvm/ADT/SmallVector.h"
15*190fadcdSZachary Turner #include "llvm/Support/ConvertUTF.h"
16*190fadcdSZachary Turner 
17172d37d3SZachary Turner #include <string>
18172d37d3SZachary Turner #include <vector>
19172d37d3SZachary Turner 
20172d37d3SZachary Turner using namespace lldb;
21172d37d3SZachary Turner using namespace lldb_private;
22172d37d3SZachary Turner 
2319e2ea8fSZachary Turner namespace
2419e2ea8fSZachary Turner {
2519e2ea8fSZachary Turner void
2619e2ea8fSZachary Turner CreateEnvironmentBuffer(const Args &env, std::vector<char> &buffer)
2719e2ea8fSZachary Turner {
2819e2ea8fSZachary Turner     if (env.GetArgumentCount() == 0)
2919e2ea8fSZachary Turner         return;
3019e2ea8fSZachary Turner 
31*190fadcdSZachary Turner     // Environment buffer is a null terminated list of null terminated strings
3219e2ea8fSZachary Turner     for (int i = 0; i < env.GetArgumentCount(); ++i)
3319e2ea8fSZachary Turner     {
34*190fadcdSZachary Turner         std::wstring warg;
35*190fadcdSZachary Turner         if (llvm::ConvertUTF8toWide(env.GetArgumentAtIndex(i), warg))
36*190fadcdSZachary Turner         {
37*190fadcdSZachary Turner             buffer.insert(buffer.end(), (char *)warg.c_str(), (char *)(warg.c_str() + warg.size() + 1));
3819e2ea8fSZachary Turner         }
39*190fadcdSZachary Turner     }
40*190fadcdSZachary Turner     // One null wchar_t (to end the block) is two null bytes
41*190fadcdSZachary Turner     buffer.push_back(0);
42*190fadcdSZachary Turner     buffer.push_back(0);
4319e2ea8fSZachary Turner }
4419e2ea8fSZachary Turner }
4519e2ea8fSZachary Turner 
46172d37d3SZachary Turner HostProcess
47172d37d3SZachary Turner ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, Error &error)
48172d37d3SZachary Turner {
49172d37d3SZachary Turner     error.Clear();
50172d37d3SZachary Turner 
51172d37d3SZachary Turner     std::string executable;
52172d37d3SZachary Turner     std::string commandLine;
53172d37d3SZachary Turner     std::vector<char> environment;
54172d37d3SZachary Turner     STARTUPINFO startupinfo = {0};
55172d37d3SZachary Turner     PROCESS_INFORMATION pi = {0};
56172d37d3SZachary Turner 
57172d37d3SZachary Turner     HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO);
58172d37d3SZachary Turner     HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO);
59172d37d3SZachary Turner     HANDLE stderr_handle = GetStdioHandle(launch_info, STDERR_FILENO);
60172d37d3SZachary Turner 
61172d37d3SZachary Turner     startupinfo.cb = sizeof(startupinfo);
62172d37d3SZachary Turner     startupinfo.dwFlags |= STARTF_USESTDHANDLES;
63e1adc7bcSAdrian McCarthy     startupinfo.hStdError  = stderr_handle ? stderr_handle : ::GetStdHandle(STD_ERROR_HANDLE);
64e1adc7bcSAdrian McCarthy     startupinfo.hStdInput  = stdin_handle  ? stdin_handle  : ::GetStdHandle(STD_INPUT_HANDLE);
65e1adc7bcSAdrian McCarthy     startupinfo.hStdOutput = stdout_handle ? stdout_handle : ::GetStdHandle(STD_OUTPUT_HANDLE);
66172d37d3SZachary Turner 
67555a7a6aSZachary Turner     const char *hide_console_var = getenv("LLDB_LAUNCH_INFERIORS_WITHOUT_CONSOLE");
68555a7a6aSZachary Turner     if (hide_console_var && llvm::StringRef(hide_console_var).equals_lower("true"))
69555a7a6aSZachary Turner     {
70555a7a6aSZachary Turner         startupinfo.dwFlags |= STARTF_USESHOWWINDOW;
71555a7a6aSZachary Turner         startupinfo.wShowWindow = SW_HIDE;
72555a7a6aSZachary Turner     }
73555a7a6aSZachary Turner 
74*190fadcdSZachary Turner     DWORD flags = CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
75742346a2SZachary Turner     if (launch_info.GetFlags().Test(eLaunchFlagDebug))
76742346a2SZachary Turner         flags |= DEBUG_ONLY_THIS_PROCESS;
77742346a2SZachary Turner 
7819e2ea8fSZachary Turner     auto &env = const_cast<Args &>(launch_info.GetEnvironmentEntries());
7919e2ea8fSZachary Turner     LPVOID env_block = nullptr;
8019e2ea8fSZachary Turner     ::CreateEnvironmentBuffer(env, environment);
8119e2ea8fSZachary Turner     if (!environment.empty())
8219e2ea8fSZachary Turner         env_block = environment.data();
8319e2ea8fSZachary Turner 
84172d37d3SZachary Turner     executable = launch_info.GetExecutableFile().GetPath();
85172d37d3SZachary Turner     launch_info.GetArguments().GetQuotedCommandString(commandLine);
86*190fadcdSZachary Turner 
87*190fadcdSZachary Turner     std::wstring wexecutable, wcommandLine, wworkingDirectory;
88*190fadcdSZachary Turner     llvm::ConvertUTF8toWide(executable, wexecutable);
89*190fadcdSZachary Turner     llvm::ConvertUTF8toWide(commandLine, wcommandLine);
90*190fadcdSZachary Turner     llvm::ConvertUTF8toWide(launch_info.GetWorkingDirectory().GetCString(), wworkingDirectory);
91*190fadcdSZachary Turner 
92*190fadcdSZachary Turner     wcommandLine.resize(PATH_MAX); // Needs to be over-allocated because CreateProcessW can modify it
93*190fadcdSZachary Turner     BOOL result = ::CreateProcessW(wexecutable.c_str(), &wcommandLine[0], NULL, NULL, TRUE, flags, env_block,
94*190fadcdSZachary Turner                                    wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), &startupinfo, &pi);
95172d37d3SZachary Turner     if (result)
96172d37d3SZachary Turner     {
97172d37d3SZachary Turner         // Do not call CloseHandle on pi.hProcess, since we want to pass that back through the HostProcess.
98172d37d3SZachary Turner         ::CloseHandle(pi.hThread);
99172d37d3SZachary Turner     }
100172d37d3SZachary Turner 
101172d37d3SZachary Turner     if (stdin_handle)
102172d37d3SZachary Turner         ::CloseHandle(stdin_handle);
103172d37d3SZachary Turner     if (stdout_handle)
104172d37d3SZachary Turner         ::CloseHandle(stdout_handle);
105172d37d3SZachary Turner     if (stderr_handle)
106172d37d3SZachary Turner         ::CloseHandle(stderr_handle);
107172d37d3SZachary Turner 
108172d37d3SZachary Turner     if (!result)
109172d37d3SZachary Turner         error.SetError(::GetLastError(), eErrorTypeWin32);
110172d37d3SZachary Turner     return HostProcess(pi.hProcess);
111172d37d3SZachary Turner }
112172d37d3SZachary Turner 
113172d37d3SZachary Turner HANDLE
114172d37d3SZachary Turner ProcessLauncherWindows::GetStdioHandle(const ProcessLaunchInfo &launch_info, int fd)
115172d37d3SZachary Turner {
116172d37d3SZachary Turner     const FileAction *action = launch_info.GetFileActionForFD(fd);
117172d37d3SZachary Turner     if (action == nullptr)
118172d37d3SZachary Turner         return NULL;
119172d37d3SZachary Turner     SECURITY_ATTRIBUTES secattr = {0};
120172d37d3SZachary Turner     secattr.nLength = sizeof(SECURITY_ATTRIBUTES);
121172d37d3SZachary Turner     secattr.bInheritHandle = TRUE;
122172d37d3SZachary Turner 
123172d37d3SZachary Turner     const char *path = action->GetPath();
124172d37d3SZachary Turner     DWORD access = 0;
125022c3c85SZachary Turner     DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
126172d37d3SZachary Turner     DWORD create = 0;
127172d37d3SZachary Turner     DWORD flags = 0;
128172d37d3SZachary Turner     if (fd == STDIN_FILENO)
129172d37d3SZachary Turner     {
130172d37d3SZachary Turner         access = GENERIC_READ;
131172d37d3SZachary Turner         create = OPEN_EXISTING;
132172d37d3SZachary Turner         flags = FILE_ATTRIBUTE_READONLY;
133172d37d3SZachary Turner     }
134172d37d3SZachary Turner     if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
135172d37d3SZachary Turner     {
136172d37d3SZachary Turner         access = GENERIC_WRITE;
137172d37d3SZachary Turner         create = CREATE_ALWAYS;
138172d37d3SZachary Turner         if (fd == STDERR_FILENO)
139172d37d3SZachary Turner             flags = FILE_FLAG_WRITE_THROUGH;
140172d37d3SZachary Turner     }
141172d37d3SZachary Turner 
142*190fadcdSZachary Turner     std::wstring wpath;
143*190fadcdSZachary Turner     llvm::ConvertUTF8toWide(path, wpath);
144*190fadcdSZachary Turner     HANDLE result = ::CreateFileW(wpath.c_str(), access, share, &secattr, create, flags, NULL);
145172d37d3SZachary Turner     return (result == INVALID_HANDLE_VALUE) ? NULL : result;
146172d37d3SZachary Turner }
147