Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db6fc65ee1 | |||
| 1002a6be68 | |||
| 017d7bb1bb |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nanometrics-agent"
|
name = "nanometrics-agent"
|
||||||
version = "0.1.7"
|
version = "0.1.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
|
|||||||
+47
-3
@@ -10,22 +10,66 @@ extern "C" fn handle_signal(_: libc::c_int) {
|
|||||||
RUNNING.store(false, Ordering::Relaxed);
|
RUNNING.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn physical_ipv4_addrs() -> Vec<String> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
unsafe {
|
||||||
|
let mut ifap = std::ptr::null_mut::<libc::ifaddrs>();
|
||||||
|
if libc::getifaddrs(&mut ifap) != 0 { return result; }
|
||||||
|
let mut ifa = ifap;
|
||||||
|
while !ifa.is_null() {
|
||||||
|
let flags = (*ifa).ifa_flags as i32;
|
||||||
|
let up = flags & libc::IFF_UP as i32 != 0;
|
||||||
|
let loopback = flags & libc::IFF_LOOPBACK as i32 != 0;
|
||||||
|
let pointop = flags & libc::IFF_POINTOPOINT as i32 != 0;
|
||||||
|
if !up || loopback || pointop { ifa = (*ifa).ifa_next; continue; }
|
||||||
|
|
||||||
|
let name = std::ffi::CStr::from_ptr((*ifa).ifa_name)
|
||||||
|
.to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
// Type 1 = ARPHRD_ETHER (Ethernet + WiFi), exclut WireGuard (65534), tunnels, etc.
|
||||||
|
let itype: u32 = std::fs::read_to_string(
|
||||||
|
format!("/sys/class/net/{}/type", name))
|
||||||
|
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
|
||||||
|
if itype != 1 { ifa = (*ifa).ifa_next; continue; }
|
||||||
|
|
||||||
|
// Exclut bridges et interfaces Docker par nom
|
||||||
|
let is_virtual = name.starts_with("br-") || name.starts_with("docker")
|
||||||
|
|| name.starts_with("virbr") || name.starts_with("veth");
|
||||||
|
if is_virtual { ifa = (*ifa).ifa_next; continue; }
|
||||||
|
|
||||||
|
if let Some(addr) = (*ifa).ifa_addr.as_ref() {
|
||||||
|
if addr.sa_family as i32 == libc::AF_INET {
|
||||||
|
let sin = addr as *const _ as *const libc::sockaddr_in;
|
||||||
|
let b = (*sin).sin_addr.s_addr.to_ne_bytes();
|
||||||
|
result.push(format!("{}.{}.{}.{}", b[0], b[1], b[2], b[3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ifa = (*ifa).ifa_next;
|
||||||
|
}
|
||||||
|
libc::freeifaddrs(ifap);
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn get_local_ip(server_ip: &str) -> String {
|
fn get_local_ip(server_ip: &str) -> String {
|
||||||
|
let physical = physical_ipv4_addrs();
|
||||||
|
|
||||||
use std::net::UdpSocket;
|
use std::net::UdpSocket;
|
||||||
// Try server IP first (always reachable), then internet fallback
|
|
||||||
for target in &[format!("{}:80", server_ip), "8.8.8.8:80".to_string()] {
|
for target in &[format!("{}:80", server_ip), "8.8.8.8:80".to_string()] {
|
||||||
if let Ok(s) = UdpSocket::bind("0.0.0.0:0") {
|
if let Ok(s) = UdpSocket::bind("0.0.0.0:0") {
|
||||||
if s.connect(target.as_str()).is_ok() {
|
if s.connect(target.as_str()).is_ok() {
|
||||||
if let Ok(addr) = s.local_addr() {
|
if let Ok(addr) = s.local_addr() {
|
||||||
let ip = addr.ip().to_string();
|
let ip = addr.ip().to_string();
|
||||||
if ip != "0.0.0.0" {
|
// N'accepte que si c'est une vraie interface physique
|
||||||
|
if ip != "0.0.0.0" && physical.contains(&ip) {
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"0.0.0.0".to_string()
|
// Fallback : première IP physique disponible
|
||||||
|
physical.into_iter().next().unwrap_or_else(|| "0.0.0.0".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_config_update(cfg: &mut config::Config, data: &[u8]) {
|
fn apply_config_update(cfg: &mut config::Config, data: &[u8]) {
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ pub fn current_yday() -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_physical(name: &str) -> bool {
|
fn is_physical(name: &str) -> bool {
|
||||||
if name == "lo" { return false; }
|
// Type 1 = ARPHRD_ETHER (Ethernet + WiFi). WireGuard = 65534, tunnels = autres.
|
||||||
for prefix in &["veth", "docker", "br-", "virbr", "vir", "tun", "tap", "dummy", "bond"] {
|
let itype: u32 = std::fs::read_to_string(format!("/sys/class/net/{}/type", name))
|
||||||
if name.starts_with(prefix) { return false; }
|
.ok().and_then(|s| s.trim().parse().ok()).unwrap_or(0);
|
||||||
}
|
if itype != 1 { return false; }
|
||||||
true
|
// Exclut bridges et interfaces Docker par nom (type 1 aussi)
|
||||||
|
!name.starts_with("br-") && !name.starts_with("docker")
|
||||||
|
&& !name.starts_with("virbr") && !name.starts_with("veth")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_sysfs(iface: &str, file: &str) -> Option<String> {
|
fn read_sysfs(iface: &str, file: &str) -> Option<String> {
|
||||||
|
|||||||
@@ -89,15 +89,29 @@ pub fn collect() -> Option<Vec<crate::payload::SmartMetrics>> {
|
|||||||
.flatten()
|
.flatten()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|e| e.file_name().into_string().unwrap_or_default())
|
.map(|e| e.file_name().into_string().unwrap_or_default())
|
||||||
.filter(|n| n.starts_with("sd") || n.starts_with("nvme"))
|
.filter_map(|n| {
|
||||||
.map(|n| format!("/dev/{}", n))
|
if n.starts_with("sd") {
|
||||||
|
Some(format!("/dev/{}", n))
|
||||||
|
} else if n.starts_with("nvme") && !n.contains('n') {
|
||||||
|
// /sys/block contient nvme0n1 (namespace) — on utilise le contrôleur nvme0
|
||||||
|
Some(format!("/dev/{}", n))
|
||||||
|
} else if n.starts_with("nvme") && n.contains('n') {
|
||||||
|
// Déduit le contrôleur depuis nvme0n1 → nvme0
|
||||||
|
let ctrl = n.split('n').next()?;
|
||||||
|
Some(format!("/dev/{}", ctrl))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<std::collections::HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
.collect();
|
.collect();
|
||||||
devs.sort();
|
devs.sort();
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for dev in &devs {
|
for dev in &devs {
|
||||||
let Ok(output) = std::process::Command::new("smartctl")
|
let Ok(output) = std::process::Command::new("smartctl")
|
||||||
.args(["-j", dev])
|
.args(["-a", "-j", dev])
|
||||||
.output() else { continue };
|
.output() else { continue };
|
||||||
let json = String::from_utf8_lossy(&output.stdout);
|
let json = String::from_utf8_lossy(&output.stdout);
|
||||||
if let Ok(metrics) = parse_json(&json) {
|
if let Ok(metrics) = parse_json(&json) {
|
||||||
|
|||||||
@@ -15,7 +15,13 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'JetBrains Mono';
|
font-family: 'JetBrains Mono';
|
||||||
src: url('../fonts/jetbrains-mono.woff2') format('woff2');
|
src: url('../fonts/jetbrains-mono.woff2') format('woff2');
|
||||||
font-weight: 400 700;
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'JetBrains Mono';
|
||||||
|
src: url('../fonts/jetbrains-mono-bold.woff2') format('woff2');
|
||||||
|
font-weight: 700;
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@ const Popups = (() => {
|
|||||||
let _currentAgentId = null;
|
let _currentAgentId = null;
|
||||||
let _agentCfgData = null;
|
let _agentCfgData = null;
|
||||||
let _resizeObs = null;
|
let _resizeObs = null;
|
||||||
|
let _resizeTimer = null;
|
||||||
|
|
||||||
// ══ POPUP DÉTAIL ══
|
// ══ POPUP DÉTAIL ══
|
||||||
async function showDetail(agentId) {
|
async function showDetail(agentId) {
|
||||||
@@ -214,11 +215,14 @@ const Popups = (() => {
|
|||||||
if (_resizeObs) _resizeObs.disconnect();
|
if (_resizeObs) _resizeObs.disconnect();
|
||||||
const pd = document.getElementById('popup-detail');
|
const pd = document.getElementById('popup-detail');
|
||||||
_resizeObs = new ResizeObserver(() => {
|
_resizeObs = new ResizeObserver(() => {
|
||||||
API.putServerConfig({
|
clearTimeout(_resizeTimer);
|
||||||
...App.serverConfig,
|
_resizeTimer = setTimeout(() => {
|
||||||
popup_detail_w: pd.offsetWidth,
|
API.putServerConfig({
|
||||||
popup_detail_h: pd.offsetHeight,
|
...App.serverConfig,
|
||||||
}).catch(() => {});
|
popup_detail_w: pd.offsetWidth,
|
||||||
|
popup_detail_h: pd.offsetHeight,
|
||||||
|
}).catch(() => {});
|
||||||
|
}, 600);
|
||||||
});
|
});
|
||||||
_resizeObs.observe(pd);
|
_resizeObs.observe(pd);
|
||||||
|
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user