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