CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
run.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module:
4
5Author: Daniel Kroening
6
7Date: August 2012
8
9\*******************************************************************/
10
11#include "run.h"
12
13#ifdef _WIN32
14// clang-format off
15#include <util/pragma_push.def>
16#ifdef _MSC_VER
17#pragma warning(disable:4668)
18 // using #if/#elif on undefined macro
19#pragma warning(disable:5039)
20// pointer or reference to potentially throwing function passed to extern C
21#endif
22#include <process.h>
23#include <windows.h>
24#include <util/pragma_pop.def>
25#include "tempfile.h"
26#include "unicode.h"
27// clang-format on
28#else
29
30#include <cstring>
31#include <cerrno>
32#include <cstdio>
33#include <cstdlib>
34
35#include <fcntl.h>
36#include <signal.h>
37#include <sys/stat.h>
38#include <sys/wait.h>
39#include <unistd.h>
40
41#endif
42
43#include <fstream>
44
45#include "invariant.h"
46#include "signal_catcher.h"
47
48int run(const std::string &what, const std::vector<std::string> &argv)
49{
50 return run(what, argv, "", "", "");
51}
52
53#ifdef _WIN32
54#define STDIN_FILENO 0
55#define STDOUT_FILENO 1
56#define STDERR_FILENO 2
57using fdt = HANDLE;
58#else
59using fdt = int;
60#endif
61
63static fdt stdio_redirection(int fd, const std::string &file)
64{
65#ifdef _WIN32
67 std::string name;
68
71 SecurityAttributes.bInheritHandle = true;
72
73 switch(fd)
74 {
75 case STDIN_FILENO:
76 name = "stdin";
77 if(file.empty())
79 else
81 widen(file).c_str(),
83 0,
87 NULL);
88 break;
89
90 case STDOUT_FILENO:
91 name = "stdout";
92 if(file.empty())
94 else
96 widen(file).c_str(),
98 0,
102 NULL);
103 break;
104
105 case STDERR_FILENO:
106 name = "stderr";
107 if(file.empty())
109 else
111 widen(file).c_str(),
113 0,
117 NULL);
118 break;
119
120 default:
122 }
123
125 perror(("Failed to open " + name + " file " + file).c_str());
126
127#else
128
129 if(file.empty())
130 return fd;
131
132 int flags = 0, mode = 0;
133 std::string name;
134
135 switch(fd)
136 {
137 case STDIN_FILENO:
138 flags = O_RDONLY;
139 name = "stdin";
140 break;
141
142 case STDOUT_FILENO:
143 case STDERR_FILENO:
144 flags = O_CREAT | O_WRONLY;
145 mode = S_IRUSR | S_IWUSR;
146 name = fd == STDOUT_FILENO ? "stdout" : "stderr";
147 break;
148
149 default:
151 }
152
153 const fdt result_fd = open(file.c_str(), flags, mode);
154
155 if(result_fd == -1)
156 perror(("Failed to open " + name + " file " + file).c_str());
157#endif
158
159 return result_fd;
160}
161
162#ifdef _WIN32
163// Read
164// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
165std::wstring quote_windows_arg(const std::wstring &src)
166{
167 // note that an empty argument requires quotes
168 if(src.find_first_of(L" \t\n\v\"") == src.npos && !src.empty())
169 return src;
170
171 std::wstring result = L"\"";
172
173 for(auto it = src.begin();; ++it)
174 {
175 std::size_t NumberBackslashes = 0;
176
177 while(it != src.end() && *it == L'\\')
178 {
179 ++it;
181 }
182
183 if(it == src.end())
184 {
185 //
186 // Escape all backslashes, but let the terminating
187 // double quotation mark we add below be interpreted
188 // as a metacharacter.
189 //
190
191 result.append(NumberBackslashes * 2, L'\\');
192 break;
193 }
194 else if(*it == L'"')
195 {
196 //
197 // Escape all backslashes and the following
198 // double quotation mark.
199 //
200
201 result.append(NumberBackslashes * 2 + 1, L'\\');
202 result.push_back(*it);
203 }
204 else
205 {
206 //
207 // Backslashes aren't special here.
208 //
209
210 result.append(NumberBackslashes, L'\\');
211 result.push_back(*it);
212 }
213 }
214
215 result.push_back(L'"');
216
217 return result;
218}
219#endif
220
221#ifdef _WIN32
222// https://stackoverflow.com/a/17387176
223// Returns the last Win32 error, in string format. Returns an empty string if
224// there is no error.
225std::string get_last_error_as_string()
226{
227 // Get the error message, if any.
229 if(error_message_id == 0)
230 return {};
231
232 LPWSTR message_buffer = nullptr;
233 std::size_t size = FormatMessageW(
236 NULL,
240 0,
241 NULL);
242
243 std::wstring message(message_buffer, size);
244
245 // Free the buffer.
247
248 return narrow(message);
249}
250#endif
251
252int run(
253 const std::string &what,
254 const std::vector<std::string> &argv,
255 const std::string &std_input,
256 const std::string &std_output,
257 const std::string &std_error)
258{
259#ifdef _WIN32
260 // unicode commandline, quoted
261 std::wstring cmdline;
262
263 // we replace argv[0] by what
264 cmdline = quote_windows_arg(widen(what));
265
266 for(std::size_t i = 1; i < argv.size(); i++)
267 {
268 cmdline += L" ";
269 cmdline += quote_windows_arg(widen(argv[i]));
270 }
271
274
277
278 siStartInfo.cb = sizeof siStartInfo;
279
283
285
286 // CreateProcessW wants to modify the command line
287 std::vector<wchar_t> mutable_cmdline(cmdline.begin(), cmdline.end());
288 mutable_cmdline.push_back(0); // zero termination
289 wchar_t *cmdline_ptr = mutable_cmdline.data();
290
292 NULL, // application name
293 cmdline_ptr, // command line
294 NULL, // process security attributes
295 NULL, // primary thread security attributes
296 true, // handles are inherited
297 0, // creation flags
298 NULL, // use parent's environment
299 NULL, // use parent's current directory
300 &siStartInfo, // STARTUPINFO
301 &piProcInfo); // PROCESS_INFORMATION
302
303 if(!bSuccess)
304 {
305 // obtain the error message before doing further system calls
307
308 if(!std_input.empty())
309 CloseHandle(siStartInfo.hStdInput);
310 if(!std_output.empty())
311 CloseHandle(siStartInfo.hStdOutput);
312 if(!std_error.empty())
313 CloseHandle(siStartInfo.hStdError);
314
315 // now re-open the file and write the above error message
316 std::ofstream stderr_stream(std_error);
318
319 return -1;
320 }
321
322 // wait for child to finish
324
325 if(!std_input.empty())
326 CloseHandle(siStartInfo.hStdInput);
327 if(!std_output.empty())
328 CloseHandle(siStartInfo.hStdOutput);
329 if(!std_error.empty())
330 CloseHandle(siStartInfo.hStdError);
331
333
334 // get exit code
336 {
337 CloseHandle(piProcInfo.hProcess);
338 CloseHandle(piProcInfo.hThread);
339 return -1;
340 }
341
342 CloseHandle(piProcInfo.hProcess);
343 CloseHandle(piProcInfo.hThread);
344
345 return exit_code;
346
347#else
351
352 if(stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1)
353 return 1;
354
355 // temporarily suspend all signals
359
360 /* now create new process */
361 pid_t childpid = fork();
362
363 if(childpid>=0) /* fork succeeded */
364 {
365 if(childpid==0) /* fork() returns 0 to the child process */
366 {
367 // resume signals
369 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
370
371 std::vector<char *> _argv(argv.size()+1);
372 for(std::size_t i=0; i<argv.size(); i++)
373 _argv[i]=strdup(argv[i].c_str());
374
375 _argv[argv.size()]=nullptr;
376
383
384 errno=0;
385 execvp(what.c_str(), _argv.data());
386
387 /* usually no return */
388 perror(std::string("execvp "+what+" failed").c_str());
389 exit(1);
390 }
391 else /* fork() returns new pid to the parent process */
392 {
393 // must do before resuming signals to avoid race
395
396 // resume signals
397 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
398
399 int status; /* parent process: child's exit status */
400
401 /* wait for child to exit, and store its status */
402 while(waitpid(childpid, &status, 0)==-1)
403 {
404 if(errno==EINTR)
405 continue; // try again
406 else
407 {
409
410 perror("Waiting for child process failed");
417 return 1;
418 }
419 }
420
422
429
430 return WEXITSTATUS(status);
431 }
432 }
433 else /* fork returns -1 on failure */
434 {
435 // resume signals
436 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
437
444
445 return 1;
446 }
447#endif
448}
449
451std::string shell_quote(const std::string &src)
452{
453 #ifdef _WIN32
454 // first check if quoting is needed at all
455
456 if(src.find(' ')==std::string::npos &&
457 src.find('"')==std::string::npos &&
458 src.find('&')==std::string::npos &&
459 src.find('|')==std::string::npos &&
460 src.find('(')==std::string::npos &&
461 src.find(')')==std::string::npos &&
462 src.find('<')==std::string::npos &&
463 src.find('>')==std::string::npos &&
464 src.find('^')==std::string::npos)
465 {
466 // seems fine -- return as is
467 return src;
468 }
469
470 std::string result;
471
472 result+='"';
473
474 for(const char ch : src)
475 {
476 if(ch=='"')
477 result+='"'; // quotes are doubled
478 result+=ch;
479 }
480
481 result+='"';
482
483 return result;
484
485 #else
486
487 // first check if quoting is needed at all
488
489 if(src.find(' ')==std::string::npos &&
490 src.find('"')==std::string::npos &&
491 src.find('*')==std::string::npos &&
492 src.find('$')==std::string::npos &&
493 src.find('\\')==std::string::npos &&
494 src.find('?')==std::string::npos &&
495 src.find('&')==std::string::npos &&
496 src.find('|')==std::string::npos &&
497 src.find('>')==std::string::npos &&
498 src.find('<')==std::string::npos &&
499 src.find('^')==std::string::npos &&
500 src.find('\'')==std::string::npos)
501 {
502 // seems fine -- return as is
503 return src;
504 }
505
506 std::string result;
507
508 // the single quotes catch everything but themselves!
509 result+='\'';
510
511 for(const char ch : src)
512 {
513 if(ch=='\'')
514 result+="'\\''";
515 result+=ch;
516 }
517
518 result+='\'';
519
520 return result;
521 #endif
522}
523
524int run(
525 const std::string &what,
526 const std::vector<std::string> &argv,
527 const std::string &std_input,
528 std::ostream &std_output,
529 const std::string &std_error)
530{
531 #ifdef _WIN32
532 temporary_filet tmpi("tmp.stdout", "");
533
534 int result = run(what, argv, std_input, tmpi(), std_error);
535
536 std::ifstream instream(tmpi());
537
538 if(instream)
539 std_output << instream.rdbuf(); // copy
540
541 return result;
542 #else
543 std::string command;
544
545 bool first = true;
546
547 // note we use 'what' instead of 'argv[0]' as the name of the executable
548 for(const auto &arg : argv)
549 {
550 if(first) // this is argv[0]
551 {
552 command += shell_quote(what);
553 first = false;
554 }
555 else
556 command += " " + shell_quote(arg);
557 }
558
559 if(!std_input.empty())
560 command += " < " + shell_quote(std_input);
561
562 if(!std_error.empty())
563 command += " 2> " + shell_quote(std_error);
564
565 FILE *stream=popen(command.c_str(), "r");
566
567 if(stream!=nullptr)
568 {
569 int ch;
570 while((ch=fgetc(stream))!=EOF)
571 std_output << (unsigned char)ch;
572
573 int result = pclose(stream);
574 return WEXITSTATUS(result);
575 }
576 else
577 return -1;
578 #endif
579}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
int open(const char *pathname, int flags,...)
Definition fcntl.c:89
output_type narrow(input_type input)
Run-time checked narrowing cast.
Definition narrow.h:34
int run(const std::string &what, const std::vector< std::string > &argv)
Definition run.cpp:48
static fdt stdio_redirection(int fd, const std::string &file)
open given file to replace either stdin, stderr, stdout
Definition run.cpp:63
std::string shell_quote(const std::string &src)
quote a string for bash and CMD
Definition run.cpp:451
void remove_signal_catcher()
void register_child(pid_t pid)
void unregister_child()
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
int fgetc(FILE *stream)
Definition stdio.c:704
void perror(const char *s)
Definition stdio.c:946
void exit(int status)
Definition stdlib.c:102
char * strdup(const char *str)
Definition string.c:590
std::wstring widen(const char *s)
Definition unicode.cpp:49
int close(int fildes)
Definition unistd.c:139