cprover
pipe_stream.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: A stdin/stdout pipe as STL stream
4 
5 Author:
6 
7 \*******************************************************************/
8 
11 
12 #include "pipe_stream.h"
13 
14 #include <cstdio>
15 #include <istream>
16 #include <vector>
17 
18 #include "unicode.h"
19 
20 #ifdef _WIN32
21 #include <io.h>
22 #include <windows.h>
23 #else
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 #include <cstring>
28 #include <csignal>
29 #endif
30 
31 #define READ_BUFFER_SIZE 1024
32 
35  const std::string &_executable,
36  const std::list<std::string> &_args):
37  std::iostream(&buffer),
38  executable(_executable),
39  args(_args)
40 {
41  #ifdef _WIN32
42  pi.hProcess = 0;
43  #endif
44 }
45 
49 #ifdef _WIN32
50 
52 {
53  HANDLE hOutputReadTmp, hOutputRead, hOutputWrite;
54  HANDLE hInputWriteTmp, hInputRead, hInputWrite;
55  HANDLE hErrorWrite;
56  SECURITY_ATTRIBUTES sa;
57 
58  sa.nLength=sizeof(SECURITY_ATTRIBUTES);
59  sa.lpSecurityDescriptor=NULL;
60  sa.bInheritHandle=TRUE;
61 
62  // Create the child output pipe
63  if(!CreatePipe(&hOutputReadTmp, &hOutputWrite, &sa, 0))
64  return -1;
65 
66  // Create duplicate of output write handle for the std error handle
67  if(!DuplicateHandle(GetCurrentProcess(), hOutputWrite,
68  GetCurrentProcess(), &hErrorWrite, 0,
69  TRUE, DUPLICATE_SAME_ACCESS))
70  return -1;
71 
72  // Create child input pipe
73  if(!CreatePipe(&hInputRead, &hInputWriteTmp, &sa, 0))
74  return -1;
75 
76  // Create new output read handle and the input write handles
77  if(!DuplicateHandle(GetCurrentProcess(), hOutputReadTmp,
78  GetCurrentProcess(),
79  &hOutputRead,
80  0, FALSE, // uninheritable.
81  DUPLICATE_SAME_ACCESS))
82  return -1;
83 
84  if(!DuplicateHandle(GetCurrentProcess(), hInputWriteTmp,
85  GetCurrentProcess(),
86  &hInputWrite,
87  0, FALSE, // uninheritable.
88  DUPLICATE_SAME_ACCESS))
89  return -1;
90 
91  if(!CloseHandle(hOutputReadTmp) || !CloseHandle(hInputWriteTmp))
92  return -1;
93 
94  // now execute the child process
95  STARTUPINFOW si;
96 
97  ZeroMemory(&si, sizeof(STARTUPINFO));
98  si.cb=sizeof(STARTUPINFO);
99  si.dwFlags=STARTF_USESTDHANDLES;
100  si.hStdOutput=hOutputWrite;
101  si.hStdInput=hInputRead;
102  si.hStdError=hErrorWrite;
103 
104  std::wstring command = ::widen(executable);
105 
106  for(const auto &s : args)
107  command += L" " + ::widen(s);
108 
109  LPWSTR lpCommandLine = new wchar_t[command.length()+1];
110 
111  #ifdef _MSC_VER
112  wcscpy_s(lpCommandLine, command.length()+1, command.c_str());
113  #else
114  wcsncpy(lpCommandLine, command.c_str(), command.length()+1);
115  #endif
116 
117  BOOL ret=CreateProcessW(NULL, lpCommandLine, NULL, NULL, TRUE,
118  CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
119 
120  delete lpCommandLine; // clean up
121 
122  if(!ret)
123  return -1;
124 
125  buffer.set_in(hInputWrite);
126  buffer.set_out(hOutputRead);
127 
128  return 0;
129 }
130 
131 #else
132 
134 {
135  filedescriptor_streambuft::HANDLE in[2], out[2];
136 
137  if(pipe(in)==-1 || pipe(out)==-1)
138  return -1;
139 
140  pid=fork();
141 
142  if(pid==0)
143  {
144  // child
145  close(in[1]);
146  close(out[0]);
147  dup2(in[0], STDIN_FILENO);
148  dup2(out[1], STDOUT_FILENO);
149 
150  char **_argv=new char * [args.size()+2];
151 
152  _argv[0]=strdup(executable.c_str());
153 
154  unsigned i=1;
155 
156  for(std::list<std::string>::const_iterator
157  a_it=args.begin();
158  a_it!=args.end();
159  a_it++, i++)
160  _argv[i]=strdup(a_it->c_str());
161 
162  _argv[args.size()+1]=nullptr;
163 
164  int result=execvp(executable.c_str(), _argv);
165 
166  if(result==-1)
167  perror(nullptr);
168 
169  return result;
170  }
171  else if(pid==-1)
172  {
173  // error on parent
174  return -1;
175  }
176 
177  // parent, mild cleanup
178  close(in[0]);
179  close(out[1]);
180 
181  // attach to streambuf
182  buffer.set_in(in[1]);
183  buffer.set_out(out[0]);
184 
185  return pid;
186 }
187 
188 #endif
189 
192 {
193  #ifdef _WIN32
194  DWORD status;
195 
196  if(pi.hProcess==0)
197  return -1;
198 
199  if(WaitForSingleObject(pi.hProcess, INFINITE)==WAIT_FAILED)
200  return -1;
201 
202  GetExitCodeProcess(pi.hProcess, &status);
203  return status;
204  #else
205  if(pid<=0)
206  return -1;
207 
208  int result, status;
209  result=waitpid(pid, &status, WUNTRACED);
210  if(result<=0)
211  return -1;
212 
213  return WEXITSTATUS(status);
214  #endif
215 }
216 
219  #ifdef _WIN32
220  proc_in(INVALID_HANDLE_VALUE),
221  proc_out(INVALID_HANDLE_VALUE)
222  #else
223  proc_in(STDOUT_FILENO),
224  proc_out(STDIN_FILENO)
225  #endif
226 {
227  in_buffer=new char[READ_BUFFER_SIZE];
228  setg(in_buffer, in_buffer, in_buffer);
229 }
230 
233 {
234  #ifdef _WIN32
235 
236  if(proc_in!=INVALID_HANDLE_VALUE)
237  CloseHandle(proc_in);
238 
239  if(proc_out!=INVALID_HANDLE_VALUE)
240  CloseHandle(proc_out);
241 
242  #else
243 
244  if(proc_in!=STDOUT_FILENO)
245  close(proc_in);
246 
247  if(proc_out!=STDIN_FILENO)
248  close(proc_out);
249 
250  #endif
251 
252  delete in_buffer;
253 }
254 
256 std::streambuf::int_type filedescriptor_streambuft::overflow(
257  std::streambuf::int_type character)
258 {
259  if(character!=EOF)
260  {
261  char buf=character;
262 #ifdef _WIN32
263  DWORD len;
264  WriteFile(proc_in, &buf, 1, &len, NULL);
265 #else
266  int len=write(proc_in, &buf, 1);
267 #endif
268  if(len!=1)
269  {
270  return EOF;
271  }
272  }
273  return character;
274 }
275 
278  const char* str, std::streamsize count)
279 {
280 #ifdef _WIN32
281  DWORD len;
282  WriteFile(proc_in, str, (DWORD)count, &len, NULL);
283  return len;
284 #else
285  return write(proc_in, str, count);
286 #endif
287 }
288 
290 std::streambuf::int_type filedescriptor_streambuft::underflow()
291 {
292  if(gptr()==nullptr)
293  return traits_type::eof();
294 
295  if(gptr()<egptr())
296  return traits_type::to_int_type(*gptr());
297 
298  #ifdef _WIN32
299  DWORD len;
300  if(!ReadFile(proc_out, eback(), READ_BUFFER_SIZE, &len, NULL))
301  return traits_type::eof();
302  #else
303  ssize_t len=read(proc_out, eback(), READ_BUFFER_SIZE);
304  if(len==-1)
305  return traits_type::eof();
306  #endif
307 
308  setg(eback(), eback(), eback()+(sizeof(char_type)*len));
309 
310  if(len==0)
311  return traits_type::eof();
312 
313  return traits_type::to_int_type(*gptr());
314 }
315 
318  char *target, std::streamsize count)
319 {
320  std::streamsize available = showmanyc();
321 
322  if(count <= available)
323  {
324  memcpy(target, gptr(), count * sizeof(char_type));
325  gbump(count);
326 
327  return count;
328  }
329 
330  memcpy(target, gptr(), available * sizeof(char_type));
331  gbump(available);
332 
333  if(traits_type::eof() == underflow())
334  return available;
335 
336  return (available + xsgetn(target+available, count-available));
337 }
338 
341 {
342  if(gptr()==nullptr)
343  return 0;
344 
345  if(gptr()<egptr())
346  return egptr()-gptr();
347 
348  return 0;
349 }
350 
351 #ifdef UNIT
352 
353 #include <iostream>
354 
355 int main(int argc, char** argv)
356 {
357  std::string command("cat");
358  std::list<std::string> arguments;
359 
360  pipe_streamt process(command, arguments);
361  if(process.run() < 0)
362  return -1;
363 
364  process << "xxx\n\n";
365 
366  char token;
367  for(int i=0; i<3; ++i)
368  {
369  process >> token;
370  std::cout << "Answer: " << token << '\n';
371  }
372 
373  return process.wait();
374 }
375 
376 #endif
pipe_streamt(const std::string &_executable, const std::list< std::string > &_args)
Constructor for external process.
Definition: pipe_stream.cpp:34
int wait()
Wait for the process to terminate.
int_type overflow(int_type)
write one character to the piped process
std::wstring widen(const char *s)
Definition: unicode.cpp:56
int_type underflow()
read a character from the piped process
std::list< std::string > args
Definition: pipe_stream.h:67
std::streamsize xsputn(const char *, std::streamsize)
write a number of character to the piped process
STL namespace.
#define READ_BUFFER_SIZE
Definition: pipe_stream.cpp:31
#define FALSE
Definition: driver.h:8
std::streamsize xsgetn(char *, std::streamsize)
read a number of characters from the piped process
int main()
filedescriptor_streambuft buffer
Definition: pipe_stream.h:75
filedescriptor_streambuft()
Constructor.
~filedescriptor_streambuft()
Destructor.
std::string executable
Definition: pipe_stream.h:66
void set_in(HANDLE in)
Definition: pipe_stream.h:39
#define TRUE
Definition: driver.h:7
A stdin/stdout pipe as STL stream.
std::streamsize showmanyc()
determine number of available characters in stream
bitvector_typet char_type()
Definition: c_types.cpp:113
int run()
Starts an external process.
void set_out(HANDLE out)
Definition: pipe_stream.h:40