#include "command_registry.hpp" #include "servers.hpp" #include "utils/output.hpp" #include "utils/execute.hpp" #include "utils/directories.hpp" #include "shared_commands.hpp" #include "tableprint.hpp" #include #include #include namespace dropshell { int hostinfo_handler(const CommandContext &ctx); static std::vector hostinfo_name_list = {"hostinfo", "sysinfo"}; void hostinfo_autocomplete(const CommandContext &ctx) { if (ctx.args.size() == 0) { std::vector servers = get_configured_servers(); for (const auto &server : servers) rawout << server.get_server_name() << std::endl; } } struct HostinfoCommandRegister { HostinfoCommandRegister() { CommandRegistry::instance().register_command({ hostinfo_name_list, hostinfo_handler, hostinfo_autocomplete, false, // hidden true, // requires_config true, // requires_install 1, // min_args 1, // max_args "hostinfo SERVER", "Display hardware and system information for a remote server.", R"( Display hardware and system information for a remote server. hostinfo SERVER Show CPU, motherboard, GPU, RAM, disk usage, IP addresses, OS, kernel, uptime, and Docker version. )" }); } } hostinfo_command_register; int hostinfo_handler(const CommandContext &ctx) { std::string server_name = ctx.args[0]; ServerConfig server_env(server_name); if (!server_env.is_valid()) { error << "Server '" << server_name << "' is not valid." << std::endl; return 1; } ASSERT(server_env.get_users().size() > 0, "No users found for server " + server_name); std::string user = server_env.get_users()[0].user; std::string agent_path = remotepath(server_name, user).agent(); std::string script_path = agent_path + "/hostinfo.sh"; std::string output; sCommand cmd(agent_path, script_path, {}); bool success = execute_ssh_command(server_env.get_SSH_INFO(user), cmd, cMode::Silent, &output); if (!success || output.empty()) { error << "Failed to retrieve host information from " << server_name << std::endl; return 1; } nlohmann::json j; try { j = nlohmann::json::parse(output); } catch (const nlohmann::json::parse_error &e) { error << "Failed to parse host info: " << e.what() << std::endl; debug << "Output was: " << output << std::endl; return 1; } auto get_str = [&](const std::string &key) -> std::string { if (j.contains(key) && j[key].is_string()) { std::string val = j[key].get(); return val.empty() ? "-" : val; } return "-"; }; // Format RAM as "X.X / Y.Y GB" std::string ram_display = "-"; { std::string used = get_str("ram_used_mb"); std::string total = get_str("ram_total_mb"); if (used != "-" && total != "-") { try { int used_mb = std::stoi(used); int total_mb = std::stoi(total); char buf[64]; snprintf(buf, sizeof(buf), "%.1f / %.1f GB", used_mb / 1024.0, total_mb / 1024.0); ram_display = buf; } catch (...) {} } } // Format disk root std::string disk_root_display = "-"; { std::string used = get_str("disk_root_used_gb"); std::string total = get_str("disk_root_total_gb"); if (used != "-" && total != "-") disk_root_display = used + " / " + total + " GB"; } // Format disk /tank (only if present) std::string disk_tank_display; { std::string used = get_str("disk_tank_used_gb"); std::string total = get_str("disk_tank_total_gb"); if (used != "-" && total != "-") disk_tank_display = used + " / " + total + " GB"; } // Format GPU list std::string gpu_display = "-"; if (j.contains("gpu") && j["gpu"].is_array() && !j["gpu"].empty()) { gpu_display.clear(); for (size_t i = 0; i < j["gpu"].size(); ++i) { if (i > 0) gpu_display += ", "; gpu_display += j["gpu"][i].get(); } } // Format CPU with cores std::string cpu_display = get_str("cpu"); std::string cores = get_str("cpu_cores"); if (cores != "-") cpu_display += " (" + cores + " cores)"; // Format IP addresses on one line std::string ip_display = get_str("ip_local"); { std::string ts = get_str("ip_tailscale"); std::string pub = get_str("ip_public"); if (ts != "-") ip_display += " ts:" + ts; if (pub != "-") ip_display += " pub:" + pub; } // Display tableprint tp("Host Info: " + server_name, true); tp.add_row({"Property", "Value"}); tp.add_row({"Hostname", get_str("hostname")}); tp.add_row({"OS", get_str("os")}); tp.add_row({"Kernel", get_str("kernel")}); tp.add_row({"Uptime", get_str("uptime")}); tp.add_row({"CPU", cpu_display}); tp.add_row({"Motherboard", get_str("motherboard")}); tp.add_row({"GPU", gpu_display}); tp.add_row({"RAM", ram_display}); tp.add_row({"Disk /", disk_root_display}); if (!disk_tank_display.empty()) tp.add_row({"Disk /tank", disk_tank_display}); tp.add_row({"IP Addresses", ip_display}); tp.add_row({"Docker", get_str("docker_version")}); tp.print(); return 0; } } // namespace dropshell