Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 : : * Author: Thun Lu <thun.lu@zohomail.cn>
4 : : * Repo: https://github.com/thun-res/vlink
5 : : * _ __ __ _ __
6 : : * | | / / / / (_) ____ / /__
7 : : * | | / / / / / / / __ \ / //_/
8 : : * | |/ / / /___ / / / / / / / ,<
9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 : : *
11 : : * Licensed under the Apache License, Version 2.0 (the "License");
12 : : * you may not use this file except in compliance with the License.
13 : : * You may obtain a copy of the License at
14 : : *
15 : : * http://www.apache.org/licenses/LICENSE-2.0
16 : : *
17 : : * Unless required by applicable law or agreed to in writing, software
18 : : * distributed under the License is distributed on an "AS IS" BASIS,
19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 : : * See the License for the specific language governing permissions and
21 : : * limitations under the License.
22 : : */
23 : :
24 : : #include "./base/process.h"
25 : :
26 : : #include <algorithm>
27 : : #include <atomic>
28 : : #include <cstdlib>
29 : : #include <limits>
30 : : #include <memory>
31 : : #include <mutex>
32 : : #include <shared_mutex>
33 : : #include <string>
34 : : #include <string_view>
35 : : #include <thread>
36 : : #include <utility>
37 : : #include <vector>
38 : :
39 : : #include "./base/condition_variable.h"
40 : : #include "./base/helpers.h"
41 : : #include "./base/logger.h"
42 : :
43 : : #ifdef _WIN32
44 : : #include <Windows.h>
45 : : #include <processthreadsapi.h>
46 : : #undef min
47 : : #undef max
48 : : #else
49 : : #include <fcntl.h>
50 : : #include <poll.h>
51 : : #include <pthread.h>
52 : : #include <sys/types.h>
53 : : #include <sys/wait.h>
54 : : #include <unistd.h>
55 : :
56 : : #include <cerrno>
57 : : #include <csignal>
58 : : #include <cstring>
59 : : #endif
60 : :
61 : : #ifdef __linux__
62 : : #include <sys/syscall.h>
63 : : #endif
64 : :
65 : : namespace vlink {
66 : :
67 : : [[maybe_unused]] static constexpr int kExecFailedExitCode{127};
68 : : [[maybe_unused]] static constexpr size_t kDefaultMaxBufferSize{16 * 1024 * 1024};
69 : : [[maybe_unused]] static constexpr int kMonitorLoopDelayMs{10};
70 : :
71 : : #ifdef _WIN32
72 : : [[maybe_unused]] static void close_handle_if_valid(HANDLE& handle) {
73 : : if VLIKELY (handle != INVALID_HANDLE_VALUE && handle != nullptr) {
74 : : ::CloseHandle(handle);
75 : : handle = INVALID_HANDLE_VALUE;
76 : : }
77 : : }
78 : :
79 : : [[maybe_unused]] static bool is_valid_handle(HANDLE handle) {
80 : : return handle != INVALID_HANDLE_VALUE && handle != nullptr;
81 : : }
82 : :
83 : : [[maybe_unused]] static bool duplicate_inheritable_handle(HANDLE source, HANDLE& duplicated) {
84 : : duplicated = INVALID_HANDLE_VALUE;
85 : :
86 : : if VUNLIKELY (!is_valid_handle(source)) {
87 : : return false;
88 : : }
89 : :
90 : : if VUNLIKELY (!::DuplicateHandle(::GetCurrentProcess(), source, ::GetCurrentProcess(), &duplicated, 0, TRUE,
91 : : DUPLICATE_SAME_ACCESS)) {
92 : : duplicated = INVALID_HANDLE_VALUE;
93 : : return false;
94 : : }
95 : :
96 : : return true;
97 : : }
98 : :
99 : : [[maybe_unused]] static void append_unique_handle(std::vector<HANDLE>& handles, HANDLE handle) {
100 : : if VUNLIKELY (!is_valid_handle(handle)) {
101 : : return;
102 : : }
103 : :
104 : : if (std::find(handles.begin(), handles.end(), handle) == handles.end()) {
105 : : handles.emplace_back(handle);
106 : : }
107 : : }
108 : :
109 : : [[maybe_unused]] static Process::ExitStatus normalize_windows_exit_status(Process::ExitStatus status,
110 : : bool forced_termination) {
111 : : if VUNLIKELY (forced_termination || status == Process::kCrashExitStatus) {
112 : : return Process::kCrashExitStatus;
113 : : }
114 : :
115 : : return status;
116 : : }
117 : :
118 : : struct WindowsWriteResult final {
119 : : bool ok{false};
120 : : bool timed_out{false};
121 : : DWORD bytes_written{0};
122 : : DWORD error{ERROR_SUCCESS};
123 : : };
124 : :
125 : : struct WindowsWriteContext final {
126 : : HANDLE handle{INVALID_HANDLE_VALUE};
127 : : const void* data{nullptr};
128 : : DWORD size{0};
129 : : DWORD bytes_written{0};
130 : : BOOL ok{FALSE};
131 : : DWORD error{ERROR_SUCCESS};
132 : : };
133 : :
134 : : static void run_windows_write(WindowsWriteContext* context) {
135 : : if VUNLIKELY (context == nullptr) {
136 : : return;
137 : : }
138 : :
139 : : context->ok = ::WriteFile(context->handle, context->data, context->size, &context->bytes_written, nullptr);
140 : : context->error = context->ok ? ERROR_SUCCESS : ::GetLastError();
141 : : }
142 : :
143 : : [[maybe_unused]] static WindowsWriteResult write_handle_with_timeout(HANDLE handle, const void* data, DWORD size,
144 : : int timeout_ms) {
145 : : WindowsWriteResult result;
146 : :
147 : : if (timeout_ms < 0) {
148 : : result.ok = ::WriteFile(handle, data, size, &result.bytes_written, nullptr) != FALSE;
149 : : result.error = result.ok ? ERROR_SUCCESS : ::GetLastError();
150 : : return result;
151 : : }
152 : :
153 : : WindowsWriteContext ctx{handle, data, size};
154 : :
155 : : std::thread writer(run_windows_write, &ctx);
156 : :
157 : : auto* writer_handle = static_cast<HANDLE>(writer.native_handle());
158 : : const DWORD wait_result = ::WaitForSingleObject(writer_handle, static_cast<DWORD>(timeout_ms));
159 : : const DWORD wait_error = (wait_result == WAIT_FAILED) ? ::GetLastError() : ERROR_SUCCESS;
160 : :
161 : : if (wait_result == WAIT_TIMEOUT) {
162 : : ::CancelSynchronousIo(writer_handle);
163 : : writer.join();
164 : :
165 : : result.ok = ctx.ok != FALSE;
166 : : result.bytes_written = ctx.bytes_written;
167 : : result.error = result.ok ? ERROR_SUCCESS : ctx.error;
168 : : result.timed_out = !result.ok && ctx.error == ERROR_OPERATION_ABORTED;
169 : : return result;
170 : : }
171 : :
172 : : writer.join();
173 : :
174 : : result.ok = ctx.ok != FALSE;
175 : : result.bytes_written = ctx.bytes_written;
176 : :
177 : : if VLIKELY (result.ok) {
178 : : result.error = ERROR_SUCCESS;
179 : : } else if VUNLIKELY (wait_result == WAIT_FAILED) {
180 : : result.error = wait_error;
181 : : } else {
182 : : result.error = ctx.error;
183 : : }
184 : :
185 : : return result;
186 : : }
187 : :
188 : : [[maybe_unused]] static std::wstring escape_argument(const std::wstring& arg) {
189 : : if VUNLIKELY (arg.empty()) {
190 : : return L"\"\"";
191 : : }
192 : :
193 : : bool needs_quotes = false;
194 : :
195 : : for (wchar_t c : arg) {
196 : : if VUNLIKELY (c == L' ' || c == L'\t' || c == L'\n' || c == L'\v' || c == L'"' || c == L'\\') {
197 : : needs_quotes = true;
198 : : break;
199 : : }
200 : : }
201 : :
202 : : if VLIKELY (!needs_quotes) {
203 : : return arg;
204 : : }
205 : :
206 : : std::wstring result = L"\"";
207 : : size_t backslash_count = 0;
208 : :
209 : : for (wchar_t c : arg) {
210 : : if (c == L'\\') {
211 : : backslash_count++;
212 : : } else if (c == L'"') {
213 : : result.append((backslash_count * 2) + 1, L'\\');
214 : : result += L'"';
215 : : backslash_count = 0;
216 : : } else {
217 : : if (backslash_count > 0) {
218 : : result.append(backslash_count, L'\\');
219 : : backslash_count = 0;
220 : : }
221 : :
222 : : result += c;
223 : : }
224 : : }
225 : :
226 : : if (backslash_count > 0) {
227 : : result.append(backslash_count * 2, L'\\');
228 : : }
229 : :
230 : : result += L'"';
231 : :
232 : : return result;
233 : : }
234 : :
235 : : [[maybe_unused]] static std::wstring build_command_line(const std::wstring& program,
236 : : const std::vector<std::wstring>& arguments) {
237 : : std::wstring cmd_line = escape_argument(program);
238 : :
239 : : for (const auto& arg : arguments) {
240 : : cmd_line += L' ';
241 : : cmd_line += escape_argument(arg);
242 : : }
243 : :
244 : : return cmd_line;
245 : : }
246 : :
247 : : [[maybe_unused]] static std::vector<wchar_t> build_environment_block(const Process::EnvironmentMap& env_map) {
248 : : if (env_map.empty()) {
249 : : return std::vector<wchar_t>{L'\0', L'\0'};
250 : : }
251 : :
252 : : std::vector<std::pair<std::wstring, std::wstring>> sorted_env;
253 : : sorted_env.reserve(env_map.size());
254 : :
255 : : for (const auto& [key, value] : env_map) {
256 : : sorted_env.emplace_back(Helpers::string_to_wstring(key), Helpers::string_to_wstring(value));
257 : : }
258 : :
259 : : std::sort(sorted_env.begin(), sorted_env.end());
260 : :
261 : : std::vector<wchar_t> env_block;
262 : :
263 : : for (const auto& [key, value] : sorted_env) {
264 : : env_block.insert(env_block.end(), key.begin(), key.end());
265 : : env_block.emplace_back(L'=');
266 : :
267 : : env_block.insert(env_block.end(), value.begin(), value.end());
268 : : env_block.emplace_back(L'\0');
269 : : }
270 : :
271 : : env_block.emplace_back(L'\0');
272 : :
273 : : return env_block;
274 : : }
275 : : #endif
276 : :
277 : : #ifndef _WIN32
278 : 181 : static void close_child_descriptors(int keep_fd) {
279 : : #if defined(__linux__) && defined(SYS_close_range)
280 : : const bool closed_before =
281 [ + - + - ]: 362 : keep_fd <= STDERR_FILENO + 1 || ::syscall(SYS_close_range, static_cast<unsigned int>(STDERR_FILENO + 1),
282 : 181 : static_cast<unsigned int>(keep_fd - 1), 0U) == 0;
283 : 181 : const bool closed_after = ::syscall(SYS_close_range, static_cast<unsigned int>(keep_fd + 1),
284 : 181 : std::numeric_limits<unsigned int>::max(), 0U) == 0;
285 : :
286 [ + - + - ]: 181 : if (closed_before && closed_after) {
287 : 181 : return;
288 : : }
289 : : #endif
290 : :
291 : 0 : int max_fd = static_cast<int>(sysconf(_SC_OPEN_MAX));
292 : :
293 [ # # ]: 0 : if VUNLIKELY (max_fd < 0) {
294 : 0 : max_fd = 1024;
295 : : }
296 : :
297 [ # # ]: 0 : for (int fd = STDERR_FILENO + 1; fd < max_fd; ++fd) {
298 [ # # ]: 0 : if (fd != keep_fd) {
299 : 0 : ::close(fd);
300 : : }
301 : : }
302 : : }
303 : :
304 : 116 : [[maybe_unused]] static std::string resolve_program_path(const std::string& program,
305 : : const Process::EnvironmentMap& env_map) {
306 [ + - + + : 116 : if VUNLIKELY (program.empty() || program.find('/') != std::string::npos) {
+ + ]
307 [ + - ]: 111 : return program;
308 : : }
309 : :
310 [ + - + - ]: 5 : auto iter = env_map.find("PATH");
311 : :
312 [ + + + + : 5 : if (iter == env_map.end() || iter->second.empty()) {
+ + ]
313 [ + - ]: 2 : return program;
314 : : }
315 : :
316 : 3 : const std::string& path_env = iter->second;
317 : 3 : size_t start = 0;
318 : :
319 [ + - ]: 4 : while (start <= path_env.size()) {
320 : 4 : size_t end = path_env.find(':', start);
321 : :
322 [ + + + - : 4 : std::string directory = (end == std::string::npos) ? path_env.substr(start) : path_env.substr(start, end - start);
+ - ]
323 : :
324 : : // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
325 [ + + + - : 4 : std::string candidate = directory.empty() ? program : directory + "/" + program;
+ - + - +
+ - - ]
326 : :
327 [ + + ]: 4 : if (access(candidate.c_str(), X_OK) == 0) {
328 : 3 : return candidate;
329 : : }
330 : :
331 [ - + ]: 1 : if (end == std::string::npos) {
332 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
333 : : }
334 : :
335 : 1 : start = end + 1;
336 [ + + - + : 7 : }
+ - ]
337 : :
338 : : return program; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
339 : : }
340 : :
341 : 31 : [[maybe_unused]] static ssize_t write_no_sigpipe(int fd, const void* data, size_t size) {
342 : : sigset_t sigpipe_set;
343 : : sigset_t old_set;
344 : : sigset_t pending_set;
345 : :
346 [ + - - + : 31 : if VUNLIKELY (sigemptyset(&sigpipe_set) != 0 || sigaddset(&sigpipe_set, SIGPIPE) != 0) {
- + ]
347 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
348 : : }
349 : :
350 : 31 : int mask_error = ::pthread_sigmask(SIG_BLOCK, &sigpipe_set, &old_set);
351 : :
352 [ - + ]: 31 : if VUNLIKELY (mask_error != 0) {
353 : : errno = mask_error; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
354 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
355 : : }
356 : :
357 : 31 : const bool sigpipe_blocked = sigismember(&old_set, SIGPIPE) == 1;
358 : :
359 [ - + ]: 31 : if VUNLIKELY (::sigpending(&pending_set) != 0) {
360 : : // LCOV_EXCL_START GCOVR_EXCL_START
361 : : int pending_error = errno;
362 : : int restore_error = ::pthread_sigmask(SIG_SETMASK, &old_set, nullptr);
363 : : errno = restore_error != 0 ? restore_error : pending_error;
364 : : return -1;
365 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
366 : : }
367 : :
368 : 31 : const bool sigpipe_pending = sigismember(&pending_set, SIGPIPE) == 1;
369 [ + - ]: 31 : ssize_t written = ::write(fd, data, size);
370 : 31 : int write_error = errno;
371 : :
372 [ + + - + : 31 : if VUNLIKELY (written < 0 && write_error == EPIPE && !sigpipe_blocked && !sigpipe_pending) {
- + - - -
+ - - -
+ ]
373 : : #ifdef __APPLE__
374 : : sigset_t pending_set;
375 : : while (true) {
376 : : if (sigpending(&pending_set) != 0) {
377 : : break;
378 : : }
379 : :
380 : : if (!sigismember(&pending_set, SIGPIPE)) {
381 : : break;
382 : : }
383 : :
384 : : int sig = 0;
385 : : int err = sigwait(&sigpipe_set, &sig);
386 : :
387 : : if (err == EINTR) {
388 : : continue;
389 : : }
390 : :
391 : : (void)sig;
392 : : break;
393 : : }
394 : : #else
395 : : const struct timespec zero_timeout{0, 0}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
396 : : while (::sigtimedwait(&sigpipe_set, nullptr, &zero_timeout) == -1 && // LCOV_EXCL_LINE GCOVR_EXCL_LINE
397 : : errno == EINTR) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
398 : : }
399 : : #endif
400 : : }
401 : :
402 : 31 : int restore_error = ::pthread_sigmask(SIG_SETMASK, &old_set, nullptr);
403 : :
404 [ - + ]: 31 : if VUNLIKELY (restore_error != 0) {
405 : : errno = restore_error; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
406 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
407 : : }
408 : :
409 : 31 : errno = write_error;
410 : 31 : return written;
411 : : }
412 : : #endif
413 : :
414 : 26 : static size_t find_newline(const std::vector<uint8_t>& buffer, size_t offset) {
415 [ + + ]: 100 : for (size_t i = offset; i < buffer.size(); ++i) {
416 [ + + ]: 94 : if (buffer[i] == '\n') {
417 : 20 : return i - offset;
418 : : }
419 : : }
420 : :
421 : 6 : return std::string::npos;
422 : : }
423 : :
424 : 34 : static void consume_buffer(std::vector<uint8_t>& buffer, size_t& offset, size_t count) {
425 : 34 : offset += count;
426 : :
427 [ + + ]: 34 : if (offset == buffer.size()) {
428 : 16 : buffer.clear();
429 : 16 : offset = 0;
430 : : }
431 : 34 : }
432 : :
433 : 14 : static void compact_buffer(std::vector<uint8_t>& buffer, size_t& offset) {
434 [ + + ]: 14 : if (offset > 0) {
435 [ + - ]: 4 : buffer.erase(buffer.begin(), buffer.begin() + offset);
436 : 4 : offset = 0;
437 : : }
438 : 14 : }
439 : :
440 : 43 : static std::string_view buffer_view(const std::vector<uint8_t>& buffer, size_t offset) {
441 : 43 : return {reinterpret_cast<const char*>(buffer.data() + offset), buffer.size() - offset};
442 : : }
443 : :
444 : : // Process::Impl
445 : : struct Process::Impl final { // NOLINT(clang-analyzer-optin.performance.Padding)
446 : : std::atomic<Process::State> state{Process::kNotRunningState};
447 : : std::atomic<Process::ExitStatus> exit_status{Process::kNormalExitStatus};
448 : : std::atomic<Process::Error> error{Process::kNoError};
449 : : std::atomic<Process::Mode> mode{Process::kSeparateMode};
450 : :
451 : : std::atomic<int> exit_code{-1};
452 : : std::atomic<bool> exit_processed{false};
453 : : std::atomic<bool> error_reported{false};
454 : : std::atomic<bool> stdout_closed{false};
455 : : std::atomic<bool> stderr_closed{false};
456 : : std::atomic<bool> inherit_environment{false};
457 : : std::atomic<bool> is_being_destroyed{false};
458 : : std::atomic<bool> forced_termination{false};
459 : : std::atomic<size_t> max_buffer_size{kDefaultMaxBufferSize};
460 : : std::atomic<bool> monitor_running{false};
461 : : std::atomic<bool> monitor_should_stop{false};
462 : :
463 : : #ifdef _WIN32
464 : : HANDLE process{INVALID_HANDLE_VALUE};
465 : : HANDLE thread{INVALID_HANDLE_VALUE};
466 : : HANDLE stdin_write{INVALID_HANDLE_VALUE};
467 : : HANDLE stdin_read{INVALID_HANDLE_VALUE};
468 : : HANDLE stdout_write{INVALID_HANDLE_VALUE};
469 : : HANDLE stdout_read{INVALID_HANDLE_VALUE};
470 : : HANDLE stderr_write{INVALID_HANDLE_VALUE};
471 : : HANDLE stderr_read{INVALID_HANDLE_VALUE};
472 : : DWORD process_id{0};
473 : : #else
474 : : std::atomic<int64_t> process_id{-1};
475 : : int stdin_pipe[2]{-1, -1};
476 : : int stdout_pipe[2]{-1, -1};
477 : : int stderr_pipe[2]{-1, -1};
478 : : #endif
479 : :
480 : : std::vector<uint8_t> stdout_buffer;
481 : : std::vector<uint8_t> stderr_buffer;
482 : : size_t stdout_offset{0};
483 : : size_t stderr_offset{0};
484 : : std::mutex buffer_mtx;
485 : :
486 : : std::mutex state_mtx;
487 : : ConditionVariable state_cv;
488 : :
489 : : std::unique_ptr<std::thread> monitor_thread;
490 : :
491 : : Process::ErrorCallback error_callback;
492 : : Process::FinishedCallback finished_callback;
493 : : Process::ReadyReadCallback ready_read_stdout_callback;
494 : : Process::ReadyReadCallback ready_read_stderr_callback;
495 : : Process::StateChangedCallback state_changed_callback;
496 : : std::shared_mutex shared_mtx;
497 : :
498 : : Process::EnvironmentMap environment_map;
499 : : std::string working_directory;
500 : : };
501 : :
502 : : // Process
503 : 184 : Process::Process() : impl_(std::make_unique<Impl>()) {}
504 : :
505 : 184 : Process::~Process() {
506 : 184 : impl_->is_being_destroyed.store(true, std::memory_order_release);
507 : :
508 [ - + ]: 184 : if (is_running()) {
509 : : #ifdef _WIN32
510 : : CLOG_W("Process: Still running (PID=%d).", static_cast<int>(impl_->process_id));
511 : : #else
512 : : // LCOV_EXCL_START GCOVR_EXCL_START
513 : : CLOG_W("Process: Still running (PID=%d).", static_cast<int>(impl_->process_id.load(std::memory_order_acquire)));
514 : : #endif
515 : :
516 : : terminate();
517 : :
518 : : read_from_pipes_with_lock();
519 : :
520 : : wait_for_finished(kDestructorWaitTimeoutMs);
521 : :
522 : : if VUNLIKELY (is_running()) {
523 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
524 : : #ifdef _WIN32
525 : : CLOG_E("Process: Force to kill (PID=%d).", static_cast<int>(impl_->process_id));
526 : : #else
527 : : // LCOV_EXCL_START GCOVR_EXCL_START
528 : : CLOG_E("Process: Force to kill (PID=%d).", static_cast<int>(impl_->process_id.load(std::memory_order_acquire)));
529 : : #endif
530 : :
531 : : kill();
532 : :
533 : : read_from_pipes_with_lock();
534 : :
535 : : wait_for_finished(1000);
536 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
537 : : }
538 : : }
539 : :
540 : 184 : cleanup();
541 : 184 : }
542 : :
543 : 4 : Process::State Process::get_state() const { return impl_->state.load(std::memory_order_acquire); }
544 : :
545 : 25 : Process::Error Process::get_error() const { return impl_->error.load(std::memory_order_acquire); }
546 : :
547 : 198 : int Process::get_exit_code() const { return impl_->exit_code.load(std::memory_order_acquire); }
548 : :
549 : 3 : Process::ExitStatus Process::get_exit_status() const { return impl_->exit_status.load(std::memory_order_acquire); }
550 : :
551 : 222 : bool Process::is_running() const { return impl_->state.load(std::memory_order_acquire) == kRunningState; }
552 : :
553 : 18 : int64_t Process::get_process_id() const {
554 [ + + ]: 18 : if (impl_->state.load(std::memory_order_acquire) != kRunningState) {
555 : 3 : return -1;
556 : : }
557 : :
558 : : #ifdef _WIN32
559 : : return static_cast<int64_t>(impl_->process_id);
560 : : #else
561 : 30 : return impl_->process_id.load(std::memory_order_acquire);
562 : : #endif
563 : : }
564 : :
565 : 18 : void Process::set_max_buffer_size(size_t size) {
566 [ + + ]: 18 : const size_t max_size = size > 0 ? size : kDefaultMaxBufferSize;
567 : 18 : impl_->max_buffer_size.store(max_size, std::memory_order_release);
568 : :
569 [ + - ]: 18 : std::lock_guard lock(impl_->buffer_mtx);
570 [ + + - + : 18 : if VUNLIKELY (impl_->stdout_buffer.size() - impl_->stdout_offset > max_size ||
+ + ]
571 : : impl_->stderr_buffer.size() - impl_->stderr_offset > max_size) {
572 [ + - ]: 1 : set_error(kBufferOverflowError);
573 : : }
574 : 18 : }
575 : :
576 : 6 : size_t Process::get_max_buffer_size() const { return impl_->max_buffer_size.load(std::memory_order_acquire); }
577 : :
578 : 69 : void Process::set_environment(const EnvironmentMap& env_map) {
579 [ + - ]: 69 : std::lock_guard lock(impl_->shared_mtx);
580 [ + - ]: 69 : impl_->environment_map = env_map;
581 : 69 : }
582 : :
583 : 1 : Process::EnvironmentMap Process::get_environment() const {
584 [ + - ]: 1 : std::shared_lock lock(impl_->shared_mtx);
585 [ + - ]: 2 : return impl_->environment_map;
586 : 1 : }
587 : :
588 : 123 : void Process::set_process_mode(Mode mode) { impl_->mode.store(mode, std::memory_order_release); }
589 : :
590 : 3 : Process::Mode Process::get_process_mode() const { return impl_->mode.load(std::memory_order_acquire); }
591 : :
592 : 70 : void Process::set_inherit_environment(bool inherit) {
593 : 70 : impl_->inherit_environment.store(inherit, std::memory_order_release);
594 : 70 : }
595 : :
596 : 3 : bool Process::get_inherit_environment() const { return impl_->inherit_environment.load(std::memory_order_acquire); }
597 : :
598 : 4 : void Process::set_working_directory(const std::string& dir) {
599 [ + - ]: 4 : std::lock_guard lock(impl_->shared_mtx);
600 [ + - ]: 4 : impl_->working_directory = dir;
601 : 4 : }
602 : :
603 : 1 : std::string Process::get_working_directory() const {
604 [ + - ]: 1 : std::shared_lock lock(impl_->shared_mtx);
605 [ + - ]: 2 : return impl_->working_directory;
606 : 1 : }
607 : :
608 : 11 : void Process::register_error_callback(ErrorCallback&& callback) {
609 [ + - ]: 11 : std::lock_guard lock(impl_->shared_mtx);
610 : 11 : impl_->error_callback = std::move(callback);
611 : 11 : }
612 : :
613 : 1 : void Process::register_finished_callback(FinishedCallback&& callback) {
614 [ + - ]: 1 : std::lock_guard lock(impl_->shared_mtx);
615 : 1 : impl_->finished_callback = std::move(callback);
616 : 1 : }
617 : :
618 : 4 : void Process::register_ready_read_stdout_callback(ReadyReadCallback&& callback) {
619 [ + - ]: 4 : std::lock_guard lock(impl_->shared_mtx);
620 : 4 : impl_->ready_read_stdout_callback = std::move(callback);
621 : 4 : }
622 : :
623 : 1 : void Process::register_ready_read_stderr_callback(ReadyReadCallback&& callback) {
624 [ + - ]: 1 : std::lock_guard lock(impl_->shared_mtx);
625 : 1 : impl_->ready_read_stderr_callback = std::move(callback);
626 : 1 : }
627 : :
628 : 15 : void Process::register_state_changed_callback(StateChangedCallback&& callback) {
629 [ + - ]: 15 : std::lock_guard lock(impl_->shared_mtx);
630 : 15 : impl_->state_changed_callback = std::move(callback);
631 : 15 : }
632 : :
633 : 181 : void Process::start(const std::string& program, const std::vector<std::string>& arguments) {
634 : 181 : State expected = kNotRunningState;
635 : :
636 [ + + ]: 181 : if VUNLIKELY (!impl_->state.compare_exchange_strong(expected, kStartingState, std::memory_order_acq_rel)) {
637 [ + - ]: 1 : set_error(kStartError);
638 : 1 : return;
639 : : }
640 : :
641 [ + - ]: 180 : cleanup();
642 : :
643 : 180 : impl_->error.store(kNoError, std::memory_order_release);
644 : 180 : impl_->exit_processed.store(false, std::memory_order_release);
645 : 180 : impl_->error_reported.store(false, std::memory_order_release);
646 : 180 : impl_->exit_code.store(-1, std::memory_order_release);
647 : 180 : impl_->exit_status.store(kNormalExitStatus, std::memory_order_release);
648 : 180 : impl_->stdout_closed.store(false, std::memory_order_release);
649 : 180 : impl_->stderr_closed.store(false, std::memory_order_release);
650 : 180 : impl_->forced_termination.store(false, std::memory_order_release);
651 : :
652 : : {
653 [ + - ]: 180 : std::lock_guard lock(impl_->buffer_mtx);
654 : 180 : impl_->stdout_buffer.clear();
655 : 180 : impl_->stderr_buffer.clear();
656 : 180 : impl_->stdout_offset = 0;
657 : 180 : impl_->stderr_offset = 0;
658 : 180 : }
659 : :
660 : 180 : impl_->state_cv.notify_all();
661 : :
662 [ + - ]: 180 : invoke_callbacks_outside_lock(kNoError, false, -1, kNormalExitStatus, kStartingState, true, false, false);
663 : :
664 [ + - ]: 180 : bool pipes_ready = setup_pipes();
665 : 180 : bool success = false;
666 : :
667 [ - + ]: 180 : if VUNLIKELY (!pipes_ready) {
668 : : // LCOV_EXCL_START GCOVR_EXCL_START
669 : : set_error(kStartError);
670 : :
671 : : {
672 : : std::lock_guard lock(impl_->state_mtx);
673 : : impl_->state.store(kNotRunningState, std::memory_order_release);
674 : : }
675 : :
676 : : impl_->state_cv.notify_all();
677 : :
678 : : invoke_callbacks_outside_lock(kStartError, false, -1, kNormalExitStatus,
679 : : impl_->state.load(std::memory_order_acquire), true, false, false);
680 : : return;
681 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
682 : : }
683 : :
684 [ + - ]: 180 : success = start_program(program, arguments);
685 : :
686 [ + + ]: 180 : if VLIKELY (success) {
687 : 172 : bool running_published = false;
688 : :
689 : : {
690 [ + - ]: 172 : std::lock_guard lock(impl_->state_mtx);
691 : :
692 [ + - ]: 172 : if VLIKELY (!impl_->exit_processed.load(std::memory_order_acquire)) {
693 : 172 : impl_->state.store(kRunningState, std::memory_order_release);
694 : 172 : running_published = true;
695 : : }
696 : 172 : }
697 : :
698 : 172 : impl_->state_cv.notify_all();
699 : :
700 [ + - ]: 172 : if VLIKELY (running_published) {
701 [ + - ]: 172 : invoke_callbacks_outside_lock(kNoError, false, -1, kNormalExitStatus, kRunningState, true, false, false);
702 : :
703 [ + - ]: 172 : start_monitor_thread();
704 : : }
705 : : } else {
706 [ + - ]: 8 : cleanup();
707 [ + - ]: 8 : set_error(kStartError);
708 : :
709 : : {
710 [ + - ]: 8 : std::lock_guard lock(impl_->state_mtx);
711 : 8 : impl_->state.store(kNotRunningState, std::memory_order_release);
712 : 8 : }
713 : :
714 : 8 : impl_->state_cv.notify_all();
715 : :
716 [ + - ]: 8 : invoke_callbacks_outside_lock(kStartError, false, -1, kNormalExitStatus,
717 : 8 : impl_->state.load(std::memory_order_acquire), true, false, false);
718 : : }
719 : : }
720 : :
721 : 8 : void Process::start_command(const std::string& command) {
722 [ + + ]: 8 : if VUNLIKELY (command.empty()) {
723 [ + - ]: 1 : set_error(kStartError);
724 : 2 : return;
725 : : }
726 : :
727 : 7 : std::vector<std::string> parts;
728 : 7 : std::string current;
729 : 7 : bool in_dquotes = false;
730 : 7 : bool in_squotes = false;
731 : 7 : bool escape = false;
732 : 7 : bool token_started = false;
733 : :
734 [ + + ]: 232 : for (auto c : command) {
735 [ + + ]: 225 : if (escape) {
736 [ + + + + : 8 : switch (c) {
+ + + + ]
737 : 1 : case 'n':
738 [ + - ]: 1 : current += '\n';
739 : 1 : break;
740 : 1 : case 't':
741 [ + - ]: 1 : current += '\t';
742 : 1 : break;
743 : 1 : case 'r':
744 [ + - ]: 1 : current += '\r';
745 : 1 : break;
746 : 1 : case '\\':
747 [ + - ]: 1 : current += '\\';
748 : 1 : break;
749 : 1 : case '"':
750 [ + - ]: 1 : current += '"';
751 : 1 : break;
752 : 1 : case '\'':
753 [ + - ]: 1 : current += '\'';
754 : 1 : break;
755 : 1 : case ' ':
756 [ + - ]: 1 : current += ' ';
757 : 1 : break;
758 : 1 : default:
759 [ + - ]: 1 : current += '\\';
760 [ + - ]: 1 : current += c;
761 : 1 : break;
762 : : }
763 : 8 : escape = false;
764 : 8 : continue;
765 : : }
766 : :
767 [ + + + + ]: 217 : if (!in_squotes && c == '\\') {
768 : 9 : token_started = true;
769 : 9 : escape = true;
770 : 9 : continue;
771 : : }
772 : :
773 [ + + + + ]: 208 : if (!in_squotes && c == '"') {
774 : 8 : token_started = true;
775 : 8 : in_dquotes = !in_dquotes;
776 : 8 : continue;
777 : : }
778 : :
779 [ + + + + ]: 200 : if (!in_dquotes && c == '\'') {
780 : 6 : token_started = true;
781 : 6 : in_squotes = !in_squotes;
782 : 6 : continue;
783 : : }
784 : :
785 [ + + + + : 194 : if ((c == ' ' || c == '\t') && !in_dquotes && !in_squotes) {
+ + + + ]
786 [ + + ]: 18 : if (token_started) {
787 [ + - ]: 14 : parts.emplace_back(std::move(current));
788 : 14 : current.clear();
789 : 14 : token_started = false;
790 : : }
791 : : } else {
792 : 176 : token_started = true;
793 [ + - ]: 176 : current += c;
794 : : }
795 : : }
796 : :
797 [ + + ]: 7 : if (escape) {
798 [ + - ]: 1 : current += '\\';
799 : : }
800 : :
801 [ + + ]: 7 : if (token_started) {
802 [ + - ]: 6 : parts.emplace_back(std::move(current));
803 : : }
804 : :
805 [ + + ]: 7 : if VUNLIKELY (parts.empty()) {
806 [ + - ]: 1 : set_error(kStartError);
807 : 1 : return;
808 : : }
809 : :
810 [ + - ]: 12 : std::string program = parts[0];
811 [ + - ]: 12 : std::vector<std::string> arguments(parts.begin() + 1, parts.end());
812 : :
813 [ + - ]: 6 : start(program, arguments);
814 [ + + + + ]: 8 : }
815 : :
816 : 51 : bool Process::wait_for_started(int msecs) {
817 : 51 : State current_state = impl_->state.load(std::memory_order_acquire);
818 : :
819 [ + + ]: 51 : if VLIKELY (current_state == kRunningState) {
820 : 46 : return true;
821 : : }
822 : :
823 [ + + ]: 5 : if VUNLIKELY (current_state == kNotRunningState) {
824 [ + + + - ]: 4 : return impl_->exit_processed.load(std::memory_order_acquire) &&
825 : 4 : impl_->error.load(std::memory_order_acquire) != kStartError;
826 : : }
827 : :
828 [ + - ]: 2 : std::unique_lock lock(impl_->state_mtx);
829 : :
830 [ + + ]: 2 : if (msecs < 0) {
831 : 1 : impl_->state_cv.wait(lock, [this]() {
832 : 2 : State state = impl_->state.load(std::memory_order_acquire);
833 : 2 : return state != kStartingState;
834 : : });
835 : : } else {
836 : 1 : auto timeout = std::chrono::milliseconds(msecs);
837 : :
838 [ + - + - ]: 3 : if VUNLIKELY (!impl_->state_cv.wait_for(lock, timeout, [this]() {
839 : : State state = impl_->state.load(std::memory_order_acquire);
840 : : return state != kStartingState;
841 : : })) {
842 : 1 : return false;
843 : : }
844 : : }
845 : :
846 : 1 : return impl_->error.load(std::memory_order_acquire) != kStartError;
847 : 2 : }
848 : :
849 : 180 : bool Process::wait_for_finished(int msecs) {
850 [ + + ]: 180 : if VUNLIKELY (impl_->state.load(std::memory_order_acquire) == kNotRunningState) {
851 : 26 : return true;
852 : : }
853 : :
854 : : #ifdef _WIN32
855 : :
856 : : if (impl_->process == INVALID_HANDLE_VALUE) {
857 : : return true;
858 : : }
859 : :
860 : : DWORD timeout = (msecs < 0) ? INFINITE : static_cast<DWORD>(msecs);
861 : :
862 : : DWORD result = ::WaitForSingleObject(impl_->process, timeout);
863 : :
864 : : if (result == WAIT_OBJECT_0) {
865 : : DWORD exit_code;
866 : :
867 : : if (::GetExitCodeProcess(impl_->process, &exit_code)) {
868 : : read_from_pipes_with_lock();
869 : : handle_process_exit(static_cast<int>(exit_code), kNormalExitStatus);
870 : : }
871 : :
872 : : return true;
873 : : }
874 : :
875 : : return false;
876 : : #else
877 [ + - ]: 154 : std::unique_lock lock(impl_->state_mtx);
878 : :
879 [ + + ]: 154 : if (msecs < 0) {
880 : 3 : impl_->state_cv.wait(lock, [this]() { return impl_->state.load(std::memory_order_acquire) == kNotRunningState; });
881 : :
882 : 1 : return true;
883 : : } else {
884 : 153 : auto timeout = std::chrono::milliseconds(msecs);
885 : :
886 [ + - + + ]: 153 : if (impl_->state_cv.wait_for(
887 : 305 : lock, timeout, [this]() { return impl_->state.load(std::memory_order_acquire) == kNotRunningState; })) {
888 : 150 : return true;
889 : : }
890 : : }
891 : :
892 [ + - ]: 3 : lock.unlock();
893 : :
894 : 3 : const auto pid = static_cast<pid_t>(impl_->process_id.load(std::memory_order_acquire));
895 : :
896 [ + - + - : 3 : if (pid > 0 && !impl_->exit_processed.load(std::memory_order_acquire)) {
+ - ]
897 : 3 : int status = 0;
898 : 3 : pid_t result = 0;
899 : :
900 : : do {
901 [ + - ]: 3 : result = waitpid(pid, &status, WNOHANG);
902 [ - + - - ]: 3 : } while (result < 0 && errno == EINTR);
903 : :
904 [ - + ]: 3 : if (result == pid) {
905 : : // LCOV_EXCL_START GCOVR_EXCL_START
906 : : int exit_code = -1;
907 : : ExitStatus exit_status = kCrashExitStatus;
908 : :
909 : : if VLIKELY (WIFEXITED(status)) {
910 : : exit_code = WEXITSTATUS(status);
911 : : exit_status = kNormalExitStatus;
912 : : } else if (WIFSIGNALED(status)) {
913 : : int sig = WTERMSIG(status);
914 : : exit_code = 128 + sig;
915 : : exit_status = kCrashExitStatus;
916 : : }
917 : :
918 : : read_from_pipes_with_lock();
919 : : handle_process_exit(exit_code, exit_status);
920 : :
921 : : return true;
922 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
923 [ - + - - ]: 3 : } else if (result < 0 && errno == ECHILD) {
924 : : // LCOV_EXCL_START GCOVR_EXCL_START
925 : : std::unique_lock relock(impl_->state_mtx);
926 : : return impl_->state_cv.wait_for(relock, std::chrono::milliseconds(kMonitorLoopDelayMs), [this]() {
927 : : return impl_->state.load(std::memory_order_acquire) == kNotRunningState;
928 : : });
929 : : }
930 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
931 : : }
932 : :
933 : 3 : return impl_->state.load(std::memory_order_acquire) == kNotRunningState;
934 : : #endif
935 : 154 : }
936 : :
937 : 13 : bool Process::wait_for_ready_read(int msecs) {
938 : 13 : auto start_time = std::chrono::steady_clock::now();
939 : : size_t initial_size;
940 : :
941 : : {
942 [ + - ]: 13 : std::lock_guard lock(impl_->buffer_mtx);
943 : 13 : initial_size =
944 : 13 : impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() - impl_->stderr_offset;
945 : 13 : }
946 : :
947 : : #ifdef _WIN32
948 : : while (true) {
949 : : read_from_pipes_with_lock();
950 : :
951 : : {
952 : : std::lock_guard lock(impl_->buffer_mtx);
953 : : size_t current_size =
954 : : impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() - impl_->stderr_offset;
955 : :
956 : : if VLIKELY (current_size > initial_size) {
957 : : return true;
958 : : }
959 : : }
960 : :
961 : : auto elapsed =
962 : : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
963 : :
964 : : if (msecs >= 0 && elapsed >= msecs) {
965 : : return false;
966 : : }
967 : :
968 : : if VUNLIKELY (impl_->state.load(std::memory_order_acquire) == kNotRunningState) {
969 : : std::lock_guard lock2(impl_->buffer_mtx);
970 : : return (impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() - impl_->stderr_offset) >
971 : : initial_size;
972 : : }
973 : :
974 : : DWORD wait_slice = 50;
975 : :
976 : : if (msecs >= 0) {
977 : : wait_slice = static_cast<DWORD>(std::min<int64_t>(msecs - elapsed, 50));
978 : : }
979 : :
980 : : if (impl_->process != INVALID_HANDLE_VALUE) {
981 : : DWORD wait_result = ::WaitForSingleObject(impl_->process, wait_slice);
982 : :
983 : : if (wait_result == WAIT_OBJECT_0) {
984 : : DWORD exit_code = 0;
985 : :
986 : : if (::GetExitCodeProcess(impl_->process, &exit_code)) {
987 : : read_from_pipes_with_lock();
988 : : handle_process_exit(static_cast<int>(exit_code), kNormalExitStatus);
989 : : }
990 : : }
991 : : } else if (wait_slice > 0) {
992 : : std::this_thread::sleep_for(std::chrono::milliseconds(wait_slice));
993 : : }
994 : : }
995 : : #else
996 : : while (true) {
997 : : struct pollfd fds[2];
998 : 25 : int nfds = 0;
999 : :
1000 : : {
1001 [ + + + + : 25 : if (impl_->stdout_pipe[0] >= 0 && !impl_->stdout_closed.load(std::memory_order_acquire)) {
+ + ]
1002 : 11 : fds[nfds].fd = impl_->stdout_pipe[0];
1003 : 11 : fds[nfds].events = POLLIN;
1004 : 11 : fds[nfds].revents = 0;
1005 : 11 : ++nfds;
1006 : : }
1007 : :
1008 [ + + + + : 25 : if (impl_->stderr_pipe[0] >= 0 && !impl_->stderr_closed.load(std::memory_order_acquire)) {
+ + ]
1009 : 11 : fds[nfds].fd = impl_->stderr_pipe[0];
1010 : 11 : fds[nfds].events = POLLIN;
1011 : 11 : fds[nfds].revents = 0;
1012 : 11 : ++nfds;
1013 : : }
1014 : : }
1015 : :
1016 : 25 : int timeout_ms = -1;
1017 : :
1018 [ + + ]: 25 : if (msecs >= 0) {
1019 : : auto elapsed =
1020 [ + - + - ]: 10 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
1021 : :
1022 [ + + ]: 10 : if (elapsed >= msecs) {
1023 [ + - ]: 1 : std::lock_guard lock(impl_->buffer_mtx);
1024 : 1 : return (impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() -
1025 : 1 : impl_->stderr_offset) > initial_size;
1026 : 1 : }
1027 : :
1028 : 9 : timeout_ms = static_cast<int>(msecs - elapsed);
1029 : : }
1030 : :
1031 : 24 : int pr = 0;
1032 : :
1033 [ + + ]: 24 : if (nfds > 0) {
1034 [ + + + - ]: 11 : pr = ::poll(fds, nfds, timeout_ms < 0 ? 50 : std::min(timeout_ms, 50));
1035 : : } else {
1036 : 13 : pr = 0;
1037 : :
1038 [ + + ]: 13 : if (timeout_ms > 0) {
1039 : : {
1040 [ + - ]: 2 : std::lock_guard lock(impl_->buffer_mtx);
1041 [ - + ]: 2 : if (impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() - impl_->stderr_offset >
1042 : : initial_size) {
1043 : 0 : return true;
1044 : : }
1045 [ + - ]: 2 : }
1046 : :
1047 [ + - ]: 2 : std::this_thread::sleep_for(std::chrono::milliseconds(timeout_ms));
1048 [ + - ]: 11 : } else if (impl_->state.load(std::memory_order_acquire) != kNotRunningState) {
1049 [ + - ]: 11 : std::this_thread::sleep_for(std::chrono::milliseconds(50));
1050 : : }
1051 : : }
1052 : :
1053 : : (void)pr;
1054 : :
1055 [ + - ]: 24 : read_from_pipes_with_lock();
1056 : :
1057 : : {
1058 [ + - ]: 24 : std::lock_guard lock(impl_->buffer_mtx);
1059 : : size_t cur =
1060 : 24 : impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() - impl_->stderr_offset;
1061 : :
1062 [ + + ]: 24 : if (cur > initial_size) {
1063 : 7 : return true;
1064 : : }
1065 [ + + ]: 24 : }
1066 : :
1067 [ + + ]: 17 : if VUNLIKELY (impl_->state.load(std::memory_order_acquire) == kNotRunningState) {
1068 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1069 : : }
1070 : :
1071 [ + + ]: 14 : if (msecs >= 0) {
1072 : : auto elapsed =
1073 [ + - + - ]: 2 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
1074 : :
1075 [ + - ]: 2 : if VUNLIKELY (elapsed >= msecs) {
1076 : 2 : return false;
1077 : : }
1078 : : }
1079 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1080 : : #endif
1081 : : }
1082 : :
1083 : 7 : void Process::terminate() {
1084 : : #ifdef _WIN32
1085 : :
1086 : : if VLIKELY (impl_->process != INVALID_HANDLE_VALUE) {
1087 : : if (::WaitForSingleObject(impl_->process, 0) == WAIT_OBJECT_0) {
1088 : : DWORD exit_code_win = 0;
1089 : :
1090 : : if (::GetExitCodeProcess(impl_->process, &exit_code_win)) {
1091 : : read_from_pipes_with_lock();
1092 : : handle_process_exit(static_cast<int>(exit_code_win), kNormalExitStatus);
1093 : : }
1094 : :
1095 : : return;
1096 : : }
1097 : :
1098 : : if (!::TerminateProcess(impl_->process, 1)) {
1099 : : return;
1100 : : }
1101 : :
1102 : : impl_->forced_termination.store(true, std::memory_order_release);
1103 : :
1104 : : DWORD wait_ret = ::WaitForSingleObject(impl_->process, 100);
1105 : :
1106 : : if (wait_ret == WAIT_OBJECT_0) {
1107 : : DWORD exit_code_win;
1108 : :
1109 : : if (::GetExitCodeProcess(impl_->process, &exit_code_win)) {
1110 : : read_from_pipes_with_lock();
1111 : : handle_process_exit(static_cast<int>(exit_code_win), kCrashExitStatus);
1112 : : }
1113 : : }
1114 : : }
1115 : : #else
1116 : :
1117 : 7 : const int64_t pid = impl_->process_id.load(std::memory_order_acquire);
1118 : :
1119 [ + - ]: 7 : if VLIKELY (pid > 0) {
1120 : 7 : ::kill(static_cast<pid_t>(pid), SIGTERM);
1121 : :
1122 [ + + ]: 109 : for (int i = 0; i < 20; ++i) {
1123 : : int status;
1124 [ + - ]: 104 : pid_t result = waitpid(static_cast<pid_t>(pid), &status, WNOHANG);
1125 : :
1126 [ + + ]: 104 : if (result == static_cast<pid_t>(pid)) {
1127 : : // LCOV_EXCL_START GCOVR_EXCL_START
1128 : : int exit_code = -1;
1129 : : ExitStatus exit_status = kCrashExitStatus;
1130 : :
1131 : : if (WIFEXITED(status)) {
1132 : : exit_code = WEXITSTATUS(status);
1133 : : exit_status = kNormalExitStatus;
1134 : : } else if (WIFSIGNALED(status)) {
1135 : : int sig = WTERMSIG(status);
1136 : : exit_code = 128 + sig;
1137 : : exit_status = kCrashExitStatus;
1138 : : }
1139 : :
1140 : : read_from_pipes_with_lock();
1141 : : handle_process_exit(exit_code, exit_status);
1142 : :
1143 : : break;
1144 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1145 : : }
1146 : :
1147 [ + - ]: 102 : std::this_thread::sleep_for(std::chrono::milliseconds(5));
1148 : : }
1149 : : }
1150 : : #endif
1151 : 7 : }
1152 : :
1153 : 13 : void Process::kill() {
1154 : : #ifdef _WIN32
1155 : :
1156 : : if VLIKELY (impl_->process != INVALID_HANDLE_VALUE) {
1157 : : if (::WaitForSingleObject(impl_->process, 0) == WAIT_OBJECT_0) {
1158 : : DWORD exit_code_win = 0;
1159 : :
1160 : : if (::GetExitCodeProcess(impl_->process, &exit_code_win)) {
1161 : : read_from_pipes_with_lock();
1162 : : handle_process_exit(static_cast<int>(exit_code_win), kNormalExitStatus);
1163 : : }
1164 : :
1165 : : return;
1166 : : }
1167 : :
1168 : : if (!::TerminateProcess(impl_->process, 9)) {
1169 : : return;
1170 : : }
1171 : :
1172 : : impl_->forced_termination.store(true, std::memory_order_release);
1173 : :
1174 : : DWORD wait_ret = ::WaitForSingleObject(impl_->process, 100);
1175 : :
1176 : : if (wait_ret == WAIT_OBJECT_0) {
1177 : : DWORD exit_code_win;
1178 : :
1179 : : if (::GetExitCodeProcess(impl_->process, &exit_code_win)) {
1180 : : read_from_pipes_with_lock();
1181 : : handle_process_exit(static_cast<int>(exit_code_win), kCrashExitStatus);
1182 : : }
1183 : : }
1184 : : }
1185 : : #else
1186 : :
1187 : 13 : const int64_t pid = impl_->process_id.load(std::memory_order_acquire);
1188 : :
1189 [ + - ]: 13 : if VLIKELY (pid > 0) {
1190 : 13 : ::kill(static_cast<pid_t>(pid), SIGKILL);
1191 : :
1192 [ + + ]: 140 : for (int i = 0; i < 20; ++i) {
1193 : : int status;
1194 [ + - ]: 134 : pid_t result = waitpid(static_cast<pid_t>(pid), &status, WNOHANG);
1195 : :
1196 [ + + ]: 134 : if (result == static_cast<pid_t>(pid)) {
1197 : 7 : int exit_code = -1;
1198 : 7 : ExitStatus exit_status = kCrashExitStatus;
1199 : :
1200 [ - + ]: 7 : if (WIFEXITED(status)) {
1201 : : exit_code = WEXITSTATUS(status); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1202 : : exit_status = kNormalExitStatus; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1203 [ + - ]: 7 : } else if (WIFSIGNALED(status)) {
1204 : 7 : int sig = WTERMSIG(status);
1205 : 7 : exit_code = 128 + sig;
1206 : 7 : exit_status = kCrashExitStatus;
1207 : : }
1208 : :
1209 [ + - ]: 7 : read_from_pipes_with_lock();
1210 [ + - ]: 7 : handle_process_exit(exit_code, exit_status);
1211 : :
1212 : 7 : break;
1213 : : }
1214 : :
1215 [ + - ]: 127 : std::this_thread::sleep_for(std::chrono::milliseconds(5));
1216 : : }
1217 : : }
1218 : : #endif
1219 : 13 : }
1220 : :
1221 : 20 : void Process::close(bool force_kill_on_timeout) {
1222 [ + + ]: 20 : if (is_running()) {
1223 : 3 : terminate();
1224 : 3 : read_from_pipes_with_lock();
1225 : :
1226 : 3 : bool finished = wait_for_finished(kDefaultWaitTimeoutMs);
1227 : :
1228 [ + + + + : 3 : if VUNLIKELY (!finished && !force_kill_on_timeout) {
+ + ]
1229 : 1 : return;
1230 : : }
1231 : :
1232 [ + + + - : 2 : if VUNLIKELY (!finished && force_kill_on_timeout) {
+ + ]
1233 : 1 : kill();
1234 : 1 : read_from_pipes_with_lock();
1235 : 1 : finished = wait_for_finished(1000);
1236 : :
1237 [ - + ]: 1 : if VUNLIKELY (!finished) {
1238 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1239 : : }
1240 : : }
1241 : : }
1242 : :
1243 : 19 : cleanup();
1244 : : }
1245 : :
1246 : 28 : size_t Process::bytes_available_stdout() const {
1247 [ + - ]: 28 : std::lock_guard lock(impl_->buffer_mtx);
1248 : 56 : return impl_->stdout_buffer.size() - impl_->stdout_offset;
1249 : 28 : }
1250 : :
1251 : 21 : size_t Process::bytes_available_stderr() const {
1252 [ + - ]: 21 : std::lock_guard lock(impl_->buffer_mtx);
1253 : 42 : return impl_->stderr_buffer.size() - impl_->stderr_offset;
1254 : 21 : }
1255 : :
1256 : 6 : bool Process::can_read_line_stdout() const {
1257 [ + - ]: 6 : std::lock_guard lock(impl_->buffer_mtx);
1258 : 12 : return find_newline(impl_->stdout_buffer, impl_->stdout_offset) != std::string::npos;
1259 : 6 : }
1260 : :
1261 : 6 : bool Process::can_read_line_stderr() const {
1262 [ + - ]: 6 : std::lock_guard lock(impl_->buffer_mtx);
1263 : 12 : return find_newline(impl_->stderr_buffer, impl_->stderr_offset) != std::string::npos;
1264 : 6 : }
1265 : :
1266 : 8 : bool Process::read_line_stdout(std::string& line) {
1267 [ + - ]: 8 : std::unique_lock lock(impl_->buffer_mtx);
1268 [ + - ]: 8 : const auto result = read_from_pipes();
1269 : :
1270 [ + + ]: 8 : if (impl_->stdout_buffer.empty()) {
1271 : 1 : line.clear();
1272 : : } else {
1273 : 7 : const size_t newline_pos = find_newline(impl_->stdout_buffer, impl_->stdout_offset);
1274 : : const size_t count =
1275 [ + + ]: 7 : newline_pos != std::string::npos ? newline_pos + 1 : impl_->stdout_buffer.size() - impl_->stdout_offset;
1276 [ + - ]: 7 : line.assign(reinterpret_cast<const char*>(impl_->stdout_buffer.data() + impl_->stdout_offset), count);
1277 : 7 : consume_buffer(impl_->stdout_buffer, impl_->stdout_offset, count);
1278 : : }
1279 : :
1280 : 8 : const bool has_data = !line.empty();
1281 [ + - ]: 8 : lock.unlock();
1282 [ + - ]: 8 : report_read_result(result, false);
1283 : 8 : return has_data;
1284 : 8 : }
1285 : :
1286 : 8 : bool Process::read_line_stderr(std::string& line) {
1287 [ + - ]: 8 : std::unique_lock lock(impl_->buffer_mtx);
1288 [ + - ]: 8 : const auto result = read_from_pipes();
1289 : :
1290 [ + + ]: 8 : if (impl_->stderr_buffer.empty()) {
1291 : 1 : line.clear();
1292 : : } else {
1293 : 7 : const size_t newline_pos = find_newline(impl_->stderr_buffer, impl_->stderr_offset);
1294 : : const size_t count =
1295 [ + + ]: 7 : newline_pos != std::string::npos ? newline_pos + 1 : impl_->stderr_buffer.size() - impl_->stderr_offset;
1296 [ + - ]: 7 : line.assign(reinterpret_cast<const char*>(impl_->stderr_buffer.data() + impl_->stderr_offset), count);
1297 : 7 : consume_buffer(impl_->stderr_buffer, impl_->stderr_offset, count);
1298 : : }
1299 : :
1300 : 8 : const bool has_data = !line.empty();
1301 [ + - ]: 8 : lock.unlock();
1302 [ + - ]: 8 : report_read_result(result, false);
1303 : 8 : return has_data;
1304 : 8 : }
1305 : :
1306 : 11 : size_t Process::read_stdout(std::vector<uint8_t>& buffer, size_t max_size) {
1307 [ + - ]: 11 : std::unique_lock lock(impl_->buffer_mtx);
1308 [ + - ]: 11 : const auto result = read_from_pipes();
1309 : 11 : const size_t to_read = std::min(max_size, impl_->stdout_buffer.size() - impl_->stdout_offset);
1310 : 11 : const auto begin = impl_->stdout_buffer.begin() + impl_->stdout_offset;
1311 [ + - ]: 11 : buffer.assign(begin, begin + to_read);
1312 : 11 : consume_buffer(impl_->stdout_buffer, impl_->stdout_offset, to_read);
1313 : :
1314 [ + - ]: 11 : lock.unlock();
1315 [ + - ]: 11 : report_read_result(result, false);
1316 : 11 : return to_read;
1317 : 11 : }
1318 : :
1319 : 9 : size_t Process::read_stderr(std::vector<uint8_t>& buffer, size_t max_size) {
1320 [ + - ]: 9 : std::unique_lock lock(impl_->buffer_mtx);
1321 [ + - ]: 9 : const auto result = read_from_pipes();
1322 : 9 : const size_t to_read = std::min(max_size, impl_->stderr_buffer.size() - impl_->stderr_offset);
1323 : 9 : const auto begin = impl_->stderr_buffer.begin() + impl_->stderr_offset;
1324 [ + - ]: 9 : buffer.assign(begin, begin + to_read);
1325 : 9 : consume_buffer(impl_->stderr_buffer, impl_->stderr_offset, to_read);
1326 : :
1327 [ + - ]: 9 : lock.unlock();
1328 [ + - ]: 9 : report_read_result(result, false);
1329 : 9 : return to_read;
1330 : 9 : }
1331 : :
1332 : 4 : bool Process::read_all_output(std::vector<uint8_t>& buffer) {
1333 [ + - ]: 4 : std::unique_lock lock(impl_->buffer_mtx);
1334 [ + - ]: 4 : const auto result = read_from_pipes();
1335 : :
1336 [ + - ]: 4 : compact_buffer(impl_->stdout_buffer, impl_->stdout_offset);
1337 : 4 : buffer = std::move(impl_->stdout_buffer);
1338 : 4 : impl_->stdout_buffer.clear();
1339 : :
1340 : 4 : const bool has_data = !buffer.empty();
1341 [ + - ]: 4 : lock.unlock();
1342 [ + - ]: 4 : report_read_result(result, false);
1343 : 4 : return has_data;
1344 : 4 : }
1345 : :
1346 : 5 : bool Process::read_all_error(std::vector<uint8_t>& buffer) {
1347 [ + - ]: 5 : std::unique_lock lock(impl_->buffer_mtx);
1348 [ + - ]: 5 : const auto result = read_from_pipes();
1349 : :
1350 [ + - ]: 5 : compact_buffer(impl_->stderr_buffer, impl_->stderr_offset);
1351 : 5 : buffer = std::move(impl_->stderr_buffer);
1352 : 5 : impl_->stderr_buffer.clear();
1353 : :
1354 : 5 : const bool has_data = !buffer.empty();
1355 [ + - ]: 5 : lock.unlock();
1356 [ + - ]: 5 : report_read_result(result, false);
1357 : 5 : return has_data;
1358 : 5 : }
1359 : :
1360 : 4 : bool Process::read_all(std::vector<uint8_t>& buffer) {
1361 [ + - ]: 4 : std::unique_lock lock(impl_->buffer_mtx);
1362 [ + - ]: 4 : const auto result = read_from_pipes();
1363 : :
1364 [ + - ]: 4 : compact_buffer(impl_->stdout_buffer, impl_->stdout_offset);
1365 : 4 : buffer = std::move(impl_->stdout_buffer);
1366 [ + - ]: 4 : buffer.insert(buffer.end(), impl_->stderr_buffer.begin() + impl_->stderr_offset, impl_->stderr_buffer.end());
1367 : :
1368 : 4 : impl_->stdout_buffer.clear();
1369 : 4 : impl_->stderr_buffer.clear();
1370 : 4 : impl_->stderr_offset = 0;
1371 : :
1372 : 4 : const bool has_data = !buffer.empty();
1373 [ + - ]: 4 : lock.unlock();
1374 [ + - ]: 4 : report_read_result(result, false);
1375 : 4 : return has_data;
1376 : 4 : }
1377 : :
1378 : 29 : bool Process::read_all_output(std::string& str) {
1379 [ + - ]: 29 : std::unique_lock lock(impl_->buffer_mtx);
1380 [ + - ]: 29 : const auto result = read_from_pipes();
1381 : :
1382 [ + - ]: 29 : str.assign(buffer_view(impl_->stdout_buffer, impl_->stdout_offset));
1383 : :
1384 : 29 : bool has_data = !impl_->stdout_buffer.empty();
1385 : 29 : impl_->stdout_buffer.clear();
1386 : 29 : impl_->stdout_offset = 0;
1387 : :
1388 [ + - ]: 29 : lock.unlock();
1389 [ + - ]: 29 : report_read_result(result, false);
1390 : 29 : return has_data;
1391 : 29 : }
1392 : :
1393 : 6 : bool Process::read_all_error(std::string& str) {
1394 [ + - ]: 6 : std::unique_lock lock(impl_->buffer_mtx);
1395 [ + - ]: 6 : const auto result = read_from_pipes();
1396 : :
1397 [ + - ]: 6 : str.assign(buffer_view(impl_->stderr_buffer, impl_->stderr_offset));
1398 : :
1399 : 6 : bool has_data = !impl_->stderr_buffer.empty();
1400 : 6 : impl_->stderr_buffer.clear();
1401 : 6 : impl_->stderr_offset = 0;
1402 : :
1403 [ + - ]: 6 : lock.unlock();
1404 [ + - ]: 6 : report_read_result(result, false);
1405 : 6 : return has_data;
1406 : 6 : }
1407 : :
1408 : 4 : bool Process::read_all(std::string& str) {
1409 [ + - ]: 4 : std::unique_lock lock(impl_->buffer_mtx);
1410 [ + - ]: 4 : const auto result = read_from_pipes();
1411 : :
1412 : 4 : str.clear();
1413 [ + - ]: 4 : str.reserve(impl_->stdout_buffer.size() - impl_->stdout_offset + impl_->stderr_buffer.size() - impl_->stderr_offset);
1414 : :
1415 [ + - ]: 4 : str.append(buffer_view(impl_->stdout_buffer, impl_->stdout_offset));
1416 [ + - ]: 4 : str.append(buffer_view(impl_->stderr_buffer, impl_->stderr_offset));
1417 : :
1418 : 4 : bool has_data = !str.empty();
1419 : :
1420 : 4 : impl_->stdout_buffer.clear();
1421 : 4 : impl_->stderr_buffer.clear();
1422 : 4 : impl_->stdout_offset = 0;
1423 : 4 : impl_->stderr_offset = 0;
1424 : :
1425 [ + - ]: 4 : lock.unlock();
1426 [ + - ]: 4 : report_read_result(result, false);
1427 : 4 : return has_data;
1428 : 4 : }
1429 : :
1430 : 5 : size_t Process::write(const std::vector<uint8_t>& buffer, int timeout_ms) {
1431 [ + + ]: 5 : if VUNLIKELY (impl_->state.load(std::memory_order_acquire) != kRunningState) {
1432 : 1 : return 0;
1433 : : }
1434 : :
1435 [ + + ]: 4 : if VUNLIKELY (buffer.empty()) {
1436 : 1 : return 0;
1437 : : }
1438 : :
1439 : : #ifdef _WIN32
1440 : : auto start_time = std::chrono::steady_clock::now();
1441 : : size_t total = 0;
1442 : : const uint8_t* data = buffer.data();
1443 : : size_t left = buffer.size();
1444 : :
1445 : : while (left > 0) {
1446 : : int remaining_timeout = timeout_ms;
1447 : :
1448 : : if (timeout_ms >= 0) {
1449 : : auto elapsed =
1450 : : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
1451 : :
1452 : : if VUNLIKELY (elapsed >= timeout_ms) {
1453 : : set_error(kTimedOutError);
1454 : : invoke_callbacks_outside_lock(kTimedOutError, false, -1, kNormalExitStatus,
1455 : : impl_->state.load(std::memory_order_acquire), false, false, false);
1456 : : break;
1457 : : }
1458 : :
1459 : : remaining_timeout = static_cast<int>(timeout_ms - elapsed);
1460 : : }
1461 : :
1462 : : const DWORD chunk_size =
1463 : : static_cast<DWORD>(std::min<size_t>(left, static_cast<size_t>(std::numeric_limits<DWORD>::max())));
1464 : : WindowsWriteResult write_result =
1465 : : write_handle_with_timeout(impl_->stdin_write, data, chunk_size, remaining_timeout);
1466 : :
1467 : : if VLIKELY (write_result.ok && write_result.bytes_written > 0) {
1468 : : total += static_cast<size_t>(write_result.bytes_written);
1469 : : data += write_result.bytes_written;
1470 : : left -= static_cast<size_t>(write_result.bytes_written);
1471 : : continue;
1472 : : }
1473 : :
1474 : : set_error(write_result.timed_out ? kTimedOutError : kWriteError);
1475 : :
1476 : : invoke_callbacks_outside_lock(write_result.timed_out ? kTimedOutError : kWriteError, false, -1, kNormalExitStatus,
1477 : : impl_->state.load(std::memory_order_acquire), false, false, false);
1478 : : break;
1479 : : }
1480 : :
1481 : : return total;
1482 : : #else
1483 : 3 : auto start_time = std::chrono::steady_clock::now();
1484 : 3 : size_t total = 0;
1485 : 3 : const uint8_t* data = buffer.data();
1486 : 3 : size_t left = buffer.size();
1487 : :
1488 [ + + ]: 16 : while (left > 0) {
1489 [ + + ]: 14 : if (timeout_ms >= 0) {
1490 : : auto elapsed =
1491 [ + - + - ]: 13 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
1492 : :
1493 [ + + ]: 13 : if (elapsed >= timeout_ms) {
1494 [ + - ]: 1 : set_error(kTimedOutError);
1495 [ + - ]: 1 : invoke_callbacks_outside_lock(kTimedOutError, false, -1, kNormalExitStatus,
1496 : 1 : impl_->state.load(std::memory_order_acquire), false, false, false);
1497 : 1 : break;
1498 : : }
1499 : : }
1500 : :
1501 [ + - ]: 13 : ssize_t written = write_no_sigpipe(impl_->stdin_pipe[1], data, left);
1502 : :
1503 [ + + ]: 13 : if VLIKELY (written > 0) {
1504 : 3 : total += static_cast<size_t>(written);
1505 : 3 : data += written;
1506 : 3 : left -= static_cast<size_t>(written);
1507 : :
1508 : 3 : continue;
1509 : : }
1510 : :
1511 [ + - ]: 10 : if VUNLIKELY (written < 0) {
1512 [ - + ]: 10 : if VLIKELY (errno == EINTR) {
1513 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1514 [ - + - - ]: 10 : } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
1515 [ + - ]: 10 : std::this_thread::sleep_for(std::chrono::milliseconds(1));
1516 : :
1517 : 10 : continue;
1518 : : } else {
1519 : : // LCOV_EXCL_START GCOVR_EXCL_START
1520 : : set_error(kWriteError);
1521 : : invoke_callbacks_outside_lock(kWriteError, false, -1, kNormalExitStatus,
1522 : : impl_->state.load(std::memory_order_acquire), false, false, false);
1523 : : break;
1524 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1525 : : }
1526 : : }
1527 : : }
1528 : :
1529 : 3 : return total;
1530 : : #endif
1531 : : }
1532 : :
1533 : 10 : size_t Process::write(const std::string& str, int timeout_ms) {
1534 [ + + ]: 10 : if VUNLIKELY (impl_->state.load(std::memory_order_acquire) != kRunningState) {
1535 : 1 : return 0;
1536 : : }
1537 : :
1538 [ + + ]: 9 : if VUNLIKELY (str.empty()) {
1539 : 1 : return 0;
1540 : : }
1541 : :
1542 : : #ifdef _WIN32
1543 : : auto start_time = std::chrono::steady_clock::now();
1544 : : size_t total = 0;
1545 : : const char* data = str.data();
1546 : : size_t left = str.size();
1547 : :
1548 : : while (left > 0) {
1549 : : int remaining_timeout = timeout_ms;
1550 : :
1551 : : if (timeout_ms >= 0) {
1552 : : auto elapsed =
1553 : : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
1554 : :
1555 : : if VUNLIKELY (elapsed >= timeout_ms) {
1556 : : set_error(kTimedOutError);
1557 : : invoke_callbacks_outside_lock(kTimedOutError, false, -1, kNormalExitStatus,
1558 : : impl_->state.load(std::memory_order_acquire), false, false, false);
1559 : : break;
1560 : : }
1561 : :
1562 : : remaining_timeout = static_cast<int>(timeout_ms - elapsed);
1563 : : }
1564 : :
1565 : : const DWORD chunk_size =
1566 : : static_cast<DWORD>(std::min<size_t>(left, static_cast<size_t>(std::numeric_limits<DWORD>::max())));
1567 : : WindowsWriteResult write_result =
1568 : : write_handle_with_timeout(impl_->stdin_write, data, chunk_size, remaining_timeout);
1569 : :
1570 : : if VLIKELY (write_result.ok && write_result.bytes_written > 0) {
1571 : : total += static_cast<size_t>(write_result.bytes_written);
1572 : : data += write_result.bytes_written;
1573 : : left -= static_cast<size_t>(write_result.bytes_written);
1574 : : continue;
1575 : : }
1576 : :
1577 : : set_error(write_result.timed_out ? kTimedOutError : kWriteError);
1578 : :
1579 : : invoke_callbacks_outside_lock(write_result.timed_out ? kTimedOutError : kWriteError, false, -1, kNormalExitStatus,
1580 : : impl_->state.load(std::memory_order_acquire), false, false, false);
1581 : :
1582 : : break;
1583 : : }
1584 : :
1585 : : return total;
1586 : : #else
1587 : 8 : auto start_time = std::chrono::steady_clock::now();
1588 : 8 : size_t total = 0;
1589 : 8 : const char* data = str.data();
1590 : 8 : size_t left = str.size();
1591 : :
1592 [ + + ]: 25 : while (left > 0) {
1593 [ + + ]: 19 : if (timeout_ms >= 0) {
1594 : : auto elapsed =
1595 [ + - + - ]: 18 : std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time).count();
1596 : :
1597 [ + + ]: 18 : if (elapsed >= timeout_ms) {
1598 [ + - ]: 1 : set_error(kTimedOutError);
1599 [ + - ]: 1 : invoke_callbacks_outside_lock(kTimedOutError, false, -1, kNormalExitStatus,
1600 : 1 : impl_->state.load(std::memory_order_acquire), false, false, false);
1601 : :
1602 : 1 : break;
1603 : : }
1604 : : }
1605 : :
1606 [ + - ]: 18 : ssize_t written = write_no_sigpipe(impl_->stdin_pipe[1], data, left);
1607 : :
1608 [ + + ]: 18 : if VLIKELY (written > 0) {
1609 : 7 : total += static_cast<size_t>(written);
1610 : 7 : data += written;
1611 : 7 : left -= static_cast<size_t>(written);
1612 : :
1613 : 7 : continue;
1614 : : }
1615 : :
1616 [ + - ]: 11 : if VUNLIKELY (written < 0) {
1617 [ - + ]: 11 : if VLIKELY (errno == EINTR) {
1618 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1619 [ + + - + ]: 11 : } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
1620 [ + - ]: 10 : std::this_thread::sleep_for(std::chrono::milliseconds(1));
1621 : :
1622 : 10 : continue;
1623 : : } else {
1624 [ + - ]: 1 : set_error(kWriteError);
1625 [ + - ]: 1 : invoke_callbacks_outside_lock(kWriteError, false, -1, kNormalExitStatus,
1626 : 1 : impl_->state.load(std::memory_order_acquire), false, false, false);
1627 : :
1628 : 1 : break;
1629 : : }
1630 : : }
1631 : : }
1632 : :
1633 : 8 : return total;
1634 : : #endif
1635 : : }
1636 : :
1637 : 10 : void Process::close_write_channel() {
1638 : : #ifdef _WIN32
1639 : :
1640 : : if VLIKELY (impl_->stdin_write != INVALID_HANDLE_VALUE) {
1641 : : close_handle_if_valid(impl_->stdin_write);
1642 : : }
1643 : : #else
1644 : :
1645 [ + - ]: 10 : if VLIKELY (impl_->stdin_pipe[1] >= 0) {
1646 : 10 : ::close(impl_->stdin_pipe[1]);
1647 : 10 : impl_->stdin_pipe[1] = -1;
1648 : : }
1649 : : #endif
1650 : 10 : }
1651 : :
1652 : 26 : int Process::execute(const std::string& program, const std::vector<std::string>& arguments, int timeout_ms) {
1653 [ + - ]: 26 : Process process;
1654 [ + - ]: 26 : process.start(program, arguments);
1655 : :
1656 [ + - + + ]: 26 : if VUNLIKELY (!process.wait_for_started(5000)) {
1657 : 1 : return -1;
1658 : : }
1659 : :
1660 [ + - + + ]: 25 : if VUNLIKELY (!process.wait_for_finished(timeout_ms)) {
1661 [ + - ]: 1 : process.terminate();
1662 [ + - ]: 1 : process.wait_for_finished(1000);
1663 : :
1664 : 1 : return -1;
1665 : : }
1666 : :
1667 [ + - ]: 24 : return process.get_exit_code();
1668 : 26 : }
1669 : :
1670 : 3 : bool Process::start_detached(const std::string& program, const std::vector<std::string>& arguments) {
1671 : : #ifdef _WIN32
1672 : : STARTUPINFOW si;
1673 : : PROCESS_INFORMATION pi;
1674 : :
1675 : : ::ZeroMemory(&si, sizeof(si));
1676 : : si.cb = sizeof(si);
1677 : : ::ZeroMemory(&pi, sizeof(pi));
1678 : :
1679 : : std::wstring program_wide = Helpers::string_to_wstring(program);
1680 : : std::vector<std::wstring> arguments_wide;
1681 : : arguments_wide.reserve(arguments.size());
1682 : :
1683 : : for (const auto& arg : arguments) {
1684 : : arguments_wide.emplace_back(Helpers::string_to_wstring(arg));
1685 : : }
1686 : :
1687 : : std::wstring cmd_line = build_command_line(program_wide, arguments_wide);
1688 : : std::vector<wchar_t> cmd_line_buf(cmd_line.begin(), cmd_line.end());
1689 : : cmd_line_buf.emplace_back(L'\0');
1690 : :
1691 : : BOOL result = ::CreateProcessW(nullptr, cmd_line_buf.data(), nullptr, nullptr, FALSE,
1692 : : DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT, nullptr,
1693 : : nullptr, &si, &pi);
1694 : :
1695 : : if VLIKELY (result) {
1696 : : ::CloseHandle(pi.hProcess);
1697 : : ::CloseHandle(pi.hThread);
1698 : :
1699 : : return true;
1700 : : }
1701 : :
1702 : : return false;
1703 : : #else
1704 : : int exec_status_pipe[2];
1705 : :
1706 : : ssize_t written;
1707 : :
1708 [ - + ]: 3 : if (pipe(exec_status_pipe) != 0) {
1709 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1710 : : }
1711 : :
1712 [ + - ]: 3 : fcntl(exec_status_pipe[1], F_SETFD, FD_CLOEXEC);
1713 : :
1714 : 3 : pid_t pid = fork();
1715 : :
1716 [ - + ]: 3 : if VUNLIKELY (pid < 0) {
1717 : : ::close(exec_status_pipe[0]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1718 : : ::close(exec_status_pipe[1]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1719 : :
1720 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1721 : : }
1722 : :
1723 [ - + ]: 3 : if VUNLIKELY (pid == 0) {
1724 : : ::close(exec_status_pipe[0]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1725 : :
1726 : : pid_t pid2 = fork(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1727 : :
1728 [ - + ]: 3 : if VUNLIKELY (pid2 < 0) {
1729 : : int error_code = errno; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1730 : :
1731 : : written = ::write(exec_status_pipe[1], &error_code, sizeof(error_code)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1732 : :
1733 : : (void)written;
1734 : :
1735 : : ::close(exec_status_pipe[1]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1736 : :
1737 : : _exit(kExecFailedExitCode); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1738 : : }
1739 : :
1740 [ - + ]: 3 : if VLIKELY (pid2 > 0) {
1741 : : ::close(exec_status_pipe[1]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1742 : : _exit(0); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1743 : : }
1744 : :
1745 : 3 : setsid();
1746 : :
1747 [ + - ]: 3 : int devnull = open("/dev/null", O_RDWR);
1748 : :
1749 [ + - ]: 3 : if VLIKELY (devnull >= 0) {
1750 : 3 : dup2(devnull, STDIN_FILENO);
1751 : 3 : dup2(devnull, STDOUT_FILENO);
1752 : 3 : dup2(devnull, STDERR_FILENO);
1753 : :
1754 [ + - ]: 3 : if (devnull > STDERR_FILENO) {
1755 [ + - ]: 3 : ::close(devnull);
1756 : : }
1757 : : }
1758 : :
1759 [ + - ]: 3 : close_child_descriptors(exec_status_pipe[1]);
1760 : :
1761 : 3 : std::vector<std::string> args_storage;
1762 [ + - ]: 3 : args_storage.reserve(arguments.size() + 2);
1763 [ + - ]: 3 : args_storage.emplace_back(program);
1764 [ + - ]: 3 : args_storage.insert(args_storage.end(), arguments.begin(), arguments.end());
1765 : :
1766 : 3 : std::vector<char*> args;
1767 [ + - ]: 3 : args.reserve(args_storage.size() + 1);
1768 : :
1769 [ + + ]: 7 : for (auto& arg : args_storage) {
1770 [ + - ]: 4 : args.emplace_back(const_cast<char*>(arg.c_str()));
1771 : : }
1772 : :
1773 [ + - ]: 3 : args.emplace_back(nullptr);
1774 : :
1775 : 3 : execvp(program.c_str(), args.data());
1776 : :
1777 : 3 : int error_code = errno;
1778 : :
1779 [ - - ]: 3 : written = ::write(exec_status_pipe[1], &error_code, sizeof(error_code));
1780 : :
1781 : : (void)written;
1782 : :
1783 : : ::close(exec_status_pipe[1]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1784 : :
1785 : : _exit(kExecFailedExitCode); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1786 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1787 : :
1788 [ + - ]: 3 : ::close(exec_status_pipe[1]);
1789 : :
1790 : 3 : int status = 0;
1791 : : pid_t result;
1792 : :
1793 : : do {
1794 [ + - ]: 3 : result = waitpid(pid, &status, 0);
1795 [ - + - - ]: 3 : } while (result < 0 && errno == EINTR);
1796 : :
1797 [ - + ]: 3 : if VUNLIKELY (result < 0) {
1798 : : ::close(exec_status_pipe[0]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1799 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1800 : : }
1801 : :
1802 [ + - - + : 3 : if VUNLIKELY (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- + ]
1803 : : ::close(exec_status_pipe[0]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1804 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1805 : : }
1806 : :
1807 : 3 : int error_code = 0;
1808 : 3 : ssize_t n = 0;
1809 : :
1810 : : do {
1811 [ + - ]: 3 : n = ::read(exec_status_pipe[0], &error_code, sizeof(error_code));
1812 [ - + - - ]: 3 : } while (n < 0 && errno == EINTR);
1813 : :
1814 [ + - ]: 3 : ::close(exec_status_pipe[0]);
1815 : :
1816 [ + + ]: 3 : if (n > 0) {
1817 : 1 : errno = error_code;
1818 : 1 : return false;
1819 : : }
1820 : :
1821 : 2 : return true;
1822 : : #endif
1823 : : }
1824 : :
1825 : 172 : void Process::set_state(State state) {
1826 : : State old_state;
1827 : :
1828 : : {
1829 [ + - ]: 172 : std::lock_guard lock(impl_->state_mtx);
1830 : 172 : old_state = impl_->state.exchange(state, std::memory_order_acq_rel);
1831 : 172 : }
1832 : :
1833 [ + - ]: 172 : if (old_state != state) {
1834 : 172 : impl_->state_cv.notify_all();
1835 : : }
1836 : 172 : }
1837 : :
1838 : 27 : void Process::set_error(Error error) { impl_->error.store(error, std::memory_order_release); }
1839 : :
1840 : 391 : void Process::cleanup() {
1841 : 391 : stop_monitor_thread();
1842 : :
1843 : : #ifdef _WIN32
1844 : :
1845 : : if (impl_->process != INVALID_HANDLE_VALUE && !impl_->exit_processed.load(std::memory_order_acquire)) {
1846 : : DWORD wait_ret = ::WaitForSingleObject(impl_->process, 0);
1847 : :
1848 : : if (wait_ret == WAIT_OBJECT_0) {
1849 : : DWORD exit_code_win;
1850 : :
1851 : : if (::GetExitCodeProcess(impl_->process, &exit_code_win)) {
1852 : : read_from_pipes_with_lock();
1853 : : handle_process_exit(static_cast<int>(exit_code_win), kNormalExitStatus);
1854 : : }
1855 : : }
1856 : : }
1857 : :
1858 : : if (impl_->process != INVALID_HANDLE_VALUE) {
1859 : : ::CloseHandle(impl_->process);
1860 : : impl_->process = INVALID_HANDLE_VALUE;
1861 : : }
1862 : :
1863 : : if (impl_->thread != INVALID_HANDLE_VALUE) {
1864 : : ::CloseHandle(impl_->thread);
1865 : : impl_->thread = INVALID_HANDLE_VALUE;
1866 : : }
1867 : :
1868 : : close_handle_if_valid(impl_->stdin_write);
1869 : : close_handle_if_valid(impl_->stdin_read);
1870 : : close_handle_if_valid(impl_->stdout_read);
1871 : :
1872 : : const HANDLE stdout_write = impl_->stdout_write;
1873 : : close_handle_if_valid(impl_->stdout_write);
1874 : :
1875 : : close_handle_if_valid(impl_->stderr_read);
1876 : :
1877 : : if (impl_->stderr_write == stdout_write) {
1878 : : impl_->stderr_write = INVALID_HANDLE_VALUE;
1879 : : }
1880 : :
1881 : : close_handle_if_valid(impl_->stderr_write);
1882 : :
1883 : : impl_->process_id = 0;
1884 : : #else
1885 : :
1886 : 391 : const int64_t pid = impl_->process_id.load(std::memory_order_acquire);
1887 : :
1888 [ - + - - : 391 : if (pid > 0 && !impl_->exit_processed.load(std::memory_order_acquire)) {
- + ]
1889 : : int status;
1890 : : pid_t r;
1891 : :
1892 : : do {
1893 : : r = waitpid(static_cast<pid_t>(pid), &status, WNOHANG); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1894 : : } while (r < 0 && errno == EINTR); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1895 : : }
1896 : :
1897 [ - + ]: 391 : if (impl_->stdin_pipe[0] >= 0) {
1898 : : ::close(impl_->stdin_pipe[0]); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1899 : : impl_->stdin_pipe[0] = -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1900 : : }
1901 : :
1902 [ + + ]: 391 : if (impl_->stdin_pipe[1] >= 0) {
1903 : 170 : ::close(impl_->stdin_pipe[1]);
1904 : 170 : impl_->stdin_pipe[1] = -1;
1905 : : }
1906 : :
1907 [ + + ]: 391 : if (impl_->stdout_pipe[0] >= 0) {
1908 : 98 : ::close(impl_->stdout_pipe[0]);
1909 : 98 : impl_->stdout_pipe[0] = -1;
1910 : : }
1911 : :
1912 [ - + ]: 391 : if (impl_->stdout_pipe[1] >= 0) {
1913 : : // LCOV_EXCL_START GCOVR_EXCL_START
1914 : : const int stdout_write = impl_->stdout_pipe[1];
1915 : : ::close(impl_->stdout_pipe[1]);
1916 : : impl_->stdout_pipe[1] = -1;
1917 : :
1918 : : if (impl_->stderr_pipe[1] == stdout_write) {
1919 : : impl_->stderr_pipe[1] = -1;
1920 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1921 : : }
1922 : : }
1923 : :
1924 [ + + ]: 391 : if (impl_->stderr_pipe[0] >= 0) {
1925 : 97 : ::close(impl_->stderr_pipe[0]);
1926 : 97 : impl_->stderr_pipe[0] = -1;
1927 : : }
1928 : :
1929 [ - + ]: 391 : if (impl_->stderr_pipe[1] >= 0) {
1930 : 0 : ::close(impl_->stderr_pipe[1]);
1931 : 0 : impl_->stderr_pipe[1] = -1;
1932 : : }
1933 : :
1934 : 391 : impl_->process_id.store(-1, std::memory_order_release);
1935 : : #endif
1936 : :
1937 : 391 : impl_->exit_processed.store(false, std::memory_order_release);
1938 : 391 : impl_->error_reported.store(false, std::memory_order_release);
1939 : 391 : impl_->stdout_closed.store(false, std::memory_order_release);
1940 : 391 : impl_->stderr_closed.store(false, std::memory_order_release);
1941 : 391 : }
1942 : :
1943 : 718 : Process::ReadResult Process::read_from_pipes() {
1944 : 718 : ReadResult result;
1945 : 718 : size_t max_buffer_size = impl_->max_buffer_size.load(std::memory_order_acquire);
1946 : :
1947 : 82 : auto append_to_buffer = [&result, max_buffer_size](std::vector<uint8_t>& target, size_t& offset, const char* data,
1948 : 186 : size_t bytes, bool& has_data) {
1949 : 82 : const size_t unread = target.size() - offset;
1950 : :
1951 [ + + ]: 82 : if VUNLIKELY (unread >= max_buffer_size) {
1952 [ + - ]: 1 : if (bytes > 0U) {
1953 : 1 : result.truncated = true;
1954 : : }
1955 : :
1956 : 1 : return;
1957 : : }
1958 : :
1959 : 81 : const size_t remaining = max_buffer_size - unread;
1960 : 81 : const size_t bytes_to_store = std::min(remaining, bytes);
1961 : :
1962 [ + - ]: 81 : if (bytes_to_store > 0U) {
1963 [ + + + + ]: 82 : if (offset > 0 &&
1964 [ - + - - ]: 1 : (bytes_to_store > target.capacity() - target.size() || target.size() > max_buffer_size - bytes_to_store)) {
1965 [ + - ]: 1 : compact_buffer(target, offset);
1966 : : }
1967 : :
1968 [ + - ]: 81 : target.insert(target.end(), data, data + bytes_to_store);
1969 : 81 : has_data = true;
1970 : : }
1971 : :
1972 [ + + ]: 81 : if VUNLIKELY (bytes_to_store < bytes) {
1973 : 22 : result.truncated = true;
1974 : : }
1975 : 718 : };
1976 : :
1977 : : #ifdef _WIN32
1978 : : char buffer[8192];
1979 : : auto drain_pipe = [&](HANDLE handle, std::vector<uint8_t>& target, size_t& offset, std::atomic<bool>& closed,
1980 : : bool& has_data) {
1981 : : while (handle != INVALID_HANDLE_VALUE && !closed.load(std::memory_order_acquire)) {
1982 : : DWORD available = 0;
1983 : :
1984 : : if (!::PeekNamedPipe(handle, nullptr, 0, nullptr, &available, nullptr)) {
1985 : : DWORD error = ::GetLastError();
1986 : :
1987 : : if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
1988 : : closed.store(true, std::memory_order_release);
1989 : : } else {
1990 : : set_error(kReadError);
1991 : : result.read_error = true;
1992 : : }
1993 : :
1994 : : break;
1995 : : }
1996 : :
1997 : : if (available == 0) {
1998 : : break;
1999 : : }
2000 : :
2001 : : const DWORD to_read = static_cast<DWORD>(std::min<size_t>(sizeof(buffer), static_cast<size_t>(available)));
2002 : : DWORD bytes_read = 0;
2003 : :
2004 : : if (!::ReadFile(handle, buffer, to_read, &bytes_read, nullptr)) {
2005 : : DWORD error = ::GetLastError();
2006 : :
2007 : : if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
2008 : : closed.store(true, std::memory_order_release);
2009 : : } else {
2010 : : set_error(kReadError);
2011 : : result.read_error = true;
2012 : : }
2013 : :
2014 : : break;
2015 : : }
2016 : :
2017 : : if (bytes_read == 0) {
2018 : : closed.store(true, std::memory_order_release);
2019 : : break;
2020 : : }
2021 : :
2022 : : append_to_buffer(target, offset, buffer, bytes_read, has_data);
2023 : : }
2024 : : };
2025 : :
2026 : : drain_pipe(impl_->stdout_read, impl_->stdout_buffer, impl_->stdout_offset, impl_->stdout_closed,
2027 : : result.has_stdout_data);
2028 : : drain_pipe(impl_->stderr_read, impl_->stderr_buffer, impl_->stderr_offset, impl_->stderr_closed,
2029 : : result.has_stderr_data);
2030 : : #else
2031 : : char buffer[8192];
2032 : : ssize_t bytes_read;
2033 : :
2034 [ + + + + : 718 : if (impl_->stdout_pipe[0] >= 0 && !impl_->stdout_closed.load(std::memory_order_acquire)) {
+ + ]
2035 : : while (true) {
2036 : 57 : bytes_read =
2037 [ + - ]: 337 : ::read(impl_->stdout_pipe[0], buffer, sizeof(buffer)); // NOLINT(clang-analyzer-unix.BlockInCriticalSection)
2038 : :
2039 [ + + ]: 337 : if VLIKELY (bytes_read > 0) {
2040 [ + - ]: 57 : append_to_buffer(impl_->stdout_buffer, impl_->stdout_offset, buffer, static_cast<size_t>(bytes_read),
2041 : : result.has_stdout_data);
2042 [ + + ]: 280 : } else if (bytes_read == 0) {
2043 : 97 : impl_->stdout_closed.store(true, std::memory_order_release);
2044 : 97 : break;
2045 : : } else {
2046 [ - + - - : 183 : if VLIKELY (errno == EAGAIN || errno == EWOULDBLOCK) {
+ - ]
2047 : 183 : break;
2048 [ # # ]: 0 : } else if VLIKELY (errno == EINTR) {
2049 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2050 : : } else {
2051 [ # # ]: 0 : set_error(kReadError);
2052 : 0 : result.read_error = true;
2053 : 0 : break;
2054 : : }
2055 : : }
2056 : : }
2057 : : }
2058 : :
2059 [ + + + + : 718 : if (impl_->stderr_pipe[0] >= 0 && !impl_->stderr_closed.load(std::memory_order_acquire)) {
+ + ]
2060 : : while (true) {
2061 : 25 : bytes_read =
2062 [ + - ]: 298 : ::read(impl_->stderr_pipe[0], buffer, sizeof(buffer)); // NOLINT(clang-analyzer-unix.BlockInCriticalSection)
2063 : :
2064 [ + + ]: 298 : if VLIKELY (bytes_read > 0) {
2065 [ + - ]: 25 : append_to_buffer(impl_->stderr_buffer, impl_->stderr_offset, buffer, static_cast<size_t>(bytes_read),
2066 : : result.has_stderr_data);
2067 [ + + ]: 273 : } else if (bytes_read == 0) {
2068 : 96 : impl_->stderr_closed.store(true, std::memory_order_release);
2069 : 96 : break;
2070 : : } else {
2071 [ - + - - : 177 : if VLIKELY (errno == EAGAIN || errno == EWOULDBLOCK) {
+ - ]
2072 : 177 : break;
2073 [ # # ]: 0 : } else if VLIKELY (errno == EINTR) {
2074 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2075 : : } else {
2076 [ # # ]: 0 : set_error(kReadError);
2077 : 0 : result.read_error = true;
2078 : 0 : break;
2079 : : }
2080 : : }
2081 : : }
2082 : : }
2083 : : #endif
2084 : :
2085 : 718 : return result;
2086 : : }
2087 : :
2088 : 718 : void Process::report_read_result(const ReadResult& result, bool notify_data) {
2089 [ + + + + : 718 : if (notify_data && (result.has_stdout_data || result.has_stderr_data)) {
+ + ]
2090 : 53 : invoke_callbacks_outside_lock(kNoError, false, -1, kNormalExitStatus, impl_->state.load(std::memory_order_acquire),
2091 : 53 : false, result.has_stdout_data, result.has_stderr_data);
2092 : : }
2093 : :
2094 [ - + ]: 718 : if VUNLIKELY (result.read_error) {
2095 : : invoke_callbacks_outside_lock(kReadError, false, -1, kNormalExitStatus, // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2096 : 0 : impl_->state.load(std::memory_order_acquire), false,
2097 : : false, // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2098 : : false); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2099 : : }
2100 : :
2101 [ + + ]: 718 : if VUNLIKELY (result.truncated) {
2102 : 12 : set_error(kBufferOverflowError);
2103 : 12 : invoke_callbacks_outside_lock(kBufferOverflowError, false, -1, kNormalExitStatus,
2104 : 12 : impl_->state.load(std::memory_order_acquire), false, false, false);
2105 : : }
2106 : 718 : }
2107 : :
2108 : 630 : void Process::read_from_pipes_with_lock() {
2109 : 630 : ReadResult result;
2110 : :
2111 : : {
2112 [ + - ]: 630 : std::lock_guard lock(impl_->buffer_mtx);
2113 [ + - ]: 630 : result = read_from_pipes();
2114 : 630 : }
2115 : :
2116 [ + - ]: 630 : report_read_result(result);
2117 : 630 : }
2118 : :
2119 : 180 : bool Process::setup_pipes() {
2120 : 180 : Mode mode = impl_->mode.load(std::memory_order_acquire);
2121 : :
2122 [ + + + + : 180 : if (mode == kForwardedMode || mode == kForwardedOutputMode || mode == kForwardedErrorMode) {
+ + ]
2123 : : #ifdef _WIN32
2124 : : SECURITY_ATTRIBUTES sa;
2125 : : sa.nLength = sizeof(SECURITY_ATTRIBUTES);
2126 : : sa.bInheritHandle = TRUE;
2127 : : sa.lpSecurityDescriptor = nullptr;
2128 : :
2129 : : if VUNLIKELY (!::CreatePipe(&impl_->stdin_read, &impl_->stdin_write, &sa, 0)) {
2130 : : set_error(kStartError);
2131 : : return false;
2132 : : }
2133 : :
2134 : : if VUNLIKELY (!::SetHandleInformation(impl_->stdin_write, HANDLE_FLAG_INHERIT, 0) ||
2135 : : !::SetHandleInformation(impl_->stdin_read, HANDLE_FLAG_INHERIT, 0)) {
2136 : : close_handle_if_valid(impl_->stdin_read);
2137 : : close_handle_if_valid(impl_->stdin_write);
2138 : :
2139 : : set_error(kStartError);
2140 : :
2141 : : return false;
2142 : : }
2143 : :
2144 : : auto close_forwarded_handles = [this]() {
2145 : : close_handle_if_valid(impl_->stdin_read);
2146 : : close_handle_if_valid(impl_->stdin_write);
2147 : : close_handle_if_valid(impl_->stdout_read);
2148 : : close_handle_if_valid(impl_->stdout_write);
2149 : : close_handle_if_valid(impl_->stderr_read);
2150 : : close_handle_if_valid(impl_->stderr_write);
2151 : : };
2152 : :
2153 : : bool capture_stdout = mode != kForwardedMode && mode != kForwardedOutputMode;
2154 : : bool capture_stderr = mode != kForwardedMode && mode != kForwardedErrorMode;
2155 : :
2156 : : if (capture_stdout) {
2157 : : if VUNLIKELY (!::CreatePipe(&impl_->stdout_read, &impl_->stdout_write, &sa, 0)) {
2158 : : close_forwarded_handles();
2159 : : set_error(kStartError);
2160 : : return false;
2161 : : }
2162 : :
2163 : : if VUNLIKELY (!::SetHandleInformation(impl_->stdout_read, HANDLE_FLAG_INHERIT, 0) ||
2164 : : !::SetHandleInformation(impl_->stdout_write, HANDLE_FLAG_INHERIT, 0)) {
2165 : : close_forwarded_handles();
2166 : : set_error(kStartError);
2167 : : return false;
2168 : : }
2169 : : } else {
2170 : : impl_->stdout_closed.store(true, std::memory_order_release);
2171 : : }
2172 : :
2173 : : if (capture_stderr) {
2174 : : if VUNLIKELY (!::CreatePipe(&impl_->stderr_read, &impl_->stderr_write, &sa, 0)) {
2175 : : close_forwarded_handles();
2176 : : set_error(kStartError);
2177 : : return false;
2178 : : }
2179 : :
2180 : : if VUNLIKELY (!::SetHandleInformation(impl_->stderr_read, HANDLE_FLAG_INHERIT, 0) ||
2181 : : !::SetHandleInformation(impl_->stderr_write, HANDLE_FLAG_INHERIT, 0)) {
2182 : : close_forwarded_handles();
2183 : : set_error(kStartError);
2184 : : return false;
2185 : : }
2186 : : } else {
2187 : : impl_->stderr_closed.store(true, std::memory_order_release);
2188 : : }
2189 : : #else
2190 : :
2191 [ - + ]: 83 : if VUNLIKELY (pipe(impl_->stdin_pipe) != 0) {
2192 : : set_error(kStartError); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2193 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2194 : : }
2195 : :
2196 : 83 : fcntl(impl_->stdin_pipe[1], F_SETFL, O_NONBLOCK);
2197 : :
2198 [ + + + + ]: 83 : bool capture_stdout = mode != kForwardedMode && mode != kForwardedOutputMode;
2199 [ + + + + ]: 83 : bool capture_stderr = mode != kForwardedMode && mode != kForwardedErrorMode;
2200 : :
2201 [ + + ]: 83 : if (capture_stdout) {
2202 [ - + ]: 1 : if VUNLIKELY (pipe(impl_->stdout_pipe) != 0) {
2203 : : // LCOV_EXCL_START GCOVR_EXCL_START
2204 : : ::close(impl_->stdin_pipe[0]);
2205 : : ::close(impl_->stdin_pipe[1]);
2206 : : impl_->stdin_pipe[0] = -1;
2207 : : impl_->stdin_pipe[1] = -1;
2208 : : set_error(kStartError);
2209 : : return false;
2210 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2211 : : }
2212 : :
2213 : 1 : fcntl(impl_->stdout_pipe[0], F_SETFL, O_NONBLOCK);
2214 : : } else {
2215 : 82 : impl_->stdout_closed.store(true, std::memory_order_release);
2216 : : }
2217 : :
2218 [ + + ]: 83 : if (capture_stderr) {
2219 [ - + ]: 1 : if VUNLIKELY (pipe(impl_->stderr_pipe) != 0) {
2220 : : // LCOV_EXCL_START GCOVR_EXCL_START
2221 : : ::close(impl_->stdin_pipe[0]);
2222 : : ::close(impl_->stdin_pipe[1]);
2223 : : impl_->stdin_pipe[0] = -1;
2224 : : impl_->stdin_pipe[1] = -1;
2225 : :
2226 : : if (impl_->stdout_pipe[0] >= 0) {
2227 : : ::close(impl_->stdout_pipe[0]);
2228 : : impl_->stdout_pipe[0] = -1;
2229 : : }
2230 : :
2231 : : if (impl_->stdout_pipe[1] >= 0) {
2232 : : ::close(impl_->stdout_pipe[1]);
2233 : : impl_->stdout_pipe[1] = -1;
2234 : : }
2235 : :
2236 : : set_error(kStartError);
2237 : : return false;
2238 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2239 : : }
2240 : :
2241 : 1 : fcntl(impl_->stderr_pipe[0], F_SETFL, O_NONBLOCK);
2242 : : } else {
2243 : 82 : impl_->stderr_closed.store(true, std::memory_order_release);
2244 : : }
2245 : : #endif
2246 : :
2247 : 83 : return true;
2248 : : }
2249 : :
2250 : : #ifdef _WIN32
2251 : : SECURITY_ATTRIBUTES sa;
2252 : : sa.nLength = sizeof(SECURITY_ATTRIBUTES);
2253 : : sa.bInheritHandle = TRUE;
2254 : : sa.lpSecurityDescriptor = nullptr;
2255 : :
2256 : : if VUNLIKELY (!::CreatePipe(&impl_->stdin_read, &impl_->stdin_write, &sa, 0)) {
2257 : : set_error(kStartError);
2258 : :
2259 : : return false;
2260 : : }
2261 : :
2262 : : if VUNLIKELY (!::SetHandleInformation(impl_->stdin_write, HANDLE_FLAG_INHERIT, 0) ||
2263 : : !::SetHandleInformation(impl_->stdin_read, HANDLE_FLAG_INHERIT, 0)) {
2264 : : close_handle_if_valid(impl_->stdin_read);
2265 : : close_handle_if_valid(impl_->stdin_write);
2266 : :
2267 : : set_error(kStartError);
2268 : :
2269 : : return false;
2270 : : }
2271 : :
2272 : : if VUNLIKELY (!::CreatePipe(&impl_->stdout_read, &impl_->stdout_write, &sa, 0)) {
2273 : : close_handle_if_valid(impl_->stdin_read);
2274 : : close_handle_if_valid(impl_->stdin_write);
2275 : :
2276 : : set_error(kStartError);
2277 : :
2278 : : return false;
2279 : : }
2280 : :
2281 : : if VUNLIKELY (!::SetHandleInformation(impl_->stdout_read, HANDLE_FLAG_INHERIT, 0) ||
2282 : : !::SetHandleInformation(impl_->stdout_write, HANDLE_FLAG_INHERIT, 0)) {
2283 : : close_handle_if_valid(impl_->stdin_read);
2284 : : close_handle_if_valid(impl_->stdin_write);
2285 : : close_handle_if_valid(impl_->stdout_read);
2286 : : close_handle_if_valid(impl_->stdout_write);
2287 : :
2288 : : set_error(kStartError);
2289 : :
2290 : : return false;
2291 : : }
2292 : :
2293 : : if (mode == kMergedMode) {
2294 : : impl_->stderr_write = impl_->stdout_write;
2295 : : impl_->stderr_read = INVALID_HANDLE_VALUE;
2296 : : } else {
2297 : : if VUNLIKELY (!::CreatePipe(&impl_->stderr_read, &impl_->stderr_write, &sa, 0)) {
2298 : : close_handle_if_valid(impl_->stdin_read);
2299 : : close_handle_if_valid(impl_->stdin_write);
2300 : : close_handle_if_valid(impl_->stdout_read);
2301 : : close_handle_if_valid(impl_->stdout_write);
2302 : :
2303 : : set_error(kStartError);
2304 : :
2305 : : return false;
2306 : : }
2307 : :
2308 : : if VUNLIKELY (!::SetHandleInformation(impl_->stderr_read, HANDLE_FLAG_INHERIT, 0) ||
2309 : : !::SetHandleInformation(impl_->stderr_write, HANDLE_FLAG_INHERIT, 0)) {
2310 : : close_handle_if_valid(impl_->stdin_read);
2311 : : close_handle_if_valid(impl_->stdin_write);
2312 : : close_handle_if_valid(impl_->stdout_read);
2313 : : close_handle_if_valid(impl_->stdout_write);
2314 : : close_handle_if_valid(impl_->stderr_read);
2315 : : close_handle_if_valid(impl_->stderr_write);
2316 : :
2317 : : set_error(kStartError);
2318 : :
2319 : : return false;
2320 : : }
2321 : : }
2322 : :
2323 : : return true;
2324 : : #else
2325 : :
2326 [ - + ]: 97 : if VUNLIKELY (pipe(impl_->stdin_pipe) != 0) {
2327 : : set_error(kStartError); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2328 : :
2329 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2330 : : }
2331 : :
2332 [ - + ]: 97 : if VUNLIKELY (pipe(impl_->stdout_pipe) != 0) {
2333 : : // LCOV_EXCL_START GCOVR_EXCL_START
2334 : : ::close(impl_->stdin_pipe[0]);
2335 : : ::close(impl_->stdin_pipe[1]);
2336 : :
2337 : : impl_->stdin_pipe[0] = -1;
2338 : : impl_->stdin_pipe[1] = -1;
2339 : :
2340 : : set_error(kStartError);
2341 : :
2342 : : return false;
2343 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2344 : : }
2345 : :
2346 [ + + ]: 97 : if (mode == kMergedMode) {
2347 : 1 : impl_->stderr_pipe[0] = -1;
2348 : 1 : impl_->stderr_pipe[1] = impl_->stdout_pipe[1];
2349 : 1 : impl_->stderr_closed.store(true, std::memory_order_release);
2350 : : } else {
2351 [ - + ]: 96 : if VUNLIKELY (pipe(impl_->stderr_pipe) != 0) {
2352 : : // LCOV_EXCL_START GCOVR_EXCL_START
2353 : : ::close(impl_->stdin_pipe[0]);
2354 : : ::close(impl_->stdin_pipe[1]);
2355 : : ::close(impl_->stdout_pipe[0]);
2356 : : ::close(impl_->stdout_pipe[1]);
2357 : :
2358 : : impl_->stdin_pipe[0] = -1;
2359 : : impl_->stdin_pipe[1] = -1;
2360 : : impl_->stdout_pipe[0] = -1;
2361 : : impl_->stdout_pipe[1] = -1;
2362 : :
2363 : : set_error(kStartError);
2364 : :
2365 : : return false;
2366 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2367 : : }
2368 : 96 : fcntl(impl_->stderr_pipe[0], F_SETFL, O_NONBLOCK);
2369 : : }
2370 : :
2371 : 97 : fcntl(impl_->stdout_pipe[0], F_SETFL, O_NONBLOCK);
2372 : 97 : fcntl(impl_->stdin_pipe[1], F_SETFL, O_NONBLOCK);
2373 : :
2374 : 97 : return true;
2375 : : #endif
2376 : : }
2377 : :
2378 : 180 : bool Process::start_program(const std::string& program, const std::vector<std::string>& arguments) {
2379 : : #ifdef _WIN32
2380 : : STARTUPINFOEXW si;
2381 : : PROCESS_INFORMATION pi;
2382 : : HANDLE child_stdin = INVALID_HANDLE_VALUE;
2383 : : HANDLE child_stdout = INVALID_HANDLE_VALUE;
2384 : : HANDLE child_stderr = INVALID_HANDLE_VALUE;
2385 : : SIZE_T attr_list_size = 0;
2386 : :
2387 : : ::ZeroMemory(&si, sizeof(si));
2388 : : si.StartupInfo.cb = sizeof(si);
2389 : :
2390 : : Mode mode = impl_->mode.load(std::memory_order_acquire);
2391 : :
2392 : : if VUNLIKELY (!duplicate_inheritable_handle(impl_->stdin_read, child_stdin)) {
2393 : : return false;
2394 : : }
2395 : :
2396 : : if (mode == kForwardedMode) {
2397 : : if VUNLIKELY (!duplicate_inheritable_handle(::GetStdHandle(STD_OUTPUT_HANDLE), child_stdout) ||
2398 : : !duplicate_inheritable_handle(::GetStdHandle(STD_ERROR_HANDLE), child_stderr)) {
2399 : : close_handle_if_valid(child_stdin);
2400 : : close_handle_if_valid(child_stdout);
2401 : : close_handle_if_valid(child_stderr);
2402 : : return false;
2403 : : }
2404 : : } else if (mode == kForwardedOutputMode) {
2405 : : if VUNLIKELY (!duplicate_inheritable_handle(::GetStdHandle(STD_OUTPUT_HANDLE), child_stdout) ||
2406 : : !duplicate_inheritable_handle(impl_->stderr_write, child_stderr)) {
2407 : : close_handle_if_valid(child_stdin);
2408 : : close_handle_if_valid(child_stdout);
2409 : : close_handle_if_valid(child_stderr);
2410 : : return false;
2411 : : }
2412 : : } else if (mode == kForwardedErrorMode) {
2413 : : if VUNLIKELY (!duplicate_inheritable_handle(impl_->stdout_write, child_stdout) ||
2414 : : !duplicate_inheritable_handle(::GetStdHandle(STD_ERROR_HANDLE), child_stderr)) {
2415 : : close_handle_if_valid(child_stdin);
2416 : : close_handle_if_valid(child_stdout);
2417 : : close_handle_if_valid(child_stderr);
2418 : : return false;
2419 : : }
2420 : : } else if (mode == kMergedMode) {
2421 : : if VUNLIKELY (!duplicate_inheritable_handle(impl_->stdout_write, child_stdout)) {
2422 : : close_handle_if_valid(child_stdin);
2423 : : return false;
2424 : : }
2425 : :
2426 : : child_stderr = child_stdout;
2427 : : } else {
2428 : : if VUNLIKELY (!duplicate_inheritable_handle(impl_->stdout_write, child_stdout) ||
2429 : : !duplicate_inheritable_handle(impl_->stderr_write, child_stderr)) {
2430 : : close_handle_if_valid(child_stdin);
2431 : : close_handle_if_valid(child_stdout);
2432 : : close_handle_if_valid(child_stderr);
2433 : : return false;
2434 : : }
2435 : : }
2436 : :
2437 : : si.StartupInfo.hStdInput = child_stdin;
2438 : : si.StartupInfo.hStdOutput = child_stdout;
2439 : : si.StartupInfo.hStdError = child_stderr;
2440 : : si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
2441 : :
2442 : : std::vector<HANDLE> inherit_handles;
2443 : : inherit_handles.reserve(3);
2444 : : append_unique_handle(inherit_handles, child_stdin);
2445 : : append_unique_handle(inherit_handles, child_stdout);
2446 : : append_unique_handle(inherit_handles, child_stderr);
2447 : : const SIZE_T inherit_handles_bytes = inherit_handles.size() * sizeof(HANDLE);
2448 : : void* inherit_handles_ptr = inherit_handles.empty() ? nullptr : static_cast<void*>(inherit_handles.data());
2449 : :
2450 : : (void)::InitializeProcThreadAttributeList(nullptr, 1, 0, &attr_list_size);
2451 : :
2452 : : std::vector<uint8_t> attr_list_storage(attr_list_size);
2453 : : si.lpAttributeList = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(attr_list_storage.data());
2454 : :
2455 : : if VUNLIKELY (!::InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &attr_list_size)) {
2456 : : if (child_stderr == child_stdout) {
2457 : : child_stderr = INVALID_HANDLE_VALUE;
2458 : : }
2459 : :
2460 : : close_handle_if_valid(child_stdin);
2461 : : close_handle_if_valid(child_stdout);
2462 : : close_handle_if_valid(child_stderr);
2463 : : return false;
2464 : : }
2465 : :
2466 : : if VUNLIKELY (!::UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
2467 : : inherit_handles_ptr, inherit_handles_bytes, nullptr, nullptr)) {
2468 : : ::DeleteProcThreadAttributeList(si.lpAttributeList);
2469 : :
2470 : : if (child_stderr == child_stdout) {
2471 : : child_stderr = INVALID_HANDLE_VALUE;
2472 : : }
2473 : :
2474 : : close_handle_if_valid(child_stdin);
2475 : : close_handle_if_valid(child_stdout);
2476 : : close_handle_if_valid(child_stderr);
2477 : : return false;
2478 : : }
2479 : :
2480 : : ::ZeroMemory(&pi, sizeof(pi));
2481 : :
2482 : : std::wstring program_wide = Helpers::string_to_wstring(program);
2483 : : std::vector<std::wstring> arguments_wide;
2484 : : arguments_wide.reserve(arguments.size());
2485 : :
2486 : : for (const auto& arg : arguments) {
2487 : : arguments_wide.emplace_back(Helpers::string_to_wstring(arg));
2488 : : }
2489 : :
2490 : : std::wstring cmd_line = build_command_line(program_wide, arguments_wide);
2491 : : std::vector<wchar_t> cmd_line_buf(cmd_line.begin(), cmd_line.end());
2492 : : cmd_line_buf.emplace_back(L'\0');
2493 : :
2494 : : std::vector<wchar_t> env_block;
2495 : :
2496 : : {
2497 : : std::shared_lock lock(impl_->shared_mtx);
2498 : :
2499 : : if (impl_->inherit_environment.load(std::memory_order_acquire)) {
2500 : : wchar_t* parent_env = ::GetEnvironmentStringsW();
2501 : : if (parent_env) {
2502 : : EnvironmentMap merged_env;
2503 : :
2504 : : wchar_t* env_ptr = parent_env;
2505 : :
2506 : : while (*env_ptr) {
2507 : : std::wstring env_str(env_ptr);
2508 : : size_t eq_pos = env_str.find(L'=');
2509 : :
2510 : : if (eq_pos != std::wstring::npos && eq_pos > 0) {
2511 : : std::wstring key = env_str.substr(0, eq_pos);
2512 : : std::wstring value = env_str.substr(eq_pos + 1);
2513 : :
2514 : : std::string key_utf8 = Helpers::wstring_to_string(key);
2515 : : std::string val_utf8 = Helpers::wstring_to_string(value);
2516 : :
2517 : : if (!key_utf8.empty()) {
2518 : : merged_env[key_utf8] = val_utf8;
2519 : : }
2520 : : }
2521 : :
2522 : : env_ptr += env_str.length() + 1;
2523 : : }
2524 : :
2525 : : ::FreeEnvironmentStringsW(parent_env);
2526 : :
2527 : : for (const auto& [key, value] : impl_->environment_map) {
2528 : : merged_env[key] = value;
2529 : : }
2530 : :
2531 : : env_block = build_environment_block(merged_env);
2532 : : }
2533 : : } else {
2534 : : env_block = build_environment_block(impl_->environment_map);
2535 : : }
2536 : : }
2537 : :
2538 : : void* env_ptr = env_block.empty() ? nullptr : env_block.data();
2539 : :
2540 : : std::wstring work_dir_wide;
2541 : : const wchar_t* work_dir = nullptr;
2542 : :
2543 : : {
2544 : : std::shared_lock lock(impl_->shared_mtx);
2545 : :
2546 : : if (!impl_->working_directory.empty()) {
2547 : : work_dir_wide = Helpers::string_to_wstring(impl_->working_directory);
2548 : : work_dir = work_dir_wide.c_str();
2549 : : }
2550 : : }
2551 : :
2552 : : BOOL result = ::CreateProcessW(nullptr, cmd_line_buf.data(), nullptr, nullptr, TRUE,
2553 : : CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT, env_ptr, work_dir,
2554 : : &si.StartupInfo, &pi);
2555 : :
2556 : : ::DeleteProcThreadAttributeList(si.lpAttributeList);
2557 : :
2558 : : if (child_stderr == child_stdout) {
2559 : : child_stderr = INVALID_HANDLE_VALUE;
2560 : : }
2561 : :
2562 : : close_handle_if_valid(child_stdin);
2563 : : close_handle_if_valid(child_stdout);
2564 : : close_handle_if_valid(child_stderr);
2565 : :
2566 : : if VUNLIKELY (!result) {
2567 : : return false;
2568 : : }
2569 : :
2570 : : impl_->process = pi.hProcess;
2571 : : impl_->thread = pi.hThread;
2572 : : impl_->process_id = pi.dwProcessId;
2573 : :
2574 : : close_handle_if_valid(impl_->stdin_read);
2575 : :
2576 : : if (mode != kForwardedMode && mode != kForwardedOutputMode) {
2577 : : const HANDLE stdout_write = impl_->stdout_write;
2578 : : close_handle_if_valid(impl_->stdout_write);
2579 : :
2580 : : if (impl_->stderr_write == stdout_write) {
2581 : : impl_->stderr_write = INVALID_HANDLE_VALUE;
2582 : : }
2583 : : }
2584 : :
2585 : : if (mode != kForwardedMode && mode != kForwardedErrorMode && mode != kMergedMode) {
2586 : : close_handle_if_valid(impl_->stderr_write);
2587 : : }
2588 : :
2589 : : return true;
2590 : : #else
2591 : 180 : EnvironmentMap env_map_copy;
2592 : 180 : std::string working_directory_copy;
2593 : 180 : bool inherit_env = impl_->inherit_environment.load(std::memory_order_acquire);
2594 : 180 : Mode mode = impl_->mode.load(std::memory_order_acquire);
2595 : : ssize_t written;
2596 : :
2597 : : {
2598 [ + - ]: 180 : std::shared_lock lock(impl_->shared_mtx);
2599 [ + - ]: 180 : env_map_copy = impl_->environment_map;
2600 [ + - ]: 180 : working_directory_copy = impl_->working_directory;
2601 : 180 : }
2602 : :
2603 : : int exec_failure_pipe[2];
2604 : :
2605 [ - + ]: 180 : if VUNLIKELY (pipe(exec_failure_pipe) != 0) {
2606 : : int err = errno; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2607 : : CLOG_E("Process: Failed to create exec failure pipe: %s.", strerror(err)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2608 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2609 : : }
2610 : :
2611 [ + - ]: 180 : fcntl(exec_failure_pipe[1], F_SETFD, FD_CLOEXEC);
2612 : :
2613 : 180 : const pid_t fork_pid = fork();
2614 : 358 : impl_->process_id.store(fork_pid, std::memory_order_release);
2615 : :
2616 [ - + ]: 358 : if VUNLIKELY (fork_pid < 0) {
2617 : : // LCOV_EXCL_START GCOVR_EXCL_START
2618 : : int err = errno;
2619 : : CLOG_E("Process: Fork failed: %s.", strerror(err));
2620 : :
2621 : : ::close(exec_failure_pipe[0]);
2622 : : ::close(exec_failure_pipe[1]);
2623 : :
2624 : : return false;
2625 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2626 : : }
2627 : :
2628 [ + + ]: 358 : if VUNLIKELY (fork_pid == 0) {
2629 [ + - ]: 178 : ::close(exec_failure_pipe[0]);
2630 : :
2631 : 178 : dup2(impl_->stdin_pipe[0], STDIN_FILENO);
2632 : :
2633 [ + + ]: 178 : if (mode == kForwardedMode) {
2634 [ + + ]: 99 : } else if (mode == kForwardedOutputMode) {
2635 : 1 : dup2(impl_->stderr_pipe[1], STDERR_FILENO);
2636 [ + + ]: 98 : } else if (mode == kForwardedErrorMode) {
2637 : 1 : dup2(impl_->stdout_pipe[1], STDOUT_FILENO);
2638 [ + + ]: 97 : } else if (mode == kMergedMode) {
2639 : 1 : dup2(impl_->stdout_pipe[1], STDOUT_FILENO);
2640 : 1 : dup2(impl_->stdout_pipe[1], STDERR_FILENO);
2641 : : } else {
2642 : 96 : dup2(impl_->stdout_pipe[1], STDOUT_FILENO);
2643 : 96 : dup2(impl_->stderr_pipe[1], STDERR_FILENO);
2644 : : }
2645 : :
2646 [ + - ]: 178 : close_child_descriptors(exec_failure_pipe[1]);
2647 : :
2648 [ + + ]: 178 : if (inherit_env) {
2649 [ + + ]: 532 : for (const auto& [key, value] : env_map_copy) {
2650 : 470 : setenv(key.c_str(), value.c_str(), 1);
2651 : : }
2652 : : }
2653 : :
2654 [ + + - + : 178 : if VUNLIKELY (!working_directory_copy.empty() && chdir(working_directory_copy.c_str()) != 0) {
- + ]
2655 : : int error_code = errno; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2656 : :
2657 : : written = ::write(exec_failure_pipe[1], &error_code, sizeof(error_code)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2658 : :
2659 : : (void)written;
2660 : :
2661 : : _exit(kExecFailedExitCode); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2662 : : }
2663 : :
2664 : 178 : std::vector<std::string> args_storage;
2665 [ + - ]: 178 : args_storage.reserve(arguments.size() + 2);
2666 [ + - ]: 178 : args_storage.emplace_back(program);
2667 [ + - ]: 178 : args_storage.insert(args_storage.end(), arguments.begin(), arguments.end());
2668 : :
2669 : 178 : std::vector<char*> args;
2670 [ + - ]: 178 : args.reserve(args_storage.size() + 1);
2671 : :
2672 [ + + ]: 712 : for (auto& arg : args_storage) {
2673 [ + - ]: 534 : args.emplace_back(const_cast<char*>(arg.c_str()));
2674 : : }
2675 : :
2676 [ + - ]: 178 : args.emplace_back(nullptr);
2677 : :
2678 [ + + ]: 178 : if (inherit_env) {
2679 : 62 : execvp(program.c_str(), args.data());
2680 : : } else {
2681 : 116 : std::vector<std::string> envp_storage;
2682 [ + - ]: 116 : envp_storage.reserve(env_map_copy.size());
2683 : :
2684 [ + + ]: 121 : for (const auto& [key, value] : env_map_copy) {
2685 : 5 : std::string tmp_envp;
2686 : :
2687 [ + - ]: 5 : tmp_envp.append(key);
2688 [ + - ]: 5 : tmp_envp.append("=");
2689 [ + - ]: 5 : tmp_envp.append(value);
2690 : :
2691 [ + - ]: 5 : envp_storage.emplace_back(std::move(tmp_envp));
2692 : 5 : }
2693 : :
2694 : 116 : std::vector<char*> envp;
2695 [ + - ]: 116 : envp.reserve(envp_storage.size() + 1);
2696 : :
2697 [ + + ]: 121 : for (auto& s : envp_storage) {
2698 [ + - ]: 5 : envp.emplace_back(const_cast<char*>(s.c_str()));
2699 : : }
2700 : :
2701 [ + - ]: 116 : envp.emplace_back(nullptr);
2702 : :
2703 [ + - ]: 116 : std::string exec_program = resolve_program_path(program, env_map_copy);
2704 : 116 : execve(exec_program.c_str(), args.data(), envp.data());
2705 : 116 : }
2706 : :
2707 : : int error_code = errno; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2708 : :
2709 : : written = ::write(exec_failure_pipe[1], &error_code, sizeof(error_code)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2710 : :
2711 : : (void)written;
2712 : :
2713 : : _exit(kExecFailedExitCode); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2714 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2715 : :
2716 [ + - ]: 180 : ::close(exec_failure_pipe[1]);
2717 : :
2718 [ + - ]: 180 : ::close(impl_->stdin_pipe[0]);
2719 : 180 : impl_->stdin_pipe[0] = -1;
2720 : :
2721 [ + + + + ]: 180 : if (mode != kForwardedMode && mode != kForwardedOutputMode) {
2722 [ + - ]: 98 : if (impl_->stdout_pipe[1] >= 0) {
2723 : 98 : const int stdout_write = impl_->stdout_pipe[1];
2724 [ + - ]: 98 : ::close(impl_->stdout_pipe[1]);
2725 : 98 : impl_->stdout_pipe[1] = -1;
2726 : :
2727 [ + + ]: 98 : if (impl_->stderr_pipe[1] == stdout_write) {
2728 : 1 : impl_->stderr_pipe[1] = -1;
2729 : : }
2730 : : }
2731 : : }
2732 : :
2733 [ + + + + : 180 : if (mode != kForwardedMode && mode != kForwardedErrorMode && mode != kMergedMode) {
+ + ]
2734 [ + - ]: 97 : if (impl_->stderr_pipe[1] >= 0) {
2735 [ + - ]: 97 : ::close(impl_->stderr_pipe[1]);
2736 : 97 : impl_->stderr_pipe[1] = -1;
2737 : : }
2738 : : }
2739 : :
2740 : 180 : int error_code = 0;
2741 : 180 : ssize_t n = 0;
2742 : :
2743 : : do {
2744 [ + - ]: 180 : n = ::read(exec_failure_pipe[0], &error_code, sizeof(error_code));
2745 [ - + - - ]: 180 : } while (n < 0 && errno == EINTR);
2746 : :
2747 [ + - ]: 180 : ::close(exec_failure_pipe[0]);
2748 : :
2749 [ + + ]: 180 : if (n > 0) {
2750 : 8 : int status = 0;
2751 : 8 : pid_t wait_result = 0;
2752 : :
2753 : : do {
2754 [ + - ]: 8 : wait_result = waitpid(fork_pid, &status, 0);
2755 [ - + - - ]: 8 : } while (wait_result < 0 && errno == EINTR);
2756 : :
2757 : 8 : impl_->process_id.store(-1, std::memory_order_release);
2758 : 8 : errno = error_code;
2759 : 8 : return false;
2760 : : }
2761 : :
2762 : 172 : return true;
2763 : : #endif
2764 : 180 : }
2765 : :
2766 : 172 : void Process::monitor_thread() {
2767 : : #ifdef _WIN32
2768 : : while (impl_->monitor_running.load(std::memory_order_acquire) &&
2769 : : impl_->state.load(std::memory_order_acquire) == kRunningState) {
2770 : : if (impl_->monitor_should_stop.load(std::memory_order_acquire)) {
2771 : : break;
2772 : : }
2773 : :
2774 : : if (impl_->exit_processed.load(std::memory_order_acquire)) {
2775 : : break;
2776 : : }
2777 : :
2778 : : DWORD wait_ret = ::WaitForSingleObject(impl_->process, 10);
2779 : :
2780 : : if (wait_ret == WAIT_OBJECT_0) {
2781 : : DWORD exit_code_win = 0;
2782 : :
2783 : : if (::GetExitCodeProcess(impl_->process, &exit_code_win)) {
2784 : : read_from_pipes_with_lock();
2785 : : handle_process_exit(static_cast<int>(exit_code_win), kNormalExitStatus);
2786 : : }
2787 : :
2788 : : break;
2789 : : }
2790 : :
2791 : : read_from_pipes_with_lock();
2792 : : }
2793 : : #else
2794 : : struct pollfd fds[3];
2795 : 172 : int nfds = 0;
2796 : :
2797 [ + + + + : 1199 : while (impl_->monitor_running.load(std::memory_order_acquire) &&
+ + ]
2798 : 597 : impl_->state.load(std::memory_order_acquire) == kRunningState) {
2799 [ + + ]: 594 : if (impl_->monitor_should_stop.load(std::memory_order_acquire)) {
2800 : 164 : break;
2801 : : }
2802 : :
2803 [ - + ]: 593 : if (impl_->exit_processed.load(std::memory_order_acquire)) {
2804 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2805 : : }
2806 : :
2807 : : int status;
2808 : :
2809 : 593 : const auto pid = static_cast<pid_t>(impl_->process_id.load(std::memory_order_acquire));
2810 : :
2811 [ - + ]: 593 : if VUNLIKELY (pid <= 0) {
2812 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2813 : : }
2814 : :
2815 [ + - ]: 593 : auto result = waitpid(pid, &status, WNOHANG);
2816 : :
2817 [ + + ]: 593 : if (result == pid) {
2818 : 163 : int exit_code = -1;
2819 : 163 : ExitStatus exit_status = kCrashExitStatus;
2820 : :
2821 [ + + ]: 163 : if VLIKELY (WIFEXITED(status)) {
2822 : 152 : exit_code = WEXITSTATUS(status);
2823 : 152 : exit_status = kNormalExitStatus;
2824 [ + - ]: 11 : } else if (WIFSIGNALED(status)) {
2825 : 11 : int sig = WTERMSIG(status);
2826 : 11 : exit_code = 128 + sig;
2827 : 11 : exit_status = kCrashExitStatus;
2828 : : }
2829 : :
2830 [ + - ]: 163 : read_from_pipes_with_lock();
2831 [ + - ]: 163 : handle_process_exit(exit_code, exit_status);
2832 : :
2833 : 163 : break;
2834 [ - + ]: 430 : } else if (result < 0) {
2835 : : if VUNLIKELY (errno != EINTR && errno != ECHILD) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2836 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2837 : : }
2838 : : }
2839 : :
2840 : 430 : nfds = 0;
2841 : :
2842 [ + + + + : 430 : if (impl_->stdout_pipe[0] >= 0 && !impl_->stdout_closed.load(std::memory_order_acquire)) {
+ + ]
2843 : 246 : fds[nfds].fd = impl_->stdout_pipe[0];
2844 : 246 : fds[nfds].events = POLLIN;
2845 : 246 : fds[nfds].revents = 0;
2846 : 246 : ++nfds;
2847 : : }
2848 : :
2849 [ + + + + : 430 : if (impl_->stderr_pipe[0] >= 0 && !impl_->stderr_closed.load(std::memory_order_acquire)) {
+ + ]
2850 : 241 : fds[nfds].fd = impl_->stderr_pipe[0];
2851 : 241 : fds[nfds].events = POLLIN;
2852 : 241 : fds[nfds].revents = 0;
2853 : 241 : ++nfds;
2854 : : }
2855 : :
2856 [ + + ]: 430 : if (nfds > 0) {
2857 [ + - ]: 248 : int poll_ret = ::poll(fds, nfds, 50);
2858 : : (void)poll_ret;
2859 : : } else {
2860 [ + - ]: 182 : std::this_thread::sleep_for(std::chrono::milliseconds(50));
2861 : : }
2862 : :
2863 [ + - ]: 430 : read_from_pipes_with_lock();
2864 : : }
2865 : : #endif
2866 : 172 : }
2867 : :
2868 : 172 : void Process::start_monitor_thread() {
2869 [ + + + - : 172 : if (impl_->monitor_thread && impl_->monitor_thread->joinable()) {
+ + ]
2870 [ - + ]: 1 : if (impl_->monitor_thread->get_id() == std::this_thread::get_id()) {
2871 : : impl_->monitor_thread->detach(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2872 : : } else {
2873 : 1 : impl_->monitor_should_stop.store(true, std::memory_order_release);
2874 : 1 : impl_->monitor_thread->join();
2875 : : }
2876 : : }
2877 : :
2878 : 172 : impl_->monitor_running.store(true, std::memory_order_release);
2879 : 172 : impl_->monitor_should_stop.store(false, std::memory_order_release);
2880 [ + - ]: 344 : impl_->monitor_thread = std::make_unique<std::thread>([this]() { monitor_thread(); });
2881 : 172 : }
2882 : :
2883 : 391 : void Process::stop_monitor_thread() {
2884 : 391 : impl_->monitor_should_stop.store(true, std::memory_order_release);
2885 : 391 : impl_->monitor_running.store(false, std::memory_order_release);
2886 : :
2887 [ + + + - : 391 : if VLIKELY (impl_->monitor_thread && impl_->monitor_thread->joinable()) {
+ + ]
2888 [ - + ]: 171 : if (impl_->monitor_thread->get_id() == std::this_thread::get_id()) {
2889 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2890 : : }
2891 : :
2892 : 171 : impl_->monitor_thread->join();
2893 : : }
2894 : :
2895 : 391 : impl_->monitor_thread.reset();
2896 : : }
2897 : :
2898 : 172 : void Process::handle_process_exit(int exit_code, ExitStatus status) {
2899 : 172 : bool expected = false;
2900 : :
2901 [ + - ]: 172 : if (impl_->exit_processed.compare_exchange_strong(expected, true,
2902 : : std::memory_order_acq_rel)) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2903 : : #ifdef _WIN32
2904 : : status =
2905 : : normalize_windows_exit_status(status, impl_->forced_termination.exchange(false, std::memory_order_acq_rel));
2906 : : #else
2907 : 172 : impl_->process_id.store(-1, std::memory_order_release);
2908 : : #endif
2909 : 172 : impl_->exit_code.store(exit_code, std::memory_order_release);
2910 : 172 : impl_->exit_status.store(status, std::memory_order_release);
2911 : :
2912 [ + - ]: 172 : set_state(kNotRunningState);
2913 : :
2914 [ + - ]: 172 : if (!impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2915 [ + - ]: 172 : invoke_callbacks_outside_lock(kNoError, true, exit_code, status, kNotRunningState, true, false, false);
2916 : : }
2917 : : }
2918 : 172 : }
2919 : :
2920 : 600 : void Process::invoke_callbacks_outside_lock(Error error_to_report, bool has_finished, int exit_code_to_report,
2921 : : ExitStatus exit_status_to_report, State state_to_report,
2922 : : bool has_state_changed, bool has_stdout_data, bool has_stderr_data) {
2923 [ - + ]: 600 : if (impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2924 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2925 : : }
2926 : :
2927 : 600 : ErrorCallback error_cb;
2928 : 600 : FinishedCallback finished_cb;
2929 : 600 : ReadyReadCallback stdout_cb;
2930 : 600 : ReadyReadCallback stderr_cb;
2931 : 600 : StateChangedCallback state_cb;
2932 : :
2933 : : {
2934 [ + - ]: 600 : std::shared_lock lock(impl_->shared_mtx);
2935 : :
2936 [ + + + - : 600 : if (error_to_report != kNoError && !impl_->error_reported.exchange(true, std::memory_order_acq_rel)) {
+ + ]
2937 [ + - ]: 23 : error_cb = impl_->error_callback;
2938 : : }
2939 : :
2940 [ + + ]: 600 : if (has_finished) {
2941 [ + - ]: 172 : finished_cb = impl_->finished_callback;
2942 : : }
2943 : :
2944 [ + + ]: 600 : if (has_stdout_data) {
2945 [ + - ]: 46 : stdout_cb = impl_->ready_read_stdout_callback;
2946 : : }
2947 : :
2948 [ + + ]: 600 : if (has_stderr_data) {
2949 [ + - ]: 15 : stderr_cb = impl_->ready_read_stderr_callback;
2950 : : }
2951 : :
2952 [ + + ]: 600 : if (has_state_changed) {
2953 [ + - ]: 532 : state_cb = impl_->state_changed_callback;
2954 : : }
2955 : 600 : }
2956 : :
2957 [ + + ]: 600 : if (stdout_cb) {
2958 [ - + ]: 6 : if (impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2959 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2960 : : }
2961 : :
2962 [ + - ]: 6 : stdout_cb();
2963 : : }
2964 : :
2965 [ + + ]: 600 : if (stderr_cb) {
2966 [ - + ]: 1 : if (impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2967 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2968 : : }
2969 : :
2970 [ + - ]: 1 : stderr_cb();
2971 : : }
2972 : :
2973 [ + + ]: 600 : if (state_cb) {
2974 [ - + ]: 50 : if (impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2975 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2976 : : }
2977 : :
2978 [ + - ]: 50 : state_cb(state_to_report);
2979 : : }
2980 : :
2981 [ + + ]: 600 : if (finished_cb) {
2982 [ - + ]: 1 : if (impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2983 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2984 : : }
2985 : :
2986 [ + - ]: 1 : finished_cb(exit_code_to_report, exit_status_to_report);
2987 : : }
2988 : :
2989 [ + + ]: 600 : if (error_cb) {
2990 [ - + ]: 11 : if (impl_->is_being_destroyed.load(std::memory_order_acquire)) {
2991 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2992 : : }
2993 : :
2994 [ + - ]: 11 : error_cb(error_to_report);
2995 : : }
2996 [ + - + - : 600 : }
+ - + - +
- ]
2997 : :
2998 : : } // namespace vlink
|