|
| 1 | +#include "netprobe.hpp" |
| 2 | + |
| 3 | +#include <fcntl.h> |
| 4 | +#include <netdb.h> |
| 5 | +#include <poll.h> |
| 6 | +#include <sys/socket.h> |
| 7 | +#include <time.h> |
| 8 | +#include <unistd.h> |
| 9 | + |
| 10 | +#include <cerrno> |
| 11 | + |
| 12 | +namespace deckback { |
| 13 | +namespace { |
| 14 | + |
| 15 | +long mono_ms() { |
| 16 | + timespec ts{}; |
| 17 | + clock_gettime(CLOCK_MONOTONIC, &ts); |
| 18 | + return ts.tv_sec * 1000L + ts.tv_nsec / 1'000'000L; |
| 19 | +} |
| 20 | + |
| 21 | +} // namespace |
| 22 | + |
| 23 | +bool tcp_reachable(const std::string& host, int port, int timeout_ms) { |
| 24 | + addrinfo hints{}; |
| 25 | + hints.ai_family = AF_UNSPEC; |
| 26 | + hints.ai_socktype = SOCK_STREAM; |
| 27 | + addrinfo* res = nullptr; |
| 28 | + const std::string port_s = std::to_string(port); |
| 29 | + if (getaddrinfo(host.c_str(), port_s.c_str(), &hints, &res) != 0 || !res) return false; |
| 30 | + |
| 31 | + bool ok = false; |
| 32 | + for (addrinfo* ai = res; ai && !ok; ai = ai->ai_next) { |
| 33 | + int fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); |
| 34 | + if (fd < 0) continue; |
| 35 | + int flags = fcntl(fd, F_GETFL, 0); |
| 36 | + fcntl(fd, F_SETFL, flags | O_NONBLOCK); |
| 37 | + int rc = connect(fd, ai->ai_addr, ai->ai_addrlen); |
| 38 | + if (rc == 0) { |
| 39 | + ok = true; |
| 40 | + } else if (errno == EINPROGRESS) { |
| 41 | + pollfd pfd{fd, POLLOUT, 0}; |
| 42 | + if (poll(&pfd, 1, timeout_ms) > 0 && (pfd.revents & POLLOUT)) { |
| 43 | + int err = 0; |
| 44 | + socklen_t l = sizeof err; |
| 45 | + getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &l); |
| 46 | + ok = (err == 0); |
| 47 | + } |
| 48 | + } |
| 49 | + close(fd); |
| 50 | + } |
| 51 | + freeaddrinfo(res); |
| 52 | + return ok; |
| 53 | +} |
| 54 | + |
| 55 | +bool wait_online(const std::string& host, int port, int max_ms) { |
| 56 | + if (max_ms <= 0) return true; |
| 57 | + const long deadline = mono_ms() + max_ms; |
| 58 | + int backoff = 200; |
| 59 | + for (;;) { |
| 60 | + int remaining = static_cast<int>(deadline - mono_ms()); |
| 61 | + if (remaining <= 0) return false; |
| 62 | + if (tcp_reachable(host, port, remaining < 1000 ? remaining : 1000)) return true; |
| 63 | + int nap = backoff < remaining ? backoff : remaining; |
| 64 | + if (nap > 0) { |
| 65 | + timespec ts{nap / 1000, (nap % 1000) * 1'000'000L}; |
| 66 | + nanosleep(&ts, nullptr); |
| 67 | + } |
| 68 | + backoff = backoff < 1600 ? backoff * 2 : 1600; |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +} // namespace deckback |
0 commit comments