474 lines
16 KiB
C++
474 lines
16 KiB
C++
/**
|
||
* @file reHDD.cpp
|
||
* @brief app logic
|
||
* @author hendrik schutter
|
||
* @date 01.05.2020
|
||
*/
|
||
|
||
#include "../include/reHDD.h"
|
||
|
||
static int fdNewDrivesInformPipe[2];//File descriptor for pipe that informs if new drives are found
|
||
|
||
static int fdShredInformPipe[2];//File descriptor for pipe that informs if a wipe thread signals
|
||
|
||
static std::mutex mxScannDrives;
|
||
|
||
static vector <Drive> vecNewDrives; //store found drives that are updated every 5sec
|
||
|
||
static vector <Drive> vecDrives; //stores all drive data from scann thread
|
||
|
||
TUI *ui;
|
||
|
||
static uint8_t u8SelectedEntry;
|
||
|
||
static fd_set selectSet;
|
||
|
||
//static struct TUI::MenuState menustate;
|
||
|
||
/**
|
||
* \brief app constructor
|
||
* \param void
|
||
* \return instance of App
|
||
*/
|
||
reHDD::reHDD(void)
|
||
{
|
||
cout << "created app" << endl;
|
||
u8SelectedEntry = 0U;
|
||
}
|
||
|
||
/**
|
||
* \brief app logic
|
||
* \param void
|
||
* \return void
|
||
*/
|
||
void reHDD::app_logic(void)
|
||
{
|
||
cout << "app logic" << endl;
|
||
ui = new TUI();
|
||
ui->initTUI();
|
||
|
||
pipe(fdNewDrivesInformPipe);
|
||
pipe(fdShredInformPipe);
|
||
|
||
thread thDevices(ThreadScannDevices); //start thread that scanns for drives
|
||
thread thUserInput(ThreadUserInput); //start thread that reads user input
|
||
|
||
while(1)
|
||
{
|
||
FD_ZERO(&selectSet);
|
||
FD_SET(fdNewDrivesInformPipe[0], &selectSet);
|
||
FD_SET(fdShredInformPipe[0], &selectSet);
|
||
|
||
select(FD_SETSIZE, &selectSet, NULL, NULL, NULL);
|
||
|
||
if( FD_ISSET(fdNewDrivesInformPipe[0], &selectSet))
|
||
{
|
||
char dummy;
|
||
read (fdNewDrivesInformPipe[0],&dummy,1);
|
||
mxScannDrives.lock();
|
||
|
||
filterNewDrives(&vecDrives, &vecNewDrives); //filter and copy to app logic vector
|
||
mxScannDrives.unlock();
|
||
}
|
||
|
||
if (FD_ISSET(fdShredInformPipe[0], &selectSet))
|
||
{
|
||
char dummy;
|
||
read (fdShredInformPipe[0],&dummy,1);
|
||
|
||
checkShredComplete(&vecDrives);
|
||
}
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
} //endless loop
|
||
thDevices.join();
|
||
thUserInput.join();
|
||
}
|
||
|
||
Drive* reHDD::getSelectedDrive()
|
||
{
|
||
if(u8SelectedEntry < vecDrives.size() )
|
||
{
|
||
return &(vecDrives.at(u8SelectedEntry));
|
||
}
|
||
else
|
||
{
|
||
return {};
|
||
}
|
||
}
|
||
|
||
void reHDD::ThreadScannDevices()
|
||
{
|
||
while(true)
|
||
{
|
||
// cout << "Thread" << endl;
|
||
mxScannDrives.lock();
|
||
vecNewDrives.clear();
|
||
searchDrives(&vecNewDrives); //search for new drives and store them in list
|
||
filterIgnoredDrives(&vecNewDrives); //filter out ignored drives
|
||
addSMARTData(&vecNewDrives); //add S.M.A.R.T. Data to the drives
|
||
mxScannDrives.unlock();
|
||
write(fdNewDrivesInformPipe[1], "A",1);
|
||
sleep(5); //sleep 5 sec
|
||
}
|
||
}
|
||
|
||
void reHDD::ThreadUserInput()
|
||
{
|
||
while(true)
|
||
{
|
||
// cout << TUI::readUserInput() << endl;
|
||
switch (TUI::readUserInput())
|
||
{
|
||
case TUI::UserInput::DownKey:
|
||
//cout << "Down" << endl;
|
||
handleArrowKey(TUI::UserInput::DownKey);
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
case TUI::UserInput::UpKey:
|
||
//cout << "Up" << endl;
|
||
handleArrowKey(TUI::UserInput::UpKey);
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
case TUI::UserInput::Undefined:
|
||
//cout << "Undefined" << endl;
|
||
break;
|
||
case TUI::UserInput::Abort:
|
||
//cout << "Abort" << endl;
|
||
handleAbort();
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
case TUI::UserInput::Delete:
|
||
//cout << "Delete" << endl;
|
||
|
||
if (getSelectedDrive() != nullptr)
|
||
{
|
||
if(getSelectedDrive()->state == Drive::NONE)
|
||
{
|
||
getSelectedDrive()->state = Drive::DELETE_SELECTED;
|
||
}
|
||
}
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
case TUI::UserInput::Shred:
|
||
//cout << "Shred" << endl;
|
||
|
||
if (getSelectedDrive() != nullptr)
|
||
{
|
||
if(getSelectedDrive()->state == Drive::NONE)
|
||
{
|
||
getSelectedDrive()->state = Drive::SHRED_SELECTED;
|
||
}
|
||
}
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
case TUI::UserInput::Enter:
|
||
//cout << "Enter" << endl;
|
||
handleEnter();
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
case TUI::UserInput::ESC:
|
||
//cout << "ESC" << endl;
|
||
handleESC();
|
||
ui->updateTUI(&vecDrives, u8SelectedEntry);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void reHDD::ThreadShred()
|
||
{
|
||
if (getSelectedDrive() != nullptr)
|
||
{
|
||
Shred::shredDrive(getSelectedDrive(), &fdShredInformPipe[1]);
|
||
}
|
||
}
|
||
|
||
void reHDD::filterNewDrives(vector <Drive>* pvecOldDrives, vector <Drive>* pvecNewDrives)
|
||
{
|
||
|
||
vector <Drive>::iterator itOld; //Iterator for current (old) drive list
|
||
vector <Drive>::iterator itNew; //Iterator for new drive list that was created from to scann thread
|
||
|
||
for (itOld = pvecOldDrives->begin(); itOld != pvecOldDrives->end(); ++itOld)
|
||
{
|
||
bool bOldDriveIsOffline = true;
|
||
for (itNew = pvecNewDrives->begin(); itNew != pvecNewDrives->end(); ++itNew)
|
||
{
|
||
if(itOld->getSerial() == itNew->getSerial())
|
||
{
|
||
bOldDriveIsOffline = false;
|
||
// copy old drive instance date in new instance
|
||
itNew->state = itOld->state; //copy state
|
||
itNew->setTaskPercentage(itOld->getTaskPercentage()); //copy percentage
|
||
itNew->bWasDeleteted = itOld->bWasDeleteted; //copy finished task delete
|
||
itNew->bWasShredded = itOld->bWasShredded; //copy finished task shred
|
||
}
|
||
}
|
||
|
||
if(bOldDriveIsOffline == true)
|
||
{
|
||
//cout << "offline drive found: " << itOld->getPath() << endl;
|
||
itOld->state = Drive::NONE;
|
||
}
|
||
}
|
||
|
||
pvecOldDrives->clear();
|
||
for (long unsigned int i=0; i<pvecNewDrives->size(); i++)
|
||
{
|
||
pvecOldDrives->push_back((*pvecNewDrives)[i]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* \brief search attached drives on /dev/sd*
|
||
* \param pointer of vector <Drive>* pvecDrives
|
||
* \return void
|
||
*/
|
||
void reHDD::searchDrives(vector <Drive>* pvecDrives)
|
||
{
|
||
// cout << "search drives ..." << endl;
|
||
char * cLine = NULL;
|
||
size_t len = 0;
|
||
|
||
FILE* outputfileHwinfo = popen("hwinfo --short --disk", "r");
|
||
|
||
if (outputfileHwinfo == NULL)
|
||
{
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
while ((getline(&cLine, &len, outputfileHwinfo)) != -1)
|
||
{
|
||
if (string(cLine).find("/dev/sd") != string::npos)
|
||
{
|
||
Drive* tmpDrive = new Drive(string(cLine).substr (2,8));
|
||
tmpDrive->state = Drive::NONE;
|
||
pvecDrives->push_back(*tmpDrive);
|
||
}
|
||
}
|
||
fclose(outputfileHwinfo);
|
||
}
|
||
|
||
/**
|
||
* \brief filter out drives that are listed in "ignoreDrives.conf"
|
||
* \param pointer of vector <Drive>* pvecDrives
|
||
* \return void
|
||
*/
|
||
void reHDD::filterIgnoredDrives(vector <Drive>* pvecDrives)
|
||
{
|
||
string sDelimiter = ":";
|
||
string sIgnoredDrivePath;
|
||
string sIgnoredDriveUUID;
|
||
|
||
vector<tuple<string, string>> vtlIgnoredDevices; //store drives from ingnore file
|
||
|
||
//vector <Drive> vecTmpDrives
|
||
|
||
ifstream input( "ignoreDrives.conf" ); //read ingnore file
|
||
|
||
for(string sLine; getline( input, sLine );)
|
||
{
|
||
if (string(sLine).find("/dev/sd") != string::npos)
|
||
{
|
||
size_t pos = 0;
|
||
string token;
|
||
while ((pos = sLine.find(sDelimiter)) != string::npos)
|
||
{
|
||
token = sLine.substr(0, pos);
|
||
sIgnoredDrivePath = token;
|
||
sLine.erase(0, pos + sDelimiter.length());
|
||
sIgnoredDriveUUID = sLine;
|
||
} //end while
|
||
//cout << "Path: " << sIgnoredDrivePath << std::endl;
|
||
//cout << "UUID: " << sIgnoredDriveUUID << std::endl;
|
||
vtlIgnoredDevices.emplace_back(sIgnoredDrivePath, sIgnoredDriveUUID); //add found path and uuid from ingnore file to vector
|
||
}
|
||
}
|
||
//loop through found entries in ingnore file
|
||
for(auto row : vtlIgnoredDevices)
|
||
{
|
||
vector <Drive>::iterator it;
|
||
for (it = pvecDrives->begin(); it != pvecDrives->end(); ++it)
|
||
{
|
||
string sUUID;
|
||
if (!get<0>(row).compare(it->getPath())) //find same drive based on path
|
||
{
|
||
char * cLine = NULL;
|
||
size_t len = 0;
|
||
string sCMD = "blkid ";
|
||
sCMD.append(it->getPath());
|
||
//cout << "cmd: " << sCMD << endl;
|
||
FILE* outputfileBlkid = popen(sCMD.c_str(), "r"); //get UUID from drive
|
||
if (outputfileBlkid == NULL)
|
||
{
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
while ((getline(&cLine, &len, outputfileBlkid)) != -1) //parse UUID from blkid
|
||
{
|
||
if (string(cLine).find("PTUUID") != string::npos)
|
||
{
|
||
string sBlkidOut = string(cLine);
|
||
sBlkidOut.erase(0, 18);
|
||
sBlkidOut.erase(36, sBlkidOut.length() - 36);
|
||
sUUID = sBlkidOut;
|
||
//cout << "blkid uuid:" << sUUID << endl;
|
||
}
|
||
}
|
||
fclose(outputfileBlkid);
|
||
//cout << "blkid uuid:" << sUUID << endl;
|
||
|
||
if (get<1>(row).compare(sUUID)) //compare uuid from ignore file and uuid from drive
|
||
{
|
||
cout << "[ERROR] different uuid found than in ignore file:" << it->getPath() << endl;
|
||
exit(EXIT_FAILURE); // exit to prevent accidentally shred a system drive
|
||
}
|
||
else
|
||
{
|
||
// same uuid found than in ignore file --> ignore this drive
|
||
it = pvecDrives->erase(it);
|
||
it--;
|
||
//cout << "same uuid found than in ignore file --> ignore this drive:" << it->getPath() << endl;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* \brief print drives with all information
|
||
* \param pointer of vector <Drive>* pvecDrives
|
||
* \return void
|
||
*/
|
||
void reHDD::printDrives(vector <Drive>* pvecDrives)
|
||
{
|
||
cout << "------------DRIVES---------------" << endl;
|
||
vector <Drive>::iterator it;
|
||
for (it = pvecDrives->begin(); it != pvecDrives->end(); ++it)
|
||
{
|
||
cout << " Drive: " << distance(pvecDrives->begin(), it) << endl;
|
||
cout << "Path: " << it->getPath() << endl;
|
||
cout << "ModelFamily: " << it->getModelFamily() << endl;
|
||
cout << "ModelName: " << it->getModelName() << endl;
|
||
cout << "Capacity: " << it->getCapacity() << endl;
|
||
cout << "Serial: " << it->getSerial() << endl;
|
||
cout << "PowerOnHours: " << it->getPowerOnHours() << endl;
|
||
cout << "PowerCycle: " << it->getPowerCycles() << endl;
|
||
cout << "ErrorCount: " << it->getErrorCount() << endl;
|
||
cout << endl;
|
||
}
|
||
cout << "---------------------------------" << endl;
|
||
}
|
||
|
||
/**
|
||
* \brief add S.M.A.R.T data from SMART
|
||
* \param pointer of vector <Drive>* pvecDrives
|
||
* \return void
|
||
*/
|
||
void reHDD::addSMARTData(vector <Drive>* pvecDrives)
|
||
{
|
||
vector <Drive>::iterator it;
|
||
for (it = pvecDrives->begin(); it != pvecDrives->end(); ++it)
|
||
{
|
||
Drive* pTmpDrive = iterator_to_pointer<Drive, std::vector<Drive>::iterator > (it);
|
||
SMART::readSMARTData(pTmpDrive);
|
||
}
|
||
}
|
||
|
||
void reHDD::handleArrowKey(TUI::UserInput userInput)
|
||
{
|
||
int8_t u8EntrySize = (int8_t) vecDrives.size();
|
||
switch (userInput)
|
||
{
|
||
case TUI::UserInput::DownKey:
|
||
u8SelectedEntry++;
|
||
if(u8SelectedEntry >= u8EntrySize)
|
||
{
|
||
u8SelectedEntry = 0;
|
||
}
|
||
break;
|
||
case TUI::UserInput::UpKey:
|
||
if(u8SelectedEntry == 0)
|
||
{
|
||
u8SelectedEntry = (u8EntrySize-1);
|
||
}
|
||
else
|
||
{
|
||
u8SelectedEntry--;
|
||
}
|
||
break;
|
||
default:
|
||
u8SelectedEntry = 0;
|
||
break;
|
||
}
|
||
}
|
||
|
||
void reHDD::handleEnter()
|
||
{
|
||
if (getSelectedDrive() != nullptr)
|
||
{
|
||
if(getSelectedDrive()->state == Drive::TaskState::SHRED_SELECTED)
|
||
{
|
||
getSelectedDrive()->state = Drive::TaskState::SHRED_ACTIVE;
|
||
//task for drive is running --> don´t show more task options
|
||
thread(ThreadShred).detach();
|
||
}
|
||
|
||
if(getSelectedDrive()->state == Drive::TaskState::DELETE_SELECTED)
|
||
{
|
||
getSelectedDrive()->state = Drive::TaskState::DELETE_ACTIVE;
|
||
//task for drive is running --> don´t show more task options
|
||
Delete::deleteDrive(getSelectedDrive()); //blocking, no thread
|
||
getSelectedDrive()->state = Drive::TaskState::NONE; //delete finished
|
||
getSelectedDrive()->bWasDeleteted = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
void reHDD::handleESC()
|
||
{
|
||
if (getSelectedDrive() != nullptr)
|
||
{
|
||
if(getSelectedDrive()->state == Drive::TaskState::SHRED_SELECTED)
|
||
{
|
||
getSelectedDrive()->state = Drive::TaskState::NONE;
|
||
//task for drive is selected --> remove selection
|
||
}
|
||
|
||
if(getSelectedDrive()->state == Drive::TaskState::DELETE_SELECTED)
|
||
{
|
||
getSelectedDrive()->state = Drive::TaskState::NONE;
|
||
//task for drive is selected --> remove selection
|
||
}
|
||
}
|
||
}
|
||
|
||
void reHDD::handleAbort()
|
||
{
|
||
if (getSelectedDrive() != nullptr)
|
||
{
|
||
if(getSelectedDrive()->state == Drive::SHRED_ACTIVE || getSelectedDrive()->state == Drive::DELETE_ACTIVE )
|
||
{
|
||
getSelectedDrive()->state = Drive::NONE;
|
||
//task for drive is running --> remove selection
|
||
}
|
||
}
|
||
}
|
||
|
||
void reHDD::checkShredComplete(vector <Drive>* pvecDrives)
|
||
{
|
||
vector <Drive>::iterator it;
|
||
for (it = pvecDrives->begin(); it != pvecDrives->end(); ++it)
|
||
{
|
||
if(it->getTaskPercentage() == 100 )
|
||
{
|
||
it->bWasShredded = true; //mark this drive as shredded
|
||
it->setTaskPercentage(0); //reset for an other shredding
|
||
it->state = Drive::NONE; //reset for an other task
|
||
}
|
||
}
|
||
}
|
||
|