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 : 196 : std::string get_app_path() noexcept {
118 [ + + + - ]: 196 : static std::string file_path;
119 : : static std::once_flag flag;
120 : :
121 : 97 : static auto trim_trailing = [](char* buffer, size_t size) -> size_t {
122 [ + - - + : 97 : while (size > 0 && (buffer[size - 1] == '\n' || buffer[size - 1] == '\r' || buffer[size - 1] == ' ' ||
- + - + ]
123 [ - + ]: 97 : buffer[size - 1] == '\0')) {
124 : : --size; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
125 : : }
126 : :
127 : 97 : buffer[size] = '\0';
128 : :
129 : 97 : return size;
130 : : };
131 : :
132 : 97 : 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 : 97 : char resolved[PATH_MAX] = {0};
144 : :
145 [ + - ]: 97 : if (::realpath(path, resolved) != nullptr) {
146 [ + - ]: 97 : result.assign(resolved);
147 : 97 : return true;
148 : : }
149 : : #endif
150 : :
151 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
152 : : };
153 : :
154 : 196 : 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 : 97 : char buffer[PATH_MAX] = {0};
169 : 97 : auto size = ::readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
170 : :
171 [ + - + - : 97 : if VLIKELY (size > 0 && size < PATH_MAX) {
+ - ]
172 : 97 : trim_trailing(buffer, static_cast<size_t>(size));
173 : :
174 [ + - - + ]: 97 : 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 : 97 : });
221 : :
222 : 196 : return file_path;
223 : : }
224 : :
225 : 14 : std::string get_app_dir() noexcept {
226 : 14 : std::string path = get_app_path();
227 : :
228 [ - + ]: 14 : if VUNLIKELY (path.empty()) {
229 : : return path; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
230 : : }
231 : :
232 : 14 : return path.substr(0, path.rfind('/'));
233 : 14 : }
234 : :
235 : 154 : std::string get_app_name() noexcept {
236 : 154 : std::string path = get_app_path();
237 : :
238 [ - + ]: 154 : if VUNLIKELY (path.empty()) {
239 : : return path; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
240 : : }
241 : :
242 : 154 : 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 : 154 : return app_name;
254 : 154 : }
255 : :
256 : 52 : std::string get_host_name() noexcept {
257 : : char hostname[256];
258 : :
259 [ + - ]: 52 : if VLIKELY (::gethostname(hostname, sizeof(hostname)) == 0) {
260 : 52 : return hostname;
261 : : } else {
262 : : return ""; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
263 : : }
264 : : }
265 : :
266 : 95 : int32_t get_pid() noexcept {
267 : : #ifdef _WIN32
268 : : return static_cast<int32_t>(_getpid());
269 : : #else
270 : 95 : return static_cast<int32_t>(getpid());
271 : : #endif
272 : : }
273 : :
274 : 495 : std::string get_pid_str() noexcept {
275 [ + + + - ]: 495 : static auto pid = get_pid();
276 : :
277 [ + + + - ]: 495 : static auto pid_str = std::to_string(pid);
278 : :
279 : 495 : return pid_str;
280 : : }
281 : :
282 : 341 : std::string get_tmp_dir() noexcept {
283 [ + + + - ]: 341 : static std::string env_tmp_dir = get_env("VLINK_TMP_DIR");
284 : :
285 [ + + ]: 341 : if (!env_tmp_dir.empty()) {
286 : 1 : return env_tmp_dir;
287 : : }
288 : :
289 : 340 : std::string tmp_dir;
290 : :
291 : : #if defined(__QNX__)
292 : : tmp_dir = "/var/log";
293 : : #else
294 : : try {
295 [ + - + - ]: 340 : 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 : 340 : return tmp_dir;
310 : 340 : }
311 : :
312 : 4495 : 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 : 4495 : const char* value = std::getenv(key.c_str());
359 : :
360 [ + + ]: 4495 : if (!value) {
361 : 4394 : return default_value;
362 : : }
363 : :
364 : 101 : return std::string(value);
365 : : #endif
366 : : }
367 : :
368 : 8 : 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 : 8 : 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 : 27 : std::vector<std::string> get_all_ipv4_address(bool filter_available) noexcept {
691 : 27 : 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 : 27 : struct ifaddrs* if_addr_struct = nullptr;
744 : 27 : struct ifaddrs* ifa = nullptr;
745 : 27 : void* tmp_addr_ptr = nullptr;
746 : :
747 : 27 : ::getifaddrs(&if_addr_struct);
748 : :
749 : : char ip_buffer[INET_ADDRSTRLEN];
750 : :
751 [ + + ]: 162 : for (ifa = if_addr_struct; ifa != nullptr; ifa = ifa->ifa_next) {
752 [ + - - + ]: 135 : if (ifa->ifa_addr == nullptr || !(ifa->ifa_flags & IFF_UP)) {
753 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
754 : : }
755 : :
756 [ + + ]: 135 : if (ifa->ifa_addr->sa_family != AF_INET) {
757 : 81 : continue;
758 : : }
759 : :
760 [ + + - + : 54 : if (filter_available && is_ignored_iface_name(ifa->ifa_name)) {
- + ]
761 : 0 : continue;
762 : : }
763 : :
764 : 54 : tmp_addr_ptr = &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr);
765 : :
766 [ + - ]: 54 : if VLIKELY (::inet_ntop(AF_INET, tmp_addr_ptr, ip_buffer, INET_ADDRSTRLEN) != nullptr) {
767 : 54 : ip_addresses.emplace_back(ip_buffer);
768 : : }
769 : : }
770 : :
771 [ + - ]: 27 : if (if_addr_struct != nullptr) {
772 : 27 : ::freeifaddrs(if_addr_struct);
773 : : }
774 : : #endif
775 : :
776 : 27 : 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 [ + - + - : 509 : 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 : 507 : auto current_time = std::chrono::steady_clock::now();
1136 : 507 : auto elapsed_time = std::chrono::duration_cast<std::chrono::milliseconds>(current_time - start_time);
1137 : :
1138 [ + - + + : 507 : if VUNLIKELY (timeout_ms >= 0 && elapsed_time.count() > timeout_ms) {
+ + ]
1139 : 2 : return false;
1140 : : }
1141 : :
1142 [ + + ]: 505 : if VLIKELY (poll_ms >= 0) {
1143 : 10 : std::this_thread::sleep_for(std::chrono::milliseconds(poll_ms));
1144 : : }
1145 : 505 : }
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 : 359 : 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 [ + + ]: 359 : if (thread) {
1175 : 356 : native_handle = thread->native_handle();
1176 : : } else {
1177 : 3 : native_handle = ::pthread_self();
1178 : : }
1179 : :
1180 : 359 : 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 : 44 : uint64_t get_native_thread_id() noexcept {
1288 : : #if defined(_WIN32)
1289 : : return static_cast<uint64_t>(::GetCurrentThreadId());
1290 : : #elif defined(__linux__)
1291 : 44 : 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 : : constexpr size_t kTerminateSignals[] = {SIGINT, SIGTERM};
1383 : :
1384 : : for (auto signal : kTerminateSignals) {
1385 : : ::signal(signal, SignalHelper::on_terminate);
1386 : : }
1387 : : #else
1388 : :
1389 : 3 : 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 : : 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 : 1 : 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 : 3 : void start_detect_keyboard(MoveFunction<void(const std::string& key)>&& callback, int poll_ms) noexcept {
1467 [ + + + - ]: 3 : static auto& instance = KeyboardHelper::get();
1468 : :
1469 [ + + ]: 3 : if (instance.has_detect.load(std::memory_order_acquire)) {
1470 : 1 : return;
1471 : : }
1472 : :
1473 : 2 : instance.quit_flag.store(false, std::memory_order_release);
1474 : :
1475 [ + + ]: 2 : if (callback) {
1476 : 1 : instance.callback = std::move(callback);
1477 : : }
1478 : :
1479 : 4 : instance.thread = std::thread([poll_ms]() {
1480 [ + - ]: 2 : std::unique_lock lock(instance.mtx);
1481 : :
1482 [ + + ]: 2 : if VUNLIKELY (!instance.callback) {
1483 : 1 : return;
1484 : : }
1485 : :
1486 : : #ifdef _WIN32
1487 : : HANDLE hconsole = ::GetStdHandle(STD_INPUT_HANDLE);
1488 : :
1489 : : DWORD hmode = 0;
1490 : : DWORD last_hmode = 0;
1491 : :
1492 : : ::GetConsoleMode(hconsole, &hmode);
1493 : : last_hmode = hmode;
1494 : :
1495 : : hmode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
1496 : : ::SetConsoleMode(hconsole, hmode);
1497 : :
1498 : : while (!instance.quit_flag.load(std::memory_order_acquire)) {
1499 : : INPUT_RECORD input_record[128];
1500 : : DWORD events = 0;
1501 : :
1502 : : auto wait_ret = ::WaitForSingleObject(hconsole, poll_ms / 2);
1503 : :
1504 : : if VLIKELY (wait_ret != WAIT_OBJECT_0) {
1505 : : instance.cv.wait_for(lock, std::chrono::milliseconds(poll_ms / 2));
1506 : : continue;
1507 : : }
1508 : :
1509 : : if (::ReadConsoleInput(hconsole, input_record, 128, &events)) {
1510 : : for (DWORD i = 0; i < events; ++i) {
1511 : : if (input_record[i].EventType == KEY_EVENT && input_record[i].Event.KeyEvent.bKeyDown) {
1512 : : std::string key;
1513 : : auto& event = input_record[i].Event.KeyEvent;
1514 : :
1515 : : if (event.uChar.AsciiChar >= 'A' && event.uChar.AsciiChar <= 'Z') {
1516 : : key = std::string(1, std::tolower(event.uChar.AsciiChar));
1517 : : } else {
1518 : : switch (event.wVirtualKeyCode) {
1519 : : case VK_UP:
1520 : : key = "up";
1521 : : break;
1522 : : case VK_DOWN:
1523 : : key = "down";
1524 : : break;
1525 : : case VK_LEFT:
1526 : : key = "left";
1527 : : break;
1528 : : case VK_RIGHT:
1529 : : key = "right";
1530 : : break;
1531 : : case VK_HOME:
1532 : : key = "home";
1533 : : break;
1534 : : case VK_END:
1535 : : key = "end";
1536 : : break;
1537 : : case VK_PRIOR:
1538 : : key = "pgup";
1539 : : break;
1540 : : case VK_NEXT:
1541 : : key = "pgdown";
1542 : : break;
1543 : : case VK_ESCAPE:
1544 : : key = "esc";
1545 : : break;
1546 : : case VK_BACK:
1547 : : key = "backspace";
1548 : : break;
1549 : : default: {
1550 : : char ascii_char = event.uChar.AsciiChar;
1551 : : if (std::isprint(ascii_char)) {
1552 : : key = std::string(1, std::tolower(ascii_char));
1553 : : } else if (ascii_char == '\n' || ascii_char == '\r') {
1554 : : key = "enter";
1555 : : }
1556 : : break;
1557 : : }
1558 : : }
1559 : : }
1560 : :
1561 : : if (!key.empty()) {
1562 : : instance.callback(key);
1563 : : }
1564 : : }
1565 : : }
1566 : : }
1567 : : }
1568 : :
1569 : : ::SetConsoleMode(hconsole, last_hmode);
1570 : :
1571 : : ::FlushConsoleInputBuffer(hconsole);
1572 : :
1573 : : #else
1574 : : termios last_ttystate;
1575 : 1 : ::tcgetattr(STDIN_FILENO, &last_ttystate);
1576 : :
1577 : 1 : termios tmp_ttystate = last_ttystate;
1578 : 1 : tmp_ttystate.c_lflag &= ~(ICANON | ECHO);
1579 : 1 : tmp_ttystate.c_cc[VTIME] = 0;
1580 : 1 : tmp_ttystate.c_cc[VMIN] = 1;
1581 : :
1582 : 1 : ::tcsetattr(STDIN_FILENO, TCSANOW, &tmp_ttystate);
1583 [ + - ]: 1 : int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
1584 [ + - ]: 1 : ::fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
1585 : :
1586 : 1 : int pending_escape_polls = 0;
1587 : 1 : std::string pending_input;
1588 [ + - ]: 1 : pending_input.reserve(64);
1589 : :
1590 [ + + ]: 5 : while (!instance.quit_flag.load(std::memory_order_acquire)) {
1591 : 4 : char buffer[64] = {0};
1592 : : ssize_t size =
1593 [ + - ]: 4 : ::read(STDIN_FILENO, &buffer, sizeof(buffer)); // NOLINT(clang-analyzer-unix.BlockInCriticalSection)
1594 : :
1595 [ + + ]: 4 : if VUNLIKELY (size > 0) {
1596 : 2 : pending_escape_polls = 0;
1597 [ + - ]: 2 : pending_input.append(buffer, static_cast<size_t>(size));
1598 : :
1599 : 2 : size_t index = 0;
1600 : :
1601 [ + + ]: 24 : while (index < pending_input.size()) {
1602 [ + - + + ]: 23 : if (pending_input[index] == '\033') {
1603 [ + + ]: 19 : if (index + 1 >= pending_input.size()) {
1604 : 1 : break;
1605 : : }
1606 : :
1607 [ + - + + ]: 18 : if (pending_input[index + 1] == 'O') {
1608 [ - + ]: 6 : if (index + 2 >= pending_input.size()) {
1609 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1610 : : }
1611 : :
1612 [ + - + + : 6 : switch (pending_input[index + 2]) {
+ + + +
- ]
1613 : 1 : case 'A':
1614 [ + - + - ]: 1 : instance.callback("up");
1615 : 1 : break;
1616 : 1 : case 'B':
1617 [ + - + - ]: 1 : instance.callback("down");
1618 : 1 : break;
1619 : 1 : case 'C':
1620 [ + - + - ]: 1 : instance.callback("right");
1621 : 1 : break;
1622 : 1 : case 'D':
1623 [ + - + - ]: 1 : instance.callback("left");
1624 : 1 : break;
1625 : 1 : case 'H':
1626 [ + - + - ]: 1 : instance.callback("home");
1627 : 1 : break;
1628 : 1 : case 'F':
1629 [ + - + - ]: 1 : instance.callback("end");
1630 : 1 : break;
1631 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1632 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1633 : : }
1634 : :
1635 : 6 : index += 3;
1636 : :
1637 : 6 : continue;
1638 : : }
1639 : :
1640 [ + - + + ]: 12 : if (pending_input[index + 1] == '[') {
1641 : 11 : size_t seq_end = index + 2;
1642 : :
1643 [ + - + + ]: 32 : while (seq_end < pending_input.size() &&
1644 [ + - + + : 16 : !(pending_input[seq_end] >= '@' && pending_input[seq_end] <= '~')) {
+ - - + ]
1645 : 5 : ++seq_end;
1646 : : }
1647 : :
1648 [ - + ]: 11 : if (seq_end >= pending_input.size()) {
1649 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1650 : : }
1651 : :
1652 [ + - ]: 11 : const char key = pending_input[seq_end];
1653 : :
1654 [ + - + + ]: 11 : if (key >= 'A' && key <= 'Z') {
1655 [ + + + + : 6 : switch (key) {
+ + - ]
1656 : 1 : case 'A':
1657 [ + - + - ]: 1 : instance.callback("up");
1658 : 1 : break;
1659 : 1 : case 'B':
1660 [ + - + - ]: 1 : instance.callback("down");
1661 : 1 : break;
1662 : 1 : case 'C':
1663 [ + - + - ]: 1 : instance.callback("right");
1664 : 1 : break;
1665 : 1 : case 'D':
1666 [ + - + - ]: 1 : instance.callback("left");
1667 : 1 : break;
1668 : 1 : case 'H':
1669 [ + - + - ]: 1 : instance.callback("home");
1670 : 1 : break;
1671 : 1 : case 'F':
1672 [ + - + - ]: 1 : instance.callback("end");
1673 : 1 : break;
1674 : : default: // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1675 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1676 : : }
1677 : 6 : index = seq_end + 1;
1678 : 6 : continue;
1679 : : }
1680 : :
1681 [ + - ]: 5 : if (key == '~') {
1682 [ + - ]: 5 : const std::string code = pending_input.substr(index + 2, seq_end - (index + 2));
1683 : :
1684 [ + + - + : 5 : if (code == "1" || code == "7") {
+ + ]
1685 [ + - + - ]: 1 : instance.callback("home");
1686 [ + + - + : 4 : } else if (code == "4" || code == "8") {
+ + ]
1687 [ + - + - ]: 1 : instance.callback("end");
1688 [ + + ]: 3 : } else if (code == "5") {
1689 [ + - + - ]: 1 : instance.callback("pgup");
1690 [ + + ]: 2 : } else if (code == "6") {
1691 [ + - + - ]: 1 : instance.callback("pgdown");
1692 : : }
1693 : :
1694 : 5 : index = seq_end + 1;
1695 : :
1696 : 5 : continue;
1697 : 5 : }
1698 : :
1699 : : index = seq_end + 1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1700 : :
1701 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1702 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1703 : :
1704 [ + - + - ]: 1 : instance.callback("esc");
1705 : 1 : ++index;
1706 : :
1707 : 1 : continue;
1708 : 1 : }
1709 : :
1710 [ + - ]: 4 : const auto ch = static_cast<unsigned char>(pending_input[index]);
1711 : :
1712 [ + + - + ]: 4 : if (ch == '\n' || ch == '\r') {
1713 [ + - + - ]: 1 : instance.callback("enter");
1714 [ + + - + ]: 3 : } else if (ch == 0x7f || ch == 0x08) {
1715 [ + - + - ]: 1 : instance.callback("backspace");
1716 [ + - ]: 2 : } else if (std::isprint(ch)) {
1717 [ + - + - ]: 2 : instance.callback(std::string(1, static_cast<char>(std::tolower(ch))));
1718 : : }
1719 : :
1720 : 4 : ++index;
1721 : : }
1722 : :
1723 [ + + ]: 2 : if (index > 0) {
1724 [ + - ]: 1 : pending_input.erase(0, index);
1725 : : }
1726 [ + - ]: 2 : } else if (pending_input == "\033") {
1727 : 2 : ++pending_escape_polls;
1728 : :
1729 [ + + ]: 2 : if (pending_escape_polls >= 2) {
1730 [ + - + - ]: 1 : instance.callback("esc");
1731 : 1 : pending_input.clear();
1732 : 1 : pending_escape_polls = 0;
1733 : : }
1734 : : }
1735 : :
1736 : 4 : instance.cv.wait_for(lock, std::chrono::milliseconds(poll_ms));
1737 : : }
1738 : :
1739 : 1 : ::tcsetattr(STDIN_FILENO, TCSANOW, &last_ttystate);
1740 : : #endif
1741 [ + + ]: 4 : });
1742 : :
1743 : 2 : instance.has_detect.store(true, std::memory_order_release);
1744 : : }
1745 : :
1746 : 4 : void stop_detect_keyboard() noexcept {
1747 [ + + + - ]: 4 : static auto& instance = KeyboardHelper::get();
1748 : :
1749 : 4 : bool expected = true;
1750 : :
1751 [ + + ]: 4 : if VUNLIKELY (!instance.has_detect.compare_exchange_strong(expected, false, std::memory_order_acq_rel,
1752 : : std::memory_order_relaxed)) {
1753 : 2 : return;
1754 : : }
1755 : :
1756 : : {
1757 : 2 : std::lock_guard lock(instance.mtx);
1758 : 2 : instance.quit_flag.store(true, std::memory_order_release);
1759 : 2 : }
1760 : :
1761 : 2 : instance.cv.notify_all();
1762 : :
1763 [ + - ]: 2 : if VLIKELY (instance.thread.joinable()) {
1764 : 2 : instance.thread.join();
1765 : : }
1766 : : }
1767 : :
1768 : 1 : std::pair<int, int> get_terminal_size() noexcept {
1769 : : #ifdef _WIN32
1770 : : CONSOLE_SCREEN_BUFFER_INFO size;
1771 : :
1772 : : if VLIKELY (::GetConsoleScreenBufferInfo(::GetStdHandle(STD_OUTPUT_HANDLE), &size)) {
1773 : : return std::pair<int, int>{size.srWindow.Right - size.srWindow.Left + 1,
1774 : : size.srWindow.Bottom - size.srWindow.Top + 1};
1775 : : }
1776 : : #else
1777 : : struct winsize size;
1778 : :
1779 [ - + - - : 1 : if VLIKELY (::ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
- + - - -
+ ]
1780 : : return std::pair<int, int>{size.ws_col, size.ws_row}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1781 : : }
1782 : :
1783 [ - + - - : 1 : if VLIKELY (::ioctl(STDERR_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
- + - - -
+ ]
1784 : : return std::pair<int, int>{size.ws_col, size.ws_row}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1785 : : }
1786 : :
1787 [ - + - - : 1 : if VLIKELY (::ioctl(STDIN_FILENO, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
- + - - -
+ ]
1788 : : return std::pair<int, int>{size.ws_col, size.ws_row}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1789 : : }
1790 : :
1791 : : {
1792 : 1 : int fd = ::open("/dev/tty", O_RDONLY);
1793 : :
1794 [ - + ]: 1 : if VLIKELY (fd >= 0) {
1795 : : // LCOV_EXCL_START GCOVR_EXCL_START
1796 : : if VLIKELY (::ioctl(fd, TIOCGWINSZ, &size) == 0 && size.ws_col > 0 && size.ws_row > 0) {
1797 : : ::close(fd);
1798 : : return std::pair<int, int>{size.ws_col, size.ws_row};
1799 : : }
1800 : :
1801 : : ::close(fd);
1802 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1803 : : }
1804 : : }
1805 : : #endif
1806 : :
1807 : 1 : return std::pair<int, int>{-1, -1};
1808 : : }
1809 : :
1810 : 3 : double get_cpu_usage() noexcept {
1811 : : #ifdef _WIN32
1812 : : FILETIME idle_time;
1813 : : FILETIME kernel_time;
1814 : : FILETIME user_time;
1815 : : GetSystemTimes(&idle_time, &kernel_time, &user_time);
1816 : :
1817 : : ULARGE_INTEGER idle;
1818 : : ULARGE_INTEGER kernel;
1819 : : ULARGE_INTEGER user;
1820 : : idle.LowPart = idle_time.dwLowDateTime;
1821 : : idle.HighPart = idle_time.dwHighDateTime;
1822 : : kernel.LowPart = kernel_time.dwLowDateTime;
1823 : : kernel.HighPart = kernel_time.dwHighDateTime;
1824 : : user.LowPart = user_time.dwLowDateTime;
1825 : : user.HighPart = user_time.dwHighDateTime;
1826 : :
1827 : : static ULARGE_INTEGER last_idle_time = {{0, 0}};
1828 : : static ULARGE_INTEGER last_kernel_time = {{0, 0}};
1829 : : static ULARGE_INTEGER last_user_time = {{0, 0}};
1830 : :
1831 : : ULARGE_INTEGER total;
1832 : : total.QuadPart = (kernel.QuadPart - last_kernel_time.QuadPart) + (user.QuadPart - last_user_time.QuadPart);
1833 : : ULARGE_INTEGER idle_diff;
1834 : : idle_diff.QuadPart = idle.QuadPart - last_idle_time.QuadPart;
1835 : :
1836 : : last_idle_time = idle;
1837 : : last_kernel_time = kernel;
1838 : : last_user_time = user;
1839 : :
1840 : : if VUNLIKELY (total.QuadPart == 0) {
1841 : : return -1;
1842 : : }
1843 : :
1844 : : return (1.0 - (static_cast<double>(idle_diff.QuadPart) / total.QuadPart)) * 100.0;
1845 : : #elif defined(__linux__)
1846 : 3 : std::ifstream cpu_file("/proc/stat");
1847 : 3 : std::string line;
1848 : 3 : std::getline(cpu_file, line);
1849 : :
1850 [ + + ]: 3 : thread_local std::istringstream iss;
1851 : 3 : iss.clear();
1852 : 3 : iss.str(line);
1853 : :
1854 : 3 : std::string cpu;
1855 : : int64_t user;
1856 : : int64_t nice;
1857 : : int64_t system;
1858 : : int64_t idle;
1859 : :
1860 : 3 : iss >> cpu >> user >> nice >> system >> idle;
1861 : :
1862 : : static int64_t last_idle = 0;
1863 : : static int64_t last_total = 0;
1864 : :
1865 : 3 : int64_t total = user + nice + system + idle;
1866 : 3 : int64_t idle_diff = idle - last_idle;
1867 : 3 : int64_t total_diff = total - last_total;
1868 : :
1869 : 3 : last_idle = idle;
1870 : 3 : last_total = total;
1871 : :
1872 [ + + ]: 3 : if VUNLIKELY (total_diff == 0) {
1873 : 2 : return -1;
1874 : : }
1875 : :
1876 : 1 : return (1.0 - static_cast<double>(idle_diff) / total_diff) * 100.0;
1877 : : #elif defined(__APPLE__)
1878 : : host_cpu_load_info_data_t cpu_info;
1879 : : mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
1880 : : kern_return_t kr = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&cpu_info, &count);
1881 : :
1882 : : if VUNLIKELY (kr != KERN_SUCCESS) {
1883 : : return -1;
1884 : : }
1885 : :
1886 : : static uint64_t last_user = 0;
1887 : : static uint64_t last_system = 0;
1888 : : static uint64_t last_idle = 0;
1889 : :
1890 : : uint64_t user = cpu_info.cpu_ticks[CPU_STATE_USER];
1891 : : uint64_t system = cpu_info.cpu_ticks[CPU_STATE_SYSTEM];
1892 : : uint64_t idle = cpu_info.cpu_ticks[CPU_STATE_IDLE];
1893 : :
1894 : : uint64_t total = (user - last_user) + (system - last_system) + (idle - last_idle);
1895 : : uint64_t idle_diff = idle - last_idle;
1896 : :
1897 : : last_user = user;
1898 : : last_system = system;
1899 : : last_idle = idle;
1900 : :
1901 : : if VUNLIKELY (total == 0) {
1902 : : return -1;
1903 : : }
1904 : :
1905 : : return (1.0 - static_cast<double>(idle_diff) / total) * 100.0;
1906 : : #else
1907 : : return -1;
1908 : : #endif
1909 : 3 : }
1910 : :
1911 : 2 : double get_memory_usage() noexcept {
1912 : : #ifdef _WIN32
1913 : : MEMORYSTATUSEX mem_status;
1914 : : mem_status.dwLength = sizeof(MEMORYSTATUSEX);
1915 : : GlobalMemoryStatusEx(&mem_status);
1916 : :
1917 : : if VUNLIKELY (mem_status.ullTotalPhys == 0) {
1918 : : return -1;
1919 : : }
1920 : :
1921 : : return (static_cast<double>(mem_status.ullTotalPhys - mem_status.ullAvailPhys) / mem_status.ullTotalPhys) * 100.0;
1922 : : #elif defined(__linux__)
1923 : 2 : std::ifstream mem_file("/proc/meminfo");
1924 : :
1925 [ - + ]: 2 : if (!mem_file.is_open()) {
1926 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1927 : : }
1928 : :
1929 : 2 : std::string key;
1930 : 2 : int64_t mem_total = 0;
1931 : 2 : int64_t mem_free = 0;
1932 : 2 : int64_t buffers = 0;
1933 : 2 : int64_t cached = 0;
1934 : :
1935 [ + + ]: 112 : while (mem_file >> key) {
1936 [ + + ]: 110 : if (key == "MemTotal:") {
1937 : 2 : mem_file >> mem_total;
1938 [ + + ]: 108 : } else if (key == "MemFree:") {
1939 : 2 : mem_file >> mem_free;
1940 [ + + ]: 106 : } else if (key == "Buffers:") {
1941 : 2 : mem_file >> buffers;
1942 [ + + ]: 104 : } else if (key == "Cached:") {
1943 : 2 : mem_file >> cached;
1944 : : }
1945 : :
1946 : 110 : mem_file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
1947 : : }
1948 : :
1949 : 2 : int64_t mem_available = mem_free + buffers + cached;
1950 : :
1951 [ - + ]: 2 : if VUNLIKELY (mem_total == 0) {
1952 : : return -1; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1953 : : }
1954 : :
1955 : 2 : return (static_cast<double>(mem_total - mem_available) / mem_total) * 100.0;
1956 : : #elif defined(__APPLE__)
1957 : : vm_size_t page_size;
1958 : : mach_port_t mach_port = mach_host_self();
1959 : : mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
1960 : : vm_statistics_data_t vm_stats;
1961 : :
1962 : : if VUNLIKELY (host_page_size(mach_port, &page_size) != KERN_SUCCESS) {
1963 : : return -1;
1964 : : }
1965 : :
1966 : : if VUNLIKELY (host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count) != KERN_SUCCESS) {
1967 : : return -1;
1968 : : }
1969 : :
1970 : : uint64_t free_memory = static_cast<uint64_t>(vm_stats.free_count) * page_size;
1971 : : uint64_t active_memory = static_cast<uint64_t>(vm_stats.active_count) * page_size;
1972 : : uint64_t inactive_memory = static_cast<uint64_t>(vm_stats.inactive_count) * page_size;
1973 : : uint64_t wired_memory = static_cast<uint64_t>(vm_stats.wire_count) * page_size;
1974 : : uint64_t total_memory = free_memory + active_memory + inactive_memory + wired_memory;
1975 : :
1976 : : if VUNLIKELY (total_memory == 0) {
1977 : : return -1;
1978 : : }
1979 : :
1980 : : return (static_cast<double>(active_memory + inactive_memory + wired_memory) / total_memory) * 100.0;
1981 : : #else
1982 : : return -1;
1983 : : #endif
1984 : 2 : }
1985 : :
1986 : 5 : bool is_process_running(const std::string& process_name) noexcept {
1987 [ + + ]: 5 : if VUNLIKELY (process_name.empty()) {
1988 : 1 : return false;
1989 : : }
1990 : :
1991 : : #ifdef _WIN32
1992 : : HANDLE hsnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
1993 : :
1994 : : if VUNLIKELY (hsnapshot == INVALID_HANDLE_VALUE) {
1995 : : return false;
1996 : : }
1997 : :
1998 : : PROCESSENTRY32 pe32;
1999 : : pe32.dwSize = sizeof(PROCESSENTRY32);
2000 : :
2001 : : if (::Process32First(hsnapshot, &pe32)) {
2002 : : do {
2003 : : if (process_name == pe32.szExeFile) {
2004 : : ::CloseHandle(hsnapshot);
2005 : : return true;
2006 : : }
2007 : : } while (::Process32Next(hsnapshot, &pe32));
2008 : : }
2009 : :
2010 : : ::CloseHandle(hsnapshot);
2011 : : return false;
2012 : : #else
2013 : 4 : const bool safe_name = std::all_of(process_name.begin(), process_name.end(), [](unsigned char c) {
2014 [ + + + + : 83 : return std::isalnum(c) != 0 || c == '_' || c == '-' || c == '.';
+ + + + ]
2015 : : });
2016 : :
2017 [ + + ]: 4 : if VUNLIKELY (!safe_name) {
2018 : 1 : return false;
2019 : : }
2020 : :
2021 : 3 : std::string process_pattern;
2022 : 3 : process_pattern.reserve(process_name.size() + 4U);
2023 : :
2024 [ + + ]: 67 : for (char c : process_name) {
2025 [ + + ]: 64 : if (c == '.') {
2026 : 1 : process_pattern.append("\\.");
2027 : : } else {
2028 : 63 : process_pattern.push_back(c);
2029 : : }
2030 : : }
2031 : :
2032 : 3 : std::ostringstream oss;
2033 : :
2034 : : #if defined(__QNX__)
2035 : : oss << "pidin | grep -- '" << process_pattern << "' | grep -v grep > /dev/null";
2036 : : #elif defined(__ANDROID__)
2037 : : oss << "ps -A | grep -- '" << process_pattern << "' | grep -v grep > /dev/null";
2038 : : #elif defined(__APPLE__)
2039 : : oss << "pgrep -x -- '" << process_pattern << "' > /dev/null 2>&1";
2040 : : #elif defined(__linux__)
2041 : 3 : oss << "pgrep -x -- '" << process_pattern << "' > /dev/null 2>&1";
2042 : : #else
2043 : : oss << "ps aux | grep -- '" << process_pattern << "' | grep -v grep > /dev/null";
2044 : : #endif
2045 : :
2046 : 3 : std::string command = oss.str();
2047 : :
2048 : : // NOLINTNEXTLINE(bugprone-command-processor)
2049 : 3 : return std::system(command.c_str()) == 0;
2050 : : #endif
2051 : 3 : }
2052 : :
2053 : 159 : int32_t get_timezone_diff() noexcept {
2054 : : #ifdef _WIN32
2055 : : TIME_ZONE_INFORMATION tzi;
2056 : : DWORD result = GetTimeZoneInformation(&tzi);
2057 : : int bias = tzi.Bias;
2058 : :
2059 : : if (result == TIME_ZONE_ID_DAYLIGHT) {
2060 : : bias += tzi.DaylightBias;
2061 : : } else if (result == TIME_ZONE_ID_STANDARD) {
2062 : : bias += tzi.StandardBias;
2063 : : }
2064 : :
2065 : : return -bias;
2066 : : #else
2067 : 159 : std::time_t now = std::time(nullptr);
2068 : 159 : std::tm local_tm = *std::localtime(&now);
2069 : : #ifdef __APPLE__
2070 : : return local_tm.tm_gmtoff / 60;
2071 : : #else
2072 : 159 : std::tm gm_tm = *std::gmtime(&now);
2073 : 159 : int diff_seconds = std::mktime(&local_tm) - std::mktime(&gm_tm);
2074 : :
2075 : 159 : return diff_seconds / 60;
2076 : : #endif
2077 : : #endif
2078 : : }
2079 : :
2080 : 1 : void try_release_sys_memory() noexcept {
2081 : : #if defined(__linux__) && !defined(__ANDROID__)
2082 : 1 : malloc_trim(0);
2083 : : #endif
2084 : 1 : }
2085 : :
2086 : 3 : std::string get_machine_id() noexcept {
2087 : : #ifdef _WIN32
2088 : : HKEY hkey;
2089 : : const char* subkey = "SOFTWARE\\Microsoft\\Cryptography";
2090 : : const char* value_name = "MachineGuid";
2091 : :
2092 : : char value[256];
2093 : : DWORD value_length = sizeof(value);
2094 : : LONG result;
2095 : :
2096 : : result = ::RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hkey);
2097 : :
2098 : : if (result != ERROR_SUCCESS) {
2099 : : return "";
2100 : : }
2101 : :
2102 : : result = ::RegQueryValueExA(hkey, value_name, nullptr, nullptr, reinterpret_cast<LPBYTE>(value), &value_length);
2103 : : ::RegCloseKey(hkey);
2104 : :
2105 : : if (result == ERROR_SUCCESS) {
2106 : : return std::string(value, value_length - 1);
2107 : : }
2108 : : #elif defined(__linux__)
2109 : 3 : const char* paths[] = {
2110 : : "/etc/machine-id",
2111 : : "/var/lib/dbus/machine-id",
2112 : : };
2113 : :
2114 : 3 : std::string id;
2115 : :
2116 [ + + ]: 9 : for (const char* path : paths) {
2117 : : try {
2118 [ + - + - : 6 : if (!std::filesystem::exists(path)) {
+ + ]
2119 : : // LCOV_EXCL_START GCOVR_EXCL_START
2120 : : continue;
2121 : : }
2122 : : } catch (std::filesystem::filesystem_error&) {
2123 : : continue;
2124 : : }
2125 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2126 : :
2127 : 3 : std::ifstream file(path);
2128 : :
2129 [ + - ]: 3 : if (file) {
2130 : 3 : std::getline(file, id);
2131 : :
2132 : 3 : id.erase(std::remove_if(id.begin(), id.end(), ::isspace), id.end());
2133 : :
2134 [ - + ]: 3 : if (!id.empty()) {
2135 : 0 : return id;
2136 : : }
2137 : : }
2138 [ + - ]: 3 : }
2139 : : #endif
2140 : :
2141 : : return std::string(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2142 : 3 : }
2143 : :
2144 : : } // namespace Utils
2145 : :
2146 : : } // namespace vlink
|