Skip to content

Commit cd593bb

Browse files
author
Daniel Kroening
authored
Merge pull request #2922 from diffblue/run-CreateProcess
replace _wspawnvp() by CreateProcessW()
2 parents e32c469 + fd9e35c commit cd593bb

File tree

1 file changed

+203
-45
lines changed

1 file changed

+203
-45
lines changed

src/util/run.cpp

Lines changed: 203 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Date: August 2012
1212

1313
#ifdef _WIN32
1414
#include <process.h>
15+
#include <windows.h>
1516
#else
1617

1718
#include <cstring>
@@ -40,14 +41,85 @@ int run(const std::string &what, const std::vector<std::string> &argv)
4041
return run(what, argv, "", "", "");
4142
}
4243

43-
#ifndef _WIN32
44+
#ifdef _WIN32
45+
#define STDIN_FILENO 0
46+
#define STDOUT_FILENO 1
47+
#define STDERR_FILENO 2
48+
using fdt = HANDLE;
49+
#else
50+
using fdt = int;
51+
#endif
52+
4453
/// open given file to replace either stdin, stderr, stdout
45-
static int stdio_redirection(int fd, const std::string &file)
54+
static fdt stdio_redirection(int fd, const std::string &file)
4655
{
47-
int result_fd = fd;
56+
fdt result_fd;
57+
58+
#ifdef _WIN32
59+
std::string name;
60+
61+
SECURITY_ATTRIBUTES SecurityAttributes;
62+
ZeroMemory(&SecurityAttributes, sizeof SecurityAttributes);
63+
SecurityAttributes.bInheritHandle = true;
64+
65+
switch(fd)
66+
{
67+
case STDIN_FILENO:
68+
name = "stdin";
69+
if(file.empty())
70+
result_fd = GetStdHandle(STD_INPUT_HANDLE);
71+
else
72+
result_fd = CreateFileW(
73+
widen(file).c_str(),
74+
GENERIC_READ,
75+
0,
76+
&SecurityAttributes,
77+
OPEN_EXISTING,
78+
FILE_ATTRIBUTE_READONLY,
79+
NULL);
80+
break;
81+
82+
case STDOUT_FILENO:
83+
name = "stdout";
84+
if(file.empty())
85+
result_fd = GetStdHandle(STD_OUTPUT_HANDLE);
86+
else
87+
result_fd = CreateFileW(
88+
widen(file).c_str(),
89+
GENERIC_WRITE,
90+
0,
91+
&SecurityAttributes,
92+
CREATE_ALWAYS,
93+
FILE_ATTRIBUTE_NORMAL,
94+
NULL);
95+
break;
96+
97+
case STDERR_FILENO:
98+
name = "stderr";
99+
if(file.empty())
100+
result_fd = GetStdHandle(STD_ERROR_HANDLE);
101+
else
102+
result_fd = CreateFileW(
103+
widen(file).c_str(),
104+
GENERIC_WRITE,
105+
0,
106+
&SecurityAttributes,
107+
CREATE_ALWAYS,
108+
FILE_ATTRIBUTE_NORMAL,
109+
NULL);
110+
break;
111+
112+
default:
113+
UNREACHABLE;
114+
}
115+
116+
if(result_fd == INVALID_HANDLE_VALUE)
117+
perror(("Failed to open " + name + " file " + file).c_str());
118+
119+
#else
48120

49121
if(file.empty())
50-
return result_fd;
122+
return fd;
51123

52124
int flags = 0, mode = 0;
53125
std::string name;
@@ -71,74 +143,160 @@ static int stdio_redirection(int fd, const std::string &file)
71143
}
72144

73145
result_fd = open(file.c_str(), flags, mode);
146+
74147
if(result_fd == -1)
75148
perror(("Failed to open " + name + " file " + file).c_str());
149+
#endif
76150

77151
return result_fd;
78152
}
79-
#endif
80153

81-
int run(
82-
const std::string &what,
83-
const std::vector<std::string> &argv,
84-
const std::string &std_input,
85-
const std::string &std_output,
86-
const std::string &std_error)
154+
#ifdef _WIN32
155+
// Read
156+
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
157+
std::wstring quote_windows_arg(const std::wstring &src)
87158
{
88-
#ifdef _WIN32
89-
// we use the cmd.exe shell to do stdin/stdout/stderr redirection on Windows
90-
if(!std_input.empty() || !std_output.empty() || !std_error.empty())
159+
if(src.find_first_of(L" \t\n\v\"") == src.npos)
160+
return src;
161+
162+
std::wstring result = L"\"";
163+
164+
for(auto it = src.begin();; ++it)
91165
{
92-
std::vector<std::string> new_argv = argv;
93-
new_argv.insert(new_argv.begin(), "cmd.exe");
94-
new_argv.insert(new_argv.begin() + 1, "/c");
166+
std::size_t NumberBackslashes = 0;
95167

96-
if(!std_input.empty())
168+
while(it != src.end() && *it == L'\\')
97169
{
98-
new_argv.push_back("<");
99-
new_argv.push_back(std_input);
170+
++it;
171+
++NumberBackslashes;
100172
}
101173

102-
if(!std_output.empty())
174+
if(it == src.end())
103175
{
104-
new_argv.push_back(">");
105-
new_argv.push_back(std_output);
176+
//
177+
// Escape all backslashes, but let the terminating
178+
// double quotation mark we add below be interpreted
179+
// as a metacharacter.
180+
//
181+
182+
result.append(NumberBackslashes * 2, L'\\');
183+
break;
106184
}
185+
else if(*it == L'"')
186+
{
187+
//
188+
// Escape all backslashes and the following
189+
// double quotation mark.
190+
//
107191

108-
if(!std_error.empty())
192+
result.append(NumberBackslashes * 2 + 1, L'\\');
193+
result.push_back(*it);
194+
}
195+
else
109196
{
110-
new_argv.push_back("2>");
111-
new_argv.push_back(std_error);
197+
//
198+
// Backslashes aren't special here.
199+
//
200+
201+
result.append(NumberBackslashes, L'\\');
202+
result.push_back(*it);
112203
}
204+
}
205+
206+
result.push_back(L'"');
207+
208+
return result;
209+
}
210+
#endif
113211

