mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Merge remote-tracking branch 'tor-github/pr/608'
This commit is contained in:
commit
5c85ba3077
@ -248,6 +248,11 @@ process_win32_exec(process_t *process)
|
|||||||
win32_process->stderr_handle.pipe = stderr_pipe_read;
|
win32_process->stderr_handle.pipe = stderr_pipe_read;
|
||||||
win32_process->stdin_handle.pipe = stdin_pipe_write;
|
win32_process->stdin_handle.pipe = stdin_pipe_write;
|
||||||
|
|
||||||
|
/* Close our ends of the pipes that is now owned by the child process. */
|
||||||
|
CloseHandle(stdout_pipe_write);
|
||||||
|
CloseHandle(stderr_pipe_write);
|
||||||
|
CloseHandle(stdin_pipe_read);
|
||||||
|
|
||||||
/* Used by the callback functions from ReadFileEx() and WriteFileEx() such
|
/* Used by the callback functions from ReadFileEx() and WriteFileEx() such
|
||||||
* that we can figure out which process_t that was responsible for the event.
|
* that we can figure out which process_t that was responsible for the event.
|
||||||
*
|
*
|
||||||
@ -324,6 +329,7 @@ process_win32_write(struct process_t *process, buf_t *buffer)
|
|||||||
|
|
||||||
process_win32_t *win32_process = process_get_win32_process(process);
|
process_win32_t *win32_process = process_get_win32_process(process);
|
||||||
BOOL ret = FALSE;
|
BOOL ret = FALSE;
|
||||||
|
DWORD error_code = 0;
|
||||||
const size_t buffer_size = buf_datalen(buffer);
|
const size_t buffer_size = buf_datalen(buffer);
|
||||||
|
|
||||||
/* Windows is still writing our buffer. */
|
/* Windows is still writing our buffer. */
|
||||||
@ -345,6 +351,12 @@ process_win32_write(struct process_t *process, buf_t *buffer)
|
|||||||
/* Read data from the process_t buffer into our intermediate buffer. */
|
/* Read data from the process_t buffer into our intermediate buffer. */
|
||||||
buf_get_bytes(buffer, win32_process->stdin_handle.buffer, write_size);
|
buf_get_bytes(buffer, win32_process->stdin_handle.buffer, write_size);
|
||||||
|
|
||||||
|
/* Because of the slightly weird API for WriteFileEx() we must set this to 0
|
||||||
|
* before we call WriteFileEx() because WriteFileEx() does not reset the last
|
||||||
|
* error itself when it's succesful. See comment below after the call to
|
||||||
|
* GetLastError(). */
|
||||||
|
SetLastError(0);
|
||||||
|
|
||||||
/* Schedule our write. */
|
/* Schedule our write. */
|
||||||
ret = WriteFileEx(win32_process->stdin_handle.pipe,
|
ret = WriteFileEx(win32_process->stdin_handle.pipe,
|
||||||
win32_process->stdin_handle.buffer,
|
win32_process->stdin_handle.buffer,
|
||||||
@ -353,11 +365,39 @@ process_win32_write(struct process_t *process, buf_t *buffer)
|
|||||||
process_win32_stdin_write_done);
|
process_win32_stdin_write_done);
|
||||||
|
|
||||||
if (! ret) {
|
if (! ret) {
|
||||||
log_warn(LD_PROCESS, "WriteFileEx() failed: %s",
|
error_code = GetLastError();
|
||||||
format_win32_error(GetLastError()));
|
|
||||||
|
/* No need to log at warning level for these two. */
|
||||||
|
if (error_code == ERROR_HANDLE_EOF || error_code == ERROR_BROKEN_PIPE) {
|
||||||
|
log_debug(LD_PROCESS, "WriteFileEx() returned EOF from pipe: %s",
|
||||||
|
format_win32_error(error_code));
|
||||||
|
} else {
|
||||||
|
log_warn(LD_PROCESS, "WriteFileEx() failed: %s",
|
||||||
|
format_win32_error(error_code));
|
||||||
|
}
|
||||||
|
|
||||||
|
win32_process->stdin_handle.reached_eof = true;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Here be dragons: According to MSDN's documentation for WriteFileEx() we
|
||||||
|
* should check GetLastError() after a call to WriteFileEx() even though the
|
||||||
|
* `ret` return value was successful. If everything is good, GetLastError()
|
||||||
|
* returns `ERROR_SUCCESS` and nothing happens.
|
||||||
|
*
|
||||||
|
* XXX(ahf): I have not managed to trigger this code while stress-testing
|
||||||
|
* this code. */
|
||||||
|
error_code = GetLastError();
|
||||||
|
|
||||||
|
if (error_code != ERROR_SUCCESS) {
|
||||||
|
/* LCOV_EXCL_START */
|
||||||
|
log_warn(LD_PROCESS, "WriteFileEx() failed after returning success: %s",
|
||||||
|
format_win32_error(error_code));
|
||||||
|
win32_process->stdin_handle.reached_eof = true;
|
||||||
|
return 0;
|
||||||
|
/* LCOV_EXCL_STOP */
|
||||||
|
}
|
||||||
|
|
||||||
/* This cast should be safe since our buffer can maximum be BUFFER_SIZE
|
/* This cast should be safe since our buffer can maximum be BUFFER_SIZE
|
||||||
* large. */
|
* large. */
|
||||||
return (int)write_size;
|
return (int)write_size;
|
||||||
@ -520,6 +560,28 @@ process_win32_timer_test_process(process_t *process)
|
|||||||
BOOL ret = FALSE;
|
BOOL ret = FALSE;
|
||||||
DWORD exit_code = 0;
|
DWORD exit_code = 0;
|
||||||
|
|
||||||
|
/* Sometimes the Windows kernel wont give us the EOF/Broken Pipe error
|
||||||
|
* message until some time after the process have actually terminated. We
|
||||||
|
* make sure that our ReadFileEx() calls for the process have *all* returned
|
||||||
|
* and both standard out and error have been marked as EOF before we try to
|
||||||
|
* see if the process terminated.
|
||||||
|
*
|
||||||
|
* This ensures that we *never* call the exit callback of the `process_t`,
|
||||||
|
* which potentially ends up calling `process_free()` on our `process_t`,
|
||||||
|
* before all data have been received from the process.
|
||||||
|
*
|
||||||
|
* We do NOT have a check here for whether standard in reached EOF since
|
||||||
|
* standard in's WriteFileEx() function is only called on-demand when we have
|
||||||
|
* something to write and is thus usually not awaiting to finish any
|
||||||
|
* operations. If we WriteFileEx() to a file that has terminated we'll simply
|
||||||
|
* get an error from ReadFileEx() or its completion routine and move on with
|
||||||
|
* life. */
|
||||||
|
if (! win32_process->stdout_handle.reached_eof)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (! win32_process->stderr_handle.reached_eof)
|
||||||
|
return false;
|
||||||
|
|
||||||
/* We start by testing whether our process is still running. */
|
/* We start by testing whether our process is still running. */
|
||||||
ret = GetExitCodeProcess(win32_process->process_information.hProcess,
|
ret = GetExitCodeProcess(win32_process->process_information.hProcess,
|
||||||
&exit_code);
|
&exit_code);
|
||||||
@ -667,36 +729,30 @@ process_win32_cleanup_handle(process_win32_handle_t *handle)
|
|||||||
tor_assert(handle);
|
tor_assert(handle);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* FIXME(ahf): My compiler does not set _WIN32_WINNT to a high enough value
|
|
||||||
* for this code to be available. Should we force it? CancelIoEx() is
|
|
||||||
* available from Windows 7 and above. If we decide to require this, we need
|
|
||||||
* to update the checks in all the three I/O completion callbacks to handle
|
|
||||||
* the ERROR_OPERATION_ABORTED as well as ERROR_BROKEN_PIPE. */
|
|
||||||
|
|
||||||
#if _WIN32_WINNT >= 0x0600
|
|
||||||
/* This code is only supported from Windows 7 and onwards. */
|
|
||||||
BOOL ret;
|
BOOL ret;
|
||||||
DWORD error_code;
|
DWORD error_code;
|
||||||
|
|
||||||
/* Cancel any pending I/O requests. */
|
/* Cancel any pending I/O requests: This means that instead of getting
|
||||||
ret = CancelIoEx(handle->pipe, &handle->overlapped);
|
* ERROR_BROKEN_PIPE we get ERROR_OPERATION_ABORTED, but it doesn't seem
|
||||||
|
* like this is needed. */
|
||||||
|
ret = CancelIo(handle->pipe);
|
||||||
|
|
||||||
if (! ret) {
|
if (! ret) {
|
||||||
error_code = GetLastError();
|
error_code = GetLastError();
|
||||||
|
|
||||||
/* There was no pending I/O requests for our handle. */
|
/* There was no pending I/O requests for our handle. */
|
||||||
if (error_code != ERROR_NOT_FOUND) {
|
if (error_code != ERROR_NOT_FOUND) {
|
||||||
log_warn(LD_PROCESS, "CancelIoEx() failed: %s",
|
log_warn(LD_PROCESS, "CancelIo() failed: %s",
|
||||||
format_win32_error(error_code));
|
format_win32_error(error_code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Close our handle. */
|
/* Close our handle. */
|
||||||
if (handle->pipe != INVALID_HANDLE_VALUE) {
|
if (handle->pipe != INVALID_HANDLE_VALUE) {
|
||||||
CloseHandle(handle->pipe);
|
CloseHandle(handle->pipe);
|
||||||
handle->pipe = INVALID_HANDLE_VALUE;
|
handle->pipe = INVALID_HANDLE_VALUE;
|
||||||
|
handle->reached_eof = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,15 +767,6 @@ process_win32_stdout_read_done(DWORD error_code,
|
|||||||
tor_assert(overlapped);
|
tor_assert(overlapped);
|
||||||
tor_assert(overlapped->hEvent);
|
tor_assert(overlapped->hEvent);
|
||||||
|
|
||||||
/* This happens when we have asked ReadFileEx() to read some data, but we
|
|
||||||
* then decided to call CloseHandle() on the HANDLE. This can happen if
|
|
||||||
* someone runs process_free() in the exit_callback of process_t, which means
|
|
||||||
* we cannot call process_get_win32_process() here. */
|
|
||||||
if (error_code == ERROR_BROKEN_PIPE) {
|
|
||||||
log_debug(LD_PROCESS, "Process reported broken pipe on standard out");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract our process_t from the hEvent member of OVERLAPPED. */
|
/* Extract our process_t from the hEvent member of OVERLAPPED. */
|
||||||
process_t *process = (process_t *)overlapped->hEvent;
|
process_t *process = (process_t *)overlapped->hEvent;
|
||||||
process_win32_t *win32_process = process_get_win32_process(process);
|
process_win32_t *win32_process = process_get_win32_process(process);
|
||||||
@ -743,15 +790,6 @@ process_win32_stderr_read_done(DWORD error_code,
|
|||||||
tor_assert(overlapped);
|
tor_assert(overlapped);
|
||||||
tor_assert(overlapped->hEvent);
|
tor_assert(overlapped->hEvent);
|
||||||
|
|
||||||
/* This happens when we have asked ReadFileEx() to read some data, but we
|
|
||||||
* then decided to call CloseHandle() on the HANDLE. This can happen if
|
|
||||||
* someone runs process_free() in the exit_callback of process_t, which means
|
|
||||||
* we cannot call process_get_win32_process() here. */
|
|
||||||
if (error_code == ERROR_BROKEN_PIPE) {
|
|
||||||
log_debug(LD_PROCESS, "Process reported broken pipe on standard error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract our process_t from the hEvent member of OVERLAPPED. */
|
/* Extract our process_t from the hEvent member of OVERLAPPED. */
|
||||||
process_t *process = (process_t *)overlapped->hEvent;
|
process_t *process = (process_t *)overlapped->hEvent;
|
||||||
process_win32_t *win32_process = process_get_win32_process(process);
|
process_win32_t *win32_process = process_get_win32_process(process);
|
||||||
@ -777,15 +815,6 @@ process_win32_stdin_write_done(DWORD error_code,
|
|||||||
|
|
||||||
(void)byte_count;
|
(void)byte_count;
|
||||||
|
|
||||||
/* This happens when we have asked WriteFileEx() to write some data, but we
|
|
||||||
* then decided to call CloseHandle() on the HANDLE. This can happen if
|
|
||||||
* someone runs process_free() in the exit_callback of process_t, which means
|
|
||||||
* we cannot call process_get_win32_process() here. */
|
|
||||||
if (error_code == ERROR_BROKEN_PIPE) {
|
|
||||||
log_debug(LD_PROCESS, "Process reported broken pipe on standard input");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
process_t *process = (process_t *)overlapped->hEvent;
|
process_t *process = (process_t *)overlapped->hEvent;
|
||||||
process_win32_t *win32_process = process_get_win32_process(process);
|
process_win32_t *win32_process = process_get_win32_process(process);
|
||||||
|
|
||||||
@ -806,7 +835,8 @@ process_win32_stdin_write_done(DWORD error_code,
|
|||||||
|
|
||||||
/* Schedule the next write. */
|
/* Schedule the next write. */
|
||||||
process_notify_event_stdin(process);
|
process_notify_event_stdin(process);
|
||||||
} else if (error_code == ERROR_HANDLE_EOF) {
|
} else if (error_code == ERROR_HANDLE_EOF ||
|
||||||
|
error_code == ERROR_BROKEN_PIPE) {
|
||||||
/* Our WriteFileEx() call was succesful, but we reached the end of our
|
/* Our WriteFileEx() call was succesful, but we reached the end of our
|
||||||
* file. We mark our handle as having reached EOF and returns. */
|
* file. We mark our handle as having reached EOF and returns. */
|
||||||
tor_assert(byte_count == 0);
|
tor_assert(byte_count == 0);
|
||||||
@ -837,6 +867,7 @@ process_win32_read_from_handle(process_win32_handle_t *handle,
|
|||||||
|
|
||||||
BOOL ret = FALSE;
|
BOOL ret = FALSE;
|
||||||
int bytes_available = 0;
|
int bytes_available = 0;
|
||||||
|
DWORD error_code = 0;
|
||||||
|
|
||||||
/* We already have a request to read data that isn't complete yet. */
|
/* We already have a request to read data that isn't complete yet. */
|
||||||
if (BUG(handle->busy))
|
if (BUG(handle->busy))
|
||||||
@ -860,6 +891,12 @@ process_win32_read_from_handle(process_win32_handle_t *handle,
|
|||||||
memset(handle->buffer, 0, sizeof(handle->buffer));
|
memset(handle->buffer, 0, sizeof(handle->buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Because of the slightly weird API for ReadFileEx() we must set this to 0
|
||||||
|
* before we call ReadFileEx() because ReadFileEx() does not reset the last
|
||||||
|
* error itself when it's succesful. See comment below after the call to
|
||||||
|
* GetLastError(). */
|
||||||
|
SetLastError(0);
|
||||||
|
|
||||||
/* Ask the Windows kernel to read data from our pipe into our buffer and call
|
/* Ask the Windows kernel to read data from our pipe into our buffer and call
|
||||||
* the callback function when it is done. */
|
* the callback function when it is done. */
|
||||||
ret = ReadFileEx(handle->pipe,
|
ret = ReadFileEx(handle->pipe,
|
||||||
@ -869,11 +906,39 @@ process_win32_read_from_handle(process_win32_handle_t *handle,
|
|||||||
callback);
|
callback);
|
||||||
|
|
||||||
if (! ret) {
|
if (! ret) {
|
||||||
log_warn(LD_PROCESS, "ReadFileEx() failed: %s",
|
error_code = GetLastError();
|
||||||
format_win32_error(GetLastError()));
|
|
||||||
|
/* No need to log at warning level for these two. */
|
||||||
|
if (error_code == ERROR_HANDLE_EOF || error_code == ERROR_BROKEN_PIPE) {
|
||||||
|
log_debug(LD_PROCESS, "ReadFileEx() returned EOF from pipe: %s",
|
||||||
|
format_win32_error(error_code));
|
||||||
|
} else {
|
||||||
|
log_warn(LD_PROCESS, "ReadFileEx() failed: %s",
|
||||||
|
format_win32_error(error_code));
|
||||||
|
}
|
||||||
|
|
||||||
|
handle->reached_eof = true;
|
||||||
return bytes_available;
|
return bytes_available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Here be dragons: According to MSDN's documentation for ReadFileEx() we
|
||||||
|
* should check GetLastError() after a call to ReadFileEx() even though the
|
||||||
|
* `ret` return value was successful. If everything is good, GetLastError()
|
||||||
|
* returns `ERROR_SUCCESS` and nothing happens.
|
||||||
|
*
|
||||||
|
* XXX(ahf): I have not managed to trigger this code while stress-testing
|
||||||
|
* this code. */
|
||||||
|
error_code = GetLastError();
|
||||||
|
|
||||||
|
if (error_code != ERROR_SUCCESS) {
|
||||||
|
/* LCOV_EXCL_START */
|
||||||
|
log_warn(LD_PROCESS, "ReadFileEx() failed after returning success: %s",
|
||||||
|
format_win32_error(error_code));
|
||||||
|
handle->reached_eof = true;
|
||||||
|
return bytes_available;
|
||||||
|
/* LCOV_EXCL_STOP */
|
||||||
|
}
|
||||||
|
|
||||||
/* We mark our handle as having a pending I/O request. */
|
/* We mark our handle as having a pending I/O request. */
|
||||||
handle->busy = true;
|
handle->busy = true;
|
||||||
|
|
||||||
@ -903,9 +968,10 @@ process_win32_handle_read_completion(process_win32_handle_t *handle,
|
|||||||
|
|
||||||
/* Tell our caller to schedule the next read. */
|
/* Tell our caller to schedule the next read. */
|
||||||
return true;
|
return true;
|
||||||
} else if (error_code == ERROR_HANDLE_EOF) {
|
} else if (error_code == ERROR_HANDLE_EOF ||
|
||||||
/* Our ReadFileEx() call was succesful, but we reached the end of our file.
|
error_code == ERROR_BROKEN_PIPE) {
|
||||||
* We mark our handle as having reached EOF and returns. */
|
/* Our ReadFileEx() finished, but we reached the end of our file. We mark
|
||||||
|
* our handle as having reached EOF and returns. */
|
||||||
tor_assert(byte_count == 0);
|
tor_assert(byte_count == 0);
|
||||||
|
|
||||||
handle->reached_eof = true;
|
handle->reached_eof = true;
|
||||||
|
@ -242,6 +242,7 @@ test_callbacks(void *arg)
|
|||||||
/* We returned. Let's see what our event loop said. */
|
/* We returned. Let's see what our event loop said. */
|
||||||
tt_int_op(smartlist_len(process_data->stdout_data), OP_EQ, 12);
|
tt_int_op(smartlist_len(process_data->stdout_data), OP_EQ, 12);
|
||||||
tt_int_op(smartlist_len(process_data->stderr_data), OP_EQ, 3);
|
tt_int_op(smartlist_len(process_data->stderr_data), OP_EQ, 3);
|
||||||
|
tt_assert(process_data->did_exit);
|
||||||
tt_int_op(process_data->exit_code, OP_EQ, 0);
|
tt_int_op(process_data->exit_code, OP_EQ, 0);
|
||||||
|
|
||||||
/* Check stdout output. */
|
/* Check stdout output. */
|
||||||
|
Loading…
Reference in New Issue
Block a user