From 6c99166b75bd45833ef55214757fba76723e9cd3 Mon Sep 17 00:00:00 2001 From: PartialVolume Date: Wed, 18 Mar 2020 20:03:35 +0000 Subject: [PATCH] Add a summary table to the log that shows each drives status Add a summary table to the log that shows each drives status i.e. erased or failed, throughput, duration of wipe, model, serial no etc. In particular it benefits those that wipe many drives simultaneously in rack servers. At a glance any failed drives can be seen without having to browse back through the log. Especially useful in --nogui mode, but also useful in GUI mode. --- CHANGELOG.md | 3 +- src/context.h | 3 + src/logging.c | 320 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/logging.h | 5 +- src/method.c | 84 ++++++++++++- src/nwipe.c | 25 ++-- src/nwipe.h | 2 +- src/prng.c | 1 + 8 files changed, 418 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba9ef7b..1d055b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ other items in 0.29 are proposed and yet to be implemented. - [DONE] Add verbose option. -v, --verbose. - [DONE] Add a spinner to the GUI for each drive being wiped. When nwipe is syncing the percentage completion pauses, having a spinner gives a clear indication that the wipe is still running. Each devices spinner disappears on completion of a given devices wipe. - [DONE] Make log messages, especially the ones with the tag 'notice' succinct and less than 80 characters including the timestamp. This is of more importance when nwipe is used on a 80x30 terminal (ALT-F2, Shredos etc) but generally makes the logs more readable. While doing this all information was still retained. - +- [DONE] Add a summary table to the log that shows each drives status, i.e. erased or failed, throughput, duration of wipe, model, serial no etc. In particular it benefits +those that wipe many drives simultaneously in rack servers. At a glance any failed drives can be seen without having to browse back through the log. - Add enhancement fibre channel wiping of non 512 bytes/sector drives such as 524/528 bytes/sector etc (work in progress by PartialVolume) - HPA/DCO detection and adjustment to wipe full drive. (work in progress by PartialVolume) diff --git a/src/context.h b/src/context.h index 9f0be2b..4313882 100644 --- a/src/context.h +++ b/src/context.h @@ -120,6 +120,9 @@ typedef struct nwipe_context_t_ int wipe_status; // Wipe finished = 0, wipe in progress = 1, wipe yet to start = -1. int spinner_idx; // Index into the spinner character array char spinner_character[1]; // The current spinner character + double duration; // Duration of the wipe in seconds + time_t start_time; // Start time of wipe + time_t end_time; // End time of wipe /* * Identity contains the raw serial number of the drive * (where applicable), however, for use within nwipe use the diff --git a/src/logging.c b/src/logging.c index ba055a7..7919384 100644 --- a/src/logging.c +++ b/src/logging.c @@ -28,6 +28,7 @@ #include "stdio.h" #include "stdlib.h" +#include "string.h" #include "stdarg.h" #include "nwipe.h" #include "context.h" @@ -77,16 +78,22 @@ void nwipe_log( nwipe_log_t level, const char* format, ... ) /* Position of writing to current log string */ int line_current_pos = 0; + /* initialise characters written */ + chars_written = 0; + /* Print the date. The rc script uses the same format. */ - chars_written = snprintf( message_buffer, - MAX_LOG_LINE_CHARS, - "[%i/%02i/%02i %02i:%02i:%02i] ", - 1900 + p->tm_year, - 1 + p->tm_mon, - p->tm_mday, - p->tm_hour, - p->tm_min, - p->tm_sec ); + if( level != NWIPE_LOG_NOTIMESTAMP ) + { + chars_written = snprintf( message_buffer, + MAX_LOG_LINE_CHARS, + "[%i/%02i/%02i %02i:%02i:%02i] ", + 1900 + p->tm_year, + 1 + p->tm_mon, + p->tm_mday, + p->tm_hour, + p->tm_min, + p->tm_sec ); + } /* * Has the end of the buffer been reached ?, snprintf returns the number of characters that would have been @@ -127,6 +134,7 @@ void nwipe_log( nwipe_log_t level, const char* format, ... ) { case NWIPE_LOG_NONE: + case NWIPE_LOG_NOTIMESTAMP: /* Do nothing. */ break; @@ -471,3 +479,297 @@ int nwipe_log_sysinfo() } return 0; } + +void nwipe_log_summary( nwipe_context_t** ptr, int nwipe_selected ) +{ + int i; + int idx_src; + int idx_dest; + char device[7]; + char status[9]; + char throughput[13]; + char total_throughput_string[13]; + char summary_top_border[256]; + char summary_top_column_titles[256]; + char blank[3]; + char verify[3]; + // char duration[5]; + char duration[314]; + char model[18]; + char serial_no[20]; + char exclamation_flag[2]; + int hours; + int minutes; + int seconds; + u64 total_duration_seconds; + u64 total_throughput; + nwipe_context_t** c; + c = ptr; + + exclamation_flag[0] = 0; + device[0] = 0; + status[0] = 0; + throughput[0] = 0; + summary_top_border[0] = 0; + summary_top_column_titles[0] = 0; + blank[0] = 0; + verify[0] = 0; + duration[0] = 0; + model[0] = 0; + serial_no[0] = 0; + hours = 0; + minutes = 0; + seconds = 0; + + /* A time buffer. */ + time_t t; + + /* A pointer to the system time struct. */ + struct tm* p; + + /* Nothing to do, user didn't select any devices */ + if( nwipe_selected == 0 ) + { + return; + } + + /* initialise */ + total_throughput = 0; + + /* Get the current time. */ + t = time( NULL ); + p = localtime( &t ); + /* IMPORTANT: Keep maximum columns (line length) to 80 characters for use with 80x30 terminals, Shredos, ALT-F2 etc + * --------------------------------01234567890123456789012345678901234567890123456789012345678901234567890123456789-*/ + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "********************************************************************************" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "! Device | Status | Thru-put | HH:MM:SS | Model/Serial Number" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "--------------------------------------------------------------------------------" ); + /* Example layout: + * "! sdv |--FAIL--| 120MB/s | 01:22:01 | WD6788.8488YNHj/ZX677888388-N " + * ); " sdv | Erased | 120MB/s | 01:25:04 | WD6784.8488JKGG/ZX677888388-N " ); " sdv | Erased | + * 120MB/s | 01:19:07 | WD6788.848HHDDR/ZX677888388-N " ); End of Example layout */ + + for( i = 0; i < nwipe_selected; i++ ) + { + /* Device name, strip any prefixed /dev/.. leaving up to 6 right justified + * characters eg " sda", prefixed with space to 6 characters, note that + * we are processing the strings right to left */ + + idx_dest = 6; + device[idx_dest--] = 0; + idx_src = strlen( c[i]->device_name ); + idx_src--; + + while( idx_dest >= 0 ) + { + /* if the device name contains a / start prefixing spaces */ + if( c[i]->device_name[idx_src] == '/' ) + { + device[idx_dest--] = ' '; + continue; + } + if( idx_src >= 0 ) + { + device[idx_dest--] = c[i]->device_name[idx_src--]; + } + } + extern int user_abort; + + /* Any errors ? if so set the exclamation_flag and fail message, + * All status messages should be eight characters EXACTLY ! + */ + if( user_abort == 1 ) + { + strncpy( exclamation_flag, "!", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, "UABORTED", 8 ); + status[8] = 0; + } + else + { + if( c[i]->result != 0 ) + { + strncpy( exclamation_flag, "!", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, "-FAILED-", 8 ); + status[8] = 0; + } + else + { + + if( c[i]->pass_errors != 0 ) + { + strncpy( exclamation_flag, "!", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, "-FAILED-", 8 ); + status[8] = 0; + } + else + { + strncpy( exclamation_flag, " ", 1 ); + exclamation_flag[1] = 0; + + strncpy( status, " Erased ", 8 ); + status[8] = 0; + } + } + } + + /* Determine the size of throughput so that the correct nomenclature can be used */ + Determine_bandwidth_nomenclature( c[i]->throughput, throughput, 13 ); + + /* Add this devices throughput to the total throughput */ + total_throughput += c[i]->throughput; + + /* Retrieve the duration of the wipe in seconds and convert hours and minutes and seconds */ + /* WARNING More work needs doing on .. + * + * model + * serial no + * the footer + * testing under various fault situations ... WARNING */ + + c[i]->duration = difftime( c[i]->end_time, c[i]->start_time ); + + total_duration_seconds = (u64) c[i]->duration; + + if( total_duration_seconds % 60 ) + { + minutes = total_duration_seconds / 60; + + seconds = total_duration_seconds - ( minutes * 60 ); + } + else + { + minutes = total_duration_seconds / 60; + + seconds = 0; + } + if( minutes > 59 ) + { + hours = minutes / 60; + if( minutes % 60 ) + { + minutes = minutes - ( hours * 60 ); + } + else + { + minutes = 0; + } + } + + /* Device Model */ + strncpy( model, c[i]->device_model, 17 ); + model[17] = 0; + + /* Serial No. */ + strncpy( serial_no, c[i]->device_serial_no, 20 ); + model[17] = 0; + + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "%s %s |%s| %s | %02i:%02i:%02i | %s/%s", + exclamation_flag, + device, + status, + throughput, + hours, + minutes, + seconds, + model, + serial_no ); + } + + /* Determine the size of throughput so that the correct nomenclature can be used */ + Determine_bandwidth_nomenclature( total_throughput, total_throughput_string, 13 ); + + /* Blank abreviations used in summary table B=blank, NB=no blank */ + if( nwipe_options.noblank ) + { + strcpy( blank, "NB" ); + } + else + { + strcpy( blank, "B" ); + } + + /* Verify abreviations used in summary table */ + switch( nwipe_options.verify ) + { + case NWIPE_VERIFY_NONE: + strcpy( verify, "NV" ); + break; + + case NWIPE_VERIFY_LAST: + strcpy( verify, "VL" ); + break; + + case NWIPE_VERIFY_ALL: + strcpy( verify, "VA" ); + break; + } + + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "--------------------------------------------------------------------------------" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "[%i/%02i/%02i %02i:%02i:%02i] Total Throughput %s, %s, %iR+%s+%s", + 1900 + p->tm_year, + 1 + p->tm_mon, + p->tm_mday, + p->tm_hour, + p->tm_min, + p->tm_sec, + total_throughput_string, + nwipe_method_label( nwipe_options.method ), + nwipe_options.rounds, + blank, + verify ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, + "********************************************************************************" ); + nwipe_log( NWIPE_LOG_NOTIMESTAMP, "" ); +} + +void Determine_bandwidth_nomenclature( u64 speed, char* result, int result_array_size ) +{ + int idx; + + /* A pointer to a result character string with a minimum of 13 characters in length + * should be provided. + * + * Outputs a string of the form xxxTB/s, xxxGB/s, xxxMB/s, xxxKB/s B/s depending on the value of 'speed' + */ + + /* Initialise the output array */ + idx = 0; + while( idx < result_array_size ) + { + result[idx++] = 0; + } + + /* Determine the size of throughput so that the correct nomenclature can be used */ + if( speed >= INT64_C( 1000000000000 ) ) + { + snprintf( result, result_array_size, "%4lluTB/s", speed / INT64_C( 1000000000000 ) ); + } + else if( speed >= INT64_C( 1000000000 ) ) + { + snprintf( result, result_array_size, "%4lluGB/s", speed / INT64_C( 1000000000 ) ); + } + else if( speed >= INT64_C( 1000000 ) ) + { + snprintf( result, result_array_size, "%4lluMB/s", speed / INT64_C( 1000000 ) ); + } + else if( speed >= INT64_C( 1000 ) ) + { + snprintf( result, result_array_size, "%4lluKB/s", speed / INT64_C( 1000 ) ); + } + else + { + snprintf( result, result_array_size, "%4llu B/s", speed / INT64_C( 1 ) ); + } +} diff --git a/src/logging.h b/src/logging.h index 06d802a..86d199a 100644 --- a/src/logging.h +++ b/src/logging.h @@ -32,11 +32,14 @@ typedef enum nwipe_log_t_ { NWIPE_LOG_WARNING, // Things that the user should know about. NWIPE_LOG_ERROR, // Non-fatal errors that result in failure. NWIPE_LOG_FATAL, // Errors that cause the program to exit. - NWIPE_LOG_SANITY // Programming errors. + NWIPE_LOG_SANITY, // Programming errors. + NWIPE_LOG_NOTIMESTAMP // logs the message without the timestamp } nwipe_log_t; void nwipe_log( nwipe_log_t level, const char* format, ... ); void nwipe_perror( int nwipe_errno, const char* f, const char* s ); int nwipe_log_sysinfo(); +void nwipe_log_summary( nwipe_context_t**, int ); // This produces the wipe status table on exit +void Determine_bandwidth_nomenclature( u64, char*, int ); #endif /* LOGGING_H_ */ diff --git a/src/method.c b/src/method.c index e79d746..ca01c2a 100644 --- a/src/method.c +++ b/src/method.c @@ -123,6 +123,9 @@ void* nwipe_zero( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -138,6 +141,9 @@ void* nwipe_zero( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_zero */ @@ -150,6 +156,9 @@ void* nwipe_verify( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -162,6 +171,9 @@ void* nwipe_verify( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_verify */ @@ -175,6 +187,9 @@ void* nwipe_dod522022m( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -230,6 +245,9 @@ void* nwipe_dod522022m( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_dod522022m */ @@ -244,6 +262,9 @@ void* nwipe_dodshort( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -292,6 +313,9 @@ void* nwipe_dodshort( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_dodshort */ @@ -305,6 +329,9 @@ void* nwipe_gutmann( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -426,6 +453,9 @@ void* nwipe_gutmann( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_gutmann */ @@ -443,6 +473,9 @@ void* nwipe_ops2( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -588,12 +621,19 @@ void* nwipe_ops2( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_ops2 */ void* nwipe_is5enh( void* ptr ) { nwipe_context_t* c = (nwipe_context_t*) ptr; + + /* get current time at the start of the wipe */ + time( &c->start_time ); + c->wipe_status = 1; char is5enh[3] = {'\x00', '\xFF', '\x00'}; @@ -604,6 +644,10 @@ void* nwipe_is5enh( void* ptr ) c->result = nwipe_runmethod( c, patterns ); c->wipe_status = 0; + + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_is5enh */ @@ -617,6 +661,9 @@ void* nwipe_random( void* ptr ) nwipe_context_t* c; c = (nwipe_context_t*) ptr; + /* get current time at the start of the wipe */ + time( &c->start_time ); + /* set wipe in progress flag for GUI */ c->wipe_status = 1; @@ -629,6 +676,9 @@ void* nwipe_random( void* ptr ) /* Finished. Set the wipe_status flag so that the GUI knows */ c->wipe_status = 0; + /* get current time at the end of the wipe */ + time( &c->end_time ); + return NULL; } /* nwipe_random */ @@ -879,8 +929,19 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns ) } /* for passes */ - nwipe_log( - NWIPE_LOG_NOTICE, "Finished round %i of %i on %s", c->round_working, c->round_count, c->device_name ); + if( c->round_working < c->round_count ) + { + nwipe_log( + NWIPE_LOG_NOTICE, "Finished round %i of %i on %s", c->round_working, c->round_count, c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, + "Finished final round %i of %i on %s", + c->round_working, + c->round_count, + c->device_name ); + } } /* while rounds */ @@ -955,8 +1016,14 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns ) { return r; } - - nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name ); + if( c->verify_errors == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_ERROR, "[FAILURE] %s IS NOT empty.", c->device_name ); + } } /* verify */ @@ -989,7 +1056,14 @@ int nwipe_runmethod( nwipe_context_t* c, nwipe_pattern_t* patterns ) return r; } - nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name ); + if( c->verify_errors == 0 ) + { + nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Verified that %s is empty.", c->device_name ); + } + else + { + nwipe_log( NWIPE_LOG_NOTICE, "[FAILURE] %s Verification errors, not empty", c->device_name ); + } } nwipe_log( NWIPE_LOG_NOTICE, "[SUCCESS] Blanked device %s", c->device_name ); diff --git a/src/nwipe.c b/src/nwipe.c index ffbc36e..dc352f2 100644 --- a/src/nwipe.c +++ b/src/nwipe.c @@ -51,6 +51,7 @@ #include int terminate_signal; +int user_abort; int main( int argc, char** argv ) { @@ -74,6 +75,9 @@ int main( int argc, char** argv ) /* Initialise the termintaion signal, 1=terminate nwipe */ terminate_signal = 0; + /* Initialise the user abort signal, 1=User aborted with CNTRL-C,SIGTERM, SIGQUIT, SIGINT etc.. */ + user_abort = 0; + /* nwipes return status value, set prior to exit at the end of nwipe, as no other exit points allowed */ int return_status = 0; @@ -313,7 +317,7 @@ int main( int argc, char** argv ) /* Print serial number of device if it exists. */ if( strlen( (const char*) c2[i]->device_serial_no ) ) { - nwipe_log( NWIPE_LOG_INFO, "Device %s has serial number %s", c2[i]->device_name, c2[i]->device_serial_no ); + nwipe_log( NWIPE_LOG_NOTICE, "%s has serial number %s", c2[i]->device_name, c2[i]->device_serial_no ); } /* Do sector size and block size checking. */ @@ -458,9 +462,10 @@ int main( int argc, char** argv ) } while( terminate_signal != 1 ); } } - - nwipe_log( NWIPE_LOG_INFO, "Exit in progress" ); - + if( nwipe_options.verbose ) + { + nwipe_log( NWIPE_LOG_INFO, "Exit in progress" ); + } /* Send a REQUEST for the wipe threads to be cancelled */ for( i = 0; i < nwipe_selected; i++ ) { @@ -548,6 +553,9 @@ int main( int argc, char** argv ) } } + /* Generate and send the drive status summary to the log */ + nwipe_log_summary( c2, nwipe_selected ); + if( return_status == 0 ) { nwipe_log( NWIPE_LOG_INFO, "Nwipe successfully exited." ); @@ -595,8 +603,7 @@ void* signal_hand( void* ptr ) { // Log current status. All values are automatically updated by the GUI - case SIGUSR1: - { + case SIGUSR1: { compute_stats( ptr ); for( i = 0; i < nwipe_misc_thread_data->nwipe_selected; i++ ) @@ -665,11 +672,13 @@ void* signal_hand( void* ptr ) case SIGHUP: case SIGINT: case SIGQUIT: - case SIGTERM: - { + case SIGTERM: { /* Set termination flag for main() which will do housekeeping prior to exit */ terminate_signal = 1; + /* Set the user abort flag */ + user_abort = 1; + /* Return control to the main thread, returning the signal received */ return ( (void*) 0 ); diff --git a/src/nwipe.h b/src/nwipe.h index bd928df..a188e34 100644 --- a/src/nwipe.h +++ b/src/nwipe.h @@ -68,7 +68,7 @@ void* signal_hand( void* ); #include #include -#include "config.h" +/*#include "config.h"*/ /* System errors. */ extern int errno; diff --git a/src/prng.c b/src/prng.c index f542a07..7a0464b 100644 --- a/src/prng.c +++ b/src/prng.c @@ -19,6 +19,7 @@ #include "nwipe.h" #include "prng.h" +#include "context.h" #include "logging.h" #include "mt19937ar-cok/mt19937ar-cok.h"