114-
// this is recursive
115-
return run(new_argv[0], new_argv, "", "", "");
212+
int run(
213+
const std::string &what,
214+
const std::vector<std::string> &argv,
215+
const std::string &std_input,
216+
const std::string &std_output,
217+
const std::string &std_error)
218+
{
219+
#ifdef _WIN32
220+
// unicode commandline, quoted
221+
std::wstring cmdline;
222+
223+
// we replace argv[0] by what
224+
cmdline = quote_windows_arg(widen(what));
225+
226+
for(std::size_t i = 1; i < argv.size(); i++)
227+
{
228+
cmdline += L" ";
229+
cmdline += quote_windows_arg(widen(argv[i]));
116230
}
117231

118-
// unicode version of the arguments
119-
std::vector<std::wstring> wargv;
232+
PROCESS_INFORMATION piProcInfo;
233+
STARTUPINFOW siStartInfo;
234+
235+
ZeroMemory(&piProcInfo, sizeof piProcInfo);
236+
ZeroMemory(&siStartInfo, sizeof siStartInfo);
120237

121-
wargv.resize(argv.size());
238+
siStartInfo.cb = sizeof siStartInfo;
122239

123-
for(std::size_t i=0; i<argv.size(); i++)
124-
wargv[i]=widen(argv[i]);
240+
siStartInfo.hStdInput = stdio_redirection(STDIN_FILENO, std_input);
241+
siStartInfo.hStdOutput = stdio_redirection(STDOUT_FILENO, std_output);
242+
siStartInfo.hStdError = stdio_redirection(STDERR_FILENO, std_error);
125243

126-
std::vector<const wchar_t *> _argv(argv.size()+1);
244+
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
127245

128-
for(std::size_t i=0; i<wargv.size(); i++)
129-
_argv[i]=wargv[i].c_str();
246+
// CreateProcessW wants to modify the command line
247+
std::vector<wchar_t> mutable_cmdline(cmdline.begin(), cmdline.end());
248+
mutable_cmdline.push_back(0); // zero termination
249+
wchar_t *cmdline_ptr = mutable_cmdline.data();
130250

131-
_argv[argv.size()]=NULL;
251+
BOOL bSuccess = CreateProcessW(
252+
NULL, // application name
253+
cmdline_ptr, // command line
254+
NULL, // process security attributes
255+
NULL, // primary thread security attributes
256+
true, // handles are inherited
257+
0, // creation flags
258+
NULL, // use parent's environment
259+
NULL, // use parent's current directory
260+
&siStartInfo, // STARTUPINFO
261+
&piProcInfo); // PROCESS_INFORMATION
132262

133-
// warning: the arguments may still need escaping,
134-
// as windows will concatenate the argv strings back together,
135-
// separating them with spaces
263+
if(!bSuccess)
264+
{
265+
if(!std_input.empty())
266+
CloseHandle(siStartInfo.hStdInput);
267+
if(!std_output.empty())
268+
CloseHandle(siStartInfo.hStdOutput);
269+
if(!std_error.empty())
270+
CloseHandle(siStartInfo.hStdError);
271+
return -1;
272+
}
136273

137-
std::wstring wide_what=widen(what);
138-
int status=_wspawnvp(_P_WAIT, wide_what.c_str(), _argv.data());
139-
return status;
274+
// wait for child to finish
275+
WaitForSingleObject(piProcInfo.hProcess, INFINITE);
140276

141-
#else
277+
if(!std_input.empty())
278+
CloseHandle(siStartInfo.hStdInput);
279+
if(!std_output.empty())
280+
CloseHandle(siStartInfo.hStdOutput);
281+
if(!std_error.empty())
282+
CloseHandle(siStartInfo.hStdError);
283+
284+
DWORD exit_code;
285+
286+
// get exit code
287+
if(!GetExitCodeProcess(piProcInfo.hProcess, &exit_code))
288+
{
289+
CloseHandle(piProcInfo.hProcess);
290+
CloseHandle(piProcInfo.hThread);
291+
return -1;
292+
}
293+
294+
CloseHandle(piProcInfo.hProcess);
295+
CloseHandle(piProcInfo.hThread);
296+
297+
return exit_code;
298+
299+
#else
142300
int stdin_fd = stdio_redirection(STDIN_FILENO, std_input);
143301
int stdout_fd = stdio_redirection(STDOUT_FILENO, std_output);
144302
int stderr_fd = stdio_redirection(STDERR_FILENO, std_error);
@@ -229,7 +387,7 @@ int run(
229387

230388
return 1;
231389
}
232-
#endif
390+
#endif
233391
}
234392

235393
int run(

0 commit comments

Comments
 (0)