chore: auto-commit (13 archivos)
- CMakeLists.txt - agent_protocol.cpp - agent_protocol.h - app.md - appicon.ico - hosts_db.cpp - hosts_db.h - http_client.cpp - http_client.h - main.cpp - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,600 @@
|
||||
#include "agent_protocol.h"
|
||||
#include "http_client.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
#endif
|
||||
|
||||
namespace pex {
|
||||
|
||||
// =========================================================================
|
||||
// HTTP-agent placeholders (issue 0111)
|
||||
// =========================================================================
|
||||
|
||||
std::vector<ProcInfo> fetch_processes(const std::string& base_url, const std::string& token) {
|
||||
if (base_url.empty()) return {};
|
||||
auto resp = pex_http::get(base_url + "/api/processes", token);
|
||||
if (resp.status != 200) return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
StatsSnapshot fetch_stats(const std::string& base_url, const std::string& token) {
|
||||
StatsSnapshot s;
|
||||
s.unix_ts = (int64_t)std::time(nullptr);
|
||||
if (base_url.empty()) return s;
|
||||
auto resp = pex_http::get(base_url + "/api/stats", token);
|
||||
if (resp.status != 200) return s;
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<DeviceInfo> fetch_devices(const std::string& base_url, const std::string& token) {
|
||||
if (base_url.empty()) return {};
|
||||
auto resp = pex_http::get(base_url + "/api/devices", token);
|
||||
if (resp.status != 200) return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ServiceInfo> fetch_services(const std::string& base_url, const std::string& token) {
|
||||
if (base_url.empty()) return {};
|
||||
auto resp = pex_http::get(base_url + "/api/services", token);
|
||||
if (resp.status != 200) return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<NetConn> fetch_netconns(const std::string& base_url, const std::string& token) {
|
||||
if (base_url.empty()) return {};
|
||||
auto resp = pex_http::get(base_url + "/api/netconns", token);
|
||||
if (resp.status != 200) return {};
|
||||
return {};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// LINUX local
|
||||
// =========================================================================
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static std::string read_file(const char* path) {
|
||||
std::ifstream f(path);
|
||||
if (!f) return {};
|
||||
std::stringstream ss; ss << f.rdbuf();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// CPU%: diff de /proc/stat entre dos llamadas.
|
||||
struct CpuJiffies { unsigned long long user, nice, system, idle, iowait, irq, softirq, steal; };
|
||||
static bool parse_proc_stat(CpuJiffies& out) {
|
||||
std::ifstream f("/proc/stat");
|
||||
if (!f) return false;
|
||||
std::string cpu;
|
||||
f >> cpu;
|
||||
if (cpu != "cpu") return false;
|
||||
f >> out.user >> out.nice >> out.system >> out.idle
|
||||
>> out.iowait >> out.irq >> out.softirq >> out.steal;
|
||||
return true;
|
||||
}
|
||||
|
||||
static float diff_cpu_pct(CpuJiffies& prev, bool& have_prev) {
|
||||
CpuJiffies cur{};
|
||||
if (!parse_proc_stat(cur)) return 0.0f;
|
||||
if (!have_prev) { prev = cur; have_prev = true; return 0.0f; }
|
||||
auto total = [](const CpuJiffies& j) {
|
||||
return j.user + j.nice + j.system + j.idle + j.iowait + j.irq + j.softirq + j.steal;
|
||||
};
|
||||
unsigned long long t0 = total(prev), t1 = total(cur);
|
||||
unsigned long long i0 = prev.idle + prev.iowait, i1 = cur.idle + cur.iowait;
|
||||
unsigned long long dt = t1 - t0, didle = i1 - i0;
|
||||
prev = cur;
|
||||
if (dt == 0) return 0.0f;
|
||||
return (float)((double)(dt - didle) / (double)dt * 100.0);
|
||||
}
|
||||
|
||||
std::vector<ProcInfo> fetch_processes_local() {
|
||||
std::vector<ProcInfo> out;
|
||||
DIR* d = opendir("/proc");
|
||||
if (!d) return out;
|
||||
long page_kb = sysconf(_SC_PAGESIZE) / 1024;
|
||||
struct dirent* e;
|
||||
while ((e = readdir(d)) != nullptr) {
|
||||
if (e->d_type != DT_DIR) continue;
|
||||
bool numeric = true;
|
||||
for (const char* p = e->d_name; *p; ++p) if (*p < '0' || *p > '9') { numeric = false; break; }
|
||||
if (!numeric) continue;
|
||||
|
||||
ProcInfo info;
|
||||
info.pid = std::atoi(e->d_name);
|
||||
|
||||
std::string stat = read_file((std::string("/proc/") + e->d_name + "/stat").c_str());
|
||||
if (!stat.empty()) {
|
||||
auto open_paren = stat.find('(');
|
||||
auto close_paren = stat.rfind(')');
|
||||
if (open_paren != std::string::npos && close_paren != std::string::npos) {
|
||||
info.name = stat.substr(open_paren + 1, close_paren - open_paren - 1);
|
||||
std::istringstream iss(stat.substr(close_paren + 2));
|
||||
char state;
|
||||
int ppid;
|
||||
iss >> state >> ppid;
|
||||
info.state.assign(1, state);
|
||||
info.ppid = ppid;
|
||||
}
|
||||
}
|
||||
|
||||
std::string statm = read_file((std::string("/proc/") + e->d_name + "/statm").c_str());
|
||||
if (!statm.empty()) {
|
||||
long size_pages, resident_pages;
|
||||
if (std::sscanf(statm.c_str(), "%ld %ld", &size_pages, &resident_pages) == 2) {
|
||||
info.ram_mb = (float)(resident_pages * page_kb) / 1024.0f;
|
||||
}
|
||||
}
|
||||
|
||||
std::string cmdline = read_file((std::string("/proc/") + e->d_name + "/cmdline").c_str());
|
||||
for (auto& c : cmdline) if (c == '\0') c = ' ';
|
||||
info.cmdline = std::move(cmdline);
|
||||
|
||||
out.push_back(std::move(info));
|
||||
}
|
||||
closedir(d);
|
||||
return out;
|
||||
}
|
||||
|
||||
StatsSnapshot fetch_stats_local() {
|
||||
StatsSnapshot s;
|
||||
s.unix_ts = (int64_t)std::time(nullptr);
|
||||
struct sysinfo si{};
|
||||
if (sysinfo(&si) == 0) {
|
||||
s.ram_total_mb = (float)(si.totalram * si.mem_unit) / (1024.0f * 1024.0f);
|
||||
s.ram_used_mb = (float)((si.totalram - si.freeram) * si.mem_unit) / (1024.0f * 1024.0f);
|
||||
}
|
||||
static CpuJiffies prev{}; static bool have_prev = false;
|
||||
s.cpu_pct = diff_cpu_pct(prev, have_prev);
|
||||
return s;
|
||||
}
|
||||
|
||||
// En Linux el "wsl" no aplica: alias del local.
|
||||
std::vector<ProcInfo> fetch_processes_wsl() { return fetch_processes_local(); }
|
||||
StatsSnapshot fetch_stats_wsl() { return fetch_stats_local(); }
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
// =========================================================================
|
||||
// WINDOWS local (psapi + GetSystemTimes)
|
||||
// =========================================================================
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static uint64_t ft_to_u64(const FILETIME& ft) {
|
||||
ULARGE_INTEGER u{}; u.LowPart = ft.dwLowDateTime; u.HighPart = ft.dwHighDateTime;
|
||||
return u.QuadPart;
|
||||
}
|
||||
|
||||
StatsSnapshot fetch_stats_local() {
|
||||
StatsSnapshot s;
|
||||
s.unix_ts = (int64_t)std::time(nullptr);
|
||||
|
||||
MEMORYSTATUSEX ms{}; ms.dwLength = sizeof(ms);
|
||||
if (GlobalMemoryStatusEx(&ms)) {
|
||||
s.ram_total_mb = (float)((double)ms.ullTotalPhys / (1024.0 * 1024.0));
|
||||
s.ram_used_mb = (float)((double)(ms.ullTotalPhys - ms.ullAvailPhys) / (1024.0 * 1024.0));
|
||||
}
|
||||
|
||||
static FILETIME prev_idle{}, prev_kernel{}, prev_user{};
|
||||
static bool have_prev = false;
|
||||
FILETIME idle, kernel, user;
|
||||
if (GetSystemTimes(&idle, &kernel, &user)) {
|
||||
if (have_prev) {
|
||||
uint64_t didle = ft_to_u64(idle) - ft_to_u64(prev_idle);
|
||||
uint64_t dkern = ft_to_u64(kernel) - ft_to_u64(prev_kernel);
|
||||
uint64_t duser = ft_to_u64(user) - ft_to_u64(prev_user);
|
||||
uint64_t total = dkern + duser; // kernel-time on Windows includes idle
|
||||
if (total > 0) {
|
||||
s.cpu_pct = (float)((double)(total - didle) / (double)total * 100.0);
|
||||
}
|
||||
}
|
||||
prev_idle = idle; prev_kernel = kernel; prev_user = user;
|
||||
have_prev = true;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<ProcInfo> fetch_processes_local() {
|
||||
std::vector<ProcInfo> out;
|
||||
DWORD pids[4096], cb_needed = 0;
|
||||
if (!EnumProcesses(pids, sizeof(pids), &cb_needed)) return out;
|
||||
int n = (int)(cb_needed / sizeof(DWORD));
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (pids[i] == 0) continue;
|
||||
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ,
|
||||
FALSE, pids[i]);
|
||||
if (!h) continue;
|
||||
|
||||
ProcInfo p;
|
||||
p.pid = (int32_t)pids[i];
|
||||
|
||||
char path[MAX_PATH] = {};
|
||||
if (GetModuleFileNameExA(h, NULL, path, sizeof(path))) {
|
||||
const char* base = std::strrchr(path, '\\');
|
||||
p.name = base ? (base + 1) : path;
|
||||
p.cmdline = path;
|
||||
} else {
|
||||
p.name = "?";
|
||||
}
|
||||
|
||||
PROCESS_MEMORY_COUNTERS pmc{};
|
||||
if (GetProcessMemoryInfo(h, &pmc, sizeof(pmc))) {
|
||||
p.ram_mb = (float)((double)pmc.WorkingSetSize / (1024.0 * 1024.0));
|
||||
}
|
||||
p.state = "R";
|
||||
|
||||
out.push_back(std::move(p));
|
||||
CloseHandle(h);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// WSL trampoline (binario Windows ejecuta `wsl.exe -e ...`)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Ejecuta un comando y captura stdout SIN abrir ventana de consola.
|
||||
// _popen() spawns cmd.exe con WshShell -> flash de consola visible en cada
|
||||
// llamada. CreateProcessA + CREATE_NO_WINDOW + STARTF_USESHOWWINDOW(SW_HIDE)
|
||||
// la oculta. Pipe anonimo para leer stdout sincronamente.
|
||||
static std::string run_capture(const char* cmd) {
|
||||
std::string out;
|
||||
|
||||
SECURITY_ATTRIBUTES sa{};
|
||||
sa.nLength = sizeof(sa);
|
||||
sa.bInheritHandle = TRUE;
|
||||
|
||||
HANDLE rd = NULL, wr = NULL;
|
||||
if (!CreatePipe(&rd, &wr, &sa, 0)) return out;
|
||||
SetHandleInformation(rd, HANDLE_FLAG_INHERIT, 0);
|
||||
|
||||
STARTUPINFOA si{};
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
|
||||
si.wShowWindow = SW_HIDE;
|
||||
si.hStdOutput = wr;
|
||||
si.hStdError = wr;
|
||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
PROCESS_INFORMATION pi{};
|
||||
// CreateProcessA muta lpCommandLine — copiar a buffer escribible.
|
||||
std::string mutable_cmd = cmd;
|
||||
if (!CreateProcessA(NULL, mutable_cmd.data(), NULL, NULL, TRUE,
|
||||
CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
|
||||
CloseHandle(rd); CloseHandle(wr);
|
||||
return out;
|
||||
}
|
||||
CloseHandle(wr); // padre cierra su extremo write para que ReadFile vea EOF
|
||||
|
||||
char buf[4096];
|
||||
DWORD n = 0;
|
||||
while (ReadFile(rd, buf, sizeof(buf), &n, NULL) && n > 0) {
|
||||
out.append(buf, n);
|
||||
}
|
||||
CloseHandle(rd);
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return out;
|
||||
}
|
||||
|
||||
struct WslCpuJiffies { unsigned long long user, nice, system, idle, iowait, irq, softirq, steal; };
|
||||
|
||||
static bool parse_wsl_proc_stat(WslCpuJiffies& out) {
|
||||
std::string txt = run_capture("wsl.exe -e sh -c \"head -n1 /proc/stat\"");
|
||||
if (txt.empty()) return false;
|
||||
std::istringstream iss(txt);
|
||||
std::string cpu; iss >> cpu;
|
||||
if (cpu != "cpu") return false;
|
||||
iss >> out.user >> out.nice >> out.system >> out.idle
|
||||
>> out.iowait >> out.irq >> out.softirq >> out.steal;
|
||||
return true;
|
||||
}
|
||||
|
||||
StatsSnapshot fetch_stats_wsl() {
|
||||
StatsSnapshot s;
|
||||
s.unix_ts = (int64_t)std::time(nullptr);
|
||||
|
||||
// RAM via /proc/meminfo
|
||||
std::string mem = run_capture("wsl.exe -e sh -c \"cat /proc/meminfo\"");
|
||||
if (mem.empty()) return s;
|
||||
long mem_total_kb = 0, mem_available_kb = 0;
|
||||
std::istringstream ms(mem);
|
||||
std::string line;
|
||||
while (std::getline(ms, line)) {
|
||||
long v;
|
||||
if (std::sscanf(line.c_str(), "MemTotal: %ld kB", &v) == 1) mem_total_kb = v;
|
||||
else if (std::sscanf(line.c_str(), "MemAvailable: %ld kB", &v) == 1) mem_available_kb = v;
|
||||
}
|
||||
if (mem_total_kb > 0) {
|
||||
s.ram_total_mb = (float)mem_total_kb / 1024.0f;
|
||||
s.ram_used_mb = (float)(mem_total_kb - mem_available_kb) / 1024.0f;
|
||||
}
|
||||
|
||||
// CPU% diff
|
||||
static WslCpuJiffies prev{}; static bool have_prev = false;
|
||||
WslCpuJiffies cur{};
|
||||
if (parse_wsl_proc_stat(cur)) {
|
||||
if (have_prev) {
|
||||
auto total = [](const WslCpuJiffies& j) {
|
||||
return j.user + j.nice + j.system + j.idle + j.iowait + j.irq + j.softirq + j.steal;
|
||||
};
|
||||
unsigned long long t0 = total(prev), t1 = total(cur);
|
||||
unsigned long long i0 = prev.idle + prev.iowait, i1 = cur.idle + cur.iowait;
|
||||
unsigned long long dt = t1 - t0, didle = i1 - i0;
|
||||
if (dt > 0) s.cpu_pct = (float)((double)(dt - didle) / (double)dt * 100.0);
|
||||
}
|
||||
prev = cur;
|
||||
have_prev = true;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<ProcInfo> fetch_processes_wsl() {
|
||||
std::vector<ProcInfo> out;
|
||||
// ps -eo pid,stat,rss,comm — sin header.
|
||||
std::string txt = run_capture("wsl.exe -e ps -eo pid,stat,rss,comm --no-headers");
|
||||
if (txt.empty()) return out;
|
||||
std::istringstream iss(txt);
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
int pid = 0, rss_kb = 0;
|
||||
char stat[8] = {}, comm[256] = {};
|
||||
if (std::sscanf(line.c_str(), "%d %7s %d %255s", &pid, stat, &rss_kb, comm) == 4) {
|
||||
ProcInfo p;
|
||||
p.pid = pid;
|
||||
p.state = stat;
|
||||
p.ram_mb = (float)rss_kb / 1024.0f;
|
||||
p.name = comm;
|
||||
out.push_back(std::move(p));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
// =========================================================================
|
||||
// Parsers compartidos para Network/Devices/Services (Linux + WSL output)
|
||||
// =========================================================================
|
||||
|
||||
static std::vector<NetConn> parse_ss_output(const std::string& text) {
|
||||
std::vector<NetConn> out;
|
||||
std::istringstream iss(text);
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
if (line.empty()) continue;
|
||||
std::istringstream ls(line);
|
||||
NetConn c;
|
||||
std::string send_q, recv_q;
|
||||
ls >> c.proto >> c.state >> send_q >> recv_q >> c.local >> c.remote;
|
||||
std::string rest; std::getline(ls, rest);
|
||||
auto pid_pos = rest.find("pid=");
|
||||
if (pid_pos != std::string::npos) c.pid = std::atoi(rest.c_str() + pid_pos + 4);
|
||||
if (!c.proto.empty()) out.push_back(c);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<DeviceInfo> parse_lsblk_output(const std::string& text) {
|
||||
std::vector<DeviceInfo> out;
|
||||
std::istringstream iss(text);
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
if (line.empty()) continue;
|
||||
std::istringstream ls(line);
|
||||
std::string name, size_bytes, mount, fstype;
|
||||
ls >> name >> size_bytes >> mount >> fstype;
|
||||
if (name.empty()) continue;
|
||||
DeviceInfo d;
|
||||
d.kind = "disk";
|
||||
d.name = name;
|
||||
double sz = (double)std::atoll(size_bytes.c_str()) / (1024.0*1024.0*1024.0);
|
||||
char buf[128];
|
||||
std::snprintf(buf, sizeof(buf), "%.1f GB %s %s",
|
||||
sz,
|
||||
fstype.empty() ? "" : fstype.c_str(),
|
||||
mount.empty() ? "" : mount.c_str());
|
||||
d.detail = buf;
|
||||
d.usage_pct = -1.0f;
|
||||
out.push_back(d);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<ServiceInfo> parse_systemctl_output(const std::string& text) {
|
||||
std::vector<ServiceInfo> out;
|
||||
std::istringstream iss(text);
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
if (line.empty()) continue;
|
||||
std::istringstream ls(line);
|
||||
std::string unit, load, active, sub;
|
||||
ls >> unit >> load >> active >> sub;
|
||||
if (unit.empty()) continue;
|
||||
ServiceInfo s;
|
||||
s.name = unit;
|
||||
s.state = active + "/" + sub;
|
||||
s.runtime = "systemd";
|
||||
std::string desc; std::getline(ls, desc);
|
||||
auto p0 = desc.find_first_not_of(' ');
|
||||
if (p0 != std::string::npos) s.description = desc.substr(p0);
|
||||
out.push_back(s);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// LINUX impls (Network/Devices/Services local)
|
||||
// =========================================================================
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static std::string popen_capture_linux(const char* cmd) {
|
||||
std::string out;
|
||||
FILE* fp = popen(cmd, "r");
|
||||
if (!fp) return out;
|
||||
char buf[4096];
|
||||
while (fgets(buf, sizeof(buf), fp)) out += buf;
|
||||
pclose(fp);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<NetConn> fetch_netconns_local() {
|
||||
return parse_ss_output(popen_capture_linux("ss -tunaH -p 2>/dev/null"));
|
||||
}
|
||||
std::vector<DeviceInfo> fetch_devices_local() {
|
||||
return parse_lsblk_output(popen_capture_linux(
|
||||
"lsblk -bno NAME,SIZE,MOUNTPOINT,FSTYPE 2>/dev/null"));
|
||||
}
|
||||
std::vector<ServiceInfo> fetch_services_local() {
|
||||
return parse_systemctl_output(popen_capture_linux(
|
||||
"systemctl list-units --type=service --all --no-legend --plain --no-pager 2>/dev/null"));
|
||||
}
|
||||
|
||||
// En Linux la "version WSL" = local (no aplica).
|
||||
std::vector<NetConn> fetch_netconns_wsl() { return fetch_netconns_local(); }
|
||||
std::vector<DeviceInfo> fetch_devices_wsl() { return fetch_devices_local(); }
|
||||
std::vector<ServiceInfo> fetch_services_wsl() { return fetch_services_local(); }
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
// =========================================================================
|
||||
// WINDOWS impls (Network/Devices/Services local + WSL trampoline)
|
||||
// =========================================================================
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static std::vector<NetConn> parse_netstat_windows(const std::string& text) {
|
||||
std::vector<NetConn> out;
|
||||
std::istringstream iss(text);
|
||||
std::string line;
|
||||
while (std::getline(iss, line)) {
|
||||
if (!line.empty() && line.back() == '\r') line.pop_back();
|
||||
size_t p0 = line.find_first_not_of(' ');
|
||||
if (p0 == std::string::npos) continue;
|
||||
std::istringstream ls(line.substr(p0));
|
||||
NetConn c;
|
||||
ls >> c.proto;
|
||||
if (c.proto != "TCP" && c.proto != "UDP") continue;
|
||||
ls >> c.local >> c.remote;
|
||||
std::string pid_str;
|
||||
if (c.proto == "TCP") { ls >> c.state >> pid_str; }
|
||||
else { ls >> pid_str; c.state = ""; }
|
||||
c.pid = std::atoi(pid_str.c_str());
|
||||
out.push_back(c);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<NetConn> fetch_netconns_local() {
|
||||
return parse_netstat_windows(run_capture("netstat -ano"));
|
||||
}
|
||||
|
||||
std::vector<DeviceInfo> fetch_devices_local() {
|
||||
std::vector<DeviceInfo> out;
|
||||
char drives[256] = {};
|
||||
DWORD n = GetLogicalDriveStringsA(sizeof(drives), drives);
|
||||
for (char* p = drives; *p && (p - drives) < (int)n; ) {
|
||||
DeviceInfo d;
|
||||
d.kind = "disk";
|
||||
d.name = p;
|
||||
ULARGE_INTEGER avail{}, total{}, freeb{};
|
||||
if (GetDiskFreeSpaceExA(p, &avail, &total, &freeb)) {
|
||||
char detail[128];
|
||||
double total_gb = (double)total.QuadPart / (1024.0*1024.0*1024.0);
|
||||
double free_gb = (double)freeb.QuadPart / (1024.0*1024.0*1024.0);
|
||||
std::snprintf(detail, sizeof(detail),
|
||||
"%.1f GB free / %.1f GB total", free_gb, total_gb);
|
||||
d.detail = detail;
|
||||
d.usage_pct = total.QuadPart
|
||||
? (float)(100.0 * (double)(total.QuadPart - freeb.QuadPart) / (double)total.QuadPart)
|
||||
: -1.0f;
|
||||
} else {
|
||||
d.detail = "(unavailable)";
|
||||
d.usage_pct = -1.0f;
|
||||
}
|
||||
out.push_back(d);
|
||||
p += std::strlen(p) + 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<ServiceInfo> parse_sc_query_output(const std::string& text) {
|
||||
std::vector<ServiceInfo> out;
|
||||
std::istringstream iss(text);
|
||||
std::string line;
|
||||
ServiceInfo cur;
|
||||
bool active = false;
|
||||
auto trim = [](std::string s) {
|
||||
size_t a = s.find_first_not_of(' ');
|
||||
if (a == std::string::npos) return std::string();
|
||||
size_t b = s.find_last_not_of(" \r\n\t");
|
||||
return s.substr(a, b - a + 1);
|
||||
};
|
||||
while (std::getline(iss, line)) {
|
||||
if (!line.empty() && line.back() == '\r') line.pop_back();
|
||||
size_t p0 = line.find_first_not_of(' ');
|
||||
if (p0 == std::string::npos) continue;
|
||||
std::string l = line.substr(p0);
|
||||
if (l.rfind("SERVICE_NAME:", 0) == 0) {
|
||||
if (active) out.push_back(cur);
|
||||
cur = {};
|
||||
cur.runtime = "sc";
|
||||
cur.name = trim(l.substr(13));
|
||||
active = true;
|
||||
} else if (l.rfind("DISPLAY_NAME:", 0) == 0) {
|
||||
cur.description = trim(l.substr(13));
|
||||
} else if (l.rfind("STATE", 0) == 0) {
|
||||
auto colon = l.find(':');
|
||||
if (colon != std::string::npos) {
|
||||
std::istringstream ss2(l.substr(colon + 1));
|
||||
int code; std::string name;
|
||||
ss2 >> code >> name;
|
||||
cur.state = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (active) out.push_back(cur);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ServiceInfo> fetch_services_local() {
|
||||
return parse_sc_query_output(run_capture("sc query state= all type= service"));
|
||||
}
|
||||
|
||||
// WSL trampolines (binario Windows ejecuta wsl.exe). run_capture es hidden.
|
||||
std::vector<NetConn> fetch_netconns_wsl() {
|
||||
return parse_ss_output(run_capture(
|
||||
"wsl.exe -e sh -c \"ss -tunaH -p 2>/dev/null\""));
|
||||
}
|
||||
std::vector<DeviceInfo> fetch_devices_wsl() {
|
||||
return parse_lsblk_output(run_capture(
|
||||
"wsl.exe -e sh -c \"lsblk -bno NAME,SIZE,MOUNTPOINT,FSTYPE 2>/dev/null\""));
|
||||
}
|
||||
std::vector<ServiceInfo> fetch_services_wsl() {
|
||||
return parse_systemctl_output(run_capture(
|
||||
"wsl.exe -e sh -c \"systemctl list-units --type=service --all --no-legend --plain --no-pager 2>/dev/null\""));
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace pex
|
||||
Reference in New Issue
Block a user