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