148 Commits

Author SHA1 Message Date
localhorst f0246a60d2 Improve shred throughput with adaptive chunksize (#98)
Measure Throughput
     │
     ▼
throughput > best?
     │
 ┌───┴───┐
YES     NO
 │       │
 │       ▼
 │   currentChunk == bestChunk?
 │       │
 │   ┌───┴───┐
 │  YES     NO
 │   │       │
 │   ▼       ▼
 │ throughput < best?  Nichts tun
 │   │
 │ ┌─┴─┐
 │YES NO
 │ │   │
 │ ▼   ▼
 │UPDATE!
 │BEST!
 │
 ▼
UPDATE
BOTH!

Reviewed-on: #98
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2026-05-03 10:06:14 +02:00
localhorst 55481b86fd Show HDD warnings based on sectors (#97)
If one of the following metrics is >0 an warning is shown

* Reallocated_Sector_Count
* Current_Pending_Sector
* Offline_Uncorrectable

Reviewed-on: #97
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2026-05-01 15:03:15 +02:00
localhorst 5478a871f1 Fix error handling if shred failes (#96)
fixes #95

Reviewed-on: #96
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2026-05-01 13:12:39 +02:00
localhorst 157e769268 Filter loop and cd/dvd drives (#92)
Reviewed-on: #92
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2025-12-13 14:16:24 +01:00
localhorst 8a42ccf9c0 Refactor start of delete thread (#90)
Reviewed-on: #90
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2025-12-12 22:56:28 +01:00
localhorst 1dce303ab6 Defensive drive state (#89)
fixes #79

Reviewed-on: #89
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2025-12-12 22:46:21 +01:00
localhorst 1449e807ad User triggered print of drive lable (#85)
implements #66

Reviewed-on: #85
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2025-12-12 22:32:51 +01:00
localhorst c4a960f3cf bugfix/dry-run-tui-freeze (#88)
Fixes #87

Reviewed-on: #88
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2025-12-12 22:14:47 +01:00
localhorst d749f23e77 Merge pull request 'State "Failed" if checksum is not zero' (#81) from feature/failed-state into master
Reviewed-on: #81
2025-12-10 22:30:50 +01:00
localhorst 238915bfee fix checksum alert 2025-12-10 22:29:30 +01:00
localhorst 3afe3517e2 only print if check is not failed 2025-12-10 22:05:11 +01:00
localhorst 7bb9013c6c Merge branch 'master' into feature/failed-state 2025-12-10 21:59:04 +01:00
localhorst 6060ae13d6 Merge pull request 'bugfix/ai-static-analysis' (#82) from bugfix/ai-static-analysis into master
Reviewed-on: #82
2025-12-10 21:52:17 +01:00
localhorst 9aca86af0c add call to logger 2025-12-10 21:49:44 +01:00
localhorst 26c42a7e5d fix shred bytes counters 2025-12-10 21:44:27 +01:00
localhorst cbf781f0e5 fix prod build 2025-12-10 21:37:14 +01:00
localhorst b953394c0d Merge pull request 'fix/atomic-drive-members' (#83) from fix/atomic-drive-members into bugfix/ai-static-analysis
Reviewed-on: #83
2025-12-10 21:21:15 +01:00
localhorst 4b0ec380b1 fix atomic 2025-12-10 21:15:39 +01:00
localhorst a4f15460d3 make members atomic 2025-12-10 20:40:48 +01:00
localhorst acc05dac8b Potential Division by Zero 2025-12-09 22:02:59 +01:00
localhorst c27471ae03 Unused Semicolon 2025-12-09 21:59:24 +01:00
localhorst 15014d8542 Global Using Namespace in Header 2025-12-09 21:58:57 +01:00
localhorst c9c4c0fac2 Missing Free for getline Allocated Memory 2025-12-09 21:53:39 +01:00
localhorst e5506fd67e Missing Validation of String Length 2025-12-09 21:51:47 +01:00
localhorst 2c45158ee8 Unchecked ioctl Return Value 2025-12-09 21:47:20 +01:00
localhorst 24d0eda150 Deprecated sprintf in Logger 2025-12-09 21:33:31 +01:00
localhorst 228d8ecf6c Missing Check for lseek Return Value 2025-12-09 21:31:31 +01:00
localhorst c4cbcb99b5 Signed/Unsigned Comparison 2025-12-09 21:23:02 +01:00
localhorst f9d63a6a3f Mutex Not Held During List Size Check 2025-12-09 21:21:44 +01:00
localhorst 8872902990 strcpy Without Bounds Checking 2025-12-09 21:15:48 +01:00
localhorst 7c92386082 Using sprintf Instead of snprintf 2025-12-09 21:09:25 +01:00
localhorst ba2f036d88 Uninitialized Memory in cLine 2025-12-07 20:41:09 +01:00
localhorst 43ccdbd4d8 fix terminate if no drive is detected 2025-12-07 20:39:04 +01:00
localhorst 460cfeab2c Iterator Invalidation Bug 2025-12-07 20:36:53 +01:00
localhorst 2ca0b8b061 Missing Validation of Pipe Return Value 2025-12-07 20:30:36 +01:00
localhorst c7d37b1d95 Missing Bounds Check on Array Access 2025-12-07 20:27:56 +01:00
localhorst 4afc155230 Unchecked strerror() with Invalid Errno 2025-12-07 19:11:35 +01:00
localhorst d82c45057b Missing Error Check on time() Calls 2025-12-07 19:04:00 +01:00
localhorst f303f9f032 Integer Overflow in sprintf Buffer 2025-12-07 19:01:13 +01:00
localhorst 46e8dae697 Race Condition with Shared Drive List 2025-12-07 18:59:19 +01:00
localhorst eded3f166c Revert "Unchecked popen/pclose Return Values"
This reverts commit bcabad0620.
2025-12-07 18:52:10 +01:00
localhorst bcabad0620 Unchecked popen/pclose Return Values 2025-12-07 18:48:38 +01:00
localhorst bc4083a98c Resource Leak - File Descriptor Not Checked 2025-12-07 18:47:24 +01:00
localhorst 2d63788352 Nullptr Return Without Proper Handling 2025-12-07 18:45:42 +01:00
localhorst ff6a1763e0 Memory Leak in Drive Scanning 2025-12-07 18:44:53 +01:00
localhorst 37cbc9500e Memory Leak in main.cpp 2025-12-07 18:42:03 +01:00
localhorst 298192111d new states 2025-12-07 18:36:32 +01:00
localhorst 80ffb3c6c3 Merge pull request 'Improve UUID parsing' (#78) from hannesbraun/reHDD:master into master
Reviewed-on: #78
Reviewed-by: Hendrik Schutter <localhorst@mosad.xyz>
2025-12-07 18:15:28 +01:00
localhorst ebe0ef9ec1 Merge pull request 'fix(UI): Restore terminal state on termination' (#77) from hannesbraun/reHDD:terminal-restore into master
Reviewed-on: #77
2025-12-07 18:11:02 +01:00
localhorst 86660cb112 Merge pull request 'feature/drive-type-ipc' (#80) from feature/drive-type-ipc into master
Reviewed-on: #80
2025-12-07 18:05:36 +01:00
localhorst 47ab9cc36f update Version 2025-12-07 18:04:52 +01:00
localhorst aaf4695656 IPC connection type 2025-12-07 17:51:16 +01:00
hannesbraun 664582f01d Improve UUID parsing
The previous approach only worked if the drive name had three letters (usually sda).
2025-07-25 14:26:20 +02:00
hannesbraun 9f6cfc17f8 Restore terminal state on termination 2025-07-24 18:55:49 +02:00
localhorst a5eb5532d5 Merge pull request 'feature/sata-usb-info' (#76) from feature/sata-usb-info into master
Reviewed-on: #76
2025-06-22 11:58:22 +02:00
localhorst 1efe3376c1 display empty string if connection type is not known 2025-06-22 11:57:40 +02:00
localhorst 36ea3a2bef disable dryrun 2025-06-22 11:46:09 +02:00
localhorst fd4ad95ab1 display connection type 2025-06-21 20:25:32 +02:00
localhorst d76cb1a195 get connection type 2025-06-21 20:19:30 +02:00
localhorst d8584486b9 Merge pull request 'Terminate reHDD on user input' (#75) from feature/control-menu into master
Reviewed-on: #75
2025-06-21 11:51:00 +02:00
localhorst 20d0dd8e90 reset build settings 2025-06-21 11:49:49 +02:00
localhorst 1ec4a2c793 fix shred.cpp 2025-06-21 11:46:45 +02:00
localhorst a0c842d90d add terminate action 2025-06-15 22:16:34 +02:00
localhorst 1ceffa56f8 Merge pull request 'Ignore system drives as live image' (#71) from bugfix/systemdrive_live into master
Reviewed-on: #71
2024-08-19 16:30:09 +02:00
localhorst 4ff1888333 ignore system drives as live image 2024-08-19 15:35:22 +02:00
localhorst 4b33fb6fdb update live image info 2024-08-17 16:02:58 +02:00
localhorst 87a859f187 skip delete if drive was shredded before 2024-08-16 10:29:12 +02:00
localhorst 952e8c8eeb add new flag 2024-08-16 10:29:12 +02:00
localhorst 1055ef57ee Disable delete after shred was once started. (#69)
documented here: #65

Reviewed-on: #69
Co-authored-by: localhorst <localhorst@mosad.xyz>
Co-committed-by: localhorst <localhorst@mosad.xyz>
2024-08-16 10:26:49 +02:00
localhorst 6dd4c44688 Merge pull request 'fix deletion of dialog' (#68) from bugfix/segmentationfault into master
Reviewed-on: #68
2024-06-23 11:13:53 +02:00
localhorst feed1b4a97 fix deletion of dialog 2024-06-23 11:11:20 +02:00
localhorst 5d2094939b Merge pull request 'feature/ignore_systemdrive' (#64) from feature/ignore_systemdrive into master
Reviewed-on: #64
2024-06-02 10:00:39 +02:00
localhorst b1b9870150 remove dryrun 2024-06-02 09:57:12 +02:00
localhorst 77b322d47d ignore system drive 2024-06-02 09:51:22 +02:00
localhorst a665f8638e find system drive 2024-06-02 09:31:55 +02:00
localhorst 282c294ae7 Merge pull request 'Fix S.M.A.R.T. data reading' (#63) from bugfix/smart into master
Reviewed-on: #63
2024-06-02 08:44:45 +02:00
localhorst 93c52f9a69 Revert "reuse working smartclt cmd"
This reverts commit 84a2da8bc2.
2024-06-01 15:04:06 +02:00
localhorst 84a2da8bc2 reuse working smartclt cmd 2024-06-01 14:52:50 +02:00
localhorst fe11419e37 add usb sata bridge 2024-06-01 14:37:03 +02:00
localhorst 76c728c241 fix dryrun 2024-06-01 14:23:53 +02:00
localhorst fb31becf1a get status from smartctl correct 2024-06-01 14:14:58 +02:00
localhorst 7d67f5aada Merge pull request 'Add support for nvme' (#61) from feature/nvme_support into master
Reviewed-on: #61
2024-04-25 21:08:10 +02:00
localhorst fc89618295 remove old style script 2024-04-24 22:34:02 +02:00
localhorst 70dda97ae2 autoformat all sources 2024-04-24 22:31:09 +02:00
localhorst e4a73556d6 update for next release 2024-04-24 22:16:53 +02:00
localhorst a3414ce331 update build settings 2024-04-24 22:11:22 +02:00
localhorst cb421885d0 filter drives with zero capacity 2024-04-24 22:04:52 +02:00
localhorst 0ad7de4352 support smartctl usb contoller options 2024-04-21 12:57:05 +02:00
localhorst aa7ddf8b36 find nvme with lsblk 2024-04-14 11:13:00 +02:00
localhorst 2306d34e91 update version 2024-04-14 08:42:48 +02:00
localhorst 776818c7f1 Merge pull request 'First stable version' (#58) from develop into master
Reviewed-on: #58
2024-04-14 08:38:10 +02:00
localhorst 8f57cd2a15 new beta version 2022-11-24 20:45:24 +01:00
localhorst 7e4555213e Finish dev version 2022-11-24 20:44:30 +01:00
localhorst 8938fe5047 feature/ipc_mgsqueue_printer (#57)
closes #56

Co-authored-by: localhorst <localhorst@mosad.xyz>
Reviewed-on: #57
2022-11-24 20:41:23 +01:00
localhorst 6f5e0584bf wget hint 2022-10-02 13:55:13 +02:00
localhorst 34f3ca6287 added usb image link 2022-10-02 13:49:15 +02:00
localhorst 6bd649e917 removed debug logging 2022-10-01 22:24:27 +02:00
localhorst 31caa29bd9 updated screenshot in README 2022-10-01 21:13:01 +02:00
localhorst c40dfe2cbb new screenshot 2022-10-01 21:10:45 +02:00
localhorst 3940a90ad5 TUI: removed boxes around enties 2022-10-01 14:21:08 +02:00
localhorst 75394a3501 TUI: shrunk down hight of entry 2022-10-01 13:48:45 +02:00
localhorst c4adf03bf5 TUI: fix formatting speed label 2022-10-01 13:28:40 +02:00
localhorst 18ea170881 TUI: support longer SN 2022-10-01 13:26:13 +02:00
localhorst 85fa895734 TUI: fix y offset entries 2022-10-01 13:18:21 +02:00
localhorst 743464efc1 format labels in entries 2022-10-01 12:02:01 +02:00
localhorst 685e359217 TUI: changed window element layout 2022-09-30 22:26:54 +02:00
localhorst 70bc8cffb4 two lines per entry 2022-09-30 21:20:23 +02:00
localhorst b73e1765b5 separate shred and check process 2022-09-27 21:58:59 +02:00
localhorst 392fe67aa2 updated readme with new screenshot; updated update script 2022-09-27 18:39:21 +02:00
localhorst aa13cde853 updated readme, version and update script 2022-09-27 18:31:45 +02:00
localhorst 573a0df080 Merge pull request 'version 0.3.0' (#52) from develop into master
Reviewed-on: #52
2022-09-20 21:56:04 +02:00
localhorst 1f50f87f97 update version and readme 2022-09-20 21:53:43 +02:00
localhorst 17cc41dc5f display human readable timestamp 2022-08-25 12:07:57 +02:00
localhorst 02c9ab99f7 display human readable timestamp 2022-08-25 12:07:18 +02:00
localhorst 7d69096df3 Revert "decrease shred iterations"
This reverts commit 7dfa805044.
2022-08-25 09:18:12 +02:00
localhorst 1b9fa348d3 display software build time 2022-08-24 16:35:12 +02:00
localhorst a347bf433c display temperature alert if drive too hot 2022-08-24 16:27:51 +02:00
localhorst 69fd10207d copy newer S.M.A.R.T. values to existing drive 2022-08-24 16:11:36 +02:00
localhorst 2df5ceb0c8 read temperature via S.M.A.R.T and display 2022-08-24 16:00:18 +02:00
localhorst 7dfa805044 decrease shred iterations 2022-08-24 14:27:43 +02:00
localhorst bb69a59794 fix frozen and metric for checking iteration 2022-08-23 23:20:50 +02:00
localhorst a656d0a9f4 UI: display drive count with index 2022-08-23 00:03:45 +02:00
localhorst 50bd896cb9 display SN in overview for entry 2022-08-22 23:42:20 +02:00
localhorst 3ee59d8c58 Merge pull request 'speedup shred' (#46) from speedup into develop
Reviewed-on: #46
2022-08-22 23:34:15 +02:00
localhorst d92448aa97 optimal chunk size 2022-08-22 23:09:41 +02:00
localhorst edcf680b95 update shred metric in main thread instead of shred thread 2022-08-22 18:35:27 +02:00
localhorst 09446b52ca using tfng instead of urandom 2022-08-22 15:27:29 +02:00
localhorst 4cf1efea7a updated readme for submodules 2022-08-22 14:36:52 +02:00
localhorst f0f1e4fd93 remove false git submodule 2022-08-22 14:27:42 +02:00
localhorst d7aaa9647d added Threefish cipher as submodule 2022-08-22 14:24:31 +02:00
localhorst 4862a45ef6 remove unnecessary mutesx 2022-08-21 16:24:55 +02:00
localhorst 70f5727eb3 protect lists with mutex 2022-08-21 15:52:47 +02:00
localhorst b6f0c5e89f fix missing include 2022-08-20 16:11:48 +02:00
localhorst e3aefb24ee added feature to start shredding for all drives 2022-08-20 16:09:40 +02:00
localhorst 9863c5591e cal metric: shred speed 2022-06-30 00:32:13 +02:00
localhorst c61859ed4e added shred speed to TUI 2022-06-29 19:27:37 +02:00
localhorst 8de45505e4 fix update script 2022-06-29 19:08:43 +02:00
localhorst f8ba9c6732 Merge branch 'master' into develop 2022-05-18 22:03:14 +02:00
localhorst c01797be7f Merge branch 'develop' 2022-05-18 22:02:47 +02:00
localhorst 0684744bdb update docs 2022-05-17 09:16:58 +02:00
localhorst 8c54e0e7d9 updated REDME 2022-01-26 19:38:16 +01:00
localhorst 46a2de7bc4 Merge pull request 'change drive filter for system drive' (#36) from develop into master
Reviewed-on: #36
2022-01-26 19:34:37 +01:00
localhorst 52f5597ba9 updated service 2022-01-26 17:32:21 +01:00
localhorst 9b9806b5c2 „README.md“ ändern 2022-01-26 17:18:12 +01:00
localhorst 1e455bde02 added curl 2022-01-26 16:52:45 +01:00
localhorst 8034ac496b Merge pull request 'adopt newer debian' (#35) from develop into master
Reviewed-on: #35
2022-01-26 16:41:56 +01:00
localhorst 354ee04e73 „README.md“ ändern 2022-01-26 16:40:20 +01:00
localhorst 45a5cb303b upload log to dev 2020-10-01 17:52:46 +02:00
33 changed files with 3149 additions and 1438 deletions
+4 -1
View File
@@ -41,7 +41,10 @@
reHDD reHDD
reHDD.log *.log
*.ods
*.txt
.vscode/
ignoreDrives.conf ignoreDrives.conf
+3
View File
@@ -0,0 +1,3 @@
[submodule "tfnoisegen"]
path = tfnoisegen
url = https://git.mosad.xyz/localhorst/tfnoisegen.git
+30 -15
View File
@@ -1,34 +1,45 @@
# reHDD # reHDD
## Useful for: ## Features:
* checking new drives for the first time * show S.M.A.R.T values of attached drives
* checking used drives for their next live * checking used drives for their next live based on threshold limits
* delete a drive instant with wipefs
* deleting a drive securely via overwriting * deleting a drive securely via overwriting
* only needs a display and keyboard
* process multiple drives at once
## Download USB Image ##
See reHDD-Bootable how the live image is created: https://git.mosad.xyz/localhorst/reHDD-Bootable
Use [Etcher](https://www.balena.io/etcher/#download) or `dd` to create an bootable USB drive .
## Screenshot ## Screenshot
![alt text](https://git.mosad.xyz/localhorst/reHDD/raw/commit/42bc26eac95429e20c0f0d59f684dfec0d600e75/doc/screenshot.png "Screenshot") ![Screenshot of reHDD with multiple drives in different states](https://git.mosad.xyz/localhorst/reHDD/raw/commit/c40dfe2cbb8f86490b49caf82db70a10015f06f9/doc/screenshot.png "Screenshot")
## Debian Build Notes ## openSUSE Build Notes
* `apt-get install ncurses-dev git make g++` * `zypper install ncurses-devel git make gcc-c++`
* `git submodule init`
* `git submodule update`
* `make release` * `make release`
## Create Standalone with Debian 11 ## Enable Label Printer ##
Instructions how to create a standalone machine that boots directly to reHDD. This is aimed for production use, like several drives a day shredding. Just install [reHDDPrinter](https://git.mosad.xyz/localhorst/reHDDPrinter).
* Start reHDD after boot without login (as a tty1 shell) No further settings needed.
* Start dmesg after boot without login (as a tty2 shell)
* Start htop after boot without login (as a tty3 shell)
* Upload reHDD log every 12h if wanted
### Software requirements ### Software requirements
* `zypper install hwinfo smartmontools curl htop sudo`
* `apt-get install hwinfo smartmontools curl`
### Installation ### Installation
clone this repo into /root/ clone this repo into /root/
```
git submodule init
git submodule update
```
`cd /root/reHDD/` `cd /root/reHDD/`
`make release` `make release`
@@ -37,9 +48,13 @@ clone this repo into /root/
If you want to upload the logs, edit `scripts/reHDDLogUploader.bash` with your nextcloud token If you want to upload the logs, edit `scripts/reHDDLogUploader.bash` with your nextcloud token
Add your system drive in `/root/reHDD/ignoreDrives.conf` like: Add ignored drives in `/root/reHDD/ignoreDrives.conf` like:
```e102f49d``` ```e102f49d```
Get the first 8 Bytes from your UUID via `blkid /dev/sdX` Get the first 8 Bytes from your UUID via `blkid /dev/sdX`
`reboot` `reboot`
## Build docs
`make docs`
open `doc/html/index.html` in browser
-17
View File
@@ -1,17 +0,0 @@
#! /bin/bash
echo starting astyle for $PWD
astyle --style=gnu src/*.cpp
rm -f src/*.orig
astyle --style=gnu src/logger/*.cpp
rm -f src/logger/*.orig
astyle --style=gnu include/*.h
rm -f include/*.orig
astyle --style=gnu include/logger/*.h
rm -f include//logger/*.orig
echo finished astyle for $PWD
+8 -8
View File
@@ -467,7 +467,7 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES. # normally produced when WARNINGS is set to YES.
# The default value is: NO. # The default value is: NO.
EXTRACT_ALL = NO EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation. # be included in the documentation.
@@ -904,7 +904,7 @@ FILE_PATTERNS = *.c \
# be searched for input files as well. # be searched for input files as well.
# The default value is: NO. # The default value is: NO.
RECURSIVE = NO RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be # The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a # excluded from the INPUT source files. This way you can easily exclude a
@@ -1493,7 +1493,7 @@ ECLIPSE_DOC_ID = org.doxygen.Project
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO DISABLE_INDEX = YES
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag # structure should be generated to display hierarchical information. If the tag
@@ -1510,7 +1510,7 @@ DISABLE_INDEX = NO
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO GENERATE_TREEVIEW = YES
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation. # doxygen will group on one line in the generated HTML documentation.
@@ -2260,7 +2260,7 @@ HIDE_UNDOC_RELATIONS = YES
# set to NO # set to NO
# The default value is: NO. # The default value is: NO.
HAVE_DOT = NO HAVE_DOT = YES
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of # to run in parallel. When set to 0 doxygen will base this on the number of
@@ -2326,7 +2326,7 @@ GROUP_GRAPHS = YES
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO UML_LOOK = YES
# If the UML_LOOK tag is enabled, the fields and methods are shown inside the # If the UML_LOOK tag is enabled, the fields and methods are shown inside the
# class node. If there are many fields or methods and many nodes the graph may # class node. If there are many fields or methods and many nodes the graph may
@@ -2377,7 +2377,7 @@ INCLUDED_BY_GRAPH = YES
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO CALL_GRAPH = YES
# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
# dependency graph for every global function or class method. # dependency graph for every global function or class method.
@@ -2389,7 +2389,7 @@ CALL_GRAPH = NO
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO CALLER_GRAPH = YES
# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
# hierarchy of all classes instead of a textual one. # hierarchy of all classes instead of a textual one.
-60
View File
@@ -1,60 +0,0 @@
# Installation
**[1]** In terminal als root einloggen\
**[2]** In reHDD_prototype wechseln\
**[3]** Abhängigkeiten installieren\
`apt-get install smartmontools`\
`apt-get install hwinfo`\
`apt-get install util-linux`
**[4]** reHDD ausführbar machen\
`chmod +x reHDD`\
**[5]** ignoreDrives.conf bearbeiten
##### Ein Eintrag in der ignoreDrives-Datei sorgt dafür das die Software bestimmte Festplatten ignoriert. Dies ist wichtig damit nicht unbeabsichtigt die Systemfestplatte oder weitere Festplatten bearbeitet (gelöscht) werden.
Beispiel Inhalt:
```
/dev/sda:508eff7d-f039-4efb-9e2c-22dffdfdfdfd
/dev/sdb:07dfffff-c4b6-46e7-9cdf-3cfdfdffd53d
/dev/sdc:dfff974d-1af2-4dfd-9dfd-a2d8e4c43dff
```
Ein Eintrag setzt sich aus zwei Teilen zusammen:
**[Pfad]:[PARTUUID]**
Der Pfad kann mittels `fdisk -l` ermittelt werden.\
Die PARTUUID kann mittels `blkid /dev/sda` ermittelt werden, wobei `/dev/sda` derzuvor ermittelte Pfad ist.
# Benutzung
reHDD starten mit `./reHDD` (Wichtig ist das reHDD mit root-Rechen ausgeführt wird, entwender als root einloggen oder mit `sudo`)
reHDD sucht automatisch nach allen verfügbaren Festplatten und filtert die zu ignorierenden heraus.\
Für die verbleibenden Festplatten wird eine Übersicht ausgegeben.
Der Nutzer wird gefragt, welche Festplatte bearbeitet werden soll und gibt die Nummer ein.
Beispiel:
```
hostname@hendrik:/reHDD_prototype # ./reHDD
refurbishingHddTool
created app
app logic
search drives ...
------------DRIVES---------------
Drive: 0
Path: /dev/sdd
ModelFamily:
ModelName: ADATA SU650
Capacity: 120034123776
Serial: H50125K001601
PowerOnHours: 93
PowerCycle: 187
ErrorCount: 0
---------------------------------
Select drive to wipe:
0
Selected drive index: 0
wipe: shred -v /dev/sdd
shred: /dev/sdd: Durchgang 1/3 (random)…
shred: /dev/sdd: Durchgang 1/3 (random)…847MiB/112GiB 0%
...
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 92 KiB

-26
View File
@@ -1,26 +0,0 @@
#! /usr/bin/bash
echo starting update
FILE=./ignoreDrives.conf
if test -f "$FILE"; then
echo backup exits
else
cp reHDD/ignoreDrives.conf ./ignoreDrives.conf
fi
cd reHDD
git reset
git stash force
git stash
git checkout master
git pull
make release
cp ../ignoreDrives.conf ./
+1
View File
@@ -1,2 +1,3 @@
4673974d 4673974d
2cb3dea4 2cb3dea4
8ffbc421
+2 -4
View File
@@ -13,13 +13,11 @@
class Delete class Delete
{ {
protected: protected:
public: public:
static void deleteDrive(Drive* drive); static void deleteDrive(Drive *drive);
private: private:
Delete(void); Delete(void);
}; };
#endif // DELETE_H_ #endif // DELETE_H_
+99 -43
View File
@@ -14,66 +14,123 @@ class Drive
{ {
public: public:
enum TaskState {NONE, enum class TaskState
SHRED_SELECTED, {
SHRED_ACTIVE, NONE,
DELETE_SELECTED, SHRED_SELECTED,
DELETE_ACTIVE, SHRED_ACTIVE, // shred iterations active
FROZEN CHECK_ACTIVE, // optional checking active
} state; CHECK_SUCCESSFUL,
CHECK_FAILED,
DELETE_SELECTED,
DELETE_ACTIVE,
FROZEN
};
bool bWasShredded = false; enum class ConnectionType
bool bWasDeleteted = false; {
UNKNOWN,
USB,
SATA,
NVME
};
struct ShredSpeed
{
time_t u32ShredTimeDelta;
std::chrono::time_point<std::chrono::system_clock>
chronoShredTimestamp;
unsigned long ulWrittenBytes;
unsigned long ulSpeedMetricBytesWritten;
};
std::atomic<TaskState> state;
std::atomic<ConnectionType> connectionType;
std::atomic<ShredSpeed> sShredSpeed;
bool bWasShredded = false; // all shred iterations done
bool bWasShredStarted = false; // shred was atleast once started
bool bWasChecked = false; // all shred iterations and optional checking done
bool bWasDeleted = false;
bool bIsOffline = false; bool bIsOffline = false;
uint32_t u32DriveChecksumAferShredding = 0U; uint32_t u32DriveChecksumAfterShredding = 0U;
uint16_t u16DriveIndex = 0U; // Index of TUI list
private: private:
string sPath; std::string sPath;
string sModelFamily; time_t u32Timestamp = 0U; // unix timestamp for detecting a frozen drive
string sModelName; double d32TaskPercentage = 0U; // in percent for Shred (1 to 100)
string sSerial; time_t u32TimestampTaskStart = 0U; // unix timestamp for duration of an action
uint64_t u64Capacity = 0U; //in byte time_t u32TaskDuration = 0U; // time needed to complete the task
uint32_t u32ErrorCount = 0U;
uint32_t u32PowerOnHours = 0U; //in hours struct
uint32_t u32PowerCycles = 0U; {
time_t u32Timestamp = 0U; //unix timestamp for detecting a frozen drive std::string sModelFamily;
double d32TaskPercentage = 0U; //in percent for Shred (1 to 100) std::string sModelName;
time_t u32TimestampTaskStart = 0U; //unix timestamp for duration of an action std::string sSerial;
time_t u32TaskDuration = 0U; //time needed to complete the task 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
uint32_t u32ReallocatedSectors = 0U; // ID 0x05 - Reallocated Sectors Count
uint32_t u32PendingSectors = 0U; // ID 0xC5 - Current Pending Sector Count
uint32_t u32UncorrectableSectors = 0U; // ID 0xC6 - Offline Uncorrectable Sector Count
} sSmartData;
private: private:
void setTimestamp(); void setTimestamp();
protected: protected:
public: public:
Drive(string path) // Copy constructor
Drive(const Drive &other);
// Copy assignment operator
Drive &operator=(const Drive &other);
// Move constructor
Drive(Drive &&other) noexcept;
// Move assignment operator
Drive &operator=(Drive &&other) noexcept;
Drive(std::string path)
{ {
this->sPath = path; this->sPath = path;
} }
string getPath(void); std::string getPath(void);
string getModelFamily(void); std::string getModelFamily(void);
string getModelName(void); std::string getModelName(void);
string getSerial(void); std::string getSerial(void);
uint64_t getCapacity(void); //in byte uint64_t getCapacity(void); // in byte
uint32_t getErrorCount(void); uint32_t getErrorCount(void);
uint32_t getPowerOnHours(void); //in hours uint32_t getPowerOnHours(void); // in hours
uint32_t getPowerCycles(void); uint32_t getPowerCycles(void);
uint32_t getTemperature(void); // in Fahrenheit, just kidding: degree Celsius
uint32_t getReallocatedSectors(void);
uint32_t getPendingSectors(void);
uint32_t getUncorrectableSectors(void);
void checkFrozenDrive(void); void checkFrozenDrive(void);
void setDriveSMARTData( string modelFamily, void setDriveSMARTData(std::string modelFamily,
string modelName, std::string modelName,
string serial, std::string serial,
uint64_t capacity, uint64_t capacity,
uint32_t errorCount, uint32_t errorCount,
uint32_t powerOnHours, uint32_t powerOnHours,
uint32_t powerCycles); uint32_t powerCycles,
uint32_t temperature,
uint32_t reallocatedSectors,
uint32_t pendingSectors,
uint32_t uncorrectableSectors);
string sCapacityToText(); std::string sCapacityToText();
string sErrorCountToText(); std::string sErrorCountToText();
string sPowerOnHoursToText(); std::string sPowerOnHoursToText();
string sPowerCyclesToText(); std::string sPowerCyclesToText();
std::string sTemperatureToText();
void setTaskPercentage(double d32TaskPercentage); void setTaskPercentage(double d32TaskPercentage);
double getTaskPercentage(void); double getTaskPercentage(void);
@@ -83,7 +140,6 @@ public:
void calculateTaskDuration(); void calculateTaskDuration();
time_t getTaskDuration(); time_t getTaskDuration();
}; };
#endif // DRIVE_H_ #endif // DRIVE_H_
+12 -9
View File
@@ -16,6 +16,10 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <ifaddrs.h>
#include <netpacket/packet.h>
#include <cstring>
#include <string>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@@ -28,26 +32,26 @@
using namespace std; using namespace std;
#define MENU_LINE_SIZE 110 //Size of menu lines #define MENU_LINE_SIZE 110 // Size of menu lines
#ifndef LOG_PATH #ifndef LOG_PATH
//#define LOG_PATH "./test.txt" // #define LOG_PATH "./test.txt"
#endif #endif
#ifndef DESCRIPTION #ifndef DESCRIPTION
#define DESCRIPTION "Software-Name - Copyright Company 2020" //use your values here #define DESCRIPTION "Software-Name - Copyright Company 2020" // use your values here
#endif #endif
#ifndef DEVICE_ID #ifndef DEVICE_ID
#define DEVICE_ID "Device-Name" //use your values here #define DEVICE_ID "Device-Name" // use your values here
#endif #endif
#ifndef SOFTWARE_VERSION #ifndef SOFTWARE_VERSION
#define SOFTWARE_VERSION "0.1.1.8" //use your values here #define SOFTWARE_VERSION "0.1.1.8" // use your values here
#endif #endif
#ifndef HARDWARE_VERSION #ifndef HARDWARE_VERSION
#define HARDWARE_VERSION "7.77.9" //use your values here #define HARDWARE_VERSION "7.77.9" // use your values here
#endif #endif
class Logger class Logger
@@ -68,13 +72,12 @@ private:
~Logger(); ~Logger();
public: public:
void info(string s); void info(string s);
void warning(string s); void warning(string s);
void error(string s); void error(string s);
void newLine(); void newLine();
static Logger* logThis(); static Logger *logThis();
}; };
#endif // LOGGER_H_ #endif // LOGGER_H_
+56
View File
@@ -0,0 +1,56 @@
/**
* @file printer.h
* @brief Send drive data to printer service using ipc msg queue
* @author Hendrik Schutter
* @date 24.11.2022
*/
#ifndef PRINTER_H_
#define PRINTER_H_
#include "reHDD.h"
#include <sys/ipc.h>
#include <sys/msg.h>
#define STR_BUFFER_SIZE 64U
#define IPC_MSG_QUEUE_KEY 0x1B11193C0
typedef struct
{
char caDriveIndex[STR_BUFFER_SIZE];
char caDriveHours[STR_BUFFER_SIZE];
char caDriveCycles[STR_BUFFER_SIZE];
char caDriveErrors[STR_BUFFER_SIZE];
char caDriveShredTimestamp[STR_BUFFER_SIZE];
char caDriveShredDuration[STR_BUFFER_SIZE];
char caDriveCapacity[STR_BUFFER_SIZE];
char caDriveState[STR_BUFFER_SIZE];
char caDriveConnectionType[STR_BUFFER_SIZE];
char caDriveModelFamily[STR_BUFFER_SIZE];
char caDriveModelName[STR_BUFFER_SIZE];
char caDriveSerialnumber[STR_BUFFER_SIZE];
char caDriveReHddVersion[STR_BUFFER_SIZE];
} t_driveData;
typedef struct
{
long msg_queue_type;
t_driveData driveData;
} t_msgQueueData;
class Printer
{
protected:
public:
static Printer *getPrinter();
void print(Drive *drive);
private:
static bool instanceFlag;
static Printer *single;
int msqid;
Printer();
~Printer();
};
#endif // PRINTER_H_
+38 -31
View File
@@ -8,32 +8,34 @@
#ifndef REHDD_H_ #ifndef REHDD_H_
#define REHDD_H_ #define REHDD_H_
#define REHDD_VERSION "bV0.2.2" #define REHDD_VERSION "V1.4.0"
// Drive handling Settings // Drive handling Settings
#define WORSE_HOURS 19200 //mark drive if at this limit or beyond #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_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 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 // Logger Settings
#define LOG_PATH "./reHDD.log" #define LOG_PATH "./reHDD.log"
#define DESCRIPTION "reHDD - Copyright Hendrik Schutter 2022" #define DESCRIPTION "reHDD - Copyright Hendrik Schutter 2026"
#define DEVICE_ID "generic" #define DEVICE_ID "generic"
#define SOFTWARE_VERSION "alpha" #define SOFTWARE_VERSION REHDD_VERSION
#define HARDWARE_VERSION "generic" #define HARDWARE_VERSION "generic"
//#define LOG_LEVEL_HIGH //log everything, like drive scan thread // #define LOG_LEVEL_HIGH // log everything, like drive scan thread
#ifndef LOG_LEVEL_HIGH #ifndef LOG_LEVEL_HIGH
#define LOG_LEVEL_LOW //log only user actions and tasks #define LOG_LEVEL_LOW // log only user actions and tasks
#endif #endif
// Logic // Logic
//#define DRYRUN //don´t touch the drives // #define DRYRUN // don't touch the drives
#define FROZEN_ALERT //show alert if drive is frozen #define FROZEN_ALERT // show alert if drive is frozen
#define ZERO_CHECK_ALERT //check drive after shred if all bytes are zero, show alert if this fails #define ZERO_CHECK // check drive after shred if all bytes are zero, show alert if this fails
//IPC pipes // IPC pipes
#define READ 0 #define READ 0
#define WRITE 1 #define WRITE 1
@@ -54,19 +56,19 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <signal.h> #include <signal.h>
#include <atomic>
using namespace std;
#include "drive.h" #include "drive.h"
#include "smart.h" #include "smart.h"
#include "shred.h" #include "shred.h"
#include "delete.h" #include "delete.h"
#include "tui.h" #include "tui.h"
#include "printer.h"
#include "logger/logger.h" #include "logger/logger.h"
extern Logger* logging; extern Logger *logging;
template <typename T, typename I> T* iterator_to_pointer(I i) template <typename T, typename I>
T *iterator_to_pointer(I i)
{ {
return (&(*i)); return (&(*i));
} }
@@ -74,28 +76,33 @@ template <typename T, typename I> T* iterator_to_pointer(I i)
class reHDD class reHDD
{ {
protected: protected:
public: public:
reHDD(void); reHDD(void);
static void app_logic(); static void app_logic();
private: private:
static void searchDrives(list<Drive> *plistDrives);
static void searchDrives(list <Drive>* plistDrives); static void printDrives(list<Drive> *plistDrives);
static void printDrives(list <Drive>* plistDrives); static void startShredAllDrives(list<Drive> *plistDrives);
static void filterIgnoredDrives(list <Drive>* plistDrives); static void stopShredAllDrives(list<Drive> *plistDrives);
static void filterNewDrives(list <Drive>* plistOldDrives, list <Drive>* plistNewDrives); static void updateShredMetrics(list<Drive> *plistDrives);
static void addSMARTData(list <Drive>* plistDrives); static void filterIgnoredDrives(list<Drive> *plistDrives);
static void ThreadScannDevices(); static void filterInvalidDrives(list<Drive> *plistDrives);
static void filterNewDrives(list<Drive> *plistOldDrives, list<Drive> *plistNewDrives);
static void addSMARTData(list<Drive> *plistDrives);
static void printAllDrives(list<Drive> *plistDrives);
static void printDrive(Drive *const pDrive);
static void ThreadScanDevices();
static void ThreadUserInput(); static void ThreadUserInput();
static void ThreadShred(); static void ThreadShred(Drive *const pDrive);
static void ThreadDelete(); static void ThreadDelete(Drive *const pDrive);
static void ThreadCheckFrozenDrives(); static void ThreadCheckFrozenDrives();
static void handleArrowKey(TUI::UserInput userInput); static void handleArrowKey(TUI::UserInput userInput);
static void handleEnter(); static void handleEnter();
static void handleESC(); static void handleESC();
static void handleAbort(); static void handleAbort();
static Drive* getSelectedDrive(); static Drive *getSelectedDrive();
static bool getSystemDrive(string &systemDrive);
}; };
#endif // REHDD_H_ #endif // REHDD_H_
+57 -14
View File
@@ -16,41 +16,84 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <chrono>
#define CHUNK_SIZE 1024*1024*2 //amount of bytes that are overwritten at once --> 2MB // Adaptive chunk size optimization with multi-armed bandit - always enabled
#define CHUNK_DIMENSION 100U //amount of chunks are read at once from random source // Chunk size configuration
#define CHUNK_SIZE_START 1024 * 1024 * 32 // Starting chunk size: 32MB
#define CHUNK_SIZE_MIN 1024 * 1024 * 16 // Minimum chunk size: 16MB (increased from 4MB to prevent premature convergence)
#define CHUNK_SIZE_MAX 1024 * 1024 * 128 // Maximum chunk size: 128MB
#define CHUNK_SIZE_STEP_UP 1024 * 1024 * 4 // Increase step: 4MB (symmetric with step down)
#define CHUNK_SIZE_STEP_DOWN 1024 * 1024 * 4 // Decrease step: 4MB (symmetric exploration)
#define CHUNK_MEASURE_INTERVAL 64 // Measure performance every 64 chunks
#define WARMUP_MEASUREMENTS 16 // Skip first 16 measurements (cache writes)
//#define DEMO_DRIVE_SIZE 1024*1024*256L // 256MB // Multi-armed bandit exploration parameters
//#define DEMO_DRIVE_SIZE 1024*1024*1024L // 1GB #define EXPLORATION_EPSILON 0.10 // 10% exploration rate (epsilon-greedy)
//#define DEMO_DRIVE_SIZE 1024*1024*1024*10L // 10GB #define REEXPLORATION_INTERVAL 500 // Force re-exploration every 500 chunks
// Buffer sizes - always use maximum for adaptive mode
#define CHUNK_SIZE CHUNK_SIZE_MAX
#define TFNG_DATA_SIZE CHUNK_SIZE_MAX
// #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; typedef int fileDescriptor;
class Shred class Shred
{ {
protected: protected:
public: public:
Shred(); Shred();
~Shred(); ~Shred();
int shredDrive(Drive* drive, int* ipSignalFd); int shredDrive(Drive *drive, int *ipSignalFd);
private: private:
fileDescriptor randomSrcFileDiscr; fileDescriptor randomSrcFileDiscr;
fileDescriptor driveFileDiscr; fileDescriptor driveFileDiscr;
unsigned char caChunk[CHUNK_DIMENSION][CHUNK_SIZE];
unsigned char *caTfngData;
unsigned char *caReadBuffer;
unsigned long ulDriveByteSize; unsigned long ulDriveByteSize;
unsigned long ulDriveByteOverallCount = 0; //all bytes shredded in all iterations + checking -> used for progress calculation unsigned long ulDriveByteOverallCount = 0; // all bytes shredded in all iterations + checking -> used for progress calculation
double d32Percent = 0.0; double d32Percent = 0.0;
double d32TmpPercent = 0.0; double d32TmpPercent = 0.0;
// Adaptive chunk size optimization members
size_t currentChunkSize;
size_t bestChunkSize;
unsigned int chunkCounter;
unsigned int totalChunkCounter; // Total chunks written (for periodic re-exploration)
unsigned int warmupCounter; // Count warm-up measurements
std::chrono::high_resolution_clock::time_point measurementStartTime;
double bestThroughputMBps;
double lastThroughputMBps;
unsigned long bytesWrittenInMeasurement;
bool throughputIncreasing;
// Multi-armed bandit exploration state
bool explorationMode; // Currently in exploration mode?
size_t explorationChunkSize; // Chunk size being tested during exploration
// Adaptive methods
void startMeasurement();
void evaluateThroughput(Drive *drive);
void adjustChunkSize(Drive *drive);
size_t getCurrentChunkSize() const;
// Multi-armed bandit methods
bool shouldExplore(); // Decide: explore or exploit?
void performExploration(Drive *drive); // Execute exploration phase
inline double calcProgress(); inline double calcProgress();
int iRewindDrive(fileDescriptor file); int iRewindDrive(fileDescriptor file);
unsigned long getDriveSizeInBytes(fileDescriptor file); long getDriveSizeInBytes(fileDescriptor file);
unsigned int uiCalcChecksum(fileDescriptor file, Drive* drive, int* ipSignalFd); unsigned int uiCalcChecksum(fileDescriptor file, Drive *drive, int *ipSignalFd);
void cleanup(); void cleanup();
}; };
#endif // SHRED_H_ #endif // SHRED_H_
+18 -20
View File
@@ -10,31 +10,29 @@
#include "reHDD.h" #include "reHDD.h"
/**
* @brief SMART data reader for drives
*
* Parses smartctl JSON output to extract:
* - Device information (model, serial, capacity)
* - Power statistics (hours, cycles)
* - Temperature
* - Critical sector counts (reallocated, pending, uncorrectable)
*
* Uses deterministic state machine parser for reliable multi-line JSON parsing.
*/
class SMART class SMART
{ {
protected: protected:
public: public:
static void readSMARTData(Drive* drive); /**
* @brief Read S.M.A.R.T. data from drive and populate Drive object
* @param drive Pointer to Drive instance to populate with SMART data
*/
static void readSMARTData(Drive *drive);
private: private:
SMART(void); SMART(void); // Utility class - no instances
static void parseModelFamily(string sLine);
static void parseModelName(string sLine);
static void parseSerial(string sLine);
static void parseCapacity(string sLine);
static void parseErrorCount(string sLine);
static void parsePowerOnHours(string sLine);
static void parsePowerCycle(string sLine);
static string modelFamily;
static string modelName;
static string serial;
static uint64_t capacity;
static uint32_t errorCount;
static uint32_t powerOnHours;
static uint32_t powerCycle;
}; };
#endif // SMART_H_ #endif // SMART_H_
+45 -29
View File
@@ -12,17 +12,30 @@
#define COLOR_AREA_STDSCR 1 #define COLOR_AREA_STDSCR 1
#define COLOR_AREA_OVERVIEW 2 #define COLOR_AREA_OVERVIEW 2
#define COLOR_AREA_ENTRY 3 #define COLOR_AREA_ENTRY_EVEN 3
#define COLOR_AREA_ENTRY_SELECTED 4 #define COLOR_AREA_ENTRY_ODD 4
#define COLOR_AREA_DETAIL 5 #define COLOR_AREA_ENTRY_SELECTED 5
#define COLOR_AREA_DETAIL 6
class TUI class TUI
{ {
protected: protected:
public: public:
enum UserInput
enum UserInput { UpKey, DownKey, Abort, Shred, Delete, Enter, ESC, Undefined}; {
UpKey,
DownKey,
Abort,
Shred,
ShredAll,
Delete,
Enter,
ESC,
Terminate,
Print,
PrintAll,
Undefined
};
struct MenuState struct MenuState
{ {
bool bAbort; bool bAbort;
@@ -36,36 +49,39 @@ public:
static void initTUI(); static void initTUI();
void updateTUI(list <Drive>* plistDrives, uint8_t u8SelectedEntry); void updateTUI(std::list<Drive> *plistDrives, uint8_t u8SelectedEntry);
static enum UserInput readUserInput(); static enum UserInput readUserInput();
static void terminateTUI();
private: private:
static string sCpuUsage; static std::string sCpuUsage;
static string sRamUsage; static std::string sRamUsage;
static string sLocalTime; static std::string sLocalTime;
WINDOW* overview; WINDOW *overview;
WINDOW* systemview; WINDOW *systemview;
WINDOW* detailview; WINDOW *detailview;
WINDOW* menuview; WINDOW *menuview;
WINDOW* dialog; WINDOW *dialog;
WINDOW* smartWarning; WINDOW *smartWarning;
static void centerTitle(WINDOW *pwin, const char * title); static void centerTitle(WINDOW *pwin, const char *title);
static WINDOW *createOverViewWindow( int iXSize, int iYSize); static WINDOW *createOverViewWindow(int iXSize, int iYSize);
static WINDOW *createDetailViewWindow( int iXSize, int iYSize, int iXStart, Drive drive); static WINDOW *createDetailViewWindow(int iXSize, int iYSize, int iXStart, Drive &drive);
static WINDOW *overwriteDetailViewWindow( int iXSize, int iYSize, int iXStart); 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, std::string sModelFamily, std::string sSerial, std::string sCapacity, std::string sState, std::string sTime, std::string sSpeed, std::string sTemp, std::string sConnection, bool bSelected);
static WINDOW *createSystemStats(int iXSize, int iYSize, int iXStart, int iYStart); 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 *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 *createDialog(int iXSize, int iYSize, int iXStart, int iYStart, std::string selectedTask, std::string optionA, std::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 *createFrozenWarning(int iXSize, int iYSize, int iXStart, int iYStart, std::string sPath, std::string sModelFamily, std::string sModelName, std::string sSerial, std::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, std::string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount, uint32_t u32Temperature, uint32_t u32ReallocatedSectors, uint32_t u32PendingSectors, uint32_t u32UncorrectableSectors);
static WINDOW* createZeroChecksumWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, string sModelFamily, string sModelName, string sSerial, uint32_t u32Checksum); static WINDOW *createZeroChecksumWarning(int iXSize, int iYSize, int iXStart, int iYStart, std::string sPath, std::string sModelFamily, std::string sModelName, std::string sSerial, uint32_t u32Checksum);
void displaySelectedDrive(Drive drive, int stdscrX, int stdscrY);
string formatTimeDuration(time_t u32Duration);
void displaySelectedDrive(Drive &drive, int stdscrX, int stdscrY);
std::string formatTimeDuration(time_t u32Duration);
std::string formatSpeed(time_t u32ShredTimeDelta, unsigned long ulWrittenBytes);
static void vTruncateText(std::string *psText, uint16_t u16MaxLenght);
}; };
#endif // TUI_H_ #endif // TUI_H_
+14 -4
View File
@@ -8,19 +8,22 @@ SRC_EXT = cpp
# Path to the source directory, relative to the makefile # Path to the source directory, relative to the makefile
SRC_PATH = src SRC_PATH = src
# Space-separated pkg-config libraries used by this project # Space-separated pkg-config libraries used by this project
LIBS = LIBS = lib
# General compiler flags # General compiler flags
COMPILE_FLAGS = -std=c++17 -Wall -Wextra -g COMPILE_FLAGS = -std=c++23 -Wall -Wextra -g
# Additional release-specific flags # Additional release-specific flags
RCOMPILE_FLAGS = -D NDEBUG RCOMPILE_FLAGS = -D NDEBUG -Ofast
# Additional debug-specific flags # Additional debug-specific flags
DCOMPILE_FLAGS = -D DEBUG DCOMPILE_FLAGS = -D DEBUG
# Add additional include paths # Add additional include paths
INCLUDES = include INCLUDES = include
# General linker settings # General linker settings
LINK_FLAGS = -lpthread -lncurses LINK_FLAGS = -Llib -lpthread -lncurses -ltfng -latomic
# Doc # Doc
DOCDIR = doc DOCDIR = doc
TFRANDDIR = tfnoisegen
TFRANDLIB = libtfng.a
#### END PROJECT SETTINGS #### #### END PROJECT SETTINGS ####
# Optionally you may move the section above to a separate config.mk file, and # Optionally you may move the section above to a separate config.mk file, and
@@ -158,6 +161,7 @@ dirs:
@echo "Creating directories" @echo "Creating directories"
@mkdir -p $(dir $(OBJECTS)) @mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH) @mkdir -p $(BIN_PATH)
@mkdir -p $(LIBS)
# Removes all build files # Removes all build files
.PHONY: clean .PHONY: clean
@@ -167,16 +171,22 @@ clean:
@echo "Deleting directories" @echo "Deleting directories"
@$(RM) -r build @$(RM) -r build
@$(RM) -r bin @$(RM) -r bin
@$(RM) -r $(LIBS)
@$(RM) -f reHDD.log @$(RM) -f reHDD.log
$(MAKE) clean -C tfnoisegen
# Main rule, checks the executable and symlinks to the output # Main rule, checks the executable and symlinks to the output
all: $(BIN_PATH)/$(BIN_NAME) all: $(BIN_PATH)/$(BIN_NAME)
$(MAKE) libtfng.a -C tfnoisegen
@cp $(TFRANDDIR)/$(TFRANDLIB) $(LIBS)
@echo "Making symlink: $(BIN_NAME) -> $<" @echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME) @$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME) @ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
# Link the executable # Link the executable
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS) $(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
$(MAKE) libtfng.a -C tfnoisegen
@cp $(TFRANDDIR)/$(TFRANDLIB) $(LIBS)
@echo "Linking: $@" @echo "Linking: $@"
@$(START_TIME) @$(START_TIME)
$(CMD_PREFIX)$(CXX) $(OBJECTS) $(LDFLAGS) -o $@ $(CMD_PREFIX)$(CXX) $(OBJECTS) $(LDFLAGS) -o $@
+1 -1
View File
@@ -4,7 +4,7 @@ Description=dmesg on tty2
[Service] [Service]
WorkingDirectory=/usr/bin/ WorkingDirectory=/usr/bin/
ExecStart= ExecStart=
ExecStart=-/usr/bin/dmesg -wH ExecStart=-/usr/bin/dmesg -wHT
StandardInput=tty StandardInput=tty
StandardOutput=tty StandardOutput=tty
Restart=always Restart=always
-6
View File
@@ -1,6 +0,0 @@
#!/bin/bash
# remove comment for the following to activate log telemetie
curl -k -T /root/reHDD/reHDD.log -u "__Place_your_token_here__:" -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
-18
View File
@@ -1,18 +0,0 @@
[Unit]
Description=reHDD log uploader
After=syslog.target
After=network.target
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
Group=root
RemainAfterExit=yes
ExecStart=/usr/bin/bash /root/reHDD/scripts/reHDDLogUploader.bash
[Install]
WantedBy=multi-user.target
-11
View File
@@ -1,11 +0,0 @@
[Unit]
Description=reHDD log uploader timer
[Timer]
OnActiveSec=30s
OnBootSec=10min
OnUnitActiveSec=12h
[Install]
WantedBy=basic.target
+36
View File
@@ -0,0 +1,36 @@
#! /usr/bin/bash
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 already"
else
cp /root/reHDD/ignoreDrives.conf /root/ignoreDrives.conf
fi
git reset
git stash force
git stash
git checkout master
git pull
git submodule init
git submodule update
make clean
make release
cp /root/ignoreDrives.conf /root/reHDD/ignoreDrives.conf
systemctl start /lib/systemd/system/getty@tty1.service.d
+17 -10
View File
@@ -6,17 +6,18 @@
*/ */
#include "../include/reHDD.h" #include "../include/reHDD.h"
using namespace std;
/** /**
* \brief delete drive with wipefs * \brief delete drive with wipefs
* \param pointer of Drive instance * \param pointer of Drive instance
* \return void * \return void
*/ */
void Delete::deleteDrive(Drive* drive) void Delete::deleteDrive(Drive *drive)
{ {
size_t len = 0; //lenght of found line size_t len = 0; // lenght of found line
char* cLine = NULL; //found line char *cLine = NULL; // found line
#ifndef DRYRUN #ifndef DRYRUN
string sCMD = ("wipefs -af "); string sCMD = ("wipefs -af ");
@@ -25,18 +26,24 @@ void Delete::deleteDrive(Drive* drive)
#endif #endif
#ifdef DRYRUN #ifdef DRYRUN
//cout << "dryrun for " << drive->getPath() << endl; // cout << "dryrun for " << drive->getPath() << endl;
string sCMD = ("echo"); string sCMD = ("echo");
#endif #endif
const char* cpComand = sCMD.c_str(); const char *cpComand = sCMD.c_str();
//cout << "delete: " << cpComand << endl; // cout << "delete: " << cpComand << endl;
FILE* deleteCmdOutput = popen(cpComand, "r"); if (drive->bWasShredStarted == false)
{
// only start delete if the drive was not shredded before
FILE *deleteCmdOutput = popen(cpComand, "r");
while ((getline(&cLine, &len, deleteCmdOutput)) != -1) while ((getline(&cLine, &len, deleteCmdOutput)) != -1)
{ {
//wipefs running // wipefs running
} }
pclose(deleteCmdOutput);
free(cLine);
pclose(deleteCmdOutput);
}
} }
+191 -50
View File
@@ -6,6 +6,95 @@
*/ */
#include "../include/reHDD.h" #include "../include/reHDD.h"
using namespace std;
// Copy constructor
Drive::Drive(const Drive &other)
: state(other.state.load()),
connectionType(other.connectionType.load()),
sShredSpeed(other.sShredSpeed.load()),
bWasShredded(other.bWasShredded),
bWasShredStarted(other.bWasShredStarted),
bWasChecked(other.bWasChecked),
bWasDeleted(other.bWasDeleted),
bIsOffline(other.bIsOffline),
u32DriveChecksumAfterShredding(other.u32DriveChecksumAfterShredding),
sPath(other.sPath),
u32Timestamp(other.u32Timestamp),
d32TaskPercentage(other.d32TaskPercentage),
u32TimestampTaskStart(other.u32TimestampTaskStart),
u32TaskDuration(other.u32TaskDuration),
sSmartData(other.sSmartData)
{
}
// Copy assignment operator
Drive &Drive::operator=(const Drive &other)
{
if (this != &other)
{
state = other.state.load();
connectionType = other.connectionType.load();
sShredSpeed = other.sShredSpeed.load();
bWasShredded = other.bWasShredded;
bWasShredStarted = other.bWasShredStarted;
bWasChecked = other.bWasChecked;
bWasDeleted = other.bWasDeleted;
bIsOffline = other.bIsOffline;
u32DriveChecksumAfterShredding = other.u32DriveChecksumAfterShredding;
sPath = other.sPath;
u32Timestamp = other.u32Timestamp;
d32TaskPercentage = other.d32TaskPercentage;
u32TimestampTaskStart = other.u32TimestampTaskStart;
u32TaskDuration = other.u32TaskDuration;
sSmartData = other.sSmartData;
}
return *this;
}
// Move constructor
Drive::Drive(Drive &&other) noexcept
: state(other.state.load()),
connectionType(other.connectionType.load()),
sShredSpeed(other.sShredSpeed.load()),
bWasShredded(other.bWasShredded),
bWasShredStarted(other.bWasShredStarted),
bWasChecked(other.bWasChecked),
bWasDeleted(other.bWasDeleted),
bIsOffline(other.bIsOffline),
u32DriveChecksumAfterShredding(other.u32DriveChecksumAfterShredding),
sPath(std::move(other.sPath)),
u32Timestamp(other.u32Timestamp),
d32TaskPercentage(other.d32TaskPercentage),
u32TimestampTaskStart(other.u32TimestampTaskStart),
u32TaskDuration(other.u32TaskDuration),
sSmartData(std::move(other.sSmartData))
{
}
// Move assignment operator
Drive &Drive::operator=(Drive &&other) noexcept
{
if (this != &other)
{
state = other.state.load();
connectionType = other.connectionType.load();
sShredSpeed = other.sShredSpeed.load();
bWasShredded = other.bWasShredded;
bWasShredStarted = other.bWasShredStarted;
bWasChecked = other.bWasChecked;
bWasDeleted = other.bWasDeleted;
bIsOffline = other.bIsOffline;
u32DriveChecksumAfterShredding = other.u32DriveChecksumAfterShredding;
sPath = std::move(other.sPath);
u32Timestamp = other.u32Timestamp;
d32TaskPercentage = other.d32TaskPercentage;
u32TimestampTaskStart = other.u32TimestampTaskStart;
u32TaskDuration = other.u32TaskDuration;
sSmartData = std::move(other.sSmartData);
}
return *this;
}
string Drive::getPath(void) string Drive::getPath(void)
{ {
@@ -14,51 +103,75 @@ string Drive::getPath(void)
string Drive::getModelFamily(void) string Drive::getModelFamily(void)
{ {
return sModelFamily; return sSmartData.sModelFamily;
} }
string Drive::getModelName(void) string Drive::getModelName(void)
{ {
return sModelName; return sSmartData.sModelName;
} }
string Drive::getSerial(void) string Drive::getSerial(void)
{ {
return sSerial; return sSmartData.sSerial;
} }
uint64_t Drive::getCapacity(void) uint64_t Drive::getCapacity(void)
{ {
return u64Capacity; return sSmartData.u64Capacity;
} }
uint32_t Drive::getErrorCount(void) uint32_t Drive::getErrorCount(void)
{ {
return u32ErrorCount; return sSmartData.u32ErrorCount;
} }
uint32_t Drive::getPowerOnHours(void) uint32_t Drive::getPowerOnHours(void)
{ {
return u32PowerOnHours; return sSmartData.u32PowerOnHours;
} }
uint32_t Drive::getPowerCycles(void) uint32_t Drive::getPowerCycles(void)
{ {
return u32PowerCycles; return sSmartData.u32PowerCycles;
}
uint32_t Drive::getTemperature(void)
{
return sSmartData.u32Temperature;
}
uint32_t Drive::getReallocatedSectors(void)
{
return sSmartData.u32ReallocatedSectors;
}
uint32_t Drive::getPendingSectors(void)
{
return sSmartData.u32PendingSectors;
}
uint32_t Drive::getUncorrectableSectors(void)
{
return sSmartData.u32UncorrectableSectors;
} }
string Drive::sCapacityToText() string Drive::sCapacityToText()
{ {
char acBuffer[16]; char acBuffer[16];
double dSize = (double) getCapacity(); double dSize = (double)getCapacity();
uint16_t u16UnitIndex = 0; uint16_t u16UnitIndex = 0;
const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; const char *units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
while (dSize >= 1000) //using the marketing capacity while (dSize >= 1000) // using the marketing capacity
{ {
dSize /= 1000; dSize /= 1000;
u16UnitIndex++; u16UnitIndex++;
} }
if (u16UnitIndex >= 9)
sprintf(acBuffer, "%.*f %s", u16UnitIndex-3, dSize, units[u16UnitIndex]); {
u16UnitIndex = 8;
}
int precision = (u16UnitIndex >= 3) ? (u16UnitIndex - 3) : 0;
sprintf(acBuffer, "%.*f %s", precision, dSize, units[u16UnitIndex]);
return acBuffer; return acBuffer;
} }
@@ -67,7 +180,6 @@ string Drive::sErrorCountToText()
return to_string(getErrorCount()); return to_string(getErrorCount());
} }
string Drive::sPowerOnHoursToText() string Drive::sPowerOnHoursToText()
{ {
double dDays = 0U; double dDays = 0U;
@@ -76,8 +188,8 @@ string Drive::sPowerOnHoursToText()
stringstream streamDays; stringstream streamDays;
stringstream streamYears; stringstream streamYears;
dDays = (double) ((double)u32Hours/(double)24U); dDays = (double)((double)u32Hours / (double)24U);
dYears = (double) ((double)u32Hours/(double)8760U); dYears = (double)((double)u32Hours / (double)8760U);
streamDays << fixed << setprecision(0) << dDays; streamDays << fixed << setprecision(0) << dDays;
streamYears << fixed << setprecision(1) << dYears; streamYears << fixed << setprecision(1) << dYears;
@@ -92,20 +204,24 @@ string Drive::sPowerCyclesToText()
return to_string(getPowerCycles()); return to_string(getPowerCycles());
} }
string Drive::sTemperatureToText()
{
return to_string(getTemperature()) + " C";
}
void Drive::setTaskPercentage(double d32TaskPercentage) void Drive::setTaskPercentage(double d32TaskPercentage)
{ {
if(d32TaskPercentage <= 100) if (d32TaskPercentage <= 100)
{ {
this->d32TaskPercentage = d32TaskPercentage; this->d32TaskPercentage = d32TaskPercentage;
this->setTimestamp(); //set timestamp for this progress for detecting a frozen drive this->setTimestamp(); // set timestamp for this progress for detecting a frozen drive
} }
} }
double Drive::getTaskPercentage(void) double Drive::getTaskPercentage(void)
{ {
return this->d32TaskPercentage; return this->d32TaskPercentage;
} }
/** /**
* \brief set S.M.A.R.T. values in model * \brief set S.M.A.R.T. values in model
* \param string modelFamily * \param string modelFamily
@@ -115,34 +231,50 @@ double Drive::getTaskPercentage(void)
* \param uint32_t errorCount * \param uint32_t errorCount
* \param uint32_t powerOnHours * \param uint32_t powerOnHours
* \param uint32_t powerCycle * \param uint32_t powerCycle
* \param uint32_t temperature
* \return void * \return void
*/ */
void Drive::setDriveSMARTData( string modelFamily, void Drive::setDriveSMARTData(string modelFamily,
string modelName, string modelName,
string serial, string serial,
uint64_t capacity, uint64_t capacity,
uint32_t errorCount, uint32_t errorCount,
uint32_t powerOnHours, uint32_t powerOnHours,
uint32_t powerCycle) uint32_t powerCycle,
uint32_t temperature,
uint32_t reallocatedSectors,
uint32_t pendingSectors,
uint32_t uncorrectableSectors)
{ {
this->sModelFamily = modelFamily; this->sSmartData.sModelFamily = modelFamily;
sModelName = modelName; this->sSmartData.sModelName = modelName;
sSerial = serial; this->sSmartData.sSerial = serial;
u64Capacity = capacity; this->sSmartData.u64Capacity = capacity;
u32ErrorCount = errorCount; this->sSmartData.u32ErrorCount = errorCount;
u32PowerOnHours = powerOnHours; this->sSmartData.u32PowerOnHours = powerOnHours;
u32PowerCycles = powerCycle; this->sSmartData.u32PowerCycles = powerCycle;
this->sSmartData.u32Temperature = temperature;
this->sSmartData.u32ReallocatedSectors = reallocatedSectors;
this->sSmartData.u32PendingSectors = pendingSectors;
this->sSmartData.u32UncorrectableSectors = uncorrectableSectors;
} }
void Drive::setTimestamp() void Drive::setTimestamp()
{ {
time(&this->u32Timestamp); if (time(&this->u32Timestamp) == -1)
{
// handle error
this->u32Timestamp = 0U;
}
} }
void Drive::setActionStartTimestamp() void Drive::setActionStartTimestamp()
{ {
time(&this->u32TimestampTaskStart); if (time(&this->u32TimestampTaskStart) == -1)
{
// handle error
this->u32TimestampTaskStart = 0U;
}
} }
time_t Drive::getActionStartTimestamp() time_t Drive::getActionStartTimestamp()
@@ -153,7 +285,11 @@ time_t Drive::getActionStartTimestamp()
void Drive::calculateTaskDuration() void Drive::calculateTaskDuration()
{ {
time_t u32localtime; time_t u32localtime;
time(&u32localtime); if (time(&u32localtime) == -1)
{
// handle error
u32localtime = 0U;
}
this->u32TaskDuration = u32localtime - this->u32TimestampTaskStart; this->u32TaskDuration = u32localtime - this->u32TimestampTaskStart;
} }
@@ -167,12 +303,17 @@ void Drive::checkFrozenDrive(void)
{ {
time_t u32localtime; time_t u32localtime;
time(&u32localtime); time(&u32localtime);
if (time(&u32localtime) == -1)
{
// handle error
u32localtime = 0U;
}
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()); Logger::logThis()->warning("Drive Frozen: " + this->getModelName() + " " + this->getSerial());
this->bWasDeleteted = false; this->bWasDeleted = false;
this->bWasShredded = false; this->bWasShredded = false;
this->state = Drive::FROZEN; this->state = Drive::TaskState::FROZEN;
} }
} }
+72 -58
View File
@@ -5,21 +5,18 @@
* @date 04.09.2020 * @date 04.09.2020
*/ */
#include "../../include/reHDD.h" //for logger settings #include "../../include/reHDD.h" //for logger settings
#include "../../include/logger/logger.h" #include "../../include/logger/logger.h"
using namespace std; using namespace std;
string version = "0.2.1"; //logger version string version = "0.2.1"; // logger version
bool Logger::instanceFlag = false; bool Logger::instanceFlag = false;
Logger* Logger::single = NULL; Logger *Logger::single = NULL;
/** /**
* \brief create new logger instance * \brief create new logger instance
* \param path to log file
* \param struct with data
* \return instance of Logger * \return instance of Logger
*/ */
Logger::Logger() Logger::Logger()
@@ -97,13 +94,13 @@ void Logger::error(string s)
void Logger::writeLog(string s) void Logger::writeLog(string s)
{ {
ofstream logFile; ofstream logFile;
Logger::mtxLog.lock(); //lock this section for other threads Logger::mtxLog.lock(); // lock this section for other threads
logFile.open(this->logPath, ios_base::app); logFile.open(this->logPath, ios_base::app);
logFile << (s + "\n"); //append to existing file logFile << (s + "\n"); // append to existing file
logFile.close(); logFile.close();
Logger::mtxLog.unlock(); //unlock this section for other threads Logger::mtxLog.unlock(); // unlock this section for other threads
} }
/** /**
@@ -122,22 +119,22 @@ void Logger::newLine()
*/ */
string Logger::getTimestamp() string Logger::getTimestamp()
{ {
struct tm * timeinfo; struct tm *timeinfo;
struct timeval tv; struct timeval tv;
int millisec; int millisec;
char cpDate [80]; char cpDate[80];
char buffer [120]; char buffer[120];
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
millisec = lrint(tv.tv_usec/1000.0); // Round to nearest millisec millisec = lrint(tv.tv_usec / 1000.0); // Round to nearest millisec
if (millisec>=1000) // Allow for rounding up to nearest second if (millisec >= 1000) // Allow for rounding up to nearest second
{ {
millisec -=1000; millisec -= 1000;
tv.tv_sec++; tv.tv_sec++;
} }
timeinfo = localtime(&tv.tv_sec); timeinfo = localtime(&tv.tv_sec);
strftime (cpDate,80,"%d/%m/%Y %T",timeinfo); strftime(cpDate, 80, "%d/%m/%Y %T", timeinfo);
sprintf(buffer, "%s.%03d", cpDate, millisec); snprintf(buffer, sizeof(buffer), "%s.%03d", cpDate, millisec);
return buffer; return buffer;
} }
@@ -146,25 +143,44 @@ string Logger::getTimestamp()
* \param void * \param void
* \return string MAC address (formatted) * \return string MAC address (formatted)
*/ */
string Logger::getMacAddress() std::string Logger::getMacAddress()
{ {
struct ifreq ifr; struct ifaddrs *ifaddr, *ifa;
int s = socket(AF_INET, SOCK_STREAM,0);
strcpy(ifr.ifr_name, "eth0"); // default MAC if none found
if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) std::string result = "00:00:00:00:00:00";
if (getifaddrs(&ifaddr) == -1)
return result;
for (ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
{
if (!ifa->ifa_addr)
continue;
// We want AF_PACKET interfaces (Ethernet)
if (ifa->ifa_addr->sa_family == AF_PACKET &&
!(ifa->ifa_flags & IFF_LOOPBACK) && // skip loopback interface
(ifa->ifa_flags & IFF_UP)) // must be up
{ {
strcpy(ifr.ifr_name, "eno1"); struct sockaddr_ll *s = (struct sockaddr_ll *)ifa->ifa_addr;
if (s->sll_halen == 6)
{
char buf[32];
snprintf(buf, sizeof(buf),
"%02X:%02X:%02X:%02X:%02X:%02X",
s->sll_addr[0], s->sll_addr[1], s->sll_addr[2],
s->sll_addr[3], s->sll_addr[4], s->sll_addr[5]);
freeifaddrs(ifaddr);
return std::string(buf);
}
} }
}
unsigned char *hwaddr = (unsigned char *)ifr.ifr_hwaddr.sa_data; freeifaddrs(ifaddr);
char buffer [80]; return result;
sprintf(buffer,"%02X:%02X:%02X:%02X:%02X:%02X", hwaddr[0], hwaddr[1], hwaddr[2],
hwaddr[3], hwaddr[4], hwaddr[5]);
close(s);
string tmp = buffer;
return tmp;
} }
/** /**
@@ -176,22 +192,22 @@ string Logger::getMacAddress()
*/ */
string Logger::padStringMenu(char cBorder, string text, uint8_t u8LineLenght) string Logger::padStringMenu(char cBorder, string text, uint8_t u8LineLenght)
{ {
string result(1,cBorder); string result(1, cBorder);
uint8_t u8TextSize = text.length(); uint8_t u8TextSize = text.length();
uint8_t u8Padding = ((u8LineLenght-u8TextSize)/2); uint8_t u8Padding = ((u8LineLenght - u8TextSize) / 2);
for(uint8_t i = 0 ; i < u8Padding; i++) for (uint8_t i = 0; i < u8Padding; i++)
{ {
result.append(" "); result.append(" ");
} }
result.append(text); result.append(text);
while((uint8_t)result.length() < (u8LineLenght-1)) while ((uint8_t)result.length() < (u8LineLenght - 1))
{ {
result.append(" "); result.append(" ");
} }
result.append(string(1, cBorder)); result.append(string(1, cBorder));
return result; return result;
@@ -205,12 +221,12 @@ string Logger::padStringMenu(char cBorder, string text, uint8_t u8LineLenght)
*/ */
string Logger::menuLine(char cBorder, uint8_t u8LineLenght) string Logger::menuLine(char cBorder, uint8_t u8LineLenght)
{ {
string result(1,cBorder); string result(1, cBorder);
while((uint8_t)result.length() < u8LineLenght) while ((uint8_t)result.length() < u8LineLenght)
{ {
result.append(string(1, cBorder)); result.append(string(1, cBorder));
} }
return result; return result;
} }
@@ -218,18 +234,16 @@ string Logger::menuLine(char cBorder, uint8_t u8LineLenght)
* \brief return a instance of the logger * \brief return a instance of the logger
* \return logger obj * \return logger obj
*/ */
Logger* Logger::logThis() Logger *Logger::logThis()
{ {
if (!instanceFlag) if (!instanceFlag)
{ {
single = new Logger(); //create new obj single = new Logger(); // create new obj
instanceFlag = true; instanceFlag = true;
return single; return single;
} }
else else
{ {
return single; //return existing obj return single; // return existing obj
} }
} }
+3 -2
View File
@@ -6,6 +6,7 @@
*/ */
#include "../include/reHDD.h" #include "../include/reHDD.h"
using namespace std;
/** /**
* \brief app entry point * \brief app entry point
@@ -16,7 +17,7 @@ int main(void)
{ {
// cout << "refurbishingHddTool" << endl; // cout << "refurbishingHddTool" << endl;
reHDD* app = new reHDD(); reHDD app;
app->app_logic(); app.app_logic();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+101
View File
@@ -0,0 +1,101 @@
/**
* @file printer.cpp
* @brief Send drive data to printer service using ipc msg queue
* @author Hendrik Schutter
* @date 24.11.2022
*/
#include "../include/reHDD.h"
bool Printer::instanceFlag = false;
Printer *Printer::single = NULL;
/**
* \brief create new Printer instance
* \param path to log file
* \param struct with data
* \return instance of Printer
*/
Printer::Printer()
{
if (-1 == (this->msqid = msgget((key_t)IPC_MSG_QUEUE_KEY, IPC_CREAT | 0666)))
{
Logger::logThis()->error("Printer: Create mgs queue failed!");
}
}
/**
* \brief deconstructor
* \return void
*/
Printer::~Printer()
{
instanceFlag = false;
}
/**
* \brief send data to msg queue
* \return void
*/
void Printer::print(Drive *drive)
{
t_msgQueueData msgQueueData;
msgQueueData.msg_queue_type = 1;
snprintf(msgQueueData.driveData.caDriveIndex, STR_BUFFER_SIZE, "%i", drive->u16DriveIndex);
snprintf(msgQueueData.driveData.caDriveState, STR_BUFFER_SIZE, "shredded");
snprintf(msgQueueData.driveData.caDriveModelFamily, STR_BUFFER_SIZE, "%s", drive->getModelFamily().c_str());
snprintf(msgQueueData.driveData.caDriveModelName, STR_BUFFER_SIZE, "%s", drive->getModelName().c_str());
snprintf(msgQueueData.driveData.caDriveCapacity, STR_BUFFER_SIZE, "%li", drive->getCapacity());
snprintf(msgQueueData.driveData.caDriveSerialnumber, STR_BUFFER_SIZE, "%s", drive->getSerial().c_str());
snprintf(msgQueueData.driveData.caDriveHours, STR_BUFFER_SIZE, "%i", drive->getPowerOnHours());
snprintf(msgQueueData.driveData.caDriveCycles, STR_BUFFER_SIZE, "%i", drive->getPowerCycles());
snprintf(msgQueueData.driveData.caDriveErrors, STR_BUFFER_SIZE, "%i", drive->getErrorCount());
snprintf(msgQueueData.driveData.caDriveShredTimestamp, STR_BUFFER_SIZE, "%li", drive->getActionStartTimestamp());
snprintf(msgQueueData.driveData.caDriveShredDuration, STR_BUFFER_SIZE, "%li", drive->getTaskDuration());
switch (drive->connectionType)
{
case Drive::ConnectionType::USB:
strncpy(msgQueueData.driveData.caDriveConnectionType, "usb", STR_BUFFER_SIZE);
break;
case Drive::ConnectionType::SATA:
strncpy(msgQueueData.driveData.caDriveConnectionType, "sata", STR_BUFFER_SIZE);
break;
case Drive::ConnectionType::NVME:
strncpy(msgQueueData.driveData.caDriveConnectionType, "nvme", STR_BUFFER_SIZE);
break;
case Drive::ConnectionType::UNKNOWN:
default:
strncpy(msgQueueData.driveData.caDriveConnectionType, "na", STR_BUFFER_SIZE);
}
snprintf(msgQueueData.driveData.caDriveReHddVersion, STR_BUFFER_SIZE, "%s", REHDD_VERSION);
if (-1 == msgsnd(this->msqid, &msgQueueData, sizeof(t_msgQueueData) - sizeof(long), 0))
{
Logger::logThis()->error("Printer: Send mgs queue failed!");
}
else
{
Logger::logThis()->info("Printer: print triggered - Drive: " + drive->getSerial());
}
}
/**
* \brief return a instance of the printer
* \return printer obj
*/
Printer *Printer::getPrinter()
{
if (!instanceFlag)
{
single = new Printer(); // create new obj
instanceFlag = true;
return single;
}
else
{
return single; // return existing obj
}
}
+719 -354
View File
File diff suppressed because it is too large Load Diff
+766 -188
View File
File diff suppressed because it is too large Load Diff
+355 -146
View File
@@ -6,173 +6,382 @@
*/ */
#include "../include/reHDD.h" #include "../include/reHDD.h"
#include <sys/wait.h> // For WIFSIGNALED, WTERMSIG
using namespace std;
string SMART::modelFamily; /**
string SMART::modelName; * \brief Parse context for SMART attribute values
string SMART::serial; */
uint64_t SMART::capacity = 0U; struct SMARTParseContext
uint32_t SMART::errorCount = 0U; {
uint32_t SMART::powerOnHours = 0U; // Device information (top-level JSON fields)
uint32_t SMART::powerCycle = 0U; string modelFamily;
string modelName;
string serial;
uint64_t capacity;
// Power and temperature (top-level JSON fields)
uint32_t errorCount;
uint32_t powerOnHours;
uint32_t powerCycles;
uint32_t temperature;
// Critical sector counts (from ata_smart_attributes table)
uint32_t reallocatedSectors; // ID 5
uint32_t pendingSectors; // ID 197
uint32_t uncorrectableSectors; // ID 198
// Parser state machine
enum State
{
SEARCHING, // Looking for next field
IN_ATTRIBUTE_5, // Inside ID 5 object
IN_ATTRIBUTE_197, // Inside ID 197 object
IN_ATTRIBUTE_198, // Inside ID 198 object
IN_RAW_SECTION // Inside "raw": { } of current attribute
};
State state;
int currentAttributeId; // Which attribute are we parsing? (5, 197, 198)
SMARTParseContext()
: capacity(0),
errorCount(0),
powerOnHours(0),
powerCycles(0),
temperature(0),
reallocatedSectors(0),
pendingSectors(0),
uncorrectableSectors(0),
state(SEARCHING),
currentAttributeId(0)
{
}
};
/**
* \brief Extract JSON string value
* \param line containing "key": "value"
* \return extracted string value
*/
static string extractStringValue(const string &line)
{
size_t colonPos = line.find(": ");
if (colonPos == string::npos)
return "";
size_t firstQuote = line.find('"', colonPos + 2);
if (firstQuote == string::npos)
return "";
size_t secondQuote = line.find('"', firstQuote + 1);
if (secondQuote == string::npos)
return "";
return line.substr(firstQuote + 1, secondQuote - firstQuote - 1);
}
/**
* \brief Extract JSON integer value
* \param line containing "key": number
* \return extracted integer value
*/
static uint64_t extractIntegerValue(const string &line)
{
size_t colonPos = line.find(": ");
if (colonPos == string::npos)
return 0;
string valueStr = line.substr(colonPos + 2);
// Remove whitespace, commas, braces
valueStr.erase(remove_if(valueStr.begin(), valueStr.end(),
[](char c)
{ return c == ' ' || c == ',' || c == '}' || c == '\n'; }),
valueStr.end());
// Verify it's a valid number
if (valueStr.empty() || valueStr.find_first_not_of("0123456789") != string::npos)
return 0;
try
{
return stoull(valueStr);
}
catch (...)
{
return 0;
}
}
/**
* \brief Process a single line of JSON output
* \param line from smartctl JSON output
* \param context parsing context with state
* \return void
*/
static void processLine(const string &line, SMARTParseContext &ctx)
{
// Trim whitespace for consistent parsing
string trimmed = line;
size_t firstNonSpace = trimmed.find_first_not_of(" \t\r\n");
if (firstNonSpace != string::npos)
{
trimmed = trimmed.substr(firstNonSpace);
}
// Parse top-level device information
if (trimmed.find("\"model_family\":") == 0)
{
ctx.modelFamily = extractStringValue(line);
return;
}
if (trimmed.find("\"model_name\":") == 0)
{
ctx.modelName = extractStringValue(line);
return;
}
if (trimmed.find("\"serial_number\":") == 0)
{
ctx.serial = extractStringValue(line);
return;
}
// Parse capacity from user_capacity.bytes
if (trimmed.find("\"bytes\":") == 0)
{
ctx.capacity = extractIntegerValue(line);
return;
}
// Parse error count from self_test log
if (trimmed.find("\"error_count_total\":") == 0)
{
ctx.errorCount = extractIntegerValue(line);
return;
}
// Parse power-on hours
if (trimmed.find("\"hours\":") == 0)
{
ctx.powerOnHours = extractIntegerValue(line);
return;
}
// Parse power cycle count
if (trimmed.find("\"power_cycle_count\":") == 0)
{
ctx.powerCycles = extractIntegerValue(line);
return;
}
// Parse temperature
if (trimmed.find("\"current\":") == 0 && ctx.temperature == 0)
{
// Only parse first occurrence (temperature section, not other "current" fields)
ctx.temperature = extractIntegerValue(line);
return;
}
// State machine for SMART attributes parsing
switch (ctx.state)
{
case SMARTParseContext::SEARCHING:
// Look for critical attribute IDs
if (trimmed.find("\"id\": 5,") == 0)
{
ctx.state = SMARTParseContext::IN_ATTRIBUTE_5;
ctx.currentAttributeId = 5;
}
else if (trimmed.find("\"id\": 197,") == 0)
{
ctx.state = SMARTParseContext::IN_ATTRIBUTE_197;
ctx.currentAttributeId = 197;
}
else if (trimmed.find("\"id\": 198,") == 0)
{
ctx.state = SMARTParseContext::IN_ATTRIBUTE_198;
ctx.currentAttributeId = 198;
}
break;
case SMARTParseContext::IN_ATTRIBUTE_5:
case SMARTParseContext::IN_ATTRIBUTE_197:
case SMARTParseContext::IN_ATTRIBUTE_198:
// Look for "raw": { start
if (trimmed.find("\"raw\":") == 0)
{
ctx.state = SMARTParseContext::IN_RAW_SECTION;
}
// Look for end of attribute object (more indented closing brace = end of attribute)
// " }," or " }" at attribute level (6 spaces)
else if (line.find(" },") == 0 || line.find(" }") == 0)
{
ctx.state = SMARTParseContext::SEARCHING;
ctx.currentAttributeId = 0;
}
break;
case SMARTParseContext::IN_RAW_SECTION:
// Look for "value": number inside raw section
if (trimmed.find("\"value\":") == 0)
{
uint64_t value = extractIntegerValue(line);
// Store value in appropriate field based on current attribute
if (ctx.currentAttributeId == 5)
{
ctx.reallocatedSectors = static_cast<uint32_t>(value);
}
else if (ctx.currentAttributeId == 197)
{
ctx.pendingSectors = static_cast<uint32_t>(value);
}
else if (ctx.currentAttributeId == 198)
{
ctx.uncorrectableSectors = static_cast<uint32_t>(value);
}
// Stay in raw section - closing brace will exit
}
// Look for end of raw object (less indented = back to attribute level)
// " }" at raw level (8 spaces)
else if (line.find(" }") == 0)
{
// Return to attribute state (raw section closed)
ctx.state = (ctx.currentAttributeId == 5) ? SMARTParseContext::IN_ATTRIBUTE_5 : (ctx.currentAttributeId == 197) ? SMARTParseContext::IN_ATTRIBUTE_197
: SMARTParseContext::IN_ATTRIBUTE_198;
}
break;
}
}
/** /**
* \brief get and set S.M.A.R.T. values in Drive * \brief get and set S.M.A.R.T. values in Drive
* \param pointer of Drive instance * \param pointer of Drive instance
* \return void * \return void
*/ */
void SMART::readSMARTData(Drive* drive) void SMART::readSMARTData(Drive *drive)
{ {
modelFamily.clear(); SMARTParseContext ctx;
modelName.clear(); uint8_t exitStatus = 255U;
serial.clear();
capacity = 0U;
errorCount = 0U;
powerOnHours = 0U;
powerCycle = 0U;
size_t len = 0; //lenght of found line // Command order optimized for USB adapters
char* cLine = NULL; //found line // Standard commands first, then device-specific variants
string sSmartctlCommands[] = {
" --json -a ", // Try standard first
" --json -d sat -a ", // SAT (SCSI/ATA Translation) - most USB adapters
" --json -d usbjmicron -a ", // USB JMicron
" --json -d usbprolific -a ", // USB Prolific
" --json -d usbsunplus -a " // USB Sunplus
};
string sCMD = ("smartctl --json -a "); for (const string &sSmartctlCommand : sSmartctlCommands)
sCMD.append(drive->getPath()); {
const char* cpComand = sCMD.c_str(); // Build command with timeout
string sCMD = "timeout 5 smartctl"; // 5 second timeout prevents hanging
sCMD.append(sSmartctlCommand);
sCMD.append(drive->getPath());
// Note: stderr NOT suppressed for debugging
FILE* outputfileSmart = popen(cpComand, "r"); Logger::logThis()->info("SMART: Executing: " + sCMD);
while ((getline(&cLine, &len, outputfileSmart)) != -1) // Execute smartctl with timeout protection
FILE *outputfileSmart = popen(sCMD.c_str(), "r");
if (outputfileSmart == nullptr)
{ {
string sLine = string(cLine); Logger::logThis()->error("SMART: Failed to execute smartctl");
continue;
SMART::parseModelFamily(sLine);
SMART::parseModelName(sLine);
SMART::parseSerial(sLine);
SMART::parseCapacity(sLine);
SMART::parseErrorCount(sLine);
SMART::parsePowerOnHours(sLine);
SMART::parsePowerCycle(sLine);
} }
pclose(outputfileSmart);
drive->setDriveSMARTData(modelFamily, modelName, serial, capacity, errorCount, powerOnHours, powerCycle); //wirte data in drive
}
/** // Reset context for new attempt
* \brief parse ModelFamiliy ctx = SMARTParseContext();
* \param string output line of smartctl
* \return void // Parse output line by line
*/ char *cLine = nullptr;
void SMART::parseModelFamily(string sLine) size_t len = 0;
{ int lineCount = 0;
string search("\"model_family\": ");
size_t found = sLine.find(search); while (getline(&cLine, &len, outputfileSmart) != -1)
if (found!=string::npos)
{ {
sLine.erase(0, sLine.find(": ") + 3); string sLine(cLine);
sLine.erase(sLine.length()-3, 3); lineCount++;
modelFamily = sLine;
}
}
/** // Parse exit status
* \brief parse ModelName if (sLine.find("\"exit_status\":") != string::npos)
* \param string output line of smartctl {
* \return void exitStatus = static_cast<uint8_t>(extractIntegerValue(sLine));
*/ }
void SMART::parseModelName(string sLine)
{ // Process this line
string search("\"model_name\": "); processLine(sLine, ctx);
size_t found = sLine.find(search); }
if (found!=string::npos)
free(cLine);
int pcloseStatus = pclose(outputfileSmart);
Logger::logThis()->info("SMART: Parsed " + to_string(lineCount) + " lines, exit status: " + to_string(exitStatus));
// Check if timeout killed the process
if (WIFSIGNALED(pcloseStatus) && WTERMSIG(pcloseStatus) == SIGTERM)
{ {
sLine.erase(0, sLine.find(": ") + 3); Logger::logThis()->warning("SMART: Command timed out (5s) - skipping to next variant");
sLine.erase(sLine.length()-3, 3); continue;
modelName = sLine;
} }
}
/** // IGNORE exit status - instead check if we got valid data!
* \brief parse Serial // Exit status 64 means "error log contains errors" but SMART data is still valid
* \param string output line of smartctl // Exit status 4 means "some prefail attributes concerning" but data is valid
* \return void // What matters: Did we parse model name and serial?
*/ if (!ctx.modelName.empty() && !ctx.serial.empty())
void SMART::parseSerial(string sLine)
{
string search("\"serial_number\": ");
size_t found = sLine.find(search);
if (found!=string::npos)
{ {
sLine.erase(0, sLine.find(": ") + 3); Logger::logThis()->info("SMART: Successfully parsed data");
sLine.erase(sLine.length()-3, 3); Logger::logThis()->info("SMART: Model: " + ctx.modelName);
serial = sLine; Logger::logThis()->info("SMART: Serial: " + ctx.serial);
} Logger::logThis()->info("SMART: Capacity: " + to_string(ctx.capacity) + " bytes");
} Logger::logThis()->info("SMART: Power-On Hours: " + to_string(ctx.powerOnHours));
Logger::logThis()->info("SMART: Temperature: " + to_string(ctx.temperature) + " C");
Logger::logThis()->info("SMART: Reallocated Sectors: " + to_string(ctx.reallocatedSectors));
Logger::logThis()->info("SMART: Pending Sectors: " + to_string(ctx.pendingSectors));
Logger::logThis()->info("SMART: Uncorrectable Sectors: " + to_string(ctx.uncorrectableSectors));
/** if (exitStatus != 0)
* \brief parse Capacity {
* \param string output line of smartctl Logger::logThis()->info("SMART: Note - exit status " + to_string(exitStatus) + " indicates warnings/errors in SMART log");
* \return void }
*/
void SMART::parseCapacity(string sLine) break; // Success - we got data!
{ }
string search("\"bytes\": "); else
size_t found = sLine.find(search);
if (found!=string::npos)
{ {
sLine.erase(0, sLine.find(": ") + 2); Logger::logThis()->warning("SMART: No valid data parsed (exit status: " + to_string(exitStatus) + ")");
sLine.erase(sLine.length()-1, 1);
capacity = stol(sLine);
} }
}
// Check if we got ANY data
if (ctx.modelName.empty() && ctx.serial.empty())
{
Logger::logThis()->warning("SMART: No SMART data available for this drive - may not support SMART or need root privileges");
// Try basic device info without SMART (use hdparm or similar as fallback)
// For now, just log that SMART is not available
ctx.modelName = "SMART not available";
ctx.serial = "N/A";
}
// Write parsed data to drive
drive->setDriveSMARTData(
ctx.modelFamily,
ctx.modelName,
ctx.serial,
ctx.capacity,
ctx.errorCount,
ctx.powerOnHours,
ctx.powerCycles,
ctx.temperature,
ctx.reallocatedSectors,
ctx.pendingSectors,
ctx.uncorrectableSectors);
} }
/**
* \brief parse ErrorCount
* \param string output line of smartctl
* \return void
*/
void SMART::parseErrorCount(string sLine)
{
string search("\"error_count_total\": ");
size_t found = sLine.find(search);
if (found!=string::npos)
{
sLine.erase(0, sLine.find(": ")+2);
sLine.erase(sLine.length()-2, 2);
errorCount = stol(sLine);
}
}
/**
* \brief parse PowerOnHours
* \param string output line of smartctl
* \return void
*/
void SMART::parsePowerOnHours(string sLine)
{
string search("\"hours\": ");
size_t found = sLine.find(search);
if (found!=string::npos)
{
sLine.erase(0, sLine.find(": ") + 2);
sLine.erase(sLine.length()-1, 1);
powerOnHours = stol(sLine);
}
}
/**
* \brief parse PowerCycle
* \param string output line of smartctl
* \return void
*/
void SMART::parsePowerCycle(string sLine)
{
string search("\"power_cycle_count\": ");
size_t found = sLine.find(search);
if (found!=string::npos)
{
sLine.erase(0, sLine.find(": ") + 2);
sLine.erase(sLine.length()-2, 2);
powerCycle = stol(sLine);
}
}
+498 -311
View File
File diff suppressed because it is too large Load Diff
Submodule
+1
Submodule tfnoisegen added at 488716ef22