/* ******************************************************************************* * INCLUDE ******************************************************************************* */ #include "SystemInfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 SystemInfo::getStorageInfo(bool returnSpace) { std::ifstream mounts("/proc/mounts"); std::string line; std::map 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 SystemInfo::getBlockDevices() { std::vector 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 SystemInfo::getFileInfoList(std::string parentPath) { std::vector 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 exculdePathOrFiles) { int i = 10; unsigned long long freeBytes = 0; while (i-- > 0) { freeBytes = getPathFreeSpace(basePath); unsigned long long limitBytes = static_cast(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 buffer; std::unique_ptr 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 Util::SystemInfo::getNetworkInterfaceNames() { std::vector 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 Util::SystemInfo::localAddresses() { std::map 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 Util::SystemInfo::localAddressesList() { std::vector 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 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 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 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 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 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 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; } }