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"