Merge branch 'js/mingw-redirect-std-handles' into maint

MinGW updates.

* js/mingw-redirect-std-handles:
  mingw: document the standard handle redirection
  mingw: optionally redirect stderr/stdout via the same handle
  mingw: add experimental feature to redirect standard handles
This commit is contained in:
Junio C Hamano 2017-11-15 12:05:03 +09:00
commit 74ef46558e
3 changed files with 88 additions and 0 deletions

View File

@ -709,6 +709,24 @@ of clones and fetches.
the background which do not want to cause lock contention with the background which do not want to cause lock contention with
other operations on the repository. Defaults to `1`. other operations on the repository. Defaults to `1`.
`GIT_REDIRECT_STDIN`::
`GIT_REDIRECT_STDOUT`::
`GIT_REDIRECT_STDERR`::
Windows-only: allow redirecting the standard input/output/error
handles to paths specified by the environment variables. This is
particularly useful in multi-threaded applications where the
canonical way to pass standard handles via `CreateProcess()` is
not an option because it would require the handles to be marked
inheritable (and consequently *every* spawned process would
inherit them, possibly blocking regular Git operations). The
primary intended use case is to use named pipes for communication
(e.g. `\\.\pipe\my-git-stdin-123`).
+
Two special values are supported: `off` will simply close the
corresponding standard handle, and if `GIT_REDIRECT_STDERR` is
`2>&1`, standard error will be redirected to the same handle as
standard output.
Discussion[[Discussion]] Discussion[[Discussion]]
------------------------ ------------------------

View File

@ -2139,6 +2139,62 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
return memcpy(malloc_startup(len), buffer, len); return memcpy(malloc_startup(len), buffer, len);
} }
static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
DWORD desired_access, DWORD flags)
{
DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
wchar_t buf[MAX_PATH];
DWORD max = ARRAY_SIZE(buf);
HANDLE handle;
DWORD ret = GetEnvironmentVariableW(key, buf, max);
if (!ret || ret >= max)
return;
/* make sure this does not leak into child processes */
SetEnvironmentVariableW(key, NULL);
if (!wcscmp(buf, L"off")) {
close(fd);
handle = GetStdHandle(std_id);
if (handle != INVALID_HANDLE_VALUE)
CloseHandle(handle);
return;
}
if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
handle = GetStdHandle(STD_OUTPUT_HANDLE);
if (handle == INVALID_HANDLE_VALUE) {
close(fd);
handle = GetStdHandle(std_id);
if (handle != INVALID_HANDLE_VALUE)
CloseHandle(handle);
} else {
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
SetStdHandle(std_id, handle);
dup2(new_fd, fd);
/* do *not* close the new_fd: that would close stdout */
}
return;
}
handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
flags, NULL);
if (handle != INVALID_HANDLE_VALUE) {
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
SetStdHandle(std_id, handle);
dup2(new_fd, fd);
close(new_fd);
}
}
static void maybe_redirect_std_handles(void)
{
maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
}
void mingw_startup(void) void mingw_startup(void)
{ {
int i, maxlen, argc; int i, maxlen, argc;
@ -2146,6 +2202,8 @@ void mingw_startup(void)
wchar_t **wenv, **wargv; wchar_t **wenv, **wargv;
_startupinfo si; _startupinfo si;
maybe_redirect_std_handles();
/* get wide char arguments and environment */ /* get wide char arguments and environment */
si.newmode = 0; si.newmode = 0;
if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0) if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)

View File

@ -453,4 +453,16 @@ test_expect_success 're-init from a linked worktree' '
) )
' '
test_expect_success MINGW 'redirect std handles' '
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
test .git = "$(cat output.txt)" &&
test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)" &&
test_must_fail env \
GIT_REDIRECT_STDOUT=output.txt \
GIT_REDIRECT_STDERR="2>&1" \
git rev-parse --git-dir --verify refs/invalid &&
printf ".git\nfatal: Needed a single revision\n" >expect &&
test_cmp expect output.txt
'
test_done test_done