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