p26001-store-acs-soc-fw-app/util/SystemInfo.cpp
2026-09-28 21:33:04 +08:00

829 lines
26 KiB
C++

/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SystemInfo.h"
#include <Poco/DirectoryIterator.h>
#include <Poco/Path.h>
#include <Poco/Pipe.h>
#include <Poco/PipeStream.h>
#include <Poco/Process.h>
#include <Poco/StreamCopier.h>
#include <dirent.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <algorithm>
#include <atomic>
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <experimental/filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include "Application.h"
#include "Logger.h"
#include "Poco/Thread.h"
#include "linux-system-usage.hpp"
using namespace get_system_usage_linux;
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
unsigned SystemInfo::cpuUsage()
{
CPU_stats t1 = read_cpu_data();
Poco::Thread::sleep(100);
CPU_stats t2 = read_cpu_data();
return (unsigned)(100.0f * get_cpu_usage(t1, t2));
}
SystemInfo::MemoryInfo SystemInfo::readMemoryInfo()
{
auto memory_data = read_memory_data();
MemoryInfo info;
info.total = memory_data.total_memory;
info.free = memory_data.free_memory;
info.usage = 100.0f * memory_data.get_memory_usage();
info.available = memory_data.available_memory;
return info;
}
std::map<std::string, Util::SystemInfo::StorageInfo> SystemInfo::getStorageInfo(bool returnSpace)
{
std::ifstream mounts("/proc/mounts");
std::string line;
std::map<std::string, StorageInfo> storageMap;
if (!mounts.is_open())
{
logger().error("Failed to open /proc/mounts");
return storageMap;
}
while (std::getline(mounts, line))
{
std::istringstream iss(line);
std::string device, path;
iss >> device >> path;
std::string deviceType = device.find("/dev/sd") == 0 ? "disk" : "";
deviceType = !deviceType.empty() ? deviceType : (device.find("/dev/mmcblk") == 0 ? "mmcblk" : "");
deviceType = !deviceType.empty() ? deviceType : (path.find("/mnt/storages/nfs") == 0 ? "nfs" : "");
deviceType = !deviceType.empty() ? deviceType : (path.find("/mnt/storages/cifs") == 0 ? "cifs" : "");
if (!deviceType.empty())
{
try
{
if (!mountPointEnable(path))
{
logger().error("%s Mount point is not available", path);
continue;
}
StorageInfo storageInfo;
storageInfo.device = device;
storageInfo.path = path;
storageInfo.status = 1;
storageInfo.type = deviceType;
if (returnSpace)
{
struct statvfs stat;
int ret_code = statvfs(path.c_str(), &stat);
if (ret_code != 0)
{
int err = errno;
logger().error("Failed to get disk usage information for: %s", strerror(err));
}
else
{
unsigned long long total = stat.f_blocks * stat.f_frsize;
unsigned long long free = stat.f_bfree * stat.f_frsize;
unsigned long long available = stat.f_bavail * stat.f_frsize;
unsigned long long used = total - free;
storageInfo.total = total;
storageInfo.used = used;
storageInfo.available = available;
storageInfo.free = free;
}
}
storageMap[device] = storageInfo;
}
catch (const std::exception& e)
{
logger().error("SystemInfo::getStorageInfo erorr: %s", std::string(e.what()));
}
}
}
auto blocks = getBlockDevices();
for (const auto& block : blocks)
{
if (block.empty() || block.back() != '1')
{
continue;
}
auto it = storageMap.find(block);
if (it == storageMap.end())
{
StorageInfo storageInfo;
storageInfo.status = 2;
storageInfo.device = block;
storageMap[block] = storageInfo;
}
}
return storageMap;
}
bool isBlockDevice(const std::string& path)
{
struct stat statbuf;
if (stat(path.c_str(), &statbuf) != 0)
{
return false;
}
return S_ISBLK(statbuf.st_mode);
}
std::vector<std::string> SystemInfo::getBlockDevices()
{
std::vector<std::string> blockDevices;
std::ifstream file("/proc/partitions");
std::string line;
while (std::getline(file, line))
{
std::istringstream iss(line);
std::string major, minor, blocks, name;
if (!(iss >> major >> minor >> blocks >> name))
{
continue;
}
std::string devicePath = "/dev/" + name;
if (devicePath.find("/dev/sd") == 0 && isBlockDevice(devicePath))
{
blockDevices.push_back(devicePath);
}
}
return blockDevices;
}
std::vector<Util::SystemInfo::FileInfo> SystemInfo::getFileInfoList(std::string parentPath)
{
std::vector<Util::SystemInfo::FileInfo> fileInfoList;
Poco::Path path(parentPath);
if (!path.isDirectory())
{
logger().error("Error: %s is not a directory.", parentPath);
return fileInfoList;
}
try
{
Poco::DirectoryIterator it(parentPath);
Poco::DirectoryIterator end;
while (it != end)
{
const Poco::Path& p = it.path();
Util::SystemInfo::FileInfo fileInfo;
fileInfo.name = p.getFileName();
fileInfo.parentPath = parentPath;
fileInfo.type = it->isDirectory() ? 2 : (it->isFile() ? 1 : 0);
if (it->isDirectory())
{
}
else if (it->isFile())
{
Poco::File file(p.toString());
fileInfo.size = file.getSize();
fileInfo.lastModifyTime = file.getLastModified().epochTime();
}
++it;
fileInfoList.push_back(fileInfo);
}
}
catch (Poco::Exception& e)
{
logger().error("An error occurred: %s", e.displayText());
}
return fileInfoList;
}
unsigned long long Util::SystemInfo::getPathFreeSpace(const std::string& path)
{
struct statvfs stat;
if (statvfs(path.c_str(), &stat) != 0)
{
logger().error("Failed to get filesystem stats for %s", path);
return 0;
}
unsigned long long free = stat.f_bfree * stat.f_frsize;
return free;
}
unsigned long long Util::SystemInfo::checkSizeAndDelEarliestFile(const std::string& basePath,
const unsigned int limitMB,
const std::string deletePath,
const std::string& deleteExtName,
const std::set<std::string> exculdePathOrFiles)
{
int i = 10;
unsigned long long freeBytes = 0;
while (i-- > 0)
{
freeBytes = getPathFreeSpace(basePath);
unsigned long long limitBytes = static_cast<unsigned long long>(limitMB) * 1024 * 1024;
logger().information(" freeBytes=%s, limitBytes=%s", std::to_string(freeBytes), std::to_string(limitBytes));
if (freeBytes < limitBytes)
{
logger().information(" Space is less than=%s. Deleting earliest file...", std::to_string(limitMB));
try
{
std::string findCmd = "find " + deletePath + " -type f -name \"*" + deleteExtName +
"\" -exec stat -c '%mY %n' {} + | sort -n | head -n 1 | cut -d' ' -f2-";
logger().information("Execute command: %s", findCmd);
std::string oldestFile;
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(findCmd.c_str(), "r"), pclose);
if (!pipe)
{
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
{
oldestFile += buffer.data();
}
if (!oldestFile.empty() && oldestFile[oldestFile.size() - 1] == '\n')
{
oldestFile.erase(oldestFile.size() - 1);
}
if (!oldestFile.empty())
{
bool isExecuteDelete = true;
for (std::string name : exculdePathOrFiles)
{
if (oldestFile.find(name) == 0)
{
isExecuteDelete = false;
break;
}
}
if (isExecuteDelete)
{
try
{
Poco::File file(oldestFile);
if (file.exists())
{
file.remove();
logger().information("File deleted successfully: %s", oldestFile);
}
else
{
logger().information("File does not exist: %s", oldestFile);
}
}
catch (const Poco::Exception& ex)
{
logger().information("Failed to delete file: : %s, error:", oldestFile, ex.displayText());
}
}
else
{
logger().information("deleted old file -> exculde path or file:%s", oldestFile);
}
}
else
{
logger().information("deleted old file -> No .mp4 files found");
}
}
catch (const std::exception& e)
{
logger().error("deleted old file -> Error: ", e.what());
}
}
else
{
break;
}
}
if (freeBytes <= 0)
{
freeBytes = getPathFreeSpace(basePath);
}
return freeBytes;
}
bool Util::SystemInfo::mountPointEnable(const std::string& localPath)
{
return Util::SystemInfo::executeCommand("ls " + localPath, 5);
}
bool Util::SystemInfo::mountCifs(std::string ip, std::string remotePath, std::string localPath, std::string username,
std::string password, std::string vers, std::string charset)
{
Util::SystemInfo::umount(localPath);
Util::SystemInfo::executeCommand("rm -rf " + localPath);
Util::SystemInfo::executeCommand("mkdir -p " + localPath);
std::string _charset = charset.empty() ? "" : ",iocharset=" + charset;
std::string mountCmd = Poco::format("mount -t cifs -o username=%s,password=%s,vers=%s%s //%s%s %s", username,
password, vers, _charset, ip, remotePath, localPath);
return Util::SystemInfo::executeCommand(mountCmd);
}
bool Util::SystemInfo::mountNfs(std::string ip, std::string remotePath, std::string localPath, std::string vers)
{
Util::SystemInfo::umount(localPath);
Util::SystemInfo::executeCommand("rm -rf " + localPath);
Util::SystemInfo::executeCommand("mkdir -p " + localPath);
std::string mountCmd = Poco::format("mount -t nfs -o nolock,vers=%s %s:%s %s", vers, ip, remotePath, localPath);
return Util::SystemInfo::executeCommand(mountCmd, 5);
}
bool Util::SystemInfo::umount(std::string localPath)
{
return Util::SystemInfo::executeCommand("umount -f " + localPath);
}
int Util::SystemInfo::getProcessPID(const std::string& processName)
{
try
{
DIR* dir = opendir("/proc");
if (!dir)
{
return 0;
}
std::string targetName = processName;
size_t lastSlash = targetName.find_last_of('/');
if (lastSlash != std::string::npos)
{
targetName = targetName.substr(lastSlash + 1);
}
struct dirent* entry;
int pid = 0;
while ((entry = readdir(dir)))
{
if (entry->d_type != DT_DIR || !isdigit(entry->d_name[0]))
{
continue;
}
std::string commPath = std::string("/proc/") + entry->d_name + "/comm";
std::ifstream commFile(commPath);
if (commFile)
{
std::string commName;
if (std::getline(commFile, commName))
{
commName.erase(std::remove(commName.begin(), commName.end(), '\n'), commName.end());
if (commName == targetName)
{
pid = std::stoi(entry->d_name);
break;
}
continue;
}
}
std::string cmdlinePath = std::string("/proc/") + entry->d_name + "/cmdline";
std::ifstream cmdlineFile(cmdlinePath);
if (cmdlineFile)
{
std::string cmdline;
if (std::getline(cmdlineFile, cmdline))
{
size_t nullPos = cmdline.find('\0');
if (nullPos != std::string::npos)
{
cmdline.resize(nullPos);
}
lastSlash = cmdline.find_last_of('/');
std::string exeName = (lastSlash != std::string::npos) ? cmdline.substr(lastSlash + 1) : cmdline;
if (exeName == targetName)
{
pid = std::stoi(entry->d_name);
break;
}
}
}
}
closedir(dir);
return pid;
}
catch (const std::exception& e)
{
logger().error("getProcessPID Error: %s", std::string(e.what()));
}
return 0;
}
std::vector<std::string> Util::SystemInfo::getNetworkInterfaceNames()
{
std::vector<std::string> interfaceNames;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list(false, false);
for (const auto& interface : interfaces)
{
if (!interface.isLoopback()
&& !interface.name().empty())
{
interfaceNames.push_back(interface.name());
}
}
}
catch (const Poco::Exception& e)
{
logger().error("getNetworkInterfaceNames Error: %s", std::string(e.what()));
}
return interfaceNames;
}
Util::SystemInfo::NetworkInterfaceInfo Util::SystemInfo::getNetWorkInterfaceInfo(const std::string& interfaceName,
bool returnGateway)
{
Util::SystemInfo::NetworkInterfaceInfo info;
try
{
auto interface = Poco::Net::NetworkInterface::forName(interfaceName);
if (interface.name() == interfaceName)
{
info.name = interface.name();
info.displayName = interface.displayName();
info.ipAddress = interface.address().toString();
info.subnetMask = interface.subnetMask().toString();
info.broadcastAddress = interface.broadcastAddress().toString();
auto macAddress = interface.macAddress();
info.macAddress = Poco::format("%02X:%02X:%02X:%02X:%02X:%02X", (unsigned)macAddress[0],
(unsigned)macAddress[1], (unsigned)macAddress[2], (unsigned)macAddress[3],
(unsigned)macAddress[4], (unsigned)macAddress[5]);
info.isUp = interface.isUp();
if (returnGateway)
{
info.gateway = gatewayAddress(interfaceName).toString();
}
info.isInitialized = true;
}
}
catch (const Poco::Exception& e)
{
logger().error("Error getNetWorkInterfaceInfo info: %s", std::string(e.what()));
}
return info;
}
Poco::Net::IPAddress Util::SystemInfo::localAddress(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).address();
}
catch (...)
{
return Poco::Net::IPAddress("0.0.0.0");
}
}
std::map<std::string, Poco::Net::IPAddress> Util::SystemInfo::localAddresses()
{
std::map<std::string, Poco::Net::IPAddress> ipMap;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list();
for (const auto& interface : interfaces)
{
if (interface.isUp() && !interface.isLoopback() && !interface.address().isWildcard() &&
interface.address().isUnicast() && interface.address().family() == Poco::Net::AddressFamily::IPv4)
{
ipMap[interface.name()] = interface.address();
}
}
}
catch (const Poco::Exception& e)
{
logger().error("localAddresses Error: %s", std::string(e.what()));
}
return ipMap;
}
std::vector<std::string> Util::SystemInfo::localAddressesList()
{
std::vector<std::string> ipList;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list();
for (const auto& interface : interfaces)
{
if (interface.isUp() && !interface.isLoopback())
{
Poco::Net::NetworkInterface::AddressList addresses = interface.addressList();
for (const auto& addressInfo : addresses)
{
const Poco::Net::IPAddress& ip = addressInfo.get<0>();
if (!ip.isWildcard() && ip.isUnicast() && ip.family() == Poco::Net::AddressFamily::IPv4)
{
ipList.push_back(ip.toString());
}
}
}
}
}
catch (const Poco::Exception& e)
{
logger().error("localAddresses Error: %s", std::string(e.what()));
}
return ipList;
}
std::string Util::SystemInfo::localAddressOfAvailable()
{
std::map<std::string, Poco::Net::IPAddress> ipMap = localAddresses();
std::string hostIp = "0.0.0.0";
if (!ipMap.empty())
{
hostIp = ipMap.begin()->second.toString();
}
return hostIp;
}
std::string Util::SystemInfo::getFirstWirelessInterfaceName()
{
auto interfaces = getNetworkInterfaceNames();
for (auto& name : interfaces)
{
std::vector<std::string> wirelessPatterns = {"wlan", "wi-fi", "wireless", "wifi", "ath", "ra", "wlp", "wlan"};
for (const auto& pattern : wirelessPatterns)
{
if (Poco::toLower(name).find(pattern) != std::string::npos)
{
return name;
}
}
}
return "";
}
Poco::Net::IPAddress Util::SystemInfo::subnetMask(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).subnetMask();
}
catch (...)
{
return Poco::Net::IPAddress("0.0.0.0");
}
}
Poco::Net::NetworkInterface::MACAddress Util::SystemInfo::macAddress(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).macAddress();
}
catch (...)
{
return Poco::Net::NetworkInterface::MACAddress();
}
}
Poco::Net::NetworkInterface::MACAddress Util::SystemInfo::getFirstMacAddress()
{
auto interfaces = getNetworkInterfaceNames();
try
{
for (const auto& name : interfaces)
{
return Poco::Net::NetworkInterface::forName(name).macAddress();
}
return Poco::Net::NetworkInterface::MACAddress();
}
catch (...)
{
return Poco::Net::NetworkInterface::MACAddress();
}
}
bool Util::SystemInfo::netInterfaceIsExist(const std::string& interfaceName)
{
if (interfaceName.empty())
{
return false;
}
std::vector<std::string> interfaceNames = Util::SystemInfo::getNetworkInterfaceNames();
for (const auto& name : interfaceNames)
{
if (interfaceName == name)
{
return true;
}
}
return false;
}
bool Util::SystemInfo::netInterfaceIsAvailable(const std::string& interfaceName)
{
std::vector<std::string> interfaceNames;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list(false, false);
for (const auto& interface : interfaces)
{
if (!interface.isLoopback() && interface.isUp() && !interface.name().empty())
{
if (interfaceName == interface.name())
{
return true;
}
}
}
}
catch (const Poco::Exception& e)
{
logger().error("netInterfaceIsAvailable Error: %s", std::string(e.what()));
}
return false;
}
Poco::Net::IPAddress Util::SystemInfo::gatewayAddress(const std::string& interfaceName)
{
#define BUFLEN 20480
Poco::Net::IPAddress invalid("0.0.0.0");
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0)
{
logger().error("NETLINK socket error");
return invalid;
}
char req[BUFLEN] = {0};
struct nlmsghdr* nh = (struct nlmsghdr*)req;
nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nh->nlmsg_type = RTM_GETROUTE;
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nh);
rtm->rtm_family = AF_INET;
rtm->rtm_table = RT_TABLE_MAIN;
struct sockaddr_nl nladdr = {0};
nladdr.nl_family = AF_NETLINK;
if (bind(fd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0)
{
logger().error("NETLINK bind error");
close(fd);
return invalid;
}
if (send(fd, req, sizeof(req), 0) < 0)
{
logger().error("NETLINK send error");
close(fd);
return invalid;
}
char buf[BUFLEN] = {0};
struct iovec iov = {buf, sizeof(buf)};
struct sockaddr_nl sa;
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, NULL, 0, 0};
int len = recvmsg(fd, &msg, 0);
if (len < 0)
{
logger().error("NETLINK recvmsg error");
close(fd);
return invalid;
}
struct nlmsghdr* nhp;
for (nhp = (struct nlmsghdr*)buf; NLMSG_OK(nhp, len); nhp = NLMSG_NEXT(nhp, len))
{
struct rtmsg* rtp = (struct rtmsg*)NLMSG_DATA(nhp);
if (rtp->rtm_table != RT_TABLE_MAIN || rtp->rtm_family != AF_INET)
{
continue;
}
struct rtattr* attr = (struct rtattr*)RTM_RTA(rtp);
int attrlen = RTM_PAYLOAD(nhp);
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen))
{
if (attr->rta_type != RTA_GATEWAY)
{
continue;
}
uint32_t gateway = *(uint32_t*)RTA_DATA(attr);
char gwstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &gateway, gwstr, sizeof(gwstr));
close(fd);
return Poco::Net::IPAddress(std::string(gwstr));
}
}
close(fd);
return invalid;
}
bool Util::SystemInfo::executeCommand(const std::string& command, int timeout_sec, std::string& output)
{
if (timeout_sec <= 0)
{
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
if (!pipe)
{
logger().error("Failed to execute command: %s", command);
return false;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
{
output += buffer;
}
int status = pclose(pipe.release());
int ret = WEXITSTATUS(status);
if (ret != 0)
{
logger().error("Command failed: %s, exit code: %d, error: %s", command, ret, output);
return false;
}
logger().information("Command succeeded: %s, exit code: %d", command, ret);
return true;
}
output.clear();
std::string timeoutCommand = "timeout -s KILL " + std::to_string(timeout_sec) + " " + command;
timeoutCommand += " 2>/dev/null || timeout -s KILL -t " + std::to_string(timeout_sec) + " " + command;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(timeoutCommand.c_str(), "r"), pclose);
if (!pipe)
{
logger().error("2Failed to execute command: %s", timeoutCommand);
return false;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
{
output += buffer;
}
int status = pclose(pipe.release());
int ret = WEXITSTATUS(status);
if (ret != 0)
{
if (ret == 124 || output.find("time") != std::string::npos)
{
logger().error("2Command timeout: %s", timeoutCommand);
}
else
{
logger().error("2Command failed: %s, exit code: %d, error: %s", timeoutCommand, ret, output);
}
return false;
}
logger().information("2Command succeeded: %s, exit code: %d", timeoutCommand, ret);
return true;
}
}