diff --git a/README b/README index dd7a24c..f6ac955 100644 --- a/README +++ b/README @@ -14,6 +14,11 @@ Andy Beverley RELEASE NOTES ============= +v0.15 +===== +- Add more detailed information to status page when wiping +- Add ability to send SIGUSR1 to print wiping current status to log + v0.14 ===== - Added explicit check for ncurses (required for Fedora). See bug 3604008. diff --git a/configure.ac b/configure.ac index d24c7af..29586bf 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ([2.64]) -AC_INIT(nwipe, 0.14, andy@andybev.com) -AM_INIT_AUTOMAKE(nwipe, 0.14) +AC_INIT(nwipe, 0.15, andy@andybev.com) +AM_INIT_AUTOMAKE(nwipe, 0.15) AC_OUTPUT(Makefile src/Makefile man/Makefile) AC_CONFIG_SRCDIR([src/nwipe.c]) AC_CONFIG_HEADERS([config.h]) diff --git a/man/nwipe.1 b/man/nwipe.1 index 817f8db..4ccc60c 100644 --- a/man/nwipe.1 +++ b/man/nwipe.1 @@ -18,6 +18,8 @@ same as dwipe, with a few changes: - The parted library is used to detect drives .TP - The code is designed to be compiled with gcc +.TP +- SIGUSR1 can be used to log the stats of the current wipe .SH OPTIONS .TP @@ -41,6 +43,7 @@ Do not wait for a key before exiting (default is to wait). \fB\-\-nogui\fR Do not show the GUI interface. Can only be used with the autonuke option. Nowait option is automatically invoked with the nogui option. +SIGUSR1 can be used to retrieve the current wiping statistics. .TP \fB\-\-verify\fR=\fITYPE\fR Whether to perform verification of erasure (default: last) diff --git a/src/context.h b/src/context.h index 0249e1e..ca13d0c 100644 --- a/src/context.h +++ b/src/context.h @@ -123,9 +123,12 @@ typedef struct nwipe_context_t_ /* Values cannot form part of the second array below, hence the need for this. */ typedef struct { - int nwipe_enumerated; /* The number of devices available. */ - int nwipe_selected; /* The number of devices being wiped. */ - pthread_t *gui_thread; /* The ID of GUI thread. */ + int nwipe_enumerated; /* The number of devices available. */ + int nwipe_selected; /* The number of devices being wiped. */ + time_t maxeta; /* The estimated runtime of the slowest device. */ + u64 throughput; /* Total throughput */ + u64 errors; /* The combined number of errors of all processes. */ + pthread_t *gui_thread; /* The ID of GUI thread. */ } nwipe_misc_thread_data_t; /* The second points to the first structure, as well as the structure of all the devices */ diff --git a/src/device.c b/src/device.c index 7f66a14..472790a 100644 --- a/src/device.c +++ b/src/device.c @@ -124,6 +124,8 @@ int check_device( nwipe_context_t*** c, PedDevice* dev, int dcount ) next_device->label = dev->model; next_device->device_name = dev->path; next_device->device_size = dev->length * dev->sector_size; + /* Attempt to get serial number of device. */ + ioctl(next_device->device_fd, HDIO_GET_IDENTITY, &next_device->identity); (*c)[dcount] = next_device; diff --git a/src/gui.c b/src/gui.c index 2240e6b..207192d 100644 --- a/src/gui.c +++ b/src/gui.c @@ -446,35 +446,39 @@ void nwipe_gui_select( int count, nwipe_context_t** c ) { case NWIPE_SELECT_TRUE: - wprintw( main_window, " [wipe] %i. %s - %s (%lld bytes)", (i + offset + 1), + wprintw( main_window, " [wipe] %i. %s - %s %s (%lld bytes)", (i + offset + 1), c[i+offset]->device_name, c[i+offset]->label, + c[i+offset]->identity.serial_no, c[i+offset]->device_size ); break; case NWIPE_SELECT_FALSE: /* Print an element that is not selected. */ - wprintw( main_window, " [ ] %i. %s - %s (%lld bytes)", (i + offset +1), + wprintw( main_window, " [ ] %i. %s - %s %s (%lld bytes)", (i + offset +1), c[i+offset]->device_name, c[i+offset]->label, + c[i+offset]->identity.serial_no, c[i+offset]->device_size ); break; case NWIPE_SELECT_TRUE_PARENT: /* This element will be wiped when its parent is wiped. */ - wprintw( main_window, " [****] %i. %s - %s (%lld bytes)", (i + offset +1), + wprintw( main_window, " [****] %i. %s - %s %s (%lld bytes)", (i + offset +1), c[i+offset]->device_name, c[i+offset]->label, + c[i+offset]->identity.serial_no, c[i+offset]->device_size ); break; case NWIPE_SELECT_FALSE_CHILD: /* We can't wipe this element because it has a child that is being wiped. */ - wprintw( main_window, " [----] %i. %s - %s (%lld bytes)", (i + offset +1), + wprintw( main_window, " [----] %i. %s - %s %s (%lld bytes)", (i + offset +1), c[i+offset]->device_name, c[i+offset]->label, + c[i+offset]->identity.serial_no, c[i+offset]->device_size ); break; @@ -1659,13 +1663,13 @@ void *nwipe_gui_status( void *ptr ) int keystroke; /* The combined througput of all processes. */ - u64 nwipe_throughput = 0; + nwipe_misc_thread_data->throughput = 0; /* The estimated runtime of the slowest device. */ - time_t nwipe_maxeta = 0; + nwipe_misc_thread_data->maxeta = 0; /* The combined number of errors of all processes. */ - u64 nwipe_errors = 0; + nwipe_misc_thread_data->errors = 0; /* Time values. */ int nwipe_hh; @@ -1801,59 +1805,22 @@ void *nwipe_gui_status( void *ptr ) if ( nwipe_gui_blank == 0 ) { - nwipe_active = 0; // Number of active wipe threads - /* Enumerate all contexts to compute statistics. */ - for( i = 0 ; i < count ; i++ ) - { - /* Check whether the child process is still running the wipe. */ - if( c[i]->thread > 0 ) - { - /* Increment the child counter. */ - nwipe_active += 1; - - /* Maintain a rolling average of throughput. */ - nwipe_update_speedring( &c[i]->speedring, c[i]->round_done, nwipe_time_now ); - - if( c[i]->speedring.timestotal > 0 ) - { - /* Update the current average throughput in bytes-per-second. */ - c[i]->throughput = c[i]->speedring.bytestotal / c[i]->speedring.timestotal; - - /* Update the estimated remaining runtime. */ - /* Check that throughput is not zero (sometimes caused during a sync) */ - if (c[i]->throughput == 0) - { - c[i]->throughput = 1; - } - - c[i]->eta = ( c[i]->round_size - c[i]->round_done ) / c[i]->throughput; - - if( c[i]->eta > nwipe_maxeta ) - { - nwipe_maxeta = c[i]->eta; - } - } - - /* Update the percentage value. */ - c[i]->round_percent = (double) c[i]->round_done / (double) c[i]->round_size * 100; - - /* Accumulate combined throughput. */ - nwipe_throughput += c[i]->throughput; - - } /* child running */ - - /* Accumulate the error count. */ - nwipe_errors += c[i]->pass_errors; - nwipe_errors += c[i]->verify_errors; - - } /* for statistics */ - + nwipe_active = compute_stats(ptr); // Returns number of active wipe threads /* Print information for the user. */ for( i = offset ; i < offset + slots && i < count ; i++ ) { /* Print the context label. */ - mvwprintw( main_window, yy++, 2, "%s", c[i]->label ); + if ( strlen(c[i]->identity.serial_no) ) + { + mvwprintw( main_window, yy++, 2, "%s - %s (%s)", c[i]->device_name, + c[i]->label, + c[i]->identity.serial_no); + } + else { + mvwprintw( main_window, yy++, 2, "%s - %s", c[i]->device_name, + c[i]->label ); + } /* Check whether the child process is still running the wipe. */ if( c[i]->thread > 0 ) @@ -1940,6 +1907,7 @@ void *nwipe_gui_status( void *ptr ) nwipe_gui_load(); + u64 nwipe_throughput = nwipe_misc_thread_data->throughput; if( nwipe_throughput >= INT64_C( 1000000000000000 ) ) { nwipe_throughput /= INT64_C( 1000000000000 ); nwipe_format = nwipe_tera; } else if( nwipe_throughput >= INT64_C( 1000000000000 ) ) @@ -1975,6 +1943,7 @@ void *nwipe_gui_status( void *ptr ) mvwprintw( stats_window, NWIPE_GUI_STATS_ETA_Y, 1, "Remaining:" ); + time_t nwipe_maxeta = nwipe_misc_thread_data->maxeta; if( nwipe_maxeta > 0 ) { /* Do it again for the estimated runtime remaining. */ @@ -1990,7 +1959,7 @@ void *nwipe_gui_status( void *ptr ) /* Print the error count. */ mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_ERRORS_X, "Errors:" ); - mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_TAB, "%llu", nwipe_errors ); + mvwprintw( stats_window, NWIPE_GUI_STATS_ERRORS_Y, NWIPE_GUI_STATS_TAB, "%llu", nwipe_misc_thread_data->errors ); /* Add a border. */ box( stats_window, 0, 0 ); @@ -2050,6 +2019,70 @@ void *nwipe_gui_status( void *ptr ) } /* nwipe_gui_status */ +int compute_stats(void *ptr) +{ + nwipe_thread_data_ptr_t *nwipe_thread_data_ptr; + nwipe_thread_data_ptr = (nwipe_thread_data_ptr_t *) ptr; + + nwipe_context_t **c; + nwipe_misc_thread_data_t *nwipe_misc_thread_data; + + c = nwipe_thread_data_ptr->c; + nwipe_misc_thread_data = nwipe_thread_data_ptr->nwipe_misc_thread_data; + int count = nwipe_misc_thread_data->nwipe_selected; + + int nwipe_active = 0; + int i; + + time_t nwipe_time_now = time( NULL ); + + /* Enumerate all contexts to compute statistics. */ + for( i = 0 ; i < count ; i++ ) + { + /* Check whether the child process is still running the wipe. */ + if( c[i]->thread > 0 ) + { + /* Increment the child counter. */ + nwipe_active += 1; + + /* Maintain a rolling average of throughput. */ + nwipe_update_speedring( &c[i]->speedring, c[i]->round_done, nwipe_time_now ); + + if( c[i]->speedring.timestotal > 0 ) + { + /* Update the current average throughput in bytes-per-second. */ + c[i]->throughput = c[i]->speedring.bytestotal / c[i]->speedring.timestotal; + + /* Update the estimated remaining runtime. */ + /* Check that throughput is not zero (sometimes caused during a sync) */ + if (c[i]->throughput == 0) + { + c[i]->throughput = 1; + } + + c[i]->eta = ( c[i]->round_size - c[i]->round_done ) / c[i]->throughput; + + if( c[i]->eta > nwipe_misc_thread_data->maxeta ) + { + nwipe_misc_thread_data->maxeta = c[i]->eta; + } + } + + /* Update the percentage value. */ + c[i]->round_percent = (double) c[i]->round_done / (double) c[i]->round_size * 100; + + /* Accumulate combined throughput. */ + nwipe_misc_thread_data->throughput += c[i]->throughput; + } /* child running */ + + /* Accumulate the error count. */ + nwipe_misc_thread_data->errors += c[i]->pass_errors; + nwipe_misc_thread_data->errors += c[i]->verify_errors; + + } /* for statistics */ + + return nwipe_active; +} void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_bytes, time_t speedring_now ) { diff --git a/src/gui.h b/src/gui.h index 1727a3c..60a3d21 100644 --- a/src/gui.h +++ b/src/gui.h @@ -35,6 +35,7 @@ void nwipe_gui_rounds( void ); /* Change the rounds op void nwipe_gui_verify( void ); /* Change the verify option. */ void nwipe_gui_noblank( void ); /* Change the noblank option. */ +int compute_stats(void *ptr); void nwipe_update_speedring( nwipe_speedring_t* speedring, u64 speedring_done, time_t speedring_now ); diff --git a/src/nwipe.c b/src/nwipe.c index 83f1de8..3aea2c6 100644 --- a/src/nwipe.c +++ b/src/nwipe.c @@ -78,6 +78,9 @@ int main( int argc, char** argv ) /* Initialised and populated in device scan. */ nwipe_context_t **c1 = 0; + /* The array of pointers to contexts that will actually be wiped. */ + nwipe_context_t **c2 = (nwipe_context_t **)malloc(nwipe_enumerated * sizeof(nwipe_context_t *)); + /* Parse command line options. */ nwipe_optind = nwipe_options_parse( argc, argv ); @@ -130,6 +133,7 @@ int main( int argc, char** argv ) sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGQUIT); sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGUSR1); pthread_sigmask(SIG_SETMASK, &sigset, NULL); /* Create a signal handler thread. This thread will catch all */ @@ -141,7 +145,7 @@ int main( int argc, char** argv ) nwipe_misc_thread_data_t nwipe_misc_thread_data; nwipe_thread_data_ptr_t nwipe_thread_data_ptr; - nwipe_thread_data_ptr.c = c1; + nwipe_thread_data_ptr.c = c2; nwipe_misc_thread_data.nwipe_enumerated = nwipe_enumerated; if( !nwipe_options.nogui ) nwipe_misc_thread_data.gui_thread = &nwipe_gui_thread; @@ -225,9 +229,6 @@ int main( int argc, char** argv ) /* Pass the number selected to the struct for other threads */ nwipe_misc_thread_data.nwipe_selected = nwipe_selected; - /* The array of pointers to contexts that will actually be wiped. */ - nwipe_context_t **c2 = (nwipe_context_t **)malloc(nwipe_enumerated * sizeof(nwipe_context_t *)); - /* Populate the array of selected contexts. */ for( i = 0, j = 0 ; i < nwipe_enumerated ; i++ ) { @@ -287,10 +288,8 @@ int main( int argc, char** argv ) } */ - /* Attempt to get serial number of device. */ - - ioctl(c2[i]->device_fd, HDIO_GET_IDENTITY, &c2[i]->identity); - if ( c2[i]->identity.serial_no ) { + /* Print serial number of device if it exists. */ + if ( strlen(c2[i]->identity.serial_no) ) { nwipe_log( NWIPE_LOG_INFO, "Device %s has serial number %s", c2[i]->device_name, c2[i]->identity.serial_no); } @@ -496,6 +495,7 @@ void *signal_hand(void *ptr) sigaddset(&sigset, SIGTERM); sigaddset(&sigset, SIGQUIT); sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGUSR1); int i; @@ -517,13 +517,60 @@ void *signal_hand(void *ptr) switch ( sig ) { + // Log current status. All values are automatically updated by the GUI + case SIGUSR1 : + { + compute_stats(ptr); + + for( i = 0; i < nwipe_misc_thread_data->nwipe_selected; i++ ) + { + + if ( c[i]->thread ) + { + char *status; + switch( c[i]->pass_type ) + { + case NWIPE_PASS_FINAL_BLANK: + status = "[blanking]"; + break; + + case NWIPE_PASS_FINAL_OPS2: + status = "[OPS-II final]"; + break; + + case NWIPE_PASS_WRITE: + status = "[writing]"; + break; + + case NWIPE_PASS_VERIFY: + status = "[verifying]"; + break; + + case NWIPE_PASS_NONE: + break; + } + if( c[i]->sync_status ) { status = "[syncing]"; } + nwipe_log( NWIPE_LOG_INFO, "%s: %05.2f%%, round %i of %i, pass %i of %i %s", \ + c[i]->device_name, c[i]->round_percent, c[i]->round_working, c[i]->round_count, c[i]->pass_working, c[i]->pass_count, status ); + } + else + { + if( c[i]->result == 0 ) { nwipe_log( NWIPE_LOG_INFO, "%s: Success", c[i]->device_name ); } + else if( c[i]->signal ) { nwipe_log( NWIPE_LOG_INFO, "%s: Failure: signal %i", c[i]->device_name, c[i]->signal ); } + else { nwipe_log( NWIPE_LOG_INFO, "%s: Failure: code %i", c[i]->device_name, c[i]->result ); } + } + } + + break; + } + case SIGHUP : case SIGINT : case SIGQUIT : case SIGTERM : { - for( i = 0; i < nwipe_misc_thread_data->nwipe_enumerated; i++ ) + for( i = 0; i < nwipe_misc_thread_data->nwipe_selected; i++ ) { if ( c[i]->thread ) @@ -551,7 +598,6 @@ void *signal_hand(void *ptr) printf("%s\n", log_lines[i]); } - printf("Program interrupted (caught signal %d)\n", sig); // Cleanup diff --git a/src/options.c b/src/options.c index 96b9a31..5ab9796 100644 --- a/src/options.c +++ b/src/options.c @@ -395,7 +395,7 @@ display_help() puts(" --noblank Do not blank disk after wipe (default is to complete a final blank pass)" ); puts(" --nowait Do not wait for a key before exiting (default is to wait)" ); puts(" --nogui Do not show the GUI interface. Automatically invokes the nowait option" ); - puts(" Must be used with --autonuke option"); + puts(" Must be used with --autonuke option. Send SIGUSR1 to log current stats"); puts(""); exit( EXIT_SUCCESS ); } diff --git a/src/version.c b/src/version.c index 0be3915..2d9bdfa 100644 --- a/src/version.c +++ b/src/version.c @@ -4,7 +4,7 @@ * used by configure to dynamically assign those values * to documentation files. */ -const char *version_string = "0.14"; +const char *version_string = "0.15"; const char *program_name = "nwipe"; const char *author_name = "Andy Beverley"; const char *email_address = "andy@andybev.com";