Compare commits
15 Commits
b0.2.0
...
0e391cebf3
Author | SHA1 | Date | |
---|---|---|---|
0e391cebf3 | |||
ecc8a71c64 | |||
48bbad914f | |||
818e78796d | |||
161e7f049b | |||
033760c328 | |||
af38d60982 | |||
25d8ca6920 | |||
84e42a430b | |||
77c7849484 | |||
f2db85aa33 | |||
c942f36e49 | |||
068413c15d | |||
7748d49b54 | |||
3cd6baed36 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -42,3 +42,6 @@
|
||||
reHDD
|
||||
|
||||
reHDD.log
|
||||
|
||||
|
||||
ignoreDrives.conf
|
||||
|
39
README.md
39
README.md
@ -11,31 +11,28 @@
|
||||
## Debian Build Notes
|
||||
|
||||
* apt-get install ncurses-dev git make g++
|
||||
* clone repo
|
||||
* clone repo in /root/
|
||||
* make release
|
||||
|
||||
## Create Standalone with Debian
|
||||
## Create Standalone with Debian 11
|
||||
|
||||
Instructions how to create a standalone machine that boots directly to reHDD. This is aimed for production use, like several drives a day shredding.
|
||||
|
||||
### Software requirements
|
||||
|
||||
* apt-get install hwinfo
|
||||
* wget http://ftp.de.debian.org/debian/pool/main/s/smartmontools/smartmontools_7.1-1_amd64.deb
|
||||
* dpkg --install smartmontools_7.1-1_amd64.deb
|
||||
* apt-get install hwinfo smartmontools curl
|
||||
|
||||
### Start reHDD after boot without login (as a tty shell)
|
||||
|
||||
nano /etc/systemd/system/reHDD.service
|
||||
```
|
||||
[Unit]
|
||||
Description=Custom user interface on tty1
|
||||
Conflicts=getty@tty1.service
|
||||
Before=getty.target
|
||||
mkdir /lib/systemd/system/getty@tty1.service.d/
|
||||
|
||||
nano /lib/systemd/system/getty@tty1.service.d/override.conf
|
||||
|
||||
```
|
||||
[Service]
|
||||
WorkingDirectory=/root/reHDD
|
||||
ExecStart=/root/reHDD/reHDD
|
||||
ExecStart=
|
||||
ExecStart=-/root/reHDD/reHDD
|
||||
StandardInput=tty
|
||||
StandardOutput=tty
|
||||
Restart=always
|
||||
@ -47,10 +44,10 @@ TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
SendSIGHUP=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
nano /etc/systemd/system/reHDDSettings.service
|
||||
```
|
||||
[Service]
|
||||
@ -66,14 +63,18 @@ nano /root/reHDDSettings.sh
|
||||
```
|
||||
#!/bin/bash
|
||||
dmesg -n 1 #disable overlay if a drive is attached/detached
|
||||
# remove comment for the following to activate log telemetie
|
||||
# curl -k -T /root/reHDD/reHDD.log -u "fgggfffgfgfgfg:" -H 'X-Requested-With: XMLHttpRequest' https://schuttercloud.com/public.php/webdav/`echo $(date '+%Y-%m-%d_%H-%M')`_reHDD.log
|
||||
rm -f /root/reHDD/reHDD.log
|
||||
```
|
||||
Make sure the binary reHDD is in /root/reHDD/
|
||||
Add your system drive in /root/reHDD/ignoreDrives.conf like:
|
||||
``` /dev/sdX:e102f49d-5ed5-462b-94c5-ef66a4345671```
|
||||
Get your UUID via blkid /dev/sdX
|
||||
chmod +x reHDDSettings.sh
|
||||
|
||||
systemctl enable reHDD.service
|
||||
|
||||
Make sure the binary reHDD is in /root/reHDD/
|
||||
|
||||
Add your system drive in /root/reHDD/ignoreDrives.conf like:
|
||||
```e102f49d```
|
||||
Get the first 8 Bytes from your UUID via blkid /dev/sdX
|
||||
|
||||
systemctl enable reHDDSettings.service
|
||||
|
||||
|
@ -1,3 +1 @@
|
||||
/dev/sdc:4673974d-1af2-44fd-996b-a2d8e4c43d9a
|
||||
/dev/sda:508ef27d-5039-4e8b-9e2c-22d7528b7149
|
||||
/dev/sdb:32b66944-ffa0-40e9-817c-3f0c52eefaf4
|
||||
4673974d
|
||||
|
@ -37,6 +37,8 @@ private:
|
||||
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
|
||||
|
||||
private:
|
||||
void setTimestamp();
|
||||
@ -75,6 +77,12 @@ public:
|
||||
void setTaskPercentage(double d32TaskPercentage);
|
||||
double getTaskPercentage(void);
|
||||
|
||||
void setActionStartTimestamp();
|
||||
time_t getActionStartTimestamp();
|
||||
|
||||
void calculateTaskDuration();
|
||||
time_t getTaskDuration();
|
||||
|
||||
};
|
||||
|
||||
#endif // DRIVE_H_
|
@ -8,7 +8,7 @@
|
||||
#ifndef REHDD_H_
|
||||
#define REHDD_H_
|
||||
|
||||
#define REHDD_VERSION "bV0.2.0"
|
||||
#define REHDD_VERSION "bV0.2.1"
|
||||
|
||||
// Drive handling Settings
|
||||
#define WORSE_HOURS 19200 //mark drive if at this limit or beyond
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
// Logger Settings
|
||||
#define LOG_PATH "./reHDD.log"
|
||||
#define DESCRIPTION "reHDD - Copyright Hendrik Schutter 2020"
|
||||
#define DESCRIPTION "reHDD - Copyright Hendrik Schutter 2022"
|
||||
#define DEVICE_ID "generic"
|
||||
#define SOFTWARE_VERSION "alpha"
|
||||
#define HARDWARE_VERSION "generic"
|
||||
@ -29,7 +29,7 @@
|
||||
#endif
|
||||
|
||||
// Logic
|
||||
//#define DRYRUN //don´t touch the drives
|
||||
#define DRYRUN //don´t touch the drives
|
||||
#define FROZEN_ALERT //show alert if drive is frozen
|
||||
|
||||
//IPC pipes
|
||||
|
@ -56,7 +56,7 @@ 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, bool bSelected);
|
||||
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 *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);
|
||||
@ -64,6 +64,7 @@ private:
|
||||
static WINDOW* createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount);
|
||||
|
||||
void displaySelectedDrive(Drive drive, int stdscrX, int stdscrY);
|
||||
string formatTimeDuration(time_t u32Duration);
|
||||
|
||||
};
|
||||
#endif // TUI_H_
|
@ -38,5 +38,5 @@ void Delete::deleteDrive(Drive* drive)
|
||||
{
|
||||
//wipefs running
|
||||
}
|
||||
fclose(deleteCmdOutput);
|
||||
pclose(deleteCmdOutput);
|
||||
}
|
||||
|
@ -48,17 +48,18 @@ uint32_t Drive::getPowerCycles(void)
|
||||
|
||||
string Drive::sCapacityToText()
|
||||
{
|
||||
if(getCapacity() <= (999*1000000000UL))
|
||||
char acBuffer[16];
|
||||
double dSize = (double) getCapacity();
|
||||
uint16_t u16UnitIndex = 0;
|
||||
const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
|
||||
while (dSize >= 1000) //using the marketing capacity
|
||||
{
|
||||
// Less or even 999 GB --> GB
|
||||
return to_string(getCapacity() / 1000000000UL) + " GB";
|
||||
dSize /= 1000;
|
||||
u16UnitIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// More 999 GB --> TB
|
||||
return to_string(getCapacity() / 1000000000000UL) + " TB";
|
||||
}
|
||||
return "ERROR";
|
||||
|
||||
sprintf(acBuffer, "%.*f %s", u16UnitIndex-3, dSize, units[u16UnitIndex]);
|
||||
return acBuffer;
|
||||
}
|
||||
|
||||
string Drive::sErrorCountToText()
|
||||
@ -131,14 +132,37 @@ void Drive::setDriveSMARTData( string modelFamily,
|
||||
u32ErrorCount = errorCount;
|
||||
u32PowerOnHours = powerOnHours;
|
||||
u32PowerCycles = powerCycle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Drive::setTimestamp()
|
||||
{
|
||||
time(&this->u32Timestamp);
|
||||
}
|
||||
|
||||
void Drive::setActionStartTimestamp()
|
||||
{
|
||||
time(&this->u32TimestampTaskStart);
|
||||
}
|
||||
|
||||
time_t Drive::getActionStartTimestamp()
|
||||
{
|
||||
return this->u32TimestampTaskStart;
|
||||
}
|
||||
|
||||
void Drive::calculateTaskDuration()
|
||||
{
|
||||
time_t u32localtime;
|
||||
time(&u32localtime);
|
||||
|
||||
this->u32TaskDuration = u32localtime - this->u32TimestampTaskStart;
|
||||
}
|
||||
|
||||
time_t Drive::getTaskDuration()
|
||||
{
|
||||
return this->u32TaskDuration;
|
||||
}
|
||||
|
||||
void Drive::checkFrozenDrive(void)
|
||||
{
|
||||
time_t u32localtime;
|
||||
|
@ -198,6 +198,7 @@ void reHDD::ThreadShred()
|
||||
{
|
||||
if (getSelectedDrive() != nullptr)
|
||||
{
|
||||
getSelectedDrive()->setActionStartTimestamp(); //save timestamp at start of shredding
|
||||
Shred* pShredTask = new Shred(); //create new shred task
|
||||
pShredTask->shredDrive(getSelectedDrive(), &fdShredInformPipe[1]); //start new shred task
|
||||
delete pShredTask; //delete shred task
|
||||
@ -209,6 +210,7 @@ void reHDD::ThreadDelete()
|
||||
{
|
||||
if (getSelectedDrive() != nullptr)
|
||||
{
|
||||
getSelectedDrive()->setActionStartTimestamp(); //save timestamp at start of deleting
|
||||
Delete::deleteDrive(getSelectedDrive()); //blocking, no thread
|
||||
getSelectedDrive()->state = Drive::TaskState::NONE; //delete finished
|
||||
getSelectedDrive()->bWasDeleteted = true;
|
||||
@ -289,30 +291,30 @@ void reHDD::filterNewDrives(list <Drive>* plistOldDrives, list <Drive>* plistNew
|
||||
*/
|
||||
void reHDD::searchDrives(list <Drive>* plistDrives)
|
||||
{
|
||||
// cout << "search drives ..." << endl;
|
||||
Logger::logThis()->info("--> search drives <--");
|
||||
char * cLine = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
FILE* outputfileHwinfo = popen("ls -1 /dev/sd*", "r");
|
||||
FILE* outputfileHwinfo = popen("lsblk -I 8 -d -o NAME", "r");
|
||||
|
||||
if (outputfileHwinfo == NULL)
|
||||
{
|
||||
Logger::logThis()->error("Unable to scann attached drives");
|
||||
Logger::logThis()->error("Unable to scan attached drives");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while ((getline(&cLine, &len, outputfileHwinfo)) != -1)
|
||||
{
|
||||
if (string(cLine).length() == 9)
|
||||
if (string(cLine).length() == 4)
|
||||
{
|
||||
Drive* tmpDrive = new Drive(string(cLine).substr(0, 8));
|
||||
Drive* tmpDrive = new Drive("/dev/" + string(cLine).substr(0, 3));
|
||||
tmpDrive->state = Drive::NONE;
|
||||
tmpDrive->bIsOffline = false;
|
||||
plistDrives->push_back(*tmpDrive);
|
||||
//Logger::logThis()->info("drive found: " + tmpDrive->getPath());
|
||||
}
|
||||
}
|
||||
fclose(outputfileHwinfo);
|
||||
pclose(outputfileHwinfo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,29 +324,13 @@ void reHDD::searchDrives(list <Drive>* plistDrives)
|
||||
*/
|
||||
void reHDD::filterIgnoredDrives(list <Drive>* plistDrives)
|
||||
{
|
||||
string sDelimiter = ":";
|
||||
string sIgnoredDrivePath;
|
||||
string sIgnoredDriveUUID;
|
||||
|
||||
list<tuple<string, string>> vtlIgnoredDevices; //store drives from ingnore file
|
||||
|
||||
list<tuple<string>> vtlIgnoredDevices; //store drives from ingnore file
|
||||
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
|
||||
vtlIgnoredDevices.emplace_back(sIgnoredDrivePath, sIgnoredDriveUUID); //add found path and uuid from ingnore file to vector
|
||||
}
|
||||
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
|
||||
for(auto row : vtlIgnoredDevices)
|
||||
@ -353,48 +339,39 @@ void reHDD::filterIgnoredDrives(list <Drive>* plistDrives)
|
||||
for (it = plistDrives->begin(); it != plistDrives->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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while ((getline(&cLine, &len, outputfileBlkid)) != -1) //parse UUID from blkid
|
||||
while ((getline(&cLine, &len, outputfileBlkid)) != -1) //parse UUID from blkid
|
||||
{
|
||||
if (string(cLine).find("PTUUID") != string::npos)
|
||||
{
|
||||
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;
|
||||
}
|
||||
string sBlkidOut = string(cLine);
|
||||
sBlkidOut.erase(0, 18);
|
||||
sBlkidOut.erase(8, sBlkidOut.length());
|
||||
sUUID = sBlkidOut;
|
||||
//cout << "blkid uuid:" << sUUID << endl;
|
||||
}
|
||||
fclose(outputfileBlkid);
|
||||
//cout << "blkid uuid:" << sUUID << endl;
|
||||
}
|
||||
pclose(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;
|
||||
Logger::logThis()->error("[ERROR] different uuid found than in ignore file: " + it->getPath());
|
||||
exit(EXIT_FAILURE); // exit to prevent accidentally shred a system drive
|
||||
}
|
||||
else
|
||||
{
|
||||
// same uuid found than in ignore file --> ignore this drive
|
||||
if (!get<0>(row).compare(sUUID)) //compare uuid from ignore file and uuid from drive
|
||||
{
|
||||
// same uuid found than in ignore file --> ignore this drive
|
||||
#ifdef LOG_LEVEL_HIGH
|
||||
Logger::logThis()->info("same uuid found than in ignore file --> ignore this drive: " + it->getPath());
|
||||
Logger::logThis()->info("same uuid found than in ignore file --> ignore this drive: " + it->getPath());
|
||||
#endif
|
||||
it = plistDrives->erase(it);
|
||||
it--;
|
||||
}
|
||||
it = plistDrives->erase(it);
|
||||
it--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,8 +43,9 @@ Shred::~Shred()
|
||||
*/
|
||||
void Shred::shredDrive(Drive* drive, int* ipSignalFd)
|
||||
{
|
||||
|
||||
#ifdef DRYRUN
|
||||
for(int i = 0; i<=100; i++)
|
||||
for(int i = 0; i<=500; i++)
|
||||
{
|
||||
if(drive->state != Drive::SHRED_ACTIVE)
|
||||
{
|
||||
|
@ -51,7 +51,7 @@ void SMART::readSMARTData(Drive* drive)
|
||||
SMART::parsePowerOnHours(sLine);
|
||||
SMART::parsePowerCycle(sLine);
|
||||
}
|
||||
fclose(outputfileSmart);
|
||||
pclose(outputfileSmart);
|
||||
drive->setDriveSMARTData(modelFamily, modelName, serial, capacity, errorCount, powerOnHours, powerCycle); //wirte data in drive
|
||||
}
|
||||
|
||||
|
28
src/tui.cpp
28
src/tui.cpp
@ -77,6 +77,8 @@ void TUI::updateTUI(list <Drive>* plistDrives, uint8_t u8SelectedEntry)
|
||||
string sModelName = it->getModelName();
|
||||
string sCapacity = it->sCapacityToText();
|
||||
string sState = " ";
|
||||
string sTime = " ";
|
||||
|
||||
|
||||
bool bSelectedEntry = false;
|
||||
|
||||
@ -95,16 +97,21 @@ void TUI::updateTUI(list <Drive>* plistDrives, uint8_t u8SelectedEntry)
|
||||
|
||||
stringstream stream;
|
||||
|
||||
|
||||
switch (it->state)
|
||||
{
|
||||
case Drive::SHRED_ACTIVE:
|
||||
|
||||
stream << fixed << setprecision(2) << (it->getTaskPercentage());
|
||||
sState = "Shredding: " + stream.str() + "%";
|
||||
break;
|
||||
|
||||
it->calculateTaskDuration();
|
||||
sTime = this->formatTimeDuration(it->getTaskDuration());
|
||||
break;
|
||||
case Drive::DELETE_ACTIVE:
|
||||
sState = "Deleting ...";
|
||||
it->calculateTaskDuration();
|
||||
sTime = this->formatTimeDuration(it->getTaskDuration());
|
||||
break;
|
||||
|
||||
case Drive::NONE:
|
||||
@ -117,6 +124,7 @@ void TUI::updateTUI(list <Drive>* plistDrives, uint8_t u8SelectedEntry)
|
||||
if (it->bWasShredded)
|
||||
{
|
||||
sState = "SHREDDED"; //mark drive as shreded previously, overwrite if deleted
|
||||
sTime = this->formatTimeDuration(it->getTaskDuration());
|
||||
}
|
||||
break;
|
||||
case Drive::FROZEN:
|
||||
@ -134,7 +142,7 @@ void TUI::updateTUI(list <Drive>* plistDrives, uint8_t u8SelectedEntry)
|
||||
break;
|
||||
}
|
||||
|
||||
WINDOW * tmp = createEntryWindow( ((int)(u16StdscrX/3) - 2), 5, 3, (5* (u8Index) )+3, sModelFamily, sModelName, sCapacity, sState, bSelectedEntry);
|
||||
WINDOW * tmp = createEntryWindow( ((int)(u16StdscrX/3) - 2), 5, 3, (5* (u8Index) )+3, sModelFamily, sModelName, sCapacity, sState, sTime, bSelectedEntry);
|
||||
wrefresh(tmp);
|
||||
u8Index++;
|
||||
}//end loop though drives
|
||||
@ -288,7 +296,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, bool bSelected)
|
||||
WINDOW* TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, string sModelFamily, string sModelName, string sCapacity, string sState, string sTime, bool bSelected)
|
||||
{
|
||||
WINDOW *newWindow;
|
||||
newWindow = newwin(iYSize, iXSize, iYStart, iXStart);
|
||||
@ -313,6 +321,7 @@ WINDOW* TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart,
|
||||
mvwaddstr(newWindow,3, 1, sCapacity.c_str());
|
||||
|
||||
mvwaddstr(newWindow,2, iXSize-sState.length()-5, sState.c_str());
|
||||
mvwaddstr(newWindow,3, iXSize-sState.length()-5, sTime.c_str());
|
||||
|
||||
return newWindow;
|
||||
}
|
||||
@ -439,6 +448,19 @@ WINDOW* TUI::createFrozenWarning(int iXSize, int iYSize, int iXStart, int iYStar
|
||||
return newWindow;
|
||||
}
|
||||
|
||||
string TUI::formatTimeDuration(time_t u32Duration)
|
||||
{
|
||||
std::ostringstream out;
|
||||
int dy=(int)((u32Duration)/86400);
|
||||
int hr=(int)(((u32Duration)/3600)%24);
|
||||
int min=((int)((u32Duration)/60))%60;
|
||||
int sec=(int)((u32Duration)%60);
|
||||
char s[25];
|
||||
sprintf(s, "%02d:%02d:%02d:%02d", dy, hr, min, sec);
|
||||
out << s;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
void TUI::displaySelectedDrive(Drive drive, int stdscrX, int stdscrY)
|
||||
{
|
||||
struct MenuState menustate;
|
||||
|
Reference in New Issue
Block a user