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 = &pi;
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