439 lines
11 KiB
C++
439 lines
11 KiB
C++
/**
|
|
* @file smart.cpp
|
|
* @brief read S.M.A.R.T values
|
|
* @author hendrik schutter
|
|
* @date 01.05.2020
|
|
*/
|
|
|
|
#include "../include/reHDD.h"
|
|
using namespace std;
|
|
|
|
/**
|
|
* \brief get and set S.M.A.R.T. values in Drive
|
|
* \param pointer of Drive instance
|
|
* \return void
|
|
*/
|
|
void SMART::readSMARTData(Drive *drive)
|
|
{
|
|
string modelFamily;
|
|
string modelName;
|
|
string serial;
|
|
uint64_t capacity = 0U;
|
|
uint32_t errorCount = 0U;
|
|
uint32_t powerOnHours = 0U;
|
|
uint32_t powerCycles = 0U;
|
|
uint32_t temperature = 0U;
|
|
uint32_t reallocatedSectors = 0U;
|
|
uint32_t pendingSectors = 0U;
|
|
uint32_t uncorrectableSectors = 0U;
|
|
|
|
modelFamily.clear();
|
|
modelName.clear();
|
|
serial.clear();
|
|
|
|
string sSmartctlCommands[] = {" --json -a ", " --json -d sntjmicron -a ", " --json -d sntasmedia -a ", " --json -d sntrealtek -a ", " --json -d sat -a "};
|
|
|
|
for (string sSmartctlCommand : sSmartctlCommands)
|
|
{
|
|
string sCMD = ("smartctl");
|
|
sCMD.append(sSmartctlCommand);
|
|
sCMD.append(drive->getPath());
|
|
const char *cpComand = sCMD.c_str();
|
|
|
|
// Logger::logThis()->info(cpComand);
|
|
|
|
FILE *outputfileSmart = popen(cpComand, "r");
|
|
size_t len = 0U; // length of found line
|
|
char *cLine = NULL; // found line
|
|
uint8_t status = 255U;
|
|
|
|
while ((getline(&cLine, &len, outputfileSmart)) != -1)
|
|
{
|
|
string sLine = string(cLine);
|
|
|
|
SMART::parseExitStatus(sLine, status);
|
|
SMART::parseModelFamily(sLine, modelFamily);
|
|
SMART::parseModelName(sLine, modelName);
|
|
SMART::parseSerial(sLine, serial);
|
|
SMART::parseCapacity(sLine, capacity);
|
|
SMART::parseErrorCount(sLine, errorCount);
|
|
SMART::parsePowerOnHours(sLine, powerOnHours);
|
|
SMART::parsePowerCycles(sLine, powerCycles);
|
|
SMART::parseTemperature(sLine, temperature);
|
|
SMART::parseReallocatedSectors(sLine, reallocatedSectors);
|
|
SMART::parsePendingSectors(sLine, pendingSectors);
|
|
SMART::parseUncorrectableSectors(sLine, uncorrectableSectors);
|
|
}
|
|
|
|
free(cLine);
|
|
pclose(outputfileSmart);
|
|
|
|
if (status == 0U)
|
|
{
|
|
// Found S.M.A.R.T. data with this command
|
|
// Logger::logThis()->info("Found S.M.A.R.T. data with this command");
|
|
break;
|
|
}
|
|
}
|
|
|
|
drive->setDriveSMARTData(modelFamily, modelName, serial, capacity, errorCount, powerOnHours, powerCycles, temperature, reallocatedSectors, pendingSectors, uncorrectableSectors); // write data in drive
|
|
}
|
|
|
|
/**
|
|
* \brief parse ExitStatus
|
|
* \param string output line of smartctl
|
|
* \param uint8_t parsed status
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseExitStatus(string sLine, uint8_t &status)
|
|
{
|
|
string search("\"exit_status\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find(": ") + 1U);
|
|
status = stol(sLine);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse ModelFamily
|
|
* \param string output line of smartctl
|
|
* \param string parsed model family
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseModelFamily(string sLine, string &modelFamily)
|
|
{
|
|
string search("\"model_family\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find(": ") + 3U);
|
|
if (sLine.length() >= 3U)
|
|
{
|
|
sLine.erase(sLine.length() - 3U, 3U);
|
|
}
|
|
modelFamily = sLine;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse ModelName
|
|
* \param string output line of smartctl
|
|
* \param string parsed model name
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseModelName(string sLine, string &modelName)
|
|
{
|
|
string search("\"model_name\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find(": ") + 3U);
|
|
if (sLine.length() >= 3U)
|
|
{
|
|
sLine.erase(sLine.length() - 3U, 3U);
|
|
}
|
|
modelName = sLine;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse Serial
|
|
* \param string output line of smartctl
|
|
* \param string parsed serial
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseSerial(string sLine, string &serial)
|
|
{
|
|
string search("\"serial_number\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0, sLine.find(": ") + 3);
|
|
if (sLine.length() >= 3U)
|
|
{
|
|
sLine.erase(sLine.length() - 3U, 3U);
|
|
}
|
|
serial = sLine;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse Capacity
|
|
* \param string output line of smartctl
|
|
* \param string parsed capacity
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseCapacity(string sLine, uint64_t &capacity)
|
|
{
|
|
string search("\"bytes\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0, sLine.find(": ") + 2);
|
|
if (sLine.length() >= 1U)
|
|
{
|
|
sLine.erase(sLine.length() - 1U, 1U);
|
|
}
|
|
capacity = stol(sLine);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse ErrorCount
|
|
* \param string output line of smartctl
|
|
* \param uint32_t parsed error count
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseErrorCount(string sLine, uint32_t &errorCount)
|
|
{
|
|
string search("\"error_count_total\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find(": ") + 2U);
|
|
if (sLine.length() >= 2U)
|
|
{
|
|
sLine.erase(sLine.length() - 2U, 2U);
|
|
}
|
|
errorCount = stol(sLine);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse PowerOnHours
|
|
* \param string output line of smartctl\
|
|
* \param uint32_t parsed power on hours
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parsePowerOnHours(string sLine, uint32_t &powerOnHours)
|
|
{
|
|
string search("\"hours\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find(": ") + 2U);
|
|
if (sLine.length() >= 1U)
|
|
{
|
|
sLine.erase(sLine.length() - 1U, 1U);
|
|
}
|
|
powerOnHours = stol(sLine);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse PowerCycle
|
|
* \param string output line of smartctl
|
|
* \param uint32_t parsed power cycles
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parsePowerCycles(string sLine, uint32_t &powerCycles)
|
|
{
|
|
string search("\"power_cycle_count\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0, sLine.find(": ") + 2);
|
|
if (sLine.length() >= 2U)
|
|
{
|
|
sLine.erase(sLine.length() - 2U, 2U);
|
|
}
|
|
powerCycles = stol(sLine);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse temperature
|
|
* \param string output line of smartctl
|
|
* \param uint32_t parsed temperature
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseTemperature(string sLine, uint32_t &temperature)
|
|
{
|
|
string search("\"current\": ");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find(": ") + 2U);
|
|
if (sLine.length() >= 1U)
|
|
{
|
|
sLine.erase(sLine.length() - 1U, 2U);
|
|
}
|
|
if (sLine == "{")
|
|
{
|
|
temperature = 0U; // this drive doesn't support temperature
|
|
}
|
|
else
|
|
{
|
|
temperature = stol(sLine);
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief parse Reallocated Sectors Count (SMART ID 0x05)
|
|
* \param string output line of smartctl
|
|
* \param uint32_t parsed reallocated sectors count
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseReallocatedSectors(string sLine, uint32_t &reallocatedSectors)
|
|
{
|
|
string search("\"id\": 5,");
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
// Found attribute ID 5 (Reallocated_Sector_Ct)
|
|
// Now we need to find the raw value in the next lines
|
|
// smartctl JSON format: "raw": { "value": <number>, ... }
|
|
return true; // Mark that we found the attribute
|
|
}
|
|
|
|
// Look for the raw value if we're in the right attribute
|
|
search = "\"value\":";
|
|
found = sLine.find(search);
|
|
if (found != string::npos && sLine.find("\"raw\":") != string::npos)
|
|
{
|
|
// Extract value after "value":
|
|
sLine.erase(0U, sLine.find("\"value\":") + 8U);
|
|
// Remove trailing characters
|
|
size_t comma = sLine.find(",");
|
|
if (comma != string::npos)
|
|
{
|
|
sLine = sLine.substr(0, comma);
|
|
}
|
|
// Remove whitespace
|
|
sLine.erase(remove(sLine.begin(), sLine.end(), ' '), sLine.end());
|
|
|
|
if (!sLine.empty() && sLine.find_first_not_of("0123456789") == string::npos)
|
|
{
|
|
reallocatedSectors = stoul(sLine);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \brief parse Current Pending Sector Count (SMART ID 0xC5)
|
|
* \param string output line of smartctl
|
|
* \param uint32_t parsed pending sectors count
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parsePendingSectors(string sLine, uint32_t &pendingSectors)
|
|
{
|
|
string search("\"id\": 197,"); // 0xC5 = 197 decimal
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
return true; // Mark that we found the attribute
|
|
}
|
|
|
|
// Look for the raw value
|
|
search = "\"value\":";
|
|
found = sLine.find(search);
|
|
if (found != string::npos && sLine.find("\"raw\":") != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find("\"value\":") + 8U);
|
|
size_t comma = sLine.find(",");
|
|
if (comma != string::npos)
|
|
{
|
|
sLine = sLine.substr(0, comma);
|
|
}
|
|
sLine.erase(remove(sLine.begin(), sLine.end(), ' '), sLine.end());
|
|
|
|
if (!sLine.empty() && sLine.find_first_not_of("0123456789") == string::npos)
|
|
{
|
|
pendingSectors = stoul(sLine);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \brief parse Offline Uncorrectable Sectors (SMART ID 0xC6)
|
|
* \param string output line of smartctl
|
|
* \param uint32_t parsed uncorrectable sectors count
|
|
* \return bool if parsing was possible
|
|
*/
|
|
bool SMART::parseUncorrectableSectors(string sLine, uint32_t &uncorrectableSectors)
|
|
{
|
|
string search("\"id\": 198,"); // 0xC6 = 198 decimal
|
|
size_t found = sLine.find(search);
|
|
if (found != string::npos)
|
|
{
|
|
return true; // Mark that we found the attribute
|
|
}
|
|
|
|
// Look for the raw value
|
|
search = "\"value\":";
|
|
found = sLine.find(search);
|
|
if (found != string::npos && sLine.find("\"raw\":") != string::npos)
|
|
{
|
|
sLine.erase(0U, sLine.find("\"value\":") + 8U);
|
|
size_t comma = sLine.find(",");
|
|
if (comma != string::npos)
|
|
{
|
|
sLine = sLine.substr(0, comma);
|
|
}
|
|
sLine.erase(remove(sLine.begin(), sLine.end(), ' '), sLine.end());
|
|
|
|
if (!sLine.empty() && sLine.find_first_not_of("0123456789") == string::npos)
|
|
{
|
|
uncorrectableSectors = stoul(sLine);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|