Refactored: Reuse existing code
[TortoiseGit.git] / src / libgit2 / system-call.c
blob2c40dc876029e83b3b2e32133410b806fed50081
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2014 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.
20 #include "buffer.h"
21 #include "netops.h"
22 #include "system-call.h"
24 static void safeCloseHandle(HANDLE *handle)
26 if (*handle != INVALID_HANDLE_VALUE) {
27 CloseHandle(*handle);
28 *handle = INVALID_HANDLE_VALUE;
32 static int command_read(HANDLE handle, char *buffer, size_t buf_size, size_t *bytes_read)
34 *bytes_read = 0;
36 if (!ReadFile(handle, buffer, (DWORD)buf_size, (DWORD*)bytes_read, NULL)) {
37 giterr_set(GITERR_OS, "could not read data from external process");
38 return -1;
41 return 0;
44 static int command_readall(HANDLE handle, git_buf *buf)
46 size_t bytes_read = 0;
47 do {
48 char buffer[65536];
49 command_read(handle, buffer, sizeof(buffer), &bytes_read);
50 git_buf_put(buf, buffer, bytes_read);
51 } while (bytes_read);
53 return 0;
56 struct ASYNCREADINGTHREADARGS {
57 HANDLE *handle;
58 git_buf *dest;
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);
71 return ret;
74 static HANDLE commmand_start_reading_thread(HANDLE *handle, git_buf *dest)
76 struct ASYNCREADINGTHREADARGS *threadArguments = git__calloc(1, sizeof(struct ASYNCREADINGTHREADARGS));
77 HANDLE thread;
78 if (!threadArguments)
79 return NULL;
81 threadArguments->handle = handle;
82 threadArguments->dest = dest;
84 thread = CreateThread(NULL, 0, AsyncReadingThread, threadArguments, 0, NULL);
85 if (!thread)
86 git__free(threadArguments);
87 return thread;
90 int commmand_start_stdout_reading_thread(COMMAND_HANDLE *commandHandle, git_buf *dest)
92 HANDLE thread = commmand_start_reading_thread(&commandHandle->out, dest);
93 if (!thread)
94 return -1;
95 commandHandle->asyncReadOutThread = thread;
96 return 0;
99 static int command_wait_reading_thread(HANDLE *handle)
101 DWORD exitCode = MAXDWORD;
102 if (*handle == INVALID_HANDLE_VALUE)
103 return -1;
105 WaitForSingleObject(*handle, INFINITE);
106 if (!GetExitCodeThread(*handle, &exitCode) || exitCode) {
107 safeCloseHandle(handle);
108 return -1;
110 safeCloseHandle(handle);
111 return 0;
114 int command_wait_stdout_reading_thread(COMMAND_HANDLE *commandHandle)
116 return command_wait_reading_thread(&commandHandle->asyncReadOutThread);
119 int command_start(wchar_t *cmd, COMMAND_HANDLE *commandHandle, LPWSTR pEnv)
121 SECURITY_ATTRIBUTES sa;
122 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;
123 STARTUPINFOW si = { 0 };
124 PROCESS_INFORMATION pi = { 0 };
126 si.cb = sizeof(STARTUPINFOW);
128 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
129 sa.lpSecurityDescriptor = NULL;
130 sa.bInheritHandle = TRUE;
131 if (!CreatePipe(&hReadOut, &hWriteOut, &sa, 0)) {
132 giterr_set(GITERR_OS, "Could not create pipe");
133 return -1;
135 if (!CreatePipe(&hReadIn, &hWriteIn, &sa, 0)) {
136 giterr_set(GITERR_OS, "Could not create pipe");
137 CloseHandle(hReadOut);
138 CloseHandle(hWriteOut);
139 return -1;
141 if (commandHandle->errBuf && !CreatePipe(&hReadError, &hWriteError, &sa, 0)) {
142 CloseHandle(hReadOut);
143 CloseHandle(hWriteOut);
144 CloseHandle(hReadIn);
145 CloseHandle(hWriteIn);
146 return -1;
149 si.hStdOutput = hWriteOut;
150 si.hStdInput = hReadIn;
151 si.hStdError = hWriteError;
153 // Ensure the read/write handle to the pipe for STDOUT resp. STDIN are not inherited.
154 if (!SetHandleInformation(hReadOut, HANDLE_FLAG_INHERIT, 0) || !SetHandleInformation(hWriteIn, HANDLE_FLAG_INHERIT, 0) || (commandHandle->errBuf && !SetHandleInformation(hReadError, HANDLE_FLAG_INHERIT, 0))) {
155 CloseHandle(hReadOut);
156 CloseHandle(hWriteOut);
157 CloseHandle(hReadIn);
158 CloseHandle(hWriteIn);
159 safeCloseHandle(&hReadError);
160 safeCloseHandle(&hWriteError);
161 return -1;
164 si.wShowWindow = SW_HIDE;
165 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
167 if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, pEnv ? CREATE_UNICODE_ENVIRONMENT : 0, pEnv, NULL, &si, &pi)) {
168 giterr_set(GITERR_OS, "Could not start external tool");
169 CloseHandle(hReadOut);
170 CloseHandle(hWriteOut);
171 CloseHandle(hReadIn);
172 CloseHandle(hWriteIn);
173 safeCloseHandle(&hReadError);
174 safeCloseHandle(&hWriteError);
175 return -1;
178 AllowSetForegroundWindow(pi.dwProcessId);
179 WaitForInputIdle(pi.hProcess, 10000);
181 CloseHandle(hReadIn);
182 CloseHandle(hWriteOut);
183 if (commandHandle->errBuf) {
184 HANDLE asyncReadErrorThread;
185 CloseHandle(hWriteError);
186 commandHandle->err = hReadError;
187 asyncReadErrorThread = commmand_start_reading_thread(&commandHandle->err, commandHandle->errBuf);
188 if (!asyncReadErrorThread) {
189 CloseHandle(hReadOut);
190 CloseHandle(hWriteIn);
191 CloseHandle(hReadError);
192 return -1;
194 commandHandle->asyncReadErrorThread = asyncReadErrorThread;
197 commandHandle->pi = pi;
198 commandHandle->out = hReadOut;
199 commandHandle->in = hWriteIn;
200 commandHandle->running = TRUE;
201 return 0;
204 int command_read_stdout(COMMAND_HANDLE *commandHandle, char *buffer, size_t buf_size, size_t *bytes_read)
206 return command_read(commandHandle->out, buffer, buf_size, bytes_read);
209 int command_write(COMMAND_HANDLE *commandHandle, const char *buffer, size_t len)
211 size_t off = 0;
212 DWORD written = 0;
214 do {
215 if (!WriteFile(commandHandle->in, buffer + off, (DWORD)(len - off), &written, NULL)) {
216 giterr_set(GITERR_OS, "could not write data to external process");
217 return -1;
220 off += written;
221 } while (off < len);
223 return 0;
226 int command_write_gitbuf(COMMAND_HANDLE *commandHandle, const git_buf *buf)
228 return command_write(commandHandle, buf->ptr, buf->size);
231 void command_close_stdin(COMMAND_HANDLE *commandHandle)
233 safeCloseHandle(&commandHandle->in);
236 void command_close_stdout(COMMAND_HANDLE *commandHandle)
238 safeCloseHandle(&commandHandle->out);
241 DWORD command_close(COMMAND_HANDLE *commandHandle)
243 DWORD exitcode = MAXDWORD;
244 if (!commandHandle->running)
245 return 1;
247 commandHandle->running = FALSE;
249 command_close_stdin(commandHandle);
250 command_wait_stdout_reading_thread(commandHandle);
251 command_close_stdout(commandHandle);
253 CloseHandle(commandHandle->pi.hThread);
255 WaitForSingleObject(commandHandle->pi.hProcess, INFINITE);
257 if (commandHandle->errBuf)
258 command_wait_reading_thread(&commandHandle->asyncReadErrorThread);
260 GetExitCodeProcess(commandHandle->pi.hProcess, &exitcode);
261 CloseHandle(commandHandle->pi.hProcess);
263 return exitcode;