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/utils.h"
25 : :
26 : : #include <algorithm>
27 : : #include <array>
28 : : #include <atomic>
29 : : #include <cctype>
30 : : #include <csignal>
31 : : #include <cstdio>
32 : : #include <filesystem>
33 : : #include <fstream>
34 : : #include <functional>
35 : : #include <iostream>
36 : : #include <mutex>
37 : : #include <new>
38 : : #include <sstream>
39 : : #include <string>
40 : : #include <thread>
41 : : #include <utility>
42 : : #include <vector>
43 : :
44 : : #include "./base/condition_variable.h"
45 : : #include "./base/memory_pool.h"
46 : :
47 : : #if __has_include(<unistd.h>)
48 : : #include <unistd.h>
49 : : #endif
50 : :
51 : : #ifdef _WIN32
52 : :
53 : : #include <unordered_map>
54 : :
55 : : #ifndef _WINSOCK2API_
56 : : #include <winsock2.h>
57 : : #endif
58 : :
59 : : #include <Windows.h>
60 : : #include <Ws2tcpip.h>
61 : : #include <iphlpapi.h>
62 : : #include <iptypes.h>
63 : : #include <mbctype.h>
64 : : #include <process.h>
65 : : #include <psapi.h>
66 : : #include <tlhelp32.h>
67 : :
68 : : // #pragma comment(lib, "Iphlpapi.lib")
69 : : // #pragma comment(lib, "ws2_32.lib")
70 : :
71 : : #include <codecvt>
72 : : #include <locale>
73 : :
74 : : #else
75 : :
76 : : #include <arpa/inet.h>
77 : : #include <fcntl.h>
78 : : #include <ifaddrs.h>
79 : : #include <net/if.h>
80 : : #include <netinet/in.h>
81 : : #include <pthread.h>
82 : : #include <sched.h>
83 : : #include <sys/file.h>
84 : : #include <sys/ioctl.h>
85 : : #include <sys/socket.h>
86 : : #include <sys/stat.h>
87 : : #include <sys/types.h>
88 : : #include <termios.h>
89 : :
90 : : #include <climits>
91 : : #include <cstdlib>
92 : : #include <cstring>
93 : :
94 : : #if defined(__linux__)
95 : : #include <malloc.h>
96 : : #include <sys/syscall.h>
97 : : #elif defined(__FreeBSD__)
98 : : #include <pthread_np.h>
99 : : #include <sys/sysctl.h>
100 : : #elif defined(__ANDROID__)
101 : : #include <sys/syscall.h>
102 : : #elif defined(__APPLE__)
103 : : #include <mach-o/dyld.h>
104 : : #include <mach/mach.h>
105 : : #include <sys/sysctl.h>
106 : : #elif defined(__QNX__)
107 : : #include <devctl.h>
108 : : #include <net/if_dl.h>
109 : : #include <process.h>
110 : : #include <sys/neutrino.h>
111 : : #include <sys/procfs.h>
112 : : #include <sys/syspage.h>
113 : : #endif
114 : :
115 : : #endif
116 : :
117 : : #ifdef __CYGWIN__
118 : : #include <Windows.h>
119 : : #endif
120 : :
121 : : namespace vlink {
122 : :
123 : : namespace Utils {
124 : :
125 : 278 : std::string get_app_path() noexcept {
126 [ + + + - ]: 278 : static std::string file_path;
127 : : static std::once_flag flag;
128 : :
129 : 117 : static auto trim_trailing = [](char* buffer, size_t size) -> size_t {
130 [ + - - + : 117 : while (size > 0 && (buffer[size - 1] == '\n' || buffer[size - 1] == '\r' || buffer[size - 1] == ' ' ||
- + - + ]
131 [ - + ]: 117 : buffer[size - 1] == '\0')) {
132 : : --size; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
133 : : }
134 : :
135 : 117 : buffer[size] = '\0';
136 : :
137 : 117 : return size;
138 : : };
139 : :
140 : 117 : static auto resolve_path = [](const char* path, std::string& result) -> bool {
141 : : #if defined(_WIN32) || defined(__CYGWIN__)
142 : : char resolved[MAX_PATH] = {0};
143 : : auto len = ::GetFullPathNameA(path, sizeof(resolved), resolved, nullptr);
144 : :
145 : : if (len > 0 && len < MAX_PATH) {
146 : : result.assign(resolved, len);
147 : : std::replace(result.begin(), result.end(), '\\', '/');
148 : : return true;
149 : : }
150 : : #else
151 : 117 : char resolved[PATH_MAX] = {0};
152 : :
153 [ + - ]: 117 : if (::realpath(path, resolved) != nullptr) {
154 [ + - ]: 117 : result.assign(resolved);
155 : 117 : return true;
156 : : }
157 : : #endif
158 : :
159 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
160 : : };
161 : :
162 : 278 : std::call_once(flag, []() {
163 : : #if defined(_WIN32) || defined(__CYGWIN__)
164 : : char buffer[MAX_PATH] = {0};
165 : : auto size = ::GetModuleFileNameA(nullptr, buffer, sizeof(buffer));
166 : :
167 : : if VLIKELY (size > 0 && size < MAX_PATH) {
168 : : trim_trailing(buffer, size);
169 : :
170 : : if (!resolve_path(buffer, file_path)) {
171 : : file_path.assign(buffer);
172 : : std::replace(file_path.begin(), file_path.end(), '\\', '/');
173 : : }
174 : : }
175 : : #elif defined(__linux__)
176 : 117 : char buffer[PATH_MAX] = {0};
177 : 117 : auto size = ::readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
178 : :
179 [ + - + - : 117 : if VLIKELY (size > 0 && size < PATH_MAX) {
+ - ]
180 : 117 : trim_trailing(buffer, static_cast<size_t>(size));
181 : :
182 [ + - - + ]: 117 : if (!resolve_path(buffer, file_path)) {
183 : : file_path.assign(buffer); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
184 : : }
185 : : }
186 : : #elif defined(__FreeBSD__)
187 : : char buffer[PATH_MAX] = {0};
188 : : int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
189 : : size_t len = sizeof(buffer);
190 : :
191 : : if VLIKELY (sysctl(mib, 4, buffer, &len, nullptr, 0) == 0 && len > 0) {
192 : : if (buffer[len - 1] == '\0') {
193 : : --len;
194 : : }
195 : :
196 : : trim_trailing(buffer, len);
197 : :
198 : : if (!resolve_path(buffer, file_path)) {
199 : : file_path.assign(buffer);
200 : : }
201 : : }
202 : : #elif defined(__QNX__)
203 : : char buffer[PATH_MAX] = {0};
204 : : FILE* fp = ::fopen("/proc/self/exefile", "r");
205 : :
206 : : if VLIKELY (fp) {
207 : : auto size = ::fread(buffer, 1, sizeof(buffer) - 1, fp);
208 : : ::fclose(fp);
209 : :
210 : : trim_trailing(buffer, size);
211 : :
212 : : if (!resolve_path(buffer, file_path)) {
213 : : file_path.assign(buffer);
214 : : }
215 : : }
216 : : #elif defined(__APPLE__)
217 : : char buffer[PATH_MAX] = {0};
218 : : uint32_t size = sizeof(buffer);
219 : :
220 : : if VLIKELY (::_NSGetExecutablePath(buffer, &size) == 0) {
221 : : trim_trailing(buffer, std::strlen(buffer));
222 : :
223 : : if (!resolve_path(buffer, file_path)) {
224 : : file_path.assign(buffer);
225 : : }
226 : : }
227 : : #endif
228 : 117 : });
229 : :
230 : 278 : return file_path;
231 : : }
232 : :
233 : 40 : std::string get_app_dir() noexcept {
234 : 40 : std::string path = get_app_path();
235 : :
236 [ - + ]: 40 : if VUNLIKELY (path.empty()) {
237 : : return path; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
238 : : }
239 : :
240 : 40 : return path.substr(0, path.rfind('/'));
241 : 40 : }
242 : :
243 : 173 : std::string get_app_name() noexcept {
244 : 173 : std::string path = get_app_path();
245 : :
246 [ - + ]: 173 : if VUNLIKELY (path.empty()) {
247 : : return path; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
248 : : }
249 : :
250 : 173 : std::string app_name = path.substr(path.rfind('/') + 1, path.length());
251 : :
252 : : #if defined(_WIN32) || defined(__CYGWIN__)
253 : : static std::string exe_suffix = ".exe";
254 : :
255 : : if (app_name.length() > exe_suffix.length() &&
256 : : app_name.compare(app_name.length() - exe_suffix.length(), exe_suffix.length(), exe_suffix) == 0) {
257 : : app_name = app_name.substr(0, app_name.length() - exe_suffix.length());
258 : : }
259 : : #endif
260 : :
261 : 173 : return app_name;
262 : 173 : }
263 : :
264 : 80 : std::string get_host_name() noexcept {
265 : 80 : char hostname[256] = {};
266 : :
267 [ + - ]: 80 : if VLIKELY (::gethostname(hostname, sizeof(hostname) - 1) == 0) {
268 : 80 : return hostname;
269 : : } else {
270 : : return ""; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
271 : : }
272 : : }
273 : :
274 : 126 : int32_t get_pid() noexcept {
275 : : #ifdef _WIN32
276 : : return static_cast<int32_t>(_getpid());
277 : : #else
278 : 126 : return static_cast<int32_t>(getpid());
279 : : #endif
280 : : }
281 : :
282 : 807 : std::string get_pid_str() noexcept {
283 [ + + + - ]: 807 : static auto pid = get_pid();
284 : :
285 [ + + + - ]: 807 : static auto pid_str = std::to_string(pid);
286 : :
287 : 807 : return pid_str;
288 : : }
289 : :
290 : 38 : uint64_t get_process_start_time(int32_t pid) noexcept {
291 [ + + ]: 38 : if VUNLIKELY (pid <= 0) {
292 : 2 : return 0;
293 : : }
294 : :
295 : : #if defined(_WIN32)
296 : : HANDLE handle = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(pid));
297 : :
298 : : if (!handle) {
299 : : return ::GetLastError() == ERROR_INVALID_PARAMETER ? 0 : kUnknownProcessStartTime;
300 : : }
301 : :
302 : : FILETIME creation_time{};
303 : : FILETIME exit_time{};
304 : : FILETIME kernel_time{};
305 : : FILETIME user_time{};
306 : : const bool exited = ::WaitForSingleObject(handle, 0) == WAIT_OBJECT_0;
307 : : const bool timed = !exited && ::GetProcessTimes(handle, &creation_time, &exit_time, &kernel_time, &user_time);
308 : : ::CloseHandle(handle);
309 : :
310 : : if (exited) {
311 : : return 0;
312 : : }
313 : :
314 : : const uint64_t start_time = (static_cast<uint64_t>(creation_time.dwHighDateTime) << 32) | creation_time.dwLowDateTime;
315 : : return timed && start_time != 0 ? start_time : kUnknownProcessStartTime;
316 : : #elif defined(__APPLE__)
317 : : struct kinfo_proc info{};
318 : : size_t length = sizeof(info);
319 : : int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, static_cast<int>(pid)};
320 : :
321 : : if (::sysctl(mib, 4, &info, &length, nullptr, 0) != 0) {
322 : : return kUnknownProcessStartTime;
323 : : }
324 : :
325 : : if (length == 0 || info.kp_proc.p_stat == SZOMB) {
326 : : return 0;
327 : : }
328 : :
329 : : const uint64_t start_time = static_cast<uint64_t>(info.kp_proc.p_starttime.tv_sec) * 1000000ULL +
330 : : static_cast<uint64_t>(info.kp_proc.p_starttime.tv_usec);
331 : : return start_time != 0 ? start_time : kUnknownProcessStartTime;
332 : : #elif defined(__QNX__)
333 : : char path[64];
334 : : std::snprintf(path, sizeof(path), "/proc/%d/as", static_cast<int>(pid));
335 : : const int fd = ::open(path, O_RDONLY);
336 : :
337 : : if (fd == -1) {
338 : : return errno == ENOENT ? 0 : kUnknownProcessStartTime;
339 : : }
340 : :
341 : : procfs_info info{};
342 : : const int result = ::devctl(fd, DCMD_PROC_INFO, &info, sizeof(info), nullptr);
343 : : ::close(fd);
344 : :
345 : : if (result != EOK || info.start_time == 0) {
346 : : return kUnknownProcessStartTime;
347 : : }
348 : :
349 : : return (info.flags & _NTO_PF_ZOMBIE) != 0 ? 0 : static_cast<uint64_t>(info.start_time);
350 : : #else
351 : : char path[64];
352 : 36 : std::snprintf(path, sizeof(path), "/proc/%d/stat", static_cast<int>(pid));
353 : 36 : const int fd = ::open(path, O_RDONLY | O_CLOEXEC);
354 : :
355 [ + + ]: 36 : if (fd == -1) {
356 [ + - + - ]: 5 : return ::kill(static_cast<pid_t>(pid), 0) == -1 && errno == ESRCH ? 0 : kUnknownProcessStartTime;
357 : : }
358 : :
359 : : char line[1024];
360 : 31 : const ssize_t length = ::read(fd, line, sizeof(line) - 1);
361 : 31 : ::close(fd);
362 : :
363 [ - + ]: 31 : if (length <= 0) {
364 : 0 : return kUnknownProcessStartTime;
365 : : }
366 : :
367 : 31 : line[length] = '\0';
368 : 31 : const char* cursor = std::strrchr(line, ')');
369 : :
370 [ + - - + ]: 31 : if (!cursor || cursor[1] != ' ') {
371 : 0 : return kUnknownProcessStartTime;
372 : : }
373 : :
374 : 31 : cursor += 2;
375 : :
376 [ - + ]: 31 : if (*cursor == 'X') {
377 : 0 : return 0;
378 : : }
379 : :
380 : 31 : const bool zombie = *cursor == 'Z';
381 : 31 : uint64_t threads = 0;
382 : :
383 [ + + ]: 620 : for (int field = 3; field < 22; ++field) {
384 : 589 : cursor = std::strchr(cursor, ' ');
385 : :
386 [ - + ]: 589 : if (!cursor) {
387 : 0 : return kUnknownProcessStartTime;
388 : : }
389 : :
390 : 589 : ++cursor;
391 : :
392 [ + + ]: 589 : if (field == 19) {
393 : 31 : threads = std::strtoull(cursor, nullptr, 10);
394 : : }
395 : : }
396 : :
397 [ + + - + ]: 31 : if (zombie && threads <= 1) {
398 : 0 : return 0;
399 : : }
400 : :
401 : 31 : const uint64_t start_time = std::strtoull(cursor, nullptr, 10);
402 [ + - ]: 31 : return start_time != 0 ? start_time : kUnknownProcessStartTime;
403 : : #endif
404 : : }
405 : :
406 : 564 : std::string get_tmp_dir() noexcept {
407 [ + + + - ]: 564 : static std::string env_tmp_dir = get_env("VLINK_TMP_DIR");
408 : :
409 [ + + ]: 564 : if (!env_tmp_dir.empty()) {
410 : 1 : return env_tmp_dir;
411 : : }
412 : :
413 : 563 : std::string tmp_dir;
414 : :
415 : : #if defined(__QNX__)
416 : : tmp_dir = "/var/log";
417 : : #else
418 : : try {
419 [ + - + - ]: 563 : tmp_dir = std::filesystem::temp_directory_path().string();
420 : : } catch (std::filesystem::filesystem_error&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
421 : : tmp_dir = "/tmp"; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
422 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
423 : : #endif
424 : :
425 : : #if defined(_WIN32) || defined(__CYGWIN__)
426 : : std::replace(tmp_dir.begin(), tmp_dir.end(), '\\', '/');
427 : :
428 : : if (!tmp_dir.empty() && tmp_dir.back() == '/') {
429 : : tmp_dir.pop_back();
430 : : }
431 : : #endif
432 : :
433 : 563 : return tmp_dir;
434 : 563 : }
435 : :
436 : 6218 : std::string get_env(const std::string& key, const std::string& default_value) noexcept {
437 : : #ifdef _WIN32
438 : : int wlen = MultiByteToWideChar(CP_UTF8, 0, key.c_str(), -1, nullptr, 0);
439 : :
440 : : if (wlen == 0) {
441 : : return default_value;
442 : : }
443 : :
444 : : std::wstring wname(wlen, L'\0');
445 : :
446 : : if (MultiByteToWideChar(CP_UTF8, 0, key.c_str(), -1, wname.data(), wlen) == 0) {
447 : : return default_value;
448 : : }
449 : :
450 : : DWORD size = GetEnvironmentVariableW(wname.c_str(), nullptr, 0);
451 : :
452 : : if (size == 0) {
453 : : return default_value;
454 : : }
455 : :
456 : : std::wstring wvalue(size, L'\0');
457 : :
458 : : if (GetEnvironmentVariableW(wname.c_str(), wvalue.data(), size) == 0) {
459 : : return default_value;
460 : : }
461 : :
462 : : int len = WideCharToMultiByte(CP_UTF8, 0, wvalue.c_str(), -1, nullptr, 0, nullptr, nullptr);
463 : :
464 : : if (len == 0) {
465 : : return default_value;
466 : : }
467 : :
468 : : std::string value(len, '\0');
469 : :
470 : : if (WideCharToMultiByte(CP_UTF8, 0, wvalue.c_str(), -1, value.data(), len, nullptr, nullptr) == 0) {
471 : : return default_value;
472 : : }
473 : :
474 : : if (!value.empty() && value.back() == '\0') {
475 : : value.pop_back();
476 : : }
477 : :
478 : : return value;
479 : : #else
480 : 6218 : const char* value = std::getenv(key.c_str());
481 : :
482 [ + + ]: 6218 : if (!value) {
483 : 5852 : return default_value;
484 : : }
485 : :
486 : 366 : return std::string(value);
487 : : #endif
488 : : }
489 : :
490 : 21 : bool set_env(const std::string& key, const std::string& value, bool force) noexcept {
491 : : #ifdef _WIN32
492 : : (void)force;
493 : :
494 : : return ::_putenv_s(key.c_str(), value.c_str()) == 0;
495 : : #else
496 : 21 : return ::setenv(key.c_str(), value.c_str(), static_cast<int>(force)) == 0;
497 : : #endif
498 : : }
499 : :
500 : 228 : bool unset_env(const std::string& key) noexcept {
501 : : #ifdef _WIN32
502 : : return ::SetEnvironmentVariable(key.c_str(), nullptr); // NOLINT(readability-implicit-bool-conversion)
503 : : #else
504 : 228 : return ::unsetenv(key.c_str()) == 0;
505 : : #endif
506 : : }
507 : :
508 : 85 : bool is_ignored_iface_name(const char* name) noexcept {
509 [ + + ]: 85 : if VUNLIKELY (!name) {
510 : 1 : return true;
511 : : }
512 : :
513 [ + + + + : 84 : switch (name[0]) {
+ + + + +
+ + + + +
+ + + + +
+ + ]
514 : 10 : case 'l': {
515 : : // if (std::strcmp(name, "lo") == 0) {
516 : : // return true;
517 : : // }
518 : :
519 : : // if (std::strncmp(name, "lo0", 3) == 0) {
520 : : // return true;
521 : : // }
522 : :
523 [ + + ]: 10 : if (std::strncmp(name, "l2tp", 4) == 0) {
524 : 1 : return true;
525 : : }
526 : :
527 [ + + ]: 9 : if (std::strncmp(name, "lxc", 3) == 0) {
528 : 1 : return true;
529 : : }
530 : :
531 [ + + ]: 8 : if (std::strncmp(name, "llw", 3) == 0) {
532 : 1 : return true;
533 : : }
534 : :
535 : 7 : return false;
536 : : }
537 : :
538 : 8 : case 'v': {
539 [ + + ]: 8 : if (std::strncmp(name, "vmnet", 5) == 0) {
540 : 1 : return true;
541 : : }
542 : :
543 [ + + ]: 7 : if (std::strncmp(name, "veth", 4) == 0) {
544 : 1 : return true;
545 : : }
546 : :
547 [ + + ]: 6 : if (std::strncmp(name, "virbr", 5) == 0) {
548 : 1 : return true;
549 : : }
550 : :
551 [ + + ]: 5 : if (std::strncmp(name, "vboxnet", 7) == 0) {
552 : 1 : return true;
553 : : }
554 : :
555 [ + + ]: 4 : if (std::strncmp(name, "vti", 3) == 0) {
556 : 1 : return true;
557 : : }
558 : :
559 [ + + ]: 3 : if (std::strncmp(name, "vrf", 3) == 0) {
560 : 1 : return true;
561 : : }
562 : :
563 [ + + ]: 2 : if (std::strncmp(name, "vxlan", 5) == 0) {
564 : 1 : return true;
565 : : }
566 : :
567 : 1 : return false;
568 : : }
569 : :
570 : 3 : case 'b': {
571 [ + + ]: 3 : if (std::strncmp(name, "br-", 3) == 0) {
572 : 1 : return true;
573 : : }
574 : :
575 [ + + ]: 2 : if (std::strncmp(name, "bridge", 6) == 0) {
576 : 1 : return true;
577 : : }
578 : :
579 : 1 : return false;
580 : : }
581 : :
582 : 2 : case 'd': {
583 [ + + ]: 2 : if (std::strncmp(name, "docker", 6) == 0) {
584 : 1 : return true;
585 : : }
586 : :
587 : 1 : return false;
588 : : }
589 : :
590 : 5 : case 'p': {
591 [ + + ]: 5 : if (std::strncmp(name, "podman", 6) == 0) {
592 : 1 : return true;
593 : : }
594 : :
595 [ + + ]: 4 : if (std::strncmp(name, "ppp", 3) == 0) {
596 : 1 : return true;
597 : : }
598 : :
599 [ + + ]: 3 : if (std::strncmp(name, "pptp", 4) == 0) {
600 : 1 : return true;
601 : : }
602 : :
603 [ + + ]: 2 : if (std::strncmp(name, "patch-", 6) == 0) {
604 : 1 : return true;
605 : : }
606 : :
607 : 1 : return false;
608 : : }
609 : :
610 : 2 : case 'o': {
611 [ + + ]: 2 : if (std::strncmp(name, "ovs", 3) == 0) {
612 : 1 : return true;
613 : : }
614 : :
615 : 1 : return false;
616 : : }
617 : :
618 : 4 : case 'c': {
619 [ + + ]: 4 : if (std::strncmp(name, "cni", 3) == 0) {
620 : 1 : return true;
621 : : }
622 : :
623 [ + + ]: 3 : if (std::strncmp(name, "cali", 4) == 0) {
624 : 1 : return true;
625 : : }
626 : :
627 [ + + ]: 2 : if (std::strncmp(name, "cri", 3) == 0) {
628 : 1 : return true;
629 : : }
630 : :
631 : 1 : return false;
632 : : }
633 : :
634 : 4 : case 'f': {
635 [ + + ]: 4 : if (std::strncmp(name, "flannel", 7) == 0) {
636 : 1 : return true;
637 : : }
638 : :
639 [ + + ]: 3 : if (std::strncmp(name, "fwln", 4) == 0) {
640 : 1 : return true;
641 : : }
642 : :
643 [ + + ]: 2 : if (std::strncmp(name, "fwpr", 4) == 0) {
644 : 1 : return true;
645 : : }
646 : :
647 : 1 : return false;
648 : : }
649 : :
650 : 6 : case 'q': {
651 [ + + ]: 6 : if (std::strncmp(name, "qbr", 3) == 0) {
652 : 1 : return true;
653 : : }
654 : :
655 [ + + ]: 5 : if (std::strncmp(name, "qvb", 3) == 0) {
656 : 1 : return true;
657 : : }
658 : :
659 [ + + ]: 4 : if (std::strncmp(name, "qvo", 3) == 0) {
660 : 1 : return true;
661 : : }
662 : :
663 [ + + ]: 3 : if (std::strncmp(name, "qr-", 3) == 0) {
664 : 1 : return true;
665 : : }
666 : :
667 [ + + ]: 2 : if (std::strncmp(name, "qg-", 3) == 0) {
668 : 1 : return true;
669 : : }
670 : :
671 : 1 : return false;
672 : : }
673 : :
674 : 4 : case 't': {
675 [ + + ]: 4 : if (std::strncmp(name, "tun", 3) == 0) {
676 : 1 : return true;
677 : : }
678 : :
679 [ + + ]: 3 : if (std::strncmp(name, "tap", 3) == 0) {
680 : 1 : return true;
681 : : }
682 : :
683 [ + + ]: 2 : if (std::strncmp(name, "tailscale", 9) == 0) {
684 : 1 : return true;
685 : : }
686 : :
687 : 1 : return false;
688 : : }
689 : :
690 : 4 : case 'w': {
691 [ + + ]: 4 : if (std::strncmp(name, "wg", 2) == 0) {
692 : 1 : return true;
693 : : }
694 : :
695 [ + + ]: 3 : if (std::strncmp(name, "weave", 5) == 0) {
696 : 1 : return true;
697 : : }
698 : :
699 [ + + ]: 2 : if (std::strncmp(name, "wlanmon", 7) == 0) {
700 : 1 : return true;
701 : : }
702 : :
703 : 1 : return false;
704 : : }
705 : :
706 : 3 : case 'z': {
707 [ + + ]: 3 : if (std::strncmp(name, "zt", 2) == 0) {
708 : 1 : return true;
709 : : }
710 : :
711 [ + + ]: 2 : if (std::strncmp(name, "zerotier", 8) == 0) {
712 : 1 : return true;
713 : : }
714 : :
715 : 1 : return false;
716 : : }
717 : :
718 : 5 : case 'i': {
719 [ + + ]: 5 : if (std::strncmp(name, "ipip", 4) == 0) {
720 : 1 : return true;
721 : : }
722 : :
723 [ + + ]: 4 : if (std::strncmp(name, "ip_vti", 6) == 0) {
724 : 1 : return true;
725 : : }
726 : :
727 [ + + ]: 3 : if (std::strncmp(name, "ip6_vti", 7) == 0) {
728 : 1 : return true;
729 : : }
730 : :
731 [ + + ]: 2 : if (std::strncmp(name, "ipvlan", 6) == 0) {
732 : 1 : return true;
733 : : }
734 : :
735 : 1 : return false;
736 : : }
737 : :
738 : 4 : case 'g': {
739 [ + + ]: 4 : if (std::strncmp(name, "gre", 3) == 0) {
740 : 2 : return true;
741 : : }
742 : :
743 [ - + ]: 2 : if (std::strncmp(name, "gretap", 6) == 0) {
744 : : return true; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
745 : : }
746 : :
747 [ + + ]: 2 : if (std::strncmp(name, "gif", 3) == 0) {
748 : 1 : return true;
749 : : }
750 : :
751 : 1 : return false;
752 : : }
753 : :
754 : 8 : case 'e': {
755 [ + + ]: 8 : if (std::strncmp(name, "erspan", 6) == 0) {
756 : 1 : return true;
757 : : }
758 : :
759 : 7 : return false;
760 : : }
761 : :
762 : 2 : case 'k': {
763 [ + + ]: 2 : if (std::strncmp(name, "kube-ipvs", 9) == 0) {
764 : 1 : return true;
765 : : }
766 : :
767 : 1 : return false;
768 : : }
769 : :
770 : 3 : case 'm': {
771 [ + + ]: 3 : if (std::strncmp(name, "macvlan", 7) == 0) {
772 : 1 : return true;
773 : : }
774 : :
775 [ + + ]: 2 : if (std::strncmp(name, "mon", 3) == 0) {
776 : 1 : return true;
777 : : }
778 : :
779 : 1 : return false;
780 : : }
781 : :
782 : 2 : case 's': {
783 [ + + ]: 2 : if (std::strncmp(name, "sit", 3) == 0) {
784 : 1 : return true;
785 : : }
786 : :
787 : 1 : return false;
788 : : }
789 : :
790 : 2 : case 'u': {
791 [ + + ]: 2 : if (std::strncmp(name, "utun", 4) == 0) {
792 : 1 : return true;
793 : : }
794 : :
795 : 1 : return false;
796 : : }
797 : :
798 : 2 : case 'a': {
799 [ + + ]: 2 : if (std::strncmp(name, "awdl", 4) == 0) {
800 : 1 : return true;
801 : : }
802 : :
803 : 1 : return false;
804 : : }
805 : :
806 : 1 : default: {
807 : 1 : return false;
808 : : }
809 : : }
810 : : }
811 : :
812 : 549 : bool ip_less(const std::string& lhs, const std::string& rhs) noexcept {
813 : 549 : const bool lhs_ipv6 = lhs.find(':') != std::string::npos;
814 : 549 : const bool rhs_ipv6 = rhs.find(':') != std::string::npos;
815 : :
816 [ + + ]: 549 : if VUNLIKELY (lhs_ipv6 != rhs_ipv6) {
817 : 98 : return !lhs_ipv6;
818 : : }
819 : :
820 : 451 : std::array<uint8_t, 16> lhs_bytes{};
821 : 451 : std::array<uint8_t, 16> rhs_bytes{};
822 [ + + ]: 451 : const int family = lhs_ipv6 ? AF_INET6 : AF_INET;
823 : :
824 : 451 : inet_pton(family, lhs.c_str(), lhs_bytes.data());
825 : 451 : inet_pton(family, rhs.c_str(), rhs_bytes.data());
826 : :
827 : 451 : return lhs_bytes < rhs_bytes;
828 : : }
829 : :
830 : 40 : std::vector<std::string> get_all_ipv4_address(bool filter_available) noexcept {
831 : 40 : std::vector<std::string> ip_addresses;
832 : :
833 : : #ifdef _WIN32
834 : : PIP_ADAPTER_ADDRESSES p_addresses = nullptr;
835 : : ULONG out_buf_len = 0;
836 : : DWORD ret = 0;
837 : :
838 : : ret = ::GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, p_addresses, &out_buf_len);
839 : :
840 : : if (ret == ERROR_BUFFER_OVERFLOW) {
841 : : // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
842 : : p_addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(new char[out_buf_len]);
843 : :
844 : : ret = ::GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, p_addresses, &out_buf_len);
845 : :
846 : : if (ret == NO_ERROR) {
847 : : char ip_buffer[INET_ADDRSTRLEN];
848 : :
849 : : for (auto* p_curr_addresses = p_addresses; p_curr_addresses != nullptr;
850 : : p_curr_addresses = p_curr_addresses->Next) {
851 : : if (p_curr_addresses->OperStatus != IfOperStatusUp) {
852 : : continue;
853 : : }
854 : :
855 : : if (filter_available) {
856 : : bool is_physical_eth_or_wifi =
857 : : (p_curr_addresses->IfType == IF_TYPE_ETHERNET_CSMACD) || (p_curr_addresses->IfType == IF_TYPE_IEEE80211);
858 : :
859 : : bool is_virtual_or_tunnel_iftype =
860 : : (p_curr_addresses->IfType == IF_TYPE_TUNNEL || p_curr_addresses->IfType == IF_TYPE_PPP ||
861 : : p_curr_addresses->IfType == IF_TYPE_PROP_VIRTUAL);
862 : :
863 : : if (!is_physical_eth_or_wifi && is_virtual_or_tunnel_iftype) {
864 : : continue;
865 : : }
866 : : }
867 : :
868 : : for (auto* p_unicast = p_curr_addresses->FirstUnicastAddress; p_unicast != nullptr;
869 : : p_unicast = p_unicast->Next) {
870 : : sockaddr* sockaddr = p_unicast->Address.lpSockaddr;
871 : :
872 : : if VLIKELY (::inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in*>(sockaddr)->sin_addr), ip_buffer,
873 : : INET_ADDRSTRLEN) != nullptr) {
874 : : ip_addresses.emplace_back(ip_buffer);
875 : : }
876 : : }
877 : : }
878 : : }
879 : :
880 : : delete[] reinterpret_cast<char*>(p_addresses);
881 : : }
882 : : #else
883 : 40 : struct ifaddrs* if_addr_struct = nullptr;
884 : 40 : struct ifaddrs* ifa = nullptr;
885 : 40 : void* tmp_addr_ptr = nullptr;
886 : :
887 : 40 : ::getifaddrs(&if_addr_struct);
888 : :
889 : : char ip_buffer[INET_ADDRSTRLEN];
890 : :
891 [ + + ]: 240 : for (ifa = if_addr_struct; ifa != nullptr; ifa = ifa->ifa_next) {
892 [ + - - + ]: 200 : if (ifa->ifa_addr == nullptr || !(ifa->ifa_flags & IFF_UP)) {
893 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
894 : : }
895 : :
896 [ + + ]: 200 : if (ifa->ifa_addr->sa_family != AF_INET) {
897 : 120 : continue;
898 : : }
899 : :
900 [ + + - + : 80 : if (filter_available && is_ignored_iface_name(ifa->ifa_name)) {
- + ]
901 : 0 : continue;
902 : : }
903 : :
904 : 80 : tmp_addr_ptr = &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr);
905 : :
906 [ + - ]: 80 : if VLIKELY (::inet_ntop(AF_INET, tmp_addr_ptr, ip_buffer, INET_ADDRSTRLEN) != nullptr) {
907 : 80 : ip_addresses.emplace_back(ip_buffer);
908 : : }
909 : : }
910 : :
911 [ + - ]: 40 : if (if_addr_struct != nullptr) {
912 : 40 : ::freeifaddrs(if_addr_struct);
913 : : }
914 : : #endif
915 : :
916 : 40 : return ip_addresses;
917 : : }
918 : :
919 : 4 : std::vector<std::string> get_all_ipv6_address(bool filter_available) noexcept {
920 : 4 : std::vector<std::string> ip_addresses;
921 : :
922 : : #ifdef _WIN32
923 : : PIP_ADAPTER_ADDRESSES p_addresses = nullptr;
924 : : ULONG out_buf_len = 0;
925 : : DWORD ret = 0;
926 : :
927 : : ret = ::GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, p_addresses, &out_buf_len);
928 : :
929 : : if (ret == ERROR_BUFFER_OVERFLOW) {
930 : : // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
931 : : p_addresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(new char[out_buf_len]);
932 : :
933 : : ret = ::GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, p_addresses, &out_buf_len);
934 : :
935 : : if (ret == NO_ERROR) {
936 : : char ip_buffer[INET6_ADDRSTRLEN];
937 : :
938 : : for (auto* p_curr_addresses = p_addresses; p_curr_addresses != nullptr;
939 : : p_curr_addresses = p_curr_addresses->Next) {
940 : : if (p_curr_addresses->OperStatus != IfOperStatusUp) {
941 : : continue;
942 : : }
943 : :
944 : : if (filter_available) {
945 : : bool is_physical_eth_or_wifi =
946 : : (p_curr_addresses->IfType == IF_TYPE_ETHERNET_CSMACD) || (p_curr_addresses->IfType == IF_TYPE_IEEE80211);
947 : :
948 : : bool is_virtual_or_tunnel_iftype =
949 : : (p_curr_addresses->IfType == IF_TYPE_TUNNEL || p_curr_addresses->IfType == IF_TYPE_PPP ||
950 : : p_curr_addresses->IfType == IF_TYPE_PROP_VIRTUAL);
951 : :
952 : : if (!is_physical_eth_or_wifi && is_virtual_or_tunnel_iftype) {
953 : : continue;
954 : : }
955 : : }
956 : :
957 : : for (auto* p_unicast = p_curr_addresses->FirstUnicastAddress; p_unicast != nullptr;
958 : : p_unicast = p_unicast->Next) {
959 : : sockaddr* sockaddr = p_unicast->Address.lpSockaddr;
960 : :
961 : : if VLIKELY (::inet_ntop(AF_INET6, &(reinterpret_cast<sockaddr_in6*>(sockaddr)->sin6_addr), ip_buffer,
962 : : INET6_ADDRSTRLEN) != nullptr) {
963 : : ip_addresses.emplace_back(ip_buffer);
964 : : }
965 : : }
966 : : }
967 : : }
968 : :
969 : : delete[] reinterpret_cast<char*>(p_addresses);
970 : : }
971 : : #else
972 : 4 : struct ifaddrs* if_addr_struct = nullptr;
973 : 4 : struct ifaddrs* ifa = nullptr;
974 : 4 : void* tmp_addr_ptr = nullptr;
975 : :
976 : 4 : ::getifaddrs(&if_addr_struct);
977 : :
978 : : char ip_buffer[INET6_ADDRSTRLEN];
979 : :
980 [ + + ]: 24 : for (ifa = if_addr_struct; ifa != nullptr; ifa = ifa->ifa_next) {
981 [ + - - + ]: 20 : if (ifa->ifa_addr == nullptr || !(ifa->ifa_flags & IFF_UP)) {
982 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
983 : : }
984 : :
985 [ + + ]: 20 : if (ifa->ifa_addr->sa_family != AF_INET6) {
986 : 16 : continue;
987 : : }
988 : :
989 [ + + - + : 4 : if (filter_available && is_ignored_iface_name(ifa->ifa_name)) {
- + ]
990 : 0 : continue;
991 : : }
992 : :
993 : 4 : tmp_addr_ptr = &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr);
994 : :
995 [ + - ]: 4 : if VLIKELY (::inet_ntop(AF_INET6, tmp_addr_ptr, ip_buffer, INET6_ADDRSTRLEN) != nullptr) {
996 : 4 : ip_addresses.emplace_back(ip_buffer);
997 : : }
998 : : }
999 : :
1000 [ + - ]: 4 : if (if_addr_struct != nullptr) {
1001 : 4 : ::freeifaddrs(if_addr_struct);
1002 : : }
1003 : : #endif
1004 : :
1005 : 4 : return ip_addresses;
1006 : : }
1007 : :
1008 : 3 : std::string get_interface_name_by_ipv4(const std::string& ipv4) noexcept {
1009 : : #ifdef _WIN32
1010 : : ULONG buf_size = 15 * 1024;
1011 : : std::vector<BYTE> buf(buf_size);
1012 : : PIP_ADAPTER_ADDRESSES adapters = nullptr;
1013 : :
1014 : : ULONG ret = ERROR_BUFFER_OVERFLOW;
1015 : :
1016 : : for (int retry = 0; retry < 3 && ret == ERROR_BUFFER_OVERFLOW; ++retry) {
1017 : : buf.resize(buf_size);
1018 : : adapters = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buf.data());
1019 : : ret = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, nullptr, adapters, &buf_size);
1020 : : }
1021 : :
1022 : : if (ret != NO_ERROR) {
1023 : : return {};
1024 : : }
1025 : :
1026 : : char addr_str[INET_ADDRSTRLEN] = {};
1027 : :
1028 : : for (auto* adapter = adapters; adapter; adapter = adapter->Next) {
1029 : : for (auto* ua = adapter->FirstUnicastAddress; ua; ua = ua->Next) {
1030 : : auto* sa = ua->Address.lpSockaddr;
1031 : :
1032 : : if (sa->sa_family != AF_INET) {
1033 : : continue;
1034 : : }
1035 : :
1036 : : addr_str[0] = '\0';
1037 : : ::inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in*>(sa)->sin_addr), addr_str, sizeof(addr_str));
1038 : :
1039 : : if (ipv4 == addr_str) {
1040 : : int len = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, nullptr, 0, nullptr, nullptr);
1041 : :
1042 : : if (len <= 0) {
1043 : : return {};
1044 : : }
1045 : :
1046 : : std::string name(len - 1, '\0');
1047 : : WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, name.data(), len, nullptr, nullptr);
1048 : :
1049 : : return name;
1050 : : }
1051 : : }
1052 : : }
1053 : : #else
1054 : 3 : struct ifaddrs* ifaddr = nullptr;
1055 : :
1056 [ - + ]: 3 : if (::getifaddrs(&ifaddr) != 0) {
1057 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1058 : : }
1059 : :
1060 : 3 : char addr_str[INET_ADDRSTRLEN] = {};
1061 : :
1062 [ + + ]: 12 : for (auto* ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
1063 [ + - - + ]: 11 : if (!ifa->ifa_addr || !ifa->ifa_name) {
1064 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1065 : : }
1066 : :
1067 [ + + ]: 11 : if (ifa->ifa_addr->sa_family != AF_INET) {
1068 : 7 : continue;
1069 : : }
1070 : :
1071 : 4 : addr_str[0] = '\0';
1072 : 4 : ::inet_ntop(AF_INET, &(reinterpret_cast<sockaddr_in*>(ifa->ifa_addr)->sin_addr), addr_str, sizeof(addr_str));
1073 : :
1074 [ + + ]: 4 : if (ipv4 == addr_str) {
1075 : 2 : std::string name(ifa->ifa_name);
1076 : 2 : ::freeifaddrs(ifaddr);
1077 : :
1078 : 2 : return name;
1079 : 2 : }
1080 : : }
1081 : :
1082 : 1 : ::freeifaddrs(ifaddr);
1083 : : #endif
1084 : 1 : return {};
1085 : : }
1086 : :
1087 : 3 : std::string get_interface_name_by_ipv6(const std::string& ipv6) noexcept {
1088 : : #ifdef _WIN32
1089 : : ULONG buf_size = 15 * 1024;
1090 : : std::vector<BYTE> buf(buf_size);
1091 : : PIP_ADAPTER_ADDRESSES adapters = nullptr;
1092 : :
1093 : : ULONG ret = ERROR_BUFFER_OVERFLOW;
1094 : :
1095 : : for (int retry = 0; retry < 3 && ret == ERROR_BUFFER_OVERFLOW; ++retry) {
1096 : : buf.resize(buf_size);
1097 : : adapters = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(buf.data());
1098 : : ret = GetAdaptersAddresses(AF_INET6, GAA_FLAG_INCLUDE_PREFIX, nullptr, adapters, &buf_size);
1099 : : }
1100 : :
1101 : : if (ret != NO_ERROR) {
1102 : : return {};
1103 : : }
1104 : :
1105 : : char addr_str[INET6_ADDRSTRLEN] = {};
1106 : :
1107 : : for (auto* adapter = adapters; adapter; adapter = adapter->Next) {
1108 : : for (auto* ua = adapter->FirstUnicastAddress; ua; ua = ua->Next) {
1109 : : auto* sa = ua->Address.lpSockaddr;
1110 : :
1111 : : if (sa->sa_family != AF_INET6) {
1112 : : continue;
1113 : : }
1114 : :
1115 : : addr_str[0] = '\0';
1116 : : ::inet_ntop(AF_INET6, &(reinterpret_cast<sockaddr_in6*>(sa)->sin6_addr), addr_str, sizeof(addr_str));
1117 : :
1118 : : if (ipv6 == addr_str) {
1119 : : int len = WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, nullptr, 0, nullptr, nullptr);
1120 : :
1121 : : if (len <= 0) {
1122 : : return {};
1123 : : }
1124 : :
1125 : : std::string name(len - 1, '\0');
1126 : : WideCharToMultiByte(CP_UTF8, 0, adapter->FriendlyName, -1, name.data(), len, nullptr, nullptr);
1127 : :
1128 : : return name;
1129 : : }
1130 : : }
1131 : : }
1132 : : #else
1133 : 3 : struct ifaddrs* ifaddr = nullptr;
1134 : :
1135 [ - + ]: 3 : if (::getifaddrs(&ifaddr) != 0) {
1136 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1137 : : }
1138 : :
1139 : 3 : char addr_str[INET6_ADDRSTRLEN] = {};
1140 : :
1141 [ + + ]: 16 : for (auto* ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
1142 [ + - - + ]: 15 : if (!ifa->ifa_addr || !ifa->ifa_name) {
1143 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1144 : : }
1145 : :
1146 [ + + ]: 15 : if (ifa->ifa_addr->sa_family != AF_INET6) {
1147 : 12 : continue;
1148 : : }
1149 : :
1150 : 3 : addr_str[0] = '\0';
1151 : 3 : ::inet_ntop(AF_INET6, &(reinterpret_cast<sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), addr_str, sizeof(addr_str));
1152 : :
1153 [ + + ]: 3 : if (ipv6 == addr_str) {
1154 : 2 : std::string name(ifa->ifa_name);
1155 : 2 : ::freeifaddrs(ifaddr);
1156 : :
1157 : 2 : return name;
1158 : 2 : }
1159 : : }
1160 : :
1161 : 1 : ::freeifaddrs(ifaddr);
1162 : : #endif
1163 : 1 : return {};
1164 : : }
1165 : :
1166 : 31 : std::vector<std::string> get_dds_default_address(bool filter_available, int max_count) noexcept {
1167 [ + + ]: 31 : if VUNLIKELY (max_count <= 0) {
1168 : 2 : return {};
1169 : : }
1170 : :
1171 : 29 : const auto all = get_all_ipv4_address(filter_available);
1172 : 29 : std::vector<std::string> list;
1173 : :
1174 : 29 : list.reserve(max_count);
1175 : :
1176 [ + - ]: 29 : for (const auto& ip : all) {
1177 [ + - ]: 29 : if (ip == "127.0.0.1") {
1178 : 29 : list.emplace_back("127.0.0.1");
1179 : 29 : break;
1180 : : }
1181 : : }
1182 : :
1183 [ + + ]: 81 : for (const auto& ip : all) {
1184 [ + + ]: 55 : if (list.size() >= static_cast<size_t>(max_count)) {
1185 : 3 : break;
1186 : : }
1187 : :
1188 [ + + ]: 52 : if (ip == "127.0.0.1") {
1189 : 26 : continue;
1190 [ - + ]: 26 : } else if (ip == "0.0.0.0") {
1191 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1192 [ - + ]: 26 : } else if (ip == "10.0.0.100") {
1193 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1194 : : }
1195 : :
1196 : 26 : list.emplace_back(ip);
1197 : : }
1198 : :
1199 : 29 : return list;
1200 : 29 : }
1201 : :
1202 : 7 : bool check_singleton(const std::string& program_name) noexcept {
1203 : 7 : std::string check_str;
1204 : :
1205 [ + + ]: 7 : if (program_name.empty()) {
1206 : 1 : check_str = get_app_name();
1207 : : } else {
1208 : 6 : check_str = program_name;
1209 : : }
1210 : :
1211 : : #if defined(_WIN32)
1212 : : HANDLE h_object = ::CreateMutex(nullptr, FALSE, check_str.c_str());
1213 : :
1214 : : if (::GetLastError() == ERROR_ALREADY_EXISTS) {
1215 : : ::CloseHandle(h_object);
1216 : : return false;
1217 : : }
1218 : : #elif defined(_POSIX_VERSION)
1219 : 7 : std::string lock_dir;
1220 : :
1221 [ + + + - ]: 7 : static std::string env_lock_dir = get_env("VLINK_LOCK_DIR");
1222 : :
1223 [ + + ]: 7 : if (env_lock_dir.empty()) {
1224 : : #if defined(__QNX__)
1225 : : lock_dir = "/var/lock";
1226 : : #elif defined(__ANDROID__)
1227 : : lock_dir = "/data/local/tmp";
1228 : : #else
1229 : 2 : lock_dir = get_tmp_dir();
1230 : : #endif
1231 : : } else {
1232 : 5 : lock_dir = env_lock_dir;
1233 : : }
1234 : :
1235 : : try {
1236 [ + - + - : 7 : if (!std::filesystem::exists(lock_dir)) {
+ + ]
1237 [ + - + - ]: 2 : std::filesystem::create_directories(lock_dir);
1238 : : }
1239 : : // LCOV_EXCL_START GCOVR_EXCL_START
1240 : : } catch (std::filesystem::filesystem_error&) {
1241 : : std::cerr << "Can not create lock dir." << std::endl;
1242 : : return false;
1243 : : }
1244 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1245 : :
1246 : 14 : std::string lock_path = lock_dir + "/" + check_str + ".lock";
1247 : :
1248 : 7 : int fd = ::open(lock_path.c_str(), O_CREAT | O_RDWR, 0666);
1249 : :
1250 [ - + ]: 7 : if VUNLIKELY (fd < 0) {
1251 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1252 : : }
1253 : :
1254 [ + + ]: 7 : if VUNLIKELY (::flock(fd, LOCK_EX | LOCK_NB) == -1) {
1255 : 1 : ::close(fd);
1256 : 1 : return false;
1257 : : }
1258 : : #endif
1259 : :
1260 : 6 : return true;
1261 : 7 : }
1262 : :
1263 : 5 : bool wait_for_device(const std::string& path, int timeout_ms, int poll_ms) noexcept {
1264 [ + + ]: 5 : if VUNLIKELY (path.empty()) {
1265 : 1 : return false;
1266 : : }
1267 : :
1268 : 4 : auto start_time = std::chrono::steady_clock::now();
1269 : :
1270 : : for (;;) {
1271 : : try {
1272 [ + - + - : 2180 : if (std::filesystem::exists(path)) {
+ + ]
1273 : 4 : return true;
1274 : : }
1275 : : } catch (std::filesystem::filesystem_error&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1276 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1277 : :
1278 : 2178 : auto current_time = std::chrono::steady_clock::now();
1279 : 2178 : auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - start_time);
1280 : :
1281 [ + - + + : 2178 : if VUNLIKELY (timeout_ms >= 0 && elapsed_time.count() > timeout_ms) {
+ + ]
1282 : 2 : return false;
1283 : : }
1284 : :
1285 [ + + ]: 2176 : if VLIKELY (poll_ms >= 0) {
1286 : 10 : std::this_thread::sleep_for(std::chrono::milliseconds(poll_ms));
1287 : : }
1288 : 2176 : }
1289 : :
1290 : : return false;
1291 : : }
1292 : :
1293 : 2 : void set_console_utf8_output() noexcept {
1294 : : #ifdef _WIN32
1295 : : ::SetConsoleOutputCP(CP_UTF8);
1296 : : ::SetConsoleCP(CP_UTF8);
1297 : : // _setmbcp(CP_UTF8);
1298 : : // SetThreadLocale(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), SORT_DEFAULT));
1299 : : #endif
1300 : 2 : }
1301 : :
1302 : 810 : bool set_thread_name(const std::string& name, std::thread* thread) noexcept {
1303 : : std::thread::native_handle_type native_handle;
1304 : : #ifdef _MSC_VER
1305 : : std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
1306 : : std::wstring wname = converter.from_bytes(name);
1307 : :
1308 : : if (thread) {
1309 : : native_handle = thread->native_handle();
1310 : : } else {
1311 : : native_handle = ::GetCurrentThread();
1312 : : }
1313 : :
1314 : : return SUCCEEDED(::SetThreadDescription(native_handle, wname.c_str()));
1315 : : #elif defined(__linux__) || defined(__QNX__)
1316 : :
1317 [ + + ]: 810 : if (thread) {
1318 : 807 : native_handle = thread->native_handle();
1319 : : } else {
1320 : 3 : native_handle = ::pthread_self();
1321 : : }
1322 : :
1323 : 810 : return ::pthread_setname_np(native_handle, name.c_str()) == 0;
1324 : : #else
1325 : : (void)name;
1326 : : (void)thread;
1327 : : (void)native_handle;
1328 : : // ::fprintf(stderr, "Not support pthread_setname_np.\n");
1329 : : // ::fflush(stderr);
1330 : :
1331 : : return false;
1332 : : #endif
1333 : : }
1334 : :
1335 : 3 : bool set_thread_priority(int priority_level, int policy, std::thread* thread) noexcept {
1336 : : std::thread::native_handle_type native_handle;
1337 : : #ifdef _MSC_VER
1338 : : (void)policy;
1339 : :
1340 : : if (thread) {
1341 : : native_handle = thread->native_handle();
1342 : : } else {
1343 : : native_handle = ::GetCurrentThread();
1344 : : }
1345 : :
1346 : : return ::SetThreadPriority(native_handle, priority_level) != FALSE;
1347 : : #elif defined(__linux__) || defined(__QNX__) || defined(__APPLE__)
1348 : :
1349 [ + + ]: 3 : if (thread) {
1350 : 1 : native_handle = thread->native_handle();
1351 : : } else {
1352 : 2 : native_handle = ::pthread_self();
1353 : : }
1354 : :
1355 : : sched_param sch;
1356 : 3 : int default_policy = 0;
1357 : :
1358 : 3 : ::pthread_getschedparam(native_handle, &default_policy, &sch);
1359 : :
1360 [ + + ]: 3 : if (policy < 0) {
1361 : 1 : policy = default_policy;
1362 : : }
1363 : :
1364 : 3 : sch.sched_priority = priority_level;
1365 : :
1366 : 3 : return ::pthread_setschedparam(native_handle, policy, &sch) == 0;
1367 : : #else
1368 : : (void)priority_level;
1369 : : (void)policy;
1370 : : (void)thread;
1371 : : (void)native_handle;
1372 : : // ::fprintf(stderr, "Not support pthread_setschedparam.\n");
1373 : : // ::fflush(stderr);
1374 : :
1375 : : return false;
1376 : : #endif
1377 : : }
1378 : :
1379 : 3 : bool set_thread_stick(uint32_t core_mask, std::thread* thread) noexcept {
1380 : : std::thread::native_handle_type native_handle;
1381 : :
1382 : : #ifdef _MSC_VER
1383 : :
1384 : : if (thread) {
1385 : : native_handle = thread->native_handle();
1386 : : } else {
1387 : : native_handle = ::GetCurrentThread();
1388 : : }
1389 : :
1390 : : return ::SetThreadAffinityMask(native_handle, core_mask) != 0;
1391 : : #elif defined(__linux__) && !defined(__ANDROID__)
1392 : :
1393 [ + + ]: 3 : if VUNLIKELY (core_mask == 0) {
1394 : 1 : return false;
1395 : : }
1396 : :
1397 : : cpu_set_t cpuset;
1398 : 2 : CPU_ZERO(&cpuset);
1399 : :
1400 : 2 : bool has_cpu = false;
1401 [ + + + - ]: 66 : for (uint32_t cpu = 0; cpu < 32 && cpu < CPU_SETSIZE; ++cpu) {
1402 [ + + ]: 64 : if ((core_mask & (1U << cpu)) != 0) {
1403 [ + - ]: 2 : CPU_SET(cpu, &cpuset);
1404 : 2 : has_cpu = true;
1405 : : }
1406 : : }
1407 : :
1408 [ - + ]: 2 : if VUNLIKELY (!has_cpu) {
1409 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1410 : : }
1411 : :
1412 [ + + ]: 2 : if (thread) {
1413 : 1 : native_handle = thread->native_handle();
1414 : : } else {
1415 : 1 : native_handle = ::pthread_self();
1416 : : }
1417 : :
1418 : 2 : return ::pthread_setaffinity_np(native_handle, sizeof(cpu_set_t), &cpuset) == 0;
1419 : : #else
1420 : : (void)core_mask;
1421 : : (void)thread;
1422 : : (void)native_handle;
1423 : : // ::fprintf(stderr, "Not support pthread_setaffinity_np.\n");
1424 : : // ::fflush(stderr);
1425 : :
1426 : : return false;
1427 : : #endif
1428 : : }
1429 : :
1430 : 337 : uint64_t get_native_thread_id() noexcept {
1431 : : #if defined(_WIN32)
1432 : : return static_cast<uint64_t>(::GetCurrentThreadId());
1433 : : #elif defined(__linux__)
1434 : 337 : return static_cast<uint64_t>(::syscall(SYS_gettid));
1435 : : #elif defined(__FreeBSD__)
1436 : : return static_cast<uint64_t>(::pthread_getthreadid_np());
1437 : : #elif defined(__ANDROID__)
1438 : : #if defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
1439 : : #define SYS_gettid __NR_gettid
1440 : : #endif
1441 : : return static_cast<uint64_t>(::syscall(SYS_gettid));
1442 : : #elif defined(__APPLE__)
1443 : : uint64_t tid = 0;
1444 : :
1445 : : #ifdef MAC_OS_X_VERSION_MAX_ALLOWED
1446 : : {
1447 : : #if (MAC_OS_X_VERSION_MAX_ALLOWED < 1060) || defined(__POWERPC__)
1448 : : tid = ::pthread_mach_thread_np(pthread_self());
1449 : : #elif MAC_OS_X_VERSION_MIN_REQUIRED < 1060
1450 : :
1451 : : if (&::pthread_threadid_np) {
1452 : : ::pthread_threadid_np(nullptr, &tid);
1453 : : } else {
1454 : : tid = ::pthread_mach_thread_np(pthread_self());
1455 : : }
1456 : : #else
1457 : : ::pthread_threadid_np(nullptr, &tid);
1458 : : #endif
1459 : : }
1460 : : #else
1461 : : ::pthread_threadid_np(nullptr, &tid);
1462 : : #endif
1463 : : return static_cast<size_t>(tid);
1464 : : #elif defined(__QNX__)
1465 : : return ::gettid();
1466 : : #else
1467 : : return static_cast<uint64_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
1468 : : #endif
1469 : : }
1470 : :
1471 : 1 : bool is_terminating() noexcept {
1472 : : #if defined(_WIN32)
1473 : : using RtlDllShutdownInProgressT = BOOLEAN(NTAPI*)();
1474 : :
1475 : : static std::atomic<RtlDllShutdownInProgressT> rtl_dll_shutdown_in_progress{nullptr};
1476 : : auto proc = rtl_dll_shutdown_in_progress.load(std::memory_order_relaxed);
1477 : :
1478 : : if VUNLIKELY (proc == nullptr) {
1479 : : proc = reinterpret_cast<RtlDllShutdownInProgressT>(
1480 : : ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "RtlDllShutdownInProgress"));
1481 : : rtl_dll_shutdown_in_progress.store(proc, std::memory_order_relaxed);
1482 : : }
1483 : :
1484 : : return proc != nullptr && proc() != FALSE;
1485 : : #else
1486 : 1 : return false;
1487 : : #endif
1488 : : }
1489 : :
1490 : : // SignalHelper
1491 : : struct SignalHelper final {
1492 : : bool is_async{false};
1493 : : bool pass_through{false};
1494 : : MoveFunction<void(int)> terminate_callback{nullptr};
1495 : : MoveFunction<void(int)> crash_callback{nullptr};
1496 : :
1497 : 8 : static SignalHelper& get() {
1498 [ + + + - : 8 : static SignalHelper instance;
+ - - - ]
1499 : 8 : return instance;
1500 : : }
1501 : :
1502 : 3 : static void on_terminate(int signal) {
1503 [ + - + - : 3 : static auto& instance = SignalHelper::get();
+ - - - ]
1504 : :
1505 : 6 : auto invoke = [signal]() {
1506 [ + - ]: 3 : if (instance.terminate_callback) {
1507 : 3 : instance.terminate_callback(signal);
1508 : : }
1509 : :
1510 [ - + ]: 3 : if (instance.pass_through) {
1511 : 0 : ::signal(signal, SIG_DFL);
1512 : : #ifdef _WIN32
1513 : : ::raise(signal);
1514 : : #else
1515 : 0 : ::kill(::getpid(), signal);
1516 : : #endif
1517 : : }
1518 : 6 : };
1519 : :
1520 [ + + ]: 3 : if (instance.is_async) {
1521 [ + - ]: 1 : std::thread thread(std::move(invoke));
1522 [ + - ]: 1 : thread.detach();
1523 : 1 : } else {
1524 [ + - ]: 2 : invoke();
1525 : : }
1526 : :
1527 : : #ifdef __unix__
1528 [ + - + - : 3 : if (instance.terminate_callback && !instance.pass_through) {
+ - ]
1529 : 3 : ::signal(signal, SIG_IGN);
1530 : : }
1531 : : #endif
1532 : 3 : }
1533 : :
1534 : : // LCOV_EXCL_START GCOVR_EXCL_START
1535 : : static void on_crash(int signal) {
1536 : : static auto& instance = SignalHelper::get();
1537 : :
1538 : : if (instance.crash_callback) {
1539 : : instance.crash_callback(signal);
1540 : : }
1541 : :
1542 : : #ifndef _WIN32
1543 : : ::signal(signal, SIG_DFL);
1544 : : ::raise(signal);
1545 : : #endif
1546 : : }
1547 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1548 : :
1549 : : private:
1550 [ + - ]: 4 : SignalHelper() { MemoryPool::global_instance(); }
1551 : :
1552 : 4 : ~SignalHelper() = default;
1553 : : };
1554 : :
1555 : 5 : void register_terminate_signal(MoveFunction<void(int)>&& callback, bool is_async, bool pass_through) noexcept {
1556 [ + + + - ]: 5 : static auto& instance = SignalHelper::get();
1557 : :
1558 [ - + ]: 5 : if VUNLIKELY (!callback) {
1559 : 0 : instance.terminate_callback = nullptr;
1560 : :
1561 : 0 : ::signal(SIGINT, SIG_DFL);
1562 : 0 : ::signal(SIGTERM, SIG_DFL);
1563 : : #ifndef _WIN32
1564 : 0 : ::signal(SIGHUP, SIG_DFL);
1565 : : #endif
1566 : :
1567 : 0 : return;
1568 : : }
1569 : :
1570 : 5 : instance.terminate_callback = std::move(callback);
1571 : 5 : instance.is_async = is_async;
1572 : 5 : instance.pass_through = pass_through;
1573 : :
1574 : : #ifdef _WIN32
1575 : : static constexpr size_t kTerminateSignals[] = {SIGINT, SIGTERM};
1576 : :
1577 : : for (auto signal : kTerminateSignals) {
1578 : : ::signal(signal, SignalHelper::on_terminate);
1579 : : }
1580 : : #else
1581 : :
1582 : : static constexpr size_t kTerminateSignals[] = {SIGINT, SIGTERM, SIGHUP};
1583 : :
1584 : 5 : struct sigaction act{};
1585 : :
1586 : : #ifdef SA_RESTART
1587 : 5 : act.sa_flags = SA_RESTART;
1588 : : #else
1589 : : act.sa_flags = 0;
1590 : : #endif
1591 : :
1592 : 5 : act.sa_handler = SignalHelper::on_terminate;
1593 : :
1594 : : #ifdef __APPLE__
1595 : : act.sa_mask = 0;
1596 : : #else
1597 : 5 : ::sigemptyset(&act.sa_mask);
1598 : : #endif
1599 : :
1600 [ + + ]: 20 : for (auto signal : kTerminateSignals) {
1601 : 15 : ::sigaction(signal, &act, nullptr);
1602 : : }
1603 : : #endif
1604 : : }
1605 : :
1606 : 1 : void register_crash_signal(MoveFunction<void(int)>&& callback) noexcept {
1607 [ + - + - ]: 1 : static auto& instance = SignalHelper::get();
1608 : :
1609 : 1 : instance.crash_callback = std::move(callback);
1610 : :
1611 : : #ifdef _WIN32
1612 : : static constexpr size_t kCrashSignals[] = {SIGABRT, SIGSEGV, SIGFPE, SIGILL};
1613 : :
1614 : : for (auto signal : kCrashSignals) {
1615 : : ::signal(signal, SignalHelper::on_crash);
1616 : : }
1617 : : #else
1618 : :
1619 : : static constexpr size_t kCrashSignals[] = {SIGABRT, SIGSEGV, SIGFPE, SIGILL, SIGBUS, SIGSYS};
1620 : :
1621 : 1 : struct sigaction act{};
1622 : :
1623 : 1 : act.sa_flags = SA_RESETHAND;
1624 : 1 : act.sa_handler = SignalHelper::on_crash;
1625 : :
1626 : : #ifdef __APPLE__
1627 : : act.sa_mask = 0;
1628 : : #else
1629 : 1 : ::sigemptyset(&act.sa_mask);
1630 : : #endif
1631 : :
1632 [ + + ]: 7 : for (auto signal : kCrashSignals) {
1633 : 6 : ::sigaction(signal, &act, nullptr);
1634 : : }
1635 : : #endif
1636 : 1 : }
1637 : :
1638 : : // KeyboardHelper
1639 : : struct KeyboardHelper final {
1640 : : std::atomic_bool quit_flag{false};
1641 : : std::atomic_bool has_detect{false};
1642 : :
1643 : : std::mutex mtx;
1644 : : ConditionVariable cv;
1645 : : std::thread thread;
1646 : : MoveFunction<void(const std::string& key)> callback;
1647 : :
1648 : 4 : static KeyboardHelper& get() {
1649 [ + + + - : 4 : static KeyboardHelper instance;
+ - - - ]
1650 : 4 : return instance;
1651 : : }
1652 : :
1653 : : private:
1654 [ + - ]: 2 : KeyboardHelper() { MemoryPool::global_instance(); }
1655 : :
1656 : 2 : ~KeyboardHelper() {
1657 : : #ifdef _WIN32
1658 : : if (is_terminating()) {
1659 : : if (thread.joinable()) {
1660 : : thread.detach();
1661 : : }
1662 : :
1663 : : (void)::new (std::nothrow) auto(std::move(callback));
1664 : : return;
1665 : : }
1666 : : #endif
1667 : :
1668 : : {
1669 : 2 : std::lock_guard lock(mtx);
1670 : 2 : quit_flag.store(true, std::memory_order_release);
1671 : 2 : }
1672 : :
1673 : 2 : cv.notify_all();
1674 : :
1675 [ - + ]: 2 : if (thread.joinable()) {
1676 : 0 : thread.join();
1677 : : }
1678 : 2 : }
1679 : : };
1680 : :
1681 : 6 : void start_detect_keyboard(MoveFunction<void(const std::string& key)>&& callback, int poll_ms) noexcept {
1682 [ + + + - ]: 6 : static auto& instance = KeyboardHelper::get();
1683 : :
1684 [ + + ]: 6 : if (instance.has_detect.load(std::memory_order_acquire)) {
1685 [ + - - + : 1 : if (!instance.quit_flag.load(std::memory_order_acquire) || instance.thread.get_id() == std::this_thread::get_id()) {
- + ]
1686 : 3 : return;
1687 : : }
1688 : :
1689 : 1 : stop_detect_keyboard();
1690 : : }
1691 : :
1692 [ + + ]: 6 : if (callback) {
1693 : 4 : instance.callback = std::move(callback);
1694 : : }
1695 : :
1696 [ + + ]: 6 : if VUNLIKELY (!instance.callback) {
1697 : 2 : return;
1698 : : }
1699 : :
1700 : : #ifndef _WIN32
1701 : 4 : termios last_ttystate{};
1702 : :
1703 [ + + ]: 4 : if VUNLIKELY (::tcgetattr(STDIN_FILENO, &last_ttystate) != 0) {
1704 : 1 : return;
1705 : : }
1706 : :
1707 : 3 : termios tmp_ttystate = last_ttystate;
1708 : 3 : tmp_ttystate.c_lflag &= ~(ICANON | ECHO);
1709 : 3 : tmp_ttystate.c_cc[VTIME] = 0;
1710 : 3 : tmp_ttystate.c_cc[VMIN] = 1;
1711 : :
1712 [ - + ]: 3 : if VUNLIKELY (::tcsetattr(STDIN_FILENO, TCSANOW, &tmp_ttystate) != 0) {
1713 : 0 : return;
1714 : : }
1715 : :
1716 : 3 : int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
1717 : :
1718 [ + - - + : 3 : if VUNLIKELY (flags == -1 || ::fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) == -1) {
- + ]
1719 : 0 : ::tcsetattr(STDIN_FILENO, TCSANOW, &last_ttystate);
1720 : 0 : return;
1721 : : }
1722 : : #endif
1723 : :
1724 : 3 : instance.quit_flag.store(false, std::memory_order_release);
1725 : :
1726 : : #ifdef _WIN32
1727 : : auto detector = [poll_ms]() {
1728 : : #else
1729 : 6 : auto detector = [poll_ms, last_ttystate, flags]() {
1730 : : #endif
1731 [ + - ]: 3 : std::unique_lock lock(instance.mtx);
1732 : :
1733 : : #ifdef _WIN32
1734 : : HANDLE hconsole = ::GetStdHandle(STD_INPUT_HANDLE);
1735 : :
1736 : : DWORD hmode = 0;
1737 : : DWORD last_hmode = 0;
1738 : :
1739 : : ::GetConsoleMode(hconsole, &hmode);
1740 : : last_hmode = hmode;
1741 : :
1742 : : hmode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
1743 : : ::SetConsoleMode(hconsole, hmode);
1744 : :
1745 : : while (!instance.quit_flag.load(std::memory_order_acquire)) {
1746 : : INPUT_RECORD input_record[128];
1747 : : DWORD events = 0;
1748 : :
1749 : : auto wait_ret = ::WaitForSingleObject(hconsole, poll_ms / 2);
1750 : :
1751 : : if VLIKELY (wait_ret != WAIT_OBJECT_0) {
1752 : : instance.cv.wait_for(lock, std::chrono::milliseconds(poll_ms / 2));
1753 : : continue;
1754 : : }
1755 : :
1756 : : if (::ReadConsoleInput(hconsole, input_record, 128, &events)) {
1757 : : for (DWORD i = 0; i < events; ++i) {
1758 : : if (input_record[i].EventType == KEY_EVENT && input_record[i].Event.KeyEvent.bKeyDown) {
1759 : : std::string key;
1760 : : auto& event = input_record[i].Event.KeyEvent;
1761 : :
1762 : : if (event.uChar.AsciiChar >= 'A' && event.uChar.AsciiChar <= 'Z') {
1763 : : key = std::string(1, std::tolower(event.uChar.AsciiChar));
1764 : : } else {
1765 : : switch (event.wVirtualKeyCode) {
1766 : : case VK_UP:
1767 : : key = "up";
1768 : : break;
1769 : : case VK_DOWN:
1770 : : key = "down";
1771 : : break;
1772 : : case VK_LEFT:
1773 : : key = "left";
1774 : : break;
1775 : : case VK_RIGHT:
1776 : : key = "right";
1777 : : break;
1778 : : case VK_HOME:
1779 : : key = "home";
1780 : : break;
1781 : : case VK_END:
1782 : : key = "end";
1783 : : break;
1784 : : case VK_PRIOR:
1785 : : key = "pgup";
1786 : : break;
1787 : : case VK_NEXT:
1788 : : key = "pgdown";
1789 : : break;
1790 : : case VK_ESCAPE:
1791 : : key = "esc";
1792 : : break;
1793 : : case VK_BACK:
1794 : : key = "backspace";
1795 : : break;
1796 : : default: {
1797 : : char ascii_char = event.uChar.AsciiChar;
1798 : : if (std::isprint(ascii_char)) {
1799 : : key = std::string(1, std::tolower(ascii_char));
1800 : : } else if (ascii_char == '\n' || ascii_char == '\r') {
1801 : : key = "enter";
1802 : : }
1803 : : break;
1804 : : }
1805 : : }
1806 : : }
1807 : :
1808 : : if (!key.empty()) {
1809 : : instance.callback(key);
1810 : : }
1811 : : }
1812 : : }
1813 : : }
1814 : : }
1815 : :
1816 : : ::SetConsoleMode(hconsole, last_hmode);
1817 : :
1818 : : ::FlushConsoleInputBuffer(hconsole);
1819 : :
1820 : : #else
1821 : 3 : int pending_escape_polls = 0;
1822 : 3 : std::string pending_input;
1823 [ + - ]: 3 : pending_input.reserve(64);
1824 : :
1825 [ + + ]: 9 : while (!instance.quit_flag.load(std::memory_order_acquire)) {
1826 : 6 : char buffer[64] = {0};
1827 : : ssize_t size =
1828 [ + - ]: 6 : ::read(STDIN_FILENO, &buffer, sizeof(buffer)); // NOLINT(clang-analyzer-unix.BlockInCriticalSection)
1829 : :
1830 [ + + ]: 6 : if VUNLIKELY (size > 0) {
1831 : 4 : pending_escape_polls = 0;
1832 [ + - ]: 4 : pending_input.append(buffer, static_cast<size_t>(size));
1833 : :
1834 : 4 : size_t index = 0;
1835 : :
1836 [ + + ]: 28 : while (index < pending_input.size()) {
1837 [ + - + + ]: 25 : if (pending_input[index] == '\033') {
1838 [ + + ]: 19 : if (index + 1 >= pending_input.size()) {
1839 : 1 : break;
1840 : : }
1841 : :
1842 [ + - + + ]: 18 : if (pending_input[index + 1] == 'O') {
1843 [ - + ]: 6 : if (index + 2 >= pending_input.size()) {
1844 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1845 : : }
1846 : :
1847 [ + - + + : 6 : switch (pending_input[index + 2]) {
+ + + +
- ]
1848 : 1 : case 'A':
1849 [ + - + - ]: 1 : instance.callback("up");
1850 : 1 : break;
1851 : 1 : case 'B':
1852 [ + - + - ]: 1 : instance.callback("down");
1853 : 1 : break;
1854 : 1 : case 'C':
1855 [ + - + - ]: 1 : instance.callback("right");
1856 : 1 : break;
1857 : 1 : case 'D':
1858 [ + - + - ]: 1 : instance.callback("left");
1859 : 1 : break;
1860 : 1 : case 'H':
1861 [ + - + - ]: 1 : instance.callback("home");
1862 : 1 : break;
1863 : 1 : case 'F':
1864 [ + - + - ]: 1 : instance.callback("end");
1865 : 1 : break;
1866 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1867 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1868 : : }
1869 : :
1870 : 6 : index += 3;
1871 : :
1872 : 6 : continue;
1873 : : }
1874 : :
1875 [ + - + + ]: 12 : if (pending_input[index + 1] == '[') {
1876 : 11 : size_t seq_end = index + 2;
1877 : :
1878 [ + - + + ]: 32 : while (seq_end < pending_input.size() &&
1879 [ + - + + : 16 : !(pending_input[seq_end] >= '@' && pending_input[seq_end] <= '~')) {
+ - - + ]
1880 : 5 : ++seq_end;
1881 : : }
1882 : :
1883 [ - + ]: 11 : if (seq_end >= pending_input.size()) {
1884 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1885 : : }
1886 : :
1887 [ + - ]: 11 : const char key = pending_input[seq_end];
1888 : :
1889 [ + - + + ]: 11 : if (key >= 'A' && key <= 'Z') {
1890 [ + + + + : 6 : switch (key) {
+ + - ]
1891 : 1 : case 'A':
1892 [ + - + - ]: 1 : instance.callback("up");
1893 : 1 : break;
1894 : 1 : case 'B':
1895 [ + - + - ]: 1 : instance.callback("down");
1896 : 1 : break;
1897 : 1 : case 'C':
1898 [ + - + - ]: 1 : instance.callback("right");
1899 : 1 : break;
1900 : 1 : case 'D':
1901 [ + - + - ]: 1 : instance.callback("left");
1902 : 1 : break;
1903 : 1 : case 'H':
1904 [ + - + - ]: 1 : instance.callback("home");
1905 : 1 : break;
1906 : 1 : case 'F':
1907 [ + - + - ]: 1 : instance.callback("end");
1908 : 1 : break;
1909 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1910 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1911 : : }
1912 : 6 : index = seq_end + 1;
1913 : 6 : continue;
1914 : : }
1915 : :
1916 [ + - ]: 5 : if (key == '~') {
1917 [ + - ]: 5 : const std::string code = pending_input.substr(index + 2, seq_end - (index + 2));
1918 : :
1919 [ + + - + : 5 : if (code == "1" || code == "7") {
+ + ]
1920 [ + - + - ]: 1 : instance.callback("home");
1921 [ + + - + : 4 : } else if (code == "4" || code == "8") {
+ + ]
1922 [ + - + - ]: 1 : instance.callback("end");
1923 [ + + ]: 3 : } else if (code == "5") {
1924 [ + - + - ]: 1 : instance.callback("pgup");
1925 [ + + ]: 2 : } else if (code == "6") {
1926 [ + - + - ]: 1 : instance.callback("pgdown");
1927 : : }
1928 : :
1929 : 5 : index = seq_end + 1;
1930 : :
1931 : 5 : continue;
1932 : 5 : }
1933 : :
1934 : : index = seq_end + 1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1935 : :
1936 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1937 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1938 : :
1939 [ + - + - ]: 1 : instance.callback("esc");
1940 : 1 : ++index;
1941 : :
1942 : 1 : continue;
1943 : 1 : }
1944 : :
1945 [ + - ]: 6 : const auto ch = static_cast<unsigned char>(pending_input[index]);
1946 : :
1947 [ + + - + ]: 6 : if (ch == '\n' || ch == '\r') {
1948 [ + - + - ]: 1 : instance.callback("enter");
1949 [ + + - + ]: 5 : } else if (ch == 0x7f || ch == 0x08) {
1950 [ + - + - ]: 1 : instance.callback("backspace");
1951 [ + - ]: 4 : } else if (std::isprint(ch)) {
1952 [ + - + - ]: 4 : instance.callback(std::string(1, static_cast<char>(std::tolower(ch))));
1953 : : }
1954 : :
1955 : 6 : ++index;
1956 : : }
1957 : :
1958 [ + + ]: 4 : if (index > 0) {
1959 [ + - ]: 3 : pending_input.erase(0, index);
1960 : : }
1961 [ + - ]: 2 : } else if (pending_input == "\033") {
1962 : 2 : ++pending_escape_polls;
1963 : :
1964 [ + + ]: 2 : if (pending_escape_polls >= 2) {
1965 [ + - + - ]: 1 : instance.callback("esc");
1966 : 1 : pending_input.clear();
1967 : 1 : pending_escape_polls = 0;
1968 : : }
1969 : : }
1970 : :
1971 : 6 : instance.cv.wait_for(lock, std::chrono::milliseconds(poll_ms));
1972 : : }
1973 : :
1974 [ + - ]: 3 : ::fcntl(STDIN_FILENO, F_SETFL, flags);
1975 : 3 : ::tcsetattr(STDIN_FILENO, TCSANOW, &last_ttystate);
1976 : : #endif
1977 : 3 : };
1978 : :
1979 : : try {
1980 [ + - ]: 3 : instance.thread = std::thread(std::move(detector));
1981 : : } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1982 : : #ifndef _WIN32
1983 : : ::fcntl(STDIN_FILENO, F_SETFL, flags); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1984 : : ::tcsetattr(STDIN_FILENO, TCSANOW, &last_ttystate); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1985 : : #endif
1986 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1987 : 0 : }
1988 : :
1989 : 3 : instance.has_detect.store(true, std::memory_order_release);
1990 : : }
1991 : :
1992 : 9 : void stop_detect_keyboard() noexcept {
1993 [ + + + - ]: 9 : static auto& instance = KeyboardHelper::get();
1994 : :
1995 [ + + ]: 9 : if (instance.thread.get_id() == std::this_thread::get_id()) {
1996 : 2 : instance.quit_flag.store(true, std::memory_order_release);
1997 : 6 : return;
1998 : : }
1999 : :
2000 : 7 : bool expected = true;
2001 : :
2002 [ + + ]: 7 : if VUNLIKELY (!instance.has_detect.compare_exchange_strong(expected, false, std::memory_order_acq_rel,
2003 : : std::memory_order_relaxed)) {
2004 : 4 : return;
2005 : : }
2006 : :
2007 : : {
2008 : 3 : std::lock_guard lock(instance.mtx);
2009 : 3 : instance.quit_flag.store(true, std::memory_order_release);
2010 : 3 : }
2011 : :
2012 : 3 : instance.cv.notify_all();
2013 : :
2014 [ + - ]: 3 : if VLIKELY (instance.thread.joinable()) {
2015 : 3 : instance.thread.join();
2016 : : }
2017 : : }
2018 : :
2019 : 1 : std::pair<int, int> get_terminal_size() noexcept {
2020 : : #ifdef _WIN32
2021 : : CONSOLE_SCREEN_BUFFER_INFO size;
2022 : :
2023 : : if VLIKELY (::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &size)) {
2024 : : return std::pair<int, int>{size.srWindow.Right - size.srWindow.Left + 1,
2025 : : size.srWindow.Bottom - size.srWindow.Top + 1};
2026 : : }
2027 : : #else
2028 : : struct winsize size;
2029 : :
2030 [ - + - - : 1 : if VLIKELY (::ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
- + - - -
+ ]
2031 : : return std::pair<int, int>{size.ws_col, size.ws_row}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2032 : : }
2033 : :
2034 [ - + - - : 1 : if VLIKELY (::ioctl(STDERR_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
- + - - -
+ ]
2035 : : return std::pair<int, int>{size.ws_col, size.ws_row}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2036 : : }
2037 : :
2038 [ - + - - : 1 : if VLIKELY (::ioctl(STDIN_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
- + - - -
+ ]
2039 : : return std::pair<int, int>{size.ws_col, size.ws_row}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2040 : : }
2041 : :
2042 : : {
2043 : 1 : int fd = ::open("/dev/tty", O_RDONLY);
2044 : :
2045 [ - + ]: 1 : if VLIKELY (fd >= 0) {
2046 : : // LCOV_EXCL_START GCOVR_EXCL_START
2047 : : if VLIKELY (::ioctl(fd, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
2048 : : ::close(fd);
2049 : : return std::pair<int, int>{size.ws_col, size.ws_row};
2050 : : }
2051 : :
2052 : : ::close(fd);
2053 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2054 : : }
2055 : : }
2056 : : #endif
2057 : :
2058 : 1 : return std::pair<int, int>{-1, -1};
2059 : : }
2060 : :
2061 : 83 : double get_cpu_usage() noexcept {
2062 : : static std::mutex sample_mtx;
2063 : 83 : std::lock_guard sample_lock(sample_mtx);
2064 : :
2065 : : #ifdef _WIN32
2066 : : FILETIME idle_time;
2067 : : FILETIME kernel_time;
2068 : : FILETIME user_time;
2069 : : GetSystemTimes(&idle_time, &kernel_time, &user_time);
2070 : :
2071 : : ULARGE_INTEGER idle;
2072 : : ULARGE_INTEGER kernel;
2073 : : ULARGE_INTEGER user;
2074 : : idle.LowPart = idle_time.dwLowDateTime;
2075 : : idle.HighPart = idle_time.dwHighDateTime;
2076 : : kernel.LowPart = kernel_time.dwLowDateTime;
2077 : : kernel.HighPart = kernel_time.dwHighDateTime;
2078 : : user.LowPart = user_time.dwLowDateTime;
2079 : : user.HighPart = user_time.dwHighDateTime;
2080 : :
2081 : : static ULARGE_INTEGER last_idle_time = {{0, 0}};
2082 : : static ULARGE_INTEGER last_kernel_time = {{0, 0}};
2083 : : static ULARGE_INTEGER last_user_time = {{0, 0}};
2084 : :
2085 : : ULARGE_INTEGER total;
2086 : : total.QuadPart = (kernel.QuadPart - last_kernel_time.QuadPart) + (user.QuadPart - last_user_time.QuadPart);
2087 : : ULARGE_INTEGER idle_diff;
2088 : : idle_diff.QuadPart = idle.QuadPart - last_idle_time.QuadPart;
2089 : :
2090 : : last_idle_time = idle;
2091 : : last_kernel_time = kernel;
2092 : : last_user_time = user;
2093 : :
2094 : : if VUNLIKELY (total.QuadPart == 0) {
2095 : : return -1;
2096 : : }
2097 : :
2098 : : return (1.0 - (static_cast<double>(idle_diff.QuadPart) / total.QuadPart)) * 100.0;
2099 : : #elif defined(__linux__)
2100 : 83 : std::ifstream cpu_file("/proc/stat");
2101 : 83 : std::string line;
2102 : 83 : std::getline(cpu_file, line);
2103 : :
2104 [ + + ]: 83 : thread_local std::istringstream iss;
2105 : 83 : iss.clear();
2106 : 83 : iss.str(line);
2107 : :
2108 : 83 : std::string cpu;
2109 : 83 : uint64_t user = 0;
2110 : 83 : uint64_t nice = 0;
2111 : 83 : uint64_t system = 0;
2112 : 83 : uint64_t idle = 0;
2113 : 83 : uint64_t io_wait = 0;
2114 : 83 : uint64_t irq = 0;
2115 : 83 : uint64_t soft_irq = 0;
2116 : 83 : uint64_t steal = 0;
2117 : :
2118 : 83 : iss >> cpu >> user >> nice >> system >> idle;
2119 : :
2120 [ + - - + : 83 : if VUNLIKELY (!iss || cpu != "cpu") {
- + ]
2121 : 0 : return -1;
2122 : : }
2123 : :
2124 : 83 : iss >> io_wait >> irq >> soft_irq >> steal;
2125 : :
2126 : : static uint64_t last_idle = 0;
2127 : : static uint64_t last_total = 0;
2128 : :
2129 : 83 : uint64_t idle_all = idle + io_wait;
2130 : 83 : uint64_t total = user + nice + system + idle_all + irq + soft_irq + steal;
2131 : :
2132 [ + - - + : 83 : if VUNLIKELY (idle_all < last_idle || total < last_total) {
- + ]
2133 : 0 : last_idle = idle_all;
2134 : 0 : last_total = total;
2135 : 0 : return -1;
2136 : : }
2137 : :
2138 : 83 : uint64_t idle_diff = idle_all - last_idle;
2139 : 83 : uint64_t total_diff = total - last_total;
2140 : :
2141 : 83 : last_idle = idle_all;
2142 : 83 : last_total = total;
2143 : :
2144 [ + + - + : 83 : if VUNLIKELY (total_diff == 0 || idle_diff > total_diff) {
+ + ]
2145 : 82 : return -1;
2146 : : }
2147 : :
2148 : 1 : return (1.0 - static_cast<double>(idle_diff) / total_diff) * 100.0;
2149 : : #elif defined(__APPLE__)
2150 : : host_cpu_load_info_data_t cpu_info;
2151 : : mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
2152 : : kern_return_t kr = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpu_info, &count);
2153 : :
2154 : : if VUNLIKELY (kr != KERN_SUCCESS) {
2155 : : return -1;
2156 : : }
2157 : :
2158 : : static uint64_t last_user = 0;
2159 : : static uint64_t last_nice = 0;
2160 : : static uint64_t last_system = 0;
2161 : : static uint64_t last_idle = 0;
2162 : :
2163 : : uint64_t user = cpu_info.cpu_ticks[CPU_STATE_USER];
2164 : : uint64_t nice = cpu_info.cpu_ticks[CPU_STATE_NICE];
2165 : : uint64_t system = cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
2166 : : uint64_t idle = cpu_info.cpu_ticks[CPU_STATE_IDLE];
2167 : :
2168 : : uint64_t total = (user - last_user) + (nice - last_nice) + (system - last_system) + (idle - last_idle);
2169 : : uint64_t idle_diff = idle - last_idle;
2170 : :
2171 : : last_user = user;
2172 : : last_nice = nice;
2173 : : last_system = system;
2174 : : last_idle = idle;
2175 : :
2176 : : if VUNLIKELY (total == 0) {
2177 : : return -1;
2178 : : }
2179 : :
2180 : : return (1.0 - static_cast<double>(idle_diff) / total) * 100.0;
2181 : : #elif defined(__QNX__)
2182 : : uint64_t current_time = 0;
2183 : :
2184 : : if VUNLIKELY (::ClockTime(CLOCK_MONOTONIC, nullptr, ¤t_time) != 0) {
2185 : : return -1;
2186 : : }
2187 : :
2188 : : const uint16_t cpu_count = _syspage_ptr->num_cpu;
2189 : :
2190 : : if VUNLIKELY (cpu_count == 0) {
2191 : : return -1;
2192 : : }
2193 : :
2194 : : uint64_t idle_time = 0;
2195 : :
2196 : : for (uint16_t cpu = 0; cpu < cpu_count; ++cpu) {
2197 : : const int clock_id = ::ClockId(1, static_cast<int>(cpu + 1));
2198 : : uint64_t cpu_idle_time = 0;
2199 : :
2200 : : if VUNLIKELY (clock_id < 0 || ::ClockTime(clock_id, nullptr, &cpu_idle_time) != 0) {
2201 : : return -1;
2202 : : }
2203 : :
2204 : : idle_time += cpu_idle_time;
2205 : : }
2206 : :
2207 : : static uint64_t last_time = 0;
2208 : : static uint64_t last_idle_time = 0;
2209 : :
2210 : : if VUNLIKELY (current_time < last_time || idle_time < last_idle_time) {
2211 : : last_time = current_time;
2212 : : last_idle_time = idle_time;
2213 : : return -1;
2214 : : }
2215 : :
2216 : : const uint64_t time_diff = current_time - last_time;
2217 : : const uint64_t idle_diff = idle_time - last_idle_time;
2218 : :
2219 : : last_time = current_time;
2220 : : last_idle_time = idle_time;
2221 : :
2222 : : const double total_time = static_cast<double>(time_diff) * cpu_count;
2223 : :
2224 : : if VUNLIKELY (total_time == 0 || static_cast<double>(idle_diff) > total_time) {
2225 : : return -1;
2226 : : }
2227 : :
2228 : : return (1.0 - static_cast<double>(idle_diff) / total_time) * 100.0;
2229 : : #else
2230 : : return -1;
2231 : : #endif
2232 : 83 : }
2233 : :
2234 : 3 : double get_memory_usage() noexcept {
2235 : : #ifdef _WIN32
2236 : : MEMORYSTATUSEX mem_status;
2237 : : mem_status.dwLength = sizeof(MEMORYSTATUSEX);
2238 : : GlobalMemoryStatusEx(&mem_status);
2239 : :
2240 : : if VUNLIKELY (mem_status.ullTotalPhys == 0) {
2241 : : return -1;
2242 : : }
2243 : :
2244 : : return (static_cast<double>(mem_status.ullTotalPhys - mem_status.ullAvailPhys) / mem_status.ullTotalPhys) * 100.0;
2245 : : #elif defined(__linux__)
2246 : 3 : std::ifstream mem_file("/proc/meminfo");
2247 : :
2248 [ - + ]: 3 : if (!mem_file.is_open()) {
2249 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2250 : : }
2251 : :
2252 : 3 : std::string key;
2253 : 3 : int64_t mem_total = 0;
2254 : 3 : int64_t mem_free = 0;
2255 : 3 : int64_t mem_available = 0;
2256 : 3 : int64_t buffers = 0;
2257 : 3 : int64_t cached = 0;
2258 : 3 : bool has_mem_available = false;
2259 : :
2260 [ + + ]: 168 : while (mem_file >> key) {
2261 [ + + ]: 165 : if (key == "MemTotal:") {
2262 : 3 : mem_file >> mem_total;
2263 [ + + ]: 162 : } else if (key == "MemAvailable:") {
2264 : 3 : mem_file >> mem_available;
2265 : 3 : has_mem_available = true;
2266 [ + + ]: 159 : } else if (key == "MemFree:") {
2267 : 3 : mem_file >> mem_free;
2268 [ + + ]: 156 : } else if (key == "Buffers:") {
2269 : 3 : mem_file >> buffers;
2270 [ + + ]: 153 : } else if (key == "Cached:") {
2271 : 3 : mem_file >> cached;
2272 : : }
2273 : :
2274 : 165 : mem_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
2275 : : }
2276 : :
2277 [ - + ]: 3 : if VUNLIKELY (mem_total == 0) {
2278 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2279 : : }
2280 : :
2281 [ - + ]: 3 : if (!has_mem_available) {
2282 : 0 : mem_available = mem_free + buffers + cached;
2283 : : }
2284 : :
2285 : 3 : return (static_cast<double>(mem_total - mem_available) / mem_total) * 100.0;
2286 : : #elif defined(__APPLE__)
2287 : : vm_size_t page_size;
2288 : : mach_port_t mach_port = mach_host_self();
2289 : : mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
2290 : : vm_statistics_data_t vm_stats;
2291 : :
2292 : : if VUNLIKELY (host_page_size(mach_port, &page_size) != KERN_SUCCESS) {
2293 : : return -1;
2294 : : }
2295 : :
2296 : : if VUNLIKELY (host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count) != KERN_SUCCESS) {
2297 : : return -1;
2298 : : }
2299 : :
2300 : : uint64_t free_memory = static_cast<uint64_t>(vm_stats.free_count) * page_size;
2301 : : uint64_t active_memory = static_cast<uint64_t>(vm_stats.active_count) * page_size;
2302 : : uint64_t inactive_memory = static_cast<uint64_t>(vm_stats.inactive_count) * page_size;
2303 : : uint64_t wired_memory = static_cast<uint64_t>(vm_stats.wire_count) * page_size;
2304 : : uint64_t total_memory = free_memory + active_memory + inactive_memory + wired_memory;
2305 : :
2306 : : if VUNLIKELY (total_memory == 0) {
2307 : : return -1;
2308 : : }
2309 : :
2310 : : return (static_cast<double>(active_memory + inactive_memory + wired_memory) / total_memory) * 100.0;
2311 : : #elif defined(__QNX__)
2312 : : struct stat proc_status{};
2313 : :
2314 : : if VUNLIKELY (::stat("/proc", &proc_status) != 0 || proc_status.st_size < 0) {
2315 : : return -1;
2316 : : }
2317 : :
2318 : : const auto* strings = SYSPAGE_ENTRY(strings)->data;
2319 : : const size_t entry_count = SYSPAGE_ENTRY_SIZE(asinfo) / SYSPAGE_ELEMENT_SIZE(asinfo);
2320 : : uint64_t total_memory = 0;
2321 : :
2322 : : for (size_t index = 0; index < entry_count; ++index) {
2323 : : const auto* entry = SYSPAGE_ARRAY_IDX(asinfo, index);
2324 : :
2325 : : if (std::strcmp(strings + entry->name, "ram") == 0) {
2326 : : total_memory += entry->end - entry->start + 1;
2327 : : }
2328 : : }
2329 : :
2330 : : const uint64_t available_memory = static_cast<uint64_t>(proc_status.st_size);
2331 : :
2332 : : if VUNLIKELY (total_memory == 0 || available_memory > total_memory) {
2333 : : return -1;
2334 : : }
2335 : :
2336 : : return static_cast<double>(total_memory - available_memory) / total_memory * 100.0;
2337 : : #else
2338 : : return -1;
2339 : : #endif
2340 : 3 : }
2341 : :
2342 : 5 : bool is_process_running(const std::string& process_name) noexcept {
2343 [ + + ]: 5 : if VUNLIKELY (process_name.empty()) {
2344 : 1 : return false;
2345 : : }
2346 : :
2347 : : #ifdef _WIN32
2348 : : HANDLE hsnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
2349 : :
2350 : : if VUNLIKELY (hsnapshot == INVALID_HANDLE_VALUE) {
2351 : : return false;
2352 : : }
2353 : :
2354 : : PROCESSENTRY32 pe32;
2355 : : pe32.dwSize = sizeof(PROCESSENTRY32);
2356 : :
2357 : : if (::Process32First(hsnapshot, &pe32)) {
2358 : : do {
2359 : : if (process_name == pe32.szExeFile) {
2360 : : ::CloseHandle(hsnapshot);
2361 : : return true;
2362 : : }
2363 : : } while (::Process32Next(hsnapshot, &pe32));
2364 : : }
2365 : :
2366 : : ::CloseHandle(hsnapshot);
2367 : : return false;
2368 : : #else
2369 : 4 : const bool safe_name = std::all_of(process_name.begin(), process_name.end(), [](unsigned char c) {
2370 [ + + + + : 83 : return std::isalnum(c) != 0 || c == '_' || c == '-' || c == '.';
+ + + + ]
2371 : : });
2372 : :
2373 [ + + ]: 4 : if VUNLIKELY (!safe_name) {
2374 : 1 : return false;
2375 : : }
2376 : :
2377 : 3 : std::string process_pattern;
2378 : 3 : process_pattern.reserve(process_name.size() + 4U);
2379 : :
2380 [ + + ]: 67 : for (char c : process_name) {
2381 [ + + ]: 64 : if (c == '.') {
2382 : 1 : process_pattern.append("\\.");
2383 : : } else {
2384 : 63 : process_pattern.push_back(c);
2385 : : }
2386 : : }
2387 : :
2388 : 3 : std::ostringstream oss;
2389 : :
2390 : : #if defined(__QNX__)
2391 : : oss << "pidin | grep -- '" << process_pattern << "' | grep -v grep > /dev/null";
2392 : : #elif defined(__ANDROID__)
2393 : : oss << "ps -A | grep -- '" << process_pattern << "' | grep -v grep > /dev/null";
2394 : : #elif defined(__APPLE__)
2395 : : oss << "pgrep -x -- '" << process_pattern << "' > /dev/null 2>&1";
2396 : : #elif defined(__linux__)
2397 : 3 : oss << "pgrep -x -- '" << process_pattern << "' > /dev/null 2>&1";
2398 : : #else
2399 : : oss << "ps aux | grep -- '" << process_pattern << "' | grep -v grep > /dev/null";
2400 : : #endif
2401 : :
2402 : 3 : std::string command = oss.str();
2403 : :
2404 : : // NOLINTNEXTLINE(bugprone-command-processor)
2405 : 3 : return std::system(command.c_str()) == 0;
2406 : : #endif
2407 : 3 : }
2408 : :
2409 : 325 : int32_t get_timezone_diff() noexcept {
2410 : : #ifdef _WIN32
2411 : : TIME_ZONE_INFORMATION tzi;
2412 : : DWORD result = GetTimeZoneInformation(&tzi);
2413 : : int bias = tzi.Bias;
2414 : :
2415 : : if (result == TIME_ZONE_ID_DAYLIGHT) {
2416 : : bias += tzi.DaylightBias;
2417 : : } else if (result == TIME_ZONE_ID_STANDARD) {
2418 : : bias += tzi.StandardBias;
2419 : : }
2420 : :
2421 : : return -bias;
2422 : : #else
2423 : 325 : std::time_t now = std::time(nullptr);
2424 : 325 : std::tm local_tm = *std::localtime(&now);
2425 : : #ifdef __APPLE__
2426 : : return local_tm.tm_gmtoff / 60;
2427 : : #else
2428 : 325 : std::tm gm_tm = *std::gmtime(&now);
2429 : 325 : gm_tm.tm_isdst = local_tm.tm_isdst;
2430 : 325 : int diff_seconds = std::mktime(&local_tm) - std::mktime(&gm_tm);
2431 : :
2432 : 325 : return diff_seconds / 60;
2433 : : #endif
2434 : : #endif
2435 : : }
2436 : :
2437 : 1 : void try_release_sys_memory() noexcept {
2438 : : #if defined(__linux__) && !defined(__ANDROID__)
2439 : 1 : malloc_trim(0);
2440 : : #endif
2441 : 1 : }
2442 : :
2443 : 3 : std::string get_machine_id() noexcept {
2444 : : #ifdef _WIN32
2445 : : HKEY hkey;
2446 : : const char* subkey = "SOFTWARE\\Microsoft\\Cryptography";
2447 : : const char* value_name = "MachineGuid";
2448 : :
2449 : : char value[256];
2450 : : DWORD value_length = sizeof(value);
2451 : : LONG result;
2452 : :
2453 : : result = ::RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hkey);
2454 : :
2455 : : if (result != ERROR_SUCCESS) {
2456 : : return "";
2457 : : }
2458 : :
2459 : : result = ::RegQueryValueExA(hkey, value_name, nullptr, nullptr, reinterpret_cast<LPBYTE>(value), &value_length);
2460 : : ::RegCloseKey(hkey);
2461 : :
2462 : : if (result == ERROR_SUCCESS) {
2463 : : return std::string(value, value_length - 1);
2464 : : }
2465 : : #elif defined(__linux__)
2466 : 3 : const char* paths[] = {
2467 : : "/etc/machine-id",
2468 : : "/var/lib/dbus/machine-id",
2469 : : };
2470 : :
2471 : 3 : std::string id;
2472 : :
2473 [ + + ]: 9 : for (const char* path : paths) {
2474 : : try {
2475 [ + - + - : 6 : if (!std::filesystem::exists(path)) {
+ + ]
2476 : : // LCOV_EXCL_START GCOVR_EXCL_START
2477 : : continue;
2478 : : }
2479 : : } catch (std::filesystem::filesystem_error&) {
2480 : : continue;
2481 : : }
2482 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2483 : :
2484 : 3 : std::ifstream file(path);
2485 : :
2486 [ + - ]: 3 : if (file) {
2487 : 3 : std::getline(file, id);
2488 : :
2489 : 3 : id.erase(std::remove_if(id.begin(), id.end(), ::isspace), id.end());
2490 : :
2491 [ - + ]: 3 : if (!id.empty()) {
2492 : 0 : return id;
2493 : : }
2494 : : }
2495 [ + - ]: 3 : }
2496 : : #elif defined(__QNX__)
2497 : : struct ifaddrs* interfaces = nullptr;
2498 : :
2499 : : if VUNLIKELY (::getifaddrs(&interfaces) != 0) {
2500 : : return {};
2501 : : }
2502 : :
2503 : : std::array<uint8_t, 6> machine_address{};
2504 : : bool found = false;
2505 : :
2506 : : for (auto* interface = interfaces; interface != nullptr; interface = interface->ifa_next) {
2507 : : if (interface->ifa_addr == nullptr || interface->ifa_addr->sa_family != AF_LINK ||
2508 : : (interface->ifa_flags & IFF_LOOPBACK) != 0) {
2509 : : continue;
2510 : : }
2511 : :
2512 : : const auto* link_address = reinterpret_cast<const struct sockaddr_dl*>(interface->ifa_addr);
2513 : :
2514 : : if (link_address->sdl_alen != machine_address.size()) {
2515 : : continue;
2516 : : }
2517 : :
2518 : : const auto* address = reinterpret_cast<const uint8_t*>(LLADDR(link_address));
2519 : : const bool valid = (address[0] & 0x01U) == 0 &&
2520 : : std::any_of(address, address + machine_address.size(), [](uint8_t value) { return value != 0; });
2521 : :
2522 : : if (valid && (!found || std::lexicographical_compare(address, address + machine_address.size(),
2523 : : machine_address.begin(), machine_address.end()))) {
2524 : : std::copy_n(address, machine_address.size(), machine_address.begin());
2525 : : found = true;
2526 : : }
2527 : : }
2528 : :
2529 : : ::freeifaddrs(interfaces);
2530 : :
2531 : : if VUNLIKELY (!found) {
2532 : : return {};
2533 : : }
2534 : :
2535 : : static constexpr char kHexDigits[] = "0123456789abcdef";
2536 : : std::string id;
2537 : : id.reserve(machine_address.size() * 2);
2538 : :
2539 : : for (uint8_t value : machine_address) {
2540 : : id.push_back(kHexDigits[value >> 4U]);
2541 : : id.push_back(kHexDigits[value & 0x0fU]);
2542 : : }
2543 : :
2544 : : return id;
2545 : : #endif
2546 : :
2547 : : return std::string(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2548 : 3 : }
2549 : :
2550 : : } // namespace Utils
2551 : :
2552 : : } // namespace vlink
|