/** * @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 vecNewDrives; //store found drives that are updated every 5sec static vector 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 * pvecOldDrives, vector * pvecNewDrives) { vector ::iterator itOld; //Iterator for current (old) drive list vector ::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; isize(); i++) { pvecOldDrives->push_back((*pvecNewDrives)[i]); } } /** * \brief search attached drives on /dev/sd* * \param pointer of vector * pvecDrives * \return void */ void reHDD::searchDrives(vector * 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 * pvecDrives * \return void */ void reHDD::filterIgnoredDrives(vector * pvecDrives) { string sDelimiter = ":"; string sIgnoredDrivePath; string sIgnoredDriveUUID; vector> vtlIgnoredDevices; //store drives from ingnore file //vector 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 ::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 * pvecDrives * \return void */ void reHDD::printDrives(vector * pvecDrives) { cout << "------------DRIVES---------------" << endl; vector ::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 * pvecDrives * \return void */ void reHDD::addSMARTData(vector * pvecDrives) { vector ::iterator it; for (it = pvecDrives->begin(); it != pvecDrives->end(); ++it) { Drive* pTmpDrive = iterator_to_pointer::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 * pvecDrives) { vector ::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 } } }