mingw: add experimental feature to redirect standard handles

Particularly when calling Git from applications, such as Visual Studio's Team Explorer, it is important that stdin/stdout/stderr are closed properly. However, when spawning processes on Windows, those handles must be marked as inheritable if we want to use them, but that flag is a global flag and may very well be used by other spawned processes which then do not know to close those handles. Let's introduce a set of environment variables (GIT_REDIRECT_STDIN and friends) that specify paths to files, or even better, named pipes (which are similar to Unix sockets) and that are used by the spawned Git process. This helps work around above-mentioned issue: those named pipes will be opened in a non-inheritable way upon startup, and no handles are passed around (and therefore no inherited handles need to be closed by any spawned child). This feature shipped with Git for Windows (marked as experimental) since v2.11.0(2), so it has seen some serious testing in the meantime. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Nov 1, 2017 at 18:10 UTC 3f944424ac899fb6705e7463d937c5ed581da207
2 files changed +49
compat/mingw.c
+43
@@ -2139,6 +2139,47 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
2139 return memcpy(malloc_startup(len), buffer, len);
2140 }
2141
2142 +static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
2143 + DWORD desired_access, DWORD flags)
2144 +{
2145 + DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
2146 + wchar_t buf[MAX_PATH];
2147 + DWORD max = ARRAY_SIZE(buf);
2148 + HANDLE handle;
2149 + DWORD ret = GetEnvironmentVariableW(key, buf, max);
2150 +
2151 + if (!ret || ret >= max)
2152 + return;
2153 +
2154 + /* make sure this does not leak into child processes */
2155 + SetEnvironmentVariableW(key, NULL);
2156 + if (!wcscmp(buf, L"off")) {
2157 + close(fd);
2158 + handle = GetStdHandle(std_id);
2159 + if (handle != INVALID_HANDLE_VALUE)
2160 + CloseHandle(handle);
2161 + return;
2162 + }
2163 + handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
2164 + flags, NULL);
2165 + if (handle != INVALID_HANDLE_VALUE) {
2166 + int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2167 + SetStdHandle(std_id, handle);
2168 + dup2(new_fd, fd);
2169 + close(new_fd);
2170 + }
2171 +}
2172 +
2173 +static void maybe_redirect_std_handles(void)
2174 +{
2175 + maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
2176 + GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
2177 + maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
2178 + GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
2179 + maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
2180 + GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
2181 +}
2182 +
2183 void mingw_startup(void)
2184 {
2185 int i, maxlen, argc;
@@ -2146,6 +2187,8 @@ void mingw_startup(void)
2187 wchar_t **wenv, **wargv;
2188 _startupinfo si;
2189
2190 + maybe_redirect_std_handles();
2191 +
2192 /* get wide char arguments and environment */
2193 si.newmode = 0;
2194 if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
t/t0001-init.sh
+6
@@ -453,4 +453,10 @@ test_expect_success 're-init from a linked worktree' '
453 )
454 '
455
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 +'
461 +
462 test_done