1 /*
2 * R : A Computer Language for Statistical Data Analysis
3 * file run.c: a simple 'reading' pipe (and a command executor)
4 * Copyright (C) 1999-2001 Guido Masarotto and Brian Ripley
5 * (C) 2007-2020 The R Core Team
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, a copy is available at
19 * https://www.R-project.org/Licenses/
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #define R_USE_SIGNALS 1
27 #include <Defn.h>
28 #include <Internal.h>
29 #include "win-nls.h"
30
31 #define WIN32_LEAN_AND_MEAN 1
32 #include <windows.h>
33 #include <mmsystem.h> /* for timeGetTime */
34 #include <string.h>
35 #include <stdlib.h>
36 #include <ctype.h>
37 #include "run.h"
38
39 #include <Startup.h> /* for CharacterMode and RGui */
40
41 #include <trioremap.h>
42
43 static char RunError[501] = "";
44
45 /* This might be given a command line (whole = 0) or just the
46 executable (whole = 1). In the later case the path may or may not
47 be quoted */
expandcmd(const char * cmd,int whole)48 static char *expandcmd(const char *cmd, int whole)
49 {
50 char c = '\0';
51 char *s, *p, *q = NULL, *f, *dest, *src;
52 int d, ext, len = strlen(cmd)+1;
53 char buf[len], fl[len], fn[MAX_PATH];
54
55 /* make a copy as we manipulate in place */
56 strcpy(buf, cmd);
57
58 /* skip leading spaces */
59 for (p = buf; *p && isspace(*p); p++);
60 /* find the command itself, possibly double-quoted */
61 if (whole) {
62 d = 0;
63 } else { // command line
64 for (q = p, d = 0; *q && ( d || !isspace(*q) ); q++)
65 if (*q == '\"') d = d ? 0 : 1;
66 if (d) {
67 strcpy(RunError, "A \" is missing (expandcmd)");
68 return NULL;
69 }
70 c = *q; /* character after the command, normally a space */
71 *q = '\0';
72 }
73
74 // This is the return value.
75 if (!(s = (char *) malloc(MAX_PATH + strlen(cmd)))) {
76 strcpy(RunError, "Insufficient memory (expandcmd)");
77 return NULL;
78 }
79
80 /*
81 * Guido resorted to this since SearchPath returned FOUND also
82 * for file name without extension -> explicitly set
83 * extension
84 */
85 for (f = p, ext = 0 ; *f ; f++) {
86 if ((*f == '\\') || (*f == '/')) ext = 0;
87 else if (*f == '.') ext = 1;
88 }
89 /* SearchPath doesn't like ", so strip out quotes */
90 for (dest = fl , src = p; *src ; src++)
91 if (*src != '"') *dest++ = *src;
92 *dest = '\0';
93 if (ext) {
94 /*
95 * user set extension; we don't check that it is executable;
96 * it might get an error after; but maybe sometimes
97 * in the future every extension will be executable
98 */
99 d = SearchPath(NULL, fl, NULL, MAX_PATH, fn, &f);
100 } else {
101 int iexts = 0;
102 const char *exts[] = { ".exe" , ".com" , ".cmd" , ".bat" , NULL };
103 while (exts[iexts]) {
104 strcpy(dest, exts[iexts]);
105 if ((d = SearchPath(NULL, fl, NULL, MAX_PATH, fn, &f))) break;
106 iexts++ ;
107 }
108 }
109 if (!d) {
110 free(s);
111 snprintf(RunError, 500, "'%s' not found", p);
112 if(!whole) *q = c;
113 return NULL;
114 }
115 /*
116 NB: as of Windows 7 SearchPath does not return short names any more.
117
118 Paranoia : on my system switching to short names is not needed
119 since SearchPath already returns 'short names'. However,
120 this is not documented so I prefer to be explicit.
121 */
122 /* NOTE: short names are not always enabled */
123 GetShortPathName(fn, s, MAX_PATH);
124 if (!whole) {
125 *q = c;
126 strcat(s, q);
127 }
128 return s;
129 }
130
131 /*
132 finput is either NULL or the name of a file from which to
133 redirect stdin for the child.
134 newconsole != 0 to use a new console (if not waiting)
135 visible = -1, 0, 1 for hide, minimized, default
136 inpipe != 0 to duplicate I/O handles
137 pi is set based on the newly created process,
138 with the hThread handle closed.
139 */
140
141 extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
142
pcreate(const char * cmd,cetype_t enc,int newconsole,int visible,HANDLE hIN,HANDLE hOUT,HANDLE hERR,pinfo * pi)143 static void pcreate(const char* cmd, cetype_t enc,
144 int newconsole, int visible,
145 HANDLE hIN, HANDLE hOUT, HANDLE hERR,
146 pinfo *pi)
147 {
148 DWORD ret;
149 STARTUPINFO si;
150 STARTUPINFOW wsi;
151 HANDLE dupIN, dupOUT, dupERR, job, port = NULL;
152 WORD showWindow = SW_SHOWDEFAULT;
153 DWORD flags;
154 BOOL inJob;
155 Rboolean breakaway;
156 JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
157 JOBOBJECT_ASSOCIATE_COMPLETION_PORT cport;
158 int inpipe;
159 char *ecmd;
160 SECURITY_ATTRIBUTES sa;
161 sa.nLength = sizeof(sa);
162 sa.lpSecurityDescriptor = NULL;
163 sa.bInheritHandle = TRUE;
164
165 /* FIXME: this might need to be done in wchar_t */
166 if (!(ecmd = expandcmd(cmd, 0))) return; /* error message already set */
167
168 inpipe = (hIN != INVALID_HANDLE_VALUE)
169 || (hOUT != INVALID_HANDLE_VALUE)
170 || (hERR != INVALID_HANDLE_VALUE);
171
172 if (inpipe) {
173 HANDLE hNULL = CreateFile("NUL:", GENERIC_READ | GENERIC_WRITE, 0,
174 &sa, OPEN_EXISTING, 0, NULL);
175 HANDLE hTHIS = GetCurrentProcess();
176
177 if (hIN == INVALID_HANDLE_VALUE) hIN = hNULL;
178 if (hOUT == INVALID_HANDLE_VALUE) hOUT = hNULL;
179 if (hERR == INVALID_HANDLE_VALUE) hERR = hNULL;
180
181 DuplicateHandle(hTHIS, hIN,
182 hTHIS, &dupIN, 0, TRUE, DUPLICATE_SAME_ACCESS);
183 DuplicateHandle(hTHIS, hOUT,
184 hTHIS, &dupOUT, 0, TRUE, DUPLICATE_SAME_ACCESS);
185 DuplicateHandle(hTHIS, hERR,
186 hTHIS, &dupERR, 0, TRUE, DUPLICATE_SAME_ACCESS);
187 CloseHandle(hTHIS);
188 CloseHandle(hNULL);
189 }
190
191 switch (visible) {
192 case -1:
193 showWindow = SW_HIDE;
194 break;
195 case 0:
196 showWindow = SW_SHOWMINIMIZED;
197 break;
198 }
199
200 if(enc == CE_UTF8) {
201 wsi.cb = sizeof(wsi);
202 wsi.lpReserved = NULL;
203 wsi.lpReserved2 = NULL;
204 wsi.cbReserved2 = 0;
205 wsi.lpDesktop = NULL;
206 wsi.lpTitle = NULL;
207 wsi.dwFlags = STARTF_USESHOWWINDOW;
208 wsi.wShowWindow = showWindow;
209 if (inpipe) {
210 wsi.dwFlags |= STARTF_USESTDHANDLES;
211 wsi.hStdInput = dupIN;
212 wsi.hStdOutput = dupOUT;
213 wsi.hStdError = dupERR;
214 }
215 } else {
216 si.cb = sizeof(si);
217 si.lpReserved = NULL;
218 si.lpReserved2 = NULL;
219 si.cbReserved2 = 0;
220 si.lpDesktop = NULL;
221 si.lpTitle = NULL;
222 si.dwFlags = STARTF_USESHOWWINDOW;
223 si.wShowWindow = showWindow;
224 if (inpipe) {
225 si.dwFlags |= STARTF_USESTDHANDLES;
226 si.hStdInput = dupIN;
227 si.hStdOutput = dupOUT;
228 si.hStdError = dupERR;
229 }
230 }
231
232 /* Originally, the external process has been waited for only using
233 waitForSingleObject, but that has been proven unreliable: sometimes
234 the output file would still be opened (and hence locked) by some
235 child process after waitForSingleObject would finish. This has been
236 observed also while running tests and particularly when building
237 vignettes, resulting in spurious "Permission denied" errors.
238
239 This has been happening almost surely due to a child process not
240 waiting for its own children to finish, which has been reported
241 to happen with Linux utilities ported to Windows as used for tests
242 in Haskell/GHC. Inspired by Haskell process and a blog post about
243 waiting for a process tree to finish, we now use job objects to
244 wait also for process trees with this issue:
245
246 https://github.com/haskell/process
247 https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743
248
249 In addition, we try to be easy on applications coded to rely on that
250 they do not run in a job, when running in old Windows that do not
251 support nested jobs. With nested jobs support, it might make sense
252 to not breakaway to better support nested R processes.
253 */
254
255 /* Creating the process with CREATE_BREAKAWAY_FROM_JOB is safe when
256 the process is not in any job or when it is in a job that allows it.
257 The documentation does not say what would happen if we set the flag,
258 but run in a job that does not allow it, so better don't. */
259 breakaway = FALSE;
260 if (IsProcessInJob(GetCurrentProcess(), NULL, &inJob) && inJob) {
261 /* The documentation does not say that it would be ok to use
262 QueryInformationJobObject when the process is not in the job,
263 so we have better tested that upfront. */
264 ZeroMemory(&jeli, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
265 ret = QueryInformationJobObject(
266 NULL,
267 JobObjectExtendedLimitInformation,
268 &jeli,
269 sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
270 NULL);
271 breakaway = ret &&
272 (jeli.BasicLimitInformation.LimitFlags &
273 JOB_OBJECT_LIMIT_BREAKAWAY_OK);
274 }
275
276 /* create a job that allows breakaway */
277 job = CreateJobObject(NULL, NULL);
278 if (job) {
279 ZeroMemory(&jeli, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
280 jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
281 ret = SetInformationJobObject(
282 job,
283 JobObjectExtendedLimitInformation,
284 &jeli,
285 sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
286 if (!ret) {
287 CloseHandle(job);
288 job = NULL;
289 }
290 }
291
292 /* create a completion port to learn when processes exit */
293 if (job) {
294 port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
295 if (!port) {
296 CloseHandle(job);
297 job = NULL;
298 }
299 }
300 if (job) {
301 ZeroMemory(&cport, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
302 cport.CompletionKey = job; /* use job handle as key */
303 cport.CompletionPort = port;
304 ret = SetInformationJobObject(
305 job,
306 JobObjectAssociateCompletionPortInformation,
307 &cport,
308 sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
309 if (!ret) {
310 CloseHandle(job);
311 CloseHandle(port);
312 job = NULL;
313 }
314 }
315
316 flags = 0;
317 if (job)
318 flags |= CREATE_SUSPENDED; /* assign to job before it runs */
319 if (newconsole && (visible == 1))
320 flags |= CREATE_NEW_CONSOLE;
321 if (job && breakaway)
322 flags |= CREATE_BREAKAWAY_FROM_JOB;
323
324 if(enc == CE_UTF8) {
325 int n = strlen(ecmd); /* max no of chars */
326 wchar_t wcmd[n+1];
327 Rf_utf8towcs(wcmd, ecmd, n+1);
328 ret = CreateProcessW(NULL, wcmd, &sa, &sa, TRUE, flags,
329 NULL, NULL, &wsi, &(pi->pi));
330 } else
331 ret = CreateProcess(NULL, ecmd, &sa, &sa, TRUE, flags,
332 NULL, NULL, &si, &(pi->pi));
333
334 if (ret && job) {
335 /* process was created as suspended */
336 if (!AssignProcessToJobObject(job, pi->pi.hProcess)) {
337 /* will fail running on Windows without support for nested jobs,
338 when running in a job that does not allow breakaway */
339 CloseHandle(job);
340 CloseHandle(port);
341 job = NULL;
342 }
343 ResumeThread(pi->pi.hThread);
344 }
345
346 if (ret && job) {
347 /* process is running in new job */
348 pi->job = job;
349 pi->port = port;
350 } else {
351 if (job) {
352 CloseHandle(job);
353 CloseHandle(port);
354 job = NULL;
355 }
356 pi->job = NULL;
357 pi->port = NULL;
358 }
359
360 if (inpipe) {
361 CloseHandle(dupIN);
362 CloseHandle(dupOUT);
363 CloseHandle(dupERR);
364 }
365 if (!ret)
366 snprintf(RunError, 500, _("'CreateProcess' failed to run '%s'"), ecmd);
367 else CloseHandle(pi->pi.hThread);
368 free(ecmd);
369 return;
370 }
371
372 /* used in rpipeOpen */
373 static DWORD CALLBACK
threadedwait(LPVOID param)374 threadedwait(LPVOID param)
375 {
376 rpipe *p = (rpipe *) param;
377
378 if (p->timeoutMillis) {
379 DWORD wres = WaitForSingleObject(p->pi.pi.hProcess, p->timeoutMillis);
380 if (wres == WAIT_TIMEOUT) {
381 TerminateProcess(p->pi.pi.hProcess, 124);
382 p->timedout = 1;
383 /* wait up to 10s for the process to actually terminate */
384 WaitForSingleObject(p->pi.pi.hProcess, 10000);
385 }
386 } else
387 WaitForSingleObject(p->pi.pi.hProcess, INFINITE);
388
389 DWORD ret;
390 GetExitCodeProcess(p->pi.pi.hProcess, &ret);
391 p->exitcode = ret;
392
393 FlushFileBuffers(p->write);
394 FlushFileBuffers(p->read);
395 p->active = 0;
396 CloseHandle(p->thread);
397 p->thread = NULL;
398 return 0;
399 }
400
runerror(void)401 char *runerror(void)
402 {
403 return RunError;
404 }
405
getInputHandle(const char * fin)406 static HANDLE getInputHandle(const char *fin)
407 {
408 if (fin && fin[0]) {
409 SECURITY_ATTRIBUTES sa;
410 sa.nLength = sizeof(sa);
411 sa.lpSecurityDescriptor = NULL;
412 sa.bInheritHandle = TRUE;
413 HANDLE hIN = CreateFile(fin, GENERIC_READ, 0,
414 &sa, OPEN_EXISTING, 0, NULL);
415 if (hIN == INVALID_HANDLE_VALUE) {
416 snprintf(RunError, 500,
417 "unable to redirect input from '%s'", fin);
418 return NULL;
419 }
420 return hIN;
421 } else if (fin) {
422 /* GetStdHandle returns NULL for processes like RGui with no standard handles defined */
423 HANDLE result = GetStdHandle(STD_INPUT_HANDLE);
424 if (result) return result;
425 }
426 return INVALID_HANDLE_VALUE;
427 }
428
getOutputHandle(const char * fout,int type)429 static HANDLE getOutputHandle(const char *fout, int type)
430 {
431 if (fout && fout[0]) {
432 SECURITY_ATTRIBUTES sa;
433 sa.nLength = sizeof(sa);
434 sa.lpSecurityDescriptor = NULL;
435 sa.bInheritHandle = TRUE;
436 HANDLE hOUT = CreateFile(fout, GENERIC_WRITE, 0,
437 &sa, CREATE_ALWAYS, 0, NULL);
438 if (hOUT == INVALID_HANDLE_VALUE) {
439 snprintf(RunError, 500,
440 "unable to redirect output to '%s'", fout);
441 return NULL;
442 } else return hOUT;
443 } else if (fout) {
444 /* GetStdHandle returns NULL for processes like RGui */
445 HANDLE result = GetStdHandle(type ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE);
446 if (result) return result;
447 }
448 return INVALID_HANDLE_VALUE;
449 }
450
TerminateWindow(HWND hwnd,LPARAM lParam)451 BOOL CALLBACK TerminateWindow(HWND hwnd, LPARAM lParam)
452 {
453 DWORD ID ;
454
455 GetWindowThreadProcessId(hwnd, &ID);
456
457 if (ID == (DWORD)lParam)
458 PostMessage(hwnd, WM_CLOSE, 0, 0);
459 return TRUE;
460 }
461
462 /* Terminate the process pwait2 is waiting for. */
463
464 extern void GA_askok(const char *info);
465
waitForJob(pinfo * pi,DWORD timeoutMillis,int * timedout)466 static void waitForJob(pinfo *pi, DWORD timeoutMillis, int* timedout)
467 {
468 DWORD code, ret;
469 ULONG_PTR key;
470 DWORD beforeMillis;
471 JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jbai;
472 LPOVERLAPPED overlapped; /* not used */
473 DWORD queryMillis;
474
475 if (timeoutMillis)
476 beforeMillis = timeGetTime();
477
478 queryMillis = 0;
479 for(;;) {
480 ret = GetQueuedCompletionStatus(pi->port, &code, &key,
481 &overlapped, queryMillis);
482 if (ret && code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO &&
483 (HANDLE)key == pi->job)
484 break;
485
486 /* start with short query timeouts because notifications often get lost,
487 this is essentially polling */
488
489 if (queryMillis == 0)
490 queryMillis = 1;
491 else if (queryMillis < 100)
492 queryMillis *= 2;
493
494 if (timeoutMillis && (timeGetTime() - beforeMillis >= timeoutMillis)) {
495 if (timedout)
496 *timedout = 1;
497 break;
498 }
499
500 /* Check also explicitly because notifications are documented to get
501 lost and they often do. */
502 ZeroMemory(&jbai, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION));
503 ret = QueryInformationJobObject(
504 pi->job,
505 JobObjectBasicAccountingInformation,
506 &jbai,
507 sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
508 NULL);
509 if (ret && jbai.ActiveProcesses == 0)
510 break;
511 }
512 CloseHandle(pi->port);
513 CloseHandle(pi->job);
514 }
515
terminate_process(void * p)516 static void terminate_process(void *p)
517 {
518 pinfo *pi = (pinfo*) p;
519 EnumWindows((WNDENUMPROC)TerminateWindow, (LPARAM)pi->pi.dwProcessId);
520
521 if (WaitForSingleObject(pi->pi.hProcess, 5000) == WAIT_TIMEOUT) {
522 if (R_Interactive)
523 GA_askok(_("Child process not responding. R will terminate it."));
524 TerminateProcess(pi->pi.hProcess, 99);
525 }
526
527 if (pi->job)
528 waitForJob(pi, 2000, NULL);
529 }
530
pwait2(pinfo * pi,DWORD timeoutMillis,int * timedout)531 static int pwait2(pinfo *pi, DWORD timeoutMillis, int* timedout)
532 {
533 DWORD ret;
534
535 if (!timeoutMillis) {
536 while( WaitForSingleObject(pi->pi.hProcess, 100) == WAIT_TIMEOUT )
537 R_CheckUserInterrupt();
538 } else {
539 DWORD beforeMillis = timeGetTime();
540 while( WaitForSingleObject(pi->pi.hProcess, 100) == WAIT_TIMEOUT ) {
541 R_CheckUserInterrupt();
542 DWORD afterMillis = timeGetTime();
543 if (afterMillis - beforeMillis >= timeoutMillis) {
544 TerminateProcess(pi->pi.hProcess, 124);
545 if (timedout)
546 *timedout = 1;
547 /* wait up to 10s for the process to actually terminate */
548 WaitForSingleObject(pi->pi.hProcess, 10000);
549 break;
550 }
551 }
552 }
553
554 GetExitCodeProcess(pi->pi.hProcess, &ret);
555
556 if (pi->job)
557 waitForJob(pi, timeoutMillis, timedout);
558
559 return ret;
560 }
561
562 /*
563 Used for external commands in file.show() and edit(), and for
564 system(intern=FALSE). Also called from postscript().
565
566 wait != 0 says wait for child to terminate before returning.
567 visible = -1, 0, 1 for hide, minimized, default
568 fin is either NULL or the name of a file from which to
569 redirect stdin for the child.
570 fout/ferr are NULL (use NUL:), "" (use standard streams) or filenames.
571 */
runcmd(const char * cmd,cetype_t enc,int wait,int visible,const char * fin,const char * fout,const char * ferr)572 int runcmd(const char *cmd, cetype_t enc, int wait, int visible,
573 const char *fin, const char *fout, const char *ferr)
574 {
575 return runcmd_timeout(cmd, enc, wait, visible, fin, fout, ferr, 0, NULL);
576 }
577
runcmd_timeout(const char * cmd,cetype_t enc,int wait,int visible,const char * fin,const char * fout,const char * ferr,int timeout,int * timedout)578 int runcmd_timeout(const char *cmd, cetype_t enc, int wait, int visible,
579 const char *fin, const char *fout, const char *ferr,
580 int timeout, int *timedout)
581 {
582 if (!wait && timeout)
583 error("Timeout with background running processes is not supported.");
584
585 HANDLE hIN = getInputHandle(fin), hOUT, hERR;
586 int ret = 0;
587 pinfo pi;
588 int close1 = 0, close2 = 0, close3 = 0;
589
590 if (hIN && fin && fin[0]) close1 = 1;
591
592 hOUT = getOutputHandle(fout, 0);
593 if (!hOUT) return 1;
594 if (fout && fout[0]) close2 = 1;
595 if (fout && fout[0] && ferr && streql(fout, ferr)) hERR = hOUT;
596 else {
597 hERR = getOutputHandle(ferr, 1);
598 if (!hERR) return 1;
599 if (ferr && ferr[0]) close3 = 1;
600 }
601
602
603 memset(&(pi.pi), 0, sizeof(PROCESS_INFORMATION));
604 pcreate(cmd, enc, !wait, visible, hIN, hOUT, hERR, &pi);
605 if (pi.pi.hProcess) {
606 if (wait) {
607 RCNTXT cntxt;
608 begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
609 R_NilValue, R_NilValue);
610 cntxt.cend = &terminate_process;
611 cntxt.cenddata = π
612 DWORD timeoutMillis = (DWORD) (1000*timeout);
613 ret = pwait2(&pi, timeoutMillis, timedout);
614 endcontext(&cntxt);
615 snprintf(RunError, 501, _("Exit code was %d"), ret);
616 ret &= 0xffff;
617 } else ret = 0;
618 CloseHandle(pi.pi.hProcess);
619 } else {
620 ret = NOLAUNCH;
621 }
622 if (close1) CloseHandle(hIN);
623 if (close2) CloseHandle(hOUT);
624 if (close3) CloseHandle(hERR);
625 return ret;
626 }
627
628 /*
629 finput is either NULL or the name of a file from which to
630 redirect stdin for the child.
631 visible = -1, 0, 1 for hide, minimized, default
632 io = 0 to read stdout from pipe, 1 to write to pipe,
633 2 to read stderr from pipe,
634 3 to read both stdout and stderr from pipe.
635 */
rpipeOpen(const char * cmd,cetype_t enc,int visible,const char * finput,int io,const char * fout,const char * ferr,int timeout)636 rpipe * rpipeOpen(const char *cmd, cetype_t enc, int visible,
637 const char *finput, int io,
638 const char *fout, const char *ferr,
639 int timeout)
640 {
641 rpipe *r;
642 HANDLE hTHIS, hIN, hOUT, hERR, hReadPipe, hWritePipe;
643 DWORD id;
644 BOOL res;
645 int close1 = 0, close2 = 0, close3 = 0;
646
647 if (!(r = (rpipe *) malloc(sizeof(struct structRPIPE)))) {
648 strcpy(RunError, _("Insufficient memory (rpipeOpen)"));
649 return NULL;
650 }
651 r->active = 0;
652 r->pi.pi.hProcess = NULL;
653 r->pi.job = NULL;
654 r->thread = NULL;
655 r->timedout = 0;
656 r->timeoutMillis = (DWORD) (1000*timeout);
657 res = CreatePipe(&hReadPipe, &hWritePipe, NULL, 0);
658 if (res == FALSE) {
659 rpipeClose(r, NULL);
660 strcpy(RunError, "CreatePipe failed");
661 return NULL;
662 }
663 if(io == 1) { /* pipe for R to write to */
664 hTHIS = GetCurrentProcess();
665 r->read = hReadPipe;
666 DuplicateHandle(hTHIS, hWritePipe, hTHIS, &r->write,
667 0, FALSE, DUPLICATE_SAME_ACCESS);
668 CloseHandle(hWritePipe);
669 CloseHandle(hTHIS);
670 /* This sends stdout and stderr to NUL: */
671 pcreate(cmd, enc, 1, visible,
672 r->read, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
673 &(r->pi));
674 r->active = 1;
675 if (!r->pi.pi.hProcess) return NULL; else return r;
676 }
677
678 /* pipe for R to read from */
679 hTHIS = GetCurrentProcess();
680 r->write = hWritePipe;
681 DuplicateHandle(hTHIS, hReadPipe, hTHIS, &r->read,
682 0, FALSE, DUPLICATE_SAME_ACCESS);
683 CloseHandle(hReadPipe);
684 CloseHandle(hTHIS);
685
686 hIN = getInputHandle(finput); /* a file or (usually NUL:) */
687
688 if (hIN && finput && finput[0]) close1 = 1;
689
690 if ((io == 0 || io == 3))
691 hOUT = r->write;
692 else {
693 if (fout && fout[0]) close2 = 1;
694 hOUT = getOutputHandle(fout, 0);
695 }
696 if (io >= 2)
697 hERR = r->write;
698 else {
699 if (ferr && ferr[0]) close3 = 1;
700 hERR = getOutputHandle(ferr, 1);
701 }
702 pcreate(cmd, enc, 0, visible, hIN, hOUT, hERR, &(r->pi));
703 if (close1) CloseHandle(hIN);
704 if (close2) CloseHandle(hOUT);
705 if (close3) CloseHandle(hERR);
706
707 r->active = 1;
708 if (!r->pi.pi.hProcess)
709 return NULL;
710 if (!(r->thread = CreateThread(NULL, 0, threadedwait, r, 0, &id))) {
711 rpipeClose(r, NULL);
712 strcpy(RunError, "CreateThread failed");
713 return NULL;
714 }
715 return r;
716 }
717
718 static void
rpipeTerminate(rpipe * r)719 rpipeTerminate(rpipe * r)
720 {
721 if (r->thread) {
722 TerminateThread(r->thread, 0);
723 CloseHandle(r->thread);
724 r->thread = NULL;
725 }
726 if (r->active) {
727 terminate_process(&(r->pi));
728 r->active = 0;
729 }
730 }
731
732 #include "graphapp/ga.h"
733 extern Rboolean UserBreak;
734
735 int
rpipeGetc(rpipe * r)736 rpipeGetc(rpipe * r)
737 {
738 DWORD a, b;
739 char c;
740
741 if (!r)
742 return NOLAUNCH;
743 while (PeekNamedPipe(r->read, NULL, 0, NULL, &a, NULL)) {
744 if (!a && !r->active) {
745 /* I got a case in which process terminated after Peek.. */
746 PeekNamedPipe(r->read, NULL, 0, NULL, &a, NULL);
747 if (!a) return NOLAUNCH;/* end of pipe */
748 }
749 if (a) {
750 if (ReadFile(r->read, &c, 1, &b, NULL) == TRUE)
751 return c;
752 else
753 return NOLAUNCH;/* error but...treated as eof */
754 }
755 /* we want to look for user break here */
756 while (peekevent()) doevent();
757 if (UserBreak) {
758 /* FIXME: close handles */
759 rpipeTerminate(r);
760 break;
761 }
762 R_ProcessEvents();
763 Sleep(100);
764 }
765 return NOLAUNCH; /* again.. */
766 }
767
768
rpipeGets(rpipe * r,char * buf,int len)769 char * rpipeGets(rpipe * r, char *buf, int len)
770 {
771 int i, c;
772
773 if ((len < 2) || !r) return NULL;
774 for (i = 0; i < (len - 1); i++) {
775 if ((c = rpipeGetc(r)) == NOLAUNCH) {
776 if (i == 0) return NULL;
777 else {
778 buf[i] = '\0';
779 return buf;
780 }
781 }
782 buf[i] = c;
783 if (c == '\n') {
784 if ((i > 0) && (buf[i - 1] == '\r')) {
785 buf[i - 1] = '\n';
786 buf[i] = '\0';
787 } else
788 buf[i + 1] = '\0';
789 return buf;
790 }
791 }
792 buf[len - 1] = '\0';
793 return buf;
794 }
795
rpipeClose(rpipe * r,int * timedout)796 int rpipeClose(rpipe *r, int *timedout)
797 {
798 int i;
799
800 if (!r) return NOLAUNCH;
801 /* Close both pipe ends before forcibly terminating the child process to
802 let it read all data (if it is reading) and exit gracefully.
803
804 r->write and r->read are set to hNULL for the case that threadedwait
805 ends up flushing file buffers
806
807 FIXME: should we be forcing the termination at all? */
808 HANDLE hNULL = CreateFile("NUL:", GENERIC_READ | GENERIC_WRITE, 0,
809 NULL, OPEN_EXISTING, 0, NULL);
810 HANDLE tmp;
811 tmp = r->read;
812 r->read = hNULL;
813 CloseHandle(tmp);
814 tmp = r->write;
815 r->write = hNULL;
816 CloseHandle(tmp);
817
818 rpipeTerminate(r);
819 /* threadedwait may have obtained the exit code of the pipe process,
820 but also may have been terminated too early; retrieve the exit
821 code again to avoid race condition */
822 DWORD ret;
823 GetExitCodeProcess(r->pi.pi.hProcess, &ret);
824 r->exitcode = ret;
825 CloseHandle(r->pi.pi.hProcess);
826 CloseHandle(hNULL);
827 i = r->exitcode;
828 if (timedout)
829 *timedout = r->timedout;
830 free(r);
831 return i &= 0xffff;
832 }
833
834 /* ------------------- Windows pipe connections --------------------- */
835
836 #include <Fileio.h>
837 #include <Rconnections.h>
838
839 typedef struct Wpipeconn {
840 rpipe *rp;
841 } *RWpipeconn;
842
843
Wpipe_open(Rconnection con)844 static Rboolean Wpipe_open(Rconnection con)
845 {
846 rpipe *rp;
847 int visible = -1, io, mlen;
848
849 io = con->mode[0] == 'w';
850 if(io) visible = 1; /* Somewhere to put the output */
851 rp = rpipeOpen(con->description, con->enc, visible, NULL, io, NULL, NULL, 0);
852 if(!rp) {
853 warning("cannot open cmd `%s'", con->description);
854 return FALSE;
855 }
856 ((RWpipeconn)(con->private))->rp = rp;
857 con->isopen = TRUE;
858 con->canwrite = io;
859 con->canread = !con->canwrite;
860 mlen = (int) strlen(con->mode);
861 if(mlen >= 2 && con->mode[mlen-1] == 'b') con->text = FALSE;
862 else con->text = TRUE;
863 con->save = -1000;
864 return TRUE;
865 }
866
Wpipe_close(Rconnection con)867 static void Wpipe_close(Rconnection con)
868 {
869 con->status = rpipeClose( ((RWpipeconn)con->private) ->rp, NULL);
870 con->isopen = FALSE;
871 }
872
Wpipe_destroy(Rconnection con)873 static void Wpipe_destroy(Rconnection con)
874 {
875 free(con->private);
876 }
877
878
Wpipe_fgetc(Rconnection con)879 static int Wpipe_fgetc(Rconnection con)
880 {
881 rpipe *rp = ((RWpipeconn)con->private) ->rp;
882 int c;
883
884 c = rpipeGetc(rp);
885 return c == NOLAUNCH ? R_EOF : c;
886 }
887
888
null_seek(Rconnection con,double where,int origin,int rw)889 static double null_seek(Rconnection con, double where, int origin, int rw)
890 {
891 error(_("seek not enabled for this connection"));
892 return 0; /* -Wall */
893 }
894
null_truncate(Rconnection con)895 static void null_truncate(Rconnection con)
896 {
897 error(_("truncate not enabled for this connection"));
898 }
899
Wpipe_fflush(Rconnection con)900 static int Wpipe_fflush(Rconnection con)
901 {
902 BOOL res;
903
904 rpipe *rp = ((RWpipeconn)con->private) ->rp;
905 res = FlushFileBuffers(rp->write);
906 return res ? 0 : EOF;
907 }
908
Wpipe_read(void * ptr,size_t size,size_t nitems,Rconnection con)909 static size_t Wpipe_read(void *ptr, size_t size, size_t nitems,
910 Rconnection con)
911 {
912 rpipe *rp = ((RWpipeconn)con->private) ->rp;
913 DWORD ntoread, read;
914
915 while (PeekNamedPipe(rp->read, NULL, 0, NULL, &ntoread, NULL)) {
916 if (!ntoread && !rp->active) {
917 /* I got a case in which process terminated after Peek.. */
918 PeekNamedPipe(rp->read, NULL, 0, NULL, &ntoread, NULL);
919 if (!ntoread) return 0; /* end of pipe */
920 }
921 if (ntoread) {
922 if (ReadFile(rp->read, ptr, nitems * size, &read, NULL) == TRUE)
923 return read/size;
924 else return 0; /* error */
925 }
926 }
927 return 0;
928 }
929
Wpipe_write(const void * ptr,size_t size,size_t nitems,Rconnection con)930 static size_t Wpipe_write(const void *ptr, size_t size, size_t nitems,
931 Rconnection con)
932 {
933 rpipe *rp = ((RWpipeconn)con->private) ->rp;
934 DWORD towrite = nitems * size, write, ret;
935
936 if(!rp->active) return 0;
937 GetExitCodeProcess(rp->pi.pi.hProcess, &ret);
938 if(ret != STILL_ACTIVE) {
939 rp->active = 0;
940 warning("broken Windows pipe");
941 return 0;
942 }
943 if (WriteFile(rp->write, ptr, towrite, &write, NULL) != 0)
944 return write/size;
945 else return 0;
946 }
947
948 #define BUFSIZE 10000
Wpipe_vfprintf(Rconnection con,const char * format,va_list ap)949 static int Wpipe_vfprintf(Rconnection con, const char *format, va_list ap)
950 {
951 R_CheckStack2(BUFSIZE);
952 char buf[BUFSIZE], *b = buf;
953 int res = 0;
954
955 res = Rvsnprintf_mbcs(b, BUFSIZE, format, ap);
956 if(res < 0 || res >= BUFSIZE) {
957 warning(_("printing of extremely long output is truncated"));
958 res = strlen(b);
959 }
960 return Wpipe_write(buf, (size_t)1, (size_t)res, con);
961 }
962
963
newWpipe(const char * description,int ienc,const char * mode)964 Rconnection newWpipe(const char *description, int ienc, const char *mode)
965 {
966 Rconnection new;
967 char *command;
968 int len;
969
970 new = (Rconnection) malloc(sizeof(struct Rconn));
971 if(!new) error(_("allocation of pipe connection failed"));
972 new->class = (char *) malloc(strlen("pipe") + 1);
973 if(!new->class) {
974 free(new);
975 error(_("allocation of pipe connection failed"));
976 }
977 strcpy(new->class, "pipe");
978
979 len = strlen(getenv("COMSPEC")) + strlen(description) + 5;
980 command = (char *) malloc(len);
981 if (command)
982 new->description = (char *) malloc(len);
983 else
984 new->description = NULL;
985
986 if(!new->description) {
987 free(command); free(new->class); free(new);
988 error(_("allocation of pipe connection failed"));
989 }
990
991 /* We always use COMSPEC here, not R_SHELL or SHELL,
992 for compatibility with Rterm.
993 We also use /c for the same reason.
994 */
995
996 strcpy(command, getenv("COMSPEC"));
997 strcat(command, " /c ");
998 strcat(command, description);
999
1000 init_con(new, command, ienc, mode);
1001 free(command);
1002
1003 new->open = &Wpipe_open;
1004 new->close = &Wpipe_close;
1005 new->destroy = &Wpipe_destroy;
1006 new->vfprintf = &Wpipe_vfprintf;
1007 new->fgetc = &Wpipe_fgetc;
1008 new->seek = &null_seek;
1009 new->truncate = &null_truncate;
1010 new->fflush = &Wpipe_fflush;
1011 new->read = &Wpipe_read;
1012 new->write = &Wpipe_write;
1013 new->private = (void *) malloc(sizeof(struct Wpipeconn));
1014 if(!new->private) {
1015 free(new->description); free(new->class); free(new);
1016 error(_("allocation of pipe connection failed"));
1017 }
1018 return new;
1019 }
1020
1021
do_syswhich(SEXP call,SEXP op,SEXP args,SEXP env)1022 SEXP do_syswhich(SEXP call, SEXP op, SEXP args, SEXP env)
1023 {
1024 SEXP nm, ans;
1025 int i, n;
1026
1027 checkArity(op, args);
1028 nm = CAR(args);
1029 if(!isString(nm))
1030 error(_("'names' is not a character vector"));
1031 n = LENGTH(nm);
1032 PROTECT(ans = allocVector(STRSXP, n));
1033 for(i = 0; i < n; i++) {
1034 if (STRING_ELT(nm, i) == NA_STRING) {
1035 SET_STRING_ELT(ans, i, NA_STRING);
1036 } else {
1037 const char *this = CHAR(STRING_ELT(nm, i));
1038 char *that = expandcmd(this, 1);
1039 SET_STRING_ELT(ans, i, mkChar(that ? that : ""));
1040 free(that);
1041 }
1042 }
1043 setAttrib(ans, R_NamesSymbol, nm);
1044 UNPROTECT(1);
1045 return ans;
1046 }
1047