Skip to content

Commit ba6df16

Browse files
author
Git for Windows Build Agent
committed
Merge branch 'redirect-std-handles'
This topic branch introduces a highly-experimental feature allowing to override stdin/stdout/stderr by setting environment variables e.g. to named pipes, solving a problem in highly multi-threaded applications where inheritable handles could cause blocked Git operations. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 7f8e722 + 0d1fa5d commit ba6df16

File tree

3 files changed

+87
-0
lines changed

3 files changed

+87
-0
lines changed

Documentation/git.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,23 @@ of clones and fetches.
697697
which feed potentially-untrusted URLS to git commands. See
698698
linkgit:git-config[1] for more details.
699699

700+
`GIT_REDIRECT_STDIN`::
701+
`GIT_REDIRECT_STDOUT`::
702+
`GIT_REDIRECT_STDERR`::
703+
(EXPERIMENTAL) Windows-only: allow redirecting the standard
704+
input/output/error handles. This is particularly useful in
705+
multi-threaded applications where the canonical way to pass
706+
standard handles via `CreateProcess()` is not an option because
707+
it would require the handles to be marked inheritable (and
708+
consequently *every* spawned process would inherit them, possibly
709+
blocking regular Git operations). The primary intended use case
710+
is to use named pipes for communication.
711+
+
712+
Two special values are supported: `off` will simply close the
713+
corresponding standard handle, and if `GIT_REDIRECT_STDERR` is
714+
`2>&1`, standard error will be redirected to the same handle as
715+
standard output.
716+
700717
Discussion[[Discussion]]
701718
------------------------
702719

compat/mingw.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,13 +2192,71 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
21922192
return memcpy(malloc_startup(len), buffer, len);
21932193
}
21942194

2195+
static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
2196+
DWORD desired_access, DWORD flags)
2197+
{
2198+
DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
2199+
wchar_t buf[MAX_LONG_PATH];
2200+
DWORD max = ARRAY_SIZE(buf);
2201+
HANDLE handle;
2202+
DWORD ret = GetEnvironmentVariableW(key, buf, max);
2203+
2204+
if (!ret || ret >= max)
2205+
return;
2206+
2207+
/* make sure this does not leak into child processes */
2208+
SetEnvironmentVariableW(key, NULL);
2209+
if (!wcscmp(buf, L"off")) {
2210+
close(fd);
2211+
handle = GetStdHandle(std_id);
2212+
if (handle != INVALID_HANDLE_VALUE)
2213+
CloseHandle(handle);
2214+
return;
2215+
}
2216+
if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
2217+
handle = GetStdHandle(STD_OUTPUT_HANDLE);
2218+
if (handle == INVALID_HANDLE_VALUE) {
2219+
close(fd);
2220+
handle = GetStdHandle(std_id);
2221+
if (handle != INVALID_HANDLE_VALUE)
2222+
CloseHandle(handle);
2223+
} else {
2224+
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2225+
SetStdHandle(std_id, handle);
2226+
dup2(new_fd, fd);
2227+
/* do *not* close the new_fd: that would close stdout */
2228+
}
2229+
return;
2230+
}
2231+
handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
2232+
flags, NULL);
2233+
if (handle != INVALID_HANDLE_VALUE) {
2234+
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2235+
SetStdHandle(std_id, handle);
2236+
dup2(new_fd, fd);
2237+
close(new_fd);
2238+
}
2239+
}
2240+
2241+
static void maybe_redirect_std_handles(void)
2242+
{
2243+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
2244+
GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
2245+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
2246+
GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
2247+
maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
2248+
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
2249+
}
2250+
21952251
void mingw_startup(void)
21962252
{
21972253
int i, maxlen, argc;
21982254
char *buffer;
21992255
wchar_t **wenv, **wargv;
22002256
_startupinfo si;
22012257

2258+
maybe_redirect_std_handles();
2259+
22022260
/* get wide char arguments and environment */
22032261
si.newmode = 0;
22042262
if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)

t/t0001-init.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,16 @@ test_expect_success 're-init from a linked worktree' '
453453
)
454454
'
455455

456+
test_expect_success MINGW 'redirect std handles' '
457+
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
458+
test .git = "$(cat output.txt)" &&
459+
test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)" &&
460+
test_must_fail env \
461+
GIT_REDIRECT_STDOUT=output.txt \
462+
GIT_REDIRECT_STDERR="2>&1" \
463+
git rev-parse --git-dir --verify refs/invalid &&
464+
printf ".git\nfatal: Needed a single revision\n" >expect &&
465+
test_cmp expect output.txt
466+
'
467+
456468
test_done

0 commit comments

Comments
 (0)