diff --git a/.gitignore b/.gitignore index 9862cb4..fef63b0 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,10 @@ reHDD -reHDD.log +*.log +*.ods +*.txt +.vscode/ ignoreDrives.conf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fec9e60 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tfnoisegen"] + path = tfnoisegen + url = https://git.mosad.xyz/localhorst/tfnoisegen.git diff --git a/README.md b/README.md index d6f37f0..5751534 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ ## Debian Build Notes * `apt-get install ncurses-dev git make g++` +* `git submodule init` +* `git submodule update` * `make release` ## Create Standalone with Debian 11 @@ -22,13 +24,18 @@ Instructions how to create a standalone machine that boots directly to reHDD. Th * Upload reHDD log every 12h if wanted ### Software requirements -* `apt-get install hwinfo smartmontools curl` +* `apt-get install hwinfo smartmontools curl htop sudo` ### Installation clone this repo into /root/ +``` +git submodule init +git submodule update +``` + `cd /root/reHDD/` `make release` diff --git a/ignoreDrives.conf b/ignoreDrives.conf index 06e17c4..b9453cc 100644 --- a/ignoreDrives.conf +++ b/ignoreDrives.conf @@ -1,2 +1,3 @@ 4673974d 2cb3dea4 +8ffbc421 diff --git a/include/drive.h b/include/drive.h index 5427eeb..e61bc8f 100644 --- a/include/drive.h +++ b/include/drive.h @@ -22,6 +22,14 @@ public: FROZEN } state; + struct + { + time_t u32ShredTimeDelta; + std::chrono::time_point chronoShredTimestamp; + unsigned long ulWrittenBytes; + unsigned long ulSpeedMetricBytesWritten; + } sShredSpeed; + bool bWasShredded = false; bool bWasDeleteted = false; bool bIsOffline = false; @@ -29,18 +37,23 @@ public: private: string sPath; - string sModelFamily; - string sModelName; - string sSerial; - uint64_t u64Capacity = 0U; //in byte - uint32_t u32ErrorCount = 0U; - uint32_t u32PowerOnHours = 0U; //in hours - uint32_t u32PowerCycles = 0U; time_t u32Timestamp = 0U; //unix timestamp for detecting a frozen drive double d32TaskPercentage = 0U; //in percent for Shred (1 to 100) time_t u32TimestampTaskStart = 0U; //unix timestamp for duration of an action time_t u32TaskDuration = 0U; //time needed to complete the task + struct + { + string sModelFamily; + string sModelName; + string sSerial; + uint64_t u64Capacity = 0U; //in byte + uint32_t u32ErrorCount = 0U; + uint32_t u32PowerOnHours = 0U; //in hours + uint32_t u32PowerCycles = 0U; + uint32_t u32Temperature = 0U; //in Fahrenheit, just kidding: degree Celsius + } sSmartData; + private: void setTimestamp(); @@ -60,6 +73,7 @@ public: uint32_t getErrorCount(void); uint32_t getPowerOnHours(void); //in hours uint32_t getPowerCycles(void); + uint32_t getTemperature(void); //in Fahrenheit, just kidding: degree Celsius void checkFrozenDrive(void); void setDriveSMARTData( string modelFamily, @@ -68,12 +82,14 @@ public: uint64_t capacity, uint32_t errorCount, uint32_t powerOnHours, - uint32_t powerCycles); + uint32_t powerCycles, + uint32_t temperature); string sCapacityToText(); string sErrorCountToText(); string sPowerOnHoursToText(); string sPowerCyclesToText(); + string sTemperatureToText(); void setTaskPercentage(double d32TaskPercentage); double getTaskPercentage(void); diff --git a/include/reHDD.h b/include/reHDD.h index 08d2ac4..e3d3856 100644 --- a/include/reHDD.h +++ b/include/reHDD.h @@ -8,13 +8,15 @@ #ifndef REHDD_H_ #define REHDD_H_ -#define REHDD_VERSION "bV0.2.2" +#define REHDD_VERSION "bV0.3.0" // Drive handling Settings #define WORSE_HOURS 19200 //mark drive if at this limit or beyond #define WORSE_POWERUP 10000 //mark drive if at this limit or beyond +#define WORSE_TEMPERATURE 55 //mark drive if at this limit or beyond #define SHRED_ITERATIONS 3U -#define FROZEN_TIMEOUT 10 //After this timeout (minutes) the drive will be marked as frozen +#define FROZEN_TIMEOUT 20 //After this timeout (minutes) the drive will be marked as frozen, if no progress +#define METRIC_THRESHOLD 3L*1000L*1000L*1000L //calc shred speed with this minimum of time delta // Logger Settings #define LOG_PATH "./reHDD.log" @@ -83,12 +85,14 @@ private: static void searchDrives(list * plistDrives); static void printDrives(list * plistDrives); - static void filterIgnoredDrives(list * plistDrives); + static void startShredAllDrives(list * plistDrives); + static void updateShredMetrics(list * plistDrives); + static void filterIgnoredDrives(list * plistDrives); static void filterNewDrives(list * plistOldDrives, list * plistNewDrives); static void addSMARTData(list * plistDrives); static void ThreadScannDevices(); static void ThreadUserInput(); - static void ThreadShred(); + static void ThreadShred(Drive* const pDrive); static void ThreadDelete(); static void ThreadCheckFrozenDrives(); static void handleArrowKey(TUI::UserInput userInput); diff --git a/include/shred.h b/include/shred.h index a89457e..c423d36 100644 --- a/include/shred.h +++ b/include/shred.h @@ -17,11 +17,13 @@ #include #include -#define CHUNK_SIZE 1024*1024*2 //amount of bytes that are overwritten at once --> 2MB -#define CHUNK_DIMENSION 100U //amount of chunks are read at once from random source + +#define CHUNK_SIZE 1024*1024*32 //amount of bytes that are overwritten at once --> 32MB +#define TFNG_DATA_SIZE CHUNK_SIZE //amount of bytes used by tfng //#define DEMO_DRIVE_SIZE 1024*1024*256L // 256MB //#define DEMO_DRIVE_SIZE 1024*1024*1024L // 1GB +//#define DEMO_DRIVE_SIZE 5*1024*1024*1024L // 5GB //#define DEMO_DRIVE_SIZE 1024*1024*1024*10L // 10GB typedef int fileDescriptor; @@ -39,7 +41,8 @@ public: private: fileDescriptor randomSrcFileDiscr; fileDescriptor driveFileDiscr; - unsigned char caChunk[CHUNK_DIMENSION][CHUNK_SIZE]; + unsigned char caTfngData[TFNG_DATA_SIZE]; + unsigned char caReadBuffer[CHUNK_SIZE]; unsigned long ulDriveByteSize; unsigned long ulDriveByteOverallCount = 0; //all bytes shredded in all iterations + checking -> used for progress calculation double d32Percent = 0.0; diff --git a/include/smart.h b/include/smart.h index 99e6cca..37890f1 100644 --- a/include/smart.h +++ b/include/smart.h @@ -27,6 +27,7 @@ private: static void parseErrorCount(string sLine); static void parsePowerOnHours(string sLine); static void parsePowerCycle(string sLine); + static void parseTemperature(string sLine); static string modelFamily; static string modelName; @@ -35,6 +36,7 @@ private: static uint32_t errorCount; static uint32_t powerOnHours; static uint32_t powerCycle; + static uint32_t temperature; }; #endif // SMART_H_ \ No newline at end of file diff --git a/include/tui.h b/include/tui.h index c18e64d..c4595fa 100644 --- a/include/tui.h +++ b/include/tui.h @@ -22,7 +22,7 @@ protected: public: - enum UserInput { UpKey, DownKey, Abort, Shred, Delete, Enter, ESC, Undefined}; + enum UserInput { UpKey, DownKey, Abort, Shred, ShredAll, Delete, Enter, ESC, Undefined}; struct MenuState { bool bAbort; @@ -56,16 +56,17 @@ private: static WINDOW *createOverViewWindow( int iXSize, int iYSize); static WINDOW *createDetailViewWindow( int iXSize, int iYSize, int iXStart, Drive drive); static WINDOW *overwriteDetailViewWindow( int iXSize, int iYSize, int iXStart); - static WINDOW *createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, string sModelFamily, string sModelName, string sCapacity, string sState, string sTime, bool bSelected); + static WINDOW *createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, int iListIndex, string sModelFamily, string sSerial, string sCapacity, string sState, string sTime, string sSpeed, string sTemp, bool bSelected); static WINDOW *createSystemStats(int iXSize, int iYSize, int iXStart, int iYStart); static WINDOW *createMenuView(int iXSize, int iYSize, int iXStart, int iYStart, struct MenuState menustate); static WINDOW *createDialog(int iXSize, int iYSize, int iXStart, int iYStart, string selectedTask, string optionA, string optionB); static WINDOW* createFrozenWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, string sModelFamily, string sModelName, string sSerial, string sProgress); - static WINDOW* createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount); + static WINDOW* createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount, uint32_t u32Temperature); static WINDOW* createZeroChecksumWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, string sModelFamily, string sModelName, string sSerial, uint32_t u32Checksum); void displaySelectedDrive(Drive drive, int stdscrX, int stdscrY); string formatTimeDuration(time_t u32Duration); + string formatSpeed(time_t u32ShredTimeDelta, unsigned long ulWrittenBytes); }; #endif // TUI_H_ \ No newline at end of file diff --git a/makefile b/makefile index fe3ada5..b8e3e3b 100644 --- a/makefile +++ b/makefile @@ -8,19 +8,22 @@ SRC_EXT = cpp # Path to the source directory, relative to the makefile SRC_PATH = src # Space-separated pkg-config libraries used by this project -LIBS = +LIBS = lib # General compiler flags COMPILE_FLAGS = -std=c++17 -Wall -Wextra -g # Additional release-specific flags -RCOMPILE_FLAGS = -D NDEBUG +RCOMPILE_FLAGS = -D NDEBUG -O3 # Additional debug-specific flags DCOMPILE_FLAGS = -D DEBUG # Add additional include paths INCLUDES = include # General linker settings -LINK_FLAGS = -lpthread -lncurses +LINK_FLAGS = -Llib -lpthread -lncurses -ltfng + # Doc DOCDIR = doc +TFRANDDIR = tfnoisegen +TFRANDLIB = libtfng.a #### END PROJECT SETTINGS #### # Optionally you may move the section above to a separate config.mk file, and @@ -158,6 +161,7 @@ dirs: @echo "Creating directories" @mkdir -p $(dir $(OBJECTS)) @mkdir -p $(BIN_PATH) + @mkdir -p $(LIBS) # Removes all build files .PHONY: clean @@ -167,16 +171,22 @@ clean: @echo "Deleting directories" @$(RM) -r build @$(RM) -r bin + @$(RM) -r $(LIBS) @$(RM) -f reHDD.log + $(MAKE) clean -C tfnoisegen # Main rule, checks the executable and symlinks to the output all: $(BIN_PATH)/$(BIN_NAME) + $(MAKE) libtfng.a -C tfnoisegen + @cp $(TFRANDDIR)/$(TFRANDLIB) $(LIBS) @echo "Making symlink: $(BIN_NAME) -> $<" @$(RM) $(BIN_NAME) @ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME) # Link the executable $(BIN_PATH)/$(BIN_NAME): $(OBJECTS) + $(MAKE) libtfng.a -C tfnoisegen + @cp $(TFRANDDIR)/$(TFRANDLIB) $(LIBS) @echo "Linking: $@" @$(START_TIME) $(CMD_PREFIX)$(CXX) $(OBJECTS) $(LDFLAGS) -o $@ diff --git a/scripts/update.sh b/scripts/update.sh index f2b4394..c4479c0 100644 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -2,15 +2,17 @@ echo starting update +systemctl stop /lib/systemd/system/getty@tty1.service.d + +cd /root/reHDD/ + FILE=./ignoreDrives.conf if test -f "$FILE"; then echo backup exits else - cp reHDD/ignoreDrives.conf ./ignoreDrives.conf + cp /root/reHDD/ignoreDrives.conf /root/ignoreDrives.conf fi -cd reHDD - git reset git stash force @@ -23,4 +25,6 @@ git pull make release -cp ../ignoreDrives.conf ./ +cp /root/ignoreDrives.conf /root/reHDD/ignoreDrives.conf + +systemctl start /lib/systemd/system/getty@tty1.service.d \ No newline at end of file diff --git a/src/drive.cpp b/src/drive.cpp index 35f934e..f31367f 100644 --- a/src/drive.cpp +++ b/src/drive.cpp @@ -14,36 +14,41 @@ string Drive::getPath(void) string Drive::getModelFamily(void) { - return sModelFamily; + return sSmartData.sModelFamily; } string Drive::getModelName(void) { - return sModelName; + return sSmartData.sModelName; } string Drive::getSerial(void) { - return sSerial; + return sSmartData.sSerial; } uint64_t Drive::getCapacity(void) { - return u64Capacity; + return sSmartData.u64Capacity; } uint32_t Drive::getErrorCount(void) { - return u32ErrorCount; + return sSmartData.u32ErrorCount; } uint32_t Drive::getPowerOnHours(void) { - return u32PowerOnHours; + return sSmartData.u32PowerOnHours; } uint32_t Drive::getPowerCycles(void) { - return u32PowerCycles; + return sSmartData.u32PowerCycles; +} + +uint32_t Drive::getTemperature(void) +{ + return sSmartData.u32Temperature; } string Drive::sCapacityToText() @@ -67,7 +72,6 @@ string Drive::sErrorCountToText() return to_string(getErrorCount()); } - string Drive::sPowerOnHoursToText() { double dDays = 0U; @@ -92,6 +96,11 @@ string Drive::sPowerCyclesToText() return to_string(getPowerCycles()); } +string Drive::sTemperatureToText() +{ + return to_string(getTemperature())+" C";; +} + void Drive::setTaskPercentage(double d32TaskPercentage) { if(d32TaskPercentage <= 100) @@ -115,6 +124,7 @@ double Drive::getTaskPercentage(void) * \param uint32_t errorCount * \param uint32_t powerOnHours * \param uint32_t powerCycle + * \param uint32_t temperature * \return void */ void Drive::setDriveSMARTData( string modelFamily, @@ -123,16 +133,17 @@ void Drive::setDriveSMARTData( string modelFamily, uint64_t capacity, uint32_t errorCount, uint32_t powerOnHours, - uint32_t powerCycle) + uint32_t powerCycle, + uint32_t temperature) { - this->sModelFamily = modelFamily; - sModelName = modelName; - sSerial = serial; - u64Capacity = capacity; - u32ErrorCount = errorCount; - u32PowerOnHours = powerOnHours; - u32PowerCycles = powerCycle; - + this->sSmartData.sModelFamily = modelFamily; + this->sSmartData.sModelName = modelName; + this->sSmartData.sSerial = serial; + this->sSmartData.u64Capacity = capacity; + this->sSmartData.u32ErrorCount = errorCount; + this->sSmartData.u32PowerOnHours = powerOnHours; + this->sSmartData.u32PowerCycles = powerCycle; + this->sSmartData.u32Temperature = temperature; } void Drive::setTimestamp() @@ -168,7 +179,7 @@ void Drive::checkFrozenDrive(void) time_t u32localtime; time(&u32localtime); - if((u32localtime - this->u32Timestamp) >= (FROZEN_TIMEOUT*60) && (this->u32Timestamp > 0)) + if((u32localtime - this->u32Timestamp) >= (FROZEN_TIMEOUT*60) && (this->u32Timestamp > 0) && (this->getTaskPercentage() < 100.0)) { Logger::logThis()->warning("Drive Frozen: " + this->getModelName() + " " + this->getSerial()); this->bWasDeleteted = false; diff --git a/src/reHDD.cpp b/src/reHDD.cpp index 3c05ddc..5dcd2eb 100644 --- a/src/reHDD.cpp +++ b/src/reHDD.cpp @@ -11,7 +11,7 @@ static int fdNewDrivesInformPipe[2];//File descriptor for pipe that informs if n static int fdShredInformPipe[2];//File descriptor for pipe that informs if a wipe thread signals -static std::mutex mxScannDrives; +static std::mutex mxDrives; list listNewDrives; //store found drives that are updated every 5sec @@ -60,20 +60,25 @@ void reHDD::app_logic(void) if(FD_ISSET(fdNewDrivesInformPipe[0], &selectSet)) { - mxScannDrives.lock(); + mxDrives.lock(); char dummy; read (fdNewDrivesInformPipe[0],&dummy,1); filterNewDrives(&listDrives, &listNewDrives); //filter and copy to app logic vector printDrives(&listDrives); - mxScannDrives.unlock(); + mxDrives.unlock(); } if(FD_ISSET(fdShredInformPipe[0], &selectSet)) { char dummy; read (fdShredInformPipe[0],&dummy,1); + updateShredMetrics(&listDrives); #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("got progress signal from a shred task"); #endif + + + + } ui->updateTUI(&listDrives, u8SelectedEntry); } //endless loop @@ -101,12 +106,12 @@ void reHDD::ThreadScannDevices() { while(true) { - mxScannDrives.lock(); + mxDrives.lock(); listNewDrives.clear(); searchDrives(&listNewDrives); //search for new drives and store them in list filterIgnoredDrives(&listNewDrives); //filter out ignored drives addSMARTData(&listNewDrives); //add S.M.A.R.T. Data to the drives - mxScannDrives.unlock(); + mxDrives.unlock(); write(fdNewDrivesInformPipe[1], "A",1); sleep(5); //sleep 5 sec } @@ -116,7 +121,7 @@ void reHDD::ThreadCheckFrozenDrives() { while(true) { - mxScannDrives.lock(); + mxDrives.lock(); for(auto it = begin(listDrives); it != end(listDrives); ++it) { if(it->state == Drive::SHRED_ACTIVE) @@ -124,7 +129,7 @@ void reHDD::ThreadCheckFrozenDrives() it->checkFrozenDrive(); } } - mxScannDrives.unlock(); + mxDrives.unlock(); sleep(13); //sleep 13 sec } } @@ -164,6 +169,7 @@ void reHDD::ThreadUserInput() getSelectedDrive()->state = Drive::DELETE_SELECTED; } } + ui->updateTUI(&listDrives, u8SelectedEntry); break; case TUI::UserInput::Shred: @@ -176,6 +182,12 @@ void reHDD::ThreadUserInput() getSelectedDrive()->state = Drive::SHRED_SELECTED; } } + + ui->updateTUI(&listDrives, u8SelectedEntry); + break; + case TUI::UserInput::ShredAll: + //cout << "ShredAll" << endl; + startShredAllDrives(&listDrives); ui->updateTUI(&listDrives, u8SelectedEntry); break; case TUI::UserInput::Enter: @@ -194,13 +206,13 @@ void reHDD::ThreadUserInput() } } -void reHDD::ThreadShred() +void reHDD::ThreadShred(Drive* const pDrive) { - if (getSelectedDrive() != nullptr) + if (pDrive != nullptr) { - getSelectedDrive()->setActionStartTimestamp(); //save timestamp at start of shredding + pDrive->setActionStartTimestamp(); //save timestamp at start of shredding Shred* pShredTask = new Shred(); //create new shred task - pShredTask->shredDrive(getSelectedDrive(), &fdShredInformPipe[1]); //start new shred task + pShredTask->shredDrive(pDrive, &fdShredInformPipe[1]); //start new shred task delete pShredTask; //delete shred task ui->updateTUI(&listDrives, u8SelectedEntry); } @@ -246,12 +258,14 @@ void reHDD::filterNewDrives(list * plistOldDrives, list * plistNew //search offline drives and mark them for (itOld = plistOldDrives->begin(); itOld != plistOldDrives->end(); ++itOld) { - itOld->bIsOffline = true; //set offline befor seachring in the new list + itOld->bIsOffline = true; //set offline before searching in the new list for (itNew = plistNewDrives->begin(); itNew != plistNewDrives->end();) { if((itOld->getSerial() == itNew->getSerial()) || (itOld->getPath() == itNew->getPath())) { itOld->bIsOffline = false; //drive is still attached + //copy new smart data to existing drive + itOld->setDriveSMARTData(itNew->getModelFamily(), itNew->getModelName(), itNew->getSerial(), itNew->getCapacity(), itNew->getErrorCount(), itNew->getPowerOnHours(), itNew->getPowerCycles(), itNew->getTemperature()); #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Delete new drive, because allready attached: " + itNew->getModelName()); #endif @@ -279,7 +293,7 @@ void reHDD::filterNewDrives(list * plistOldDrives, list * plistNew for (itNew = plistNewDrives->begin(); itNew != plistNewDrives->end(); ++itNew) { plistOldDrives->push_back(*itNew); - Logger::logThis()->info("Add new drive: " + itNew->getModelName()); + //Logger::logThis()->info("Add new drive: " + itNew->getModelName()); } plistNewDrives->clear(); } @@ -291,7 +305,7 @@ void reHDD::filterNewDrives(list * plistOldDrives, list * plistNew */ void reHDD::searchDrives(list * plistDrives) { - Logger::logThis()->info("--> search drives <--"); + //Logger::logThis()->info("--> search drives <--"); char * cLine = NULL; size_t len = 0; @@ -329,7 +343,7 @@ void reHDD::filterIgnoredDrives(list * plistDrives) for(string sLine; getline( input, sLine );) { - Logger::logThis()->info("read uuid: " + sLine); + //Logger::logThis()->info("read uuid: " + sLine); vtlIgnoredDevices.emplace_back(sLine); //add found path and uuid from ignore file to vector } //loop through found entries in ingnore file @@ -377,6 +391,32 @@ void reHDD::filterIgnoredDrives(list * plistDrives) } } +/** + * \brief start shred for all drives + * \param pointer of list * plistDrives + * \return void + */ +void reHDD::startShredAllDrives(list * plistDrives) +{ + list ::iterator it; + mxDrives.lock(); + for (it = plistDrives->begin(); it != plistDrives->end(); ++it) + { + if(it->state == Drive::NONE) + { + Drive* pTmpDrive = iterator_to_pointer::iterator > (it); +#ifdef LOG_LEVEL_HIGH + ostringstream address; + address << (void const *)&(*pTmpDrive); + Logger::logThis()->info("Started shred (all) for: " + pTmpDrive->getModelName() + "-" + pTmpDrive->getSerial() + " @" + address.str()); +#endif + pTmpDrive->state = Drive::TaskState::SHRED_ACTIVE; + thread(ThreadShred, pTmpDrive).detach(); + } + } + mxDrives.unlock(); +} + /** * \brief print drives with all information * \param pointer of list * plistDrives @@ -412,6 +452,33 @@ void reHDD::printDrives(list * plistDrives) #endif } +/** + * \brief update shred metrics for all drives + * \param pointer of list * plistDrives + * \return void + */ +void reHDD::updateShredMetrics(list * plistDrives) +{ + list ::iterator it; + for (it = plistDrives->begin(); it != plistDrives->end(); ++it) + { + if(it->state == Drive::SHRED_ACTIVE) + { + Drive* pTmpDrive = iterator_to_pointer::iterator > (it); + //set metrics for calculating shred speed + std::chrono::time_point chronoCurrentTimestamp = std::chrono::system_clock::now(); + time_t u32ShredTimeDelta = (chronoCurrentTimestamp - pTmpDrive->sShredSpeed.chronoShredTimestamp).count(); + if(u32ShredTimeDelta > METRIC_THRESHOLD) + { + pTmpDrive->sShredSpeed.u32ShredTimeDelta = u32ShredTimeDelta; + pTmpDrive->sShredSpeed.chronoShredTimestamp = std::chrono::system_clock::now(); + pTmpDrive->sShredSpeed.ulWrittenBytes = pTmpDrive->sShredSpeed.ulSpeedMetricBytesWritten; + pTmpDrive->sShredSpeed.ulSpeedMetricBytesWritten = 0U; + } + } + } +} + /** * \brief add S.M.A.R.T data from SMART * \param pointer of list * plistDrives @@ -454,11 +521,12 @@ void reHDD::handleArrowKey(TUI::UserInput userInput) break; } - Logger::logThis()->info("ArrowKey - selected drive: " + to_string(u8SelectedEntry)); + //Logger::logThis()->info("ArrowKey - selected drive: " + to_string(u8SelectedEntry)); } void reHDD::handleEnter() { + if (getSelectedDrive() != nullptr) { if(getSelectedDrive()->state == Drive::TaskState::SHRED_SELECTED) @@ -466,7 +534,8 @@ void reHDD::handleEnter() Logger::logThis()->info("Started shred for: " + getSelectedDrive()->getModelName() + "-" + getSelectedDrive()->getSerial()); getSelectedDrive()->state = Drive::TaskState::SHRED_ACTIVE; //task for drive is running --> donĀ“t show more task options - thread(ThreadShred).detach(); + Drive* pTmpDrive = getSelectedDrive(); + thread(ThreadShred, pTmpDrive).detach(); } if(getSelectedDrive()->state == Drive::TaskState::DELETE_SELECTED) diff --git a/src/shred.cpp b/src/shred.cpp index 8eee2d2..aaf541f 100644 --- a/src/shred.cpp +++ b/src/shred.cpp @@ -2,11 +2,19 @@ * @file shred.cpp * @brief shred drive * @author hendrik schutter - * @date 03.05.2020 + * @date 22.08.2022 */ #include "../include/reHDD.h" +#ifdef __cplusplus +extern "C" { +#endif +#include "../tfnoisegen/tfprng.h" +#ifdef __cplusplus +} +#endif + const static char *randomsrc = (char*) "/dev/urandom"; Shred::Shred() @@ -24,6 +32,9 @@ Shred::~Shred() */ int Shred::shredDrive(Drive* drive, int* ipSignalFd) { + ostringstream address; + address << (void const *)&(*drive); + Logger::logThis()->info("Shred-Task started - Drive: " + drive->getModelName() + "-" + drive->getSerial() + " @" + address.str()); #ifdef DRYRUN for(int i = 0; i<=500; i++) @@ -40,6 +51,7 @@ int Shred::shredDrive(Drive* drive, int* ipSignalFd) #ifndef DRYRUN const char *cpDrivePath = drive->getPath().c_str(); + unsigned char ucKey[TFNG_KEY_SIZE]; //open random source randomSrcFileDiscr = open(randomsrc, O_RDONLY | O_LARGEFILE); @@ -63,7 +75,22 @@ int Shred::shredDrive(Drive* drive, int* ipSignalFd) return -1; } + //read key for random generator + ssize_t readRet = read(randomSrcFileDiscr, ucKey, sizeof(ucKey)) ; + if (readRet <= 0) + { + std::string errorMsg(strerror(readRet)); + Logger::logThis()->error("Shred-Task: Read random key failed! " + errorMsg + " - Drive: " + drive->getSerial()); + perror(randomsrc); + cleanup(); + return -1; + } + + tfng_prng_seedkey(ucKey); + this->ulDriveByteSize = getDriveSizeInBytes(driveFileDiscr); + drive->sShredSpeed.chronoShredTimestamp = std::chrono::system_clock::now();; //set inital timestamp for speed metric + drive->sShredSpeed.ulSpeedMetricBytesWritten = 0U; //uses to calculate speed metric #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: Bytes-Size of Drive: " + to_string(this->ulDriveByteSize) + " - Drive: " + drive->getSerial()); @@ -72,44 +99,21 @@ int Shred::shredDrive(Drive* drive, int* ipSignalFd) for (unsigned int uiShredIterationCounter = 0U; uiShredIterationCounter < SHRED_ITERATIONS; uiShredIterationCounter++) { unsigned long ulDriveByteCounter = 0U; //used for one shred-iteration to keep track of the current drive position - uint32_t u32ChunkDimensionIndex = 0U; if(uiShredIterationCounter == (SHRED_ITERATIONS-1)) { - //last shred iteration --> overwrite with zeros instead with random data - memset(caChunk, 0U, CHUNK_DIMENSION*CHUNK_SIZE); + //last shred iteration --> overwrite (just the write chunk) bytes with zeros instead with random data + memset(caTfngData, 0U, CHUNK_SIZE); } while (ulDriveByteCounter < ulDriveByteSize) { int iBytesToShred = 0; //Bytes that will be overwritten in this chunk-iteration - if((u32ChunkDimensionIndex == 0U) && (uiShredIterationCounter != (SHRED_ITERATIONS-1))) + if(uiShredIterationCounter != (SHRED_ITERATIONS-1)) { - //read new chunks from random source if needed and this is NOT the last shred iteration - unsigned long ulBytesInChunkBuffer = 0U; - - while (ulBytesInChunkBuffer < CHUNK_DIMENSION*CHUNK_SIZE) - { - //read new random bytes - int iReadBytes = read(randomSrcFileDiscr, caChunk, ((CHUNK_DIMENSION*CHUNK_SIZE)-ulBytesInChunkBuffer)); - if (iReadBytes > 0) - { - ulBytesInChunkBuffer += iReadBytes; - } - else - { - std::string errorMsg(strerror(iReadBytes)); - Logger::logThis()->error("Shred-Task: Read from random source failed! " + errorMsg + " - Drive: " + drive->getSerial()); - perror("unable to read random data"); - cleanup(); - return -1;; - } - } //end chunk read -#ifdef LOG_LEVEL_HIGH - Logger::logThis()->info("Shred-Task: Read new random data - Drive: " + drive->getSerial()); -#endif - + //NOT last shred iteration --> generate new random data + tfng_prng_genrandom(caTfngData, TFNG_DATA_SIZE); } if((ulDriveByteSize-ulDriveByteCounter) < CHUNK_SIZE) @@ -121,7 +125,7 @@ int Shred::shredDrive(Drive* drive, int* ipSignalFd) iBytesToShred = CHUNK_SIZE; } - int iByteShredded = write(driveFileDiscr, caChunk[u32ChunkDimensionIndex], iBytesToShred); + int iByteShredded = write(driveFileDiscr, caTfngData, iBytesToShred); if(iByteShredded <= 0) { @@ -132,18 +136,20 @@ int Shred::shredDrive(Drive* drive, int* ipSignalFd) return -1; } - u32ChunkDimensionIndex = (u32ChunkDimensionIndex+1)%CHUNK_DIMENSION; ulDriveByteCounter += iByteShredded; ulDriveByteOverallCount += iByteShredded; d32Percent = this->calcProgress(); + drive->sShredSpeed.ulSpeedMetricBytesWritten += iByteShredded; #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task: ByteCount: " + to_string(ulDriveByteCounter) + " - iteration: " + to_string((uiShredIterationCounter+1)) + " - progress: " + to_string(d32Percent) + " - Drive: " + drive->getSerial()); #endif if((d32Percent-d32TmpPercent) >= 0.01) { + //set shred percantage drive->setTaskPercentage(d32TmpPercent); d32TmpPercent = d32Percent; + //signal process in shreding write(*ipSignalFd, "A",1); } @@ -157,13 +163,19 @@ int Shred::shredDrive(Drive* drive, int* ipSignalFd) cleanup(); return -1; } - }//end one chunk write + //end one chunk write + } if(0 != iRewindDrive(driveFileDiscr)) { + Logger::logThis()->error("Shred-Task: Unable to rewind drive! - Drive: " + drive->getSerial()); cleanup(); return -1; } - } //end one shred iteration + //end one shred iteration + } + //end of all shred iteratio + + tfng_prng_seedkey(NULL); //reset random generator #ifdef ZERO_CHECK_ALERT drive->u32DriveChecksumAferShredding = uiCalcChecksum(driveFileDiscr, drive, ipSignalFd); @@ -250,18 +262,19 @@ unsigned int Shred::uiCalcChecksum(fileDescriptor file,Drive* drive, int* ipSign { iBytesToCheck = CHUNK_SIZE; } - int iReadBytes = read(file, caChunk, iBytesToCheck); + int iReadBytes = read(file, caReadBuffer, iBytesToCheck); for (int iReadBytesCounter = 0U; iReadBytesCounter < iReadBytes; iReadBytesCounter++) { - uiChecksum += caChunk[0][iReadBytesCounter]; + uiChecksum += caReadBuffer[iReadBytesCounter]; } ulDriveByteCounter += iReadBytes; ulDriveByteOverallCount += iReadBytes; d32Percent = this->calcProgress(); + drive->sShredSpeed.ulSpeedMetricBytesWritten += iReadBytes; #ifdef LOG_LEVEL_HIGH Logger::logThis()->info("Shred-Task (Checksum): ByteCount: " + to_string(ulDriveByteCounter) + " - progress: " + to_string(d32Percent) + " - Drive: " + drive->getSerial()); #endif - if((d32Percent-d32TmpPercent) >= 0.9) + if((d32Percent-d32TmpPercent) >= 0.01) { drive->setTaskPercentage(d32TmpPercent); d32TmpPercent = d32Percent; @@ -274,5 +287,5 @@ unsigned int Shred::uiCalcChecksum(fileDescriptor file,Drive* drive, int* ipSign void Shred::cleanup() { close(driveFileDiscr); - close( randomSrcFileDiscr); + close(randomSrcFileDiscr); } \ No newline at end of file diff --git a/src/smart.cpp b/src/smart.cpp index 1a787af..a19fa67 100644 --- a/src/smart.cpp +++ b/src/smart.cpp @@ -14,6 +14,7 @@ uint64_t SMART::capacity = 0U; uint32_t SMART::errorCount = 0U; uint32_t SMART::powerOnHours = 0U; uint32_t SMART::powerCycle = 0U; +uint32_t SMART::temperature = 0U; /** * \brief get and set S.M.A.R.T. values in Drive @@ -29,6 +30,7 @@ void SMART::readSMARTData(Drive* drive) errorCount = 0U; powerOnHours = 0U; powerCycle = 0U; + temperature = 0U; size_t len = 0; //lenght of found line char* cLine = NULL; //found line @@ -50,9 +52,10 @@ void SMART::readSMARTData(Drive* drive) SMART::parseErrorCount(sLine); SMART::parsePowerOnHours(sLine); SMART::parsePowerCycle(sLine); + SMART::parseTemperature(sLine); } pclose(outputfileSmart); - drive->setDriveSMARTData(modelFamily, modelName, serial, capacity, errorCount, powerOnHours, powerCycle); //wirte data in drive + drive->setDriveSMARTData(modelFamily, modelName, serial, capacity, errorCount, powerOnHours, powerCycle, temperature); //wirte data in drive } /** @@ -176,3 +179,26 @@ void SMART::parsePowerCycle(string sLine) } } +/** + * \brief parse temperature + * \param string output line of smartctl + * \return void + */ +void SMART::parseTemperature(string sLine) +{ + string search("\"current\": "); + size_t found = sLine.find(search); + if (found!=string::npos) + { + sLine.erase(0, sLine.find(": ") + 2); + sLine.erase(sLine.length()-1, 2); + if(sLine == "{") + { + temperature = 0U; // this drive doesn't support temperatur + } + else + { + temperature = stol(sLine); + } + } +} diff --git a/src/tui.cpp b/src/tui.cpp index 6d2b4c0..895c506 100644 --- a/src/tui.cpp +++ b/src/tui.cpp @@ -74,11 +74,12 @@ void TUI::updateTUI(list * plistDrives, uint8_t u8SelectedEntry) for (it = plistDrives->begin(); it != plistDrives->end(); ++it) { string sModelFamily = it->getModelFamily(); - string sModelName = it->getModelName(); + string sSerial = "SN: " + it->getSerial(); string sCapacity = it->sCapacityToText(); string sState = " "; + string sSpeed = " "; string sTime = " "; - + string sTemp = it->sTemperatureToText(); bool bSelectedEntry = false; @@ -87,17 +88,16 @@ void TUI::updateTUI(list * plistDrives, uint8_t u8SelectedEntry) bSelectedEntry = true; //mark this drive in entries list displaySelectedDrive(*it, u16StdscrX, u16StdscrY); - if((it->getPowerOnHours() >= WORSE_HOURS) || (it->getPowerCycles() >= WORSE_POWERUP) || (it->getErrorCount() > 0)) + if((it->getPowerOnHours() >= WORSE_HOURS) || (it->getPowerCycles() >= WORSE_POWERUP) || (it->getErrorCount() > 0) || (it->getTemperature() >= WORSE_TEMPERATURE)) { // smart values are bad --> show warning - smartWarning=createSmartWarning(50, 10, ((u16StdscrX)-(int)(u16StdscrX/2)+35),(int)(u16StdscrY/2)-5, it->getPath(), it->getPowerOnHours(), it->getPowerCycles(), it->getErrorCount()); + smartWarning=createSmartWarning(50, 10, ((u16StdscrX)-(int)(u16StdscrX/2)+35),(int)(u16StdscrY/2)-5, it->getPath(), it->getPowerOnHours(), it->getPowerCycles(), it->getErrorCount(), it->getTemperature()); wrefresh(smartWarning); } } stringstream stream; - switch (it->state) { case Drive::SHRED_ACTIVE: @@ -107,6 +107,7 @@ void TUI::updateTUI(list * plistDrives, uint8_t u8SelectedEntry) it->calculateTaskDuration(); sTime = this->formatTimeDuration(it->getTaskDuration()); + sSpeed = this->formatSpeed(it->sShredSpeed.u32ShredTimeDelta, it->sShredSpeed.ulWrittenBytes); break; case Drive::DELETE_ACTIVE: sState = "Deleting ..."; @@ -151,7 +152,7 @@ void TUI::updateTUI(list * plistDrives, uint8_t u8SelectedEntry) break; } - WINDOW * tmp = createEntryWindow( ((int)(u16StdscrX/3) - 2), 5, 3, (5* (u8Index) )+3, sModelFamily, sModelName, sCapacity, sState, sTime, bSelectedEntry); + WINDOW * tmp = createEntryWindow( ((int)(u16StdscrX/3) - 2), 5, 3, (5* (u8Index) )+3, (distance(plistDrives->begin(), it)+1), sModelFamily, sSerial, sCapacity, sState, sTime, sSpeed, sTemp, bSelectedEntry); wrefresh(tmp); u8Index++; }//end loop though drives @@ -203,6 +204,9 @@ enum TUI::UserInput TUI::readUserInput() case 's': return TUI::UserInput::Shred; break; + case 'S': + return TUI::UserInput::ShredAll; + break; default: return TUI::UserInput::Undefined; break; @@ -305,7 +309,7 @@ WINDOW* TUI::overwriteDetailViewWindow( int iXSize, int iYSize, int iXStart) return newWindow; } -WINDOW* TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, string sModelFamily, string sModelName, string sCapacity, string sState, string sTime, bool bSelected) +WINDOW* TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, int iListIndex, string sModelFamily, string sSerial, string sCapacity, string sState, string sTime, string sSpeed, string sTemp, bool bSelected) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); @@ -325,12 +329,16 @@ WINDOW* TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, box(newWindow, ACS_VLINE, ACS_HLINE); - mvwaddstr(newWindow,1, 1, sModelFamily.c_str()); - mvwaddstr(newWindow,2, 1, sModelName.c_str()); - mvwaddstr(newWindow,3, 1, sCapacity.c_str()); + mvwaddstr(newWindow,1, 1, to_string(iListIndex).c_str()); + mvwaddstr(newWindow,1, 5, sModelFamily.c_str()); + mvwaddstr(newWindow,2, 5, sSerial.c_str()); + mvwaddstr(newWindow,3, 5, sCapacity.c_str()); + mvwaddstr(newWindow,3, 5+sCapacity.length()+3, sTemp.c_str()); + + mvwaddstr(newWindow,1, iXSize-sSpeed.length()-5, sSpeed.c_str()); mvwaddstr(newWindow,2, iXSize-sState.length()-5, sState.c_str()); - mvwaddstr(newWindow,3, iXSize-sState.length()-5, sTime.c_str()); + mvwaddstr(newWindow,3, iXSize-sTime.length()-5, sTime.c_str()); return newWindow; } @@ -353,10 +361,14 @@ WINDOW* TUI::createSystemStats(int iXSize, int iYSize, int iXStart, int iYStart) strftime(buffer,sizeof(buffer),"Date: %d-%m-%Y Time: %H:%M",timeinfo); string time(buffer); - string sLine01 = "reHDD - hard drive refurbishing tool"; - string sLine02 = "Version: " + string(REHDD_VERSION); - string sLine03 = "Available under GPL 3.0"; - string sLine04 = "https://git.mosad.xyz/localhorst/reHDD"; + string sLine01 = "reHDD - hard drive refurbishing tool"; + string sLine02 = "Version: " + string(REHDD_VERSION); + string sLine03 = "Build time: "; + sLine03.append(__DATE__); + sLine03.append(" "); + sLine03.append(__TIME__); + string sLine04 = "Available under GPL 3.0"; + string sLine05 = "https://git.mosad.xyz/localhorst/reHDD"; uint16_t u16Line = 2; @@ -364,6 +376,7 @@ WINDOW* TUI::createSystemStats(int iXSize, int iYSize, int iXStart, int iYStart) mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), sLine02.c_str()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), sLine03.c_str()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), sLine04.c_str()); + mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), sLine05.c_str()); u16Line++; mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), time.c_str()); @@ -384,19 +397,19 @@ WINDOW* TUI::createMenuView(int iXSize, int iYSize, int iXStart, int iYStart, st if(menustate.bAbort) { - string sLineTmp = "Press A for Abort"; + string sLineTmp = "Press a for Abort"; mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLineTmp.size()/2), sLineTmp.c_str()); u16Line++; } if(menustate.bShred) { - string sLineTmp = "Press S for Shred "; + string sLineTmp = "Press s for Shred (S for all drives)"; mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLineTmp.size()/2), sLineTmp.c_str()); u16Line++; } if(menustate.bDelete) { - string sLineTmp = "Press D for Delete"; + string sLineTmp = "Press d for Delete"; mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLineTmp.size()/2), sLineTmp.c_str()); } @@ -506,6 +519,17 @@ string TUI::formatTimeDuration(time_t u32Duration) return out.str(); } +string TUI::formatSpeed(time_t u32ShredTimeDelta, unsigned long ulWrittenBytes) +{ + std::ostringstream out; + double dDeltaSec = ((double)((u32ShredTimeDelta)/1000000000.0)); //convert nano in sec + double speed = ((ulWrittenBytes/1000000.0)/dDeltaSec); + char s[25]; + sprintf(s, "%0.2lf MB/s", speed); + out << s; + return out.str(); +} + void TUI::displaySelectedDrive(Drive drive, int stdscrX, int stdscrY) { struct MenuState menustate; @@ -563,7 +587,7 @@ void TUI::displaySelectedDrive(Drive drive, int stdscrX, int stdscrY) } } -WINDOW* TUI::createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount) +WINDOW* TUI::createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount, uint32_t u32Temperature) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); @@ -598,6 +622,13 @@ WINDOW* TUI::createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart { string sLineTmp = "S.M.A.R.T. erros detected: " + to_string(u32ErrorCount); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), sLineTmp.c_str()); + u16Line++; + } + + if(u32Temperature >= WORSE_TEMPERATURE) + { + string sLineTmp = "Drive too hot: " + to_string(u32Temperature)+" C"; + mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine01.size()/2), sLineTmp.c_str()); } return newWindow; } \ No newline at end of file diff --git a/tfnoisegen b/tfnoisegen new file mode 160000 index 0000000..488716e --- /dev/null +++ b/tfnoisegen @@ -0,0 +1 @@ +Subproject commit 488716ef22ac5a1aae235a59bea2997ac7e8e45a