1 
2 #include <R.h>
3 
4 #include "../processx.h"
5 #include "processx-stdio.h"
6 
7 #include <stdio.h>
8 #include <io.h>
9 
10 HANDLE processx__default_iocp = NULL;
11 
processx__create_nul_handle(HANDLE * handle_ptr,DWORD access)12 static int processx__create_nul_handle(HANDLE *handle_ptr, DWORD access) {
13   HANDLE handle;
14   SECURITY_ATTRIBUTES sa;
15 
16   sa.nLength = sizeof(sa);
17   sa.lpSecurityDescriptor = NULL;
18   sa.bInheritHandle = TRUE;
19 
20   handle = CreateFileW(
21     /* lpFilename =            */ L"NUL",
22     /* dwDesiredAccess=        */ access,
23     /* dwShareMode =           */ FILE_SHARE_READ | FILE_SHARE_WRITE,
24     /* lpSecurityAttributes =  */ &sa,
25     /* dwCreationDisposition = */ OPEN_EXISTING,
26     /* dwFlagsAndAttributes =  */ 0,
27     /* hTemplateFile =         */ NULL);
28   if (handle == INVALID_HANDLE_VALUE) { return GetLastError(); }
29 
30   *handle_ptr = handle;
31   return 0;
32 }
33 
processx__create_input_handle(HANDLE * handle_ptr,const char * file,DWORD access)34 static int processx__create_input_handle(HANDLE *handle_ptr, const char *file,
35 					  DWORD access) {
36   HANDLE handle;
37   SECURITY_ATTRIBUTES sa;
38   int  err;
39 
40   sa.nLength = sizeof(sa);
41   sa.lpSecurityDescriptor = NULL;
42   sa.bInheritHandle = TRUE;
43   WCHAR *filew;
44 
45   err = processx__utf8_to_utf16_alloc(file, &filew);
46   if (err) return(err);
47 
48   handle = CreateFileW(
49     /* lpFilename =            */ filew,
50     /* dwDesiredAccess=        */ access,
51     /* dwShareMode =           */ FILE_SHARE_READ | FILE_SHARE_WRITE,
52     /* lpSecurityAttributes =  */ &sa,
53     /* dwCreationDisposition = */ OPEN_EXISTING,
54     /* dwFlagsAndAttributes =  */ 0,
55     /* hTemplateFile =         */ NULL);
56   if (handle == INVALID_HANDLE_VALUE) { return GetLastError(); }
57 
58   *handle_ptr = handle;
59   return 0;
60 }
61 
processx__create_output_handle(HANDLE * handle_ptr,const char * file,DWORD access)62 static int processx__create_output_handle(HANDLE *handle_ptr, const char *file,
63 					  DWORD access) {
64   HANDLE handle;
65   SECURITY_ATTRIBUTES sa;
66   int err;
67 
68   sa.nLength = sizeof(sa);
69   sa.lpSecurityDescriptor = NULL;
70   sa.bInheritHandle = TRUE;
71   WCHAR *filew;
72 
73   err = processx__utf8_to_utf16_alloc(file, &filew);
74   if (err) return(err);
75 
76   handle = CreateFileW(
77     /* lpFilename =            */ filew,
78     /* dwDesiredAccess=        */ access,
79     /* dwShareMode =           */ FILE_SHARE_READ | FILE_SHARE_WRITE,
80     /* lpSecurityAttributes =  */ &sa,
81     /* dwCreationDisposition = */ CREATE_ALWAYS,
82     /* dwFlagsAndAttributes =  */ 0,
83     /* hTemplateFile =         */ NULL);
84   if (handle == INVALID_HANDLE_VALUE) { return GetLastError(); }
85 
86   /* We will append, so set pointer to end of file */
87   SetFilePointer(handle, 0, NULL, FILE_END);
88 
89   *handle_ptr = handle;
90   return 0;
91 }
92 
processx__unique_pipe_name(char * ptr,char * name,size_t size)93 static void processx__unique_pipe_name(char* ptr, char* name, size_t size) {
94   int r;
95   GetRNGstate();
96   r = (int)(unif_rand() * 65000);
97   snprintf(name, size, "\\\\?\\pipe\\px\\%p-%lu", ptr + r, GetCurrentProcessId());
98   PutRNGstate();
99 }
100 
processx__create_pipe(void * id,HANDLE * parent_pipe_ptr,HANDLE * child_pipe_ptr,const char * cname)101 int processx__create_pipe(void *id, HANDLE* parent_pipe_ptr,
102                           HANDLE* child_pipe_ptr, const char *cname) {
103 
104   char pipe_name[40];
105   HANDLE hOutputRead = INVALID_HANDLE_VALUE;
106   HANDLE hOutputWrite = INVALID_HANDLE_VALUE;
107   SECURITY_ATTRIBUTES sa;
108   DWORD err;
109   char *errmessage = "error for process '%s'";
110 
111   sa.nLength = sizeof(sa);
112   sa.lpSecurityDescriptor = NULL;
113   sa.bInheritHandle = TRUE;
114 
115   for (;;) {
116     processx__unique_pipe_name(id, pipe_name, sizeof(pipe_name));
117 
118     hOutputRead = CreateNamedPipeA(
119       pipe_name,
120       PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND |
121         FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE,
122       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
123       1,
124       65536,
125       65536,
126       0,
127       NULL);
128 
129     if (hOutputRead != INVALID_HANDLE_VALUE) {
130       break;
131     }
132 
133     err = GetLastError();
134     if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
135       errmessage = "creating read pipe for '%s'";
136       goto error;
137     }
138   }
139 
140   hOutputWrite = CreateFileA(
141     pipe_name,
142     GENERIC_WRITE,
143     0,
144     &sa,
145     OPEN_EXISTING,
146     FILE_ATTRIBUTE_NORMAL,
147     NULL);
148 
149   if (hOutputWrite == INVALID_HANDLE_VALUE) {
150     err = GetLastError();
151     errmessage = "creating write pipe for '%s'";
152     goto error;
153   }
154 
155   *parent_pipe_ptr = hOutputRead;
156   *child_pipe_ptr  = hOutputWrite;
157 
158   return 0;
159 
160  error:
161   if (hOutputRead != INVALID_HANDLE_VALUE) CloseHandle(hOutputRead);
162   if (hOutputWrite != INVALID_HANDLE_VALUE) CloseHandle(hOutputWrite);
163   R_THROW_SYSTEM_ERROR_CODE(err, errmessage, cname);
164   return 0;			/* never reached */
165 }
166 
processx__create_input_pipe(void * id,HANDLE * parent_pipe_ptr,HANDLE * child_pipe_ptr,const char * cname)167 int processx__create_input_pipe(void *id, HANDLE* parent_pipe_ptr,
168                                 HANDLE* child_pipe_ptr, const char *cname) {
169 
170   char pipe_name[40];
171   HANDLE hOutputRead = INVALID_HANDLE_VALUE;
172   HANDLE hOutputWrite = INVALID_HANDLE_VALUE;
173   SECURITY_ATTRIBUTES sa;
174   DWORD err;
175   char *errmessage = "error for '%s'";
176 
177   sa.nLength = sizeof(sa);
178   sa.lpSecurityDescriptor = NULL;
179   sa.bInheritHandle = TRUE;
180 
181   for (;;) {
182     processx__unique_pipe_name(id, pipe_name, sizeof(pipe_name));
183 
184     hOutputRead = CreateNamedPipeA(
185       pipe_name,
186       PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND |
187         FILE_FLAG_FIRST_PIPE_INSTANCE,
188       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
189       1,
190       65536,
191       65536,
192       0,
193       NULL);
194 
195     if (hOutputRead != INVALID_HANDLE_VALUE) {
196       break;
197     }
198 
199     err = GetLastError();
200     if (err != ERROR_PIPE_BUSY && err != ERROR_ACCESS_DENIED) {
201       errmessage = "creating read pipe for '%s'";
202       goto error;
203     }
204   }
205 
206   hOutputWrite = CreateFileA(
207     pipe_name,
208     GENERIC_READ,
209     0,
210     &sa,
211     OPEN_EXISTING,
212     FILE_ATTRIBUTE_NORMAL,
213     NULL);
214 
215   if (hOutputWrite == INVALID_HANDLE_VALUE) {
216     err = GetLastError();
217     errmessage = "creating write pipe for '%s'";
218     goto error;
219   }
220 
221   *parent_pipe_ptr = hOutputRead;
222   *child_pipe_ptr  = hOutputWrite;
223 
224   return 0;
225 
226  error:
227   if (hOutputRead != INVALID_HANDLE_VALUE) CloseHandle(hOutputRead);
228   if (hOutputWrite != INVALID_HANDLE_VALUE) CloseHandle(hOutputWrite);
229   R_THROW_SYSTEM_ERROR_CODE(err, errmessage, cname);
230   return 0;			/* never reached */
231 }
232 
processx__create_connection(HANDLE pipe_handle,const char * membername,SEXP private,const char * encoding,BOOL async)233 processx_connection_t * processx__create_connection(
234   HANDLE pipe_handle, const char *membername, SEXP private,
235   const char *encoding, BOOL async) {
236 
237   processx_connection_t *con;
238   SEXP res;
239 
240   con = processx_c_connection_create(
241     pipe_handle,
242     async ? PROCESSX_FILE_TYPE_ASYNCPIPE : PROCESSX_FILE_TYPE_PIPE,
243     encoding, &res);
244 
245   defineVar(install(membername), res, private);
246 
247   return con;
248 }
249 
processx__duplicate_handle(HANDLE handle,HANDLE * dup)250 static int processx__duplicate_handle(HANDLE handle, HANDLE* dup) {
251   HANDLE current_process;
252 
253   /* _get_osfhandle will sometimes return -2 in case of an error. This seems */
254   /* to happen when fd <= 2 and the process' corresponding stdio handle is */
255   /* set to NULL. Unfortunately DuplicateHandle will happily duplicate */
256   /* (HANDLE) -2, so this situation goes unnoticed until someone tries to */
257   /* use the duplicate. Therefore we filter out known-invalid handles here. */
258   if (handle == INVALID_HANDLE_VALUE ||
259       handle == NULL ||
260       handle == (HANDLE) -2) {
261     *dup = INVALID_HANDLE_VALUE;
262     return ERROR_INVALID_HANDLE;
263   }
264 
265   current_process = GetCurrentProcess();
266 
267   if (!DuplicateHandle(current_process,
268                        handle,
269                        current_process,
270                        dup,
271                        0,
272                        TRUE,
273                        DUPLICATE_SAME_ACCESS)) {
274     *dup = INVALID_HANDLE_VALUE;
275     return GetLastError();
276   }
277 
278   return 0;
279 }
280 
processx__stdio_create(processx_handle_t * handle,SEXP connections,BYTE ** buffer_ptr,SEXP private,const char * encoding,const char * cname,int * inherit_std)281 int processx__stdio_create(processx_handle_t *handle,
282 			   SEXP connections, BYTE** buffer_ptr, SEXP private,
283 			   const char *encoding, const char *cname, int* inherit_std) {
284   BYTE* buffer;
285   int i;
286   int err;
287   int count = LENGTH(connections);
288 
289   if (count > 255) {
290     R_THROW_ERROR("Too many processx connections to inherit, '%s'", cname);
291   }
292 
293   /* Allocate the child stdio buffer */
294   buffer = malloc(CHILD_STDIO_SIZE(count));
295   if (!buffer) { R_THROW_ERROR("Out of memory for process"); }
296 
297   /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles, so we can
298      clean up on failure*/
299   CHILD_STDIO_COUNT(buffer) = count;
300   for (i = 0; i < count; i++) {
301     CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
302     CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
303   }
304 
305   handle->pipes[0] = handle->pipes[1] = handle->pipes[2] = 0;
306 
307   for (i = 0; i < count; i++) {
308     DWORD access = (i == 0) ?
309       FILE_GENERIC_READ | FILE_WRITE_ATTRIBUTES :
310       FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
311 
312     SEXP output = VECTOR_ELT(connections, i);
313     const char *stroutput =
314       Rf_isString(output) ? CHAR(STRING_ELT(output, 0)) : NULL;
315 
316     /* NULL means ignore */
317     if (isNull(output)) {
318       /* ignored output */
319       err = processx__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i), access);
320       if (err) { goto error; }
321       CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
322 
323     } else if (i == 2 && stroutput && ! strcmp("2>&1", stroutput)) {
324       /* This is stderr, sent to stdout */
325       /* We need to turn off buffering, otherwise the output on
326        the two handles won't be correctly interleaved.
327        We set FDEV on the pipes/files. This tricks windows
328        into turning off the CRT buffering */
329       CHILD_STDIO_COPY(buffer, 2, 1);
330       CHILD_STDIO_CRT_FLAGS(buffer, 1) = FOPEN | FDEV;
331       CHILD_STDIO_CRT_FLAGS(buffer, 2) = FOPEN | FDEV;
332 
333     } else if (stroutput && ! strcmp("|", stroutput)) {
334       /* piped output */
335       processx_connection_t *con = 0;
336       HANDLE parent_handle;
337       const char *r_pipe_name = i == 0 ? "stdin_pipe" :
338         (i == 1 ? "stdout_pipe" : "stderr_pipe");
339       if (i == 0) {
340         err = processx__create_input_pipe(handle, &parent_handle,
341                                           &CHILD_STDIO_HANDLE(buffer, i), cname);
342       } else {
343         err = processx__create_pipe(handle, &parent_handle,
344                                     &CHILD_STDIO_HANDLE(buffer, i), cname);
345       }
346       if (err) goto error;
347       CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
348       con = processx__create_connection(parent_handle, r_pipe_name,
349                                         private, encoding, i != 0);
350       handle->pipes[i] = con;
351 
352     } else if (stroutput && strcmp("", stroutput)) {
353       /* output to file */
354       if (i == 0) {
355         err = processx__create_input_handle(&CHILD_STDIO_HANDLE(buffer, i),
356                                             stroutput, access);
357       } else {
358         err = processx__create_output_handle(&CHILD_STDIO_HANDLE(buffer, i),
359                                              stroutput, access);
360       }
361       if (err) { goto error; }
362       CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
363 
364     } else {
365       /* inherit connection */
366       HANDLE child_handle;
367       HANDLE ihnd;
368 
369       /* std connection or extra connection */
370       if (stroutput) {
371         *inherit_std = 1;
372         DWORD nh = i == 0 ? STD_INPUT_HANDLE :
373           (i == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE );
374         ihnd = GetStdHandle(nh);
375         if (ihnd == INVALID_HANDLE_VALUE ||
376             ihnd == NULL ||
377             ihnd == (HANDLE) -2) {
378           FILE *sh = i == 0 ? stdin : (i == 1 ? stdout : stderr);
379           int fd = _fileno(sh);
380           REprintf("Opening fd %i\n", fd);
381           ihnd = (HANDLE) _get_osfhandle(fd);
382         }
383 
384       } else {
385         processx_connection_t *ccon = R_ExternalPtrAddr(output);
386         if (!ccon) R_THROW_ERROR("Invalid (closed) connection");
387         ihnd = (HANDLE*) processx_c_connection_fileno(ccon);
388       }
389 
390       err = processx__duplicate_handle(ihnd, &child_handle);
391       if (err) goto error;
392 
393       switch (GetFileType(child_handle)) {
394       case FILE_TYPE_DISK:
395         CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN;
396         break;
397       case FILE_TYPE_PIPE:
398         CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
399         break;
400       case FILE_TYPE_CHAR:
401       case FILE_TYPE_REMOTE:
402       case FILE_TYPE_UNKNOWN:
403         CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
404         break;
405       default:
406         err = -1;
407         goto error;
408       }
409 
410       CHILD_STDIO_HANDLE(buffer, i) = child_handle;
411     }
412   }
413 
414   *buffer_ptr = buffer;
415   return 0;
416 
417  error:
418   processx__stdio_destroy(buffer);
419   for (i = 0; i < 3; i++) {
420     if (handle->pipes[i]) {
421       processx_c_connection_destroy(handle->pipes[i]);
422     }
423   }
424   return err;
425 }
426 
processx__stdio_destroy(BYTE * buffer)427 void processx__stdio_destroy(BYTE* buffer) {
428   int i, count;
429 
430   count = CHILD_STDIO_COUNT(buffer);
431   for (i = 0; i < count; i++) {
432     HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
433     if (handle != INVALID_HANDLE_VALUE) {
434       CloseHandle(handle);
435     }
436   }
437 
438   free(buffer);
439 }
440 
processx__stdio_noinherit(BYTE * buffer)441 void processx__stdio_noinherit(BYTE* buffer) {
442   int i, count;
443 
444   count = CHILD_STDIO_COUNT(buffer);
445   for (i = 0; i < count; i++) {
446     HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
447     if (handle != INVALID_HANDLE_VALUE) {
448       SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
449     }
450   }
451 }
452 
processx__stdio_verify(BYTE * buffer,WORD size)453 int processx__stdio_verify(BYTE* buffer, WORD size) {
454   unsigned int count;
455 
456   /* Check the buffer pointer. */
457   if (buffer == NULL)
458     return 0;
459 
460   /* Verify that the buffer is at least big enough to hold the count. */
461   if (size < CHILD_STDIO_SIZE(0))
462     return 0;
463 
464   /* Verify if the count is within range. */
465   count = CHILD_STDIO_COUNT(buffer);
466   if (count > 256)
467     return 0;
468 
469   /* Verify that the buffer size is big enough to hold info for N FDs. */
470   if (size < CHILD_STDIO_SIZE(count))
471     return 0;
472 
473   return 1;
474 }
475 
processx__stdio_size(BYTE * buffer)476 WORD processx__stdio_size(BYTE* buffer) {
477   return (WORD) CHILD_STDIO_SIZE(CHILD_STDIO_COUNT((buffer)));
478 }
479 
processx__stdio_handle(BYTE * buffer,int fd)480 HANDLE processx__stdio_handle(BYTE* buffer, int fd) {
481   return CHILD_STDIO_HANDLE(buffer, fd);
482 }
483