1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2014, 2016 TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "system-call.h"
24 static void safeCloseHandle(HANDLE
*handle
)
26 if (*handle
!= INVALID_HANDLE_VALUE
) {
28 *handle
= INVALID_HANDLE_VALUE
;
32 static int command_read(HANDLE handle
, char *buffer
, size_t buf_size
, size_t *bytes_read
)
36 if (!ReadFile(handle
, buffer
, (DWORD
)buf_size
, (DWORD
*)bytes_read
, NULL
)) {
37 giterr_set(GITERR_OS
, "could not read data from external process");
44 static int command_readall(HANDLE handle
, git_buf
*buf
)
46 size_t bytes_read
= 0;
49 command_read(handle
, buffer
, sizeof(buffer
), &bytes_read
);
50 git_buf_put(buf
, buffer
, bytes_read
);
56 struct ASYNCREADINGTHREADARGS
{
61 static DWORD WINAPI
AsyncReadingThread(LPVOID lpParam
)
63 struct ASYNCREADINGTHREADARGS
* pDataArray
= (struct ASYNCREADINGTHREADARGS
*)lpParam
;
65 int ret
= command_readall(*pDataArray
->handle
, pDataArray
->dest
);
67 safeCloseHandle(pDataArray
->handle
);
69 git__free(pDataArray
);
74 static HANDLE
commmand_start_reading_thread(HANDLE
*handle
, git_buf
*dest
)
76 struct ASYNCREADINGTHREADARGS
*threadArguments
= git__calloc(1, sizeof(struct ASYNCREADINGTHREADARGS
));
78 if (!threadArguments
) {
83 threadArguments
->handle
= handle
;
84 threadArguments
->dest
= dest
;
86 thread
= CreateThread(NULL
, 0, AsyncReadingThread
, threadArguments
, 0, NULL
);
88 git__free(threadArguments
);
89 giterr_set(GITERR_OS
, "Could not create thread");
94 int commmand_start_stdout_reading_thread(COMMAND_HANDLE
*commandHandle
, git_buf
*dest
)
96 HANDLE thread
= commmand_start_reading_thread(&commandHandle
->out
, dest
);
99 commandHandle
->asyncReadOutThread
= thread
;
103 static int command_wait_reading_thread(HANDLE
*handle
)
105 DWORD exitCode
= MAXDWORD
;
106 if (*handle
== INVALID_HANDLE_VALUE
)
109 WaitForSingleObject(*handle
, INFINITE
);
110 if (!GetExitCodeThread(*handle
, &exitCode
) || exitCode
) {
111 safeCloseHandle(handle
);
114 safeCloseHandle(handle
);
118 int command_wait_stdout_reading_thread(COMMAND_HANDLE
*commandHandle
)
120 return command_wait_reading_thread(&commandHandle
->asyncReadOutThread
);
123 void command_init(COMMAND_HANDLE
*commandHandle
)
125 memset(commandHandle
, 0, sizeof(COMMAND_HANDLE
));
126 commandHandle
->in
= INVALID_HANDLE_VALUE
;
127 commandHandle
->out
= INVALID_HANDLE_VALUE
;
128 commandHandle
->err
= INVALID_HANDLE_VALUE
;
129 commandHandle
->asyncReadErrorThread
= INVALID_HANDLE_VALUE
;
130 commandHandle
->asyncReadOutThread
= INVALID_HANDLE_VALUE
;
133 int command_start(wchar_t *cmd
, COMMAND_HANDLE
*commandHandle
, LPWSTR
* pEnv
, DWORD flags
)
135 SECURITY_ATTRIBUTES sa
;
136 HANDLE hReadOut
= INVALID_HANDLE_VALUE
, hWriteOut
= INVALID_HANDLE_VALUE
, hReadIn
= INVALID_HANDLE_VALUE
, hWriteIn
= INVALID_HANDLE_VALUE
, hReadError
= INVALID_HANDLE_VALUE
, hWriteError
= INVALID_HANDLE_VALUE
;
137 STARTUPINFOW si
= { 0 };
138 PROCESS_INFORMATION pi
= { 0 };
140 si
.cb
= sizeof(STARTUPINFOW
);
142 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
143 sa
.lpSecurityDescriptor
= NULL
;
144 sa
.bInheritHandle
= TRUE
;
145 if (!CreatePipe(&hReadOut
, &hWriteOut
, &sa
, 0)) {
146 giterr_set(GITERR_OS
, "Could not create pipe");
149 if (!CreatePipe(&hReadIn
, &hWriteIn
, &sa
, 0)) {
150 giterr_set(GITERR_OS
, "Could not create pipe");
151 CloseHandle(hReadOut
);
152 CloseHandle(hWriteOut
);
155 if (commandHandle
->errBuf
&& !CreatePipe(&hReadError
, &hWriteError
, &sa
, 0)) {
156 giterr_set(GITERR_OS
, "Could not create pipe");
157 CloseHandle(hReadOut
);
158 CloseHandle(hWriteOut
);
159 CloseHandle(hReadIn
);
160 CloseHandle(hWriteIn
);
164 si
.hStdOutput
= hWriteOut
;
165 si
.hStdInput
= hReadIn
;
166 si
.hStdError
= hWriteError
;
168 // Ensure the read/write handle to the pipe for STDOUT resp. STDIN are not inherited.
169 if (!SetHandleInformation(hReadOut
, HANDLE_FLAG_INHERIT
, 0) || !SetHandleInformation(hWriteIn
, HANDLE_FLAG_INHERIT
, 0) || (commandHandle
->errBuf
&& !SetHandleInformation(hReadError
, HANDLE_FLAG_INHERIT
, 0))) {
170 giterr_set(GITERR_OS
, "SetHandleInformation failed");
171 CloseHandle(hReadOut
);
172 CloseHandle(hWriteOut
);
173 CloseHandle(hReadIn
);
174 CloseHandle(hWriteIn
);
175 safeCloseHandle(&hReadError
);
176 safeCloseHandle(&hWriteError
);
180 si
.wShowWindow
= SW_HIDE
;
181 si
.dwFlags
= STARTF_USESTDHANDLES
| STARTF_USESHOWWINDOW
;
183 if (!CreateProcessW(NULL
, cmd
, NULL
, NULL
, TRUE
, CREATE_UNICODE_ENVIRONMENT
| flags
, pEnv
? *pEnv
: NULL
, NULL
, &si
, &pi
)) {
184 giterr_set(GITERR_OS
, "Could not start external tool");
185 CloseHandle(hReadOut
);
186 CloseHandle(hWriteOut
);
187 CloseHandle(hReadIn
);
188 CloseHandle(hWriteIn
);
189 safeCloseHandle(&hReadError
);
190 safeCloseHandle(&hWriteError
);
194 AllowSetForegroundWindow(pi
.dwProcessId
);
195 WaitForInputIdle(pi
.hProcess
, 10000);
197 CloseHandle(hReadIn
);
198 CloseHandle(hWriteOut
);
199 if (commandHandle
->errBuf
) {
200 HANDLE asyncReadErrorThread
;
201 CloseHandle(hWriteError
);
202 commandHandle
->err
= hReadError
;
203 asyncReadErrorThread
= commmand_start_reading_thread(&commandHandle
->err
, commandHandle
->errBuf
);
204 if (!asyncReadErrorThread
) {
205 CloseHandle(hReadOut
);
206 CloseHandle(hWriteIn
);
207 CloseHandle(hReadError
);
210 commandHandle
->asyncReadErrorThread
= asyncReadErrorThread
;
213 commandHandle
->pi
= pi
;
214 commandHandle
->out
= hReadOut
;
215 commandHandle
->in
= hWriteIn
;
216 commandHandle
->running
= TRUE
;
220 int command_read_stdout(COMMAND_HANDLE
*commandHandle
, char *buffer
, size_t buf_size
, size_t *bytes_read
)
222 return command_read(commandHandle
->out
, buffer
, buf_size
, bytes_read
);
225 int command_write(COMMAND_HANDLE
*commandHandle
, const char *buffer
, size_t len
)
231 if (!WriteFile(commandHandle
->in
, buffer
+ off
, (DWORD
)(len
- off
), &written
, NULL
)) {
232 giterr_set(GITERR_OS
, "could not write data to external process");
242 int command_write_gitbuf(COMMAND_HANDLE
*commandHandle
, const git_buf
*buf
)
244 return command_write(commandHandle
, buf
->ptr
, buf
->size
);
247 void command_close_stdin(COMMAND_HANDLE
*commandHandle
)
249 safeCloseHandle(&commandHandle
->in
);
252 void command_close_stdout(COMMAND_HANDLE
*commandHandle
)
254 safeCloseHandle(&commandHandle
->out
);
257 DWORD
command_close(COMMAND_HANDLE
*commandHandle
)
259 DWORD exitcode
= MAXDWORD
;
260 if (!commandHandle
->running
)
263 commandHandle
->running
= FALSE
;
265 command_close_stdin(commandHandle
);
266 command_wait_stdout_reading_thread(commandHandle
);
267 command_close_stdout(commandHandle
);
269 CloseHandle(commandHandle
->pi
.hThread
);
271 WaitForSingleObject(commandHandle
->pi
.hProcess
, INFINITE
);
273 if (commandHandle
->errBuf
)
274 command_wait_reading_thread(&commandHandle
->asyncReadErrorThread
);
276 GetExitCodeProcess(commandHandle
->pi
.hProcess
, &exitcode
);
277 CloseHandle(commandHandle
->pi
.hProcess
);