feat(beacon,otop): live UDP/WAN shm aggregation, Android Termux support, aarch64 launcher
This commit is contained in:
Binary file not shown.
+14
-8
@@ -11,19 +11,25 @@ if [ -z "$FLEET_NODE" ] || [ -z "$FLEET_ARCH" ]; then
|
|||||||
[ -f "$BIN_DIR/bin-env" ] && source "$BIN_DIR/bin-env" 2>/dev/null || true
|
[ -f "$BIN_DIR/bin-env" ] && source "$BIN_DIR/bin-env" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 1. Prefer native C compiled binary (< 1MB RAM, 0% CPU)
|
# 1. Compile or recompile native C binary if source is newer or binary missing
|
||||||
if [ -x "$BIN_DIR/fleet-beacon-c" ]; then
|
CC_BIN=""
|
||||||
exec "$BIN_DIR/fleet-beacon-c" "$@"
|
if command -v gcc >/dev/null 2>&1; then
|
||||||
|
CC_BIN="gcc"
|
||||||
|
elif command -v clang >/dev/null 2>&1; then
|
||||||
|
CC_BIN="clang"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. Compile native C binary on the fly if compiler exists
|
if [ -n "$CC_BIN" ] && [ -f "$BIN_DIR/src/fleet-beacon.c" ]; then
|
||||||
if command -v gcc >/dev/null 2>&1 && [ -f "$BIN_DIR/src/fleet-beacon.c" ]; then
|
if [ ! -x "$BIN_DIR/fleet-beacon-c" ] || [ "$BIN_DIR/src/fleet-beacon.c" -nt "$BIN_DIR/fleet-beacon-c" ]; then
|
||||||
gcc -O2 -Wall -Wextra -pedantic -o "$BIN_DIR/fleet-beacon-c" "$BIN_DIR/src/fleet-beacon.c" 2>/dev/null || true
|
"$CC_BIN" -O2 -Wall -Wextra -pedantic -o "$BIN_DIR/fleet-beacon-c" "$BIN_DIR/src/fleet-beacon.c" 2>/dev/null || true
|
||||||
if [ -x "$BIN_DIR/fleet-beacon-c" ]; then
|
|
||||||
exec "$BIN_DIR/fleet-beacon-c" "$@"
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 2. Prefer native C compiled binary (< 1MB RAM, 0% CPU)
|
||||||
|
if [ -x "$BIN_DIR/fleet-beacon-c" ]; then
|
||||||
|
exec "$BIN_DIR/fleet-beacon-c" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
# 3. Fallback to pure Bash implementation
|
# 3. Fallback to pure Bash implementation
|
||||||
if [ -x "$BIN_DIR/fleet-beacon.sh" ]; then
|
if [ -x "$BIN_DIR/fleet-beacon.sh" ]; then
|
||||||
exec "$BIN_DIR/fleet-beacon.sh" "$@"
|
exec "$BIN_DIR/fleet-beacon.sh" "$@"
|
||||||
|
|||||||
@@ -4,8 +4,13 @@
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
ARCH="$(uname -m)"
|
ARCH="$(uname -m)"
|
||||||
|
|
||||||
# 1. Nativní architektura v ~/bin/arch/$ARCH/otop
|
# 0. Termux / Android Bionic: prefer local Bionic build first
|
||||||
if [ -x "$HOME/bin/arch/$ARCH/otop" ]; then
|
if [ -d "/data/data/com.termux" ] && [ -x "$HOME/src/observator/otop/bin/otop" ]; then
|
||||||
|
exec "$HOME/src/observator/otop/bin/otop" "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 1. Nativní architektura v ~/bin/arch/$ARCH/otop (glibc Linux)
|
||||||
|
if [ ! -d "/data/data/com.termux" ] && [ -x "$HOME/bin/arch/$ARCH/otop" ]; then
|
||||||
exec "$HOME/bin/arch/$ARCH/otop" "$@"
|
exec "$HOME/bin/arch/$ARCH/otop" "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -19,12 +24,14 @@ if [ -x "/usr/local/bin/otop" ]; then
|
|||||||
exec "/usr/local/bin/otop" "$@"
|
exec "/usr/local/bin/otop" "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. Automatické stažení pro x86_64 pokud chybí
|
# 4. Automatické stažení pro x86_64 / aarch64 (glibc Linux) pokud chybí
|
||||||
if [ "$ARCH" = "x86_64" ]; then
|
if [ ! -d "/data/data/com.termux" ] && { [ "$ARCH" = "x86_64" ] || [ "$ARCH" = "aarch64" ]; }; then
|
||||||
mkdir -p "$HOME/bin/arch/x86_64"
|
mkdir -p "$HOME/bin/arch/$ARCH"
|
||||||
if curl -sL https://xn--peek-h6a.com/otop-x86_64 -o "$HOME/bin/arch/x86_64/otop" || curl -sL http://91.139.118.43/otop-x86_64 -o "$HOME/bin/arch/x86_64/otop"; then
|
if curl -sfL "http://192.168.1.101/otop-$ARCH" -o "$HOME/bin/arch/$ARCH/otop" || \
|
||||||
chmod +x "$HOME/bin/arch/x86_64/otop"
|
curl -sfL "https://xn--peek-h6a.com/otop-$ARCH" -o "$HOME/bin/arch/$ARCH/otop" || \
|
||||||
exec "$HOME/bin/arch/x86_64/otop" "$@"
|
curl -sfL "http://91.139.118.43/otop-$ARCH" -o "$HOME/bin/arch/$ARCH/otop"; then
|
||||||
|
chmod +x "$HOME/bin/arch/$ARCH/otop"
|
||||||
|
exec "$HOME/bin/arch/$ARCH/otop" "$@"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+274
-43
@@ -143,21 +143,63 @@ static void resolve_identity(char *node, size_t n_len, char *arch, size_t a_len,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double read_cpu_cpufreq_fallback(void) {
|
||||||
|
glob_t gl;
|
||||||
|
double sum_pct = 0.0;
|
||||||
|
int count = 0;
|
||||||
|
if (glob("/sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_cur_freq", 0, NULL, &gl) == 0) {
|
||||||
|
for (size_t i = 0; i < gl.gl_pathc; ++i) {
|
||||||
|
long cur = 0, min_f = 0, max_f = 0;
|
||||||
|
FILE *fp = fopen(gl.gl_pathv[i], "r");
|
||||||
|
if (fp) {
|
||||||
|
if (fscanf(fp, "%ld", &cur) != 1) cur = 0;
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
char path_buf[256];
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%s", gl.gl_pathv[i]);
|
||||||
|
char *slash = strrchr(path_buf, '/');
|
||||||
|
if (slash) {
|
||||||
|
strcpy(slash + 1, "cpuinfo_min_freq");
|
||||||
|
fp = fopen(path_buf, "r");
|
||||||
|
if (fp) {
|
||||||
|
if (fscanf(fp, "%ld", &min_f) != 1) min_f = 0;
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
strcpy(slash + 1, "cpuinfo_max_freq");
|
||||||
|
fp = fopen(path_buf, "r");
|
||||||
|
if (fp) {
|
||||||
|
if (fscanf(fp, "%ld", &max_f) != 1) max_f = 0;
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (max_f > min_f && cur >= min_f) {
|
||||||
|
double c_pct = ((double)(cur - min_f) / (double)(max_f - min_f)) * 100.0;
|
||||||
|
if (c_pct < 0.0) c_pct = 0.0;
|
||||||
|
if (c_pct > 100.0) c_pct = 100.0;
|
||||||
|
sum_pct += c_pct;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
return (count > 0) ? (sum_pct / (double)count) : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
static double read_cpu(unsigned long long *prev_work, unsigned long long *prev_total) {
|
static double read_cpu(unsigned long long *prev_work, unsigned long long *prev_total) {
|
||||||
FILE *fp = fopen("/proc/stat", "r");
|
FILE *fp = fopen("/proc/stat", "r");
|
||||||
if (!fp) return 0.0;
|
if (!fp) return read_cpu_cpufreq_fallback();
|
||||||
|
|
||||||
char line[256];
|
char line[256];
|
||||||
if (!fgets(line, sizeof(line), fp)) {
|
if (!fgets(line, sizeof(line), fp)) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return 0.0;
|
return read_cpu_cpufreq_fallback();
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0;
|
unsigned long long user = 0, nice = 0, system = 0, idle = 0, iowait = 0, irq = 0, softirq = 0, steal = 0;
|
||||||
if (sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
|
if (sscanf(line, "cpu %llu %llu %llu %llu %llu %llu %llu %llu",
|
||||||
&user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal) < 4) {
|
&user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal) < 4) {
|
||||||
return 0.0;
|
return read_cpu_cpufreq_fallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long work = user + nice + system + irq + softirq + steal;
|
unsigned long long work = user + nice + system + irq + softirq + steal;
|
||||||
@@ -223,6 +265,10 @@ static void read_disk(const char *node, char *mount_out, size_t m_len, double *f
|
|||||||
candidates[0] = "/mnt/ssd";
|
candidates[0] = "/mnt/ssd";
|
||||||
candidates[1] = "/mnt/seagate";
|
candidates[1] = "/mnt/seagate";
|
||||||
candidates[2] = "/";
|
candidates[2] = "/";
|
||||||
|
} else if (strcmp(node, "android") == 0 || strcmp(node, "phone") == 0) {
|
||||||
|
candidates[0] = "/data";
|
||||||
|
candidates[1] = "/data/data/com.termux";
|
||||||
|
candidates[2] = "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; candidates[i] != NULL; ++i) {
|
for (int i = 0; candidates[i] != NULL; ++i) {
|
||||||
@@ -309,8 +355,46 @@ static int read_battery(void) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *get_shm_path(void) {
|
||||||
|
if (access("/dev/shm", W_OK) == 0) {
|
||||||
|
return "/dev/shm/fleet_telemetry.json";
|
||||||
|
}
|
||||||
|
if (access("/data/data/com.termux/files/usr/tmp", W_OK) == 0) {
|
||||||
|
return "/data/data/com.termux/files/usr/tmp/fleet_telemetry.json";
|
||||||
|
}
|
||||||
|
return "/tmp/fleet_telemetry.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extract_json_str(const char *buf, const char *key, char *out, size_t out_len) {
|
||||||
|
char pat[64];
|
||||||
|
snprintf(pat, sizeof(pat), "\"%s\"", key);
|
||||||
|
const char *pos = strstr(buf, pat);
|
||||||
|
if (!pos) return 0;
|
||||||
|
const char *colon = strchr(pos + strlen(pat), ':');
|
||||||
|
if (!colon) return 0;
|
||||||
|
const char *q1 = strchr(colon + 1, '"');
|
||||||
|
if (!q1) return 0;
|
||||||
|
const char *q2 = strchr(q1 + 1, '"');
|
||||||
|
if (!q2) return 0;
|
||||||
|
size_t len = (size_t)(q2 - (q1 + 1));
|
||||||
|
if (len >= out_len) len = out_len - 1;
|
||||||
|
memcpy(out, q1 + 1, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double extract_json_num(const char *buf, const char *key) {
|
||||||
|
char pat[64];
|
||||||
|
snprintf(pat, sizeof(pat), "\"%s\"", key);
|
||||||
|
const char *pos = strstr(buf, pat);
|
||||||
|
if (!pos) return -1.0;
|
||||||
|
const char *colon = strchr(pos + strlen(pat), ':');
|
||||||
|
if (!colon) return -1.0;
|
||||||
|
return strtod(colon + 1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void update_shm(const char *node, const char *payload) {
|
static void update_shm(const char *node, const char *payload) {
|
||||||
const char *shm_path = "/dev/shm/fleet_telemetry.json";
|
const char *shm_path = get_shm_path();
|
||||||
char tmp_path[256];
|
char tmp_path[256];
|
||||||
snprintf(tmp_path, sizeof(tmp_path), "%s.tmp.%d", shm_path, (int)getpid());
|
snprintf(tmp_path, sizeof(tmp_path), "%s.tmp.%d", shm_path, (int)getpid());
|
||||||
|
|
||||||
@@ -326,16 +410,23 @@ static void update_shm(const char *node, const char *payload) {
|
|||||||
FILE *out = fopen(tmp_path, "w");
|
FILE *out = fopen(tmp_path, "w");
|
||||||
if (!out) return;
|
if (!out) return;
|
||||||
|
|
||||||
|
double now_ts = (double)time(NULL);
|
||||||
|
|
||||||
/* Write simple JSON envelope */
|
/* Write simple JSON envelope */
|
||||||
if (in_buf[0] == '{') {
|
if (in_buf[0] == '{') {
|
||||||
/* Filter out existing node key and append new */
|
/* Filter out existing node key and append new */
|
||||||
fprintf(out, "{\n");
|
fprintf(out, "{\n");
|
||||||
int wrote_any = 0;
|
int wrote_any = 0;
|
||||||
|
|
||||||
/* Scan and copy other nodes */
|
/* Scan and copy other nodes if still fresh (< 30s) */
|
||||||
const char *known[] = { "t520", "bazina", "boxC", "crostini", "penguin", "android", "phone", NULL };
|
const char *known[] = { "t520", "bazina", "boxC", "crostini", "penguin", "android", "phone", NULL };
|
||||||
for (int k = 0; known[k] != NULL; ++k) {
|
for (int k = 0; known[k] != NULL; ++k) {
|
||||||
if (strcmp(known[k], node) == 0) continue;
|
if (strcmp(known[k], node) == 0) continue;
|
||||||
|
if ((strcmp(node, "crostini") == 0 && strcmp(known[k], "penguin") == 0) ||
|
||||||
|
(strcmp(node, "penguin") == 0 && strcmp(known[k], "crostini") == 0)) continue;
|
||||||
|
if ((strcmp(node, "android") == 0 && strcmp(known[k], "phone") == 0) ||
|
||||||
|
(strcmp(node, "phone") == 0 && strcmp(known[k], "android") == 0)) continue;
|
||||||
|
|
||||||
char needle[64];
|
char needle[64];
|
||||||
snprintf(needle, sizeof(needle), "\"%s\":", known[k]);
|
snprintf(needle, sizeof(needle), "\"%s\":", known[k]);
|
||||||
char *pos = strstr(in_buf, needle);
|
char *pos = strstr(in_buf, needle);
|
||||||
@@ -351,10 +442,19 @@ static void update_shm(const char *node, const char *payload) {
|
|||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
if (wrote_any) fprintf(out, ",\n");
|
size_t obj_len = (size_t)(ptr - brace_start);
|
||||||
fprintf(out, " \"%s\": ", known[k]);
|
char obj_buf[4096];
|
||||||
fwrite(brace_start, 1, (size_t)(ptr - brace_start), out);
|
if (obj_len < sizeof(obj_buf)) {
|
||||||
wrote_any = 1;
|
memcpy(obj_buf, brace_start, obj_len);
|
||||||
|
obj_buf[obj_len] = '\0';
|
||||||
|
double ts = extract_json_num(obj_buf, "timestamp");
|
||||||
|
if (ts <= 0.0 || (now_ts - ts) <= 30.0) {
|
||||||
|
if (wrote_any) fprintf(out, ",\n");
|
||||||
|
fprintf(out, " \"%s\": ", known[k]);
|
||||||
|
fwrite(brace_start, 1, obj_len, out);
|
||||||
|
wrote_any = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,36 +471,147 @@ static void update_shm(const char *node, const char *payload) {
|
|||||||
rename(tmp_path, shm_path);
|
rename(tmp_path, shm_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_wan_http(const char *payload) {
|
static void drain_udp_and_wan(int udp_sock, const char *local_node) {
|
||||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
/* 1. Drain incoming UDP telemetry packets on port 9850 */
|
||||||
if (sock < 0) return;
|
if (udp_sock >= 0) {
|
||||||
|
char buf[4096];
|
||||||
struct timeval tv;
|
while (1) {
|
||||||
tv.tv_sec = 1;
|
ssize_t n = recv(udp_sock, buf, sizeof(buf) - 1, 0);
|
||||||
tv.tv_usec = 500000;
|
if (n <= 0) break;
|
||||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
buf[n] = '\0';
|
||||||
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
char rnode[64] = {0};
|
||||||
|
if (!extract_json_str(buf, "node", rnode, sizeof(rnode))) {
|
||||||
struct sockaddr_in srv;
|
extract_json_str(buf, "name", rnode, sizeof(rnode));
|
||||||
memset(&srv, 0, sizeof(srv));
|
}
|
||||||
srv.sin_family = AF_INET;
|
if (rnode[0] != '\0' && strcmp(rnode, local_node) != 0) {
|
||||||
srv.sin_port = htons(80);
|
update_shm(rnode, buf);
|
||||||
inet_pton(AF_INET, "91.139.118.43", &srv.sin_addr);
|
}
|
||||||
|
|
||||||
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) == 0) {
|
|
||||||
char req[4096];
|
|
||||||
int req_len = snprintf(req, sizeof(req),
|
|
||||||
"POST /telemetry.php HTTP/1.1\r\n"
|
|
||||||
"Host: 91.139.118.43\r\n"
|
|
||||||
"Content-Type: application/json\r\n"
|
|
||||||
"Content-Length: %zu\r\n"
|
|
||||||
"Connection: close\r\n\r\n"
|
|
||||||
"%s", strlen(payload), payload);
|
|
||||||
if (req_len > 0) {
|
|
||||||
write(sock, req, (size_t)req_len);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(sock);
|
|
||||||
|
/* 2. On boxC, merge /dev/shm/fleet_wan_*.json (< 30s old) and rebroadcast NAT nodes */
|
||||||
|
glob_t gl;
|
||||||
|
if (glob("/dev/shm/fleet_wan_*.json", 0, NULL, &gl) == 0) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
for (size_t i = 0; i < gl.gl_pathc; ++i) {
|
||||||
|
struct stat st;
|
||||||
|
if (stat(gl.gl_pathv[i], &st) == 0 && (now - st.st_mtime) < 30) {
|
||||||
|
FILE *fp = fopen(gl.gl_pathv[i], "r");
|
||||||
|
if (fp) {
|
||||||
|
char wbuf[4096] = {0};
|
||||||
|
size_t wn = fread(wbuf, 1, sizeof(wbuf) - 1, fp);
|
||||||
|
fclose(fp);
|
||||||
|
if (wn > 0 && wbuf[0] == '{') {
|
||||||
|
wbuf[wn] = '\0';
|
||||||
|
char wnode[64] = {0};
|
||||||
|
if (!extract_json_str(wbuf, "node", wnode, sizeof(wnode))) {
|
||||||
|
extract_json_str(wbuf, "name", wnode, sizeof(wnode));
|
||||||
|
}
|
||||||
|
if (wnode[0] != '\0' && strcmp(wnode, local_node) != 0) {
|
||||||
|
update_shm(wnode, wbuf);
|
||||||
|
/* Rebroadcast NAT/WAN nodes (penguin/crostini/android) to LAN */
|
||||||
|
if (udp_sock >= 0 && (strcmp(wnode, "penguin") == 0 || strcmp(wnode, "crostini") == 0 || strcmp(wnode, "android") == 0 || strcmp(wnode, "phone") == 0)) {
|
||||||
|
const char *lan_targets[] = { "192.168.1.255", "192.168.1.100", "192.168.1.104", "192.168.1.103", NULL };
|
||||||
|
for (int t = 0; lan_targets[t] != NULL; ++t) {
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
memset(&sin, 0, sizeof(sin));
|
||||||
|
sin.sin_family = AF_INET;
|
||||||
|
sin.sin_port = htons(BEACON_PORT);
|
||||||
|
if (inet_pton(AF_INET, lan_targets[t], &sin.sin_addr) > 0) {
|
||||||
|
sendto(udp_sock, wbuf, wn, 0, (struct sockaddr *)&sin, sizeof(sin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
globfree(&gl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_wan_http(const char *local_node, const char *payload) {
|
||||||
|
const char *hosts[] = { "192.168.1.101", "91.139.118.43", NULL };
|
||||||
|
for (int h = 0; hosts[h] != NULL; ++h) {
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) continue;
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = 1;
|
||||||
|
tv.tv_usec = 200000;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
||||||
|
|
||||||
|
struct sockaddr_in srv;
|
||||||
|
memset(&srv, 0, sizeof(srv));
|
||||||
|
srv.sin_family = AF_INET;
|
||||||
|
srv.sin_port = htons(80);
|
||||||
|
inet_pton(AF_INET, hosts[h], &srv.sin_addr);
|
||||||
|
|
||||||
|
if (connect(sock, (struct sockaddr *)&srv, sizeof(srv)) == 0) {
|
||||||
|
char req[4096];
|
||||||
|
int req_len = snprintf(req, sizeof(req),
|
||||||
|
"POST /telemetry.php HTTP/1.1\r\n"
|
||||||
|
"Host: %s\r\n"
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Content-Length: %zu\r\n"
|
||||||
|
"Connection: close\r\n\r\n"
|
||||||
|
"%s", hosts[h], strlen(payload), payload);
|
||||||
|
if (req_len > 0 && write(sock, req, (size_t)req_len) > 0) {
|
||||||
|
char resp[16384] = {0};
|
||||||
|
size_t total_r = 0;
|
||||||
|
while (total_r < sizeof(resp) - 1) {
|
||||||
|
ssize_t r = read(sock, resp + total_r, sizeof(resp) - 1 - total_r);
|
||||||
|
if (r <= 0) break;
|
||||||
|
total_r += (size_t)r;
|
||||||
|
}
|
||||||
|
resp[total_r] = '\0';
|
||||||
|
char *body = strstr(resp, "\r\n\r\n");
|
||||||
|
if (body) {
|
||||||
|
body += 4;
|
||||||
|
char *first_brace = strchr(body, '{');
|
||||||
|
if (first_brace) {
|
||||||
|
const char *known[] = { "t520", "bazina", "boxC", "crostini", "penguin", "android", "phone", NULL };
|
||||||
|
double now_ts = (double)time(NULL);
|
||||||
|
for (int k = 0; known[k] != NULL; ++k) {
|
||||||
|
if (strcmp(known[k], local_node) == 0) continue;
|
||||||
|
char needle[64];
|
||||||
|
snprintf(needle, sizeof(needle), "\"%s\":", known[k]);
|
||||||
|
char *pos = strstr(first_brace, needle);
|
||||||
|
if (pos) {
|
||||||
|
char *bstart = strchr(pos, '{');
|
||||||
|
if (bstart) {
|
||||||
|
int depth = 1;
|
||||||
|
char *ptr = bstart + 1;
|
||||||
|
while (*ptr && depth > 0) {
|
||||||
|
if (*ptr == '{') depth++;
|
||||||
|
else if (*ptr == '}') depth--;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
if (depth == 0) {
|
||||||
|
size_t olen = (size_t)(ptr - bstart);
|
||||||
|
char obuf[4096];
|
||||||
|
if (olen < sizeof(obuf)) {
|
||||||
|
memcpy(obuf, bstart, olen);
|
||||||
|
obuf[olen] = '\0';
|
||||||
|
double ts = extract_json_num(obuf, "timestamp");
|
||||||
|
if (ts > 0.0 && (now_ts - ts) <= 30.0) {
|
||||||
|
update_shm(known[k], obuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
@@ -416,8 +627,23 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
int udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
int udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
if (udp_sock >= 0) {
|
if (udp_sock >= 0) {
|
||||||
int broadcast = 1;
|
int opt = 1;
|
||||||
setsockopt(udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
|
setsockopt(udp_sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt));
|
||||||
|
setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||||
|
#ifdef SO_REUSEPORT
|
||||||
|
setsockopt(udp_sock, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
|
||||||
|
#endif
|
||||||
|
int flags = fcntl(udp_sock, F_GETFL, 0);
|
||||||
|
if (flags >= 0) fcntl(udp_sock, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
if (!run_once) {
|
||||||
|
struct sockaddr_in baddr;
|
||||||
|
memset(&baddr, 0, sizeof(baddr));
|
||||||
|
baddr.sin_family = AF_INET;
|
||||||
|
baddr.sin_port = htons(BEACON_PORT);
|
||||||
|
baddr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
bind(udp_sock, (struct sockaddr *)&baddr, sizeof(baddr));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!print_json) {
|
if (!print_json) {
|
||||||
@@ -463,7 +689,12 @@ int main(int argc, char **argv) {
|
|||||||
mount, disk_free, disk_total,
|
mount, disk_free, disk_total,
|
||||||
gpu_str, temp_c, bat_pct, alert);
|
gpu_str, temp_c, bat_pct, alert);
|
||||||
|
|
||||||
/* 1. Broadcast UDP */
|
/* 1. Drain incoming UDP packets & WAN files */
|
||||||
|
if (!run_once) {
|
||||||
|
drain_udp_and_wan(udp_sock, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Broadcast UDP */
|
||||||
if (udp_sock >= 0) {
|
if (udp_sock >= 0) {
|
||||||
size_t payload_len = strlen(payload);
|
size_t payload_len = strlen(payload);
|
||||||
for (int i = 0; TARGETS[i] != NULL; ++i) {
|
for (int i = 0; TARGETS[i] != NULL; ++i) {
|
||||||
@@ -477,12 +708,12 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2. Update local SHM cache */
|
/* 3. Update local SHM cache */
|
||||||
update_shm(node, payload);
|
update_shm(node, payload);
|
||||||
|
|
||||||
/* 3. HTTP WAN Fallback */
|
/* 4. HTTP Bridge & Sync */
|
||||||
if (strcmp(node, "boxC") != 0) {
|
if (strcmp(node, "boxC") != 0) {
|
||||||
send_wan_http(payload);
|
send_wan_http(node, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (print_json) {
|
if (print_json) {
|
||||||
|
|||||||
Reference in New Issue
Block a user