CBMC
Loading...
Searching...
No Matches
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 <sys/stat.h>
31# include <sys/wait.h>
32
33# include <cctype>
34# include <cerrno>
35# include <cstdio>
36# include <cstdlib>
37# include <cstring>
38# include <fcntl.h>
39# include <signal.h>
40# include <unistd.h>
41
42#endif
43
44#include <fstream>
45
46#include "invariant.h"
47#include "signal_catcher.h"
48
49int run(const std::string &what, const std::vector<std::string> &argv)
50{
51 return run(what, argv, "", "", "");
52}
53
54#ifdef _WIN32
55#define STDIN_FILENO 0
56#define STDOUT_FILENO 1
57#define STDERR_FILENO 2
58using fdt = HANDLE;
59#else
60using fdt = int;
61#endif
62
64static fdt stdio_redirection(int fd, const std::string &file)
65{
66#ifdef _WIN32
68 std::string name;
69
72 SecurityAttributes.bInheritHandle = true;
73
74 switch(fd)
75 {
76 case STDIN_FILENO:
77 name = "stdin";
78 if(file.empty())
80 else
82 widen(file).c_str(),
84 0,
88 NULL);
89 break;
90
91 case STDOUT_FILENO:
92 name = "stdout";
93 if(file.empty())
95 else
97 widen(file).c_str(),
99 0,
103 NULL);
104 break;
105
106 case STDERR_FILENO:
107 name = "stderr";
108 if(file.empty())
110 else
112 widen(file).c_str(),
114 0,
118 NULL);
119 break;
120
121 default:
123 }
124
126 perror(("Failed to open " + name + " file " + file).c_str());
127
128#else
129
130 if(file.empty())
131 return fd;
132
133 int flags = 0, mode = 0;
134 std::string name;
135
136 switch(fd)
137 {
138 case STDIN_FILENO:
139 flags = O_RDONLY;
140 name = "stdin";
141 break;
142
143 case STDOUT_FILENO:
144 case STDERR_FILENO:
145 flags = O_CREAT | O_WRONLY;
146 mode = S_IRUSR | S_IWUSR;
147 name = fd == STDOUT_FILENO ? "stdout" : "stderr";
148 break;
149
150 default:
152 }
153
154 const fdt result_fd = open(file.c_str(), flags, mode);
155
156 if(result_fd == -1)
157 perror(("Failed to open " + name + " file " + file).c_str());
158#endif
159
160 return result_fd;
161}
162
163#ifdef _WIN32
164// Read
165// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
166std::wstring quote_windows_arg(const std::wstring &src)
167{
168 // note that an empty argument requires quotes
169 if(src.find_first_of(L" \t\n\v\"") == src.npos && !src.empty())
170 return src;
171
172 std::wstring result = L"\"";
173
174 for(auto it = src.begin();; ++it)
175 {
176 std::size_t NumberBackslashes = 0;
177
178 while(it != src.end() && *it == L'\\')
179 {
180 ++it;
182 }
183
184 if(it == src.end())
185 {
186 //
187 // Escape all backslashes, but let the terminating
188 // double quotation mark we add below be interpreted
189 // as a metacharacter.
190 //
191
192 result.append(NumberBackslashes * 2, L'\\');
193 break;
194 }
195 else if(*it == L'"')
196 {
197 //
198 // Escape all backslashes and the following
199 // double quotation mark.
200 //
201
202 result.append(NumberBackslashes * 2 + 1, L'\\');
203 result.push_back(*it);
204 }
205 else
206 {
207 //
208 // Backslashes aren't special here.
209 //
210
211 result.append(NumberBackslashes, L'\\');
212 result.push_back(*it);
213 }
214 }
215
216 result.push_back(L'"');
217
218 return result;
219}
220#endif
221
222#ifdef _WIN32
223// https://stackoverflow.com/a/17387176
224// Returns the last Win32 error, in string format. Returns an empty string if
225// there is no error.
226std::string get_last_error_as_string()
227{
228 // Get the error message, if any.
230 if(error_message_id == 0)
231 return {};
232
233 LPWSTR message_buffer = nullptr;
234 std::size_t size = FormatMessageW(
237 NULL,
241 0,
242 NULL);
243
244 std::wstring message(message_buffer, size);
245
246 // Free the buffer.
248
249 return narrow(message);
250}
251#endif
252
253int run(
254 const std::string &what,
255 const std::vector<std::string> &argv,
256 const std::string &std_input,
257 const std::string &std_output,
258 const std::string &std_error)
259{
260#ifdef _WIN32
261 // unicode commandline, quoted
262 std::wstring cmdline;
263
264 // we replace argv[0] by what
265 cmdline = quote_windows_arg(widen(what));
266
267 for(std::size_t i = 1; i < argv.size(); i++)
268 {
269 cmdline += L" ";
270 cmdline += quote_windows_arg(widen(argv[i]));
271 }
272
275
278
279 siStartInfo.cb = sizeof siStartInfo;
280
284
286
287 // CreateProcessW wants to modify the command line
288 std::vector<wchar_t> mutable_cmdline(cmdline.begin(), cmdline.end());
289 mutable_cmdline.push_back(0); // zero termination
290 wchar_t *cmdline_ptr = mutable_cmdline.data();
291
293 NULL, // application name
294 cmdline_ptr, // command line
295 NULL, // process security attributes
296 NULL, // primary thread security attributes
297 true, // handles are inherited
298 0, // creation flags
299 NULL, // use parent's environment
300 NULL, // use parent's current directory
301 &siStartInfo, // STARTUPINFO
302 &piProcInfo); // PROCESS_INFORMATION
303
304 if(!bSuccess)
305 {
306 // obtain the error message before doing further system calls
308
309 if(!std_input.empty())
310 CloseHandle(siStartInfo.hStdInput);
311 if(!std_output.empty())
312 CloseHandle(siStartInfo.hStdOutput);
313 if(!std_error.empty())
314 CloseHandle(siStartInfo.hStdError);
315
316 // now re-open the file and write the above error message
317 std::ofstream stderr_stream(std_error);
319
320 return -1;
321 }
322
323 // wait for child to finish
325
326 if(!std_input.empty())
327 CloseHandle(siStartInfo.hStdInput);
328 if(!std_output.empty())
329 CloseHandle(siStartInfo.hStdOutput);
330 if(!std_error.empty())
331 CloseHandle(siStartInfo.hStdError);
332
334
335 // get exit code
337 {
338 CloseHandle(piProcInfo.hProcess);
339 CloseHandle(piProcInfo.hThread);
340 return -1;
341 }
342
343 CloseHandle(piProcInfo.hProcess);
344 CloseHandle(piProcInfo.hThread);
345
346 return exit_code;
347
348#else
352
353 if(stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1)
354 return 1;
355
356 // temporarily suspend all signals
360
361 /* now create new process */
362 pid_t childpid = fork();
363
364 if(childpid>=0) /* fork succeeded */
365 {
366 if(childpid==0) /* fork() returns 0 to the child process */
367 {
368 // resume signals
370 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
371
372 std::vector<char *> _argv(argv.size()+1);
373 for(std::size_t i=0; i<argv.size(); i++)
374 _argv[i]=strdup(argv[i].c_str());
375
376 _argv[argv.size()]=nullptr;
377
384
385 errno=0;
386 execvp(what.c_str(), _argv.data());
387
388 /* usually no return */
389 perror(std::string("execvp "+what+" failed").c_str());
390 exit(1);
391 }
392 else /* fork() returns new pid to the parent process */
393 {
394 // must do before resuming signals to avoid race
396
397 // resume signals
398 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
399
400 int status; /* parent process: child's exit status */
401
402 /* wait for child to exit, and store its status */
403 while(waitpid(childpid, &status, 0)==-1)
404 {
405 if(errno==EINTR)
406 continue; // try again
407 else
408 {
410
411 perror("Waiting for child process failed");
418 return 1;
419 }
420 }
421
423
430
431 return WEXITSTATUS(status);
432 }
433 }
434 else /* fork returns -1 on failure */
435 {
436 // resume signals
437 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
438
445
446 return 1;
447 }
448#endif
449}
450
452std::string shell_quote(const std::string &src)
453{
454 #ifdef _WIN32
455 // first check if quoting is needed at all
456
457 if(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 src.find('^')==std::string::npos)
466 {
467 // seems fine -- return as is
468 return src;
469 }
470
471 std::string result;
472
473 result+='"';
474
475 for(const char ch : src)
476 {
477 if(ch=='"')
478 result+='"'; // quotes are doubled
479 result+=ch;
480 }
481
482 result+='"';
483
484 return result;
485
486 #else
487
488 // first check if quoting is needed at all
489
490 bool quotes_needed = false;
491
492 if(src.empty())
493 quotes_needed = true;
494 else
495 {
496 for(auto &ch : src)
497 if(!isalnum(ch) && ch != '_' && ch != '.' && ch != '/' && ch != '-')
498 quotes_needed = true;
499 }
500
501 if(!quotes_needed)
502 {
503 // seems fine -- return as is
504 return src;
505 }
506
507 std::string result;
508
509 // the single quotes catch everything but themselves!
510 result+='\'';
511
512 for(const char ch : src)
513 {
514 if(ch=='\'')
515 result+="'\\''";
516 result+=ch;
517 }
518
519 result+='\'';
520
521 return result;
522 #endif
523}
524
525int run(
526 const std::string &what,
527 const std::vector<std::string> &argv,
528 const std::string &std_input,
529 std::ostream &std_output,
530 const std::string &std_error)
531{
532 #ifdef _WIN32
533 temporary_filet tmpi("tmp.stdout", "");
534
535 int result = run(what, argv, std_input, tmpi(), std_error);
536
537 std::ifstream instream(tmpi());
538
539 if(instream)
540 std_output << instream.rdbuf(); // copy
541
542 return result;
543 #else
544 int pipefd[2];
545 if(pipe(pipefd) == -1)
546 return -1;
547
549 int stdout_fd = pipefd[1];
551
552 if(stdin_fd == -1 || stdout_fd == -1 || stderr_fd == -1)
553 return 1;
554
555 // temporarily suspend all signals
559
560 /* now create new process */
561 pid_t childpid = fork();
562
563 if(childpid >= 0) /* fork succeeded */
564 {
565 if(childpid == 0) /* fork() returns 0 to the child process */
566 {
567 // resume signals
569 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
570
571 close(pipefd[0]); // unused in child
572
573 std::vector<char *> _argv(argv.size() + 1);
574 for(std::size_t i = 0; i < argv.size(); i++)
575 _argv[i] = strdup(argv[i].c_str());
576
577 _argv[argv.size()] = nullptr;
578
585
586 errno = 0;
587 execvp(what.c_str(), _argv.data());
588
589 /* usually no return */
590 perror(std::string("execvp " + what + " failed").c_str());
591 exit(1);
592 }
593 else /* fork() returns new pid to the parent process */
594 {
595 // must do before resuming signals to avoid race
597
598 // resume signals
599 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
600
601 close(pipefd[1]); // unused in the parent
602
603 const int buffer_size = 1024;
604 std::vector<char> buffer(buffer_size);
606
607 while((bytes_read = read(pipefd[0], buffer.data(), buffer_size)) > 0)
608 std_output.write(buffer.data(), bytes_read);
609
610 int status; /* parent process: child's exit status */
611
612 /* wait for child to exit, and store its status */
613 while(waitpid(childpid, &status, 0) == -1)
614 {
615 if(errno == EINTR)
616 continue; // try again
617 else
618 {
620
621 perror("Waiting for child process failed");
628 return 1;
629 }
630 }
631
633
640
641 return WEXITSTATUS(status);
642 }
643 }
644 else /* fork returns -1 on failure */
645 {
646 // resume signals
647 sigprocmask(SIG_SETMASK, &old_mask, nullptr);
648
655
656 return 1;
657 }
658#endif
659}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
int isalnum(int c)
Definition ctype.c:4
int open(const char *pathname, int flags,...)
Definition fcntl.c:89
int __CPROVER_ID java::java io InputStream read
Definition java.io.c:5
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:49
static fdt stdio_redirection(int fd, const std::string &file)
open given file to replace either stdin, stderr, stdout
Definition run.cpp:64
std::string shell_quote(const std::string &src)
quote a string for bash and CMD
Definition run.cpp:452
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
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
exprt buffer_size(const exprt &what)
std::wstring widen(const char *s)
Definition unicode.cpp:49
int close(int fildes)
Definition unistd.c:139
int pipe(int fildes[2])
Definition unistd.c:90